JAVA基础面试题详解及源码分析_java面试说说了解哪些底层源码-程序员宅基地

技术标签: jvm  面试  java  Java基础知识  

目录

1.== 和equals的区别

2.hashCode 和equals 的关系

3. ArrayList 和LinkedList 的区别

4.ConcurrentHashMap 的底层原理

5.final关键字

6.CopyOnWriteArrayList

7.HashMap 和HashTable

8.java 异常体系

9.List 和Set

10.String StringBuilder StringBuffer 区别

11.HashSet 和TreeSet的区别

12.Java的4钟引用类型

13.总结


1.== 和equals的区别


        ==比较的是jvm栈中的数据,如果是基础类型,则比较的是变量的值,如果是引用类型,则比较的是堆内存中对象到栈中的引用地址
        equals 默认情况使用的是Object对象的equals方法,比较规则和==是一样的,但是如果比较string的话,string对equals进行了重写,会先判断比较对象是否为string类型,然后判断长度后逐个拿出char字符来进行比较
        String变量比较,如果是直接定义赋值的变量,并且在堆内存中的String常量池中不存在,会创建并存放到常量池冲中,将引用地址赋值给变量,如果存在,则直接把引用地址赋值给变量;
如果是new的对象,则会在堆内存中创建String对象并赋值引用地址。
所以 String == 比较的时候,直接赋值和new同一个值,是不相等的
String a = "test" 
String b = "test"
String c  = new String ("test");
a == b true
a == c false


2.hashCode 和equals 的关系


        hashCode是java.Object对象的native方法,他会更具对象生成一个int整数作为hash码,在堆内存中会维护一张hash表,K,V格式,K为hash码,V为对象在堆内存中的引用地址
以HashSet为例,添加对象的时候就会先通过对象生成的hashCode码获取对应的内存地址,如果内存地址已经存在对象,则通过equals判断对象是否相等,如果相等则不存放,如果不相等则会重新计算出新的Hash码然后和要添加的对象的内存地址进行关联
2.1两个对象相等,则HashCode一定相等
2.2两个对象相等,则equals一定相等
2.3两个对象hashCode相等,对象不一定相等
2.4如果重写equals方法,则需要重写hashCode方法
源码参考这位老哥的博客:String的equals底层源码解读_Venhoul的博客-程序员宅基地


3. ArrayList 和LinkedList 的区别


        ArrayList 底层是由动态数据组成,是一块连续的内存,查找的时候可以通过下标进行随机查找,时间复杂度是O(1),如果初始化的时候没有定义长度,默认是0,
add的时候会进行扩容,第一次扩容默认长度是10,如果list.size+1超过默认容量10,则会进行下一次扩容,计算出新的容量(旧容量+旧容量/2),如果新容量大于默认值10,继续判断新容量是否大于最大容量  Integer.MAX_VALUE -8 ,如果大于则新容量为最大容量,然后使用Arrays.copy将elementData拷贝到新容量的数组里面,如果不是尾部添加,则需要进行数据移动(底层还是使用复制数据扩容)如果一开始指定List长度,一直进行尾部新增,效率比LinkedList要快很多。

源码参考这位老哥的博客:ArrayList源码分析(超详细)_算不出来没办法的博客-程序员宅基地
        LinkedList 是由链表组成,底层内存不连续,通过指针指向下一个数据,新增和删除只需要改变指针指向的位置,LinkedList继承了Deque,是一个双端队列,可以头部插入和尾部插入,ADD方法是添加到链表的尾部,很快但是不适合查询,查询的时候需要逐一遍历去查询,查询时间复杂度为O(N),而且最好使用Iterator(迭代器)去遍历,如果使用for循环,每次get(i)都需要对list进行遍历,性能消耗很大。另外最好不要使用IndexOf返回元素索引,然后用返回的索引进行遍历,当结果为空时会遍历整个list。


4.ConcurrentHashMap 的底层原理


        ConcurrentHashMap 在1.7是用Reentrantlock + segment + hashEntry 结构实现的,多线程是通过segment实现,线程安全是通过继承reentrantlock锁定segment实现
