前言
运行时类型信息(RTTI:Runtime Type Information)使得我们可以在程序运行时发现和使用类型信息。
Java在运行时识别对象和类的信息的方式:
(1)一种是RTTI,它假定我们在编译时已经知道了所有的类型。
(2)另一种是反射机制,它允许我们在运行时发现和使用类的信息。
为什么需要RTTI
以使用了多态的类层次结构的例子举例:
如上图,泛型是基类Shape,而派生出来的具体类有Circle,Square和Triangle。
这是一个类层次结构图,基类位于顶部,而派生类向下拓展。
代码值操纵对基类的引用,如果添加一个新类来拓展程序也不会影响原来的代码(可拓展性),如图中Shape接口中动态绑定的draw()方法,目的就是使用泛化的Shape引用来调用draw()。draw()方法在所有派生类中都会被覆盖,由于是draw()方法是被动态绑定的,所以通过泛化的Shape引用来调用,也能产生正确的行为,即多态。
动态绑定:http://beginnersbook.com/2013/04/java-static-dynamic-binding/
http://www.javatpoint.com/static-binding-and-dynamic-binding
通常,我们会创建一个具体对象(本例中,Circle,Square或Triangle),然后把它向上转型为Shape(忽略对象的具体类型),并在后面的程序中使用匿名(即不知道具体类型)的Shape引用。
Shape shape = new Circle();
//Shape对象放入数组时会向上转型,转型为Shape且丢失了Shape对象的具体类型
//对数组而言,它们只是Shape类的对象
List<Shape> shapeList = Arrays.asList(new Circle(),new Square(),new Triangle());
//从数组中取出元素时,这种容器(实际上将所有事物都当作Object持有)会自动将结果转型为Shape。
for(Shape shape:shapeList){
shape.draw();
}
Java中,所有的类型转换都是在运行时进行正确性检查的,也就是RTTI(Runtime Type Information)名字的含义:在运行时,识别一个对象的类型。
通常,我们希望大部分代码尽可能地少了解对象的具体类型,而是只与对象家族中的一个通用表示打交道。这样,代码更容易写,更容易读,更便于维护,设计也更容易实现,理解和改变。所以,多态是面向对象编程的基本目标。
使用RTTI,可以查询某个Shape引用所指向的对象的确切类型,然后选择或者剔除特例。
Class对象
理解RTTI在Java中工作原理——>知道类型信息在运行时是如何表示的——>由Class对象的特殊对象完成的
Class对象:包含了与类有关的信息,Class对象是用来创建类的所有的“常规”对象的。Java使用Class对象来执行其RTTI。
编写并编译一个新类——>产生一个Class对象(被保存在一个同名的.class文件中)——>为了生成这个类的对象,运行这个程序的JVM将使用被称为"类加载器"的子系统。
参考学习:Java类加载器
Java程序在它开始运行之前并非被完全加载,其各个部分是在必需时才加载的
Class对象和其他对象一样,我们可以获取并操作它的引用。
//这个方法是Class类(所有Class对象都属于这个类)的一个静态成员。
//是取得Class对象的引用的一种方法。用一个包含目标类的文本名的字符串作为输入参数,返回一个Class对象的引用。
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
Class中包含很多有用的方法,如下:
//全限定类名
getName();
//判断是否是接口
isInterface();
//获取不含包名的类名
getSimpleName();
//获取全限定的类名
getCanonicalName();
//获取直接基类
getSuperClass();
//获取包含的接口
getInterfaces();
//获取实例
newInstance();
类字面常量
Java除了forName()获取Class对象引用外,还可通过使用类字面常量
FancyToy.class;
//这样使用简单,安全,编译时会受到检查(不需要trycatch语句的使用)
类字面常量不仅可以应用于普通的类,更可以应用于接口,数组以及基本数据结构。(对于基本数据类型的包装器类,它有一个标准字段TYPE。TYPE字段是一个引用,指向对应的基本数据类型的Class对象)
//等价于,一般直接使用".class"的形式,以保持与普通类的一致性。
boolean.class ======Boolean.TYPE
void.class=========Void.TYPE
char.class=========Character.TYPE
使用".class"来创建对Class对象的引用时,不会自动初始化Class对象。
为了使用类而做的准备工作有:
1.加载(由类加载器执行,查找字节码并从中创建一个Class对象)
2.链接 (验证类中的字节码,为静态域分配存储空间)
3.初始化(若该类具有超类,则对其初始化,执行静态初始化器和静态初始化块)
泛化的Class引用
Class引用指向某个Class对象,可制造类的实例,并包含可作用于这些实例的所有方法代码,还包含该类的静态成员。
Java SE5允许对Class引用所指向的Class对象的类型进行限定(通过泛型语法)
Class intClass = int.class;
//通过泛型语法,可让编译器强制执行额外的类型检查
Class<Integer> genericIntClass = int.class;
//使用了通配符?(表示任何事物)
Class<?> intClass = int.class;
向Class引用添加泛型语法的原因仅仅是为了提供编译期类型检查。
类型转换前先做检查
RTTI的几种表现形式:
1.传统的类型转换,如"(Shape)"。由RTTI确保类型转换的正确性,若执行了一个错误的类型转换,抛出ClassCastException异常。(Java中,此操作要执行类型检查,有向上转型和向下转型两种)
2.代表对象的类型的Class对象。通过查询Class对象可以获取运行时所需的信息。
3.关键字instanceof,它返回一个布尔值,告诉我们对象是不是某个特定类型的实例。
if(x instanceof Dog)
((Dog)x).bark();
动态的instanceof
Class.isInstance()方法提供了一种动态测试对象的途径。
instanceof与Class的等价性
class Base{
}
class Derived extends Base{
}
public class FamilyVsExactType {
static void test(Object x){
System.out.println("x type:"+x.getClass());
System.out.println("x instanceof Base"+(x instanceof Base));
System.out.println("x instanceof Derived"+(x instanceof Derived));
System.out.println("Base.isInstance(x)"+Base.class.isInstance(x));
System.out.println("Derived.isInstance(x)"+Derived.class.isInstance(x));
System.out.println("x.getClass()==Base.class"+(x.getClass()==Base.class));
System.out.println("x.getClass()==Derived.class"+(x.getClass()==Derived.class));
System.out.println("x.getClass().equals(Base.class)"+(x.getClass().equals(Base.class)));
System.out.println("x.getClass().equals(Derived.class)"+(x.getClass().equals(Derived.class)));
}
public static void main(String[] args) {
test(new Base());
test(new Derived());
}
}
//输出结果
//x type:class com.ijtsai.Base
//x instanceof Basetrue
//x instanceof Derivedfalse
//Base.isInstance(x)true
//Derived.isInstance(x)false
//x.getClass()==Base.classtrue
//x.getClass()==Derived.classfalse
//x.getClass().equals(Base.class)true
//x.getClass().equals(Derived.class)false
//x type:class com.ijtsai.Derived
//x instanceof Basetrue
//x instanceof Derivedtrue
//Base.isInstance(x)true
//Derived.isInstance(x)true
//x.getClass()==Base.classfalse
//x.getClass()==Derived.classtrue
//x.getClass().equals(Base.class)false
//x.getClass().equals(Derived.class)true
//
instanceof保持了类型的概念,指的是“你是这个类吗,或者你是这个类的派生类吗”
"=="则是比较的实际的Class对象,没有考虑继承(要么是这个确切的类型,要么不是)
反射:运行时的类信息
反射相关教程:
1.https://docs.oracle.com/javase/tutorial/reflect/
2.http://*.com/questions/37628/what-is-reflection-and-why-is-it-useful
3.http://www.infoq.com/cn/articles/cf-java-reflection-dynamic-proxy
4.http://www.cnblogs.com/rollenholt/archive/2011/09/02/2163758.html
反射提供了一种机制——用来检查可用的方法并返回方法名。
Class类和java.lang.reflect类库一起对反射的概念进行了支持。reflect类库中包含了Field,Method以及Constructor类(每个类都实现了Member接口)
//Field
//获得实现的接口或父类的属性
Class.getFields();
//本类的全部属性
Class.getDeclaredFields();
//Constructor
Class.getConstrutors();
//Method
Class.getMethod();
下面通过一个例子来学习reflect类库中各种类的用法。
package com.ijtsai;
import java.lang.reflect.*;
/**
* Created by JohnTsai on 15/10/5.
*/
class Vehicle {
public int speed;
}
interface ride {
void left();
void right();
void speedup();
void speeddown();
}
class Car extends Vehicle implements ride {
private String Name;
private String Color;
protected int height;
public double price;
public Car() {
System.out.println("Car()");
}
public Car(String color, String name) {
this.Name = name;
this.Color = color;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public String getColor() {
return Color;
}
public void setColor(String color) {
Color = color;
}
@Override
public String toString() {
return "Car Color:" + this.Color + "Name:" + this.Name;
}
@Override
public void left() {
}
@Override
public void right() {
}
@Override
public void speedup() {
}
@Override
public void speeddown() {
}
}
public class ReflectDemo {
public static void main(String[] args) {
Class<?> clazz = null;
Car car = null;
try {
clazz = Class.forName("com.ijtsai.Car");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//获取类的实例
try {
car = (Car) clazz.newInstance();
car.setName("Benz");
car.setColor("Pink");
System.out.println(car);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
//获取类的属性
//All the public fields up the entire class hierarchy.
Field[] fields = clazz.getFields();
//Returns an array of {@code Field} objects reflecting all the fields
// declared by the class or interface represented by this
//{@code Class} object. This includes public, protected, default
//(package) access, and private fields, but excludes inherited fields.
Field[] declareFields = clazz.getDeclaredFields();
System.out.println("-------------getFields()-----------");
printField(fields);
System.out.println("\n-------------getDeclaredFields()-----------");
printField(declareFields);
//获取类的构造方法
Constructor<?> constructors[] = clazz.getConstructors();
Car car1 = null, car2 = null;
try {
car1 = (Car) constructors[0].newInstance();
car2 = (Car) constructors[1].newInstance("White", "VW");
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
System.out.println("\n" + car1);
System.out.println(car2);
System.out.println("\n-----getConstructors()------");
printConstructors(constructors);
//获取类的方法
Method[] methods = clazz.getMethods();
System.out.println("\n------getMethods()--------");
printMethods(methods);
}
/**
* 打印Field
*
* @param fields
*/
static void printField(Field[] fields) {
for (Field field : fields) {
//权限修饰符
int modifier = field.getModifiers();
String modifierStr = Modifier.toString(modifier);
//属性类型
Class<?> type = field.getType();
System.out.println(modifierStr + " " + type.getName() + " " + field.getName());
}
}
/**
* 打印Constructor
*
* @param constructors
*/
static void printConstructors(Constructor[] constructors) {
for (Constructor constructor : constructors) {
Class<?> parameter[] = constructor.getParameterTypes();
int modifier = constructor.getModifiers();
System.out.print(Modifier.toString(modifier) + " " + constructor.getName());
System.out.print("(");
for (int i = 0; i < parameter.length; i++) {
System.out.print("arg " + i + " " + parameter[i].getName());
if (i < parameter.length - 1)
System.out.print(",");
}
System.out.println("){}");
}
}
/**
* 打印Method
*
* @param methods
*/
static void printMethods(Method[] methods) {
for (Method method : methods) {
Class<?> returnType = method.getReturnType();
Class<?> parameter[] = method.getParameterTypes();
int mod = method.getModifiers();
System.out.print(Modifier.toString(mod) + " ");
System.out.print(returnType.getName() + " ");
System.out.print(method.getName() + " ");
System.out.print("(");
for (int i = 0; i < parameter.length; i++) {
System.out.println("arg" + i + " " + parameter[i].getName());
}
//方法抛出的exception
Class<?> exceptions[] = method.getExceptionTypes();
if (exceptions.length > 0) {
System.out.print(")throws");
for (int j = 0; j < exceptions.length; j++) {
System.out.print(exceptions[j].getName() + " ");
if (j < exceptions.length - 1) {
System.out.print(",");
}
}
} else {
System.out.print(")");
}
System.out.println();
}
}
}
//输出结果
-------------getFields()-----------
public double price
public int speed
-------------getDeclaredFields()-----------
private java.lang.String Name
private java.lang.String Color
protected int height
public double price
Car()
Car Color:nullName:null
Car Color:WhiteName:VW
-----getConstructors()------
public com.ijtsai.Car(){}
public com.ijtsai.Car(arg 0 java.lang.String,arg 1 java.lang.String){}
------getMethods()--------
public java.lang.String toString ()
public java.lang.String getName ()
public void setName (arg0 java.lang.String
)
public void left ()
public void right ()
public void setColor (arg0 java.lang.String
)
public java.lang.String getColor ()
public void speedup ()
public void speeddown ()
public final void wait (arg0 long
arg1 int
)throwsjava.lang.InterruptedException
public final native void wait (arg0 long
)throwsjava.lang.InterruptedException
public final void wait ()throwsjava.lang.InterruptedException
public boolean equals (arg0 java.lang.Object
)
public native int hashCode ()
public final native java.lang.Class getClass ()
public final native void notify ()
public final native void notifyAll ()