java基础与java集合面试题

类和对象

Q:讲一下面向对象OOP思想。
面向对象主要是抽象,封装,继承,多态。
多态又分为重载和重写。重载主要是方法参数类型不一样,重写则是方法内容不一样。
Q:抽象类和接口有什么区别?

  1. 抽象类中可以没有抽象方法;接口中的方法必须是public abstract抽象方法;
  2. 抽象类中可以有普通的成员变量;接口中的变量必须是public static final 类型的,必须被初始化,接口中只有常量,没有变量。
  3. 抽象类只能单继承,接口可以继承多个父接口;
  4. Java 8 中接口中会有 default 方法,即方法可以被实现。

Q:java是值传递还是引用传递?
java是值传递。
因为 Java 是值传递的,也就是说,我们在调用一个需要传递参数的函数时,传递给函数的参数并不是我们传递进去的参数本身,而是它的一个副本,我们改变了数据其实只是改变了副本的数据而已,并不会对原来的参数有任何的改变。
Q:类初始化的顺序是怎样的?
创建类的对象的时候,先初始化静态变量和静态代码块(静态变量和静态代码块的初始化顺序按照在类中的先后顺序),然后再初始化非静态变量和非静态代码块(非静态变量和非静态代码块之间也是按照在类中的先后顺序),如果非静态变量未显示赋值会被赋值为默认值。最后执行构造函数。
Q:final修饰符有什么作用?
1.final修饰的类,不可以被继承。
2.final修饰方法,可以把方法锁定,以防任何继承类修改它的含义。
3.fianl修饰的变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
Q:创建对象的方式有哪些?
new,反射(包括使用Class类的newInstance方法和Constructor类的newInstance方法),clone,反序列化
还可以使用建造者模式。

Object

Q:Object有哪些方法?
getClass(),hashCode(),equals(),wait(),notify(),notifyAll(),toString(),finalize(),clone()
Q:如何理解hashCode()?
Q:hashCode()和equals()有什么联系?
重写equals()必须重写hashCode()。
首先equals与hashcode间的关系是这样的:
1、如果两个对象相同(即用equals比较返回true),那么它们的hashCode值一定要相同;
2、如果两个对象的hashCode相同,它们并不一定相同(即用equals比较返回false)
Q:clone()的深克隆和浅克隆,有什么区别?
浅克隆:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅克隆仅仅复制所考虑的对象,而不复制它所引用的对象。
深克隆:被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深克隆把要复制的对象所引用的对象都复制了一遍。

基础类型

Q:基础类型有哪些?怎么比较?
int,char,float,double都是基本类型。 String不是基本类型。
基础类型直接用==比较。
Q:Integer和int的区别?
Integer是int的包装类。
Integer默认值是null,int默认值是0。
int默认是4个字节,Integer是16个字节。

Q:Integer和int怎么比较?Integer怎么比较?
Integer是int的包装类。Integer跟Integer不可以使用==比较。
Integer跟int可以使用==比较,Integer会自动拆箱。

String

Q:String怎么比较?从内存角度讲。
使用equals()比较。如果使用==比较的是内存,使用equals()比较的是对象的内容。
Q:String可以继承吗?
String用final修饰,不可以继承。
Q:String和StringBulider、StringBuffer有什么区别?
String长度不可变。StringBuilder、StringBuffer长度可变。
StringBuffer是线程安全的,StringBuilder是线程不安全的。
StringBuffer通过synchronized 关键字来实现同步操作
Q:String里面的equals()是怎么实现的?

IO

Q:IO中用到了哪些设计模式?
IO中用到了装饰器模式,适配器模式。
装饰器模式表现如: BufferedReader reader=new BufferedReader(new FileReader( new File(fileName)));
同样是增强对象的功能,为什么使用装饰器, 而不是继承?
比如功能一,功能二,功能三。装饰器可以根据需要增强功能,随意组合装配。
而由于功能的随机组合有六种,想通过继承增强功能要写六个类。当需要的功能较多时,组合总和太大,通过继承完成不太理想。

