您的位置:首页 > 编程语言 > Java开发

java编程思想笔记14-类型信息

2018-01-17 21:12 483 查看
运行时类型信息使得你可以在程序运行时发现和使用类型信息.

14.1 为什么需要RTTI(runtime type information)

代码操作的是基类的引用,而调用的是子类的方法,这种情况在编译器是不知道该引用的具体类型,所以需要运行时类型信息.

14.2 Class对象

要理解rtti在java中的工作远离,首先必须直到类型信息在运行时是如何标识的.这项工作是由称为Class对象的特殊对象完成的,它包含了与类有关的信息.

类是程序的一部分,每个类都有一个Class对象.换言之,每当编写并编译了一个新类,就会产生一个Class对象,(更恰当的说,是被保存在一个同名的.class文件中).

所有的类都是在对其第一次使用时,动态加载到jvm中的.当程序创建第一个对类的静态成员的引用时,就会加载这个类.这个证明构造器也是类的静态方法,即使在构造器之前并没有使用static关键字.
java程序在它开始运行之前并非被完全加载,其各个部分实在必需时才加载的.这一点与许多传统的语言都不同.

14.2.1 类字面常量

java还提供了另一种方法来生产对Class对象的引用,即使用类字面常量.
A.class
更简单,更安全.因为它会在编译期收到检查.并且它根除了对forName()方法的调用给,更高效.
类字面常量不仅可以应用于普通的类,也可以应用于接口,基本数据类型,另外,对于基本数据类型的包装器类,还有一个标准字段TYPE.(获取数据类型)
使用.class的特点
加载,这是由类加载器执行的.该步骤将查找字节码(通常在classpath所指定的路径中查找,但者并非是必要的),并从这些字节码中创建一个Class对象.
链接,在链接阶段将验证类中的字节码,为静态域分配存储空间,并且如果必需的话,将解析这个类创建其他类的引用.
初始化.如果该类具有超类,则对其初始化,执行静态初始化和静态初始化块.
初始化被延迟到了堆静态方法(构造器隐式是静态的)或者非常数静态域进行首次引用时才执行.

public class MainTest {

public static void main(String[] args) throws ClassNotFoundException {

System.out.println("==============A===============");
Class<A> aClass = A.class;
System.out.println("A.class excute successfully");
int a = A.a;
System.out.println("==============B===============");
Class<B> bClass = B.class;
System.out.println("B.class excute successfully");
int b = B.b;
System.out.println("B.b excute");
int bb = B.bb;
System.out.println("B.bb excute");
System.out.println("==============C===============");
Class<?> cClass = Class.forName("zhb.C");
System.out.println("C.class excute successfully");
int c = C.c;
}
}

class A {
static int a = 1;

static {
System.out.println("A.static excute");
}
}

class B {
static final int bb = 45;
static final int b = new Integer("152");

static {
System.out.println("B.static excute");
}
}

class C {

static int c = 3;

static {
System.out.println("C.static excute");
}
}
输出:
==============A===============
A.class excute
a7c0
successfully
A.static excute
==============B===============
B.class excute successfully
B.static excute
B.b excute
B.bb excute
==============C===============
C.static excute
C.class excute successfully

初始化有效地实现了尽可能的惰性,对A.class可以看出,它不会在获得类的引用就引发初始化.但是为了产生Class引用,Class.forName()立即就进行了初始化,就像C的引用创建中所看到的.

如果是一个static final值,它是"编译器常量",就像B中所看到那样,这个值不需要对B进行初始化就可以获得它的值.但是,如果只是讲一个域设置为static和final的,还不足以确保这种行为,例如我们对B.b的访问会强制进行类的初始化,因为它不是一个编译期的常量.

在.class情况下,要看一个类是否被初始化,关键在于调用的的是否是编译期常量,如果是,类就不会被初始化,反之.

14.2.2 泛化的Class引用

Class对象总是指向某个Class对象,它可以创建类的实例,并包含作用于这些实例的所有方法代码.它还包含该类的静态成员.

java5将它的类型变得更具体了一些,使用了泛型.
Class<A> aClass = A.class;
 
Class<?> 标识您宾菲是碰巧或者是由于疏忽,而是你本来就是用了非具体的版本.
 
在这里遇见了<? super E> <? extends E>
下面是示例代码
public class MainTest {

public static void main(String[] args) throws ClassNotFoundException {
List<A> list = new ArrayList();
list.add(new A());
extendsE(list);

List<B> listB = new ArrayList();
listB.add(new B());
superE(listB);
}
public static void extendsE(List<? extends B> list) {
}

public static void superE(List<? super A> list) {
}
}

class A extends B {
}

class B  {
}

14.2.3 新的转型语法

cast方法
public class MainTest {

public static void main(String[] args) throws ClassNotFoundException {
Build b = new House();
Class<House> houseClass = House.class;
House cast = houseClass.cast(b);
System.out.println(cast);
}
}
class Build{}
class House extends Build{}

14.3 类型转换前先做检查

instanceof
对instanceof有比较严格的限制:只可将其与命名类型进行比较,而不能与Class对象做比较.

14.3.1 使用字面常量

使用.class,编译期间会收到检查,无异常信息

14.3.2 动态的instanceof

Class.isInstanceof()

14.3.3 递归计数

递归方法找出类型的父类个数.

14.4 注册工厂

(工厂设计模式中详解)

14.5 instanceof与Class的等价性

在查询类型信息时,以instanceof的形式与直接比较Class对象有一个很重要的差别.

14.6 反射:运行时的类信息

反射机制并没有什么神奇之处.当通过与一个位置类型的对象打交道时,jvm只是简单的检查这个对象,看它属于哪个特定的类.在用他做其他事情之前必须先知道哪个类的Class对象.因此,那个类的.class文件对于jvm来说必须的可获得的;要么在本地机器上,要么可以通过网络取得.所以RTTI和反射之间真正的区别只在于,堆RTTI来说,编译器在编译时打开和检查.class文件.而对于反射来说,.class文件在编译时是不可获取的,所以在运行时打开和检查.class文件.

14.6.1 类方法提取器

Class<House> houseClass = House.class;
    Method[] methods = houseClass.getMethods();

14.7 动态代理

(设计模式详解)

14.8 空对象null

通常空对象都是单例,因此,这里将其作为静态final实例创建.我认为它在静态常量区存储了唯一的空间.

14.9 接口与类型信息

Class<House> houseClass = House.class;
    AnnotatedType[] annotatedInterfaces = houseClass.getAnnotatedInterfaces();
反射可以调用私有方法.

但是,final域实际上在遭遇修改时是安全的.运行时系统在会抛出异常.本人使用java1.8

14.10 总结

对于初级开发程序员而言,很难让它们不把程序组织成一系列的switch,我们应该更好的使用RTTI者一点,但是这样就在代码开发和维护中损失了多态的重要价值.面向对象编程语言的目的是让我们凡是可以使用的地方都使用多态机制,只在必须的时候使用RTTI.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: