Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angularjs/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 将创建多少个字符串对象_Java_Scjp - Fatal编程技术网

Java 将创建多少个字符串对象

Java 将创建多少个字符串对象,java,scjp,Java,Scjp,我有以下Java代码: public String makinStrings() { String s = "Fred"; s = s + "47"; s = s.substring(2, 5); s = s.toUpperCase(); return s.toString(); } 问题很简单:调用此方法时将创建多少字符串对象 一开始,我回答说创建了5个字符串对象,但我书中的答案是只创建了3个对象,没有给出任何解释。这是一个SCJP问题 在我看来,有5个对象: 弗雷德,4

我有以下Java代码:

public String makinStrings() {
  String s = "Fred";
  s = s + "47";
  s = s.substring(2, 5);
  s = s.toUpperCase();
  return s.toString();
}
问题很简单:调用此方法时将创建多少字符串对象

一开始,我回答说创建了5个字符串对象,但我书中的答案是只创建了3个对象,没有给出任何解释。这是一个SCJP问题

在我看来,有5个对象: 弗雷德,47岁,弗雷德,47岁,4岁,4岁


我在SCJP模拟考试中也发现了这个问题,答案是3。

Fred和47将来自字符串文字池。因此,在调用该方法时不会创建它们。相反,如果其他类使用具有相同值的常量,则它们将在类加载时或更早的时候放在那里


Fred47、ed4和ed4是在每次方法调用时创建的3个字符串对象。

程序的代码中往往包含大量字符串文本。在Java中,为了提高效率,这些常量被收集在称为字符串表的东西中。例如,如果您使用字符串名称:在十个不同的位置,JVM通常只有该字符串的一个实例,并且在使用该字符串的所有十个位置,引用都指向该实例。这样可以节省内存

这种优化是可能的,因为字符串是不可变的。如果有可能改变一个字符串,那么改变一个位置就意味着改变另外九个位置。这就是为什么任何更改字符串的操作都会返回一个新实例。这就是为什么如果你这样做:

String s = "boink";
s.toUpperCase();
System.out.println(s);
它打印的是boink,不是boink

现在还有一个更棘手的问题:java.lang.String的多个实例可能会针对其字符数据指向相同的底层char[],换句话说,它们可能是同一char[]上的不同视图,只需使用数组的一部分即可。再一次,优化效率。子字符串方法就是发生这种情况的一种

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是在方法调用期间创建的,与Fred47//2共享相同的支持数组 字符串ED4是在方法调用期间创建的//3. s.toString并没有创建新的字符串,它只是返回这个。 <>这是一个有趣的边缘案例:考虑如果你有一个很长的字符串,例如从互联网上获取的网页,如果说char []的长度是两兆字节。如果取其中的子字符串0,4,就会得到一个新字符串,看起来只有四个字符长,但它仍然共享这两兆字节的备份数据。这在现实世界中并不常见,但可能是对内存的巨大浪费!在罕见的情况下,您会遇到这个问题,您可以使用新的StringhugeString.substring0,4创建一个带有新的小背景数组的字符串


最后,可以在运行时通过调用intern将字符串强制放入字符串表中。这种情况下的基本规则是:不要这样做。扩展规则:除非您使用内存分析器来确定它是一个有用的优化,否则不要这样做。

基于javap输出,它看起来像是在concatation期间创建了一个StringBuilder,而不是一个字符串。然后有三个字符串被称为substring、toUpperCase和toString

最后一个调用不是多余的,因为它将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

}

我设想编译器将内联前两条语句,并删除最后一个冗余方法调用。这就剩下Fred47,ed4,ed4了。@Jared s不是编译时常量表达式,所以不会发生这种情况。编译代码并使用javap-c,甚至字符串。在第一次方法调用后,Fred47、ed4和ed4是否也会在字符串文本池中出现可能的重复?我相信Jared的回答/评论是正确的+1,因为我所知道的所有编译器都做这些简单的内联。这就是为什么串接字符串不再那么昂贵的原因,尽管在某种程度上仍然如此。JVM应该在字符串文本第一次从常量池加载时实例化它们,而不是在类加载时轻松地加载它们。我认为这个实现细节与最初的问题并不相关,但这是一个有趣的琐事。请注意,如果前两行是作为一条语句编写的,Fred47将直接使用,因为Fred+47是一个编译时常量表达式。此外,该库也可能以不同的方式实现并生成不同数量的字符串对象。@Joachim Sauer。。在创建之前,它将如何放置某些东西。。。?即使是字符串文字也必须先实例化,然后再重新使用。@ntc:是的,但字符串文字是在类级别定义的。您可以查看类文件格式规范,以验证这一点,并且JVM可以在加载类时轻松地加载它们。正如斯图尔特·库克(Stuart Cook)所指出的,不需要这样做
但是,在类加载时这样做。这是不正确的。没有一个字符串常量来自字符串池,它们都来自类常量池。如果在几个LM类中使用了与Fred47相同的文字常量,则每个类的常量池中都有相同的常量。参见Chip McCormick答案的字节码。