String类是不可变的,其中一个原因是该类被声明为final,尽管还有其他原因。 但是为什么StringBuffer或StringBuilder是final却是可变的呢? 那么有哪些其他因素决定了String是不可变的呢?
String类是不可变的,其中一个原因是该类被声明为final,尽管还有其他原因。 但是为什么StringBuffer或StringBuilder是final却是可变的呢? 那么有哪些其他因素决定了String是不可变的呢?
StringBuffer
和StringBuilder
主要用于单个方法内的字符串连接操作,使用它们的代码通常是由编译器生成的。因此,扩展它们不是典型的用例。
另一方面,使用final
可以在JVM内进行更好的优化,至少在过去是这样;然而,今天的HotSpot JVM并不需要它,但从这些类中更改final
声明的原因从来没有改变过。
请注意,扩展StringBuilder
并覆盖方法以实现多态行为有点无意义,因为整个JRE中没有任何接受或返回StringBuilder
实例的public
方法(除了StringBuilder
本身)。填补这一空白的是Appendable
和CharSequence
,它们提供了更大的灵活性。
可变性和不可变性的概念与final
类的概念完全不同。它们仅取决于类提供的方法或操作的类型。String
没有允许修改其内容的方法,而StringBuffer
和StringBuilder
有这样的方法。声明一个不可变类为final
只是帮助禁止子类引入支持突变的方法,但这不是硬性要求。
StringBuilder
的 append
方法都已经声明为返回 StringBuilder
而不是你的假想子类,因此当使用继承来的 append
方法时,yourBuilder.append(x).appendLn(y)
不起作用,因为在第一个调用(继承的)append
方法之后,编译器会抱怨 StringBuilder
没有你的新方法。除非你覆盖所有这些方法以返回更特定的类型,即你的子类。但是当你必须覆盖所有方法时,扩展并不比委托更容易。 - Holgerfinal int i=5;
i++// Will give an error.
但是:
对于类: final关键字的作用是使类不可继承,即该类无法被子类化! 例如:
final class question
{}
class answer extends question//Will give an error!!!
{}
回答你的问题:
现在,你是对的,字符串是不可变的,因为一旦字符串对象被初始化,就不能被改变,而Stringbuilder和Stringbuffer是可变的(反之亦然),这与这些类是final无关!你没有正确地子类化它们!
我希望我有所帮助!
编辑:
我想我应该完全解释一下Stringbuilder为什么是可变的,而String不是:
我将逐行解释此代码:
Stringbuilder a = new Stringbuilder("a");//Initial String!!
a.append("b"); //Now the String is ab!!
String a = "a";
a = a + "b"; //Explained below.
尽管在您看来,字符串对象a正在改变其值,因此是可变的,但实际上并非如此,因为在幕后,Stringbuilder正在工作。 即编译器在上述代码中实际执行的操作是:
a = new StringBuilder(a.append("b")); //The actual code...
那么决定String不可变的其他因素是什么?
答案很简单,因为String类的所有数据成员都是final的,这使它成为不可变的。
如果您想使自己的类不可变,请将该类的所有数据成员都设置为final。并在构造函数中初始化所有最终数据成员,以便一旦它们被初始化,就永远不会改变。
如果您查看不可变性的String类实现,您会发现:
private final char[] value;
值的初始化将在构造函数部分进行:
public String(String toCopy){
value = toCopy.value;
}
如果你查看 StringBuffer 类的实现,你会发现:
private char[] value;
public StringBuffer(String toCopy){
value = toCopy.value;
}
String
不是不可变的,因为它是final的;final
和不可变性或多或少是无关的。将一个类设为final
是使其不可变的方法之一,但是无论你想要什么样的行为,将一个类设为final
也是良好编程实践的一部分。
一个不可变类不能被外部用户扩展。它可以在自己的包中有几个实现,例如Guava的ImmutableMap
,在这种情况下,该类不需要是final的,但是您也可以通过将构造函数设置为包私有来禁止外部用户扩展该类。
final
类是任何不能被扩展的类。在Effective Java中,这是许多情况下的推荐做法--也就是说,每当您没有显式的理由使类可子类化时,将其设为final通常是一个好的实践。
如果您关注的是扩展StringBuilder或StringBuffer的行为,您仍然可以这样做 - 只是不能通过继承。您可以使用组合或委托。String、StringBuffer和StringBuilder类都实现了CharacterSequence。因此,创建实现CharacterSequence的类,并将所有方法委托给组成的StringBuffer或StringBuilder。在GoF中,组合优于继承是黄金法则 - 请参见讨论。
使一个类变为final是一种更强大的封装层次 - 可能代码的作者认为任何人不应该通过扩展来修改行为,因为他可能会觉得问题类的子类在转换方面并不真正代表它自己。
StringBuffer
是可变字符串的概念,是线程安全的,也就是说,对它执行的所有操作都是同步的。
StringBuilder
推荐用来替代 StringBuffer
。当涉及到字符串时,很少使用同步,因此引入了它。
String
就是字符串。为什么它是不可变的?可能原因是线程安全和成本问题。如果您想存储全局的只读字符串,如果它是可变的,那么所有读取都必须进行同步 - 这是不必要的额外开销。
这就是为什么提供了这三个类 - 您可以选择要支付什么。
正如您所注意到的,所有这些类 - String
、StringBuilder
和 StringBuffer
都是 final 的。原因是您不能(也不应该)修改这些类的行为。
最好实现另一个类,其中包含(例如)StringBuilder
并提供类似的功能,而不是扩展它。
如果您愿意,可以通过实现CharSequence来提供类似的功能 - 所有这些类都实现了该接口。
final
即不可扩展性和不可变性的概念。 - jdphenix