Java学习
1. 解释下什么是面向对象?面向对象和面向过程的区别? 2. 面向对象的三大特性?分别解释下? 3. JDK、JRE、JVM 三者之间的关系? 4. 重载和重写的区别? 5. Java 中是否可以重写一个 private 或者 static 方法? 6. 构造方法有哪些特性? 7. 在 Java 中定义一个不做事且没有参数的构造方法有什么作用? 8. Java 中创建对象的几种方式? 9. 抽象类和接口有什么区别? 10. 静态变量和实例变量的区别? 11. 12、short s1 = 1;s1 = s1 + 1;有什么错?那么 short s1 = 1; s1 += 1;呢?有没有错误? 12. Integer 和 int 的区别? 13. 装箱和拆箱的区别 14. switch 语句能否作用在 byte 上,能否作用在 long 上,能否作用在 String 上? 15. 16、final、finally、finalize 的区别 16. == 和 equals 的区别? 17. 两个对象的 hashCode() 相同,则 equals() 也一定为 true 吗? 18. 为什么重写 equals() 就一定要重写 hashCode() 方法? 19. & 和 && 的区别? 20. Java 中的参数传递时传值呢?还是传引用? 21. Java 中的 Math.round(-1.5) 等于多少? 22. 如何实现对象的克隆? 23. 深克隆和浅克隆的区别? 24. 什么是 Java 的序列化,如何实现 Java 的序列化? 25. 什么情况下需要序列化? 26. Java 的泛型是如何工作的 ? 什么是类型擦除 ? 27. 什么是泛型中的限定通配符和非限定通配符 ? 28. List 和 List 之间有什么区别 ? 29. Java 中的反射是什么意思?有哪些应用场景? 30. 反射的优缺点? 31. Java 中的动态代理是什么?有哪些应用? 32. 怎么实现动态代理? 33. static 关键字的作用? 34. super 关键字的作用? 35. 字节和字符的区别? 36. String 为什么要设计为不可变类? 37. String、StringBuilder、StringBuffer 的区别? 38. String 字符串修改实现的原理? 39. String str = "i" 与 String str = new String("i") 一样吗? 40. String 类的常用方法都有那些? 41. final 修饰 StringBuffer 后还可以 append 吗? 42. Java 中的 IO 流的分类?说出几个你熟悉的实现类? 43. 字节流和字符流有什么区别? 44. BIO、NIO、AIO 有什么区别?
- 面向对象(Object-Oriented,简称 OO)是一种编程范式,其核心思想是将程序抽象成一系列互相交互的对象。面向对象的关键特点包括封装、继承和多态。面向过程(Procedure-Oriented)编程是另一种编程范式,它将程序视为一系列有序的过程和函数调用。
- 面向对象的三大特性:封装(将对象的状态和行为捆绑在一起,隐藏内部实现细节),继承(子类继承父类的属性和方法,实现代码复用)和多态(同一接口或父类下的多个子类具有不同的行为实现)。
- JDK(Java Development Kit,Java 开发工具包):提供了编写、编译、运行 Java 程序所需的工具;JRE(Java Runtime Environment,Java 运行环境):提供运行 Java 程序所需的环境;JVM(Java Virtual Machine,Java 虚拟机):执行 Java 程序的平台,负责将 Java 字节码转换为机器码并执行。
- 重载(Overloading):同一类中存在相同方法名但参数列表不同的方法;重写(Overriding):子类覆盖父类中的方法,要求方法名、参数列表和返回类型相同。
- Java 中不能重写 private 或 static 方法。private 方法无法被子类继承;static 方法属于类级别,子类调用的是父类的静态方法,而非覆盖。
- 构造方法特性:与类名相同;无返回类型;可以重载;不能被继承;默认提供无参构造方法;可以调用其他构造方法。
- 定义一个不做事且没有参数的构造方法,可以在创建对象时使用无参构造方法,避免使用者必须提供参数的麻烦。
- Java 中创建对象的几种方式:new 关键字;反射(Class.forName().newInstance());克隆(clone() 方法);反序列化。
- 抽象类:可以包含抽象方法(无具体实现)和具体方法,不能被实例化,可作为基类;接口:仅包含抽象方法、默认方法和静态方法,可以被多个类实现。
- 静态变量:类级别,所有实例共享;实例变量:对象级别,每个实例独立。
- short s1 = 1; s1 = s1 + 1; 错误,因为 s1 + 1 结果为 int 类型,需强制类型转换;short s1 = 1; s1 += 1; 无错,+= 自动进行类型转换。
- Integer:包装类,可处理null 值,有方法可用;int:基本数据类型,更高效,无法处理 null 值。
- 装箱:基本数据类型转换为包装类型;拆箱:包装类型转换为基本数据类型。
- switch 语句可作用在 byte、short、char、int、枚举和 String 类型上,但不能作用在 long 上。
- final:修饰符,表示不可更改(类、方法、变量);finally:异常处理中的代码块,始终执行;finalize:Object 类的方法,垃圾回收前调用。
- ==:比较对象引用或基本类型值是否相等;equals:比较对象内容是否相等,需要重写。
- 两个对象的 hashCode() 相同,equals() 不一定为 true。hashCode() 相同表示对象在同一哈希桶,但可能具有不同内容。
- 重写 equals() 后,应重写 hashCode(),以保证相等对象具有相同的哈希码,确保在哈希表中正确存储和查找。
- &:按位与操作符,同时也是逻辑与操作符;&&:短路逻辑与操作符,左边表达式为 false 时,不计算右边表达式。
- Java 参数传递采用传值方式。基本类型传递值本身,引用类型传递引用的值(指向对象的内存地址)。
- Java 中的 Math.round(-1.5) 等于 -1。
- 实现对象的克隆:实现 Cloneable 接口,并重写 clone() 方法。
- 深克隆:复制对象及其引用对象;浅克隆:仅复制对象本身,不复制引用对象。
- Java 序列化:将对象转换为字节序列,以便存储或传输;实现:实现 Serializable 接口,使用 ObjectOutputStream 和 ObjectInputStream。
- 需要序列化的情况:对象存储(如文件系统);对象传输(如网络通信);对象拷贝(如深克隆)。
- Java 泛型:在编译时提供类型安全,但在运行时被擦除;类型擦除:运行时泛型信息被移除,保证与非泛型代码兼容。
- 限定通配符:有界通配符,如 ? extends T(上界)和 ? super T(下界);非限定通配符:无界通配符,如 ?。
- List:有序、可重复、支持索引的集合;List:泛型列表,可指定元素类型。
- Java 反射:在运行时分析、使用类或对象的能力;应用场景:动态加载类、动态代理、框架开发等。
- 反射优点:灵活、动态;缺点:性能较低、可读性差、可能破坏封装。
- Java 动态代理:在运行时动态创建代理对象,实现对目标对象的代理;应用:AOP(面向切面编程)、框架开发、监控等。
- 实现动态代理:使用 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口。
- static 关键字:表示静态成员(方法、变量、代码块、内部类),属于类级别,无需创建实例即可访问。
- super 关键字:表示父类对象,用于在子类中调用父类的方法或属性。
- 字节:8 位二进制数,表示计算机中的数据单位;字符:表示文本中的单个元素(如字母、数字、标点符号)。
- String 设计为不可变类,因为:线程安全、安全性(如作为散列键)、节省内存(字符串常量池)。
- String:不可变字符序列,适用于不需修改的字符串;StringBuilder:可变字符序列,非线程安全,适用于频繁修改的字符串;StringBuffer:可变字符序列,线程安全,适用于多线程环境下的字符串修改。
- String 字符串修改实现:通过创建新的 String 对象并返回,原字符串不变。
- String str = "i":在字符串常量池创建或引用字符串;String str = new String("i"):在堆上创建新的 String 对象,同时引用字符串常量池的字符串。
- String 类常用方法:length()、charAt()、substring()、indexOf()、lastIndexOf()、startsWith()、endsWith()、equals()、equalsIgnoreCase()、compareTo()、concat()、trim()、toLowerCase()、toUpperCase()、valueOf()、replace()、replaceAll()、split() 等。
- final 修饰的 StringBuffer 对象的引用不可更改,但可以调用 append() 修改其内容。
- Java IO 流分类:字节流(InputStream、OutputStream)和字符流(Reader、Writer);实现类:FileInputStream、FileOutputStream、FileReader、FileWriter 等。
- 字节流:以字节为单位,处理二进制数据;字符流:以字符为单位,处理文本数据。
- BIO(Blocking IO,阻塞式 IO):同步阻塞模式,效率低;NIO(Non-blocking IO,非阻塞式 IO):同步非阻塞模式,多路复用,效率高;AIO(Asynchronous IO,异步 IO):异步非阻塞模式,使用回调机制,性能更高。
1. Java 中常用的容器有哪些?
2. ArrayList 和 LinkedList 的区别?
3. ArrayList 实现 RandomAccess 接口有何作用?为何 LinkedList 却没实现这个接口?
4. ArrayList 的扩容机制?
5. Array 和 ArrayList 有何区别?什么时候更适合用 Array?
6. HashMap 的实现原理/底层数据结构?JDK1.7 和 JDK1.8
7. HashMap 的 put 方法的执行过程?
8. HashMap 的 get 方法的执行过程?
9. HashMap 的 resize 方法的执行过程?
10. HashMap 的 size 为什么必须是 2 的整数次方?
11. HashMap 多线程死循环问题?
12. HashMap 的 get 方法能否判断某个元素是否在 map 中?
13. HashMap 与 HashTable 的区别是什么?
14. HashMap 与 ConcurrentHashMap 的区别是什么?
15. HashTable 和 ConcurrentHashMap 的区别?
16. ConcurrentHashMap 的实现原理是什么?
17. HashSet 的实现原理?
18. HashSet 怎么保证元素不重复的?
19. LinkedHashMap 的实现原理?
20. Iterator 怎么使用?有什么特点?
21. Iterator 和 ListIterator 有什么区别?
22. Iterator 和 Enumeration 接口的区别?
23. fail-fast 与 fail-safe 有什么区别?
24. Collection 和 Collections 有什么区别
- Java 常用容器:List(如 ArrayList、LinkedList)、Set(如 HashSet、LinkedHashSet、TreeSet)、Map(如 HashMap、LinkedHashMap、TreeMap)、Queue(如 PriorityQueue、LinkedList)。
- ArrayList:基于动态数组实现,支持随机访问,插入删除性能较差;LinkedList:基于双向链表实现,插入删除性能较好,不支持随机访问。
- ArrayList 实现 RandomAccess 接口表示支持高效随机访问;LinkedList 没实现该接口,因为链表结构不适合随机访问,性能较差。
- ArrayList 扩容机制:当容量不足时,创建一个新数组(1.5 倍原容量),将原数组内容复制到新数组,并回收原数组。
- Array:固定长度、基本类型或引用类型、效率高;ArrayList:动态长度、仅引用类型、功能多。当长度固定且使用基本类型时,Array 更合适。
- HashMap 实现原理:基于哈希表实现,数组 + 链表/红黑树;JDK1.7:数组 + 链表;JDK1.8:数组 + 链表/红黑树。
- HashMap put 方法执行过程:计算键的哈希值,确定数组下标;判断是否需要扩容;遍历链表,若有相同键则更新值,否则添加新键值对。
- HashMap get 方法执行过程:计算键的哈希值,确定数组下标;遍历链表,查找键,返回对应值。
- HashMap resize 方法执行过程:创建新数组(2 倍原容量),重新计算键值对在新数组中的位置,将原数组的键值对复制到新数组中。
- HashMap size 必须是 2 的整数次方,以便高效计算数组下标(与运算替代取模运算)。
- HashMap 多线程死循环问题:在 JDK1.7 中,多线程同时修改 HashMap 可导致链表形成环形结构,引发死循环。
- HashMap get 方法可判断元素是否在 map 中:如果返回 null,表示元素不存在(但需注意键对应的值可能为 null)。
- HashMap 与 HashTable 区别:线程安全性(HashMap 非线程安全,HashTable 线程安全);性能(HashMap 性能较高);允许 null 键值(HashMap 允许,HashTable 不允许);实现方式(HashMap 基于数组 + 链表/红黑树,HashTable 基于数组 + 链表)。
- HashMap 与 ConcurrentHashMap 区别:线程安全性(HashMap 非线程安全,ConcurrentHashMap 线程安全);实现方式(ConcurrentHashMap 使用分段锁或 CAS 保证线程安全)。
- HashTable 和HashTable 和 ConcurrentHashMap 区别:线程安全性(HashTable 使用对象级锁,ConcurrentHashMap 使用分段锁或 CAS);性能(ConcurrentHashMap 性能较高,因为其锁粒度更小);实现方式(ConcurrentHashMap 基于数组 + 链表/红黑树,HashTable 基于数组 + 链表)。
- ConcurrentHashMap 实现原理:JDK1.7:基于分段锁实现线程安全,数组 + 链表;JDK1.8:基于 CAS + synchronized 实现线程安全,数组 + 链表/红黑树。
- HashSet 实现原理:基于 HashMap 实现,存储元素的哈希值。
- HashSet 保证元素不重复:通过 HashMap 的键来存储元素,利用键的唯一性保证元素不重复。
- LinkedHashMap 实现原理:在 HashMap 的基础上,维护一个双向链表,记录元素插入顺序或访问顺序。
- Iterator 使用:通过 hasNext() 判断是否有下一个元素,next() 获取下一个元素,remove() 删除当前元素;特点:可以在遍历过程中删除元素。
- Iterator 和 ListIterator 区别:ListIterator 继承自 Iterator,仅适用于 List 类型;支持双向遍历、添加元素、设置元素值等额外功能。
- Iterator 和 Enumeration 接口区别:Iterator 支持删除操作,Enumeration 不支持;Iterator 是 Java 集合框架的一部分,Enumeration 是较早的遗留接口。
- fail-fast 和 fail-safe 区别:fail-fast(如 HashMap、ArrayList)遍历过程中修改集合会抛出 ConcurrentModificationException;fail-safe(如 ConcurrentHashMap、CopyOnWriteArrayList)遍历时允许修改集合,不抛异常。
- Collection:Java 集合框架的顶层接口,表示一组对象;Collections:集合框架的工具类,提供对集合操作的静态方法(如排序、查找、同步等)。
1. 并行和并发有什么区别?
2. 线程和进程的区别?
3. 守护线程是什么?
4. 创建线程的几种方式?
5. Runnable 和 Callable 有什么区别?
6. 线程状态及转换?
7. sleep() 和 wait() 的区别?
8. 线程的 run() 和 start() 有什么区别?
9. 在 Java 程序中怎么保证多线程的运行安全?
10. Java 线程同步的几种方法?
11. Thread.interrupt() 方法的工作原理是什么?
12. 谈谈对 ThreadLocal 的理解?
13. 在哪些场景下会使用到 ThreadLocal?
14. 说一说自己对于 synchronized 关键字的了解?
15. 如何在项目中使用 synchronized 的?
16. 说说 JDK1.6 之后的 synchronized 关键字底层做了哪些优化,可以详细介绍一下这些优化吗?
17. 谈谈 synchronized 和 ReenTrantLock 的区别?
18. synchronized 和 volatile 的区别是什么?
19. 谈一下你对 volatile 关键字的理解?
20. 说下对 ReentrantReadWriteLock 的理解?
21. 说下对悲观锁和乐观锁的理解?
22. 乐观锁常见的两种实现方式是什么?
23. 乐观锁的缺点有哪些?
24. CAS 和 synchronized 的使用场景?
25. 简单说下对 Java 中的原子类的理解?
26. atomic 的原理是什么?
27. 说下对同步器 AQS 的理解?
28. AQS 的原理是什么?
29. AQS 对资源的共享模式有哪些?
30. AQS 底层使用了模板方法模式,你能说出几个需要重写的方法吗?
31. 说下对信号量 Semaphore 的理解?
32. CountDownLatch 和 CyclicBarrier 有什么区别?
33. 说下对线程池的理解?为什么要使用线程池?
34. 创建线程池的参数有哪些?
35. 如何创建线程池?
36. 线程池中的的线程数一般怎么设置?需要考虑哪些问题?
37. 执行 execute() 方法和 submit() 方法的区别是什么呢?
38. 说下对 Fork和Join 并行计算框架的理解?
39. JDK 中提供了哪些并发容器?
40. 谈谈对 CopyOnWriteArrayList 的理解?
41. 谈谈对 BlockingQueue 的理解?分别有哪些实现类?
42. 谈谈对 ConcurrentSkipListMap 的理解?
- 并行与并发区别:并行是指多个任务同时执行(如多核 CPU 同时处理多个任务);并发是指在同一时间段内,多个任务被交替执行(如单核 CPU 快速切换执行多个任务)。
- 线程和进程的区别:进程是操作系统进行资源分配和调度的基本单位,是程序运行时的一个独立单位;线程是进程中的一个执行流程,是 CPU 调度的基本单位,同一进程下的线程共享进程资源。
- 守护线程:在其他非守护线程执行结束后自动结束的线程,如垃圾回收线程。
- 创建线程的几种方式:继承 Thread 类,重写 run() 方法;实现 Runnable 接口,重写 run() 方法;实现 Callable 接口,重写 call() 方法;使用线程池。
- Runnable 和 Callable 区别:Runnable 没有返回值,Callable 有返回值;Runnable 的 run() 方法不抛异常,Callable 的 call() 方法可以抛异常。
- 线程状态及转换:新建(New);就绪(Runnable);运行(Running);阻塞(Blocked);等待(Waiting);超时等待(Timed Waiting);终止(Terminated)。
- sleep() 和 wait() 区别:sleep() 不释放锁,让当前线程暂停指定时间;wait() 释放锁,让当前线程等待,直至被其他线程唤醒。
- 线程的 run() 和 start() 区别:run() 直接执行线程逻辑,不开启新线程;start() 创建新线程并执行 run() 方法。
- Java 程序中保证多线程运行安全:使用 synchronized、Lock 接口、原子类、volatile 关键字、并发容器等。
- Java 线程同步的几种方法:synchronized 关键字、Lock 接口、CountDownLatch、Semaphore、CyclicBarrier 等。
- Thread.interrupt() 工作原理:给目标线程设置一个中断标志,如果目标线程处于阻塞状态,会抛出 InterruptedException 异常。
- ThreadLocal:用于保存线程独享的局部变量,每个线程对 ThreadLocal 变量的操作不会影响其他线程。
- ThreadLocal 使用场景:数据库连接、Session 管理、格式化类等。
- synchronized 关键字:Java 提供的一种同步机制,用于保证代码块或方法在同一时刻只能被一个线程访问。synchronized 是 Java 中的一个关键字,用于实现同步和互斥。它可以确保在多线程环境下,对共享资源的访问被同步控制。synchronized 可以修饰方法或者代码块。
- 修饰方法:当 synchronized 修饰实例方法时,它会锁住当前实例对象(this),在同一时刻只能有一个线程访问该方法。当 synchronized 修饰静态方法时,它会锁住当前类的 Class 对象,在同一时刻只能有一个线程访问该静态方法。
- 修饰代码块:synchronized 可以修饰一个代码块,这时需要指定一个锁对象,同一时刻只能有一个线程持有这个锁对象才能进入代码块。
- 锁的是同一个对象才能起到互斥作用,如果锁的是不同对象,那么线程之间仍然可以并发执行。
- 当一个线程获得锁时,其他线程需要等待锁释放。这可能导致线程阻塞,进而导致性能降低。
- synchronized 是可重入的,即一个线程在持有锁的情况下可以再次获取同一个锁,不会导致死锁。
- 使用 synchronized:修饰方法、修饰代码块。
- JDK1.6 后 synchronized 底层优化:偏向锁、轻量级锁、锁升级、锁消除等。
- synchronized 和 ReentrantLock 区别(续):synchronized 是关键字,ReentrantLock 是类;synchronized 无法中断等待获取锁的线程,ReentrantLock 可以;synchronized 不支持公平锁,ReentrantLock 支持公平和非公平锁;synchronized 自动释放锁,ReentrantLock 需要手动释放锁;ReentrantLock 提供了更多的灵活性。
- synchronized 和 volatile 区别:synchronized 提供互斥性和可见性,用于同步控制;volatile 仅提供可见性,用于保证变量在多线程间的可见性。
- volatile 关键字:用于修饰变量,保证其在多线程环境下的可见性,禁止指令重排序。
- ReentrantReadWriteLock:一种支持读锁共享、写锁独占的可重入锁。
- 悲观锁与乐观锁:悲观锁假设并发操作会导致冲突,通过锁机制保证同步;乐观锁假设并发操作不会导致冲突,通过版本号或 CAS 原理实现同步。
- 乐观锁实现方式:版本号(如数据库中的 version 字段);CAS(比较并交换)。
- 乐观锁缺点:在高并发场景下,CAS 操作可能频繁失败,导致性能下降;可能出现 ABA 问题。
- CAS 和 synchronized 使用场景:CAS 适用于竞争不激烈的场景;synchronized 适用于竞争激烈的场景。
- Java 原子类:基于 CAS 实现的线程安全操作类,如 AtomicInteger、AtomicLong 等。
- atomic 原理:通过无锁化技术(CAS)实现线程安全的操作。
- AQS(AbstractQueuedSynchronizer):Java 同步组件的基础框架,用于构建锁、同步器等。
- AQS 原理:基于 CLH 队列锁实现,维护一个同步状态和一个等待队列。
- AQS 资源共享模式:独占模式(如 ReentrantLock)、共享模式(如 Semaphore、CountDownLatch)。
- AQS 需要重写的方法:tryAcquire()、tryRelease()、tryAcquireShared()、tryReleaseShared()、isHeldExclusively()。
- Semaphore:信号量,用于限制同时访问特定资源的线程数量。
- CountDownLatch 和 CyclicBarrier 区别:CountDownLatch 用于等待指定数量的事件发生;CyclicBarrier 用于等待指定数量的线程到达屏障点。
- 线程池:用于管理线程的一种线程池(续):用于管理线程的一种机制,可以重用线程、减少创建和销毁线程的开销,提高系统性能。
- 创建线程池的参数:核心线程数、最大线程数、空闲线程存活时间、时间单位、任务队列、线程工厂、拒绝策略。
- 创建线程池方法:使用 Executors 工具类创建,如 newFixedThreadPool、newCachedThreadPool、newSingleThreadExecutor 等;或使用 ThreadPoolExecutor 构造方法自定义创建。
- 线程池线程数设置:需要考虑 CPU 核心数、IO 密集程度、系统负载等因素。
- execute() 和 submit() 区别:execute() 无返回值,submit() 有返回值;execute() 用于执行 Runnable 任务,submit() 可以执行 Runnable 和 Callable 任务。
- Fork/Join 并行计算框架:Java 提供的一种基于分治思想的并行计算框架,适用于大规模数据处理任务。
- JDK 提供的并发容器:CopyOnWriteArrayList、CopyOnWriteArraySet、ConcurrentHashMap、ConcurrentSkipListMap 等。
- CopyOnWriteArrayList:一种线程安全的动态数组,采用写时复制策略,适用于读多写少的场景。
- BlockingQueue:一种线程安全的队列,支持阻塞操作。实现类有 ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue 等。
- ConcurrentSkipListMap:一种线程安全的跳表实现的有序映射,适用于高并发、有序的场景。
使用 synchronized 时需要注意以下几点:
总之,synchronized 是 Java 提供的一种基本的线程同步手段,可以实现多线程环境下的互斥访问,但在高并发场景下可能存在性能问题。为了解决这个问题,Java 还提供了其他同步工具类,如 ReentrantLock、Semaphore 等。
1. 说一下 Jvm 的主要组成部分?及其作用?
2. 谈谈对运行时数据区的理解?
3. 堆和栈的区别是什么?
4. 堆中存什么?栈中存什么?
5. 为什么要把堆和栈区分出来呢?栈中不是也可以存储数据吗?
6. Java 中的参数传递时传值呢?还是传引用?
7. Java 对象的大小是怎么计算的?
8. 对象的访问定位的两种方式?
9. 判断垃圾可以回收的方法有哪些?
10. 垃圾回收是从哪里开始的呢?
11. 被标记为垃圾的对象一定会被回收吗?
12. 谈谈对 Java 中引用的了解?
13. 谈谈对内存泄漏的理解?
14. 内存泄露的根本原因是什么?
15. 举几个可能发生内存泄漏的情况?
16. 尽量避免内存泄漏的方法?
17. 常用的垃圾收集算法有哪些?
18. 为什么要采用分代收集算法?
19. 分代收集下的年轻代和老年代应该采用什么样的垃圾回收算法?
20. 什么是浮动垃圾?
21. 什么是内存碎片?如何解决?
22. 常用的垃圾收集器有哪些?
23. 谈谈你对 CMS 垃圾收集器的理解?
24. 谈谈你对 G1 收集器的理解?
25. 说下你对垃圾回收策略的理解/垃圾回收时机?
26. 谈谈你对内存分配的理解?大对象怎么分配?空间分配担保?
27. 说下你用过的 JVM 监控工具?
28. 如何利用监控工具调优?
29. JVM 的一些参数?
30. 谈谈你对类文件结构的理解?有哪些部分组成?
31. 谈谈你对类加载机制的了解?
32. 类加载各阶段的作用分别是什么?
33. 有哪些类加载器?分别有什么作用?
34. 类与类加载器的关系?
35. 谈谈你对双亲委派模型的理解?工作过程?为什么要使用
36. 怎么实现一个自定义的类加载器?需要注意什么?
37. 怎么打破双亲委派模型?
38. 有哪些实际场景是需要打破双亲委派模型的?
39. 谈谈你对编译期优化和运行期优化的理解?
40. 为何 HotSpot 虚拟机要使用解释器与编译器并存的架构?
41. 说下你对 Java 内存模型的理解?
42. 内存间的交互操作有哪些?需要满足什么规则?
- JVM主要组成部分:类加载器(Class Loader)、运行时数据区(Runtime Data Area)、执行引擎(Execution Engine)、本地接口库(Native Interface Library)和本地方法栈(Native Method Stack)。类加载器负责加载类;运行时数据区存储运行时所需数据;执行引擎负责执行字节码;本地接口库和本地方法栈支持执行本地方法。
- 运行时数据区包括:堆(Heap)、方法区(Method Area)、Java栈(Java Stack)、本地方法栈(Native Method Stack)和程序计数器(Program Counter Register)。这些区域负责存储对象、类元信息、线程执行的栈帧、本地方法调用信息和当前执行指令等。
- 堆和栈的区别:堆是用于存储对象实例的内存区域,栈主要用于存储局部变量和方法调用。堆是线程共享的,而栈是线程私有的。
- 堆中存储对象实例和数组。栈中存储基本类型的局部变量、对象引用和方法调用的栈帧。
- 把堆和栈区分出来的原因:栈主要存储短期生命周期的数据,其存储结构和访问速度优化使得栈具有更快的响应速度;堆用于存储长期生命周期的数据,如对象实例。
- Java中的参数传递是值传递。传递基本类型时传递的是值的拷贝,传递引用类型时传递的是引用的拷贝。
- Java对象大小的计算:对象头(包括对象头、类指针和数组长度等)+ 实例数据(成员变量)+ 对齐填充(确保内存对齐)。
- 对象访问定位的两种方式:句柄访问和直接指针访问。
- 判断垃圾可回收的方法:引用计数法、可达性分析法。
- 垃圾回收从根节点(GC Roots)开始。
- 被标记为垃圾的对象并不一定会立即被回收,具体取决于垃圾回收算法和垃圾收集器的实现。
- Java中的引用:强引用、软引用、弱引用和虚引用。
- 内存泄漏:程序在申请内存后,无法释放已经不再使用的内存空间。
- 内存泄露的根本原因:对象无法被回收,导致无法释放的内存空间持续增长。
- 可能发生内存泄漏的情况:静态集合类、监听器
- 可能发生内存泄漏的情况(续):注册未注销、内部类持有外部类引用、线程池、缓存等。
- 尽量避免内存泄漏的方法:合理使用静态集合类、正确管理监听器、避免不必要的内部类引用、使用线程池时确保任务执行完毕后关闭线程池、设置合适的缓存大小和回收策略等。
- 常用的垃圾收集算法:标记-清除(Mark-Sweep)、复制(Copying)、标记-整理(Mark-Compact)、分代收集(Generational Collection)等。
- 采用分代收集算法的原因:根据对象生命周期的不同,将对象分为不同的区域,以提高垃圾回收效率。
- 分代收集下的年轻代和老年代应采用的垃圾回收算法:年轻代常使用复制算法,老年代常使用标记-清除或标记-整理算法。
- 浮动垃圾:垃圾回收过程中产生的新垃圾。
- 内存碎片:内存中的非连续空闲区域。可以通过内存整理、合并空闲区域等方法解决。
- 常用的垃圾收集器:串行收集器(Serial)、并行收集器(Parallel)、并发标记-清除(CMS)收集器、G1 收集器等。
- CMS垃圾收集器:一种以最小停顿时间为目标的收集器,采用并发标记-清除算法,适用于响应时间要求高的场景。
- G1收集器:一种面向服务器应用的垃圾收集器,采用分区整理算法,能预测垃圾回收停顿时间,适用于大内存和多处理器环境。
- 垃圾回收策略/时机:当堆内存快要耗尽时,垃圾收集器会启动回收过程,回收不再使用的对象,释放内存空间。
- 内存分配:对象分配到年轻代的eden区,晋升到老年代发生在对象在年轻代经历一定次数垃圾回收后仍存活。大对象直接分配到老年代。空间分配担保:确保对象晋升到老年代时有足够的空间。
- JVM监控工具:jstat、jmap、jconsole、VisualVM、MAT、JProfiler等。
- 利用监控工具调优:分析监控数据,调整堆大小、分代比例、利用监控工具调优(续):调整垃圾收集器参数、设置合适的堆内存布局,以优化内存使用和垃圾回收效率。
- JVM参数:-Xmx(设置最大堆内存)、-Xms(设置初始堆内存)、-Xmn(设置年轻代大小)、-XX:SurvivorRatio(设置eden区与survivor区比例)、-XX:MaxTenuringThreshold(设置对象晋升老年代的年龄阈值)等。
- 类文件结构:魔数、版本号、常量池、类访问标志、类名、父类名、接口表、字段表、方法表、属性表等。
- 类加载机制:通过类加载器将.class文件加载到内存中,分为加载、连接(验证、准备、解析)和初始化三个阶段。
- 类加载各阶段作用:加载阶段加载类文件;连接阶段验证类文件结构,为静态变量分配内存并赋初始值,解析类的符号引用;初始化阶段执行类的静态代码块和静态变量初始化。
- 类加载器:启动类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)、应用类加载器(Application ClassLoader)。启动类加载器负责加载JVM基础类;扩展类加载器负责加载扩展库;应用类加载器负责加载应用程序类。
- 类与类加载器关系:类的唯一性由类名和加载它的类加载器共同确定。
- 双亲委派模型:一种类加载器工作机制,当一个类加载器收到类加载请求时,先将请求委托给父类加载器,直至启动类加载器;若父类加载器无法加载,则当前类加载器尝试加载。这样确保类的唯一性和安全性。
- 实现自定义类加载器:继承java.lang.ClassLoader类,重写findClass()方法,加载指定路径下的类文件。注意遵循双亲委派模型。
- 打破双亲委派模型:重写自定义类加载器的loadClass()方法,改变类加载请求的委派方式。
- 需要打破双亲委派模型的场景:如OSGi、热部署、沙箱隔离等。
- 编译期优化和运行期优化:编译期优化指在编译时进行的优化,如内联、常量折叠等;运行期优化指在运行时进行的优化,如JIT编译、逃逸分析等。
- HotSpot虚拟机使用解释器与编译器并存的架构:解释器负责快速执行字HotSpot虚拟机使用解释器与编译器并存的架构(续):解释器负责快速执行字节码,适用于代码初次运行;编译器负责将热点代码编译为本地机器码,提高运行效率。这种并存架构兼顾了启动速度和运行性能。
- Java内存模型:一个用于描述Java程序中共享变量访问规则和线程间通信的抽象模型,解决了由于多线程和多处理器带来的内存可见性和有序性问题。
- 内存间的交互操作:读(read)、写(write)、锁(lock)、解锁(unlock)、volatile读(volatile read)、volatile写(volatile write)、开始(start)和结束(join)。这些操作需要满足以下规则:
- 原子性:读-写、写-读、读-锁、锁-读、写-解锁、解锁-写、锁-解锁操作之间不能重排序。
- 有序性:volatile变量的读-写操作具有全局的顺序性。
- 可见性:当一个线程修改了共享变量,另一个线程能够立即看到修改后的值。
- 禁止重排序:在不影响单线程语义的前提下,允许编译器和处理器对指令进行重排序,以提高执行效率。但是需要遵守原子性、有序性和可见性规则。
1. 使用 Spring 框架的好处是什么?
2. 解释下什么是 AOP?
3. AOP 的代理有哪几种方式?
4. 怎么实现 JDK 动态代理?
5. AOP 的基本概念:切面、连接点、切入点等?
6. 通知类型(Advice)型(Advice)有哪些?
7. 谈谈你对 IOC 的理解?
8. Bean 的生命周期?
9. Bean 的作用域?
10. Spring 中的单例 Bean 的线程安全问题了解吗?
11. 谈谈你对 Spring 中的事物的理解?
12. Spring 中的事务隔离级别?
13. Spring 中的事物传播行为?
14. Spring 常用的注入方式有哪些?
15. Spring 框架中用到了哪些设计模式?
16. ApplicationContext 通常的实现有哪些?
17. 谈谈你对 MVC 模式的理解?
18. SpringMVC 的工作原理/执行流程?
19. SpringMVC 的核心组件有哪些?
20. SpringMVC 常用的注解有哪些?
21. @RequestMapping 的作用是什么?
22. 如何解决 POST 请求中文乱码问题,GET 的又如何处理呢?
23. SpringMVC 的控制器是不是单例模式,如果是会有什么问题,怎么解决?
24. SpringMVC 怎么样设定重定向和转发的?
25. SpringMVC 里面拦截器是怎么写的?
26. SpringMVC 和 Struts2 的区别有哪些?
27. 谈谈你对 MyBatis 的理解?
28. MyBaits 的优缺点有哪些?
29. MyBatis 与 Hibernate 有哪些不同?
30. MyBatis 中 #{} 和 ${}的区别是什么?
31. MyBatis 是如何进行分页的?分页插件的原理是什么?
32. MyBatis 有几种分页方式?
33. MyBatis 逻辑分页和物理分页的区别是什么?
34. MyBatis 是否支持延迟加载?如果支持,它的实现原理是什么?
35. 说一下 MyBatis 的一级缓存和二级缓存?
36. Mybatis 有哪些执行器(Executor)?
37. MyBatis 动态 SQL 是做什么的?都有哪些动态 SQL?能简述一下动态 SQL的执行原理不?
- 使用Spring框架的好处:依赖注入:简化对象之间的依赖管理和解耦面向切面编程:实现关注点分离,提高代码复用性和可维护性声明式事务管理:简化事务操作,提高开发效率集成其他框架:与众多流行框架的集成和支持灵活的配置:支持XML和注解方式的配置
- AOP(Aspect Oriented Programming)是一种编程范式,用于解决横切关注点的问题。它将这些横切关注点从业务逻辑中分离,提高代码的复用性和可维护性。
- AOP代理有两种方式:JDK动态代理和CGLIB代理。JDK动态代理基于接口实现,CGLIB代理基于类继承实现。
- 实现JDK动态代理:定义一个接口和目标对象创建InvocationHandler实现类,实现invoke()方法,处理代理逻辑通过Proxy.newProxyInstance()方法创建代理对象
- AOP基本概念:切面(Aspect):封装横切关注点的模块连接点(Joinpoint):程序执行过程中的某个特定点,如方法调用、异常抛出等切入点(Pointcut):用于匹配连接点的规则,确定在哪些连接点应用切面
- 通知类型(Advice):前置通知(Before):在连接点之前执行后置通知(After):在连接点之后执行返回通知(After-returning):在连接点返回后执行异常通知(After-throwing):在连接点抛出异常后执行环绕通知(Around):在连接点前后执行,可控制执行流程
- IOC(Inversion of Control):控制反转,是一种设计原则。将对象的创建、配置和管理交给容器,降低对象之间的耦合度。
- Bean的生命周期:实例化->属性赋值->调用初始化方法->Bean可用->调用销毁方法
- Bean的作用域:singleton(单例)、prototype(原型)、request(请求)、session(会话)和application(全局)。
- Spring中的单例Bean是线程不安全的。解决方案:将共享变量设置为局部变量或使用线程局部变量;使用同步机制;将Bean设置为原型作用域。
- Spring中的事务:通过声明式或编程式方式实现对数据库操作的事务控制,保证数据的完整性和一致性。Spring支持多种事务管理器,如JDBC、JPA和Hibernate等。
Spring Boot使用Spring框架的事务管理来实现事务。Spring的事务管理有两种方式:编程式事务管理和声明式事务管理。在Spring Boot中,通常使用声明式事务管理。
声明式事务管理基于AOP,使用@Transactional注解来标注事务的边界。在Spring Boot中,实现事务的步骤如下:
- 引入依赖:在pom.xml文件中添加Spring Boot事务管理的依赖(如:spring-boot-starter-data-jpa)。
- 配置数据源:在application.properties或application.yml文件中配置数据源信息,如数据库URL、用户名、密码等。
- 启用事务管理:在Spring Boot的主配置类(通常是@SpringBootApplication注解的类)上添加@EnableTransactionManagement注解。这个注解告诉Spring Boot启用事务管理功能。
- 使用@Transactional注解:在需要进行事务管理的方法上添加@Transactional注解。这个注解表明该方法需要在事务中执行。如果方法执行过程中发生异常,事务将回滚;如果方法正常执行完成,事务将提交。
例如:
- Spring中的事务隔离级别:读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)和串行化(Serializable)。
- Spring中的事务传播行为:REQUIRED(必须存在事务)、SUPPORTS(支持当前事务,不存在则不创建)、MANDATORY(必须存在事务,不存在则抛出异常)、REQUIRES_NEW(创建新事务,当前事务挂起)、NOT_SUPPORTED(不支持事务,当前事务挂起)、NEVER(不支持事务,存在则抛出异常)、NESTED(嵌套事务,存在则嵌套,不存在则创建新事务)。
- Spring常用的注入方式:构造器注入、setter方法注入、基于注解的注入。
- Spring框架中用到的设计模式:单例模式、工厂模式、模板方法模式、代理模式、策略模式、适配器模式等。
- ApplicationContext通常的实现:ClassPathXmlApplicationContext、FileSystemXmlApplicationContext、AnnotationConfigApplicationContext等。
- MVC(Model-View-Controller)模式:一种设计模式,将业务逻辑、数据展示和用户交互分离,提高代码的可维护性和扩展性。
- SpringMVC的工作原理/执行流程:
- 用户发送请求至前端控制器(DispatcherServlet)
- 前端控制器查询处理器映射器(HandlerMapping),获取处理器(Controller)
- 前端控制器调用处理器适配器(HandlerAdapter)执行处理器
- 处理器返回ModelAndView对象给前端控制器
- 前端控制器请求视图解析器(ViewResolver)解析视图
- 视图解析器返回视图对象给前端控制器
- 前端控制器进行视图渲染
- 前端控制器将响应结果返回给用户
- SpringMVC核心组件:前端控制器(DispatcherServlet)、处理器映射器(HandlerMapping)、处理器适配器(HandlerAdapter)、视图解析器(ViewResolver)等。
- SpringMVC常用注解:@Controller、@RequestMapping、@RequestParam、@PathVariable、@ModelAttribute、@RequestBody、@ResponseBody、@Autowired等。
- @RequestMapping:用于映射请求路径和处理器方法之间的关系。
- 解决POST请求中文乱码问题:使用filter过滤器,设置request和response的编码为UTF-8;GET请求可以通过URL编码和解码解决。
- SpringMVC控制器默认为单例模式。问题:线程安全问题。解决方案:避免使用共享变量,将共享变量设置为局部变量或使用线程SpringMVC控制器默认为单例模式(续):局部变量或使用线程局部变量;或者使用同步机制。
- SpringMVC设定重定向和转发:
- 重定向:返回"redirect:/targetURL",浏览器会收到一个新的URL,发起新的请求。
- 转发:返回"forward:/targetURL",请求在服务器内部进行跳转,浏览器地址栏不会改变。
- SpringMVC中拦截器的编写:创建一个实现HandlerInterceptor接口的类,重写preHandle、postHandle和afterCompletion方法,然后在SpringMVC配置文件中注册拦截器。
- SpringMVC与Struts2区别:
- SpringMVC是基于Servlet的,Struts2是基于Filter的。
- SpringMVC使用DispatcherServlet作为前端控制器,Struts2使用FilterDispatcher。
- SpringMVC基于方法的映射,而Struts2基于类的映射。
- SpringMVC与Spring框架集成更好,Struts2主要集成了Hibernate和其他框架。
- MyBatis:一个优秀的持久层框架,它将SQL语句与Java对象映射,简化了数据库操作。
- MyBatis优缺点:优点:
- 灵活:支持定制化SQL、存储过程和高级映射。
- 简单易用:XML配置简单,使用注解减少配置。
- 与Spring框架集成良好。 缺点:
- 需要手写SQL,工作量较大。
- 查询结果集较大时,性能可能不如Hibernate。
- MyBatis与Hibernate区别:
- MyBatis是半自动化ORM框架,需要手写SQL;Hibernate是全自动化ORM框架,自动生成SQL。
- MyBatis更灵活,可以实现复杂SQL;Hibernate实现复杂SQL较困难。
- Hibernate具有缓存机制,MyBatis需要手动实现。
- MyBatis更轻量级,学习成本较低;Hibernate功能丰富,但学习成本较高。
- MyBatis中#{}和${}区别:
- #{}:预编译参数占位符,使用PreparedStatement传递参数,有SQL注入防护。
- ${}:直接替换占位符,容易导致SQL注入。
- MyBatis分页:使用limit子句进行物理分页;分页插件原理:基于拦截器,在执行SQL之前修改SQL语句,添加limit子句。
- MyBatis有两种分页方式:物理分页和逻辑分页。物理分页通过SQL语句查询分页数据,性能较好;逻辑分页查询所有数据后MyBatis有两种分页方式(续):逻辑分页查询所有数据后,在内存中进行分页,性能较差。
- MyBatis逻辑分页与物理分页区别:
- 逻辑分页:查询所有数据,然后在内存中进行分页,适用于数据量较小的情况。
- 物理分页:通过修改SQL语句,直接在数据库层面进行分页,性能较好,适用于数据量较大的情况。
- MyBatis支持延迟加载。实现原理:使用CGLIB生成目标对象的代理对象,在访问目标对象的关联属性时,触发代理对象的懒加载逻辑,执行相应的SQL语句加载关联数据。
- MyBatis一级缓存与二级缓存:
- 一级缓存:SqlSession级别的缓存,同一个SqlSession执行相同的查询会使用缓存数据。
- 二级缓存:Mapper级别的缓存,跨SqlSession共享,需要在Mapper配置文件中开启。
- MyBatis执行器(Executor):SimpleExecutor(普通执行器)、ReuseExecutor(重用执行器,重用预编译语句)、BatchExecutor(批量执行器,批量操作)。
- MyBatis动态SQL:根据条件拼接SQL语句,提高SQL的可复用性。动态SQL元素有:if、choose、when、otherwise、trim、where、set、foreach等。执行原理:根据动态SQL元素解析XML配置文件,生成相应的SQL语句。