Java字符串:“String s = new String(”傻“);”
我是一个学习Java的C ++人。 我正在阅读有效的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中没有“复制构造函数”的概念?
String
是一种特殊的语言内置类。 它只适用于你应该避免说的String
类
String s = new String("Polish");
因为文字"Polish"
已经是String
类型了,所以你正在创建一个额外的不必要的对象。 对于其他班级,说
CaseInsensitiveString cis = new CaseInsensitiveString("Polish");
是正确的(只有在这种情况下)的事情。
我相信使用文字形式(即“foo”而不是新字符串(“foo”))的主要好处是所有字符串文字都由虚拟机“截取”。 换句话说,它被添加到一个池中,任何其他创建相同字符串的代码将使用池字符串,而不是创建一个新的实例。
为了说明,下面的代码将在第一行显示为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(因为字符串会把这个行为加到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构造函数创建字符串对象,即由于通过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
在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
是对象引用 - 当显式声明字符串时,会创建新实例并且其引用不会相同。
我认为你在尝试使用不区分大小写的课程的方法是错误的。 把琴弦放在一边。 你真正关心的是如何显示或比较值。 使用另一个类来格式化字符串或进行比较。
即
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");
你创建两个字符串对象:一个在池中,另一个在堆中。 该引用将引用堆中的对象。
在你的第一个例子中,你正在创建一个“愚蠢的”字符串,然后把它作为一个参数传递给另一个字符串的拷贝构造函数,这就产生了与第一个字符串相同的第二个字符串。 由于Java字符串是不可变的(经常使用习惯于C字符串的东西),这是不必要的资源浪费。 你应该使用第二个例子,因为它跳过了几个不必要的步骤。
但是,字符串字面不是一个CaseInsensitiveString,所以你不能做你想要的最后一个例子。 而且,没有办法像在C ++中那样重载一个转换运算符,所以实际上没有办法做到你想要的。 您必须将其作为参数传递给您的类的构造函数。 当然,我可能只是使用String.toLowerCase()并完成它。
另外,您的CaseInsensitiveString应该实现CharSequence接口以及可能的Serializable和Comparable接口。 当然,如果你实现了Comparable,你应该重写equals()和hashCode()。
– 我如何使CaseInsensitiveString行为像字符串,所以上述语句是好的(与w / out扩展字符串)? 什么是关于字符串,使它可以通过它像这样的文字确定? 根据我的理解,Java中没有“拷贝构造函数”的概念吗?
第一点已经说够了。 “Polish”是一个字符串文字,不能分配给CaseInsentiviveString类。
现在讲第二点
虽然你不能创建新的文字,但是你可以按照这本书的第一个项目来“类似”的方法,所以下面的说法是正确的:
// Lets test the insensitiveness CaseInsensitiveString cis5 = CaseInsensitiveString.valueOf("sOmEtHiNg"); CaseInsensitiveString cis6 = CaseInsensitiveString.valueOf("SoMeThInG"); assert cis5 == cis6; assert cis5.equals(cis6);
这是代码。
C:\oreyes\samples\java\insensitive>type CaseInsensitiveString.java import java.util.Map; import java.util.HashMap; public final class CaseInsensitiveString { private static final Map<String,CaseInsensitiveString> innerPool = new HashMap<String,CaseInsensitiveString>(); private final String s; // Effective Java Item 1: Consider providing static factory methods instead of constructors public static CaseInsensitiveString valueOf( String s ) { if ( s == null ) { return null; } String value = s.toLowerCase(); if ( !CaseInsensitiveString.innerPool.containsKey( value ) ) { CaseInsensitiveString.innerPool.put( value , new CaseInsensitiveString( value ) ); } return CaseInsensitiveString.innerPool.get( value ); } // Class constructor: This creates a new instance each time it is invoked. public CaseInsensitiveString(String s){ if (s == null) { throw new NullPointerException(); } this.s = s.toLowerCase(); } public boolean equals( Object other ) { if ( other instanceof CaseInsensitiveString ) { CaseInsensitiveString otherInstance = ( CaseInsensitiveString ) other; return this.s.equals( otherInstance.s ); } return false; } public int hashCode(){ return this.s.hashCode(); }
//使用“assert”关键字测试类
public static void main( String [] args ) { // Creating two different objects as in new String("Polish") == new String("Polish") is false CaseInsensitiveString cis1 = new CaseInsensitiveString("Polish"); CaseInsensitiveString cis2 = new CaseInsensitiveString("Polish"); // references cis1 and cis2 points to differents objects. // so the following is true assert cis1 != cis2; // Yes they're different assert cis1.equals(cis2); // Yes they're equals thanks to the equals method // Now let's try the valueOf idiom CaseInsensitiveString cis3 = CaseInsensitiveString.valueOf("Polish"); CaseInsensitiveString cis4 = CaseInsensitiveString.valueOf("Polish"); // References cis3 and cis4 points to same object. // so the following is true assert cis3 == cis4; // Yes they point to the same object assert cis3.equals(cis4); // and still equals. // Lets test the insensitiveness CaseInsensitiveString cis5 = CaseInsensitiveString.valueOf("sOmEtHiNg"); CaseInsensitiveString cis6 = CaseInsensitiveString.valueOf("SoMeThInG"); assert cis5 == cis6; assert cis5.equals(cis6); // Futhermore CaseInsensitiveString cis7 = CaseInsensitiveString.valueOf("SomethinG"); CaseInsensitiveString cis8 = CaseInsensitiveString.valueOf("someThing"); assert cis8 == cis5 && cis7 == cis6; assert cis7.equals(cis5) && cis6.equals(cis8); } } C:\oreyes\samples\java\insensitive>javac CaseInsensitiveString.java C:\oreyes\samples\java\insensitive>java -ea CaseInsensitiveString C:\oreyes\samples\java\insensitive>
也就是说,创建一个CaseInsensitiveString对象的内部池,并从那里返回相应的实例。
这样“==”运算符为表示相同值的两个对象引用返回true 。
当相似的对象被频繁地使用并且创造成本昂贵时这是有用的。
字符串类文档指出该类使用内部池
这个类没有完成,当我们试图在实现CharSequence接口的时候走过对象的内容时,会出现一些有趣的问题,但是这个代码足以证明Book中的这个项目可以被应用。
重要的是要注意,通过使用internalPool对象,引用不会被释放,因此不会被垃圾收集,如果创建了很多对象,这可能会成为一个问题。
它适用于String类,因为它被集中使用,并且池仅由“interned”对象组成。
它也适用于布尔类,因为只有两个可能的值。
最后,这也是为什么类Integer中的valueOf(int)被限制为-128到127个int值的原因。
仅仅因为你的类中有String
这个词,并不意味着你得到了内建String
类的所有特殊功能。
CaseInsensitiveString
不是一个String
虽然它包含一个String
。 例如“example”的String
文字只能分配给一个String
。
CaseInsensitiveString和String是不同的对象。 你不能这样做:
CaseInsensitiveString cis = "Polish";
因为“波兰语”是一个字符串,而不是一个CaseInsensitiveString。 如果字符串扩展CaseInsensitiveString字符串,那么你会没事,但显然不是。
不要担心在这里建设,你不会做不必要的东西。 如果您查看构造函数的代码,它所做的只是存储对您传递的字符串的引用。不会创建任何额外的东西。
在String s = new String(“foobar”)的情况下,它正在做一些不同的事情。 您首先创建字符串“foobar”,然后通过构建一个新的字符串创建它的副本。 没有必要创建该副本。
当他们说写
String s = "Silly";
代替
String s = new String("Silly");
当创建一个String对象时,它们是指它,因为上面的两个语句创建一个String对象,但是新的String()版本创建了两个String对象:一个在堆中,另一个在字符串常量池中。 因此使用更多的内存。
但是,当你写
CaseInsensitiveString cis = new CaseInsensitiveString("Polish");
你不是创建一个字符串,而是创建一个类CaseInsensitiveString的对象。 因此你需要使用新的操作符。
如果我理解正确,你的问题意味着为什么我们不能通过直接分配一个值来创建一个对象,不要把它限制在java中的一个String类的Wrapper中。
为了回答这个问题,纯粹面向对象的编程语言有一些构造,它说,所有文字单独编写时都可以直接转换成给定类型的对象。
这正好意味着,如果解释器看到3,它将被转换成Integer对象,因为integer是为这些文字定义的类型。
如果解释器看到单引号中的任何内容(如“a”),它将直接创建一个字符类型的对象,您不需要指定它,因为该语言定义了类型字符的默认对象。
同样,如果解释器在“”中看到某些东西,它将被视为默认类型的对象,即字符串。 这是一些在后台工作的本地代码。
感谢麻省理工学院视频讲座课程6.00,我得到了这个答案的提示。
在Java中,语法“text”创建一个java.lang.String类的实例。 分配:
String foo = "text";
是一个简单的任务,没有必要的复制构造函数。
MyString bar = "text";
不管你做什么都是非法的,因为MyString类不是java.lang.String,也不是java.lang.String的超类。
首先,你不能创建一个从String继承的类,因为String是最后一个类。 和Java管理字符串不同于其他类,所以只能使用字符串,你可以做
String s = "Polish";
但是,你必须调用构造函数。 所以,这个代码很好。
我只是补充说,Java有复制构造函数 …
那么,这是一个普通的构造函数,具有与参数类型相同的对象。
在JDK的大多数版本中,两个版本将是相同的:
String s = new String(“傻”);
String s =“不再愚蠢”;
因为字符串是不可变的,所以编译器维护一个字符串常量列表,如果你试图创建一个新的字符串,首先会检查字符串是否已经被定义。 如果是,则返回对现有的不可变字符串的引用。
澄清 – 当你说“String s =”时,你正在定义一个占用堆栈空间的新变量 – 那么不管你说“不再愚蠢”还是新的String(“愚蠢”)完全相同的事情发生 – 一个新的常量字符串被编译到您的应用程序中,并且参考指向那个。
我没有看到这里的区别。 然而,对于你自己的类,这是不可改变的,这种行为是无关紧要的,你必须调用你的构造函数。
更新:我错了! 基于反对票和附加评论我测试了这一点,并认识到我的理解是错误的 – 新的字符串(“傻”)确实创造了一个新的字符串,而不是重复使用现有的。 我不清楚为什么会这样(有什么好处?),但代码比语言更响亮!
字符串是您可以在没有新的Sring部分的情况下创建它们的特殊类之一
这是一样的
int x = y;
要么
char c;
这是一个基本的规律,在Java中的字符串是不可改变的,并区分大小写。
String str1 = "foo"; String str2 = "foo";
str1和str2都属于同一个String对象,“foo”,b'coz Java在StringPool中管理Strings,所以如果一个新变量引用同一个String,就不会创建另外一个,而是分配与StringPool中相同的alerady 。
String str1 = new String("foo"); String str2 = new String("foo");
这里str1和str2都属于不同的对象,b'coz new String()强制创建一个新的String对象。
Java为您在代码中使用的每个字符串字面值创建一个String对象。 任何时候使用""
,它就像调用new String()
。
字符串是复杂的数据,就像原始数据一样“动作”。 字符串文字实际上是对象,即使我们假装它们是原始文字,比如6, 6.0, 'c',
等。所以字符串“literal” "text"
返回一个新的String对象,其值为char[] value = {'t','e','x','t}
。 所以打电话
new String("text");
实际上类似于呼叫
new String(new String(new char[]{'t','e','x','t'}));
希望从这里,你可以看到为什么你的教科书认为这是多余的。
作为参考,这里是字符串的实现: http : //www.docjar.com/html/api/java/lang/String.java.html
这是一个有趣的阅读,可能会启发一些洞察力。 代码演示非常专业且符合常规的代码对于初学者来说也是很好的阅读和尝试理解。
另一个很好的参考是Strings上的Java教程: http : //docs.oracle.com/javase/tutorial/java/data/strings.html