6

私は次のJavaコードを持っています:

public String makinStrings() {
  String s = "Fred";
  s = s + "47";
  s = s.substring(2, 5);
  s = s.toUpperCase();
  return s.toString();
}

質問はどういうわけか単純です:Stringこのメソッドが呼び出されたときにいくつのオブジェクトが作成されますか?

最初は5つの文字列オブジェクトが作成されたと答えましたが、私の本の答えでは、3つのオブジェクトしか作成されておらず、説明もありません(これはSCJPの質問です)。

私の見解では、「Fred」、「47」、「Fred47」、「ed4」、「ED4」の5つのオブジェクトがあります。

この質問はSCJPシミュレーション試験でも見つかりましたが、同じ答えが3つあります。

4

3 に答える 3

15

「Fred」と「47」は、文字列リテラルプールから取得されます。そのため、メソッドが呼び出されたときに作成されません。代わりに、クラスがロードされるとき(または、他のクラスが同じ値の定数を使用する場合はそれ以前)に配置されます。

「Fred47」、「ed4」、および「ED4」は、String各メソッド呼び出しで作成される3つのオブジェクトです。

于 2011-09-10T08:50:30.257 に答える
2

プログラムのコードには、多くの文字列リテラルが含まれている傾向があります。Javaでは、これらの定数は効率を上げるために文字列テーブルと呼ばれるものに収集されます。たとえば、文字列"Name: "を10の異なる場所で使用する場合、JVMには(通常)その文字列のインスタンスが1つだけあり、使用される10の場所すべてで、参照はすべてその1つのインスタンスを指します。これにより、メモリが節約されます。

String不変であるため、この最適化が可能です。文字列を変更できる場合、1つの場所を変更すると、他の9つの文字列も変更されることになります。そのため、文字列を変更する操作はすべて新しいインスタンスを返します。そのため、これを行うと次のようになります。

String s = "boink";
s.toUpperCase();
System.out.println(s);

boinkではなく、印刷しBOINKます。

ここでもう1つ注意が必要な点があります。の複数のインスタンスは、文字データの基になる同じものを指しているjava.lang.String 可能性があります。つまり、配列のスライスだけを使用することで、同じ上の異なるビューである可能性があります。繰り返しますが、効率の最適化。この方法は、これが発生するケースの1つです。char[]char[]substring()

s1 = "Fred47";

//String s1: data=[ 'F', 'r', 'e', 'd', '4', '7'], offset=0, length=6
//                   ^........................^

s2 = s1.substring(2, 5);

//String s2: data=[ 'F', 'r', 'e', 'd', '4', '7'], offset=2, length=3
//                             ^.........^
// the two strings are sharing the same char[]!

SCJPの質問では、これはすべて次のように要約されます。

  • 文字列"Fred"は文字列テーブルから取得されます。
  • 文字列"47"は文字列テーブルから取得されます。
  • 文字列"Fred47"は、メソッド呼び出し中に作成されます。// 1
  • 文字列"ed4"はメソッド呼び出し中に作成され、//2と同じバッキング配列を共有します"Fred47"
  • 文字列"ED4"は、メソッド呼び出し中に作成されます。// 3
  • s.toString()新しいものを作成せず、単にを返しますthis

このすべての興味深いエッジケースの1つ:たとえば、インターネットから取得したWebページなど、非常に長い文字列がある場合にどうなるかを考えてみましょう。たとえば、の長さchar[]が2メガバイトであるとします。これを利用すると、4文字の長さのように見えるsubstring(0, 4)新しい文字列が得られますが、それでも2メガバイトのバッキングデータを共有します。これは現実の世界ではそれほど一般的ではありませんが、メモリの大きな浪費になる可能性があります。問題としてこれに遭遇する(まれな)ケースでは、新しい小さなバッキング配列を使用して文字列を作成するために使用できます。new String(hugeString.substring(0, 4))

最後に、文字列を呼び出すことにより、実行時に文字列を文字列テーブルに強制的に入れることができますintern()。この場合の基本的なルール:それをしないでください。拡張ルール:メモリプロファイラーを使用して、それが有用な最適化であることを確認していない限り、これを行わないでください。

于 2011-09-10T09:20:51.173 に答える
2

javapの出力に基づくと、連結中にStringではなくStringBuilderが作成されたように見えます。次に、substring()、toUpperCase()、およびtoString()に対して呼び出される3つの文字列があります。

最後の呼び出しは、StringBuilderを文字列に変換するため、冗長ではありません。

>javap -c Test
Compiled from "Test.java"

public java.lang.String makinStrings();
Code:
0:   ldc     #5; //String Fred
2:   astore_1
3:   new     #6; //class java/lang/StringBuilder
6:   dup
7:   invokespecial   #7; //Method java/lang/StringBuilder."<init>":()V
10:  aload_1
11:  invokevirtual   #8; //Method java/lang/StringBuilder.append:   (Ljava/lang/String;)Ljava/lang/StringBuilder;
14:  ldc     #9; //String 47
16:  invokevirtual   #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19:  invokevirtual   #10; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22:  astore_1
23:  aload_1
24:  iconst_2
25:  iconst_5
26:  invokevirtual   #11; //Method java/lang/String.substring:(II)Ljava/lang/String;
29:  astore_1
30:  aload_1
31:  invokevirtual   #12; //Method java/lang/String.toUpperCase:()Ljava/lang/String;
34:  astore_1
35:  aload_1
36:  invokevirtual   #13; //Method java/lang/String.toString:()Ljava/lang/String;
39:  areturn

}

于 2011-09-10T11:16:43.597 に答える