Java文字列:“ String s = new String(” silly“);”


85

私はJavaを学ぶC ++の人です。私はEffectiveJavaを読んでいて、何かが私を混乱させました。次のようなコードは絶対に書かないでください。

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";
  1. 最初のステートメントが問題ないのはなぜですか?あるべきではない

    CaseInsensitiveString cis = "Polish";

  2. 上記のステートメントが(拡張の有無にかかわらず)OKになるCaseInsensitiveStringように動作させるにはどうすればよいですか?Stringについて、そのようなリテラルを渡すことができても問題ないのは何ですか?私の理解では、Javaには「コピーコンストラクタ」の概念はありませんか?StringString


2
文字列str1 = "foo"; 文字列str2 = "foo"; str1とstr2はどちらも同じ文字列オブジェクト「foo」に属しています。b'cozforJavaはStringPoolで文字列を管理するため、新しい変数は同じ文字列を参照します。別の変数を作成するのではなく、に存在する同じアレラディを割り当てます。 StringPool。しかし、これを行うと、次のようになります。Stringstr1 = new String( "foo"); 文字列str2 = new String( "foo"); ここでは、str1とstr2の両方が異なるオブジェクトに属しており、b'coz new String()は新しい文字列オブジェクトを強制的に作成します。
akash5288 2013

回答:


110

String言語の特別な組み込みクラスです。それはあなたが言うのを避けるべきであるStringクラスのためだけです

String s = new String("Polish");

リテラル"Polish"はすでに型Stringであり、余分な不要なオブジェクトを作成しているためです。他のクラスの場合、

CaseInsensitiveString cis = new CaseInsensitiveString("Polish");

正しい(そしてこの場合のみ)ことです。


8
2つ目のポイントは、コンパイラが文字列リテラルを使ってinternig / propagating-stuffを空想できることです。これは、「String(literal)」のような奇妙な関数呼び出しでは必ずしも可能ではありません
Tetha

4
を呼び出すべきではないのでnew String("foo")、コンストラクターnew String(String)が存在する理由を自問することができます。答えは時々それの良い使用法があるということです: stackoverflow.com/a/390854/1442870
Enwired

参考までに、上記のTethaのコメントは、文字列のインターンのように、「インターン」という単語のスペルを間違えています
バジルバーク2015

56

リテラル形式(つまり、new String( "foo")ではなく "foo")を使用する主な利点は、すべての文字列リテラルがVMによって「インターン」されることだと思います。つまり、同じ文字列を作成する他のコードが新しいインスタンスを作成するのではなく、プールされた文字列を使用するように、プールに追加されます。

説明のために、次のコードは最初の行ではtrueを出力し、2番目の行ではfalseを出力します。

System.out.println("foo" == "foo");
System.out.println(new String("bar") == new String("bar"));

14
同様に、FindBugsが「newInteger(N)」を「Integer.valueOf(N)」に置き換えるように指示するのはそのためです-そのインターンのためです。
ポールトゥームリン

6
また、 "foo" == new String( "foo")。intern()を追加する必要があります
James Schek 2008

4
修正:文字列リテラルは、VMではなくコンパイラーによって同じ参照を指すように作成されます。VMは実行時にStringオブジェクトをインターンする可能性があるため、2行目はtrueまたはfalseを返す可能性があります。
Craig P. Motlin 2008

1
@Motlin:それが正しいかどうかはわかりません。Stringクラスのjavadocは、「すべてのリテラル文字列と文字列値の定数式をインターンする」ことを義務付けています。したがって、リテラルがインターンされていることに依存できます。つまり、 "foo" == "foo"は常にtrueを返す必要があります。
リー

3
@Leighはい、リテラルはインターンされますが、VMではなくコンパイラによって行われます。Motlinが得ているのは、VMが文字列を追加でインターンする可能性があるため、new String( "bar")== new String( "bar")-> falseが実装に依存するかどうかです。
Aaron Maenpaa 2008

30

文字列は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でのみ機能します。


不変性について言及するための+1。これは、Javaでの本当の理由strA = strBですstrA = new String(strB)。文字列のインターンとはあまり関係ありません。
kritzikratzi 2013

それらは参照カウントでは処理されません。JLSでは文字列プーリングが必要です。
ローン侯爵2016

