一、引言 

  很多时候我们的程序可能需要在运行时识别对象和类的信息,比如多态就是基于运行时环境进行动态判断实际引用的对象。在运行时识别对象和类的信息主要有两种方式:1.RTTI,具体是Class对象,它假定我们在编译时已经知道了所有类型。2.反射机制,运行我们在运行时发现和使用类的信息。

二、RTTI

  RTTI(Run-Time Type Infomation),运行时类型信息。可以在运行时识别一个对象的类型。类型信息在运行时通过Class对象表示,Class对象包含与类有关的信息,可以使用Class对象来创建类的实例。

  每个类对应一个Class对象,这个Class对象放在.class文件中,当我们的程序中首次主动使用某类型时,会把该类型所对应的Class对象加载进内存,在这篇文章JVM之类加载器中阐述了哪些情况符合首次主动使用。

  既然RTTI和Class对象有莫大的关系,即有了Class对象,就可以进行很多操作,那么,我们如何获取到Class对象呢?有三种方法1. Class.forName("全限定名");(其中,全限定名为包名+类名)。2. 类字面常量,如String.class,对应String类的Class对象。3.通过getClass()方法获取Class对象,如String str = "abc";str.getClass();。

  通过一个类对应的Class对象后,我们可以做什么?我们可以获取该类的父类、接口、创建该类的对象、该类的构造器、字段、方法等等。总之,威力相当大。

  下面我们通过一个例子来熟悉Class对象的各种用法。

package com.hust.grid.leesf.algorithms;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method; interface SuperInterfaceA {
}; interface SuperInterfaceB {
}; class SuperC {
private String name; public SuperC() { } public SuperC(String name) {
this.name = name;
} @Override
public String toString() {
return name;
}
} class Sub extends SuperC implements SuperInterfaceA, SuperInterfaceB {
private String name;
public Sub() {
super();
} public Sub(String name) {
super(name);
this.name = name;
} public String getName() {
return name;
}
} public class Main {
public static Sub makeInstance(Class<?> clazz) {
Sub sub = null;
try {
sub = (Sub) clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
} return sub;
} public static void printBasicInfo(Class<?> clazz) {
System.out.println("CanonicalName : " + clazz.getCanonicalName());
System.out.println("Name : " + clazz.getName());
System.out.println("Simple Name : " + clazz.getSimpleName());
System.out.println("SuperClass Name : "
+ clazz.getSuperclass().getName());
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> inter : interfaces) {
System.out.println("Interface SimpleName : "
+ inter.getSimpleName());
}
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> cons : constructors) {
System.out.println("Constructor Name : " + cons.getName()
+ " And Parameter Count : " + cons.getParameterCount());
}
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println("Method Name : " + method.getName());
}
} public static void main(String[] args) {
//Sub sub = new Sub();
//Class<?> clazz = sub.getClass();
Class<?> clazz = Sub.class;
Sub instance = makeInstance(clazz);
if (instance != null) {
System.out.println("make instance successful");
} else {
System.out.println("make instance unsuccessful");
}
printBasicInfo(clazz);
}
}

  运行结果: 

make instance successful
CanonicalName : com.hust.grid.leesf.algorithms.Sub
Name : com.hust.grid.leesf.algorithms.Sub
Simple Name : Sub
SuperClass Name : com.hust.grid.leesf.algorithms.SuperC
Interface SimpleName : SuperInterfaceA
Interface SimpleName : SuperInterfaceB
Constructor Name : com.hust.grid.leesf.algorithms.Sub And Parameter Count : 0
Constructor Name : com.hust.grid.leesf.algorithms.Sub And Parameter Count : 1
Method Name : getName

  说明:使用method1、method2、method3三种方法都可以获得Class对象,运行结果是等效的。但是三者还是有稍许的区别。区别是从类的初始化角度来看的。如Class.forName("全限定名")会导致类型的加载、链接、初始化过程,而.class则不会初始化该类。显然,getClass肯定是会初始化该类的,因为这个方法时依托于类的对象。

  下面我们通过一个例子比较.class和forName()两种方法的区别。

