JAVA基础面试题(根据今年面经收集)

27 篇文章 0 订阅

为什么Arraylist每次扩容1.5倍?

首先扩容因子应该小于2,不然后面创建的数组会一直无法使用前面释放的空间。
例如1,2,4,8,当第三次扩容的时候,容量为8,而前面的容量加起来才7。
确定应该在1-2之间之后,再如何取就需要综合考虑,容量太小,就扩容频繁,影响性能,
容量太大,就会造成空间的浪费,1.5是权衡利弊的结果。

说说copyonwritearraylist

  1. CopyOnWriteArrayList是ArrayList线程安全的一种实现。CopyOnWrite也叫写诗复制容器,从字面看也就是先复制一份,然后再进行写操作。
  2. 在进行元素修改的时候,会先将数组拷贝一份,在拷贝的数组上进行更改,然后再将新数组更新到原来的引用上面。数组的拷贝主要使用System.arraycpoy和Arrays.copyOf.
  3. 在add,set,remove等方法都加了可重入锁,这样写操作就不能并发,但是在写时候还可以进行读操作,因为读操作没有锁,不过读到的数据是旧数据。
  4. CopyOnWriteArrayList有一个专属的迭代器,COWiterator。如果使用原生iterator迭代器,在迭代器里面对元素进行操作就会触发UnsupportedOperationException异常。

优点:

  1. CopyOnWriteArrayList适用于读多写少的情况,比如商品列表。

缺点:

  1. 内存占用大,因为写诗复制需要额外拷贝一份,需要双倍内存。
  2. 数据修改时间长,随着数组元素增加,进行数组拷贝会越来越慢。
  3. 数据一致性问题,因为使用写诗复制技术,因此只能确保最终的结果是一致的,不能保证数据的实时性。

concurrenthashmap1.7和1.8的区别

  1. 1.7的时候底层使用ReentrantLock+Segment数组+HashEntry数组+HashEntry链表,1.8使用synchronized+node数组+node链表+红黑树。
  2. 1.8相对于1.7,锁的粒度变小了。1.7锁的是Segment数组,相当于锁了多个链表的头节点,而1.8锁的是单个链表的头节点。
  3. 1.8开始,哈希碰撞产生的链表可以转红黑树,在链表长度大于8,就会转红黑树,这样查询复杂度从O(n)下降到O(logn),红黑树结点数少于6转换回链表。
  4. 1.7使用Segment分段锁来实现同步,所以1.7的并发度就是segment个数。,而1.8使用CAS+Synchronized实现同步,并发度是数组容量。
  5. 1.7有参构造函数初始化的时候,初始化容量是不小于参数n的最小2的m次方值。而1.8的初始化容量是不小于1.5n的2的m次方最小值。
  6. 1.7是单线程扩容,而1.8可以多线程进行协助扩容,通过给node数组加volatile确保扩容时可见性。
  7. 定位元素1.7需要进行两次hash然后再遍历链表,而1.8只需要一次hash,然后遍历链表或者红黑树就可以了。
  8. 读操作都不加锁,靠volatile来确保可见性,防止读到脏数据。

线程安全的容器

CurrentHashMap,CopyOnWriteArrayList,CopyOnWriteArraySet,Vector,HashTable.

CurrentHashMap扩容过程

1.7的时候CurrentHashMap是基于segment分段锁来实现的,数组的每个节点里面都是一个hashentry数组,每个segment相当于一个小型的hashmap,扩容原理和hashmap类似。创建容量两倍新数组,转移节点,重新赋值。不同segment之间互不影响,每个segment内部会单独判断是否超过阈值,需要扩容。
1.8的CurrentHashMap不再基于segment实现。当某个线程put元素的时候,如果正在扩容,就加入协助扩容;如果当前没有线程正在扩容,就会将节点加入CurrentHashMap中,然后判断链表是否超过阈值8,同时数组是否超过阈值64,如果超过,链表就转化为红黑树。最后再判断是否需要扩容。
1.8的CurrentHashMap支持协助扩容,扩容时会将原数组分组,然后每个线程每次获取一个分组进行协助扩容。
扩容的过程也是先创建一个容量为原容量两倍的数组,每个线程的扩容过程和1.8的hashmap差不多。

JDK1.7和1.8区别

  1. 多了lambda表达式。
  2. hashmap底层多了红黑树。
  3. currenthashmap底层线程安全实现由segment分段锁换成了CAS+synchronized。
  4. 用元空间替换掉了方法区。

JAVA和C,C++的区别

  1. JAVA有自动内存管理,不需要程序员主动释放内存;而C,C++程序员申请的内存需要手动释放,否则会出现内存泄漏的问题。
  2. JAVA只支持单继承,但是支持实现多个接口;而C,C++支持多继承。,支持实现多个接口。因此JAVA保留了类似于多继承的优点,减去了多继承带来的混乱。
  3. JAVA中没有指针这个概念,C++中可以通过指针操作内存,在实际操作中,很容易发生各种错误。而JAVA中没有指针,因此也有效地防止了一系列由指针引起的操作层失误。
  4. JAVA中所有的方法和数据都是类的一部分,想要访问需要通过对象或者类名访问;而C,C++可以将函数和变量定义为全局,需要的时候再调用。
  5. C,C++中可以进行操作符重载;而JAVA中放弃了操作符重载,不过可以通过函数来达到类似于操作符重载的效果。
  6. C,C++可以自动强制类似转换,而JAVA强制类似转换需要手动实现。
  7. C,C++中字符串是基本数据类型,程序中使用“Null”终止符代表字符串的结束;而JAVA中字符串是用类对象来实现的,并且可以通过加号进行字符串拼接。

说说异常

java中,异常分为受检异常和非受检异常。
受检异常必须手动处理,否则编译不通过。处理的方式有try catch捕捉;或者显式抛给上一层进行处理。如IO流的异常。
非受检异常不要求必须处理,因为非受检异常非常多,如果全部都处理,性能消耗大。如除数为0,空指针异常。
异常、错误关系图:java异常处理包括异常和错误;异常能被程序处理,而错误无法处理。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值