Java字符串:“String s = new String(”傻“);”

我是一个学习Java的C ++人。 我正在阅读Effective Java,有些事让我很困惑。 它说从不写这样的代码:

String s = new String("silly");

因为它会创建不必要的CaseInsensitiveString对象。 但相反它应该写成这样:

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";
  1. 为什么第一个陈述好吗? 不应该

    CaseInsensitiveString

  2. 我如何使CaseInsensitiveString表现得像String所以上面的说法是正确的(有和没有延伸String)? 它是什么让它能够传递像这样的文字? 根据我的理解,Java中没有“复制构造函数”概念?

JavaNewbie asked 2019-09-10T22:46:30Z
23个解决方案
108 votes

"Polish"是一种特殊的内置类语言。 这是String课程,你应该避免说

String s = new String("Polish");

因为文字"Polish"已经是String类型,并且您正在创建一个额外的不必要的对象。 对于任何其他类,说

CaseInsensitiveString cis = new CaseInsensitiveString("Polish");

是正确的(在这种情况下,只有)这样做的事情。

Adam Rosenfield answered 2019-09-10T22:46:59Z
55 votes

我相信使用文字形式(即“foo”而不是新的String(“foo”))的主要好处是所有字符串文字都被VM“拦截”。 换句话说,它被添加到池中,使得创建相同字符串的任何其他代码将使用池化的String而不是创建新实例。

为了说明,下面的代码将为第一行打印为true,但对于第二行则为false:

System.out.println("foo" == "foo");
System.out.println(new String("bar") == new String("bar"));
Leigh answered 2019-09-10T22:47:30Z
30 votes

字符串在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。

Steve B. answered 2019-09-10T22:48:34Z
19 votes
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(s3==s4);// **false** due to object

System.out.println(s3==s4);// **false** due to object

比较(s3和s4在堆中的不同位置创建)

Vikas answered 2019-09-10T22:49:37Z
12 votes

Strings在Java中很特殊 - 它们是不可变的,字符串常量自动变成final对象。

您的String示例无法应用于任何其他类。

你也不能延伸String,因为它被宣布为final,这意味着不允许进行子类化。

Alnitak answered 2019-09-10T22:50:15Z
7 votes

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
  • a1a2是对象引用
  • 当显式声明string时,将创建新实例并且它们的引用将不相同。

我认为你试图使用casesensitive类是错误的。 不管弦乐。 您真正关心的是如何显示或比较值。 使用另一个类来格式化字符串或进行比较。

TextUtility.compare(string 1, string 2) 
TextUtility.compareIgnoreCase(string 1, string 2)
TextUtility.camelHump(string 1)

由于您正在组成课程,因此您可以根据需要进行比较 - 比较文本值。

mson answered 2019-09-10T22:51:45Z
6 votes

你不能。 Java中双引号的东西被编译器特别识别为字符串,不幸的是你无法覆盖它(或扩展java.lang.String - 它声明为final)。

Dan Vinton answered 2019-09-10T22:52:12Z
6 votes

回答问题的最佳方法是让您熟悉“字符串常量池”。 在java中,字符串对象是不可变的(即它们的值在初始化后无法更改),因此在编辑字符串对象时,最终会创建一个新编辑的字符串对象,而旧对象只是在特殊内存中浮动,称为“字符串” 常数池“。 通过创建一个新的字符串对象

String s = "Hello";

将只在池中创建一个字符串对象,引用s将引用它,但通过使用

String s = new String("Hello");

你创建了两个字符串对象:一个在池中,另一个在堆中。 引用将引用堆中的对象。

Surender Thakran answered 2019-09-10T22:52:52Z
4 votes

- 我如何让CaseInsensitiveString像String一样,所以上面的语句是正确的(使用和w / out扩展String)? 它是什么让它能够传递它像这样的文字? 根据我的理解,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值的原因。

OscarRyz answered 2019-09-10T22:54:49Z
3 votes

在第一个示例中,您正在创建一个String,“愚蠢”,然后将其作为参数传递给另一个String的复制构造函数,该构造函数生成第二个与第一个相同的String。 由于Java字符串是不可变的(经常刺痛习惯于C字符串的人),这是不必要的资源浪费。 您应该使用第二个示例,因为它会跳过几个不必要的步骤。

但是,String文字不是CaseInsensitiveString,因此您在上一个示例中无法执行所需操作。 此外,没有办法像C ++一样重载一个转换操作符,所以实际上没有办法做你想要的。 您必须将其作为参数传递给类的构造函数。 当然,我可能只是使用String.toLowerCase()并完成它。

此外,您的CaseInsensitiveString应该实现CharSequence接口以及可能的Serializable和Comparable接口。 当然,如果你实现Comparable,你也应该重写equals()和hashCode()。

James answered 2019-09-10T22:55:30Z
3 votes

仅仅因为你的班级中有String这个词,并不意味着你获得了内置String类的所有特殊功能。

javaguy answered 2019-09-10T22:55:57Z
3 votes

CaseInsensitiveString不是String虽然它包含String.String文字例如“example”只能分配给String

fastcodejava answered 2019-09-10T22:56:24Z
2 votes

CaseInsensitiveString和String是不同的对象。 你做不到:

CaseInsensitiveString cis = "Polish";

因为“Polish”是一个String,而不是CaseInsensitiveString。 如果String扩展了CaseInsensitiveString String那么你就可以了,但显然它没有。

而且不要担心这里的建筑,你不会做出不必要的物体。 如果你看一下构造函数的代码,它所做的就是存储对你传入的字符串的引用。没有任何额外的东西被创建。

