就在最近,我看了java.lang.String类(http://www.docjar.com/html/api/java/lang/String.java.html)的源代码,我注意到使用的构造函数(或至少一个特定的构造函数)依赖于String类型的参数。怎么可能?假设以下代码
String s = new String("String!");
编译器如何执行此操作?在括号内传递一个字符串对象,对吧?但这需要以某种方式实例化(我猜是编译器?)?或者它是如何工作的?
我故意使用String对象而不是字符串文字的实例化,因为字符串文字在内存池上的分配方式不同,而不是字符串对象(堆)。
一切顺利,大卫
答案 0 :(得分:1)
String
类有多个构造函数。传入的实例可以使用不同的构造函数创建:
String string = new String();
new String(string);
您不需要了解字符串文字如何创建/重用对象,以了解String
如何拥有构造函数String(String)
答案 1 :(得分:0)
java编译器将编译很多,然后进行一些检查。这是可能的,因为类类似于.obj或.o,稍后包含名称"链接"。
在提到的情况下,事实上对编译或运行时使用没有任何挑战。同样适用于非标准课程:一个人可以总是null
。
还有更多丑陋的事情要看:
public interface IFC {
public static final IFC SOME_CONSTANT = new IMPL();
}
public class IMPL implements IFC {
}
关于那个例子。构造函数String(String)
源于Java的开头。在C ++中,人们会将其称为复制构造函数。由于String是不可变的,因此它没有任何用途:s.equals(new String(s))
所以它实际上是错误的样式。 (可能是为了禁用==
而添加的。)
答案 2 :(得分:0)
编译器如何执行此操作?在括号内传递一个字符串对象,对吗?
不是真的。如果您查看String构造函数的API,它实际上会将参数作为char array
接收。
String str = "abc";
相当于:
char data[] = {'a', 'b', 'c'};
String str = new String(data);
答案 3 :(得分:0)
这就是编译器在幕后所做的事情。
编译器可以例如将字符串文字("abc"
)转换为字符数组,并使用接受char[]
的ctor。
答案 4 :(得分:0)
假设你有一个这样的程序:
public class Test {
public static void main(String[] args) {
String s = new String("String!");
System.out.println(s);
}
}
编译时,您将获得一个包含该程序字节码的.class文件。您可以通过调用javap -c -v -l -s Test.class
来查看字节码(javap
是JDK的一部分),它会打印出类似这样的内容:
Classfile /C:/Test.class
Last modified 08.03.2015; size 454 bytes
MD5 checksum 0649bf262c557480c276e268762a1dd5
Compiled from "Test.java"
public class Test
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #8.#17 // java/lang/Object."<init>":()V
#2 = Class #18 // java/lang/String
#3 = String #19 // String!
#4 = Methodref #2.#20 // java/lang/String."<init>":(Ljava/lang/String;)V
#5 = Fieldref #21.#22 // java/lang/System.out:Ljava/io/PrintStream;
#6 = Methodref #23.#24 // java/io/PrintStream.println:(Ljava/lang/String;)V
#7 = Class #25 // Test
#8 = Class #26 // java/lang/Object
#9 = Utf8 <init>
#10 = Utf8 ()V
#11 = Utf8 Code
#12 = Utf8 LineNumberTable
#13 = Utf8 main
#14 = Utf8 ([Ljava/lang/String;)V
#15 = Utf8 SourceFile
#16 = Utf8 Test.java
#17 = NameAndType #9:#10 // "<init>":()V
#18 = Utf8 java/lang/String
#19 = Utf8 String!
#20 = NameAndType #9:#27 // "<init>":(Ljava/lang/String;)V
#21 = Class #28 // java/lang/System
#22 = NameAndType #29:#30 // out:Ljava/io/PrintStream;
#23 = Class #31 // java/io/PrintStream
#24 = NameAndType #32:#27 // println:(Ljava/lang/String;)V
#25 = Utf8 Test
#26 = Utf8 java/lang/Object
#27 = Utf8 (Ljava/lang/String;)V
#28 = Utf8 java/lang/System
#29 = Utf8 out
#30 = Utf8 Ljava/io/PrintStream;
#31 = Utf8 java/io/PrintStream
#32 = Utf8 println
{
public Test();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=2, args_size=1
0: new #2 // class java/lang/String
3: dup
4: ldc #3 // String String!
6: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V
9: astore_1
10: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
13: aload_1
14: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
17: return
LineNumberTable:
line 3: 0
line 4: 10
line 5: 17
}
SourceFile: "Test.java"
虽然这看起来很混乱(而且我对字节码的处理不是很深),我会尝试将其缩减为:
常量池是包含“固定”信息的类文件的一部分,如方法签名或文本(是:字符串文本)。代码部分包含应该执行的实际代码,它使用对常量池的引用。
那么,文本"String!"
如何实际加载?在ldc #3
上的代码块中,索引#3
的值被加载并放在堆栈中。 #3
是#19
的引用,它是实际文本String!
。
然后,下一个字节码指令使用索引invokespecial
执行#4
,这是String
构造函数,另一个String
作为参数 - “复制构造函数”,如其他所述答案(<init>
指的是构造函数,另一部分是参数类型和返回类型)。
虽然这可能总结了至少部分发生的事情,但一个细节仍然不明确:运行时如何将文字文本String!
从#19
转换为 String
对象?
在玩了一些类似的代码及其字节码表示后,我只能做出有根据的猜测:运行时具有硬编码支持将字符串文字转换为String
个对象(例如,以类似的方式代码{{1} }被分为long c = 42L;
和#2 = Long 42l
,您可以通过修改示例进行测试,然后对其进行编译和反编译。)
更多链接: