1、java主要有两种方式在运行时识别对象和类的信息:RTTI和“反射”机制。
2、类加载器系统实际上可以包含一条类加载器链,但是只有一个原生态加载器,它是JVM实现的一部分。原生态加载器加载的是所谓的可信类,包括Java API,它们通常都是从本地盘加载的。在这条链中,通常不需要添加额外的类加载器,但是如果你有需求,那么你有一种方式可以挂载额外的类加载器。
3、所有的类都是在对其第一次使用时,动态加载到JVM中。java程序再它开始运行之前并非完全加载,其各个部分是在必需时才加载。
4、使用Class.forName()加载类时,必需使用全限定名(包括包名)。
5、java还提供了“类字面常量”来获得Class对象的引用。即类A获取Class引用的方法为A.class。这样做不仅简单,安全(因为在编译期是就会检查),而且比forName的方法高效。对于基本类型的包装类,还有一个标准字段TYPE指向Class对象的引用。boolean.class等价于Boolean.TYPE。
6、 为了使用类而做的准备有三个步骤:
加载:由类加载器进行,该步骤将查找字节码,并从字节码中创建Class对象引用。
链接:验证类中的字节码,为静态域分配存储空间,并且如果必须的话,将解析这个类创建的对其他类的所有引用。
初始化:如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块。
public class Initable {
static final int staticFinal = 47;
static final int staticFinal2 = new Random().nextInt(100);
static {
System.out.println("Initable");
}
}
public class Initable2 {
static int staticNonFinal = 74;
static{
System.out.println("Initable2");
}
}
public class Initable3 {
static int staticNonFinal = 174;
static{
System.out.println("Initable3");
}
}
public class ClassInitialization {
public static void main(String[] args) throws Exception{
Class initable = Initable.class; //不会引起初始化
System.out.println("after creating Initable reference");
System.out.println(Initable.staticFinal); //引用编译器常量不会引起初始化
System.out.println(Initable.staticFinal2); //引起初始化
System.out.println(Initable2.staticNoFinal); //引用非编译期常量会引起初始化
Class initable3 = Class.forName("Initable3"); //默认会引起初始化
System.out.println("after creating Initable3 reference");
System.out.println(Initable3.staticNoFinal);//前面已经初始化此处不用再初始化
}
}
/* Output
After creating Initable ref
47
Initable
15
Initable2
74
Initable3
After creating Initable3 ref
174
*/
初始化有效地实现了尽可能的“惰性”。仅使用.class不会触发类的初始化。而调用Class.forName会导致类初始化。(比如Initable.class和Class.forName("com.yanguang.Initable3"))
如果一个类的变量是static final的
这个变量的值是编译器常量:这个值可以在类不被初始化时就可以读取。(Initable.staticFinal)
这个变量的值动态获得(比如通过随机函数生成),那么对这个值得调用会导致类的初始化。(Initable.staticFinal2)
如果一个static域不是final的,那么在访问它时,总是要求在它被读取前,先进行链接(分配空间)和初始化(初始化该空间)。(Initable2.staticNonFinal)