并发个数受segment个数限制,默认是16,可以在初始化定义指定个数,但是不支持变更(扩容),get方法无需枷锁,通过volatile关键字保证。

        ConcurrentHashMap 在1.8中底层结构Synchronied + CAS + 链表/红黑树 他的核心结构是Node数组 table,关键变量是sizeCtl,如果sizeCtl是-1,则有线程正在初始化table,如果是-N,则说明有N-1个线程在进行扩容,如果是table未初始化,sizeCtl表示table数组的大小,如果已经初始化,sizeCtl表示table数组扩容的阈值(容量*哈希因子-0.75)
多线程在对ConcurrentHashMap进行扩容的时候,每个线程会通过字段transferIndex判断已经分配到哪一个下标了,然后根据下标,计算出自己的扩容区间,再通过CAS对transferIndex进行更改,这样的话多个线程就扩容自己区间的数据,扩容一个Node对象后会在table数组里当前位置设置为 ForwardingNode(FWD),表示如果一个线程在进行put的时候发现对象为FWD,则在nextTable里面去找对应的最新数据。

        ConcurrentHashMap 1.7源码请参考这位老哥的博客:深入理解ConcurrentHashMap1.7源码_concurrenthashmap源码1.7_XHHP的博客-程序员宅基地

        ConcurrentHashMap 1.8源码请参考这位老哥的博客:【ConcurrentHashMap源码详细解析 jdk1.8版本 包括putVal、addCount、fullAddCount和transfer方法详解】_concurrenthashmap源码分析1.8_鱼跃龙门^我跃架构的博客-程序员宅基地
 

5.final关键字


        final关键字修饰类,不能被继承,修饰方法不能被重写,但可以重载,修饰变量,一旦赋值,则不能更改final修饰基本类型,不能再更改值,修饰引用类型,不能更改引用地址,但是可以修改引用地址对应对象里面的值
        匿名内部类和局部内部类访问主类的局部变量,必须用final修饰,因为匿名内部类和主类再编译的时候会生成两个class文件,当主类方法执行完成后,会进行回收,但是匿名内部类还需要访问主类的变量。这样的话匿名内部类
就会访问到一个已经回收了的变量,为了防止这种问题,就将主类的局部变量复制了一份到匿名内部类里面,为了防止主类对变量进行修改而导致两个类的变量不一致,所以要求主类里面对匿名内部类访问的局部变量添加final关键字,局部内部类原理一样的。 


6.CopyOnWriteArrayList

        CopyOnWriteArrayList线程安全的,它在添加对象的时候,通过复制一个size+1的数组,来将原来的对象进行copy,将新对象添加到数组最后一个,并且写操作会加锁,保证数据安全,然后将原数组指向复制的数组,读对象会在新创建的数组里面读,适合读多写少的场景,读出来的数据可能不是最新的,所以不适合实时性很强的场景


7.HashMap 和HashTable

        HashMap是线程不安全的,HashTable是线程安全的,但是HashTable底层添加和扩容都会加锁,所以性能很低,如果是多线程情况下应该使用ConcurrentHashMap,HashMap 1.7是数组+链式结构对象组成,1.8是数组+链式结构/红黑树结构对象(NODE<KEY,VALUE> 对象,属性有key,value,hash,nextNode)组成,源码原理讲解,添加KEY,VALUE时,会先计算出key的hash值,然后高16位与低16位进行异或运算(主要是为了增加hash值的随机性),然后与数组长度减一进行与运算,算出数组下标,接着判断元素下标是否有数据,如果没有直接添加,如果有,判断下标数据key值是否一致,一致则替换,不一致,则判断元素nextnode是否有值,有值则先判断元素结构是红黑树还是链式结构(红黑树是TreeNode对象类型,不同类型数据赋值不一样),继续判断KEY是否相等,没值则创建Node对象赋值给nextNode(或者进行红黑树赋值),有值相等则替换,不相等则继续判断下一个元素期间会有链表长度判断,如果链表长度大于8,且数组长度大于64,链式结构会进行红黑树转换(如果长度大于8,但是数组长度未超过64会优先进行扩容)。
HashMap 扩容是当前数据存入后判断数据个数是否超过数组长度*哈希因子,如果超过,则会创建当前容量*2的新数组,然后对元素重新hash后插入到新数组中,如果插入到新数组中有hash冲突,1.8以后采用尾插法(为了防止环化)

源码请参考这位老哥的博客:深入理解ConcurrentHashMap1.7源码_concurrenthashmap源码1.7_XHHP的博客-程序员宅基地

HashMap 1.7头插法死循环问题请参考这位老哥的博客:多线程情况下HashMap的头插法和尾插法,为什么尾插法会避免死循环? - 知乎

