• Java 类型信息


    《Thinking in Java 4th》第14章 类型信息

    运行时类型信息(Run-Time Type Identification)使得你可以在程序运行时发现和使用类型信息。

    14.1 为什么需要RTTI

    Circle,Square,Triangle三个类都继承自抽象类Shape,现有一个List<Shape>的数组,存的是Circle,Square,Triangle的对象,当你拿出一个对象时,你只知道它是Shape类,但不知道它的具体类型。使用RTTI,可以查询某个Space引用所指向的对象的具体类型。

    14.2 Class对象

    Java使用Class对象来执行其RTTI,它包含了与类有关的信息。每一个类都有一个Class对象,被保存在同名的.class文件中。为了生成这个类的对象,运行这个程序的JVM将使用被称为“类加载器”的子系统。

    所有的类都是在对其第一次使用时,动态加载到JVM中的。当程序创建第一个对类的静态成员的引用时,就会加载这个类。因此,Java程序在它开始运行之前并未被完全加载。其各部分是在必需时才加载的。

    类加载器首先检查这个类的Class对象时候已经加载,如果尚未加载,默认的类加载器就会根据类名查找.class文件。一旦某个类的Class对象被载入内存,它就被用来创建这个类的所有对象。

    package typeinfo;
    
    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 create Candy");
            try {
                Class.forName("typeinfo.Gum");//书上没有加包名,亲测需要加
            } catch (ClassNotFoundException e) {
                System.out.println("Couldn't find Gum");
            }
            System.out.println("After Class.forName("Gum")");
            new Cookie();
            System.out.println("After create Cookie");
        }
    }
    typeinfo.SweetShop.java

    forName()是取得Class对象引用的一种方法,包含目标类的文本名的String作为输入,返回Class对象的引用。

    package typeinfo.toys;
    
    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());//Canonical:规范化=_=
        }
        public static void main(String[] args) {
            Class c = null;
            try {
                c = Class.forName("typeinfo.toys.FancyToy");
            } catch (ClassNotFoundException e) {
                System.out.println("Can't find FancyToy");
                System.exit(1);
            }
            printInfo(c);
            for (Class face: c.getInterfaces()) {
                printInfo(face);
            }
            Class up = c.getSuperclass();
            Object obj = null;
            try {
                // 需要默认构造函数 即无参的构造函数 否则会报错
                obj = up.newInstance();
            } catch (InstantiationException e) {
                System.out.println("Cannot instantiate");
                System.exit(1);
            } catch (IllegalAccessException e) {
                System.out.println("Cannot access");
                System.exit(1);
            }
            printInfo(obj.getClass());
        }
    }
    typeinfo/toys/ToyTest.java

    通过对象.getClass()可以直接得到Class引用。

    可以看到Class包含很多有用的方法。可以得到接口信息,父类信息,以及类名等。

    newInstance()创建的类,必须带有默认构造器。

    练习10.编写一个java程序,用来判断一个char数组是个基本类型还是个对象?

    属于对象,Java基本类型只有 boolean, byte, short, char, int, long, float, double 8种,Java中数组都是对象。

    package typeinfo;
    
    public class Exercise10 {
        public static void main(String[] args) {
            char[] ch = new char[10];
            Class cc = ch.getClass();
            typeinfo.toys.ToyTest.printInfo(cc);
            System.out.println(ch instanceof Object);
        }
    }
    /**
    Class name: [C is interface? [false]
    Simple name: char[]
    Canonical name: char[]
    true
    */
    Exercise10

    14.2.1 类字面常量

    通过类名.class也可以得到Class对象的引用。当使用".class"来创建对Class对象的引用时,不会自动地初始化该Class对象。

    基本类型.class = 基本类型的包装类.class

    package typeinfo;
    
    import java.util.*;
    
    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 staticNotFinal = 147;
        static { System.out.println("Initializing Initable2"); } 
    }
    
    class Initable3 {
        static int staticNotFinal = 74;
        static { System.out.println("Initializing Initable3"); } 
    }
    
    public class ClassInitialization {
        public static Random rand = new Random(47);
        public static void main(String[] args) throws Exception {
            Class initable = Initable.class;
            System.out.println("After create Initable ref");
            System.out.println(initable.isInterface());
            System.out.println(Initable.staticFinal);
            System.out.println(Initable.staticFinal2);
            System.out.println(Initable2.staticNotFinal);
            Class initable3 = Class.forName("typeinfo.Initable3");
            System.out.println("After create Initable3 ref");
            System.out.println(Initable3.staticNotFinal);
        }
    
    }
    typeinfo/ClassInitialization.java

    如果一个static final是“编译期常量”,那个这个值不需要对类初始化就可以读取。

    14.2.2 泛化的Class引用

    例: Class<Integer> intClass = int.class; 

    使用Class<?>优于Class。

    向Class添加泛型是为了提供编译期类型检查。

    感觉Java的泛型真神奇啊(= =)

    Class<? extends T> 我也就忍了 竟然还有 Class<? super T> up = TObj.getSuperclass();

    Object obj = up.newInstance(); <-- 得到的只是Object

    14.2.3 新的转型语法

    按作者的意思,这个并没有什么用……

    package typeinfo;
    
    class Building {}
    class House extends Building {}
    
    public class ClassCasts {
        public static void main(String[] args) {
            Building b = new House();
            Class<House> houseType = House.class;
            House h = houseType.cast(b);
            h = (House)b;    // ... or just do this.
        }
    }
    typeinfo/ClassCasts.java

    14.3 类型转换前先做检查

    如果你想向下转型,先用关键字instanceof检查

     if (x instanceof Dog) ((Dog)x).bark(); 

     //我发现作者有一种神奇的能力——把很简单的事情写成一大堆神烦的代码……就算写代码少写几行不行吗啊魂淡……一定是按字数付稿费……

     //我把那一堆代码抄完我都懒得复制过来了,头疼= =

    使用 a instanceof AClass 就必须写AClass的类名。但是没有储存类名的容器呀,所以就想到改进方法,使用Class对象,然后使用它的Class对象.isInstanceof(目标对象)就可以辣!

    isAssignableFrom   是用来判断一个类Class1和另一个类Class2是否相同或是另一个类的超类或接口。 

    通常调用格式是 :  Class1.isAssignableFrom(Class2)  

    14.4 注册工厂

     没看懂(捂脸 先过……

    14.5 instanceof 与 Class 的等价性

    a instanceof B 等价于 B.class.isInstance(a) 当对象a的类型与B相同或者是其派生类时,返回true

    a.getClass()==B.class 等价于 a.getClass.equals(B.class) 当且仅当对象a的类型与B相同时,返回true

    14.6 反射:运行时的类信息

    RTTI和反射之间真正的区别只在于,对RTTI来说,编译器在编译时打开检查.class文件。而对于反射机制来说,.class文件在编译时是不可获取的,所以是在运行时打开时和检查.class文件。

    Class<?> c = Class.forName(args[0]);
    Method[] methods = c.getMethods();
    Constructor[] ctors = c.getConstructors();

    args[0]在编译时是不可知的,但是通过反射,你可以知道类中的方法以及构造器。

    事实上反射可以获得类的所以域和方法,甚至包括private。

    设计模式什么的,真的看不懂啊~~~>_<~~~

  • 相关阅读:
    读大道至简有感
    动手动脑7
    大道至简第七章第八章
    课后作业(接口与继承)
    大道至简第六章
    课后题以及动手动脑1 3题
    大道至简第五章
    课后作业和动手动脑(字符串)
    大道至简第四章
    课后作业及动手动脑
  • 原文地址:https://www.cnblogs.com/wenruo/p/6017331.html
Copyright © 2020-2023  润新知