我是一个学习Java的C++人.我正在阅读Effective Java,有些事让我很困惑.它说从不写这样的代码:
String s = new String("silly");
因为它会创建不必要的String
对象 但相反它应该写成这样:
String s = "No longer silly";
好吧到目前为止......但是,鉴于这个课:
public final class CaseInsensitiveString { private String s; public CaseInsensitiveString(String s) { if (s == null) { throw new NullPointerException(); } this.s = s; } : : } CaseInsensitiveString cis = new CaseInsensitiveString("Polish"); String s = "polish";
为什么第一个陈述好吗?不应该
CaseInsensitiveString cis = "Polish";
我如何使CaseInsensitiveString
行为String
如此上述声明是可以的(有和没有扩展String
)?它是什么让它能够传递像这样的文字?根据我的理解,Java中没有"复制构造函数"概念?
Adam Rosenfi.. 108
String
是一种特殊的语言内置类.它是为String
类只有在你应该避免说
String s = new String("Polish");
因为文字"Polish"
已经是类型String
,并且您正在创建一个额外的不必要的对象.对于任何其他类,说
CaseInsensitiveString cis = new CaseInsensitiveString("Polish");
是正确的(在这种情况下,只有)这样做的事情.
String
是一种特殊的语言内置类.它是为String
类只有在你应该避免说
String s = new String("Polish");
因为文字"Polish"
已经是类型String
,并且您正在创建一个额外的不必要的对象.对于任何其他类,说
CaseInsensitiveString cis = new CaseInsensitiveString("Polish");
是正确的(在这种情况下,只有)这样做的事情.
我相信使用文字形式(即"foo"而不是新的String("foo"))的主要好处是所有字符串文字都被VM"拦截".换句话说,它被添加到池中,使得创建相同字符串的任何其他代码将使用池化的String而不是创建新实例.
为了说明,下面的代码将为第一行打印为true,但对于第二行则为false:
System.out.println("foo" == "foo"); System.out.println(new String("bar") == new String("bar"));
字符串在java中有点特殊处理,它们是不可变的,所以通过引用计数来处理它们是安全的.
如果你写
String s = "Polish"; String t = "Polish";
然后s和t实际上引用相同的对象,并且s == t将返回true,对于"=="对于读取"是同一个对象"的对象(或者,无论如何,我不确定这是否属于实际的语言规范或只是编译器实现的细节 - 所以也许依靠这个并不安全.
如果你写
String s = new String("Polish"); String t = new String("Polish");
那么s!= t(因为你已经明确地创建了一个新的字符串)虽然s.equals(t)将返回true(因为string将此行为添加到equals).
你要写的东西,
CaseInsensitiveString cis = "Polish";
无法工作,因为你认为引用是你的对象的某种短路构造函数,实际上这只适用于普通的旧java.lang.Strings.
String s1="foo";
文字将进入池中,s1将参考.
String s2="foo";
这次它将检查"foo"文字是否已经在StringPool中可用,因为它现在存在,所以s2将引用相同的文字.
String s3=new String("foo");
首先在StringPool中创建"foo"文字,然后通过字符串arg构造函数创建String对象,即由于通过new运算符创建对象而在堆中创建"foo",然后s3将引用它.
String s4=new String("foo");
与s3相同
所以 System.out.println(s1==s2);// **true** due to literal comparison.
和 System.out.println(s3==s4);// **false** due to object
比较(s3和s4在堆中的不同位置创建)
String
s在Java中很特殊 - 它们是不可变的,字符串常量会自动转换为String
对象.
您的SomeStringClass cis = "value"
示例无法应用于任何其他类.
你也不能扩展String
,因为它被声明为final
,意味着不允许进行子类化.
Java字符串很有趣.看起来回答已经涵盖了一些有趣的观点.这是我的两分钱.
字符串是不可变的(你永远不能改变它们)
String x = "x"; x = "Y";
第一行将创建一个变量x,它将包含字符串值"x".JVM将查看其字符串值池并查看是否存在"x",如果存在,则将变量x指向它,如果它不存在,它将创建它然后执行赋值
第二行将删除对"x"的引用,并查看字符串值池中是否存在"Y".如果它确实存在,它将分配它,如果它不存在,它将首先创建它然后分配.由于使用或不使用字符串值,将回收字符串值池中的内存空间.
字符串比较取决于您所比较的内容
String a1 = new String("A"); String a2 = new String("A");
a1
不等于 a2
a1
并且a2
是对象引用
当显式声明string时,将创建新实例并且它们的引用将不相同.
我认为你试图使用casesensitive类是错误的.不管弦乐.您真正关心的是如何显示或比较值.使用另一个类来格式化字符串或进行比较.
即
TextUtility.compare(string 1, string 2) TextUtility.compareIgnoreCase(string 1, string 2) TextUtility.camelHump(string 1)
由于您正在组成课程,因此您可以根据需要进行比较 - 比较文本值.
你不能.Java中双引号的东西被编译器特别识别为字符串,不幸的是你不能覆盖它(或者扩展java.lang.String
- 声明它final
).
回答问题的最佳方法是让您熟悉"字符串常量池".在java中,字符串对象是不可变的(即它们的值在初始化后无法更改),因此在编辑字符串对象时,最终会创建一个新编辑的字符串对象,而旧对象只是在特殊内存中浮动,称为"字符串"常数池".通过创建一个新的字符串对象
String s = "Hello";
将只在池中创建一个字符串对象,引用s将引用它,但通过使用
String s = new String("Hello");
你创建了两个字符串对象:一个在池中,另一个在堆中.引用将引用堆中的对象.