反射库提供了一个非常丰富且精心设计的工具集,以便编写能够动态操纵java代码的程序库。这项功能被大量地应用于JavaBeans中。反射机制提供了在运行状态中获得和调用修改任何一个类的属性和方法的能力。

  Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。

  首先让我们来看一个简单的小程序,感性的认识一下Java反射机制:

 import java.lang.reflect.*;

 public class reflection {
public static void main(String args[]) {
try {
Class c = Class.forName("java.util.Stack");
Method m[] = c.getDeclaredMethods(); for (int i = 0; i < m.length; i++)
System.out.println(m[i].toString());
}
catch (Throwable e){
System.err.println(e);
}
}
}

输出结果为:

public synchronized java.lang.Object java.util.Stack.pop()
public java.lang.Object java.util.Stack.push(java.lang.Object)
public boolean java.util.Stack.empty()
public synchronized java.lang.Object java.util.Stack.peek()
public synchronized int java.util.Stack.search(java.lang.Object)

  上述代码通过Java反射机制列出了java.util.Stack 类的各方法名以及它们的限制符和返回类型。接下来,让我们详细分析一下Java反射机制的原理和使用。

Class类

  要了解Java反射机制,首先必须得了解Class类。在程序运行期间,Java运行时系统始终为所有的对象维护一个被成为运行时的类型标识,这个信息跟踪着每个对象所属的类。虚拟机利用运行时信息选择相应的方法执行。保存这些信息的类被称为Class,这个名字很容易让人混淆。下面来看一个例子:

 package Reflect;

 /**
* 通过一个对象获得完整的包名和类名
* */
class Demo{
//other codes...
} class hello{
public static void main(String[] args) {
Demo demo=new Demo();
System.out.println(demo.getClass().getName());
}
}

  每个类中所包含的getClass()方法将会返回一个Class类的实例(getClass方法被定义在Object类中,Object类是所有类的祖先),最常用的Class方法是getName。这个方法将返回类的名字,如果类在某个包中,包的名字也作为类名的一部分返回。这也是获得Class对象的第一种方法。

运行结果:Reflect.Demo

还可以调用Class类的静态方法forName获得类名对应的Class对象:

String className = "java.util.Date";
Class cl = Class.forName(className);

  注意,className一定要包含包的路径。这是获得Class对象的第二种方法。

  这边再强调一下,Class类对象保存了每个对象所属类的信息,下面会讲到,通过Class类对象也可以创建一个相应类(Class保存信息的类)的实例,即某个对象。

  获得Class类对象的第三种方法很简单。如果T是任意的Java类型,T.class将代表匹配的对象。例如:

