面试官:请简要介绍一下Java核心知识中的面向对象三大特性。
王铁牛:这个我知道,封装、继承、多态嘛。
面试官:不错,回答正确。那在多线程场景下,如何确保线程安全?
王铁牛:可以用synchronized关键字,或者用Lock接口。
面试官:嗯,基本思路是对的。再问一个,说说JVM的内存结构。
王铁牛:有堆、栈、方法区这些。
第一轮结束。
面试官:接下来问几个关于JUC的问题。CountDownLatch的作用是什么?
王铁牛:它可以让一个线程等待其他线程完成操作。
面试官:那CyclicBarrier呢?
王铁牛:这个……好像是让一组线程互相等待,直到都到达某个点。
面试官:最后一个,Semaphore有什么用?
王铁牛:不太清楚,感觉像是信号量之类的吧。
第二轮结束。
面试官:说说HashMap的底层实现原理。
王铁牛:它是数组加链表,后来又有了红黑树。
面试官:那ArrayList的扩容机制呢?
王铁牛:好像是每次扩容1.5倍。
面试官:Spring框架中,依赖注入有几种方式?
王铁牛:接口注入、构造器注入、setter方法注入。
第三轮结束。
面试结束,面试官表示会让王铁牛回家等通知。
答案:
- 面向对象三大特性:
- 封装:将数据和操作数据的方法封装在一起,对外提供统一的接口,隐藏内部实现细节,提高数据的安全性和程序的可维护性。
- 继承:子类继承父类的属性和方法,实现代码复用,提高开发效率。
- 多态:同一个行为具有多个不同表现形式或形态的能力。包括编译时多态(方法重载)和运行时多态(方法重写)。
- 多线程确保线程安全:
- synchronized关键字:可以修饰方法或代码块,保证同一时刻只能有一个线程访问被修饰的部分,从而实现线程同步。
- Lock接口:提供了比synchronized更灵活的锁控制,如可中断锁、定时锁等。通过Lock对象的lock()和unlock()方法来控制锁的获取和释放。
- JVM内存结构:
- 堆:是Java对象实例存储的地方,也是垃圾回收主要进行的区域。
- 栈:存储局部变量、方法调用等信息。
- 方法区:存储类信息、常量、静态变量等。
- CountDownLatch作用:
- 用于某个线程等待其他一组线程完成各自的工作之后再执行。通过构造函数传入需要等待的线程数量,调用countDown()方法来减少计数,当计数为0时,等待的线程被唤醒继续执行。
- CyclicBarrier作用:
- 让一组线程互相等待,直到所有线程都到达某个屏障点。它可以循环使用,当所有线程到达屏障点后,会执行预先设定的任务,然后屏障被重置,线程可以再次等待。
- Semaphore作用:
- 信号量,用于控制对共享资源的访问数量。通过构造函数指定许可数量,线程调用acquire()方法获取许可,调用release()方法释放许可。
- HashMap底层实现原理:
- 初始是一个数组,当有新元素插入时,通过计算key的哈希值,找到对应的数组下标位置。
- 如果该位置为空,则直接插入新元素。
- 如果不为空,则会形成链表或红黑树结构(当链表长度达到一定阈值时会转换为红黑树),新元素会插入到链表或红黑树的尾部。
- ArrayList扩容机制:
- 当向ArrayList中添加元素时,如果当前容量不足,会进行扩容。
- 扩容时,新容量是原容量的1.5倍(如果原容量小于10,则新容量为10)。
- 然后会创建一个新的更大的数组,将原数组中的元素复制到新数组中,再将新元素添加到新数组。
- Spring框架依赖注入方式:
- 接口注入:通过实现特定接口来进行依赖注入,不太常用。
- 构造器注入:在类的构造函数中通过参数传入依赖对象,确保对象在创建时就完成依赖注入,保证对象的完整性。
- setter方法注入:通过类的setter方法来设置依赖对象,灵活性较高,可以在对象创建后再进行依赖注入。