前言

运行时类型信息(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对象)

Java包装器类

Java Wrapper 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://stackoverflow.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 ()

Java编程思想学习笔记——类型信息的更多相关文章

  1. [Java编程思想-学习笔记]第3章 操作符

    3.1  更简单的打印语句 学习编程语言的通许遇到的第一个程序无非打印"Hello, world"了,然而在Java中要写成 System.out.println("He ...

  2. [Java编程思想-学习笔记]第1章 对象导论

    1.1  抽象过程 Java是一门面向对象的语言,它的一个优点在于只针对待解问题抽象,而不用为具体的计算机结构而烦心,这使得Java有完美的移植性,也即Java的口号"Write Once, ...

  3. Java编程思想(十五) —— 类型信息之反射

    讲完.class,Class之后,继续. 1)泛化的Class引用 Class也能够增加泛型,增加之后会进行类型检查. 贴一下书上原话,Class<?>优于Class,尽管他们是等价的,C ...

  4. Java编程思想 学习笔记9

    九.接口 接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法. 1.抽象类和抽象方法  抽象类是普通的类与接口之间的一种中庸之道.创建抽象类是希望通过这个通用接口操纵一系列类. Java提 ...

  5. Java编程思想 学习笔记8

    八.多态  在面向对象的程序设计语言中,多态是继数据抽象和继承之后的第三种基本特征. 多态通过分离做什么和怎么做,从另一角度将接口和实现分离开来. “封装”通过合并特征和行为来创建新的数据类型.“实现 ...

  6. Java编程思想学习笔记——枚举类型

    前言 关键字enum可以将一组具名的值有限集合创建一种为新的类型,而这些具名的值可以作为常规的程序组件使用. 正文 基本enum特性 调用enum的values()方法可以遍历enum实例,value ...

  7. Java编程思想 学习笔记1

    一.对象导论 1.抽象过程 Alan Kay曾经总结了第一个成功的面向对象语言.同时也是Java所基于的语言之一的Smalltalk的五个基本特性,这些特性表现了纯粹的面向对象程序设计方式 1)万物皆 ...

  8. Java编程思想 学习笔记12

    十二.通过异常处理错误  Java的基本理念是“结构不佳的代码不能运行”. Java中的异常处理的目的在于通过使用少于目前数量的代码来简化大型.可靠的程序的生成,并且通过这种方式可以使你更加自信:你的 ...

  9. Java编程思想 学习笔记11

    十一.持有对象  通常,程序总是根据运行时才知道的某些条件去创建新对象.在此之前,不会知道所需对象的数量,甚至不知道确切的类型. Java实用库还提供了一套相当完整的容器类来解决这个问题,其中基本的类 ...

随机推荐

  1. [转]mysql的full join的实现

    原文地址:https://blog.csdn.net/qq_1017097573/article/details/52638360 数据库多表查询主要有以下几种 inner join内连接查询,只有两 ...

  2. [4G]4G模块的热重启

    最近在调试4G模块,发现在开机启动时执行的AT指令会概率性的出现返回杂乱字符串的问题.想尽了各种办法还是行不通,在系统中使用minicom敲AT指令就不会有问题,开始怀疑是串口初始化的问题,修改了很多 ...

  3. Tomcat配置JMX远程监控(Windown7 Linxu)

    一:Window7下配置方式. 1.配置catalina.bat 在第一行加入下面配置 注意下面这些配置要在一行,注意包含空格. set JAVA_OPTS=-Dcom.sun.management. ...

  4. Vagrant (1) —— 基本安装与配置(上)

    Vagrant (1) -- 基本安装与配置(上) 摘要 基本安装与配置 版本 Vagrant版本: 1.8.1 内容 启动运行 $ vagrant init hashicorp/precise64 ...

  5. Java 源码赏析 - java.lang - Void

    被人鄙视了,于是也来读读源码... package java.lang; /** * The Void class is an uninstantiable placeholder class to ...

  6. 通过android studio上传项目到github

    第一步,下载git客户端,并且安装 下载地址:https://git-for-windows.github.io/ 第二步,在android studio中配置git(注意第一张图中的C:\Progr ...

  7. 基于CSS3飘带状3D菜单 菜单带小图标

    这次我们要来分享一款很特别的CSS3菜单,菜单的外观是飘带状的,并且每一个菜单项有一个精美的小图标,鼠标滑过菜单项时,菜单项就会向上凸起,像是飘带飘动一样,形成非常酷的3D视觉效果.这款CSS3飘带状 ...

  8. Process.StandardOutput

    Namespace:  System.DiagnosticsAssembly:  System (in System.dll) Syntax   C# C++ F# VB   [BrowsableAt ...

  9. 【HBase】zookeeper在HBase中的应用

    转自:http://support.huawei.com/ecommunity/bbs/10242721.html Zookeeper在HBase中的应用 HBase部署相对是一个较大的动作,其依赖于 ...

  10. Golang (Go语言) Mac OS X下环境搭建 环境变量配置 开发工具配置 Sublime Text 2 【转】

    一.安装Golang的SDK 在官网 http://golang.org/ 直接下载安装包安装即可.下载pkg格式的最新安装包,直接双击运行,一路按照提示操作即可完成安装. 安装完成后,打开终端,输入 ...