java 面试题

1Java 基础

1、HashMap的源码,实现原理,JDK8中对HashMap做了怎样的优化。

        jdk1.7实现:

        源码是采用Node数组方式实现,node即链表节点,故jdk1.7中hashMap是采用数组+链表方式实现,hashMap在进行初始化时,数组长度默认为16,当对key进行hash运算取数组下标发现该下标已存在时,会针对此key重新生成一个node节点然后追加到数组下标已存在对应的节点后面

        jdk1.8实现:

        原理大部分和1.7类似,最大的区别在于设置了链表默认长度,当链表长度大于8时,会转化为红黑树存储节点数据,以提升其查找效率

2、HaspMap扩容是怎样扩容的,为什么都是2的N次幂的大小

        ​​​​​​​

  • 空参数的构造函数:以默认容量、默认负载因子、默认阈值初始化数组。内部数组是空数组
  • 有参构造函数:根据参数确定容量、负载因子、阈值等。
  • 第一次put时会初始化数组,其容量变为不小于指定容量的2的幂数。然后根据负载因子确定阈值。
  • 如果不是第一次扩容,则新容量=旧容量*2,新阈值=新容量*负载因子

         为什么都是2的N次幂?性能优化吧,估计跟计算机的存储结构以及其使用的取模运算有关

3、HashMap,HashTable,ConcurrentHashMap的区别。

        hashMap:内部采用数组+链表(1.7)或数据+链表+红黑树实现(1.8),线程不安全

        HashTabel:内部结构数组+链表,线程安全,对里面的所有存取值方法采用了synchronize加锁方式

        ConcurrentHashMap:

        jdk1.7:采用分段锁机制,即基本结构为ReetrantLock+segement+数组+链表,其大概思想为将底层数组进行分段加锁,相比于hashTable,锁粒度相对较小,hashTable锁全局数据,ConcurrentHashMap则锁部分数据,需要2次hash运算才能定位到该元素,第一次定位segement,第二次定位segement里的链表节点头部,segement继承ReetrantLock进行加锁,并发度为segement个数,segement扩容时对其他segement不造成影响,其get方法无需加锁,采用volatile保证对其他线程可见

        jdk1.8:采用Synchronized+CAS+数组+链表+红黑树,相比于jdk1.7锁粒度更小,只锁链表头部节点,节点内部(node)的val和next都是volatile保证其可见性,替换,赋值都采用CAS

因为其锁链表头部节点,不影响其他元素读写,所以锁粒度更小,并发时效率也更高,扩容时阻塞所有读写操作,并发扩容,读操作无需加锁,采用volatile修饰node的val和next是保证读写线程对该变量可见,数组用volatile修饰,是保证扩容时被读线程感知

     

4、极高并发下HashTable和ConcurrentHashMap哪个性能更好,为什么,如何实现的。

ConcurrentHashMap,锁粒度更小,写并发效率更高

5、HashMap在高并发下如果没有处理线程安全会有怎样的安全隐患,具体表现是什么。

多线程put时可能会导致get无限循环,具体表现为CPU使用率100%; 原因:在向HashMap put元素时,会检查HashMap的容量是否足够,如果不足,则会新建一个比原来容量大两倍的Hash表,然后把数组从老的Hash表中迁移到新的Hash表中,迁移的过程就是一个rehash()的过程,多个线程同时操作就有可能会形成循环链表,所以在使用get()时,就会出现Infinite Loop的情况

写覆盖即多线程put时可能导致元素丢失 原因:当多个线程同时执行addEntry(hash,key ,value,i)时,如果产生哈希碰撞,导致两个线程得到同样的bucketIndex去存储,就可能会发生元素覆盖丢失的情况

6、java中四种修饰符的限制范围。

懒得写

7、Object类中的方法。

equals、hashCode等

8、接口和抽象类的区别,注意JDK8的接口可以有实现。

9、动态代理的两种方式,以及区别。

jdk动态代理:

Proxy:Proxy是所有动态代理的父类;它提供了一个静态的方法创建代理的Class对象来配置生成代理类Class文件的方法与参数,主要就是通过Proxy.newProxyInstance(类加载器,类实现的接口,InvocationHandler实现类),返回Object类型,通过接口类型强转换即可使用代理类;

InvacationHandler:每个动态代理实例都有一个关联的InvocationHandler;被代理类的代理方法被调用时,方法将被转发到InvocationalHandler的invoke方法执行

cglib动态代理:

