一、引言 

  很多时候我们的程序可能需要在运行时识别对象和类的信息,比如多态就是基于运行时环境进行动态判断实际引用的对象。在运行时识别对象和类的信息主要有两种方式: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;}@Overridepublic 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);}
}

View Code

  运行结果: 

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

View Code

  说明:使用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();}}
}

View Code

  运行结果:

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

View Code

  说明:从结果也进一步验证了.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);}
}

View Code

  输入: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

View Code

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

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

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

  下面是动态代理的例子

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 {@Overridepublic void doSomething() {System.out.println("doSomething");}@Overridepublic void doSomethingElse(String str) {System.out.println("doSomething else " + str);}
}class DynamicProxyHandler implements InvocationHandler {private Object proxied;public DynamicProxyHandler(Object proxied) {this.proxied = proxied;}@Overridepublic 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");}
}

View Code

  运行结果: 

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

View Code

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

五、总结

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

  

【Java基础】RTTI与反射之Java相关推荐

  1. Java基础-注解和反射

    Java基础-注解和反射 前言 对于注解,我主要还是在自定义APT还有运行时反射获取类来让自己能够构建出复用性更高的代码逻辑. 知识点1-注解: 注解的应用场景由元注解@Retention来进行指定, ...

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

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

  3. Java基础10(反射)

    Java基础10(反射) 1 什么是反射 官方解释:反射(reflection)技术通常被用来检测和改变应用程序在 Java 虚拟机中的行为表现.它是一个相对而言比较高级的技术,反射是一种强有力的技术 ...

  4. java反射jdk1.8,Java基础----jdk1.8 反射实验

    Java基础----jdk1.8 反射实验 (写在最前:还没入门的搬砖工的一本正经的胡说八道) 引言:  最近做到的项目中,需要给对接方提供一个公共接口,根据对方传入的 XML 文件的 rootele ...

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

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

  6. Java基础知识第二讲:Java开发手册/JVM/集合框架/异常体系/Java反射/语法知识/Java IO

    Java基础知识第二讲(Java编程规范/JVM/集合框架/异常体系/Java反射/语法知识/Java IO/码出高效) 分享在java学习及工作中,常使用的一些基础知识,本文从JVM出发,讲解了JV ...

  7. java基础(十三)-----详解内部类——Java高级开发必须懂的

    java基础(十三)-----详解内部类--Java高级开发必须懂的 目录 为什么要使用内部类 内部类基础 静态内部类 成员内部类 成员内部类的对象创建 继承成员内部类 局部内部类 推荐博客 匿名内部 ...

  8. Java基础笔记(14)—— Java的基础类型和字节大小

    Java基础笔记(14)-- Java的基础类型和字节大小 Java基础笔记(14)-- Java的基础类型和字节大小 Java语言提供了八种基本类型.六种数字类型(四个整数型(默认是int 型),两 ...

  9. Java基础之泛型反射

    a.泛型 含义:是JDK1.5的新特性,本质是参数化类型,即所操作的数据类型被指定为一个参数,使用时通过传参来指定具体的类型. 好处:安全简单.具体体现在提供编译时的强类型检查,而不用等到运行:可避免 ...

最新文章

  1. 常用的数据分析图表及方法介绍
  2. [architecture]-ARMV7架构下SecureMonitor双系统切换时保存和恢复哪些寄存
  3. vs.net 2005 beta 2安装问题
  4. P2764 最小路径覆盖问题(网络流)
  5. 大数据实验报告总结体会_建设大数据中台架构思考与总结
  6. Centos7升级Python2到Python3
  7. 针对关键字是字符串的一个比较好的散列函数
  8. 第三章:数组[5常见算法]--[6反转]
  9. OpenGL图形学中的DDA算法
  10. Ubuntu系统上安装WPS
  11. Linux命令详解之 cat
  12. MacOS 安装 brew (国内镜像源)
  13. 【7gyy】cdma无线通讯上使用的技术
  14. c语言转义字符详解,C语言转义字符和格式控制符参考
  15. 数据可视化:科研论文配色
  16. 完成对靓号号码的筛选
  17. 空间相机的像质评价体系
  18. 递归:汉罗塔问题的程序实现
  19. JAVA怎么产生一个随机数
  20. 高光谱图像分类(一)入门

热门文章

  1. 《Oracle高性能SQL引擎剖析:SQL优化与调优机制详解》一2.3 执行计划各个列的含义...
  2. Java线程池(ThreadPool)详解
  3. 任务二十九:表单(一)单个表单项的检验
  4. C++ 对象间的赋值与拷贝构造函数
  5. python day11
  6. Beta冲刺——day6
  7. Slim Span UVA - 1395 (并查集)
  8. 2014.10.1 Form中显示pdf文件
  9. 2018-02-03-PY3下经典数据集iris的机器学习算法举例-零基础
  10. 黄聪:WordPress动作钩子函数add_action()、do_action()源码解析