ãJavaé«çº§ææ¯ãè¯¾ç¨ - 软件工ç¨ç 究æ- å京大å¦
ãJavaé«çº§ææ¯ãè¯¾ç¨ - 软件工ç¨ç 究æ- å京大å¦
ãJavaé«çº§ææ¯ãè¯¾ç¨ - 软件工ç¨ç 究æ- å京大å¦
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
《Java 高 级 技 术 》 课 程<br />
Java 语 言 的 高 级 特 性 (8)<br />
李 戈<br />
北 京 大 学 信 息 科 学 技 术 学 院 软 件 研 究 所<br />
2008 年 5 月 11 日
运 行 期 类 型 识 别<br />
• RTTI ( Run-Time Type Identification )<br />
在 运 行 期 间 , 识 别 一 个 对 象 的 类 型 。
运 行 期 类 型 识 别<br />
• RTTI 的 三 种 方 法<br />
强 制 类 型 转 换<br />
利 用 Class 对 象<br />
通 过 比 较 识 别 类 型
(1) 利 用 强 制 类 型 转 换 判 别 运 行 期 类 型<br />
class Shape {<br />
void draw() { System.out.println ( this + ".draw()"); }<br />
}<br />
class Circle extends Shape {<br />
public String toString() { return "Circle"; }<br />
}<br />
class Square extends Shape {<br />
public String toString() { return "Square"; }<br />
}<br />
class Triangle extends Shape {<br />
public String toString() { return "Triangle"; }<br />
}<br />
public class Shapes {<br />
public static void main(String[] args) {<br />
// Array of Object, not Shape:<br />
Object[ ] shapeList = { new Circle(), new Square(), new Triangle() };<br />
for (int i = 0; i < shapeList.length; i++)<br />
((Shape) shapeList[i]).draw();<br />
}<br />
}
• 强 制 类 型 转 换 并 不 彻 底<br />
问 题<br />
Object 被 转 型 为 Shape, 而 不 是 转 型 为 Circle, Square,<br />
或 者 Triangle。<br />
• 假 如 能 够 知 道 某 个 引 用 的 确 切 类 型 , 则 可 以 进 行 更<br />
加 个 性 化 的 处 理<br />
程 序 中 有 一 段 代 码 用 来 旋 转 列 表 中 的 所 有 图 形 , 但 你<br />
想 跳 过 圆 形 , 因 为 对 圆 形 作 旋 转 没 有 意 义 。 如 果 你 可<br />
以 获 取 到 某 个 Shape 引 用 所 指 向 的 对 象 的 确 切 类 型 ,<br />
就 可 以 选 择 或 者 剔 除 这 个 特 例 。<br />
• 那 么 , 类 型 信 息 在 运 行 期 是 如 何 表 示 的 ?
• Class 对 象<br />
类 型 信 息 的 表 示 方 式<br />
对 象 的 类 型 信 息 是 由 被 称 为 Class 对 象 的 特 殊<br />
对 象 表 达 ;<br />
Class 对 象 描 述 了 运 行 中 的 classes 和 interfaces<br />
的 相 关 信 息 ; 通 过 Class 对 象 可 以 在 运 行 中 取<br />
得 这 些 信 息 ;<br />
Class 对 象 被 用 来 创 建 类 的 “ 常 规 ” 对 象 ;<br />
每 个 类 都 有 一 个 Class 对 象 , 保 存 在 与 其 同 名<br />
的 .class 文 件 中 ;<br />
所 有 Class 对 象 都 属 于 Class 类 ;
• Class 对 象 的 作 用<br />
类 型 信 息 的 表 示 方 式<br />
在 运 行 期 间 , 一 旦 我 们 想 生 成 某 个 类 的 一 个 对<br />
象 :<br />
● 运 行 这 个 程 序 的 Java 虚 拟 机 (JVM) 首 先 检 查<br />
这 个 类 的 Class 对 象 是 否 已 经 加 载 。<br />
● 如 果 尚 未 加 载 ,JVM 就 会 根 据 类 名 查 找 .class<br />
文 件 , 并 将 其 载 入 。<br />
● 一 旦 某 个 类 的 Class 对 象 被 载 入 内 存 , 它 就 被 用<br />
来 创 建 这 个 类 的 所 有 对 象 。
• 步 骤 :<br />
(2) 利 用 Class 对 象 识 别 对 象 的 类 型<br />
获 取 某 个 对 象 的 Class 对 象 ;<br />
获 取 Class 对 象 中 的 类 型 相 关 信 息 ;<br />
● Class 类 的 getName() : 以 String 的 形 式 返 回 此 Class<br />
对 象 所 表 示 的 实 体 ( 类 、 接 口 、 数 组 类 、 基 本 类 型 或<br />
void) 名 称 。<br />
例 如 :<br />
void printClassName(Object obj) {<br />
System.out.println("The class of " + obj + " is " +<br />
obj.getClass().getName());<br />
}
类 型 信 息 的 获 取 方 式<br />
• 如 何 获 取 一 个 class 的 Class 对 象<br />
通 过 Object.getClass( ) 方 法 来 获 得<br />
通 过 Class.forName( ) 方 法 获 得<br />
通 过 类 字 面 常 量 (Class Literals) 获 得
类 型 信 息 的 获 取 方 式<br />
• 通 过 Class.forName( ) 方 法 获 得<br />
Class 类 的 forName( ) 方 法<br />
forName(String className) : 返 回 与 带 有 给 定<br />
字 符 串 名 的 类 或 接 口 相 关 联 的 Class 对 象 。<br />
例 如 :<br />
class Dog {………..}<br />
Class referencetodog = Class.forName(“Dog”);
类 型 信 息 的 获 取 方 式<br />
• 通 过 类 字 面 常 量 (Class Literals) 获 得<br />
字 面 常 量 的 形 式 为 :classname.class。<br />
对 于 基 本 类 型 , 每 种 基 本 类 型 的 包 装 类 都 有 一<br />
个 名 为 TYPE 的 标 准 数 据 , 能 够 产 生 一 个 指 向<br />
相 应 的 基 本 类 型 的 Class 对 象 的 引 用 。<br />
● Integer.TYPE 等 同 于 int.class<br />
例 如 :<br />
class dog {………..}<br />
Class referencetodog = dog.class;
class Cat {<br />
Cat( ) {<br />
System.out.println("Init Cat()");<br />
}<br />
static {<br />
System.out.println("Loading Cat");<br />
}}<br />
class Dog {<br />
Dog( ) {<br />
System.out.println("Init Dog()");<br />
}<br />
static {<br />
System.out.println("Loading Dog");<br />
}}<br />
class Duck {<br />
Duck( ) {<br />
System.out.println("Init Duck()");<br />
}<br />
static {<br />
System.out.println("Loading Duck");<br />
}}<br />
in main()<br />
Loading Cat<br />
Init Cat()<br />
After create Cat()<br />
Loading Dog<br />
After Class.forName("Dog")<br />
Loading Duck<br />
Init Duck()<br />
After create Duck()<br />
After d.getClass("Duck")
public class LetterClass {<br />
public static void main(String[] args) {<br />
System.out.println("in main()");<br />
new Cat();<br />
System.out.println("After create Cat()");<br />
try {<br />
Class c1 = Class.forName("Dog");<br />
//System.out.println(c1.getName());<br />
Class c2 = Dog.class;<br />
//System.out.println(c2.getName());<br />
} catch (ClassNotFoundException cnfe) {<br />
cnfe.printStackTrace();<br />
}<br />
System.out.println("After Class.forName(\"Dog\")");<br />
Duck d = new Duck();<br />
System.out.println("After create Duck()");<br />
Class c3 = d.getClass();<br />
//System.out.println(c3.getName());<br />
System.out.println("After d.getClass(\"Duck\")");<br />
}<br />
}
(3) 通 过 比 较 识 别 对 象 类 型<br />
• 比 较 的 方 式<br />
直 接 比 较 ;<br />
利 用 isInstance( ) 函 数 进 行 比 较 ;<br />
利 用 isinstanceof 操 作 符 进 行 比 较 ;
• 类 型 直 接 比 较 的 方 法<br />
boolean equals(Object obj)<br />
直 接 比 较<br />
指 示 其 他 某 个 对 象 是 否 与 此 对 象 “ 相 等 ”。<br />
A.equals(B);<br />
利 用 == 运 算 符 进 行 比 较<br />
If the operands of an equality operator are both of<br />
either reference type or the null type, then the<br />
operation is object equality.<br />
注 意 : 两 个 Class 对 象 不 论 是 通 过 equals() 函 数 还 是 直<br />
接 用 == 运 算 符 进 行 比 较 , 比 较 的 都 是 类 型 是 否 相 同 。
利 用 isInstance( ) 函 数 进 行 比 较<br />
• boolean isInstance(Object obj)<br />
判 定 指 定 的 Object 是 否 与 此 Class 所 表 示 的 对 象<br />
赋 值 兼 容<br />
class Cat{ }<br />
class Dog{ }<br />
class Duck{ }<br />
public class Test{<br />
public static void main(String[] args){<br />
Class c2, c3;<br />
Duck d = new Duck();<br />
c2 = Dog.class;<br />
c3 = d.getClass();<br />
}<br />
}<br />
运 行 结 果 为 :<br />
c2.isIntance(d):false<br />
c3.isIntance(d):true<br />
System.out.println("c2.isIntance(d):" + (c2.isInstance(d)));<br />
System.out.println("c3.isIntance(d):" + (c3.isInstance(d)));
• instanceof<br />
利 用 instanceof 操 作 符 进 行 比 较<br />
Java 语 言 的 保 留 字 , 用 于 检 测 一 个 对 象 引 用 是 否 是 某 个 类<br />
的 实 例 ; 当 这 个 对 象 引 用 的 类 型 是 这 个 类 或 者 是 这 个 类 的<br />
子 类 型 时 返 回 true, 否 则 返 回 false。<br />
class Cat{ }<br />
class Dog{ }<br />
class Duck{ }<br />
public class Test{<br />
public static void main(String[] args){<br />
Class c3;<br />
Object d = new Duck();<br />
// Duck d = new Duck(); ?????<br />
c3 = d.getClass();<br />
System.out.println("d instanceof Dog:" + (d instanceof Dog));//(3)<br />
}<br />
}
• RTTI 有 一 个 限 制<br />
问 题<br />
对 象 的 类 型 在 编 译 期 必 须 是 已 知 的 ,<br />
但 是 假 设 你 获 取 了 一 个 指 向 某 个 并 不 在 你 的 程<br />
序 空 间 中 的 对 象 的 引 用 。<br />
● 例 如 , 假 设 你 从 磁 盘 文 件 , 或 者 网 络 连 接 中 获<br />
取 了 一 串 字 节 , 并 且 你 被 告 知 这 些 字 节 代 表 了<br />
一 个 类 。 编 译 器 在 编 译 你 的 程 序 代 码 的 时 候 不<br />
可 能 了 解 有 关 这 个 在 后 面 才 会 出 现 的 类 的 信 息 。<br />
你 怎 样 才 能 使 用 这 样 的 类 呢 ?
• Class 类 支 持 反 射 的 概 念 ,<br />
反 射 (Reflection)<br />
Java 附 带 的 库 java.lang.reflect 包 含 了 Field,Method 以<br />
及 Constructor 类 。 这 些 类 型 的 对 象 是 由 JVM 在 运 行 期<br />
创 建 的 , 用 以 表 示 未 知 类 里 对 应 的 成 员 。<br />
可 以 实 现 :<br />
● 使 用 Constructor 创 建 新 的 对 象 ;<br />
● 用 get() 和 set() 方 法 读 取 和 修 改 与 Field 对 象 关 联 的 属 性 ,<br />
● 用 invoke() 方 法 调 用 与 Method 对 象 关 联 的 方 法 ;<br />
● 调 用 getFields(),getMethods(),getConstructors() 方<br />
法 , 以 返 回 表 示 属 性 、 方 法 以 及 构 造 器 的 对 象 数 组 ;<br />
匿 名 对 象 的 类 信 息 就 能 在 运 行 期 被 完 全 确 定 下 来 , 而<br />
在 编 译 期 不 需 要 知 道 任 何 事 情 。
反 射 (Reflection)<br />
• Java 反 射 机 制 主 要 提 供 了 以 下 功 能 :<br />
在 运 行 时 判 断 任 意 一 个 对 象 所 属 的 类 ;<br />
在 运 行 时 构 造 任 意 一 个 类 的 对 象 ;<br />
在 运 行 时 判 断 任 意 一 个 类 所 具 有 的 成 员 变 量<br />
和 方 法 ;<br />
在 运 行 时 调 用 任 意 一 个 对 象 的 方 法 ;<br />
生 成 动 态 代 理 。
反 射 (Reflection)<br />
• JDK 中 主 要 由 以 下 类 实 现 Java 反 射 机 制 :<br />
Class 类 : 其 实 例 表 示 正 在 运 行 的 Java 应 用 程 序 中 的<br />
类 和 接 口 。<br />
Field 类 : 提 供 有 关 类 或 接 口 的 单 个 字 段 的 信 息 , 以 及<br />
对 它 的 动 态 访 问 权 限 ;<br />
Method 类 : 提 供 关 于 类 或 接 口 的 某 个 方 法 ( 以 及 如<br />
何 访 问 该 方 法 ) 的 信 息 ;<br />
Constructor 类 : 提 供 关 于 类 的 单 个 构 造 方 法 的 信 息<br />
以 及 对 它 的 访 问 权 限 ;<br />
Array 类 : 提 供 了 动 态 创 建 和 访 问 Java 数 组 的 方 法 。
import java.lang.reflect.*;<br />
例 1: 获 取 类 信 息<br />
public class DumpMethods {<br />
public static void main(String args[]) throws Exception {<br />
// 加 载 并 初 始 化 命 令 行 参 数 指 定 的 类<br />
Class classType = Class.forName(args[0]);<br />
// 获 得 类 的 所 有 方 法<br />
Method methods[] = classType.getDeclaredMethods();<br />
for (int i = 0; i < methods.length; i++)<br />
System.out.println(methods[i].toString());<br />
}<br />
}<br />
输 入 :java DumpMethods java.util.Stack<br />
输 出 :<br />
public synchronized java.lang.Object java.util.Stack.pop()<br />
public java.lang.Object java.util.Stack.push(java.lang.Object)<br />
public boolean java.util.Stack.empty()<br />
public synchronized java.lang.Object java.util.Stack.peek()<br />
public synchronized int java.util.Stack.search(java.lang.Object)
import java.lang.reflect.*;<br />
public class InvokeTester {<br />
public int add(int param1, int param2) {<br />
return param1 + param2;<br />
}<br />
public String echo(String msg) {<br />
return "echo:" + msg;<br />
}<br />
public static void main(String[] args) throws Exception {<br />
Class classType = InvokeTester.class;<br />
Object invokeTester = classType.newInstance();<br />
// 调 用 InvokeTester 对 象 的 add() 方 法<br />
Method addMethod = classType.getMethod("add", new Class[] { int.class,<br />
int.class });<br />
Object result = addMethod.invoke(invokeTester, new Object[] {<br />
new Integer(100), new Integer(200) });<br />
System.out.println((Integer) result);<br />
// 调 用 InvokeTester 对 象 的 echo() 方 法<br />
Method echoMethod = classType.getMethod("echo",<br />
new Class[] { String.class });<br />
result = echoMethod.invoke(invokeTester, new Object[] { "Hello" });<br />
System.out.println((String) result);<br />
}}<br />
例 2: 调 用 类 方 法
import java.lang.reflect.*;<br />
public class InvokeTester {<br />
public int add(int param1, int param2) {<br />
return param1 + param2;<br />
}<br />
public String echo(String msg) {<br />
return "echo:" + msg;<br />
}<br />
public static void main(String[] args) throws Exception {<br />
Class classType = InvokeTester.class;<br />
Object invokeTester = classType.newInstance();<br />
// 调 用 InvokeTester 对 象 的 add() 方 法<br />
Method addMethod = classType.getMethod("add", new Class[] { int.class,<br />
int.class });<br />
Object result = addMethod.invoke(invokeTester, new Object[] {<br />
new Integer(100), new Integer(200) });<br />
System.out.println((Integer) result);<br />
// 调 用 InvokeTester 对 象 的 echo() 方 法<br />
Method echoMethod = classType.getMethod("echo",<br />
new Class[] { String.class });<br />
result = echoMethod.invoke(invokeTester, new Object[] { "Hello" });<br />
System.out.println((String) result);<br />
}}<br />
例 2: 调 用 类 方 法
Customer 类 是 一 个 JavaBean<br />
class Customer {<br />
private Long id;<br />
private String name;<br />
private int age;<br />
public Customer() { }<br />
public Customer(String name, int age) {<br />
this.name = name;<br />
this.age = age;<br />
}<br />
public Long getId() { return id; }<br />
public void setId(Long id) { this.id = id; }<br />
public String getName() { return name; }<br />
}<br />
例 3: 综 合 应 用<br />
目 标 : 构 造 一 个 copy(Object object) 方<br />
法 , 这 个 方 法 能 够 创 建 一 个 和 参 数<br />
object 同 样 类 型 的 对 象 , 并 把 object<br />
对 象 中 的 所 有 属 性 复 制 到 新 建 的 对<br />
象 中 , 并 将 它 返 回 。<br />
public void setName(String name) { this.name = name; }<br />
public int getAge() { return age; }<br />
public void setAge(int age) { this.age = age; }
import java.lang.reflect.*;<br />
public class ReflectTester {<br />
public Object copy(Object object) throws Exception {<br />
// 获 得 对 象 的 类 型<br />
Class classType = object.getClass();<br />
System.out.println("Class:" + classType.getName());<br />
// 通 过 默 认 构 造 方 法 创 建 一 个 新 的 对 象<br />
Object objectCopy =<br />
classType.getConstructor(new Class[] {}).newInstance(new Object[] {});<br />
// 获 得 对 象 的 所 有 属 性<br />
Field fields[] = classType.getDeclaredFields();<br />
for (int i = 0; i < fields.length; i++) {<br />
……<br />
}<br />
return objectCopy;<br />
}<br />
}<br />
public static void main(String[] args) throws Exception {<br />
……<br />
}
import java.lang.reflect.*;<br />
public class ReflectTester {<br />
public Object copy(Object object) throws Exception {<br />
// 获 得 对 象 的 类 型<br />
Class classType = object.getClass();<br />
System.out.println("Class:" + classType.getName());<br />
// 通 过 默 认 构 造 方 法 创 建 一 个 新 的 对 象<br />
Object objectCopy = classType.getConstructor(new Class[] {})<br />
.newInstance(new Object[] {});<br />
// 获 得 对 象 的 所 有 属 性<br />
Field fields[] = classType.getDeclaredFields();<br />
for (int i = 0; i < fields.length; i++) {<br />
Field field = fields[i];<br />
String fieldName = field.getName();<br />
String firstLetter = fieldName.substring(0, 1).toUpperCase();<br />
// 获 得 和 属 性 对 应 的 getXXX() 方 法 的 名 字<br />
String getMethodName = "get" + firstLetter + fieldName.substring(1);<br />
// 获 得 和 属 性 对 应 的 setXXX() 方 法 的 名 字<br />
String setMethodName = "set" + firstLetter + fieldName.substring(1);<br />
// 获 得 和 属 性 对 应 的 getXXX() 方 法<br />
Method getMethod = classType.getMethod(getMethodName,<br />
new Class[] {});<br />
// 获 得 和 属 性 对 应 的 setXXX() 方 法<br />
Method setMethod = classType.getMethod(setMethodName,<br />
new Class[] { field.getType() });<br />
// 调 用 原 对 象 的 getXXX() 方 法<br />
Object value = getMethod.invoke(object, new Object[] {});<br />
System.out.println(fieldName + ":" + value);<br />
// 调 用 复 制 对 象 的 setXXX() 方 法<br />
setMethod.invoke(objectCopy, new Object[] { value });<br />
}<br />
return objectCopy;<br />
}<br />
public static void main(String[] args) throws Exception {<br />
}<br />
}
import java.lang.reflect.*;<br />
public class ReflectTester {<br />
public Object copy(Object object) throws Exception {<br />
// 获 得 对 象 的 类 型<br />
Class classType = object.getClass();<br />
System.out.println("Class:" + classType.getName());<br />
// 通 过 默 认 构 造 方 法 创 建 一 个 新 的 对 象<br />
Object objectCopy = classType.getConstructor(new Class[] {})<br />
.newInstance(new Object[] {});<br />
// 获 得 对 象 的 所 有 属 性<br />
Field fields[] = classType.getDeclaredFields();<br />
for (int i = 0; i < fields.length; i++) {<br />
Field field = fields[i];<br />
String fieldName = field.getName();<br />
String firstLetter = fieldName.substring(0, 1).toUpperCase();<br />
// 获 得 和 属 性 对 应 的 getXXX() 方 法 的 名 字<br />
String getMethodName = "get" + firstLetter + fieldName.substring(1);<br />
// 获 得 和 属 性 对 应 的 setXXX() 方 法 的 名 字<br />
String setMethodName = "set" + firstLetter + fieldName.substring(1);<br />
// 获 得 和 属 性 对 应 的 getXXX() 方 法<br />
Method getMethod = classType.getMethod(getMethodName,<br />
new Class[] {});<br />
// 获 得 和 属 性 对 应 的 setXXX() 方 法<br />
Method setMethod = classType.getMethod(setMethodName,<br />
new Class[] { field.getType() });<br />
// 调 用 原 对 象 的 getXXX() 方 法<br />
Object value = getMethod.invoke(object, new Object[] {});<br />
System.out.println(fieldName + ":" + value);<br />
// 调 用 复 制 对 象 的 setXXX() 方 法<br />
setMethod.invoke(objectCopy, new Object[] { value });<br />
}<br />
return objectCopy;<br />
}<br />
}<br />
public static void main(String[] args) throws Exception {<br />
Customer customer = new Customer("Tom", 21);<br />
customer.setId(new Long(1));<br />
Customer customerCopy<br />
= (Customer) new ReflectTester().copy(customer);<br />
System.out.println("Copy information:" + customerCopy.getName() + ""<br />
+ customerCopy.getAge());<br />
}
RTTI vs. Reflection<br />
• 重 要 的 是 , 反 射 机 制 并 没 有 什 么 魔 法 。 当 你 通 过 反<br />
射 与 一 个 未 知 类 型 的 对 象 打 交 道 时 ,JVM 只 是 简 单<br />
地 检 查 这 个 对 象 , 看 它 属 于 哪 个 特 定 的 类 ( 就 象<br />
RTTI 那 样 )。<br />
• 在 这 之 后 , 在 做 其 它 事 情 之 前 , 必 须 加 载 那 个 类 的<br />
Class 对 象 。 因 此 , 那 个 类 的 .class 文 件 对 于 JVM 来 说<br />
必 须 是 可 获 取 的 , 要 么 在 本 地 机 器 上 , 要 么 可 以 通<br />
过 网 络 取 得 。<br />
• 所 以 RTTI 和 反 射 之 间 真 正 的 区 别 只 在 于 , 对 RTTI<br />
来 说 , 编 译 器 在 编 译 期 打 开 和 检 查 .class 文 件 , 而 对<br />
于 反 射 机 制 来 说 是 在 运 行 期 打 开 和 检 查 .class 文 件 。
好 好 想 想 , 有 没 有 问 题 ?<br />
谢 谢 !