9. **String和StringBuffer, StringBuilder的区别有哪些?所有类名包含Buffer的类的内部实现原理是什么?有什么优势?**
- **`String`**:`String`是不可变类,每次对字符串进行修改(如拼接、替换等)都会创建一个新的`String`对象。由于其不可变性,`String`对象在多线程环境下是线程安全的,但性能较差,因为每次修改都需要分配新的内存。
- **`StringBuffer`**:`StringBuffer`是可变类,它能够对字符串进行修改而不重新创建对象。它是线程安全的,通过对每个方法的同步来保证线程安全,但同步机制会影响性能。`StringBuffer`通常用于多线程环境下,避免多个线程修改字符串时出现数据不一致。
- **`StringBuilder`**:`StringBuilder`与`StringBuffer`相似,也是可变类,可以在不创建新对象的情况下修改字符串,但它不保证线程安全。`StringBuilder`的性能优于`StringBuffer`,因此它更适合在单线程环境中使用。
**类名包含Buffer的类的内部实现原理**:
- `StringBuffer`和`StringBuilder`都使用一个**字符数组**来存储字符串。当字符串长度发生变化时,内部的字符数组会动态扩展,通常扩展倍数为1.5倍。
- 这两者的区别在于:`StringBuffer`的操作是**同步的**,而`StringBuilder`是**非同步的**。**优势**:
- **性能**:`StringBuffer`和`StringBuilder`在频繁修改字符串的场景下,远比`String`高效,因为它们避免了每次修改都重新创建对象。
- **内存使用**:`StringBuffer`和`StringBuilder`使用一个可变的字符数组,避免了`String`的内存重复分配问题。
- **线程安全**:`StringBuffer`的线程安全特性适用于多线程环境,而`StringBuilder`适合单线程环境,提供了更好的性能。---
10. **String字符串的不可变是指哪里不可变?**
`String`字符串的不可变性指的是**字符串内容不可改变**。一旦创建了一个`String`对象,它的字符数据就无法修改。如果对字符串进行操作(如拼接、替换、删除字符等),会创建一个新的`String`对象,而原始的`String`对象不受影响。
这种不可变性有以下好处:
- **线程安全**:由于`String`对象不可变,它可以安全地在多个线程之间共享,不需要进行同步操作。
- **哈希值缓存**:`String`对象的不可变性使得它可以被用作哈希表(如`HashMap`的键),而不必担心它的值在存储后发生改变。
- **性能优化**:Java会缓存`String`常量,在常量池中存储相同内容的字符串,避免重复创建`String`对象,从而提高性能。---
11. **字符串常量池是什么?不同的JDK版本都分别位于哪个区域?**
**字符串常量池**是一个特殊的内存区域,用于存储**字符串字面量**(如`"hello"`)的唯一副本。通过使用常量池,Java能够避免创建重复的`String`对象,从而节省内存。
**JDK 7及之前**:
- 字符串常量池位于**方法区**。在JVM中,方法区是存放类信息、常量、静态变量等数据的区域,字符串常量池就是其中的一部分。**JDK 8及之后**:
- 在JDK 8中,字符串常量池被迁移到了**堆内存**,与对象的存储区域相同。这样做的目的是改善性能,尤其是在大量字符串字面量的情况下,堆内存更易于扩展。**如何操作字符串常量池**:
- 通过`String.intern()`方法,开发者可以显式地将一个字符串添加到常量池中。例如,`"hello".intern()`可以将字符串`"hello"`放入常量池,如果常量池中已经存在该字符串,它将直接返回常量池中的引用。---
12. **Java异常类有哪些?分别管理什么异常?**
Java的异常机制分为两大类:
1. **`Throwable`**:是所有异常和错误类的父类,分为两种子类:
- **`Error`**:表示系统级别的问题,通常由JVM触发,程序无法处理。例如:
- `OutOfMemoryError`:内存溢出错误
- `StackOverflowError`:栈溢出错误
- `VirtualMachineError`:虚拟机错误
- **`Exception`**:表示程序中出现的异常,可以通过捕获来处理。`Exception`类又分为:
- **`RuntimeException`**:运行时异常,通常由程序的逻辑错误引起,属于**非受检异常**(unchecked exception),不需要显式声明或捕获。常见的有:
- `NullPointerException`:空指针异常
- `IndexOutOfBoundsException`:索引越界异常
- `ArithmeticException`:算术异常(如除以零)
- **`Checked Exception`**:检查异常,程序必须显式捕获或声明抛出。常见的有:
- `IOException`:输入输出异常
- `SQLException`:数据库操作异常
- `ClassNotFoundException`:类找不到异常---
13. **Java反射获取类信息的方式有哪几种?分别是什么?**
Java反射是Java提供的一种机制,允许程序在运行时获取类的结构(如字段、方法等)和实例化对象。获取类信息的方式包括:
1. **`Class.forName(String className)`**:通过类的全限定名(类的路径)动态加载类,返回该类的`Class`对象。这是反射中最常用的方式。
- 示例:`Class clazz = Class.forName("java.lang.String");`
2. **`getClass()`**:通过对象调用`getClass()`方法,返回该对象所属类的`Class`对象。
- 示例:`String str = "Hello"; Class clazz = str.getClass();`
3. **`getSuperclass()`**:获取类的父类。
- 示例:`Class superClass = clazz.getSuperclass();`
4. **`getInterfaces()`**:获取类实现的所有接口。
- 示例:`Class[] interfaces = clazz.getInterfaces();`反射的强大之处在于可以在运行时动态操作类,获取类的信息、方法、字段等,甚至修改类的私有属性。
---
14. **Java代理的主要方法有哪几种?列举代理的使用场景2个。**
Java代理分为两类:
1. **JDK动态代理**:JDK动态代理需要目标类实现一个或多个接口。代理类会在运行时动态生成,并通过反射调用目标方法。使用`Proxy`类来创建代理实例。
- **特点**:需要目标类实现接口,代理类不需要手动编写,生成时由JVM动态生成。2. **CGLIB代理**:CGLIB代理通过字节码生成技术,动态生成目标类的子类来实现代理。与JDK动态代理不同,CGLIB不要求目标类实现接口。
- **特点**:适用于没有实现接口的类,可以代理没有接口的类。**代理的使用场景**:
1. **AOP(面向切面编程)**:通过代理实现方法的增强,如记录日志、事务管理、权限控制等。
- 示例:在Spring框架中,使用AOP代理来实现事务管理和日志记录。
2. **动态代理**:用于在运行时决定代理的行为,常见于ORM框架(如Hibernate)中,用于代理数据访问操作。---
15. **equals()方法的作用是什么?重写equals需要注意哪些事项?为什么?**
`equals()`方法用于比较两个对象的内容是否相等,而不是比较它们的内存地址(即引用是否相同)。默认的`equals()`方法比较的是对象的内存地址(即引用),但对于大多数类,我们需要重写`equals()`方法来比较对象的内容。
**重写`equals()`时需要注意**:
1. **自反性**:`a.equals(a)`应始终返回`true`。
2. **对称性**:`a.equals(b)`和`b.equals(a)`应该返回相同的结果。
3. **传递性**:如果`a.equals(b)`和`b.equals(c)`都为`true`,则`a.equals(c)`也应该返回`true`。
4. **一致性**:如果`a.equals(b)`始终返回相同的结果,除非对象的内容发生变化。
5. **与`null`的比较**:任何对象与`null`的比较应返回`false`。
6. **`hashCode()`**:如果重写`equals()`,必须重写`hashCode()`方法,保证相等的对象具有相同的哈希码。**原因**:为了保证对象比较的逻辑符合直觉,并且与集合框架(如`HashMap`、`HashSet`等)的一致性,正确实现`equals()`和`hashCode()`方法是非常重要的。
---