29.10.2014 Views

《Java高级技术》课程 - 软件工程研究所- 北京大学

《Java高级技术》课程 - 软件工程研究所- 北京大学

《Java高级技术》课程 - 软件工程研究所- 北京大学

SHOW MORE
SHOW LESS

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 />

谢 谢 !

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!