从当我得知这个类java.lang.String
被声明为final在Java中,我想知道这是为什么。 我没有找到任何答案,但今天,这个帖子: 如何创建String类的Java中的一个副本? 让我想起了我的查询。
当然,字符串提供了所有我所需要的功能,我从来没有想过这将需要String类的扩展任何操作,但还是你永远不会知道有人会需要什么!
因此,没有人知道什么是设计师的意图是,当他们决定让它决赛?
从当我得知这个类java.lang.String
被声明为final在Java中,我想知道这是为什么。 我没有找到任何答案,但今天,这个帖子: 如何创建String类的Java中的一个副本? 让我想起了我的查询。
当然,字符串提供了所有我所需要的功能,我从来没有想过这将需要String类的扩展任何操作,但还是你永远不会知道有人会需要什么!
因此,没有人知道什么是设计师的意图是,当他们决定让它决赛?
这是具有不可变对象实现字符串是非常有用的。 你应该阅读有关不变性更了解它。
不可变对象的一个优点是,
您可以通过向他们指出单个实例共享副本。
(从这里 )。
如果字符串不是最终的,你可以创建一个子类,并有两个字符串,看起来当“为弦乐看过”一样,但实际上是不同的。
这是一个很好的文章 ,概述了上述答案已经提到的两个原因:
这可能是该文章中的最详细的评论。 它与Java和安全问题的字符串池做。 它如何决定什么进入字符串池。 假设两个字符串是相等的,如果他们的字符序列是相同的,那么,我们对谁抢先一步的竞争条件,并与它一起安全问题。 如果没有,那么该字符串池将包含冗余串从而失去在第一时间拥有它的优势。 刚读了就知道,好不好?
扩展字符串会严重破坏平等和实习生。 JavaDoc的说等于:
比较此字符串与指定的对象。 当且仅当该参数不是null,并且是表示字符的相同序列与此对象字符串对象的结果为真。
假设java.lang.String
不是最后,一个SafeString
可以等于一个String
,反之亦然; 因为他们会代表相同的字符序列。
如果应用会发生什么intern
到SafeString
将在- SafeString
进入JVM的字符串池? 的ClassLoader
和所有对象的SafeString
举行引用然后将被锁定到位的JVM的生命周期。 你会得到关于谁可能是第一个实习生的字符序列中的竞争条件-也许你SafeString
会赢,也许一个String
,或者一个SafeString
由不同的类加载器(这样不同的类)加载。
如果你赢得了比赛入池,这将是一个真正的单身人士和人们可以通过反射和访问您的整个环境(沙盒) secretKey.intern().getClass().getClassLoader()
或者,JVM可以通过确保只有具体的String对象(无子类)加入到池堵塞这个漏洞。
如果等号开始实施,使得SafeString
!= String
,然后SafeString.intern
!= String.intern
和SafeString
将不得不添加到池中。 然后,池会成为池<Class, String>
代替<String>
和所有你需要进入游泳池将是一个新的类加载器。
该字符串是不可变的或最终的绝对最重要的原因是,它是由类加载机制使用,因此具有深刻的和基本的安全方面。
假如字符串被可变与否最终,加载“的java.io.Writer”的要求可能有所改变加载“mil.vogoon.DiskErasingWriter”
参考: 为什么字符串是Java不变
String
是在Java中一个非常核心类,很多事情依靠它的工作一定的方式,例如是不可改变的。
使该类final
阻止了可能会破坏这些假设的子类。
需要注意的是,即使是现在,如果你使用反射, 你可以打破字符串 (改变其值或散列码)。 反射可以用安全管理器停止。 如果String
不是final
,每个人都可以做到这一点。
未申报的其他类final
允许你定义有点破的子类(你可以有一个List
,增加了错误的位置,例如),但至少在JVM不依赖于那些核心业务。
布鲁诺说,这是关于不变性。 这不仅是字符串,但是还有任何包装如双,整数,字符等,有很多方面的原因:
基本上,它使你作为一个程序员,可以肯定的是你的字符串将永远不会改变。 它为好,如果你知道它是如何工作的,可以提高记忆力managemnt。 尝试创建接连两个相同的字符串之一,例如“你好”。 你会发现,如果你调试,他们有相同的ID,这意味着它们是完全一样的对象。 这是由于这样的事实,Java的让你这样做的。 这不会是posssible如果字符串是muttable。 他们可以有我倒是一样的,等等,因为他们永远不会改变。 所以,如果你决定创建百万字符串“hello”你真的做的是什么创造百万指向“你好”。 还有阿灵弦上的任何功能,或出于这个原因任何包装,会导致创建另一个对象(再看看对象ID - 它会改变)。
的方法,另外最终在Java中并不一定意味着该对象不能改变(它是例如C ++不同)。 这意味着,地址,它指向不能改变,但你仍然可以改变它的性质和/或属性。 因此,了解一些情况下不变性和最终的区别可能是非常重要的。
HTH
参考文献:
它可能已被简化实施。 如果你设计一个类,这将是由类的用户继承,那么你有一个全新的用例要考虑到设计中。 如果他们这样做,或者与X proptected领域会发生什么? 使得它最终他们能够专注于获取公共接口正常工作,并确保它是固体。
随着大量的好点已经mentined我想补充的,为什么字符串的原因,另外一个酮是不可改变的在Java中是允许的字符串缓存的哈希码 ,是在Java中不变的字符串缓存的哈希码, 不计算每时间我们称之为字符串的hashCode方法 ,这使得它非常快,HashMap的关键,在Java中的HashMap中。
总之,因为字符串是不可改变的,任何人都无法改变其内容一旦创建了保证字符串的哈希码是在多个调用相同。
如果你看到String
类已被声明为
/** Cache the hash code for the string */
private int hash; // Default to 0
及hashcode()
函数是如下-
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
如果它已经是计算机只返回值。
除了在其他的答案提出的显而易见的原因,使得String类的最终的一个想法也可能与虚拟方法的性能开销。 记住String是一个沉重的类,使得这最后的,意味着没有子实施肯定,意味着没有间接调用开销过。 当然,现在我们有一样的东西虚拟调用等,它总是做这几样优化的为您服务。
除了在其他的答案(安全性,不变性,性能)提到的原因,应当注意的是, String
有特殊的语言支持。 你可以写String
字面量并有用于支撑+
运营商。 允许程序员继承String
,将鼓励黑客如:
class MyComplex extends String { ... }
MyComplex a = new MyComplex("5+3i");
MyComplex b = new MyComplex("7+4i");
MyComplex c = new MyComplex(a + b); // would work since a and b are strings,
// and a string + a string is a string.
嗯,我有一些不同的想法我不知道我是否正确与否,但在Java中的字符串是可以被视为一个原始数据类型,以及我的意思是,我们可以创建一个String对象的字符串名称=“java的唯一对象“。 现在,像其他基本数据类型,其是通过复制值 不是引用字符串复制 ,预计将有同样的行为,因此这就是为什么字符串是最终决定。 那是什么我想它。 如果完全不合逻辑请忽略。
字符串的终结也保护他们作为一个标准。 在C ++中,你可以创建字符串的子类,所以每一个程序商店可以有它自己的字符串的版本。 这将导致缺乏一个强有力的标准。
为了确保我们没有得到更好的实现。 它当然应该一直接口。
[编辑]嗯,越来越无能下来选票。 答案是非常严重的。 我有我的身边愚蠢的字符串实现数倍的方式进行编程,从而导致严重的性能和生产力损失
比方说,你有一个Employee
,有一个方法类greet
。 当greet
方法被调用它简单地打印Hello everyone!
。 所以这是预期的行为 greet
方法
public class Employee {
void greet() {
System.out.println("Hello everyone!");
}
}
现在,让我们GrumpyEmployee
子类Employee
和倍率greet
方法如下所示。
public class GrumpyEmployee extends Employee {
@Override
void greet() {
System.out.println("Get lost!");
}
}
现在,在下面的代码看看在sayHello
方法。 它需要Employee
实例作为参数,并调用迎接方法希望它会说Hello everyone!
但是,我们得到了什么是Get lost!
。 这种行为的改变是因为Employee grumpyEmployee = new GrumpyEmployee();
public class TestFinal {
static Employee grumpyEmployee = new GrumpyEmployee();
public static void main(String[] args) {
TestFinal testFinal = new TestFinal();
testFinal.sayHello(grumpyEmployee);
}
private void sayHello(Employee employee) {
employee.greet(); //Here you would expect a warm greeting, but what you get is "Get lost!"
}
}
如果能够避免这种情况Employee
类作出final
。 现在就看你的想象力混乱的厚脸皮的程序员可能会导致如果量String
类是没有宣布final
。
JVM是否知道什么是一成不变的? 答案是否定的,常量池包含了所有不变的领域,但所有固定字段/对象并不只存储在常量池。 只有我们的,因为它实现不可变性和其功能的方式实现它。 CustomString可以不使用MarkerInterface这将为其提供汇集java的特殊行为使其最终实现,该功能目前仍在等待!
大部分的答案都与永恒 - 为什么字符串类型的对象不能就地更新。 有很多很好的讨论在这里,和Java社区将做好采取不变性作为主要。 (不是我屏住呼吸。)
然而,OP的问题是关于为什么它的最后 - 为何不能延长。 这里的一些没有利用这个,但我会与OP认为这里有一个真正的差距同意。 其他语言允许开发者为一个类型创建新的名义类型。 例如在Haskell,我可以创建以下新类型,可在运行时文字相同,但在编译时提供结合安全。
newtype AccountCode = AccountCode Text
newtype FundCode = FundCode Text
所以,我把下面的建议前,以增强Java语言:
newtype AccountCode of String;
newtype FundCode of String;
AccountCode acctCode = "099876";
FundCode fundCode = "099876";
acctCode.equals(fundCode); // evaluates to false;
acctCode.toString().equals(fundCode.toString()); // evaluates to true;
acctCode=fundCode; // compile error
getAccount(fundCode); // compile error
(也许我们可以开始吐温自己爪哇)
如果创建一个字符串,一旦它会认为,这是一个对象,如果要修改,这是不可能的,它会创建新的对象。