Enchancer:来指定要代理的目标对象;实际处理逻辑的对象;最终通过create()方法得到代理对象,对这个对象的非final()方法的调用都会转发给代理对象;
bMethodInterceptor:动态代理的方法调用都会转发到intercept()上进行增强

preview

10、Java序列化的方式。

Java原生序列化
只要让类实现 Serializable 接口就行,序列化具体的实现是由ObjectOutputStream和ObjectInputStream来实现的

JSON序列化
JSON 可能是我们最熟悉的一种序列化格式了,JSON 是典型的 Key-Value 方式,没有数据类型,是一种文本型序列化框架,JSON 的具体格式和特性,网上相关的资料非常多,这里就不再介绍了。他在应用上还是很广泛的,无论是前台 Web 用 Ajax 调用、用磁盘存储文本类型的数据,还是基于 HTTP 协议的 RPC 框架通信,都会选择 JSON 格式

11、传值和传引用的区别,Java是怎么样的,有没有传值引用。

前者传的是真实内存里的值,后者传的是另外一个对象的地址,对于值引用,赋值运算符会直接改变变量的值,原来的值被覆盖掉,对于传引用,赋值运算符会改变引用中所保存的地址,原来的地址被覆盖掉。但是原来的对象不会被改变

12、一个ArrayList在循环过程中删除,会不会出问题,为什么。

肯定会啦,

异常:

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

list执行的remove方法:

public boolean remove(Object o) {
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                // 示例中调用的是此处的 fastRemove
                fastRemove(index);
                return true;
            }
    }
    return false;
}
private void fastRemove(int index) {
    // 此处 modCount + 1
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work
}

改变了modCount值,导致判断出现异常

Iterator中的remove方法:

public void remove() {
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();

    try {
        AbstractList.this.remove(lastRet);
        if (lastRet < cursor)
            cursor--;
        lastRet = -1;
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException e) {
        throw new ConcurrentModificationException();
    }

删除元素时expectedModCount也要做相应变更

2JVM

1、JVM的内存结构。

2、JVM方法栈的工作过程,方法栈和本地方法栈有什么区别。

3、JVM的栈中引用如何和堆中的对象产生关联。

4、可以了解一下逃逸分析技术。

5、GC的常见算法,CMS以及G1的垃圾回收过程,CMS的各个阶段哪两个是Stop the world的,CMS会不会产生碎片,G1的优势。

6、标记清除和标记整理算法的理解以及优缺点。

7、eden survivor区的比例,为什么是这个比例,eden survivor的工作过程。

8、JVM如何判断一个对象是否该被GC,可以视为root的都有哪几种类型。

9、强软弱虚引用的区别以及GC对他们执行怎样的操作。

10、Java是否可以GC直接内存。

11、Java类加载的过程。

12、双亲委派模型的过程以及优势。

13、常用的JVM调优参数。

14、dump文件的分析。

15、Java有没有主动触发GC的方式(没有)。

3数据结构与算法

1、B+树

2、快速排序,堆排序,插入排序(八大排序算法)

3、一致性Hash算法,一致性Hash算法的应用

4多线程

1、Java实现多线程有哪几种方式。

2、Callable和Future的了解。

3、线程池的参数有哪些,在线程池创建一个线程的过程。

4、volitile关键字的作用,原理。

5、synchronized关键字的用法,优缺点。

6、Lock接口有哪些实现类,使用场景是什么。

7、可重入锁的用处及实现原理,写时复制的过程,读写锁,分段锁(ConcurrentHashMap中的segment)。

8、悲观锁,乐观锁,优缺点,CAS有什么缺陷,该如何解决。

9、ABC三个线程如何保证顺序执行。

10、线程的状态都有哪些。

11、sleep和wait的区别。

12、notify和notifyall的区别。

13、ThreadLocal的了解,实现原理。

5分布式

1、分布式事务的控制。分布式锁如何设计。

2、分布式session如何设计。

3、dubbo的组件有哪些,各有什么作用。

4、zookeeper的负载均衡算法有哪些。

5、dubbo是如何利用接口就可以通信的。

6框架相关

1、SpringMVC的Controller是如何将参数和前端传来的数据一一对应的。

2、Mybatis如何找到指定的Mapper的,如何完成查询的。

3、Quartz是如何完成定时任务的。自定义注解的实现。

4、Spring使用了哪些设计模式。Spring的IOC有什么优势。

5、Spring如何维护它拥有的bean。

6、一些较新的东西JDK8的新特性,流的概念及优势,为什么有这种优势。

7、区块链了解如何设计双11交易总额面板,要做到高并发高可用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值