Java反射(Reflection)机制及使用

一、什么是反射

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。但是JAVA有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。

二、Class类

知道一个类后,我们怎么知道这个类的所有属性和方法呢?这就需要用到Class类了。

Class 类十分特殊。它和一般类一样继承自Object,其实体用以表达Java程序运行时的classes和interfaces,也用来表达enum、array、primitive Java types(boolean, byte, char, short, int, long, float, double)以及关键词void。

当我们编写完一个 Java 项目之后,所有的 Java 文件都会被编译成一个.class 文件,这些 Class 对象承载了这个类型的父类、接口、构造函数、方法、属性等原始信息,这些 class 文件在程序运行时会被 ClassLoader 加载到虚拟机中。当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM 便自动产生一个Class 对象。我们通过 new 的形式创建对象实际上就是通过这些 Class 来创建,只是这个过程对于我们是不透明的而已。

  • Class类是Java反射的起源。
  • 运行中的类或接口在JVM中都会有一个对应的Class对象存在,它保存了对应类或接口的类型信息。
  • JVM为每种类型管理着一个独一无二的Class对象。

Class的API很多,具体大家可以查看文档,或者这个在线中文文档。

三、反射的具体使用

获得Class对象

有三种方式:
1.直接调用类的class

Class clazz = Dog.class;

2.调用对象的getClass()方法

Dog dog = new Dog("小黄");
Class clazz2 = dog.getClass();

3.使用Class类的forName(String className)方法,这里注意className要填写类的全部路径,包括包名和类名。

try {
    Class clazz3 = Class.forName("com.wangjj.android_training.reflection.Dog");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

获取构造方法并调用

返回所有:
getConstructors(): 获取所有"公有的"构造方法,返回一个包含某些 Constructor对象的数组。
getDeclaredConstructors(): 获取所有的构造方法(包括私有、受保护、默认、公有) 

返回单个:
getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法,参数是要获取的构造方法的参数Class
getDeclaredConstructor(Class... parameterTypes):获取单个的构造方法(包括私有、受保护、默认、公有),参数是要获取的构造方法的参数Class

调用构造方法: 
Constructor-->newInstance(Object... args) 

下面看一个例子:

public class Animal {
    protected String name;
    protected String color;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}

狗类继承自动物类,有三个构造方法,分别是私有有参,公有无参,公有有参的。

public class Dog extends Animal {
    private int loyalty;
    private int weight;


    private Dog(int loyalty){
        this.loyalty = loyalty;

    }

    public Dog(){

    }

    public Dog(String name){
       this.name = name;
    }


    public int getLoyalty() {
        return loyalty;
    }

    public void setLoyalty(int loyalty) {
        this.loyalty = loyalty;
    }

    public int getWeight() {
        return weight;
    }

    private void setWeight(int weight) {
        this.weight = weight;
    }

}
Class clazz = Dog.class;

System.out.println("**********************所有公有构造方法*********************************");
Constructor[] conArray = clazz.getConstructors();
for(Constructor c : conArray){
    System.out.println(c);
}


System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
conArray = clazz.getDeclaredConstructors();
for(Constructor c : conArray){
    System.out.println(c);
}

System.out.println("*****************获取公有、无参的构造方法*******************************");
try {
    Constructor con = clazz.getConstructor(null);
    System.out.println(con);
} catch (NoSuchMethodException e) {
    e.printStackTrace();
}

System.out.println("******************获取私有构造方法,并调用*******************************");
try {
    Constructor con = clazz.getDeclaredConstructor(int.class);
    //调用构造方法
    con.setAccessible(true);// 忽略掉访问修饰符
    Dog dog1 = (Dog) con.newInstance(100);
    System.out.println(con);
    System.out.println("忠诚度:"+ dog1.getLoyalty());

} catch (NoSuchMethodException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
} catch (InstantiationException e) {
    e.printStackTrace();
} catch (InvocationTargetException e) {
    e.printStackTrace();
}

打印结果如下:

**********************所有公有构造方法*********************************
public com.wangjj.android_training.reflection.Dog(java.lang.String)
public com.wangjj.android_training.reflection.Dog()
************所有的构造方法(包括:私有、受保护、默认、公有)***************
public com.wangjj.android_training.reflection.Dog(java.lang.String)
public com.wangjj.android_training.reflection.Dog()
private com.wangjj.android_training.reflection.Dog(int)
*****************获取公有、无参的构造方法*******************************
public com.wangjj.android_training.reflection.Dog()
******************获取私有构造方法,并调用*******************************
private com.wangjj.android_training.reflection.Dog(int)
忠诚度:100

获取成员变量并调用

返回所有:
Field[] getFields():获取所有的"公有字段",包括继承的公有字段
Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有,不包括继承的字段

返回单个:
public Field getField(String fieldName):获取某个"公有的"字段; 
public Field getDeclaredField(String fieldName):获取某个字段

设置字段的值: 
Field --> public void set(Object obj,Object value): 
参数说明: 
1.obj:要设置的字段所在的对象; 
2.value:要为字段设置的值; 

例如:

Field field = clazz.getDeclaredField("loyalty");
field.setAccessible(true);
field.set(dog1, 60);
System.out.println("忠诚度:"+ dog1.getLoyalty()); // 忠诚度:60

获取成员方法并调用

返回所有: 
public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类) 
public Method[] getDeclaredMethods():获取所有的成员方法,包括:私有、受保护、默认、公有(不包括继承的) 

返回单个: 
public Method getMethod(String name,Class... parameterTypes) 
public Method getDeclaredMethod(String name,Class... parameterTypes) 
参数: 
name : 方法名; 
Class ... : 形参的Class类型对象 


调用方法: 
Method --> public Object invoke(Object obj,Object... args): 
参数说明: 
obj: 要调用方法的对象; 
args:调用方式时所传递的实参; 

如下:

Method method = clazz.getDeclaredMethod("setWeight", int.class);
method.setAccessible(true);
method.invoke(mydog, 2);
System.out.println(mydog.getWeight());  // 2

四、反射的用途

反射最重要的用途就是开发各种通用框架。很多框架(比如Spring)都是配置化的(比如通过XML文件配置JavaBean,Action之类的),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射——运行时动态加载需要加载的对象。

当我们在使用IDE(如Eclipse,IDEA)时,当我们输入一个对象或类并想调用它的属性或方法时,一按点号,编译器就会自动列出它的属性或方法,这里就会用到反射。

反射还可以用来越过泛型检查。

参考

Java基础之—反射(非常重要)

深入解析Java反射(1) - 基础

你可能感兴趣的:(Java进阶)