【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
为什么需要RTTI
面向对象编程的主要目的是: 让代码只操纵对基类的引用, 这样方便扩展. 在运行期间, 通过RTTI来确定具体的对象, 并通过多态来确定具体的方法调用.
Class对象
Class对象: 包含一个类有关的信息的特殊对象.
对于类加载器来说, 它首先加载原生类, 如Java API. 其次, 如果一个类的静态成员被引用, 则进行动态加载(这从侧面说明构造函数实际上为static).
class A {
static {
System.out.println("Loading A.");
}
}
class B {
static {
System.out.println("Loading B.");
}
}
public class Test {
public static void main(String[] args) {
new A();
try {
Class.forName("B");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
当我们执行: javac Test.java时候, 会生成A.class, B.class, Test.class
但这里必须明确一点的是: 无论A, 还是B的class对象, 均未被载入内存. 只有当我们运行: java Test时候, 会实际调用A/B对象时候, 才会将其class对象载入内存中.
验证的方式也很简单: 如果一个class对象没有被加载, 那么类加载器会根据其类名寻找其.class对象进行加载. 我们将B.class文件删除, 则运行java Test时候直接报异常, 说class B并未寻找到.
关于Class对象, 我们可以使用Class.forName来创建, 也可以通过具体的对象: object.getClass()来获取. 以下例子是对Class对象的简单说明:
interface A{}
interface B{}
class C {
C() {}
}
class D extends C implements A, B {
D() {}
}
public class Test {
public static void printInfo(Class c) {
System.out.println("Class name: " + c.getName() + " is interface? [" + c.isInterface() + "]");
System.out.println("Simple name:" + c.getSimpleName());
System.out.println("Canonical name:" + c.getCanonicalName());
}
public static void main(String[] args) {
Class c = null;
try {
c = Class.forName("D");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
printInfo(c);
for (Class face: c.getInterfaces()) {
printInfo(face);
}
Class up = c.getSuperclass();
printInfo(up);
Object obj = null;
try {
obj = up.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
printInfo(obj.getClass());
}
}
对代码进行的方法进行解释:
c.getName(): 获取其类名.
c.isInterface(): 判断是否为接口.
c.getSimpleName(): 获取其不包含包名的类名
c.getCanonicalName(): 获取其全限定的类名(含包名)
c.getInterfaces(): 获取其所包含的接口.
c.getSuperClass(): 获取其父类的class对象.
up.newInstance(): 通过class对象调用默认构造函数新建一个对象.
类字面常量
使用FancyToy.class来生成class对象. 类字面常量不仅可以应用于普通类, 也可以应用于接口, 数组以及基本数据类型.
当使用".class"来创建对Class对象的引用时, 不会自动的初始化Class对象. 为了使用类而做了三个准备:
1. 加载: 由类加载器执行. 该步骤将查找字节码, 并从这些字节码中创建一个Class对象.
2. 链接: 在链接阶段将验证类中的字节码, 为静态域分配存储空间, 并且如果必须的话, 将解析这个类创建的对其他类的所有引用.
3. 初始化. 如果该类具有超类, 则对其初始化, 执行静态初始化器和静态初始化块.
import java.util.*;
class Initable {
static final int staticFinal = 47;
static final int staticFinal2 = Test.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 Test {
public static Random rand = new Random(47);
public static void main(String[] args) throws Exception {
Class initable = Initable.class;
System.out.println("After creating Initable ref");
System.out.println(Initable.staticFinal);
System.out.println(Initable.staticFinal2);
System.out.println(Initable2.staticNonFinal);
Class initable3 = Class.forName("Initable3");
System.out.println("After creating Initable3 ref");
System.out.println(Initable3.staticNonFinal);
}
}
输出:
After creating Initable ref
47
Initializing Initable
258
Initializing Initable2
147
Initializing Initable3
After creating Initable3 ref
74
类型转换时的检查
一般有三种RTTI格式:
1. 传统的强制类型转换.
2. 代表对象的类型的Class对象. 通过查询Class对象可以获取运行时所需的信息.
3. 使用instanceof, 它返回一个布尔值, 告诉我们对象是不是某个特定类型的实例.
针对Class对象, 有一个知识点是: class A extends B, 但A.class和B.clas并无任何的关联, 即不能将B.class的对象强制转换未A.class对象:
import java.util.*;
class A {}
class B extends A{}
class C extends B {}
public class Test {
public static void main(String[] args) throws Exception {
List<Class<? extends A>> list = new ArrayList<>();
list.add(A.class);
list.add(B.class);
list.add(C.class);
Class c1 = B.class;
// ERROR
// Class c2 = (A.class)c1;
System.out.println(list.get(1).newInstance() instanceof A);
System.out.println(list.get(1) instanceof Class);
}
}
这里有三个知识点:
1) <? extends A> 是泛型的一个语法, 代表所有继承于A的类型.
2) 类B继承于类A, 但B.class跟A.class并无任何的关联.
3) instanceof是对象实例之间的判断, 而Class本身也是对象, 所以可以使用instanceof来判断.
来源:oschina
链接:https://my.oschina.net/u/1017135/blog/717334