java编程思想第四版第十四章 类型信息总结

时间:2023-02-22 22:34:34

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来实例化的类不许带有一个默认的构造器

 2. 类字面常量

  Toy.class

  • 所有的类都有类字面常量,普通类, 接口,数组,以及基本数据类型。
  • 使用 .class创建一个Class对象的引用时,不会自动初始化该Class对象,这个加载过程包括以下三步
    1. 加载。 由类加载器执行。该步骤将查找字节码,通常在classpath所制定的路径中查找,并从这些字节码中创建一个class对象。
    2. 链接:验证类中的字节码,为静态域分配存储空间。并且,如果必要的话,会解析这个类创建的对其他类的所有引用。
    3. 初始化:如果该类具有超类,则对其初始化。执行静态初始化构造器或者静态初始化代码块。。初始化的过程将会被延迟,直到对静态方法或者非常数静态域进行首次引用时才执行初始化。(注意:类构造器其实就是隐式的静态方法)
      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. 里面主要说了几点

      1. .class方式构造的对象的实例化会延迟
      2. 静态常量的调用不会触发实例化
      3. 非静态常量的调用会触发实例化.
      4. class.forName()会立即触发初始化