在 Java 字节码中,所有对象都存储为对象。需要时添加显式类型检查。例如这个Java函数
public Integer getValue(Object number){
int i = ((Number) number).toInt();
return new Integer(i);
}
被翻译成这样的字节码:
(accepts java.lang.Object, returns java.lang.Integer)
-read the first argument as an Object
-if the value is not a Number, raise an exception
-call the virtual method toInt(java.lang.Integer) of the value
and remember the int result
-use the value as an int argument
-instantiate a new java.lang.Integer
-call the constructor(int) of java.lang.Integer on the new number,
getting an Object back
[since the declared return value of Number.toInt is the same
as the return value of our function, no type checking is needed]
-return the value
因此,编译器会去除未使用的变量类型。公共和受保护字段的类型与其类一起存储。
对象的运行时类型与对象一起存储。在 C++ 中,它是指向虚拟方法表的指针。在 Java 中,它是所有已加载类表的 16 位索引。
Java 类文件将所有依赖类的索引存储在一个类似的表中。这里只存储类名。然后所有字段描述都指向该表。
因此,当您编写String s = new String("abc")
(甚至String s = "abc"
)时,您的班级会存储:
- 它依赖于依赖表中的类 java.lang.String
- 字符串文字表中的“abc”
- 您的方法按 ID 加载字符串文字
- (在第一种情况下)您的方法调用其第一个依赖类(String)的构造函数,并将第一个依赖类(String)作为参数。
- 编译器可以证明将新字符串存储在字符串变量中是安全的,因此它会跳过类型检查。
一个类可以在被引用后立即加载,或者在第一次使用时加载(在这种情况下,它由其依赖的类和类中的 ID 引用)。我认为后者现在总是如此。
加载类时:
-its class loader is asked to retreive the class by its name.
-(in the case of the system loader) the class loader looks
for the corresponding file in the program JAR, in the system library
and in all libraries referenced.
-the byte stream is then decoded into a structure in memory
-(in the case of early loading) all dependent classes are loaded recursively
if not already loaded
-it is stored in the class table
-(in the case of late loading) its static initialiser is run
(possibly loading more classes in the process).
在 C++ 中,不会发生任何类加载,因为所有用户类和大多数库都作为单纯的虚拟方法表和相应的方法存储在程序中。所有系统函数(不是类)仍然可以存储在 DLL(在 Windows 的情况下)或类似文件中,并在运行时由库加载。如果显式类型转换隐含类型检查,则在虚拟方法表上执行。另请注意,C++ 有一段时间没有类型检查机制。