异常

Q:Java中的异常有哪些?
Exception分为运行时异常和非运行时异常。
RuntimeException, 运行时异常,特点是Java编译器不会检查它。
非运行时异常 (编译异常): 是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常
Error:用于指示合理的应用程序不应该试图捕获的严重问题。如OutOfMemoryError、StackOverFlowError。
Q:RuntimeException有哪些?
空指针异常类:NullPointerException
类型强制转换异常:ClassCastException
数组下标越界异常:ArrayIndexOutOfBoundsException
文件已结束异常:EOFException
文件未找到异常:FileNotFoundException
字符串转换为数字异常:NumberFormatException
操作数据库异常:SQLException
输入输出异常:IOException
方法未找到异常:NoSuchMethodException
Q:什么时候会出现OutOfMemoryError、StackOverFlowError?
内存溢出OutOfMemoryError,当内存不够用时,会抛出OutOfMemoryError。需要优化代码,或者调整jvm参数,通过-Xmx将最大内存数调大。
栈溢出StackOverFlowError,当大量嵌套递归时,可能会出现StackOverFlowError,递归从本质上讲也是栈的使用。

Jdk

Q:Jdk各个版本有哪些差别?
Q:Jdk1.8以后的特性,有没有了解过?
Jdk1.5:foreach迭代,可变参数,枚举,自动拆装箱,泛型,注解;
Jdk1.6:轻量级HttpServer的API,使用Complier API,对脚本语言的支持;
Jdk1.7:Switch支持字符串,泛型类型自动推断,try-with-resource资源关闭技巧(省去了自己在finally里去关闭资源),Objects工具类,ForkJoinPool
Jdk1.8:接口的默认方法实现与静态方法,Lambda表达式,函数式接口,方法与构造函数引用,新的日期与时间API,流式处理。
Jdk1.9:优化了HTTP Client,支持HTTP/2和HTTPS/TLS协议。
Jigsaw模块化项目,简化进程API,轻量级JSON API,钱和货币的API,进程改善和锁机制优化,代码分段缓存。
Jdk10:局部变量的类型推断var,改进GC和内存管理,线程本地握手,备用内存设备上的堆分配
Jdk11:可拓展的低延迟垃圾收集器ZGC。
Q:Java8的Lambda表达式,你会用吗?可以做哪些事情?能否手写一下?
匿名函数式。可以进行筛选,排序,求值,查找等功能。
Q:看过哪些Jdk源码?
集合,多线程,并发。

java集合面试题(重点)

关键词:ArrayList、LinkedList、HashTable、HashMap、ConcurrentHashMap、HashSet、
Q:List和Set有什么区别?
List有序可重复,Set无序不重复

ArrayList

Q:ArrayList和LinkedList有什么区别?(常问)
ArrayList数据结构是数组,查询比较快。LinkedList是一个双向链表,增删比较快。
Q:ArrayList什么时候扩容?如何扩容?扩容多少?
ArrayList在初始化的时候,是不会扩容的。比如List list=new ArrayList(20); 执行这句代码时不会进行扩容。
ArrayList数组的初始长度是10,当数组填满后会进行扩容,每次扩容0.5倍,也就是原来的1.5倍。
扩容的过程,由于数组的长度是不可变的,只能新建一个数组。
扩容的过程就是数组拷贝 Arrays.copyOf的过程,每一次扩容就会开辟一块新的内存空间和数据的复制移动。
Q:ArrayList是线程安全的么?
不是
Q:线程安全的list有哪些?
Collections.synchronizedList:适合写多读少的场景。
CopyOnWriteList:适合读多写少的场景。

HashSet

