基本概念
在计算机科学中,反射是指计算机程序在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力。在Java中,反射允许在编译期间不知道接口的名称,字段、方法的情况下,在运行时检查类、接口、字段和方法。它还允许实例化新对象、修改成员变量和调用方法。
使用
要在Java中使用反射,就必须使用到Class类。在面向对象的世界里,万事万物皆对象。所以类也是对象,类是java.lang.Class的实例化对象。也就是在Java中任何一个类都是都是Class类的实例化对象。在进行反射操作前必须先取得该类的类类型(即该类对应的Class类的实例化对象),有三种方法可以完成此项操作:
假定需要进行反射操作的类如下:
class A{
private String name;
public int age;
public String getName(){
return this.name;
}
public A(){}
public A(String name, int age){
this.name = name;
this.age = age;
}
public void print(int a, int b){
System.out.println(a + b);
}
public void print(String a, String b){
System.out.println(a + b);
}
}
- 通过任何一个类中都有隐含的静态成员变量class取得
Class c1 = A.class;
- 若已知某个类的实例化对象,通过getClass方法取得
A a = new A();
Class c2 = a.getClass();
- 通过Class类提供的forName(包名.类名称)方法取得(此方法可用于类的动态加载,详情见后)
Class c3 = Class.forName("A");
注: 一个类只可能是Class类的一个实例对象,所以以上c1、c2、c3是相同的
测试代码
package practicve.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* Created by lin_y on 2017/6/17.
*/
class A{
private String name;
public String getName(){
return this.name;
}
public void print(int a, int b){
System.out.println(a + b);
}
public void print(String a, String b){
System.out.println(a + b);
}
}
public class Refelct {
public static void main(String [] args) throws ClassNotFoundException {
// 方式一:通过类中的隐含静态变量class获取A类的类类型(Class类对象)
Class c1 = A.class;
// 方式二:通过A的对象a调用getClass方法获取A的类类型(Class类对象)
A a = new A();
Class c2 = a.getClass();
// 方式三:使用Class类的静态方法forName获取A的类类型(Class类对象)
Class c3 = Class.forName("A");
// 一个类只可能是Class类的一个实例对象(c1、c2、c3相等)
System.out.println(c1 == c2 && c2 == c3);
}
}
运行结果
获取Class的实例对象后,便可完成该类的反射操作。下面主要介绍三种反射操作:
1. 构造函数的反射操作
2. 成员变量的反射操作
3. 方法的反射操作
- 构造方法的反射操作
class A{
private String name;
public int age;
public String getName(){
return this.name;
}
public A(){}
public A(String name, int age){
this.name = name;
this.age = age;
}
public void print(int a, int b){
System.out.println(a + b);
}
public void print(String a, String b){
System.out.println(a + b);
}
public String toString(){
return "name:" + this.name + " age:" + this.age;
}
}
public class Refelct {
public static void main(String [] args) throws Exception {
// 方式三:使用Class类的静态方法forName获取A的类类型(Class类对象)
Class c = Class.forName("practicve.reflect.A");
// 获取含参构造方法
Constructor cs = c.getConstructor(String.class, int.class);
// 调用构造方法实例化A类对象
Object obj = cs.newInstance("zhangsan", 20);
System.out.println(obj.toString());
}
}
程序运行结果
- 成员变量的反射操作
package practicve.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* Created by lin_y on 2017/6/17.
*/
class A{
private String name;
public int age;
public String getName(){
return this.name;
}
public A(){}
public A(String name, int age){
this.name = name;
this.age = age;
}
public void print(int a, int b){
System.out.println(a + b);
}
public void print(String a, String b){
System.out.println(a + b);
}
public String toString(){
return "name:" + this.name + " age:" + this.age;
}
}
public class Refelct {
public static void main(String [] args) throws Exception {
// 方式三:使用Class类的静态方法forName获取A的类类型(Class类对象)
Class c = Class.forName("practicve.reflect.A");
// 获取A类中的成员变量
Field name = c.getDeclaredField("name");
Field age = c.getDeclaredField("age");
// 允许改变private变量
name.setAccessible(true);
A a = new A();
// 变量反射操作
name.set(a, "zhangsan");
age.set(a, 20);
System.out.println(a.toString());
}
}
程序运行结果
- 方法的反射操作
package practicve.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* Created by lin_y on 2017/6/17.
*/
class A{
private String name;
public int age;
public String getName(){
return this.name;
}
public A(){}
public A(String name, int age){
this.name = name;
this.age = age;
}
public void print(int a, int b){
System.out.println(a + b);
}
public void print(String a, String b){
System.out.println(a + b);
}
public String toString(){
return "name:" + this.name + " age:" + this.age;
}
}
public class Refelct {
public static void main(String [] args) throws Exception {
// 方式三:使用Class类的静态方法forName获取A的类类型(Class类对象)
Class c = Class.forName("practicve.reflect.A");
// 获取A类中的方法
Method m1 = c.getDeclaredMethod("print", String.class, String.class);
Method m2 = c.getDeclaredMethod("print", int.class, int.class);
A a = new A();
// 方法的调用
m1.invoke(a, "12", "34");
m2.invoke(a, 12, 34);
}
}
程序运行结果
注: 方法的名称和方法的参数列表才能唯一确定一个方法
附:动态加载
基本概念:使用 new 创建对象是静态加载类,在编译时刻就需要加载所有的可能使用到的类。而动态加载类,是在运行时刻加载所需的类。
比如说,要写一个Office软件,里面包含多种功能,便可以使用动态加载类来实现 需要什么功能在加载指定类即可,不需提前将所有功能加载,可以实现系统的在线升级或添加功能 ,具体实现见如下例子:
OfficeAble接口
package practicve.reflect;
/**
* Created by lin_y on 2017/6/17.
*/
public interface OfficeAble {
public void print();
}
Office主类
package practicve.reflect;
/**
* Created by lin_y on 2017/6/17.
*/
public class Office {
public static void main(String [] args) throws Exception {
OfficeAble office = (OfficeAble) Class.forName(args[0]).newInstance();
office.print();
}
}
Word功能
package practicve.reflect;
/**
* Created by lin_y on 2017/6/17.
*/
public class Word implements OfficeAble {
@Override
public void print() {
System.out.println("Word start ...");
}
}
Excel功能
package practicve.reflect;
/**
* Created by lin_y on 2017/6/17.
*/
public class Excel implements OfficeAble{
@Override
public void print() {
System.out.println("Excel start ...");
}
}
程序运行时args[0]参数为practicve.reflect.Word,程序运行结果为:
注: 1.为解决类型强制转换问题:使用接口(标准);2.功能性的类应该尽可能使用动态加载的方式完成,这样可以实现产品的在线升级,添加功能等。