我认为关键点在于线程2恢复后table数组变成线程1扩容完成后的数组了,导致线程2循环去拿next节点对象

8.java 异常体系

        Throwable 所有异常的父类,子类有Exception和Error,Exception可以被处理的异常,ERROR是不可以被处理的异常,例如OOM,Exception异常又分为RunTimeException和CheckException,后者在写代码时
可以被检测出来,RunTimeException可以被捕获并处理,例如NullpointException异常


9.List 和Set


        List 按对象添加的顺序排序,有序,允许添加多个null对象,获取对象除了使用get(i),还可以使用迭代器Iterator进行遍历
Set 无序,只允许一个null对象,获取对象只能用迭代器Iterator遍历获取

10.String StringBuilder StringBuffer 区别


        String 定义好值后不能修改值的内容,如果修改变量只是新建了一个值,然后把变量引用地址换成了新建的值,StringBuilder StringBuffer是可变的,都是在原对象上操作。
StringBuffer是线程安全的,它的append()方法通过synchronized关键字修饰 StringBuilder 线程不安全,它的append()方法并没有加锁。
性能 StringBuilder > StringBuffer > String 使用场景:优先使用StringBuilder,如果是多线程情况且需要操作共享变量优先使用StringBuffer

11.HashSet 和TreeSet的区别

懒得写了,参考一下这位老哥的博客吧:HashSet和TreeSet的区别_treeset和hashset的区别_Android _ 业炎丶的博客-程序员宅基地

12.Java的4钟引用类型

12.1、强引用

当我们使用new创建对象时,被创建的对象就是强引用,如Object object = new Object(),其中的object就是一个强引用了。只要强引用存在,垃圾回收器将永远不会回收被引用的对象,哪怕内存不足时,JVM也会直接抛出OutOfMemoryError,不会去回收。如果想中断强引用与对象之间的联系,可以显示的将强引用赋值为null,这样一来,JVM就可以适时的回收对象了

12.2、软引用

软引用是用来描述一些非必需但仍有用的对象。在内存足够的时候,软引用对象不会被回收,只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。这种特性常常被用来实现缓存技术,比如网页缓存,图片缓存等。在 JDK1.2 之后,用java.lang.ref.SoftReference类来表示软引用。

如果一个对象只具备软引用,如果内存空间足够,那么JVM就不会GC它,如果内存空间不足了,就会GC该对象。 

12.3、弱引用

弱引用的引用强度比软引用要更弱一些,无论内存是否足够,只要 JVM 开始进行垃圾回收,那些被弱引用关联的对象都会被回收。在 JDK1.2 之后,用 java.lang.ref.WeakReference 来表示弱引用。

如果一个对象只具有弱引用,只要JVM的GC线程检测到了,就会立即回收。弱引用的生命周期要比软引用短很多。不过,如果垃圾回收器是一个优先级很低的线程,也不一定会很快就会释放掉软引用的内存。

4、虚引用

虚引用是最弱的一种引用关系,如果一个对象仅持有虚引用,那么它就和没有任何引用一样,它随时可能会被回收,在 JDK1.2 之后,用 PhantomReference 类来表示,通过查看这个类的源码,发现它只有一个构造函数和一个 get() 方法,而且它的 get() 方法仅仅是返回一个null,也就是说将永远无法通过虚引用来获取对象,虚引用必须要和 ReferenceQueue 引用队列一起使用。

如果一个对象只具有虚引用,那么它就和没有任何引用一样,随时会被JVM当作垃圾进行GC。

13.总结

看完了这些面试题讲解,相信大家对于JAVA基础已经有了很大的提升,有没有增加一点自己的信心,下面我们例举几个面试题,大家可以根据自己学到的知识进行测试

12.1 阿里二面:jdk 1.7和1.8 HashMap发生了什么变化

12.2 阿里二面:jdk 1.7和1.8 JVM虚拟机发生了什么变化(这个自己查一下)

12.3 阿里一面:说一下ArrayList和LinkedList的区别

12.4 阿里一面:说一下HashMap的put方法

12.5 京东二面:说一下泛型中extends和super的区别(自己查一下)

12.6 京东一面:说一下ConcurrentHashMap的扩容机制

12.7 蚂蚁二面:说一下ConcurrentHashMap在1.8中如何保证线程安全

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_41637062/article/details/129929049

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法