Q:讲一下HashSet
无序不重复,允许为null。
Q:HashSet是怎么实现的?
基于HashMap来实现的,底层采用HashMap来保存元素。
Q:HashSet源码是怎么实现不重复的?
HashMap的key是不能重复的,而这里HashSet的元素又是作为了map的key,当然也不能重复了。
HashMap的key怎么保证是不能重复的?
HashMap调用了对象的hashCode和equals方法进行的判断。

TreeSet

Q:TreeSet和HashSet的区别是什么?
TreeSet是有序的,HashSet是无序的。
Q:TreeSet的数据结构是什么?
底层是红黑树数据结构,也就说是一棵自平衡的排序二叉树,这样就可以保证当需要快速检索指定节点。

HashTable

Q:HashTable是怎么实现的?数据结构是什么?
哈希表(hash table, 也叫散列表),是种根据关键码值(Key value)而直接进行访问数据结构,它可以提供快速的插入操作和查找操作。也就是说,它通过把关键字值映射到一个位置来访问记录,以加快查找的速度。这个映射函数称为哈希函数。
Hashtable是通过"拉链法"实现的哈希表。由一个数组实现,数组元素都是单向链表。

HashMap(重点)

Q:HashMap有哪些遍历方式?
Map.Entry,EntrySet,Iterator(迭代器)
Q:HashMap和Hashtable的区别有哪些?
HashMap没有考虑同步,是线程不安全的;Hashtable使用了synchronized关键字,是线程安全的;
前者允许null作为Key;后者不允许null作为Key
HashTable继承了Dictionary类。而HashMap是基于AbstractMap

Q:HashMap的数据结构是什么?
数组+链表+红黑树。
HashMap数据结构是一个长度为16的数组,数组元素是链表,在jdk1.8中,数组长度达到64.并且链表长度大于8时,会转换为红黑树。

Q:HashMap的链表,什么时候会转变成红黑树?
在jdk1.8中,数组长度达到64.并且链表长度大于8时,会转换为红黑树。
详情见: https://blog.csdn.net/g5zhu5896/article/details/82968287

Q:讲一下红黑树有哪些特征?
红黑树是一种自平衡排序二叉树,树中每个节点的值,都大于或等于在它的左子树中的所有节点的值,并且小于或等于在它的右子树中的所有节点的值,这确保红黑树运行时可以快速地在树中查找和定位的所需节点。
如果一棵树的左子节点或右子节点不存在,就被认定为是黑色。
红黑树是一种二叉查找树,每个节点都标记红色或黑色,根节点为黑色,规律是“有红必有黑,红红不相连”。
红黑树保持平衡操作:1.变色 ,2.旋转。
红黑树,任何不平衡都能在3次旋转内调整完成。对于频繁插入和删掉的情景,红黑树的优势明显。
https://blog.csdn.net/weixin_44780082/article/details/112239269

Q:HashMap如何保证key的唯一性?如何保证key不重复?
HashMap每次添加元素都会先判断是否有重复的元素,hashcode()方法进行比较,若一样再通过equals()方法比较,他们的底层数据结构如果也相同的话,就认为数据已经存在了,就不会添加数据。
简单点说,就是HashMap调用了对象的hashCode和equals方法进行的判断。

Q:HashMap什么时候扩容?扩容多少?
HashMap中有一个负载因子loadFactor,0.75。当数组的长度达到数组长度(16)*负载因子(0.75)也就是12时,会进行扩容。
HashMap扩容时长度会翻倍。

Q:HashMap扩容后,是怎么查找数据的?数据的位置会变吗?Hash算法需要变吗??
扩容后需要重新计算数据的位置。
位置可能改变,就是需要通过hash值与扩容后的32 -1 进行&运算,重新计算数据所在的位置。

Q:HashMap中的Hash算法具体是怎样的?
Hash算法本质上就是三步:取key的hashCode值、与2^(n-1)做位运算(相当于取模运算)。
通过Hash算法,对key计算出数组下标,把数据放在对应下标元素的链表上。
详情见: https://www.cnblogs.com/expiator/p/10062968.html

