1.Java反射概念
1.1定义
Java反射机制是指在运行状态下,对于任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。简单来说,就是只要知道类的名称,就能获取到它的属性和方法。
1.2反射的优缺点
为什么要用反射机制?直接创建对象不就可以了么,这就涉及到静态和动态的概念。
- 静态编译:在编译时确定类型,绑定对象
- 动态编译:运行时确定类型,绑定对象。动态编译最大限度的发挥了java的灵活性,提现了多态的应用,降低了类之间的耦合性。
- 优点
可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中它的灵活性就表现的十分明显。比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。 - 缺点
对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。
2.Class类
2.1概念
我们常说:
在面向对象的世界里,万事万物皆为对象。
Java里对象是什么,它是一个类的实例,那么我们创建的类是一个对象么?答案:是。类是java.lang.Class类的对象。(there is a class named Class)
Java程序在运行时,系统一直对所有的对象进行所谓的运行时类型标识。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。Class没有公共的构造方法,Class对象是在加载类时由Java虚拟机以及通过调用类加载器中的defineClass()方法自动构造的。
在运行程序的时候,JVM会首先检查所要加载的类对应的Class对象是否已经加载,如果没有加载,则会根据类名查找.class文件,并将其Class对象载入。
2.2类的对象如何表示
搞明白了Class类,下面我们看看Class类的实例如何表示。如下:
public class ReflectionTest1 {
public static void main (String[] args){
//通常A的实例对象的表示
A a = new A();
//表示方式一:直接访问class变量 --->实际在告诉我们每一个类都一个隐含的静态成员变量class
Class class1 = A.class;
//表示方式二:已知该类的对象 调用getClass方法
Class class2 = a.getClass();
//class1 class2称为A类的类类型(class type),同时也是Class类的对象,这个对象我们称之为该类的类类型
//不管class1 or class2都代表了A的类类型,一个类只可能是Class类的一个实例对象
System.out.println(class1 == class2); //true
//表示方式三:
try {
Class class3 = Class.forName("com.learn.java.A");
System.out.println(class3 == class2); //true
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class A{
public void run(){
System.out.println("A's run method!");
}
}
我们可以看到得到类对象的实例有3中方式,可以发现:
- 得到的Class类的实例我们称为对应类的类类型(class type)
- 不管我们以三种方式中的哪一种方法得到Class类的实例,其他他们都是一个实例。从原理上讲,当JVM发现这个类的Class对象没有被加载的时候,才会去加载生成Class对象,所以只会有一个。
- 第三种方式,Class.forName()方法中的字符串须为类的全路径名。
从上面的代码中我们得到了class1 class2 class3,这三个都是Class类的对象,也就是对应的A类,那如何得到A类的对象了?又是怎么可以调用方法了?代码修改如下:
public class ReflectionTest1 {
public static void main (String[] args){
A a = new A();
Class class1 = A.class;
try {
A a1 = (A)class1.newInstance(); //这里得到的a1就是A的一个实例
a1.run();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
注意这里,如果调用newInstance()方法,需要保证A类中有无参构造。
3.反射的使用方法
3.1获取构造函数
上面代码中我们可以通过class1.newInstance()的方式,获取得到A的一个实例对象,但是这种方式有一个前提,就是必须有一个无参构造方法,那如果是没有或者想调用有参数的构造器,怎么办了?下面我们就来看看。
类的成构造函数也是一个对象,它是java.lang.reflect.Constructor的一个对象,所以我们通过java.lang.reflect.Constructor里面封装的方法来获取这些信息。
1.获取单个的构造方法。
public Constructor getConstructor(Class>... parameterTypes) //获取该类指定参数的public访问权限的构造器
public Constructor getDeclaredConstructor(Class>... parameterTypes) //获取该类指定参数的所有构造器
例如:
public class ReflectionTest1 {
public static void main (String[] args){
try {
Class classA = Class.forName("com.learn.java.A");
Constructor constructor = classA.getConstructor(int.class);
System.out.println(constructor.getName());
A a = (A)constructor.newInstance(6);
a.run();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
class A{
private int age;
private String name;
public A(int age) {
this.age = age;
}
public A(String name) {
this.name = name;
}
public A(int age, String name) {
this.age = age;
this.name = name;
}
public void run(){
System.out.println("A's run method!");
}
}
//运行结果:
//com.learn.java.A
//A's run method!
2.获取所有的构造函数
public Constructor>[] getConstructors() throws SecurityException
public Constructor>[] getDeclaredConstructors() throws SecurityException
如下:
public static void main (String[] args){
try {
Class classA = Class.forName("com.learn.java.A");
Constructor[] constructors = classA.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//返回结果:
//public com.learn.java.A(int,java.lang.String)
//public com.learn.java.A(java.lang.String)
//public com.learn.java.A(int)
A类中的方法同上。
3.2获取成员变量
同样,在java.lang.reflect的包中,有一个Field的类,类的成员变量也是一个对象,它是java.lang.reflect.Field的一个对象,所以我们通过java.lang.reflect.Field里面封装的方法来获取这些信息。
1.单个的获取成员变量
public Field getField(String name) //获取指定参数的public变量
public Field getDeclaredField(String name) //获取指定参数的所有变量
示例如下:
public static void main (String[] args){
try {
Class classA = Class.forName("com.learn.java.A");
//方式一:getField
//Field field = classA.getField("age");
//方式二:getDeclaredField
Field field = classA.getDeclaredField("age");
System.out.println(field);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
//输出:
//方式一:
//java.lang.NoSuchFieldException: age
//方式二:
//private int com.learn.java.A.age
2.获取所有的成员变量
public Field[] getFields() throws SecurityException
public Field[] getDeclaredFields() throws SecurityException
3.3获取方法
类的方法也是一个对象,它是java.lang.reflect.Method的一个对象,所以我们通过java.lang.reflect.Method里面封装的方法来获取这些信息。
1.获取单个的方法
public Method getDeclaredMethod(String name, Class>... parameterTypes) // 得到该类所有的方法,不包括父类的
public Method getMethod(String name, Class>... parameterTypes) // 得到该类所有的public方法,包括父类的
示例
public class ReflectionTest1 {
public static void main (String[] args){
try {
Class classA = Class.forName("com.learn.java.A");
Object a = classA.newInstance();
//方式二:getDeclaredMethod()
System.out.println("second type:");
Method method2 = classA.getDeclaredMethod("introduce", String.class, int.class);
System.out.println(method2);
method2.setAccessible(true); //因为introduce方法是私有的,所以需要设置
method2.invoke(a, "meimei", 18);
//方式一:getMethod()
System.out.println("first type:");
Method method1 = classA.getMethod("introduce", String.class, int.class);
System.out.println(method1);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
class A{
private int age;
private String name;
private void introduce(String name, int age) {
System.out.println("hello ! my name is " + name + ", I'm " + age +" years old!");
}
public void run(){
System.out.println("A's run method!");
}
}
//运行结果:
//second type:
//private void com.learn.java.A.introduce(java.lang.String,int)
//hello ! my name is meimei, I'm 18 years old!
//first type:
//java.lang.NoSuchMethodException: com.learn.java.A.introduce(java.lang.String, int)
at java.lang.Class.getMethod(Class.java:1773)
at com.learn.java.ReflectionTest1.main(ReflectionTest1.java:20)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
2.获取所有的方法
public Method[] getDeclaredMethods() throws SecurityException // 得到该类所有的方法,不包括父类的
或者:
public Method[] getMethods() throws SecurityException// 得到该类所有的public方法,包括父类的
4.反射的应用
4.1动态加载
详见文章列表
4.2集合类反射原理剖析
详见文章列表
4.3框架相关反射应用
spring 的 ioc/di 也是反射....
javaBean和jsp之间调用也是反射....
struts的 FormBean 和页面之间...也是通过反射调用....
JDBC 的 classForName()也是反射.....
hibernate的 find(Class clazz) 也是反射...
参考
Java反射机制
反射机制的理解与用途
Java反射入门
Java反射机制
Java反射机制深入理解