Java RTTI与反射
来源:互联网 发布:2014新疆网络管制 编辑:程序博客网 时间:2024/05/23 13:21
Java提供了在运行时发现和使用类型信息的能力。
RTTI:运行时类型识别
运行时类型识别(RTTI)可以让你在程序运行时发现和使用类型信息,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个类型标识跟踪每个对象所属的类,虚拟机利用运行时类型信息选择相应的方法执行,保存这些信息的类就是Class类,Class
对象是在运行时加载到JVM中的。
Java中的任何类型都具有一个类字面常量,例如int.class
,它在编译时就会受到检查,可用于类、接口、数组和基本数据类型。
为了使用类,所做的工作有:
- 加载:由类加载器执行,将查找.class
文件,并创建一个Class对象;
- 链接:验证类中的字节码,为静态域分配存储空间,解析此类创建时对其他类的引用;
- 初始化:执行静态初始化器和静态初始化块。
注意:
- 当使用类字面常量来创建对Class对象的引用时,不会初始化该Class对象;
- 如果一个static final
域是编译期常量,则此域不用对类进行初始化就能被读取;
- 如果一个static
域不是final
的,则在访问它时,必须先进行链接和初始化。
反射:运行时的类信息
Class类与java.lang.reflect包一起对反射提供了支持。java.lang.reflect
包中的Field
、Method
和Constructor
类分别用于描述类的域、方法和构造器,这些类型的对象由JVM在运行时创建,用来表示未知类中的成员。
通过Field
类可以获得数据域的类型、数据某一时刻的状态(值)和数据的修饰符;Method
类中包括获得方法参数、方法返回值和方法修饰符的方法;而Constructor
类提供了获取构造器参数和构造器修饰符的方法,另外还可以通过Constructor
类在运行时新建对象实例(调用相应的构造器方法)。 java.lang.reflect
包提供了一个Modifier
类,此类可以用来分析Field
、Method
和Constructor
类中获取的修饰符。
通过Class对象的getFields
、getMethods
和getConstructors
方法可以获得此Class类中的public域、方法和构造器,其中包括父类的public成员;通过Class对象的getDeclareFields
、getDeclareMethods
和getDeclareConstructors
方法可以获得此Class类中声明的全部域、方法和构造器,包括private和protected成员,但不包括父类的成员。
用反射获取类信息
下面设计了一个在运行时查看类结构的类ReflectUtil
,它能显示一个类的继承层次:
public class ReflectUtil { public static Class<?> analyseClass(String className){ try { // 加载类信息 Class<?> cl=Class.forName(className); // 打印类头 - public class MyClass2 extends MyClass1 Class<?> supercl=showClassHead(cl); System.out.println("{"); // 打印数据域 - private String s; showClassFields(cl); // 打印构造器方法 - public MyClass2(); showClassConstructors(cl); // 打印方法 - public void f(); showClassMethods(cl); System.out.println("}"); return supercl; } catch (ClassNotFoundException e) { System.out.println("not find "+className); return null; } } public static Class<?> showClassHead(Class<?> cl){ // 打印修饰符 String modifier=Modifier.toString(cl.getModifiers()); if(modifier!=null && modifier.length()>0) System.out.print(modifier+" "); // 打印类名 System.out.print("class "+cl.getName()+" "); // 打印父类 Class<?> supercl=cl.getSuperclass(); if(supercl!=null && supercl!=Object.class) System.out.print("extends "+supercl.getName()+" "); return supercl; } public static void showClassFields(Class<?> cl){ Field[] fields=cl.getDeclaredFields(); for(Field field : fields){ System.out.print(" "); // 打印修饰符 String modifier=Modifier.toString(field.getModifiers()); if(modifier!=null && modifier.length()>0) System.out.print(modifier+" "); // 打印类型和变量名 System.out.println(field.getType().getName()+" "+field.getName()+";"); } } public static void showClassConstructors(Class<?> cl){ Constructor<?>[] constructors=cl.getDeclaredConstructors(); for(Constructor<?> constructor : constructors){ System.out.print(" "); // 打印修饰符 String modifier=Modifier.toString(constructor.getModifiers()); if(modifier!=null && modifier.length()>0) System.out.print(modifier+" "); // 打印构造器方法名 System.out.print(cl.getName()+"("); // 打印方法参数列表 Class<?>[] paramTypes=constructor.getParameterTypes(); if(paramTypes!=null && paramTypes.length>0){ int i; for(i=0; i<paramTypes.length-1; i++) System.out.print(paramTypes[i].getName()+", "); System.out.print(paramTypes[i].getName()); } System.out.print(")"); // 打印异常声明列表 Class<?>[] exceptionTypes=constructor.getExceptionTypes(); if(exceptionTypes!=null && exceptionTypes.length>0){ System.out.print(" throws "); int j; for(j=0; j<exceptionTypes.length-1; j++) System.out.print(exceptionTypes[j].getName()+", "); System.out.print(exceptionTypes[j].getName()); } System.out.println(";"); } } public static void showClassMethods(Class<?> cl){ Method[] methods=cl.getDeclaredMethods(); for(Method method : methods){ System.out.print(" "); // 打印修饰符 String modifier=Modifier.toString(method.getModifiers()); if(modifier!=null && modifier.length()>0) System.out.print(modifier+" "); // 打印方法返回类型 System.out.print(method.getReturnType().getName()+" "); // 打印方法名 System.out.print(method.getName()+"("); // 打印方法参数列表 Class<?>[] paramTypes=method.getParameterTypes(); if(paramTypes!=null && paramTypes.length>0){ int i; for(i=0; i<paramTypes.length-1; i++) System.out.print(paramTypes[i].getName()+", "); System.out.print(paramTypes[i].getName()); } System.out.print(")"); // 打印异常声明列表 Class<?>[] exceptionTypes=method.getExceptionTypes(); if(exceptionTypes!=null && exceptionTypes.length>0){ System.out.print(" throws "); int j; for(j=0; j<exceptionTypes.length-1; j++) System.out.print(exceptionTypes[j].getName()+", "); System.out.print(exceptionTypes[j].getName()); } System.out.println(";"); } }}
现在我们自己编写两个测试类Shape
和Circle
,让Circle
继承自Shape
,并且Shape
和Circle
类都在com.zzw.reflect
包中:
// Shape类public class Shape { private float width; // 宽度 private float height; // 高度 private float x; // 横坐标 private float y; // 纵坐标 private int color; // 颜色 public Shape(float w, float h){ this(w, h, 0, 0, 0x000); } public Shape(float w, float h, float x, float y, int c){ width=w; height=h; this.x=x; this.y=y; color=c; } public float getWidth(){ return width; } public float getHeight(){ return height; } public float getX(){ return x; } public float getY(){ return y; } public int getColor(){ return color; } public void setWidth(float w){ width=w; } public void setHeight(float h){ height=h; } public void setX(float x){ this.x=x; } public void setY(float y){ this.y=y; } public void setColor(int c){ color=c; }}// Circle类public class Circle extends Shape { private float radius; // 半径 public Circle(float r) { super(r, r); radius=r; } public float getRadius(){ return radius; } public void setRadius(float r){ radius=r; } public float getArea(){ return (float)(Math.PI*radius*radius); } private void f(int a, int b) throws Exception { throw new Exception(); } public int g(int c){ try { f(c, c); } catch (Exception e) { e.printStackTrace(); } return 0; }}
下面是我们的测试代码:
Class<?> supercl=null; String className="com.zzw.reflect.Circle"; do{ supercl=ReflectUtil.analyseClass(className); className=supercl.getName(); System.out.println("--------------------"); }while(supercl!=null && supercl!=Object.class);// output:public class com.zzw.reflect.Circle extends com.zzw.reflect.Shape { private float radius; public com.zzw.reflect.Circle(float); public float getArea(); private void f(int, int) throws java.lang.Exception; public void setRadius(float); public int g(int); public float getRadius();}--------------------public class com.zzw.reflect.Shape { private float width; private float height; private float x; private float y; private int color; public com.zzw.reflect.Shape(float, float); public com.zzw.reflect.Shape(float, float, float, float, int); public void setColor(int); public int getColor(); public void setWidth(float); public void setX(float); public void setHeight(float); public float getHeight(); public float getY(); public void setY(float); public float getX(); public float getWidth();}--------------------
可以看到,我们成功的获得了Circle
类的结构和继承层次,当然,如果你想知道其它类的信息,只需要简单改变一下analyseClass
方法的参数就行了。
在运行时使用反射分析对象
现在我们已经知道如何通过反射来查看类结构,但你可能会说,直接看API和源代码不是更简单吗?这样说的确没错,但是反射不仅止于此,它还能在运行时查看和设置对象的状态,我们还是以Circle
类为例说明。
Circle circle=new Circle(1.0f); try { // 获取Circle对象的radius域 Field f=circle.getClass().getDeclaredField("radius"); // 由于radius是私有的,必须设置成可访问的 f.setAccessible(true); float radius=(Float)f.get(circle); // 或者 f.getFloat(circle); System.out.println("radius="+radius); // 设置Circle对象的radius域 f.set(circle, 10.0f); // 或者 f.setFloat(circle, 10.0f); System.out.println("radius="+circle.getRadius()); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); }// output:radius=1.0radius=10.0
可以看到,通过反射,我们成功的获得和修改了Circle
对象的radius
域。此时你可能会说,我只要调用radius
的get
和set
方法照样能行,为什么还要写这么长的代码?没错,这里可以直接用get
和set
方法,但是,我们在编程中大部分使用的都是系统代码或者别人写的代码,如果他们没有提供get
和set
方法呢?此时就只能使用反射,现在你知道反射的强大了吧。
调用任意方法,Java中的“函数指针”
Java中由于安全性的考虑没有提供指针类型,因此无法像C++那样任意的传递方法(函数)地址,相比于C++显得不是那么自由。反射机制在一定程度上弥补了这点,它允许使用者调用任意方法。
和访问/修改对象域类似,Method
类提供了invoke
方法来调用对象方法。这里我们先在Circle
类中添加一些代码:
public static void e(int a, int b){ System.out.println("this is Circle.e("+a+", "+b+")"); } private void f(double a){ System.out.println("this is Circle instance's f("+a+")"); }
我们在Circle
类中添加了一个公开的静态方法e
和一个私有的重载方法f
,下面我们将展示如何使用反射来调用这两个方法。
Circle circle=new Circle(1.0f); Method m=null; try { // 调用 Circle.e(int, int) m=Circle.class.getDeclaredMethod("e", int.class, int.class); m.invoke(null, 1, 2); // 调用 circle.f(double) m=circle.getClass().getDeclaredMethod("f", double.class); m.setAccessible(true); m.invoke(circle, 3); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); }// output:this is Circle.e(1, 2)this is Circle instance's f(3.0)
注意到,对于静态方法和非静态方法的调用稍微有所区别。对于静态方法,可以使用Circle.class
来获得Class对象,同时invoke
方法的第一个参数设置成null
;而非静态方法使用circle.getClass()
来获得Class对象,同时invoke
方法的第一个参数需要传入circle
(运行时对象)。
由于一个类可以重载多个同名方法,因此在调用getDeclaredMethod方法时需要传入参数列表的类型序列。除此之外还要注意访问权限,可以看到对f
方法的调用需要设置为可访问的。
在运行时创建对象
我们发现,Class类中有一个newInstance
方法,它能调用相关类的默认构造方法来创建对象,但是对于那些没有默认构造方法或者想要调用其它构造方法的类,它就无能为力了。 Constructor
类弥补了这一点,它能调用任意的构造器方法来创建对象,这里我们以创建一个Circle
对象为例。
try { Constructor<Circle> c=Circle.class.getDeclaredConstructor(float.class); Circle circle=c.newInstance(3.0f); System.out.println("radius="+circle.getRadius()); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); }// output:radius=3.0
不出所料,我们利用Constructor
对象成功创建了一个Circle
对象。当然,这里我们调用的是Circle
类的公开构造方法,如果想要调用一个私有构造方法,也要先设置访问权限哦。
反射:强大背后的隐患
通过一系列实例,相信你已经了解了Java的反射机制的强大能力,使用反射我们几乎可以做任何事情!但是,这并不意味着你能随意使用反射。
我们现在回头想想当初为什么要使用反射?访问/设置对象的私有/保护域?调用任意的对象方法?创建任意的对象实例?没错,通过反射这些我们都能做到。但是,Java作为一种OOP的语言,最明显的特点之一就是封装,而反射完全绕过了这个安全层!过度使用反射可能导致代码很快陷入混乱,而且它比起正常的操作速度也要慢很多。
那么,什么时候使用反射合适呢?我认为,在编写一般代码无法实现某个功能而反射能实现,或者实现某个功能的一般代码比起使用反射要繁琐很多时,可以考虑使用反射。
RTTI和反射
RTTI和反射都能在运行时发现和使用类型信息,不过反射相比于RTTI更加强大。另外,对于RTTI,编译器是在编译时打开和检查.class
文件的;而反射在运行时打开和检查.class
文件。
- Java RTTI与反射
- java-----RTTI与反射
- Java RTTI与反射
- Java RTTI与反射
- Java RTTI与反射
- Java中的RTTI与反射
- Java基础:RTTI与反射
- 【Java基础】RTTI与反射之Java
- 【Java基础】RTTI与反射之Java
- java:RTTI与反射的区别
- JAVA的反射机制与RTTI
- RTTI与反射
- RTTI与反射区别
- RTTI与反射
- java RTTI和反射
- 《Java编程思想》之类型检查(RTTI与反射机制)
- Java:RTTI与反射机制的一些概念
- java的RTTI与反射机制的区别和联系
- 有道2.1,多了好多参数,爬取失败...
- 基于讯飞语音,百度语音,图灵机器人树莓派的智能语音机器人毕业设计第二天
- UVA 10375 Choose and divide
- PHP字符串常用函数总结
- together进度记录6
- Java RTTI与反射
- 封装
- Arduinoの温湿度OLED及串口显示
- I have a dream
- 我的梦想
- 教育技术发展方向
- ASP.NET+MVC+EF6.0项目部署时BUG实录
- 教育技术的发展
- 教育技术的定义