美文网首页
《Thinking in Java》学习笔记——14章类型信息

《Thinking in Java》学习笔记——14章类型信息

作者: zpauly | 来源:发表于2016-08-14 22:41 被阅读0次

    Class对象

    1.每当编写并编译了一个新类,就会产生一个Class对象(更恰当地说,是被保存在一个同名的.class文件中)。为了生成这个类的对象,运行这个程序的Java虚拟机将使用被称为“类加载器”的子系统。
    2.所有的类都是在对其第一次使用时,动态加载到JVM中的。当程序创建第一个对类的静态成员的引用时,就会加载这个类。这个证明构造器也是类的静态方法,因此使用new操作符创建类的新对象也会被当作对类的静态成员的引用。
    3.Java程序在它开始运行之前并非被完全加载,其各个部分是在必需时才加载的
    4.Class对象仅在需要的时候才被加载,static初始化是在类加载时进行的
    5.无论如何,只要你想在运行时使用类型信息,就必需首先获得对恰当的Class对象的引用。Class.forName()就是实现此功能的便捷途径,因为你不需要为了获得Class引用而持有该类型的对象。如果已经拥有了一个类型的对象,那就可以通过调用getClass()方法来获取Class引用
    6.getName()来产生全限定的类名;getSimpleName()来产生不含包名的类名;getCanonicalName()来产生全限定的类名;isInterface()方法可以告诉你这个Class对象是否表示一个接口;getInterfaces()方法返回的是Class对象,它们表示在Class对象中所包含的接口;getSuperclass()方法可用来查询基类,将返回用来进一步查询的Class对象;newInstance()可用来创建Class对象所对应的类,但必须带有默认构造器

    一.类字面常量

    1.Java还可以使用类字面常量——“.class”的形式来生成对Class对象的引用。
    2.类字面常量不仅可以应用于普通的类,也可以应用于接口、数组以及基本数据类型。
    3.与通过Class中的方法创建对Class对象的引用不同的是,通过“.class”的方法创建不会自动地初始化该Class对象,初始化被延迟到了对静态方法(包括构造器)或者非常数静态域进行首次引用的时候才会执行
    4.使用类需要包括的三个步骤:
    (1).加载,这是由类加载器执行的。该步骤将查找字节码,并从这些字节码中创建一个Class对象。
    (2).链接。在链接阶段将验证类中的字节码,为静态域分配存储空间,并且如果必须的话,将解析这个创建的对其他类的引用。
    (3).初始化。如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块。
    5.如果一个static final值是“编译期常量”,那么这个值不需要对类进行初始化就可以被读取。如果一个static域不是final的,那么在对它访问时,总是要求在它被读取之前,要先进行链接(为这个域分配存储空间)和初始化(初始化该存储空间)。

    class Initable {
        static final int staticFinal = 47;//static final的编译期常量,使用时不用初始化
        static final int staticFinal2 = ClassInitaialization.rand.nextINt(1000);//虽然是static final的,但是不是编译期常量,使用时需要初始化
        static int staticFinal3 = 47;//不是final的,使用时需要进行初始化
    }
    
    二.泛化的Class引用

    1.Class引用表示的就是它所指向的对象的确切类型,而该对象便是Class类的一个对象。
    2.使用泛型语法,实现对Class引用所指向的Class对象的类型进行限定。

    public class GenericClassReference {
        public static void main(String...args) {
            Class intClass = int.class;
            Class<Integer> genericIntClass = int.class;
            genericIntClass = Integer.class; // Same thing
            intClass = double.class; // Success
            //genericIntClass = double.class; // Illegal
        }
    }
    

    为了在使用泛化的Class引用时放松限制,可以使用通配符。

    Class<?> intClass = int.class;
    intClass = double.class;
    

    3.当将泛型语法用于Class对象的时候:newInstance()将返回该对象的确切类型,而不仅仅只是基本的Object

    三.新的转型语法

    1.JavaSE5中添加了用于Class引用的转型语法,即cast()方法:

    Building b = new House();
    Class<House> houseType = House.class;
    House h = houseType.cast(b); // 与 h = (House) b; 效果相同
    

    类型转换前先做检查

    1.使用关键字instanceof,它返回一个布尔值,告诉我们对象是不是某个特定类型的实例:

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

    进行向下转型前,如果没有其他信息告诉你对象是什么类型,那么使用instanceof是非常重要的,否则会得到一个ClassCastException异常。
    2.对instanceof有比较严格的限制:只可将其与命名类型进行比较,而不能与Class对象作比较。如果程序中编写了许多的instanceof表达式,就说明你的设计可能存在瑕疵。

    一.动态的instanceof

    1.isInstance()方法不用与命名类型进行比较,而可以直接与Class对象进行比较

    Pet pet = new Dog();
    if (pet.isInstance(Dog.class)))
        ((Dog) pet).bark();
    

    2.使用isAssignableFrom()来执行运行时检查,以校验你传递的对象确实属于我们感兴趣的继承结构。

    二.instanceof与Class的等价性

    1.在查询类型信息的时候,以instanceofisInstance()的形式与直接比较Class对象有一个很重要的区别。instanceof保持了类型的概念,它指的是“你是这个类吗,或者你是这个类的派生类吗?”而如果用“==”比较实际的Class对象,就没有考虑继承——它或者是这个确切的类型,或者不是

    反射:运行时的类信息

    1.Class类与java.lang.reflect类库一起对反射的概念进行了支持,该类库包括了FieldMethod以及Constructor类(每个类都实现了Member接口)。这些类型的对象是由JVM在运行时创建的用以表示未知类里对应的成员。
    2.RTTI和反射之间真正的区别只在于,对RTTI来说,编译器在编译时打开和检查.class文件。而对于反射机制来说,.class文件在编译时是不可获取的,所以是在运行时打开和检查.class文件。

    类方法提取器

    1.ClassgetMethods()getConstructors()方法分别返回Method对象的数组和Constructor对象的数组。
    2.Class.forName()生成的结果在编译时是不可知的,因此所有的方法特征签名信息都是在执行时被提取出来的。反射机制提供了足够的支持,使得能够创建一个编译时完全未知的对象,并调用此对象的方法。

    接口与类型信息

    1.通过使用反射,即使实现包的访问权限,仍旧可以到达并调用所有方法,甚至是private方法。如果知道方法名,就可以在其Method对象上调用setAccessible(true),然后通过invoke()方法来使用该方法。
    2.final域实际上在遭遇修改时是安全的。运行时系统会在不抛异常的情况下接受任何修改尝试,但实际上不会发生任何修改。

    相关文章

      网友评论

          本文标题:《Thinking in Java》学习笔记——14章类型信息

          本文链接:https://www.haomeiwen.com/subject/xmcysttx.html