lassLoader.class。
4 java.lang.Class对象和对象的内存布局
4.1 Class对象中到底存了什么?
从已有资料来看,Class对象在不同的虚拟机在实现上存储的内容都不一致,但是理论上来讲, Class对象内部一定存储了方法区中该类的所有方法签名,属性签名,和每个方法对应的字节码的地址。
4.2 实例和实例方法之间的关系?
obj.setName(“zhang san”)
在实际执行过程中等价于
setName(obj, “zhang san”)
也就是对象时作为参数传递到实例方法里面的,对象本身不含指向方法的指针(这里说的是非Class对象)。方法的具体实现都位于方法区中相应的代码段中。当虚拟机调用该方法时,只要将虚拟机执行引擎的PC(程序计数器)指向该方法的地址,然后将实例对象传递给该方法即可。通过实例直接调用方法时,实际上没有,也没有必要通过Class对象。
下面的示例表示了,锁住Person.Class对象不能阻止其它线程的代码创建该对象的实例,并调用实例方法。
package javalearning;
public class ClassLockTest {
public static class T1 extends Thread{
private Class<?> cls;
private boolean done;
public T1(Class<?> cls){
this.cls = cls;
}
@Override
public void run() {
synchronized(cls){
while(!done){
}
}
}
public void done(){
done = true;
}
}
public static void main(String[] args) throws InterruptedException{
/*我们先让线程t1锁住Person.class对象,然后在主线程中创建该对象的实例,并调用toString方法*/
Class<?> cls = Person.class;
T1 t1 = new T1(cls);
t1.start();
while(!t1.isAlive()){
System.out.println("t1 is not alive");
Thread.sleep(500);
}
Person p = new Person();
System.out.println(p);
t1.done();
System.out.println("over");
}
}
运行结果
zx 18
over
4.3 Class对象有哪些功能?
1)反射(关于反射的使用会在后续博客中讲解)
2)多态的实现。
我们通过以下代码来讲解Class对象在多态中的应用
package demo;
public class ClassObjectDemo1 {
/*定义两个具有继承关系的类,两个类内部有同一个方法的不同实现*/
public static class Person{
public void speak(){
System.out.println("i am a person");
}
}
public static class Coder extends Person{
public void speak(){
System.out.println("i am a coder");
}
}
/*定义了一个静态方法,静态方法会调用对应类型的speak方法*/
public static void speakByType(Person p){
p.speak();
}
public static void main(String[] args) {
Person p0 = new Coder();
speakByType(coder);
Person p1 = new Person();
speakByType(person);
}
}
运行结果
i am a coder
i am a person
我们定义的静态方法speakByType,显然编译器在编译这个方法的时候不能确定到底调用哪一个speak方法,需要依据对象的具体类型才能确定符号引用。
现在我们通过工具重点查看一下speakByType的字节码
public static void speakByType(demo.ClassObjectDemo1$Person p) {
/* L20 */
0 aload_0; /* p */
1 invokevirtual 16; /* void speak() */
/* L21 */
4 return;
}
我们发现里面出现了一条字节码调用语句 invokevirtual方法。而invokevirtual指令在执行时,首先会找到当前对象的类的Class对象,然后通过该Class对象查找sepak方法,如果通过该Class对象查找到了签名一致的的sepak方法就会调用它。每个Class对象都会持有表示父类的Class对象的引用(通过Class的getSuperClass方法获取),Object.class除外,自己想想为啥?否则从其父类的Class对象中继续查找签名一致的speak方法。显然如果没有找到,会沿着有继承关系的Class的路径继续向下查找,如果直到Object.class对象中还未找到就会抛出异常。
3)instace of和向上转型
当我们判断某个对象是否属于某个类时,比如 a instance of A,显然只要判断
a.getClass() == A.class && a.getClass().classLoader() == A.classLoader()即可,如果不满足就沿着getSuperClass的路径继续向下找,如果直到Object.class还不满足条件就返回false。同理在运行时,我们还能依据Class对象判断向上转型是否正确。
4.4 Class.class对象存在的意义是什么?
我们通常不会通过Class.class对象来间接访问forName方法和其它相应方法,而是直接使用该类的方法。所以一种可能的情况就是利用Class对象进行类型判断,即判断一个对象是不是Class对象还是普通对象(判断obj.getClass() == Class.class是否成立)。另一种可能就是保持概念的完整性,每一个类都有一个Class对象与之对应。
4.5 假设B类继承了A类,那么B类的实例在内存中应该是什么样子的?
java语言的设计者考虑到对象向上转型等问题,每一个类的数据成员显然要按照继承关系的先后顺序排列,同时考虑执行效率,还存在数据对齐等问题。
4.6 Jav