Java Strings: “String s = new String(”silly“);”

2018-12-31 15:32发布

I'm a C++ guy learning Java. I'm reading Effective Java and something confused me. It says never to write code like this:

String s = new String("silly");

Because it creates unnecessary String objects. But instead it should be written like this:

String s = "No longer silly";

Ok fine so far...However, given this class:

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. Why is the first statement ok? Shouldn't it be

    CaseInsensitiveString cis = "Polish";

  2. How do I make CaseInsensitiveString behave like String so the above statement is OK (with and without extending String)? What is it about String that makes it OK to just be able to pass it a literal like that? From my understanding there is no "copy constructor" concept in Java?

标签: java string
23条回答
妖精总统
2楼-- · 2018-12-31 16:21

Strings are treated a bit specially in java, they're immutable so it's safe for them to be handled by reference counting.

If you write

String s = "Polish";
String t = "Polish";

then s and t actually refer to the same object, and s==t will return true, for "==" for objects read "is the same object" (or can, anyway, I"m not sure if this is part of the actual language spec or simply a detail of the compiler implementation-so maybe it's not safe to rely on this) .

If you write

String s = new String("Polish");
String t = new String("Polish");

then s!=t (because you've explicitly created a new string) although s.equals(t) will return true (because string adds this behavior to equals).

The thing you want to write,

CaseInsensitiveString cis = "Polish";

can't work because you're thinking that the quotations are some sort of short-circuit constructor for your object, when in fact this only works for plain old java.lang.Strings.

查看更多
只若初见
3楼-- · 2018-12-31 16:23

String is one of the special classes in which you can create them without the new Sring part

it's the same as

int x = y;

or

char c;

查看更多
旧时光的记忆
4楼-- · 2018-12-31 16:23

I believe the main benefit of using the literal form (ie, "foo" rather than new String("foo")) is that all String literals are 'interned' by the VM. In other words it is added to a pool such that any other code that creates the same string will use the pooled String rather than creating a new instance.

To illustrate, the following code will print true for the first line, but false for the second:

System.out.println("foo" == "foo");
System.out.println(new String("bar") == new String("bar"));
查看更多
听够珍惜
5楼-- · 2018-12-31 16:23

In your first example, you are creating a String, "silly" and then passing it as a parameter to another String's copy constructor, which makes a second String which is identical to the first. Since Java Strings are immutable (something that frequently stings people who are used to C strings), this is a needless waste of resources. You should instead use the second example because it skips several needless steps.

However, the String literal is not a CaseInsensitiveString so there you cannot do what you want in your last example. Furthermore, there is no way to overload a casting operator like you can in C++ so there is literally no way to do what you want. You must instead pass it in as a parameter to your class's constructor. Of course, I'd probably just use String.toLowerCase() and be done with it.

Also, your CaseInsensitiveString should implement the CharSequence interface as well as probably the Serializable and Comparable interfaces. Of course, if you implement Comparable, you should override equals() and hashCode() as well.

查看更多
美炸的是我
6楼-- · 2018-12-31 16:23

In most versions of the JDK the two versions will be the same:

String s = new String("silly");

String s = "No longer silly";

Because strings are immutable the compiler maintains a list of string constants and if you try to make a new one will first check to see if the string is already defined. If it is then a reference to the existing immutable string is returned.

To clarify - when you say "String s = " you are defining a new variable which takes up space on the stack - then whether you say "No longer silly" or new String("silly") exactly the same thing happens - a new constant string is compiled into your application and the reference points to that.

I dont see the distinction here. However for your own class, which is not immutable, this behaviour is irrelevant and you must call your constructor.

UPDATE: I was wrong! Based on a down vote and comment attached I tested this and realise that my understanding is wrong - new String("Silly") does indeed create a new string rather than reuse the existing one. I am unclear why this would be (what is the benefit?) but code speaks louder than words!

查看更多
登录 后发表回答