1. Class 对象:
- 所有的类都是在对其第一次使用的时候,动态加载到JVM中的。当程序创建第一个对类的静态成员的引用时,就会加载这个类。这说明构造器也是类的静态方法。即使在构造器之前并没有static关键字,这个类也会被加载。
- java程序在它开始运行之前并非完全被加载。其各个部分是在必要时才加载的。
- 类加载器的执行过程
1、 类加载器首先检查这个类的Class对象是否已经加载。如果尚未加载,默认的类加载器就会根据类名查找.class文件。
2、在这个类的字节码被加载是,他们会接收验证,以确保其没有被损坏。并且不包含不良代码。这时java中用于安全防范目的的措施之一。
3、一旦某个类被加载,他就用来创建这个类的所有对象。
下面这个例子说明了一下两点:
1. 类何时被加载
2. 如何加载
package net.mindview.typeinfo; /** * 首先, 下面的每一个类都有一个静态的代码块. * 这个代码块, 在类第一次被加载时执行。 * @author samsung * */ 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. 以后都是从这个类中创建实例对象 new Candy(); System.out.println("After creating Candy!"); try { //Class.forName 获取对象引用的一种方法.参数是类的全限定文件名. 返回值是一个Class对象的引用. //如果Gum没有被加载, 那么这个类就会被加载. //使用类加载器加载类Gum, 在第一次加载Gum时, 也会主动去加载Gum这个类, 以后就从这个类中创建实例. Class.forName("Gum"); } catch (ClassNotFoundException e) { System.out.println("Could`t find Gum"); } System.out.println("After Class.forName(\"Gum\")"); new Cookie(); System.out.println("After creating Cookie"); //这个例子展示, 第二次实例化Cookie是, static静态代码块没有被再次执行, 它只会在第一次加载时执行. new Cookie(); System.out.println("After creating Cookie twice"); } }
- 下面展示如何通过Class来发现整个类的基本结构. 详细看代码注释
package net.mindview.typeinfo.toys; /** * 以下:展示了完全的获取一个类的完整的继承结构. * @author samsung * */ 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){ /** * 这里调用了Class的一些基本方法 * cc.isInterface(): 是接口么 * cc.getSimpleName(): 类名, 不含路径 * cc.getCanonicalName(): 全限定类名 */ 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()); } public static void main(String[] args) { Class c = null; try { //这里必须使用全限定名 c = Class.forName("net.mindview.typeinfo.toys.FancyToy"); } catch (ClassNotFoundException e) { System.out.println("Can`t find FancyToy"); System.exit(1); } printInfo(c); System.out.println(); /** * c.getInterfaces(): 获取这个类已继承的所有的接口 */ for(Class face: c.getInterfaces()){ //打印接口类 printInfo(face); System.out.println(); } /** * c.getSuperclass(): 获取这个类的父类 */ Class up = c.getSuperclass(); Object obj = null; try { //将父类实例化 //使用newInstance来实例化的类不许带有一个默认的构造器 obj = up.newInstance(); } catch (InstantiationException e) { System.out.println("Can`t instantiate"); System.exit(1); } catch (IllegalAccessException e) { System.out.println("Can`t instantiate"); System.exit(1); } //打印父类基本细腻 printInfo(obj.getClass()); } }
运行结果:
Class name:net.mindview.typeinfo.toys.FancyToy is interface ? [false] Simple name: FancyToy Canonical name: net.mindview.typeinfo.toys.FancyToy Class name:net.mindview.typeinfo.toys.HasBatteries is interface ? [true] Simple name: HasBatteries Canonical name: net.mindview.typeinfo.toys.HasBatteries Class name:net.mindview.typeinfo.toys.Waterproof is interface ? [true] Simple name: Waterproof Canonical name: net.mindview.typeinfo.toys.Waterproof Class name:net.mindview.typeinfo.toys.Shoots is interface ? [true] Simple name: Shoots Canonical name: net.mindview.typeinfo.toys.Shoots Class name:net.mindview.typeinfo.toys.Toy is interface ? [false] Simple name: Toy Canonical name: net.mindview.typeinfo.toys.Toy
总结:
-
cc.isInterface(): 是接口么
-
cc.getSimpleName(): 类名, 不含路径
-
cc.getCanonicalName(): 全限定类名
- c.getInterfaces(): 获取这个类已继承的所有的接口
-
- c.getSuperclass(): 获取这个类的父类
- 使用newInstance来实例化的类不许带有一个默认的构造器
- c.getSuperclass(): 获取这个类的父类
2. 类字面常量
Toy.class
- 所有的类都有类字面常量,普通类, 接口,数组,以及基本数据类型。
- 使用 .class创建一个Class对象的引用时,不会自动初始化该Class对象,这个加载过程包括以下三步
- 加载。 由类加载器执行。该步骤将查找字节码,通常在classpath所制定的路径中查找,并从这些字节码中创建一个class对象。
- 链接:验证类中的字节码,为静态域分配存储空间。并且,如果必要的话,会解析这个类创建的对其他类的所有引用。
- 初始化:如果该类具有超类,则对其初始化。执行静态初始化构造器或者静态初始化代码块。。初始化的过程将会被延迟,直到对静态方法或者非常数静态域进行首次引用时才执行初始化。(注意:类构造器其实就是隐式的静态方法)
package net.mindview.typeinfo; import java.util.Random; 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 { //下面这句话执行完,并没有对Initable这个类进行初始化. Class initable = Initable.class; System.out.println("创建Initable引用之后"); //没有触发初始化---Initable.staticFinal是一个常数, 所以不会触发初始化 System.out.println(Initable.staticFinal); System.out.println("-------1----------"); //触发初始化 -- Initable.staticFinal2是引用常数, 触发初始化 System.out.println(Initable.staticFinal2); System.out.println("---------2--------"); //触发初始化---Initable2.staticNonFinal非常数静态域, 调用后会触发初始化 System.out.println(Initable2.staticNonFinal); System.out.println("---------3--------"); //执行Class.forName的时候, 会将对象初始化. Class initable3 = Class.forName("net.mindview.typeinfo.Initable3"); System.out.println("创建Initable3引用后"); System.out.println("---------4--------"); System.out.println(Initable3.staticNonFinal); } }
结果运行:
创建Initable引用之后 47 -------1---------- Initializing Initable 258 ---------2-------- Initializing Initable2 147 ---------3-------- Initializing Initable3 创建Initable3引用后 ---------4-------- 74
详细看这个demo. 里面主要说了几点
- .class方式构造的对象的实例化会延迟
- 静态常量的调用不会触发实例化
- 非静态常量的调用会触发实例化.
- class.forName()会立即触发初始化