final 修饰 StringBuffer 后还可以 append 吗?
参考回答
可以,final 修饰的 StringBuffer 对象仍然可以调用其方法(如 append)来修改内容。final 的作用是让引用不可变,而不是让对象本身不可变。
也就是说,final 修饰后,引用变量不能被重新赋值,但引用指向的对象内容仍然可以修改。
详细讲解与拓展
1. final 的作用
final 修饰变量的规则是:
- 基本数据类型:变量的值不能被修改。
- 引用数据类型:变量的引用不能被重新赋值,但引用指向的对象内容可以被修改。
示例:
public class TestFinal {
public static void main(String[] args) {
final StringBuffer sb = new StringBuffer("Hello");
sb.append(" World"); // 修改对象内容
System.out.println(sb); // 输出 "Hello World"
// sb = new StringBuffer("New"); // 编译错误,不能重新赋值引用
}
}
解释:
final修饰sb,表示sb引用不能指向新的StringBuffer对象。- 但
sb指向的对象内容仍然可以通过其方法(如append)修改。
2. final 的行为
假设 final 修饰了一个 StringBuffer 对象 sb:
final StringBuffer sb = new StringBuffer("Hello");
- 可以修改对象内容:
sb.append(" World"); // 修改内容,合法append方法会修改sb指向的StringBuffer对象的内部状态。 -
不能改变引用:
sb = new StringBuffer("New String"); // 编译错误这里尝试将
sb指向另一个新的对象,会引发编译错误。
3. 不可变类(如 String)的对比
与 String 的不可变性不同,StringBuffer 是一个可变类,它的内容可以在对象被创建后通过方法(如 append、delete)进行修改。
以下是两者的区别:
String:
- 不可变类,每次修改都会创建新对象。
- 即使是
final修饰的String,内容仍然无法改变。final String str = "Hello"; str.concat(" World"); // str 内容未改变 System.out.println(str); // 输出 "Hello"
StringBuffer:
- 可变类,
final修饰后内容仍然可以修改。final StringBuffer sb = new StringBuffer("Hello"); sb.append(" World"); System.out.println(sb); // 输出 "Hello World"
4. final 与线程安全
final与引用不可变:- 如果一个对象引用是
final,则其他线程无法更改这个引用指向的对象。 - 但如果对象本身是可变的(如
StringBuffer),对象内容仍然可以被多线程修改。
- 如果一个对象引用是
- 线程安全性示例:
public class ThreadTest { public static void main(String[] args) { final StringBuffer sb = new StringBuffer("Hello"); new Thread(() -> sb.append(" World")).start(); new Thread(() -> sb.append(" Java")).start(); System.out.println(sb); // 输出可能为 "Hello World Java" 或其他结果 } }虽然
sb的引用是final,但StringBuffer是线程不安全的,其内容可以被多个线程同时修改,导致不可预测的结果。
解决方案:如果需要线程安全,可以使用 StringBuffer 的同步方法,或者改用线程安全的 StringBuilder。
5. final 的应用场景
- 保护引用不可变: 如果一个引用被
final修饰,则可以确保它始终指向同一个对象。public class Example { private final StringBuffer buffer = new StringBuffer("Immutable Reference"); public StringBuffer getBuffer() { return buffer; } } - 配合不可变类: 在不可变类中,
final常用来确保所有字段的值不可变。
6. 拓展知识
- 不可变类的特性
String是不可变的,而StringBuffer和StringBuilder是可变的。- 如果希望一个
StringBuffer的内容也不可修改,可以通过封装实现。
-
封装不可修改的
StringBufferpublic class ImmutableStringBuffer { private final StringBuffer buffer; public ImmutableStringBuffer(String str) { buffer = new StringBuffer(str); } public String getContent() { return buffer.toString(); } } -
线程安全与
StringBuffer
StringBuffer是线程安全的,因为它的方法使用了同步机制。- 但过多的同步会影响性能,因此在单线程环境下推荐使用
StringBuilder。
7. 总结
final修饰StringBuffer后,可以通过其方法(如append、delete)修改内容,但无法重新赋值引用。StringBuffer是可变类,其内容可以被修改;而String是不可变类,内容不可修改。- 需要注意线程安全问题,
final只保证引用不可变,不能保证对象内容不变。