Thinking in Java -- 类型信息RTTI

Thinking in Java – 类型信息

个人感觉 java 中的比较难的部分了,在看了些netty源码发现其实这块非常有用。

这章重点是RTTI和反射。先说下自己的理解
RTTI是运行时识别,在c++中是用virtual来实现的,在编译期会忽略对象的具体类型信息,假定我们已经知道,并在运行时具体识别。

Java反射机制实在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对 象的方法的功能称为Java的反射机制

Class对象

Class对象就是用来创建类的所有的“常规”对象的。Java使用Class对象来执行其RTTI.
Class对象是什么
Java是一门纯面向对象的语言,在Java中,一切都是对象,也就是class,对于每个类文件(每个对象)编译后我们都会生成一个.class文件(javac hello.java —> hello.class),*.class文件就是Class对象,在我们要第一次使用hello这个类文件时,JVM的类加载器会来加载hello.class文件,感觉Class对象就是一个模具,用来生产对象的。
注意:构造器也是静态方法

例子:

package onefour_chapter;

/** * Created by wwh on 15-8-7. */

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("Gum");
        }catch (ClassNotFoundException e){
            System.out.println("not found Gum");
        }
        System.out.println("After Class.forName(\"Gum\")");
        new Cookie();
        System.out.println("After create Cookie");
    }
}

Thinking in Java -- 类型信息RTTI_第1张图片
我运行完发现结果和书上不一致,原因是Class.forName()参数要求是完全限定名,我的代码在包中,前面加上包名字即可,如下
Class.forName(“包名.Gum”);

关键点:

static代码块

static修饰的代码块只在类被加载时执行并且仅会被执行一次,一般用来初始化静态变量和调用静态方法,不管你new多少次,在jvm的声明周期里一个类只被加载一次。
前面说构造器也是静态方法,所以test t = new test(),在第一次创建对象时jvm就会加载test类的Class对象(.class文件),Class对象就和其他对象一样,我们可以操控它的引用,forName()就是获取Class对象引用的一种方法。
Class.forName()返回对象的Class引用,从Class对象中我们可以获得非常多的信息从而在运行时来抉择一些事情,非常像c++的type_traits。注意使用newInstance()创造的类必须带有默认的构造器

static语句块
static语句块在类加载时执行。只被执行一次
在用单例模式写封装数据库连接池时,new一个数据库连接池就被我写在了static代码块中,保证在类加载时就初始化好连接池,且只初始化一次。(注意,访问静态常量时,如果编译器能计算出来,则不会加载)

类加载:在终端下Java加载.class文件,Java命令的作用是启动虚拟机,虚拟机通过流从磁盘上将字节码(.class文件)中的内容读入虚拟机,并保存起来的过程。

类字面常量

Java还提供了一种方法来生成Class对象的引用,即类字面常量,classname.class,比如hello.class,它更安全和高效也不用try语句包裹。所以一般我们不使用forName(),可用于类,接口,数组,基本类型。建议使用.class的形式,.class执行了“尽可能”懒惰初始化,而forName()立即就初始化
为了使用类而做的准备工作有3个,加载–>连接–>初始化

范化的Class引用

Class引用总是指向某个Class对象,它可以制造类的实例,并包含可作用于这些实例的所有方法代码。它还包含该类的静态成员,因此,Class引用表示的就是它所指向的对象的确切类型,该对象便是Class类的一个对象。
向Class引用添加范型语法原因仅仅是为了编译器类型检查,前面说了RTTI和反射都是运行时的,现在我们在编译时也做了一些限定,更加保证了运行的正确性
为了在使用范化的Class引用时放松限制,可以使用了通配符,也可是使用通配符和extends关键字相结合,创建一个范围。
Class

import java.util.ArrayList;
import java.util.List;

/** * Created by wwh on 15-8-7. */

class CountedInteger{
    static {
        System.out.println("Class 加载");
    }
    private static long counter;
    private final long id = counter++;
    public String toString() { return Long.toString(id); }
}

public class FilledList<T> {
    private Class<T> type;
    /* 构造函数,参数为被限定的Class对象,我们可以在实例化时来指定 */
    public FilledList(Class<T> type){ this.type = type; }
    public List<T> create(int nElements){
        List<T> result = new ArrayList<T>();
        try{
            for(int i = 0; i < nElements; i++){
                result.add(type.newInstance());
            }
        }catch (Exception e){
            throw new RuntimeException();
        }
        return result;
    }

    public static void main(String[] args){
        /* 执行限定的类型,并传递.class作为构造函数的参数 */
        FilledList<CountedInteger> fl = new FilledList<CountedInteger>(CountedInteger.class);
        System.out.println(fl.create(15));
    }

} 

RTTI在Java中的第三种形式是instanceof,它返回一个布尔值,告诉我们对象是不是某个特定的实例。
if(obj instanceof Dog) obj.exe();
instanceof有比较严格的限制,只能将其与命名类型进行比较,而不能与FClass对象作比较
Class.isInstance()方法提供了一种动态地测试对象的途径。

你可能感兴趣的:(java,Class)