20
String s1="foo";

リテラルはプールに入れられ、s1が参照します。

String s2="foo";

今回は、「foo」リテラルがStringPoolですでに使用可能かどうかをチェックするため、s2は同じリテラルを参照します。

String s3=new String("foo");

「foo」リテラルは最初にStringPoolで作成され、次にstring 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はヒープ内の異なる場所で作成されます)


1
引用符で囲まれていないテキストには引用符の書式を使用しないでください。また、コードにはコードの書式を使用してください。
ローン侯爵2016

12

StringsはJavaで特別です-それらは不変であり、文字列定数は自動的にStringオブジェクトに変換されます。

あなたのSomeStringClass cis = "value"例を他のクラスに適用する方法はありません。

Stringとして宣言されているため、拡張することもできませんfinal。つまり、サブクラス化は許可されていません。


7

Java文字列は興味深いものです。回答はいくつかの興味深い点をカバーしているようです。これが私の2セントです。

文字列は不変です(変更することはできません)

String x = "x";
x = "Y"; 
  • 最初の行は、文字列値「x」を含む変数xを作成します。JVMは、文字列値のプールを調べて、「x」が存在するかどうかを確認します。存在する場合は、変数xをポイントし、存在しない場合は、作成してから割り当てを行います。
  • 2行目では、「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)

クラスを構成しているので、比較に必要なことを実行させることができます-テキスト値を比較します。


コンパイラは、JVMではなく文字列プールを作成します。変数にはオブジェクトが含まれていません。オブジェクトを参照します。文字列リテラルの文字列プールスペースが再利用されることはありません。
ローン侯爵2016

6

できません。Javaで二重引用符で囲まれているものは、コンパイラによって文字列として特別に認識されます。残念ながら、これをオーバーライドすることはできません(または拡張できますjava.lang.String-宣言されていますfinal)。


決勝はここの赤いニシンです。Stringが最終的なものでなかったとしても、この場合、Stringを拡張しても彼は役に立ちません。
ダロン

1
幸いなことに、これをオーバーライドすることはできません。:)
Craig P. Motlin

@モトリン:ハ!あなたは正しいかもしれません。Javaは、このような興味深いものを意図的に除外することで、誰もが愚かなことをするのを防ぐように設計されていることをどこかで読んだと思います...
Dan Vinton

6

あなたの質問に答える最良の方法は、「文字列定数プール」に慣れることです。Javaでは、文字列オブジェクトは不変です(つまり、初期化すると値を変更できません)。したがって、文字列オブジェクトを編集すると、新しい編集済み文字列オブジェクトが作成され、古いオブジェクトが特別なメモリに浮かんでいるだけです。これは「文字列」と呼ばれます。一定のプール」。によって新しい文字列オブジェクトを作成する

String s = "Hello";

プール内に文字列オブジェクトを作成するだけで、参照はそれを参照しますが、

String s = new String("Hello");

2つの文字列オブジェクトを作成します。1つはプールに、もう1つはヒープに作成します。参照は、ヒープ内のオブジェクトを参照します。


4

-CaseInsensitiveStringをStringのように動作させて、上記のステートメントで問題がないようにするにはどうすればよいですか(Stringを拡張する場合としない場合)。そのようなリテラルを渡すことができるようにするのは、Stringについて何ですか?私の理解では、Javaには「コピーコンストラクタ」の概念はありませんよね?

最初の点から十分に言われています。「ポーランド語」は文字列リテラルであり、CaseInsentiviveStringクラスに割り当てることはできません。

さて、2点目について

新しいリテラルを作成することはできませんが、その本の最初の項目に従って「同様の」アプローチをとることができるため、次のステートメントが当てはまります。

    // 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オブジェクトの内部プールを作成し、そこから対応するインスタンスを返します。

このように、「==」演算子は、同じ値を表す2つのオブジェクト参照に対してtrueを返します

これは、同様のオブジェクトが非常に頻繁に使用され、作成コストが高い場合に役立ちます。

文字列クラスのドキュメントには、クラスが内部プールを使用すると記載されています

クラスは完全ではありません。CharSequenceインターフェイスの実装時にオブジェクトのコンテンツをウォークしようとすると、いくつかの興味深い問題が発生しますが、このコードは、ブック内のそのアイテムをどのように適用できるかを示すのに十分です。