package com.hust.grid.leesf.algorithms;
import java.util.Random;
class Init1 {
static final int staticFinal1 = 1;
static final int staticFinal2 = Main.random.nextInt(100);
static {
System.out.println("init init1");
}
} class Init2 {
static int staticNonFinal1 = 3;
static {
System.out.println("init init2");
}
} class Init3 {
static int staticNonFinal1 = 5;
static {
System.out.println("init init3");
}
} public class Main {
public static Random random = new Random(47);
public static void main(String[] args) {
Class<?> clazzClass = Init1.class;
System.out.println("after init init1 ref");
System.out.println(Init1.staticFinal1);
System.out.println(Init1.staticFinal2); System.out.println(Init2.staticNonFinal1);
try {
Class<?> clazz1 = Class.forName("com.hust.grid.leesf.algorithms.Init3");
System.out.println("after init init3 ref");
System.out.println(Init3.staticNonFinal1);
} catch (ClassNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}

  运行结果:

after init init1 ref
1
init init1
58
init init2
3
init init3
after init init3 ref
5

  说明:从结果也进一步验证了.class不会初始化类,而.forName()会初始化类。并且,对常量静态域的使用也不会导致类的初始化。

三、反射

  与RTTI必须在编译器就知道所有类型不同,反射不必在编译期就知道所有的类型,它可以在运行过程中使用动态加载的类,而这个类不必在编译期就已经知道。反射主要由java.lang.reflect类库的Field、Method、Constructor类支持。这些类的对象都是JVM在运行时进行创建,用来表示未知的类。

  关于两者的区别更深刻表达如下:对于RTTI而言,编译器在编译时打开和检查.class文件;对于反射而言,.class文件在编译时是不可获取的,所以在运行时打开和检查.class文件。

  其实在的第一个例子中我们已经用到了Constructor、Method类,现在我们来更加具体的了解Constructor、Method、Field类。  

package com.hust.grid.leesf.algorithms;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Scanner; class Human { } class Girl extends Human {
private boolean beautiful;
private int height;
private String name; public Girl() { } public Girl(String name, int height, boolean beautiful) {
this.name = name;
this.height = height;
this.beautiful = beautiful;
} public boolean isBeautiful() {
return beautiful;
} public String toString() {
return "height = " + height + " name = " + name + " beautiful = " + beautiful;
} private void print() {
System.out.println("i am a private method");
}
} class Boy extends Human {
private boolean handsome;
private int height;
private String name;
public Boy() { } public Boy(String name, int height, boolean handsome) {
this.name = name;
this.height = height;
this.handsome = handsome;
} public boolean isHandsome() {
return handsome;
} public String toString() {
return "height = " + height + " name = " + name + " handsome = " + handsome;
} private void print() {
System.out.println("i am a private method");
}
} public class Test {
public static void main(String[] args) throws NoSuchMethodException,
SecurityException, InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException,
NoSuchFieldException {
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();
Human human = null;
String name = "leesf";
int height = 180;
boolean handsome = true;
boolean flag = false;
if ("boy".equals(input)) {
human = new Boy(name, height, handsome);
flag = true;
} else {
human = new Girl("dyd", 168, true);
}
scanner.close();
Class<?> clazz = human.getClass();
Constructor<?> constructor = clazz.getConstructor(String.class,
int.class, boolean.class);
Human human1 = (Human) constructor.newInstance("leesf_dyd", 175, true);
System.out.println(human1);
Method method = null;
if (flag) {
method = clazz.getMethod("isHandsome");
} else {
method = clazz.getMethod("isBeautiful");
}
System.out.println(method.invoke(human));
Method method2 = clazz.getDeclaredMethod("print");
method2.setAccessible(true);
method2.invoke(human); Field field = clazz.getDeclaredField("height");
System.out.println(human);
field.setAccessible(true);
field.set(human, 200);
System.out.println(human);
}
}

  输入:boy

  运行结果: 

boy
height = 175 name = leesf_dyd handsome = true
true
i am a private method
height = 180 name = leesf handsome = true
height = 200 name = leesf handsome = true

  说明:反射可以让我们创建一个类的实例、在类外部访问类的私有方法、私有字段。反射真的很强大~

四、动态代理-反射的应用

  动态创建代理并且动态处理对所代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器上。

  下面是动态代理的例子

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; interface Interface {
void doSomething(); void doSomethingElse(String str);
} class RealObject implements Interface {
@Override
public void doSomething() {
System.out.println("doSomething");
} @Override
public void doSomethingElse(String str) {
System.out.println("doSomething else " + str);
}
} class DynamicProxyHandler implements InvocationHandler {
private Object proxied; public DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
} @Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (method.getName().startsWith("do")) {
System.out.println("call do*** methods");
}
method.invoke(proxied, args);
return null;
} } public class DynamicProxy {
public static void main(String[] args) {
RealObject proxied = new RealObject();
proxied.doSomething();
proxied.doSomethingElse("leesf");
Interface proxy = (Interface) Proxy.newProxyInstance(Interface.class
.getClassLoader(), new Class[] { Interface.class },
new DynamicProxyHandler(proxied));
proxy.doSomething();
proxy.doSomethingElse("leesf");
}
}

  运行结果: 

doSomething
doSomething else leesf
call do*** methods
doSomething
call do*** methods
doSomething else leesf

  说明:可以在invoke方法中进行过滤操作。过滤出以do开头的方法进行转发。

五、总结

  RTTI和反射分析就到此为止,RTTI和反射确实很强大,可以帮助我们干很多事情,用对地方绝对威力无穷,谢谢各位园友的观看~

  