Q:HashMap是如何解决hash碰撞的 ?
Hash碰撞:通过Key进行Hash的计算,就可以获取Key对应的HashCode。如果出现了重复的hashCode,就称作碰撞。
如果发生了碰撞事件,那么意味这数组的一个位置要插入两个或者多个元素,这个时候数组上面挂的链表起作用了,链表会将数组某个节点上多出的元素按照尾插法(jdk1.7及以前为头插法)的方式添加。
详情见: https://juejin.cn/post/6876105622274703368

Q:HashMap中的链表,在jdk1.8和jdk1.8之前,有什么区别?
在jdk1.8之前使用的是数组+单链表的数据结构会先进行扩容再插入,执行的是头插法,先将原位置的数据移到后一位,再插入数据到该位置;在并发情况下,会出现环形链表死循环问题。
在jdk1.8及之后时,使用的是数组+链表+红黑树的数据结构(当链表的深度达到8的时候,也就是默认阈值,就会自动扩容把链表转成红黑树的数据结构来把时间复杂度从O(n)变成O(logN)提高了效率),会先进行插入再进行扩容,执行的是尾插法,直接插到链表尾部/红黑数树,不会出现环形链表死循环问题。

简单点说就是,"插头会死(循环),插尾不会死"

Q:JDK1.7版本的HashMap死循环怎么出现的?
第一步:线程启动,有线程t1和t2线程都准备对HashMap进行扩容,此时t1和t2指向的都是头结点A,而t1和t2的下一个结点分别是t1.next和t2.next,他们都指向B结点。
第二步:进行扩容,假设t2线程的时间片用完,进入休眠状态,而线程t1开始进行扩容操作,一直到t1线程执行完成,t2线程才被唤醒。
因为HashMap扩容采用的是头插法,线程t1执行完成之后,链表的节点顺序发生了变化,但是t2线程对发生的一切还是不可知的,所以它指向的节点引用依然没变。
当线程t2恢复执行之后,死循环就出现了:因为t1线程完成扩容之后,B节点的下一节点是A节点,而t2线程的首节点是A,A的下一个节点是B,这样t1执行完成之后的顺序是B到A,而T2的顺序是A到B,这样A节点和B节点就形成了死循环。
详情见:https://blog.csdn.net/zhang2383906154/article/details/125262034

Q:HashMap 的长度为什么是2的幂次方?Q:HashMap为什么要用2的幂次方-1去做位运算??
小知识:跟2^(n-1)进行与运算,相当于做取余。
在Hash算法中,需要进行以下操作:

  1. 通过将 Key 的 hash 值与 length - 1 进行 & 运算,实现了当前 Key 的定位,2 的幂次方可以减少冲突(碰撞)的次数,提高 HashMap 查询效率
  2. 如果 length 为 2 的次幂 则 length-1 转化为二进制必定是 11111……的形式,在与 h 的二进制与操作效率会非常的快,而且空间不浪费;如果 length 不是 2 的次幂,比如 length 为 15,则 length - 1 为 14,对应的二进制为 1110,在于 h 与操作,最后一位都为 0 ,而 0001,0011,0101,1001,1011,0111,1101 这几个位置永远都不能存放元素了,空间浪费相当大,更糟的是这种情况中,数组可以使用的位置比数组长度小了很多,这意味着进一步增加了碰撞的几率,减慢了查询的效率!这样就会造成空间的浪费。
    简单理解,不同的hash值,和2^(n-1)进行位运算后,能够得出不同的值,使得添加的元素能够均匀分布在集合中不同的位置上,避免hash碰撞。

