每个类都有一个Class对象。就是说,每当编写并且编译了一个新类,就会产生一个Class对象,被保存在一个同名的.class文件中。c
所有的类都是在第一次使用时,动态加载到JVM中的。比如使用new操作符创建类的新对象(构造器是默认类的静态方法)。
一旦某个类的Class对象被载入内存,它就被用来创建这个类的所有对象。
class Candy{
static {
System.out.println("Loading Candy");
}
}
class Gum{
static {
System.out.println("Loading Gum");
}
}
class Cookie{
static {
System.out.println("Loading Cookie");
}
}
public class SweetShop {
public static void main(String[] args) {
System.out.println("inside main");
new Candy();
System.out.println("After creating Candy");
try {
Class.forName("com.sangyu.test.test21.Gum");
} catch (ClassNotFoundException e) {
System.out.println("Could not find Gum");
}
System.out.println("After Class.forName(\"Gum\")");
new Cookie();
System.out.println("After creating Cookie");
}
}
上面例子中,每个类Candy、Gum、Cookie,都有一个static子句,该子句在类第一次被加载时执行。这时会有相应的信息打印出来,告诉我们这个类什么时候被加载了。
Class.forName("Gum")
这个方法是Class类的静态方法,需要用一个包含目标类的文本名(注意拼写和大小写)的String作为输入参数,返回的是Class对象的引用。这个方法还有一个其他的作用:如果类Gum没有被加载的情况,调用这个方法时就会被加载。在加载过程中,Gum的static子句被执行
package com.sangyu.test.test21;
interface HasBatteries{}
interface Waterproof{}
interface Shoots{}
class Toy{
Toy(){}
Toy(int i){}
}
class FancyToy extends Toy implements HasBatteries,Waterproof,Shoots{
FancyToy(){
super(1);
}
}
public class ToyTest {
static void printInfo(Class cc){
System.out.println("Class name: " + cc.getName() + " is interface? [" + cc.isInterface() + "]");
System.out.println("Simple name: " + cc.getSimpleName());
System.out.println("Canonical name: " + cc.getCanonicalName());
System.out.println("------------------------------------------------");
}
public static void main(String[] args) {
Class c = null;
try{
c = Class.forName("com.sangyu.test.test21.FancyToy"); //
} catch (ClassNotFoundException e) {
System.out.println("Can't find FancyToy");
}
printInfo(c);
for(Class face : c.getInterfaces()){
printInfo(face);
}
Class up = c.getSuperclass();
Object obj = null;
try {
obj = up.newInstance();
} catch (IllegalAccessException e) {
System.out.println("Can't instantiate");
} catch (InstantiationException e) {
System.out.println("Can't access");
}
printInfo(obj.getClass());
}
}
FancyToy 继承Toy并实现HasBatteries,Waterproof,Shoots接口,main()方法中创建了Class的引用,并将其指向了FancyToy。printInfo()方法中getName()获得全限定的类名,isInterface()判断是否为接口,getSimpleName()获得不含包名的类名,getCanonicalName()获得全限定的类名。main()方法中其他的方法比如getInterfaces()返回Class中所包含的接口,getSuperclass()获得基类,newInstance() 创建实例。在上面例子中,getSuperclass()获得的class引用指向了up,在创建实例时,Object的引用指向的是Toy对象。另外,使用newInstance() 来创建的类,必须带有默认的构造器
Java还提供了另一种方法来生成对Class对象的引用,对上述程序可以这么写:FancyToy.class; 这样做不仅要简单,而且更安全,因为它在编译时就会受到检查(因此不需要置于try语句块中),并且根除了对forName()方法的调用,所以也更高效。
当使用.class来创建对Class对象的引用时,不会自动地初始化该Class对象。
class Initable{
static final int staticFinal = 47;
static final int staticFinal2 = ClassInitialization.rand.nextInt(1000);
static {
System.out.println("Initializing Initable");
}
}
class Initable2{
static int staticNonFinal = 147;
static {
System.out.println("Initializing Initable2");
}
}
class Initable3{
static int staticNonFinal = 74;
static {
System.out.println("Initializing Initable3");
}
}
public class ClassInitialization {
public static Random rand = new Random(47);
public static void main(String[] args) throws ClassNotFoundException {
Class initable = Initable.class;
System.out.println("After creating Initable ref");
System.out.println(Initable.staticFinal);
System.out.println(Initable.staticFinal2);
System.out.println(Initable2.staticNonFinal);
Class initable3 = Class.forName("com.sangyu.test.test21.Initable3");
System.out.println("After creating Initable3 ref");
System.out.println(Initable3.staticNonFinal);
}
}
在上述例子中,Initable引用的创建不会引发初始化,Class.forName("Initable3") 立即进行了初始化。static final 修饰的值是编译器常量,Initable类没有初始化就可以访问,但staticFinal2并非编译器常量,所以在访问时强制进行类的初始化。Initable2.staticNonFinal 是一个static域不会final的,在对它访问时,要先进行链接(为这个域分配存储空间)和初始化(初始化该存储空间)。