Java面试题
Java基础
八种基本数据类型的大小,和它们的封装类
基本类型 | 字节大小 | 位数 | 封装类 | 默认值 |
---|---|---|---|---|
byte | 1 | 8 | Byte | 0 |
short | 2 | 16 | Short | 0 |
int | 4 | 32 | Integer | 0 |
long | 8 | 64 | Long | 0l或0L |
float | 4 | 32 | Float | 0.0F或0.0f |
double | 8 | 64 | Double | 0.0 |
char | 2 | 16 | Character | u0000 |
boolean | - | - | Boolean | false |
引用数据类型(类)
1、Java提供好的类:
如:Random类,Scanner类等
2、自定义类
Switch能否用string做参数
jdk1.7之前,switch仅支持多种数据类型: int byte float double 以及它们的封装类型。
其对应的封装类型会自动转换为int,所以float、double等数据会出现失精的情况。
jdk1.7之后,switch是支持string的。
equals 和 == 的区别
== 对于基本类型来说是指的比较,对于引用类型来说引用的比较。
equals 本质上就是 == ,只不过 String 和 Integer 等重写了 equals 方法,把
equals 变成了值的比较。
*两个对面的hashCode()相同,则 equals()也一定为 true吗
答:不一定,两个对象的的hashCode()相同,equals() 也不一定为 true
示例:
String str1 = "通话";
String str2 = "重地";
sout(String.format("str1:%d|str2:%d",str1.hashCode(),str2.hashCode()));
sout(str1.equals(str2));
//执行结果:
//str1: 1179395 | str2: 1179395
//false
自动装箱
需要装箱、拆箱的类型
常量池
Object 有哪些公用方法
方法 | 释义 |
---|---|
clone | 保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出异常。 java里除了8种基本数据类型传参数是值传递,其他的类对象传参数都是引用传递,如果不希望将方法里的参数改变,就需要在类中复写clone方法 |
toString | 该方法用的比较多,一般子类都有覆盖 |
getClass | final方法,获得运行时类型 |
equals | 该方法是非常重要的方法,一般equals和 == 是不一样的,但是在做Object中两者是一样的。子类一般都要重写这个方法 |
hashCode | 该方法用于哈希查找,可以减少在重找中使用equals的次数,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection 中用到。 如果不重写hashCode(),在HashSet中添加两个equals的对象,会将两个对象都加入进去 |
wait | wait方法就是是当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait() 一直等待,知道获得锁或者被中断。wait(long timeout) 设定一个超时间隔,如果在规定时间内没有获得锁就返回。 调用该方法后,当前线程进入睡眠状态,直到以下事件发生:1、其他线程调用了该对象的notify方法 2、其他线程调用了该对象的notifyAll方法 3、其他线程调用了interrupt中断该线程 4、时间间隔到了。 此时该线程就可以被调度,如果是被中断的话,就抛出一个InterruptedException异常。 |
notify | 该方法唤醒在改对象上等待的某个线程 |
notifyAll | 该方法唤醒在改对象上等待的所有线程 |
finalize | 该方法用于释放资源。因为无法确定该方法什么时候被调用,很少使用 |
Java 中的四种引用
引用 | 释义 |
---|---|
StrongReference(强引用) | 强引用是醉经常使用的一种引用,如new 操作创建对象就属于强引用。无论如何JVM都不会去回收其内存 如:Obejct obj =new Object(); |
SoftReference(软引用) | JVM内存不足时才会去回收该对象,可以用来做内存预警 |
WeakReference(弱引用) | 对于弱引用 JVM会回收仅被弱引用所关联的对象 ,也就是说,弱引用对象会在一次 gc 之后被回收 |
PhantomReference(虚引用) | 虚引用对其原对象的生命周期毫无影响,其可以作为一种标记,档期所引用对象被会收拾其会自动加入到引用队列中,也就是说可以通过虚引用得到哪些对象已被回收 |
HashCode 的作用
该方法返回对象的哈希值
*当equals方法被重写的时候,通常有必要重写hashCode方法,以维护hashCode方法的常规协定,即相等对象必须具有相等的哈希值
HashMap中hashCode的作用
- 用于查找的便利性,hashCode是用来在散列表中确定对象的存储位置的
- 如果两个对象相同,就是使用于 equals(java.lang.Object)方法,那么这两个对象的hashCode一定要相同
- 如果对象的 equals 方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和 equals方法中使用的一致,否则就会违法上面提到的第2点。
- 两个对象的hashCode相同,并不一定表示两个对象就相等,也就是不一定使用于 equals(java.lang.Object)方法,只能说明这两个对象在散列存储结构中, 如Hashtable,他们“存放在同一个篮子里”。
hashCode什么时候需要重载?
一般的地方不需要重载hashCode,只有当类需要放在Hashtable、HashMap、HashSet 等等 hash 结构的集合时才会重载hashCode 。HashMap就是一个大内存块,里面有很多小内存块,小内存块里面是一系列的对象,可以利用hashCode来查找小内存块 hashCode%size(小内存块数量), 所以当 equals 相等时,hashCode 必须相等,而且如果是 object 对象。必须重载hashCode 和 equals 方法。
ArrayList、LinkedList、Vector的区别
相同点: 三者都是实现集合框架中的List,也就是所谓的 有序集合 。
释义 :
Vector是java早起提供的线程安全的动态数组 ,如果不需要线程安全,并不建议选择,毕竟同步是有额外开销的。Vector内部是使用对象数组来保存数据的,可以根据需要自动的增加容量,当数组满了,会创建新的数组,并拷贝数组数据。
ArrayList 是应用更加广泛的动态数组实现,它本身不是线程安全 ,所以性能要好很多。与 Vector 近似。ArrayList也是根据需要调整的容量,不过两者的天正逻辑是有所区别的。Vector在扩容时会提高一倍,而 ArrayList 则是增加 50% 。
LinkedList是 Java提供的双向链表 ,它不需要像上面两种那样调整容量,它也不是线程安全的。
ArrayList | Vector | LinkedList | |
---|---|---|---|
底层实现方式 | Object数组 | Vector是基于 synchronized实现的线程安全的ArrayList | 双向循环链表(jdk1.6之前),双向链表(jdk1.7) |
读写机制 | 插入:超过当前数组预定的最大值时,数组需要扩容,扩容过程需要调用底层System.arraycopy() 方法进行数组赋值操作 删除:删除元素并不会减少数组的容量,如需缩小容量,可调用trimToSize()方法,在查找元素时要遍历数组,对非 null 的元素采取 equals 的方式查找 | 插入: 需创建一个新的Entry对象,并更新相应元素的前后元素的引用 查询 :遍历链表 删除:遍历链表再删除 | |
读写效率 | ArrayList 对元素的增加和删除都会引起数组的内存分配空间动态发生变化,对其进行插入和删除速度较慢,但检索速度快 | LinkedList 是基于链表方式存放数据,增加和删除元素的速度比较快,但是检索速度比较慢 | |
线程安全 | 非线程安全 | 非线程安全 |
三者适合的应用场景
- Vector 和 ArrayList 作为动态数组,其内部元素以数组形式 顺序存储 的,所以非常 适合随机访问的场合 。除了尾部插入和删除元素,往往性能会相对较差,如: 数组中间位置插入一个元素,需要移动后续所有的元素
- LinkedList 进行节点插入、删除却要高效得多 ,但是随机访问性能则要比动态数组慢
- 在用用开发中,如果事先可以估计到, 应用操作是偏向于插入、删除,还是随机查询较多 ,就可以针对性的进行选择
链表和数组的对比
数组 | 链表 |
---|---|
最好用于索引有语意的情况,例如 scores[2] | 不适合用于索引有语意的情况 |
最大的优点: 支持快速查询(随机访问) | 优点:动态数据结构,不需要处理固定容量的问题。 缺点:丧失了随机访问能力 |
物理存储单元上连续 | 物理存储单元上不连续 |
String 、 StringBuffer 和 StringBuilder 的区别
String | StringBuffer | StringBuilder | |
---|---|---|---|
执行速度 | 最差 | 其次 | 最高 |
线程安全 | 线程安全 | 线程安全 | 线程不安全 |
使用场景 | 少量字符串操作 | 多线程环境下的大量操作 | 单线程环境下的大量操作 |
*StringBuffer : 效率不如 StringBuilder ,但远比 String 要高
Map、Set、List、Queue和Stack的特点和用法
Map | Set | List | Queue | Stack | |
---|---|---|---|---|---|
特点 | 保存映射关系的数据(key-value) 不包含重复的key TreeMap可保证其顺序 HashMap不可保证其顺序 | 不包含重复元素的Collection 最多包含一个 null 元素 | 可随机访问包含的元素 元素是有序的 可在任意位置增删元素 允许重复元素 | 先进先出 | 后进先出 继承自Vector(可增长的对象数组),也是同步的 |
用法 | keySet() ,抽取 key 序列,将 map 中所有的key 生成一个Set values() ,抽取 value 序列,将map 中所有的values 生成一个Collection | 只能使用Iterator 实现单向遍历 没有同步方法 | 可用Iterator 实现单向遍历 也可用 ListIterator 实现双向遍历 | 使用时应尽量避免使用 Collection 的add() 和 remover() 方法,而是使用 offer() 来加入元素,使用 poll() 来获取并移出元素。 如果不需要移出该元素,可以使用 element() 或者 peek() 方法 LinkedList 实现了Queue 接口 Queue通常不允许插入 null 元素,因为poll() 方法的一个特殊返回值是 null ,表名的是该队列不包含元素 | 提供了 push() 和 pop() 操作,以及取堆栈顶点的 peek 方法、堆栈是否为空的 empty 方法、堆栈中查找项并确定到堆栈顶距离的 search 方法 |
适合的场景 | 涉及到堆栈、队列的操作用List 涉及到快速随机访问元素的用ArrayList | 涉及到快速插入、删除元素的应该使用 LinkedList |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cT0xVD8O-1639138588204)(C:\Users\西法\AppData\Roaming\Typora\typora-user-images\image-20211210134107030.png)]
HashMap 和 HashTable 的区别
HashMap 简介
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l57XRB9z-1639138588205)(C:\Users\西法\AppData\Roaming\Typora\typora-user-images\image-20211210140142824.png)]
jdk1.7之前,hashmap由数组和链表组成
在jdk1.7中,只是单纯的数组+链表的结构,但是如果散列表中的hash碰撞过多时,会造成效率的降低,
所以在JKD1.8中对这种情况进行了控制,当一个hash值上的链表长度大于8时,该节点上的数据就不再以链表进行存储,而是转成了一 个红黑树。
map.put 操作
map.put(“tom”,203)
立即调用值(“tom”)的hashCode的方法计算key的hash值,然后这个值会再次经过运算得出最终的哈希值,根据哈希值计算出在 Node数组的存储下标
1、 如果该下标位置没有存储值,那么key-value直接存入
2、 如果该下标位置有值(一个单向链表,可能有多个Node节点),调用equals方法和链表中每个节点的key做判断,如果都返回 false,那么key-value存入,如果返回true,则将要存入的value替换之前已经存入的value
HashTable 简介
Hashtable同样是基于哈希表实现的,同样每个元素是一个key-value对,其内部也是通过单链表解决冲突问题,容量不足 (超过了阀值)时,同样会自动增长。
Hashtable也是JDK1.0引入的类,是线程安全的,能用于多线程环境中。
Hashtable同样实现了Serializable接口,它支持序列化,实现了Cloneable接口,能被克隆。
区别
HashMap | HashTable | |
---|---|---|
线程安全 | 不安全,如果想要线程安全,可以通过 Collections 类的静态方法 synchronizedMap 获取线程安全的 HashMap Map map =Collections.synchronizedMap(new HashMap()); | 安全 |
所继承父类 | AbstractMap类 | Dictionary类 (一个被废弃的类) |
包含的方法 | 没有 contains 方法,有 containsValue 和 containsKey 方法 | 有 contains 方法, 有 containsValue, 还有 containsKey 方法 |
是否允许 null 值 | 允许 | 不允许 |
计算hash值的方式 | ||
扩容方式 | 必须为原容量的 2倍,一定是 2 的幂次倍的扩容结果,原数组内的元素依次重新计算存放位置 | 扩容为原容量的 2倍 +1 |
JDK7 和 JDK8 中 hashMap 的实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-euOR76jr-1639138588206)(C:\Users\西法\AppData\Roaming\Typora\typora-user-images\image-20211210140142824.png)]
jdk1.7之前,hashmap由数组和链表组成
在jdk1.7中,只是单纯的数组+链表的结构,但是如果散列表中的hash碰撞过多时,会造成效率的降低,
所以在JKD1.8中对这种情况进行了控制,当一个hash值上的链表长度大于8时,该节点上的数据就不再以链表进行存储,而是转成了一 个红黑树。
Hashmap和currentHashmap的区别
Hashmap | currentHashmap | |
---|---|---|
本质 | 数组+链表 | HashMap 的基础上将数据分为多个segment(段),默认16个(concurrency level),每次操作对一个 segment(段)加锁,避免多线程锁的几率,提高并发效率 |
currentHashTable 可以完全替代 Hashtable吗
不能,hashtable 大小增加到一定的大小,性能会急剧下降,因此在迭代的时候,需要被锁定很长的时间。 currentHashTable 引入了 segment 的结构(segmentation), 无论变得多大,仅仅需要锁定 map 的某个部分。
| HashMap 的基础上将数据分为多个segment(段),默认16个(concurrency level),每次操作对一个 segment(段)加锁,避免多线程锁的几率,提高并发效率 |
currentHashTable 可以完全替代 Hashtable吗
不能,hashtable 大小增加到一定的大小,性能会急剧下降,因此在迭代的时候,需要被锁定很长的时间。 currentHashTable 引入了 segment 的结构(segmentation), 无论变得多大,仅仅需要锁定 map 的某个部分。