Q:HashMap之1.7和1.8的区别?

  • 底层数据结构不一样,1.7是数组+链表,1.8则是数组+链表+红黑树结构(当链表长度大于8,转为红黑树)
  • JDK1.8中resize()方法在表为空时,创建表;在表不为空时,扩容;而JDK1.7中resize()方法负责扩容,inflateTable()负责创建表。
  • 1.8中没有区分键为null的情况,而1.7版本中对于键为null的情况调用putForNullKey()方法。但是两个版本中如果键为null,那么调用hash()方法得到的都将是0,所以键为null的元素都始终位于哈希表table【0】中。
  • 当1.8中的桶中元素处于链表的情况,遍历的同时最后如果没有匹配的,直接将节点添加到链表尾部;而1.7在遍历的同时没有添加数据,而是另外调用了addEntry()方法,将节点添加到链表头部。
  • 1.7中新增节点采用头插法,1.8中新增节点采用尾插法。这也是为什么1.8不容易出现环型链表的原因。
  • 1.7中是通过更改hashSeed值修改节点的hash值从而达到rehash时的链表分散,而1.8中键的hash值不会改变,rehash时根据(hash&oldCap)==0将链表分散。
  • 1.8rehash时保证原链表的顺序,而1.7中rehash时有可能改变链表的顺序(头插法导致)。
  • 在扩容的时候:1.7在插入数据之前扩容,而1.8插入数据成功之后扩容。

详情见: https://blog.csdn.net/h1458280799/article/details/85265135

ConcurrentHashMap(重点)

Q:可以在遍历ArrayList或者HashMap时,一边增删集合吗?
不可以。集合的fail-fast原则。
在遍历ArrayList或者HashMap时,一边增删集合会抛出ModifyCountException。

Q:HashMap和ConcurrentHashMap的区别是什么?
HashMap是线程不安全的。ConcurrentHashMap线程安全。
HashMap的Key和Value可以是null。而ConcurrentHashMap不可以。

Q:ConcurrentHashMap是如何实现的?
在jdk1.8之前,ConcurrentHashMap使用锁分段和ReentrantLock的。
在jdk1.8,ConcurrentHashMap主要使用了CAS(compareAndSwap)、volatile(保证可见性)、synchronized锁。

Q:为什么ConcurrentHashMap在jdk1.8要使用CAS+Synchronized取代锁分段和ReentrantLock ?
(1)ConcurrentHashMap 的 synchronized 只锁定当前链表或红⿊⼆叉树的⾸节点,这样只要 hash 不冲突,就不会产⽣并发,效率得到提升。
Synchronized上锁的对象是链表里的Node,将锁细化了。除非两个线程同时操作一个链表节点,那么才会争抢同一把锁.
(2)1.8的Synchronized有进行过优化。因为自旋锁,偏向锁,轻量级锁的原因,不用将等待线程挂起,所以在这种情况下要比ReentrantLock高效.
只要线程通过自旋拿到锁,那么Synchronized就不会升级为重量级锁,而等待的线程也就不用被挂起,我们也就少了挂起和唤醒这个上下文切换的过程开销.

Q:ConcurrentHashMap是怎么保证线程安全的?
CAS,比较和替换,运用了乐观锁的机制。
synchronized锁。

Q: ArrayList是线程不安全的,如果要求线程安全,应该用什么集合代替ArrayList?
CopyOnWriteArrayList。写时拷背。

集合排序

集合排序,偏向算法,问的人比较少。
Q:Collections.sort()怎么排序?默认的排序是什么?
必须实现Comparable接口的compare方法。
默认的排序方法是Timsort。结合了合并排序和插入排序而得出的排序算法。
Q:Arrays.sort();默认的排序算法是什么?
数组有序就会采用归并排序,数组无序就会采用快速排序。
详情见:https://blog.csdn.net/timheath/article/details/68930482

参考资料:
《码出高效 java开发手册》
https://www.cnblogs.com/wmyskxz/p/9016611.html

posted on 2018-12-28 21:39  乐之者v  阅读(685)  评论(0编辑  收藏  举报

导航