internalPoolオブジェクトを使用すると、参照が解放されないため、ガベージコレクターになりません。これは、多数のオブジェクトが作成される場合に問題になる可能性があることに注意してください。

集中的に使用され、プールは「インターン」オブジェクトのみで構成されているため、Stringクラスで機能します。

可能な値は2つしかないため、ブールクラスでもうまく機能します。

そして最後に、それが整数クラスのvalueOf(int)が-128から127のint値に制限されている理由でもあります。


3

最初の例では、「愚かな」文字列を作成し、それをパラメータとして別の文字列のコピーコンストラクタに渡します。これにより、最初の文字列と同じ2番目の文字列が作成されます。Java文字列は不変であるため(C文字列に慣れている人を頻繁に刺すもの)、これは不必要なリソースの浪費です。代わりに、いくつかの不要な手順をスキップする2番目の例を使用する必要があります。

ただし、文字列リテラルはCaseInsensitiveStringではないため、最後の例で必要なことを実行できません。さらに、C ++のようにキャスト演算子をオーバーロードする方法はないため、文字通り、必要なことを実行する方法はありません。代わりに、クラスのコンストラクターにパラメーターとして渡す必要があります。もちろん、私はおそらくString.toLowerCase()を使用して、それで終わります。

また、CaseInsensitiveStringは、CharSequenceインターフェイスと、おそらくSerializableおよびComparableインターフェイスを実装する必要があります。もちろん、Comparableを実装する場合は、equals()とhashCode()もオーバーライドする必要があります。


3

Stringクラスに単語があるからといって、組み込みStringクラスのすべての特別な機能を利用できるわけではありません。


3

CaseInsensitiveStringString含まれていますが、ではありませんStringStringリテラル例えば「例」のみに割り当てることができますString


2

CaseInsensitiveStringとStringは異なるオブジェクトです。あなたはできません:

CaseInsensitiveString cis = "Polish";

「ポーランド語」は文字列であり、CaseInsensitiveStringではないためです。StringがCaseInsensitiveStringStringを拡張した場合は問題ありませんが、明らかにそうではありません。

そして、ここでの構造について心配する必要はありません。不要なオブジェクトを作成することはありません。コンストラクターのコードを見ると、渡した文字列への参照を格納するだけです。余分なものは何も作成されていません。

String s = new String( "foobar")の場合、何か別のことをしています。最初にリテラル文字列「foobar」を作成し、次にそれから新しい文字列を作成してそのコピーを作成します。そのコピーを作成する必要はありません。


Stringを拡張しても、これは機能しません。CaseInsensitiveStringを拡張するには、Stringが必要です。
ダロン

いずれの場合も、文字列が組み込まれているため、または宣言されたファイナルのため、不可能です
ルーク

2

彼らが書くと言うとき

String s = "Silly";

の代わりに

String s = new String("Silly");

上記のステートメントは両方ともStringオブジェクトを作成しますが、新しいString()バージョンは2つのStringオブジェクトを作成するためです。1つはヒープに、もう1つは文字列定数プールに作成されます。したがって、より多くのメモリを使用します。

しかし、あなたが書くとき

CaseInsensitiveString cis = new CaseInsensitiveString("Polish");

文字列を作成するのではなく、CaseInsensitiveStringクラスのオブジェクトを作成します。したがって、new演算子を使用する必要があります。


1

私が正しく理解していれば、あなたの質問は、オブジェクトに値を直接割り当ててオブジェクトを作成できない理由を意味し、JavaのWrapper ofStringクラスに制限することはできません。

私がちょうど言うことに答えるために、純粋にオブジェクト指向プログラミング言語にはいくつかの構造があり、それは、単独で書かれたときのすべてのリテラルは、与えられたタイプのオブジェクトに直接変換できると言います。

つまり、整数はそのようなリテラルに対して定義された型であるため、インタプリタが3を検出すると、整数オブジェクトに変換されます。

インタプリタが「a」のような一重引用符で囲まれたものを見ると、文字型のオブジェクトが直接作成されます。言語が文字型のデフォルトオブジェクトを定義しているため、指定する必要はありません。