Class cl1 = Date.class;
Class cl2 = int.class;
Class cl3 = Double[].class;

  一个Class对象实际上表示的是一个类型,而这个类型未必一定是一种类。例如,int不是类,但int.class是一个Class类型的对象。

  通过newInstance()函数可以快速地创建一个类的实例:

 package Reflect;

 class Person{

     public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString(){
return "["+this.name+" "+this.age+"]";
} private String name;
private int age;
} class hello{
public static void main(String[] args) {
Class<?> demo=null;
try{
demo=Class.forName("Reflect.Person");
}catch (Exception e) {
e.printStackTrace();
}
Person per=null;
try {
per=(Person)demo.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
per.setName("Rollen");
per.setAge(20);
System.out.println(per);
}
}
运行结果:[Rollen  20]

  注意,newInstance方法调用默认的构造器初始化新创建的对象,如果这个类没有默认的构造器,就会抛出一个异常:

java.lang.InstantiationException: Reflect.Person
at java.lang.Class.newInstance0(Class.java:340)
at java.lang.Class.newInstance(Class.java:308)
at Reflect.hello.main(hello.java:39)
Exception in thread "main" java.lang.NullPointerException
at Reflect.hello.main(hello.java:47)

利用反射分析类的能力

  在java.lang.reflect包中有三个类Field、Method和Constructor分别用于描述类的域、方法和构造器。这三个类都有一个叫做getName的方法,用来返回项目的名称。Field类有一个getType方法,用来返回描述域所属类型的Class对象。Method和Constructor类有能够报告参数类型的方法,Method类还有一个可以报告返回类型的方法。这三个类还有一个叫做getModifiers的方法,它将返回一个整数,用不同的位开关描述public和static这样的修饰符使用状况。

  Class类中的getFields、getMethods和getConstructors方法将分别返回类提供的public域、方法和构造器数组,其中包括超类的共有成员。Class类的getDeclareFields、getDeclareMethods和getDeclareConstructors方法将分别返回类中声明的全部域、方法和构造器,包括私有成员和受保护成员,但不包括超类的成员

  下面看一个通过Class调用其他类中的构造函数的例子:

 package Reflect;

 import java.lang.reflect.Constructor;

 class Person{
public Person() {
}
public Person(String name){
this.name=name;
}
public Person(int age){
this.age=age;
}
public Person(String name, int age) {
this.age=age;
this.name=name;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString(){
return "["+this.name+" "+this.age+"]";
}
private String name;
private int age;
} class hello{
public static void main(String[] args) {
Class<?> demo=null;
try{
demo=Class.forName("Reflect.Person"); //Demo中保存了Person类的信息,包括域、方法和构造器
}catch (Exception e) {
e.printStackTrace();
}
Person per1=null;
Person per2=null;
Person per3=null;
Person per4=null;
//取得全部的构造函数
Constructor<?> cons[]= try{
per1=(Person)cons[0].newInstance();
per2=(Person)cons[1].newInstance("Rollen");
per3=(Person)cons[2].newInstance(20);
per4=(Person)cons[3].newInstance("Rollen",20);
}catch(Exception e){
e.printStackTrace();
}
System.out.println(per1);
System.out.println(per2);
System.out.println(per3);
System.out.println(per4);
}
}

返回一个实现类的接口:

 package Reflect;

 interface China{
public static final String name="Rollen";
public static int age=20;
public void sayChina();
public void sayHello(String name, int age);
} class Person implements China{
public Person() { }
public Person(String sex){
this.sex=sex;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public void sayChina(){
System.out.println("hello ,china");
}
@Override
public void sayHello(String name, int age){
System.out.println(name+" "+age);
}
private String sex;
} class hello{
public static void main(String[] args) {
Class<?> demo=null;
try{
demo=Class.forName("Reflect.Person");
}catch (Exception e) {
e.printStackTrace();
}
//保存所有的接口
Class<?> intes[]=demo.getInterfaces();
for (int i = 0; i < intes.length; i++) {
System.out.println("实现的接口 "+intes[i].getName());
}
}
}

运行结果:

实现的接口   Reflect.China

取得其他类中的父类:

 class hello{
public static void main(String[] args) {
Class<?> demo=null;
try{
demo=Class.forName("Reflect.Person");
}catch (Exception e) {
e.printStackTrace();
}
//取得父类
Class<?> temp=demo.getSuperclass();
System.out.println("继承的父类为: "+temp.getName());
}
}

运行结果:

继承的父类为:   java.lang.Object

  下面通过一个完整的例子来理解Field、Method和Constructor对象的具体使用。这个程序将提醒用户输入类名,然后输出类中所有的方法和构造器的签名,以及全部的域名。假如用户输入

  java.lang.Double

程序将会输出:

 public final class java.lang.Double extends java.lang.Number
{
public java.lang.Double(double);
public java.lang.Double(java.lang.String); public boolean equals(java.lang.Object);
public static java.lang.String toString(double);
public java.lang.String toString();
public int hashCode();
public static int hashCode(double);
public static double min(double, double);
public static double max(double, double);
public static native long doubleToRawLongBits(double);
public static long doubleToLongBits(double);
public static native double longBitsToDouble(long);
public volatile int compareTo(java.lang.Object);
public int compareTo(java.lang.Double);
public byte byteValue();
public short shortValue();
public int intValue();
public long longValue();
public float floatValue();
public double doubleValue();
public static java.lang.Double valueOf(java.lang.String);
public static java.lang.Double valueOf(double);
public static java.lang.String toHexString(double);
public static int compare(double, double);
public static boolean isNaN(double);
public boolean isNaN();
public static boolean isFinite(double);
public static boolean isInfinite(double);
public boolean isInfinite();
public static double sum(double, double);
public static double parseDouble(java.lang.String); public static final double POSITIVE_INFINITY;
public static final double NEGATIVE_INFINITY;
public static final double NaN;
public static final double MAX_VALUE;
public static final double MIN_NORMAL;
public static final double MIN_VALUE;
public static final int MAX_EXPONENT;
public static final int MIN_EXPONENT;
public static final int SIZE;
public static final int BYTES;
public static final java.lang.Class TYPE;
private final double value;
private static final long serialVersionUID;
}

下面是源码:

 public class ReflectionTest
{
public static void main(String[] args)
{
// read class name from command line args or user input
String name;
if (args.length > 0) name = args[0];
else
{
Scanner in = new Scanner(System.in);
System.out.println("Enter class name (e.g. java.util.Date): ");
name = in.next();
} try
{
// print class name and superclass name (if != Object)
Class cl = Class.forName(name);    
Class supercl =
String modifiers = if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.print("class " + name);
if (supercl != null && supercl != Object.class) System.out.print(" extends "
+ supercl.getName()); System.out.print("\n{\n");
System.out.println();
System.out.println();
System.out.println("}");
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
System.exit(0);
} /**
* Prints all constructors of a class
* @param cl a class
*/
public static void printConstructors(Class cl)
{
Constructor[] constructors =
   
for (Constructor c : constructors)
{
String name =
System.out.print(" ");
String modifiers =
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.print(name + "("); // print parameter types
Class[] paramTypes =
for (int j = 0; j < paramTypes.length; j++)
{
if (j > 0) System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
} /**
* Prints all methods of a class
* @param cl a class
*/
public static void printMethods(Class cl)
{
Method[] methods =

for (Method m : methods)
{
Class retType =
String name =
System.out.print(" ");
// print modifiers, return type and method name
String modifiers =
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.print(retType.getName() + " " + name + "("); // print parameter types
Class[] paramTypes =
for (int j = 0; j < paramTypes.length; j++)
{
if (j > 0) System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
} /**
* Prints all fields of a class
* @param cl a class
*/
public static void printFields(Class cl)
{
Field[] fields = cl.getDeclaredFields(); for (Field f : fields)
{
Class type = f.getType();
String name = f.getName();
System.out.print(" ");
String modifiers = Modifier.toString(f.getModifiers());
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.println(type.getName() + " " + name + ";");
}
}
}

在运行时分析对象

  在编译程序时,如果知道想要查看的域名和类型,查看指定的域是一件很容易的事情,而利用反射机制可以查看在编译时还不清楚的对象域。我们知道,通过调用getDeclareFields方法可获得Field类对象,前面的程序中我们也可以知道,通过Field对象的getType和getName方法可以得到域的类型和名字,而通过get方法我们可以得到对应域的值。如果f是一个Field类型的对象,obj是某个包含f域的类的对象,f.get(obj)将返回一个对象,其值为obj的f域的当前值。同样,我们可以通过set函数在运行中设置某一个域的值。

 class hello {
public static void main(String[] args) throws Exception {
Class<?> demo = null;
Object obj = null; demo = Class.forName("Reflect.Person");
obj = demo.newInstance(); Field field = demo.getDeclaredField("name");
field.setAccessible(true);
field.set(obj, "Jack");
System.out.println(field.get(obj));
}
}

  这里注意的一点是,反射机制的默认行为是受限于Java的访问控制的,name是一个私有的域,所以get和set访问它将会抛出一个异常。如果一个java程序没有受到安全管理器的控制,就可以覆盖访问控制。为了达到这个目的,需要调用Field、Method和Constructor对象的setAccessible方法。

 使用反射编写泛型数组代码

  在实际使用中我们常常会用到Arrays类的copyOf方法实现扩展已经填满的数组(动态的创建数组)。

Person[] a = new Person[100];
...
//arrays is full
a = Arrays.copyOf(a, 2*a.length);

  那么现在问题来了,我们不看源码,考虑如何实现这样一个通用的动态数组生成方法呢?我们首先可能会想到把Person[]数组转变为Object[]数组,So我们会这样实现:

public static Object[] badCopyOf(Object[] a, int newLength) {
Object[] newarray = new Object[newLength];
System.arraycopy(a, 0, newarray, 0, newLength);
return newarray;
}

  然而,在实际使用得到的结果数组时会出现问题,因为返回的结果为Object[]类型,不能被转换成其他类型的数组。将一个Person[]临时转化成Object[]数组,然后再把它转换回来是可以的,但一个从开始就是Object[]的数组却永远不能转换成Person[]数组。So,我们需要知道原数组的类型才能创建与原数组类型相同的新数组。而在构建数组的过程中非常重要的一个函数是java.lang.reflect包中Array类中的静态方法newInstance,它能够构造新数组:

Object newArray = Array.newInstance(componentType, newLength);

这两个参数,第一个是数组元素的类型,第二个是数组的长度。我们可以通过Array.getLength(a)来得到数组长度。

那么我们现在主要的任务是获得数组元素类型,一般需要进行以下工作:

1)首先获得a数组的类对象。

2)确认它是一个数组。

3)使用Class类(只能定义表示数组的类对象)的getComponentType方法确定数组对应的类型。

于是乎:

public static Object goodCopyOf(Object[] a, int newLength) {
Class cl = a.getClass();
if(!cl.isArray())
return null;
Class componentType = cl.getComponentType();
int length = Array.getLength(a);
Object newArray = Array.newInstance(componentType, length);
System.arraycopy(a, 0, newArray, 0, Math.min(newLength, length));
return newArray;
}

简单测试:

int[] a = {1, 2, 3, 4, 5};
a = (int[]) goodCopyOf(a, 10);

最后我们再附上jdk中copyOf的实现源码:

public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}

参考资料:

http://www.cnblogs.com/rollenholt/archive/2011/09/02/2163758.html

《java核心技术 卷一》

初探Java反射机制的更多相关文章

  1. Java反射机制详解(3) -java的反射和代理实现IOC模式 模拟spring

    IOC(Inverse of Control) 可翻译为“控制反转”,但大多数人都习惯将它称为“依赖注入”.在Spring中,通过IOC可以将实现类.参数信息等配置在其对应的配置文件中,那么当 需要更 ...

  2. 浅谈java反射机制

    目录 什么是反射 初探 初始化 类 构造函数 属性 方法 总结 思考 什么是反射 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意 ...

  3. 第28章 java反射机制

    java反射机制 1.类加载机制 1.1.jvm和类 运行Java程序:java 带有main方法的类名 之后java会启动jvm,并加载字节码(字节码就是一个类在内存空间的状态) 当调用java命令 ...

  4. Java反射机制

    Java反射机制 一:什么事反射机制 简单地说,就是程序运行时能够通过反射的到类的所有信息,只需要获得类名,方法名,属性名. 二:为什么要用反射:     静态编译:在编译时确定类型,绑定对象,即通过 ...

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

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

  6. java基础知识(十一)java反射机制(下)

    1.什么是反射机制? java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象都能够调用他的属性和方法,这种动态获取属性和方法的功能称为java的反射机制. ...

  7. Java反射机制专题

    ·Java Reflection Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方 ...

  8. java反射机制深入详解

    java反射机制深入详解  转自:http://www.cnblogs.com/hxsyl/archive/2013/03/23/2977593.html 一.概念 反射就是把Java的各种成分映射成 ...

  9. Java反射机制DOME

    Java反射机制 public class TestHibernate { @Test public void TestHb(){ try { Class cs = Class.forName(&qu ...

随机推荐

  1. python练习题:三级菜单

    需求:可依次选择进入各子菜单可从任意一层往回退到上一层可从任意一层退出程序所需新知识点:列表.字典 测试环境:win7系统,python3.7.0,工具:pycharm-community-2018. ...

  2. iOS 真机调试 Xcode 显示 device Error: device unavailable

    一般来说有两个原因: 1. iphone没有加到test device里,将iphone的设备id加到test device列表里 2. Xcode不支持当前的iOS版本,将Xcode升级到最新版

  3. Egret - timer

    相关:http://edn.egret.com/cn/index.php/article/index/id/154 1.Timer 的使用方法非常简单,我们只需要关心两个属性,三个方法和两个事件即可. ...

  4. ubuntu下安装jdk,tomcat,mysql,ftp,telnet,svn

    需求分析:自己弄了个小网站,想放到云服务器上,同时把自己积累的代码也放上去,服务器上的文件可以简单的在windows上查看,也可以方便的通过windows连接linux服务器. 解决:运行网站要用到j ...

  5. 进化的Spark, 从DataFrame说起

    进化的Spark, 从DataFrame说起:http://www.tuicool.com/articles/IzeY7zM

  6. Python—— *与** 参数说明

    Python *与** 参数说明 '''*用来传递任意个无名字参数,这些参数会一个Tuple的形式访问''' def fall(*z): print sum(z) print "keys t ...

  7. mysql 用户及赋予权限

    查询用户: use mysql; select host,user from mysql.user; 创建用户: create user 'mhc'@'%' identified by 'mhc.12 ...

  8. 解决windows 下mysql 表名自动转成小写的问题

    由于web用的是mvc,数据库用的是mysql.为了方便开发,在windows7下面也安装了个mysql,今天在创建表的时候,遇到了个棘手的问题.所有的表名都转成了小写,这不是我要的,作为处女座,是不 ...

  9. 第八章 高级搜索树 (b3)B-树:查找

  10. HttpClient实战二:单线程和多线程连接池实例

    为什么使用HTTP连接池? 随着系统架构风格逐渐向前后端分离架构,微服务架构转变,RestFul风格API的开发与设计,同时SpringMVC也很好的支持了REST风格接口.各个系统之间服务的调用大多 ...