在String s = new String(“foobar”)的情况下,它正在做一些不同的事情。 您首先创建文字字符串“foobar”,然后通过构造一个新的字符串来创建它的副本。 没有必要创建该副本。

Herms answered 2019-09-10T22:57:12Z
2 votes

当他们说要写的时候

String s = "Silly";

代替

String s = new String("Silly");

它们是指创建String对象时的意思,因为上述两个语句都创建了一个String对象,但新的String()版本创建了两个String对象:一个在堆中,另一个在字符串常量池中。 因此使用更多的内存。

但是当你写作

CaseInsensitiveString cis = new CaseInsensitiveString("Polish");

你没有创建一个String,而是创建一个CaseInsensitiveString类的对象。 因此,您需要使用new运算符。

answered 2019-09-10T22:58:01Z
1 votes

如果我理解正确,那么你的问题意味着我们不能通过直接为它赋值来创建一个对象,不要将它限制为java中的String包装类。

回答我只想说,纯粹面向对象的编程语言有一些结构,它说,单独编写的所有文字都可以直接转换为给定类型的对象。

这恰恰意味着,如果解释器看到3它将被转换为Integer对象,因为integer是为这样的文字定义的类型。

如果解释器在单引号中看到任何东西,如'a',它将直接创建一个字符类型的对象,您不需要指定它,因为该语言为它定义了类型字符的默认对象。

类似地,如果解释器在“”中看到某些内容,则它将被视为其默认类型的对象,即字符串。 这是在后台工作的一些本机代码。

感谢麻省理工学院视频讲座课程6.00,我得到了这个答案的提示。

dharam answered 2019-09-10T22:59:01Z
0 votes

在Java中,语法“text”创建类java.lang.String的实例。 分配:

String foo = "text";

是一个简单的赋值,不需要复制构造函数。

MyString bar = "text";

无论你做什么都是非法的,因为MyString类不是java.lang.String或java.lang.String的超类。

Darron answered 2019-09-10T22:59:40Z
0 votes

首先,您不能创建一个从String扩展的类,因为String是最终类。 并且java管理字符串与其他类不同,因此只能使用String

String s = "Polish";

但是对于你的类,你必须调用构造函数。 所以,那个代码很好。

Lucas Gabriel Sánchez answered 2019-09-10T23:00:16Z
0 votes

我只想补充一点,Java有Copy构造函数......

嗯,这是一个普通的构造函数,其对象的类型与参数相同。

PhiLho answered 2019-09-10T23:00:49Z
0 votes

在JDK的大多数版本中,两个版本将是相同的:

String s = new String(“傻”);

String s =“不再傻”;

因为字符串是不可变的,所以编译器维护一个字符串常量列表,如果你尝试创建一个新的字符串常量,将首先检查字符串是否已经定义。 如果是,则返回对现有不可变字符串的引用。

澄清一下 - 当你说“String s =”时你正在定义一个新的变量来占用堆栈上的空间 - 那么你是说“不再傻”还是新的String(“傻”)完全相同的事情发生了 - 一个新的 常量字符串被编译到您的应用程序中,引用指向它。

我没有看到这里的区别。 但是对于你自己的类,它不是不可变的,这种行为是无关紧要的,你必须调用你的构造函数。

更新:我错了!基于向下投票和评论,我测试了这一点并意识到我的理解是错误的 - 新的String(“傻瓜”)确实创建了一个新的字符串,而不是重用现有的字符串。 我不清楚为什么会这样(有什么好处?)但是代码比言语更响亮!

Ewan Makepeace answered 2019-09-10T23:01:58Z
0 votes

String是一个特殊的类,您可以在没有新的Sring部分的情况下创建它们

它是一样的

int x = y;

要么

char c;

Tolga E answered 2019-09-10T23:02:47Z
0 votes

java中的字符串是不可变的并且区分大小写是一个基本法则。

user1945649 answered 2019-09-10T23:03:16Z
0 votes
 String str1 = "foo"; 
 String str2 = "foo"; 

str1和str2都属于相同的String对象,“foo”,b'coz Java管理StringPool中的字符串,所以如果一个新变量引用相同的String,它不会创建另一个而是分配StringPool中存在的相同的alerady。

 String str1 = new String("foo"); 
 String str2 = new String("foo");

这里str1和str2都属于不同的对象,b'coz new String()强制创建一个新的String对象。

Akash5288 answered 2019-09-10T23:03:51Z
-1 votes

Java为您在代码中使用的每个字符串文字创建一个String对象。 任何时候使用6, 6.0, 'c',,它与致电"text"相同。

字符串是复杂的数据,只是像原始数据一样“行动”。 字符串文字实际上是对象,即使我们假装它们是原始文字,如6, 6.0, 'c',等。因此字符串“literal”"text"返回一个值为char[] value = {'t','e','x','t}的新String对象。因此,调用

new String("text"); 

实际上就像打电话一样

new String(new String(new char[]{'t','e','x','t'}));

希望从这里开始,您可以看到为什么您的教科书认为这是多余的。

作为参考,这里是String的实现:[http://www.docjar.com/html/api/java/lang/String.java.html]

这是一个有趣的阅读,可能会激发一些洞察力。 对于初学者来说,阅读和尝试理解它也很棒,因为代码演示了非常专业和符合惯例的代码。

另一个很好的参考是关于字符串的Java教程:[http://docs.oracle.com/javase/tutorial/java/data/strings.html]

Patrick Michaelsen answered 2019-09-10T23:04:59Z
translate from https://stackoverflow.com:/questions/334518/java-strings-string-s-new-stringsilly