【面经】汇总

面经

Java基础

集合都有哪些

答:

Java集合主要分为两类,一类是实现了Collection接口的,另一类是实现Map接口的。

Collection集合可以分为:

  • Set:存储的元素无序,不能重复
    • HashSet:底层实现是基于HashMap,只使用了HashMap的Key
    • LinkedHashSet:底层实现是基于HashMap,并且将各个节点通过双向链表连接。
    • TreeSet:底层实现是基于红黑树,并且可以自定义排序规则,不允许存储NULL元素。
  • List:存储的元素有序,可以重复
    • ArrayList:底层实现是基于数组,内存空间连续,可以实现随机访问。
    • LinkedList :底层实现是基于双向链表
  • Queue:存储的元素先进先出,可以重复
    • PriorityQueue:底层实现是基于,默认是小顶堆,不允许存储NULL元素
    • Deque:双端队列
      • ArrayDeque:底层实现是基于数组,使用两个指针指向首尾,不允许存储NULL元素
      • LinkedList:同上
    • BlockingQueue:阻塞队列
      • ArrayBlockingQueue:底层实现是基于数组的有界阻塞队列
      • LinkedBlockingQueue:底层实现是基于单向链表的可选有界阻塞队列
      • PriorityBlockingQueue:底层实现是基于的有界阻塞队列,支持按元素优先级排序
      • SynchronousQueue同步队列,每个插入操作都必须等待对应的删除操作,反之删除操作也必须等待插入操作。不存储元素。
      • DelayQueue延迟队列,其中的元素只有到了其指定的延迟时间,才能够从队列中出队。

Map集合可以分为:

  • HashMap:不是线程安全,key 和 value 可以存储 null 值
    • JDK 8之前,底层实现是基于数组 + 链表
    • JDK 8之后,当链表长度大于阈值(默认8),则将链表转化为红黑树
  • Hashtable:线程安全,key 和 value 不可以存储 null 值
    • 底层实现是基于数组 + 链表
  • TreeMap:底层实现是基于红黑树,并且可以自定义排序规则。

面向对象的三大特点

答:

  • 封装: 把属性和方法封装到对象的内部,对外只暴露获取和修改的方法。
  • 继承: 类之间的一种扩展关系,子类拥有父类对象的属性和方法(包括私有属性和私有方法,但无法访问)。可以重写父类的方法。
  • 多态: 具体表现为父类的引用指向子类的实例。(编译看左边,运行看右边

ArrayList和LinkedList的区别?

答:

ArrayList

  • 基于动态数组实现的,默认初始容量为10,当元素数量超过当前容量时会进行自动扩容。因为是基于数组,所以可以实现随机访问。

LinkedList

  • 基于双向链表实现的,每个节点不仅要存储数据,还要存储指向前一个节点和后一个节点的指针。不支持随机访问,插入和删除效率高。

都不是线程安全的。

ArrayList底层扩容是怎么实现的?

答:

  • ArrayList在添加一个元素之前,首先会检查数组容量是否足够,如果元素数量超过了数组容量的大小,则会进行扩容。
  • 扩容:调用grow(int)方法进行扩容。扩容的大小为原数组容量的1.5倍,
  • 复制:扩容后,会调用Arrays.copyOf(elementData, newCapacity)方法,创建一个容量为扩容后大小的数组,并把原数组的元素复制进去。
  • 添加:再将一开始要添加的元素加入到数组中。

讲一讲HashMap、以及put方法的过程

答:

  • HashMap是一个用于存储键值对类型数据的集合,JDK1.8之前,使用的是数组 + 链表实现的。
  • HashMap存储元素的过程(put方法过程):
  • 首先会对key进行hash,得到要存储在数组中的哪个位置(因为hash后得到的值非常大,超过了数组的范围,所以hash值要对数组长度求余),知道要存储的位置后,首先判断该位置是否有元素存在,如果没有元素存在,则在该位置创建一个node节点,并将key,value保存到node节点内部
  • 如果该位置已经存在元素了,意味着产生了哈希冲突。然后遍历由node节点组成的链表,判断其中有没有节点中key的hash和值是否与插入的key相等的,如果有,则将其value进行覆盖。如果遍历到链表末尾都没有,则在尾部插入一个新的节点。
  • 插入新节点后,同时会判断链表的长度是否大于了阈值(8),如果大于了则将链表转化为红黑树,提高查询效率。

讲一讲HashMap的扩容过程

答:

  • 当数组中元素个数大于数组容量乘以负载因子0.75)的值时,会对数组容量进行扩容。
  • 新创建一个数组,大小为原数组的2倍。将原数组中所有的元素重新哈希,并放到新数组的对应位置。