【Java基础】RTTI与反射之Java的更多相关文章

  1. 黑马程序猿————Java基础日常笔记---反射与正則表達式

    ------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 黑马程序猿----Java基础日常笔记---反射与正則表達式 1.1反射 反射的理解和作用: 首 ...

  2. Java基础13:反射与注解详解

    Java基础13:反射与注解详解 什么是反射? 反射(Reflection)是Java 程序开发语言的特征之一,它允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性. Orac ...

  3. Java基础教程:反射基础

    Java基础教程:反射基础 引入反射 反射是什么 能够动态分析类能力的程序称为反射. 反射是一种很强大且复杂的机制. Class类 在程序运行期间,Java运行时系统始终为所有对象维护一个被称为运行时 ...

  4. Java基础:三步学会Java Socket编程

    Java基础:三步学会Java Socket编程 http://tech.163.com 2006-04-10 09:17:18 来源: java-cn 网友评论11 条 论坛        第一步 ...

  5. java基础知识(十一)java反射机制(上)

    java.lang.Class类详解 java Class类详解 一.class类 Class类是java语言定义的特定类的实现,在java中每个类都有一个相应的Class对象,以便java程序运行时 ...

  6. java之RTTI和反射的理解

    最近在读 Thinking in Java 这本书.读到类型信息这一张时,刚开始对书中所说的RTTI和反射彻底混了,不知道有什么联系,有哪些相同点和区别.于是在网上又找了些内容辅助理解,这一章又重新读 ...

  7. 学习Spring必学的Java基础知识(1)----反射(转)

    引述要学习Spring框架的技术内幕,必须事先掌握一些基本的Java知识,正所谓"登高必自卑,涉远必自迩".以下几项Java知识和Spring框架息息相关,不可不学(我将通过一个系 ...

  8. 学习Spring必学的Java基础知识(1)----反射

    引述要学习Spring框架的技术内幕,必须事先掌握一些基本的Java知识,正所谓"登高必自卑,涉远必自迩".以下几项Java知识和Spring框架息息相关,不可不学(我将通过一个系 ...

  9. Java基础学习(1)——反射

    反射就是把Java类中的各种成分映射成相应的Java类(主要用于框架开发) 反射的基石–>Class类 Java程序中的各个类属于同一事物,描述这类事务的Java类名就是Class. Class ...

随机推荐

  1. python easy_install pip django

    1. install python (2.7.8) 2. set PATH, add python27 3. python easy_install.py 4. easy_install pip 5. ...

  2. 持续集成(CI)相关的一些工具,后续补充。。。。

    持续集成的目标:使项目开发更加便捷 1.make工具 最原始的工具,负责组织构建的过程,即指挥编译器如何编译,连接器如何链接,最后生成一个可用文件. 2.Ant工具 Ant是一个构建工具,它只有一个配 ...

  3. 常用正则表达式-copy

    匹配中文:[\u4e00-\u9fa5] 英文字母:[a-zA-Z] 数字:[0-9] 匹配中文,英文字母和数字及_: ^[\u4e00-\u9fa5_a-zA-Z0-9]+$ 同时判断输入长度:[\ ...

  4. 一次千万级别的SQL查询简单优化体验

    背景:从两张有关联的表查询数据,A表数据量1400万,B表数据量8000万.A与B通过ID逻辑关联,没有实际的外键.B表是后来扩展出来的. 问题:根据某个ID查询时超时,运行时跑不出结果. 原因:使用 ...

  5. Wix 安装部署(一)同MSBuild 自动生成打包文件

    因为项目需要,最近在研究Wix打包部署,园子里也有一些关于wix的博客,方方面面,讲的点各不同.我自己也在测试过程中,写下过程,以供参考.最新版本WiX Toolset v3.7,如何安装的就不说了, ...

  6. 推荐书籍 -《移动App测试的22条军规》

    在今天的博文中,博主希望给大家分享一本博主同事黄勇的最新利作:<移动App测试的22条军规>.黄勇是ThoughtWorks资深敏捷QA和咨询师.对于我来说,和黄勇在一起的工作的这个项目, ...

  7. VS开发中的代码编写小技巧——避免重复代码编写的几种方法

    上一篇文章中程序员的幸福生活--有你的日子,每天都是情人节,收到了大家的很多好评.鼓励和祝福,非常感动,真诚的谢谢大家.也希望每个朋友都能保持一个积极向上的心态,去迎接丰富多彩的人生. 在开发过程中, ...

  8. Linux下安装SVN服务端小白教程

    安装 使用yum安装非常简单: yum install subversion 配置 创建仓库 我们这里在/home下建立一个名为svn的仓库(repository),以后所有代码都放在这个下面,创建成 ...

  9. Amazon Dynamo论文学习

    Dynamo是一个key-value数据存储系统,去中心化.高可扩展.高可用,使用一致性哈希来分区和备份数据,使用数据版本化来实现一致性. 核心技术 CAP:一致性.可用性.扩展性 一致性哈希:切分数 ...

  10. Linux网络编程系列-TCP编程实例

    实例: client #include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #inc ...