目录
String、StringBuilder、StringBuffer的区别?
String、StringBuilder、StringBuffer的区别?
1、可变性 :String
是不可变的(Immutable),一旦创建,内容无法修改,每次修改都会生成一个新的对象。StringBuilder
和 StringBuffer
是可变的(Mutable),可以直接对字符串内容进行修改而不会创建新对象。
2、线程安全性 :String
因为不可变,天然线程安全。StringBuilder
不是线程安全的,适用于单线程环境。StringBuffer
是线程安全的,其方法通过 synchronized
关键字实现同步,适用于多线程环境。
3、性能 :String
性能最低,尤其是在频繁修改字符串时会生成大量临时对象,增加内存开销和垃圾回收压力。StringBuilder
性能最高,因为它没有线程安全的开销,适合单线程下的字符串操作。StringBuffer
性能略低于 StringBuilder
,因为它的线程安全机制引入了同步开销。
4、使用场景 :如果字符串内容固定或不常变化,优先使用 String
。如果需要频繁修改字符串且在单线程环境下,使用 StringBuilder
。如果需要频繁修改字符串且在多线程环境下,使用 StringBuffer
。
对比总结如下:
特性 | String | StringBuilder | StringBuffer |
---|---|---|---|
不可变性 | 不可变 | 可变 | 可变 |
线程安全 | 是(因不可变) | 否 | 是(同步方法) |
性能 | 低(频繁修改时) | 高(单线程) | 中(多线程安全) |
适用场景 | 静态字符串 | 单线程动态字符串 | 多线程动态字符串 |
例子代码如下:
// String的不可变性
String str = "abc";
str = str + "def"; // 新建对象,str指向新对象
// StringBuilder(单线程高效)
StringBuilder sb = new StringBuilder();
sb.append("abc").append("def"); // 直接修改内部数组
// StringBuffer(多线程安全)
StringBuffer sbf = new StringBuffer();
sbf.append("abc").append("def"); // 同步方法保证线程安全
抽象类和接口的区别是什么?
抽象类和接口的主要区别如下:
-
定义 :抽象类是一个不能实例化的类,可以包含具体方法和抽象方法;接口是一个完全抽象的结构,主要用于定义行为规范。
-
继承与实现 :抽象类通过
extends
继承,只能单继承;接口通过implements
实现,可以多实现。 -
成员变量 :抽象类可以有普通变量和常量;接口只能有常量(
public static final
)。 -
方法 :抽象类可以有抽象方法和具体方法;接口从 JDK 8 开始支持默认方法和静态方法。
-
构造器 :抽象类可以有构造器,接口不能有构造器。
-
设计目的 :抽象类用于描述“是什么”,强调类的继承关系;接口用于描述“能做什么”,强调行为的规范。
接口的成员变量和方法的访问权限是怎样的?
成员类型 | 默认修饰符 | 访问权限说明 | 其他特性说明 |
---|---|---|---|
成员变量 | public static final |
可以被任何类访问,因为是 public ;通过接口名直接访问,因为是 static |
必须在定义时初始化,且初始化后值不能被修改,是常量 |
抽象方法(Java 8 之前接口中的普通方法) | public abstract |
任何实现该接口的类都要实现这些方法,可被外部类访问 | 只有方法声明,没有方法体,由实现类提供具体实现 |
default 方法(Java 8 及以后) |
public |
实现类可直接使用,也可重写,外部类通过实现类对象调用 | 有方法体,为实现类提供默认行为 |
static 方法(Java 8 及以后) |
public |
通过接口名直接调用 | 有方法体,属于接口本身,与接口的实例无关 |
private 方法(Java 9 及以后) |
private |
只能在接口内部被其他 default 方法或 private static 方法调用 |
用于抽取接口中 default 方法或 static 方法的重复代码 |
private static 方法(Java 9 及以后) |
private static |
只能在接口内部被其他 static 方法或 default 方法调用 |
接口中的成员变量默认是public
、static
和final
的。这意味着:
-
public
:可以被任何类访问。 -
static
:它属于接口本身,而不是接口的实例。 -
final
:变量的值在初始化后不能被修改,即它是一个常量。所以在接口中定义成员变量时,通常会同时进行赋值,例如public static final int MAX_COUNT = 100;
,并且实际编码中public static final
这些修饰符常常省略。
接口中的方法默认是public
和abstract
的。这表示:
-
public
:任何实现该接口的类都必须实现这些方法,并且可以被外部类访问。 -
abstract
:这些方法只有声明,没有方法体,需要由实现接口的类来提供具体的实现逻辑。在 Java 8 及以后版本,接口中还可以有default
方法和static
方法,default
方法有方法体,为接口的实现类提供了一种默认的行为实现;static
方法属于接口自身,可通过接口名直接调用,它们的访问权限同样是public
。
内部类能否访问外部类的私有变量?
部类可以访问外部类的私有变量,这是因为:
-
内部类是外部类的一部分,可以直接访问外部类的所有成员,包括私有成员。
-
对于非静态内部类,它持有对外部类实例的隐式引用(
OuterClass.this
),因此可以访问外部类的非静态私有变量,比如下面的例子,展示内部类如何访问外部类的私有变量:
public class OuterClass {
private String outerField = "I am a private field of OuterClass";
// 非静态内部类
class InnerClass {
public void accessOuterField() {
// 内部类可以直接访问外部类的私有变量
System.out.println("Accessing outerField: " + outerField);
}
}
public static void main(String[] args) {
// 创建外部类实例
OuterClass outer = new OuterClass();
// 创建内部类实例
OuterClass.InnerClass inner = outer.new InnerClass();
// 调用内部类方法,访问外部类的私有变量
inner.accessOuterField();
}
}
//输出结果
Accessing outerField: I am a private field of OuterClass
-
对于静态内部类,它只能访问外部类的静态私有变量,因为它不依赖于外部类的实例。例子如下代码:
public class OuterClass {
private String outerField = "I am a private field of OuterClass";
private static String staticField = "I am a static private field";
// 静态内部类
static class StaticInnerClass {
public void accessStaticField() {
// 可以访问外部类的静态私有变量
System.out.println("Accessing staticField: " + staticField);
// 无法直接访问非静态私有变量
// System.out.println(outerField); // 编译错误
}
}
public static void main(String[] args) {
// 创建静态内部类实例
OuterClass.StaticInnerClass staticInner = new OuterClass.StaticInnerClass();
staticInner.accessStaticField();
}
}
//输出结果
Accessing staticField: I am a static private field
深拷贝和浅拷贝的区别是什么?
null
-
浅拷贝是指只复制对象本身和其内部的值类型字段,但不会复制对象内部的引用类型字段。换句话说,浅拷贝只是创建一个新的对象,然后将原对象的字段值复制到新对象中,但如果原对象内部有引用类型的字段,只是将引用复制到新对象中,两个对象指向的是同一个引用对象。
-
深拷贝是指在复制对象的同时,将对象内部的所有引用类型字段的内容也复制一份,而不是共享引用。换句话说,深拷贝会递归复制对象内部所有引用类型的字段,生成一个全新的对象以及其内部的所有对象。
HashMap如何处理哈希冲突?
在 JDK 1.7 版本之前, HashMap 数据结构是数组和链表,HashMap通过哈希算法将元素的键(Key)映射到数组中的槽位(Bucket)。如果多个键映射到同一个槽位,它们会以链表的形式存储在同一个槽位上,因为链表的查询时间是O(n),所以冲突很严重,一个索引上的链表非常长,效率就很低了。
所以在 JDK 1.8 版本的时候做了优化,当一个链表的长度超过8的时候就转换数据结构,不再使用链表存储,而是使用红黑树,查找时使用红黑树,时间复杂度O(log n),可以提高查询性能,但是在数量较少时,即数量小于6时,会将红黑树转换回链表。