Hashmap为什么要用红黑树而不用其他的树?

答:

红黑树: 红黑树是一种平衡二叉树,其插入、删除、查找的时间复杂度都为O(logn)

相比于其他树:

  • 普通二叉树: 避免了二叉树在最坏情况下O(n)的时间复杂度。
  • 其他平衡二叉树: 其他平衡二叉树是比红黑树更严格的平衡树,为了保持平衡,需要旋转的次数更多。
  • b树/b+树: 用B/B+树的话,在数据量不是很多的情况下,数据都会挤在一个结点里面,这时候就退化成了链表。B和B+树主要用于数据存储在磁盘上的场景

Java8新特性有哪些

答:

  1. 支持Lambda表达式
    • 就是对匿名实现类的表现形式进行简写
  2. 支持方法引用
  3. 新增函数式接口
    • 在一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口
  4. 新增了Stream API

LoadFactor负载因子参数怎么调?什么时候调?

答:

  • 负载因子:用于衡量HashMap内部存储空间的充满程度。比如说0.4,那么表示当容器填满40%的时候,HashMap就会进行扩容,扩充为原来的2倍大小。
  • 负载因子越小:冲突的几率就越低,但是会消耗更多的空间。
  • 负载因子越大:冲突的几率就越大,但是会更节省空间。
  • 如果内存资源充足,希望提高查询效率: 负载因子就可以调低一点。
  • 如果内存资源紧张,查询效率不那么重要: 负载因子就可以调高一点。
  • 默认负载因子:0.75

Object的hashCode和equals为什么要重写,假如没有重写hashCode会有什么问题?

答:

重写equals: 是为了判断当两个对象的属性值相同时,才认为是相同的对象。
重写hashCode: 为了根据对象的属性值来生成哈希码,与equals保持一致。
没有重写hashCode: 当向HashSet集合添加元素时,两个对象即使属性值一样,但也会添加进去。

阻塞队列怎么实现,用哪些变量实现,入队出队函数是什么样的

  • 阻塞队列(Blocking Queue)是一种线程安全的数据结构,它在队列为空时阻塞出队操作,在队列满时阻塞入队操作。

使用变量

  • 队列(queue):存储数据的容器。
  • 最大容量(maxSize):队列的最大容量。
  • 锁对象(lock):用于确保线程安全的对象锁。

入队函数

  1. 获取锁对象。
  2. 使用while循环检查队列是否已满,如果已满,调用lock.wait()使线程等待。
  3. 将元素添加到队列末尾,更新队列状态。
  4. 调用lock.notifyAll()通知所有等待的线程队列状态已改变。
  5. 释放锁对象。

出队函数

  1. 获取锁对象。
  2. 使用while循环检查队列是否为空,如果为空,调用lock.wait()使线程等待。
  3. 从队列头部移除一个元素,更新队列状态。
  4. 调用lock.notifyAll()通知所有等待的线程队列状态已改变。
  5. 释放锁对象。
import java.util.LinkedList;
import java.util.Queue;

public class BlockingQueue<T> {
   
    private Queue<T> queue = new LinkedList<>();
    private int maxSize;
    private final Object lock = new Object();

    public BlockingQueue(int maxSize) {
   
        this.maxSize = maxSize;
    }

    public void enqueue(T item) throws InterruptedException {
   
        synchronized (lock) {
   
            while (queue.size() == maxSize) {
   
                lock.wait();
            }
            queue.add(item);
            lock.notifyAll();
        }
    }