同様に、インタプリタが ""に何かを見つけた場合、それはデフォルトタイプのオブジェクト、つまり文字列と見なされます。これは、バックグラウンドで動作するネイティブコードです。

この答えのヒントを得たMITビデオ講義コース6.00に感謝します。


0

Javaでは、構文「text」はクラスjava.lang.Stringのインスタンスを作成します。割り当て:

String foo = "text";

は単純な割り当てであり、コピーコンストラクタは必要ありません。

MyString bar = "text";

MyStringクラスはjava.lang.Stringでもjava.lang.Stringのスーパークラスでもないため、何をしても違法です。


0

まず、Stringは最終クラスであるため、Stringから拡張するクラスを作成することはできません。また、Javaは他のクラスとは異なる方法で文字列を管理するため、文字列でのみ実行できます

String s = "Polish";

ただし、クラスではコンストラクターを呼び出す必要があります。したがって、そのコードは問題ありません。


0

JavaにはCopyコンストラクターがあることを付け加えておきます...

まあ、それは引数と同じ型のオブジェクトを持つ通常のコンストラクタです。


2
これはデザインパターンであり、言語構造ではありません。Javaでは、すべてが常に「参照による」ものであり、各オブジェクトには1つのコピーしかないため、コピーコンストラクターが興味深い用途はほとんどありません。実際、コピーを作成すると、多くの問題が発生します。
ビルK

0

JDKのほとんどのバージョンでは、2つのバージョンは同じです。

String s = new String( "silly");

String s = "もうばかげていない";

文字列は不変であるため、コンパイラは文字列定数のリストを維持し、新しい文字列を作成しようとすると、最初に文字列がすでに定義されているかどうかを確認します。その場合、既存の不変文字列への参照が返されます。

明確にするために、「String s =」と言うと、スタック上のスペースを占める新しい変数を定義しています。「もうばかげていない」と言っても、新しいString(「ばかげている」)と言っても、まったく同じことが起こります。定数文字列がアプリケーションにコンパイルされ、参照がそれを指します。

ここでは違いがわかりません。ただし、不変ではない独自のクラスの場合、この動作は無関係であり、コンストラクターを呼び出す必要があります。

更新:私は間違っていました!反対票と添付のコメントに基づいて、私はこれをテストし、私の理解が間違っていることに気付きました-new String( "Silly")は、既存の文字列を再利用するのではなく、実際に新しい文字列を作成します。なぜこれが起こるのか(利点は何ですか?)はわかりませんが、コードは言葉よりも雄弁です!


0

文字列は、新しいSringパーツなしで作成できる特別なクラスの1つです。

それはと同じです

int x = y;

または

char c;


0

Javaの文字列は不変であり、大文字と小文字が区別されるというのが基本法則です。


0
 String str1 = "foo"; 
 String str2 = "foo"; 

str1とstr2はどちらも同じ文字列オブジェクト「foo」に属しています。b'cozJavaはStringPoolで文字列を管理するため、新しい変数が同じ文字列を参照する場合、別の変数を作成するのではなく、StringPoolに存在する同じアレラディを割り当てます。 。

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

ここでは、str1とstr2の両方が異なるオブジェクトに属しており、b'coz new String()は新しい文字列オブジェクトを強制的に作成します。


-1

Javaは、コードで使用する文字列リテラルごとにStringオブジェクトを作成します。いつでも""使用できますが、を呼び出すのと同じnew String()です。

文字列は、プリミティブデータのように「機能」する複雑なデータです。文字列リテラルは、などのようにプリミティブリテラルであるかのように見せかけても、実際にはオブジェクトです6, 6.0, 'c',。したがって、文字列「literal」"text"は、値がchar[] value = {'t','e','x','t}。したがって、

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

それは楽しい読み物であり、いくつかの洞察を刺激するかもしれません。コードは非常に専門的で規則に準拠したコードを示しているため、初心者が読んで理解しようとするのも素晴らしいことです。

もう1つの優れたリファレンスは、文字列に関するJavaチュートリアルです。http//docs.oracle.com/javase/tutorial/java/data/strings.html


「」が使用されている場合は常に、同じものへの参照です常に、定数プール内の文字列です。
ローン侯爵2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.