    public T dequeue() throws InterruptedException {
   
        synchronized (lock) {
   
            while (queue.isEmpty()) {
   
                lock.wait();
            }
            T item = queue.poll();
            lock.notifyAll();
            return item;
        }
    }
}

ConcurrentHashMap 加锁流程

  1. put() 操作时,先对 key 和 value 是否为 null 进行判断,如果是的话则抛出 NullPointerException。
  2. 通过对 key 哈希并对数组长度取余,获得数组对应的位置。
  3. 如果要添加的位置还没有其他 Node 节点,使用 Unsafe 的 CAS 操作进行添加。
  4. 如果要添加的位置已经有其他 Node 节点,则对 Node 节点添加 synchronized 锁。
  5. 遍历由 Node 节点组成的链表或是红黑树,如果存在 Node 节点key的hash与待插入的key的hash一致,并且equals方法也一致,则会对该位置的 Node 节点进行修改。如果遍历到数组尾部还没有,则新创建一个 Node 节点。

ConcurrentHashMap 为什么 key 和 value 不能为 null?

  • 为了避免歧义:在一个高并发的环境中,假设一个线程读取键值对,而另一个线程恰好删除了这个键值对,这时候第一个线程获取的值有可能是null,如果是允许存储null的话,就不知道原本这个值就是null,还是其他线程修改的null了。

JVM

在java怎么确保一个类不被重复加载

答:

  • 依靠双亲委派机制
  • 当一个类加载器要加载字节码文件时,首先向上查找父类加载器是否加载过
  • 如果加载过,则直接返回
  • 如果一直到顶级类加载器(Bootstrap)也没有加载过,则再从上至下尝试加载
  • 好处:避免类的重复加载、保证JDK的核心类库不会被替换

有哪些类加载器

答:

  • 启动类加载器(Bootstrap):默认加载Java安装目录/jre/lib下的类文件,比如rt.jar,tools.jar,resources.jar等。
  • 扩展类加载器:默认加载Java安装目录/jre/lib/ext下的类文件
  • 应用程序类加载器:默认加载为应用程序classpath下的类文件。
  • 自定义类加载器:继承ClassLoader抽象类,重写findClass方法。在findClass方法中,定义从哪里读取字节码文件,然后调用defineClass方法,在方法区和堆区创建对象。

Class对象能够被GC吗

答:

满足以下3个条件,就可以被回收

  • 此类所有实例对象以及子类对象都已经被回收
  • 加载该类的类加载器已经被回收
  • 该类对应的 java.lang.Class 对象没有在任何地方被引用。

方法区在JDK8是怎么实现的

答:

  • JDK7及之前的版本将方法区存放在堆区域中的永久代空间。
  • JDK8及之后的版本将方法区存放在元空间中,元空间位于操作系统维护的直接内存中。会发生内存溢出

线上项目发生内存溢出原因,如何定位,怎么解决

答:

内存溢出原因

  1. 一次性申请的对象太多。例如:一次性将数据库的千万级数据查询出来放到内存中。解决方法:分页查询
  2. 内存资源耗尽未释放。例如:高并发场景下,不断的使用资源信息例如jdbc connection没有释放。解决方法:池化技术
  3. 本身资源不够。例如:分配的堆内存太小,不足以支撑业务。解决方法:使用jmap -heap查看堆信息。

如何定位

1、程序已经OOM挂了

  • 提前设置了JVM参数:-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=保存路径如果没有设置,提桶跑路吧

2、系统运行中还未OOM

  • 使用命令 jmap -dump:format=b,file=demo.hprof java进程号 在线导出dump文件。(缺点:会进行一次Full GC

如何解决

  • 使用jvisualvm工具(JVM诊断工具)导入保存的dump文件,查看跟业务有关的对象,找到根对象,查看根对象的线程栈。定位到内存泄漏点。进行代码修复或者JVM参数调优。

什么是堆存储文件

答:

  • 堆转储
  • 35
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值