多读书多实践,勤思考善领悟

浅析Java反射

本文于1931天之前发表,文中内容可能已经过时。

一、反射概述

  1. 能够分析类能力的程序称为反射

  2. JAVA反射机制是在运行状态中:

    • 对于任意一个类,都能够知道这个类的所有属性和方法;
1
2
* 对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
* 要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
  1. 反射就是把java类中的各种成分映射成一个个的Java对象

    • 例如:一个类有:成员变量、方法、构造方法、包等等信息
    • 利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
    • (其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)
    • 如图是类的正常加载过程:反射的原理在与class对象。
    • 熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。

img

image

  1. 若要学习反射,必须了解Class类

二、Class类

  • Class 类的实例表示正在运行的 Java 应用程序中的类和接口。
1
* 也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型)
  • Class 没有公共构造方法。
  • Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。

返回Class类型实例

  1. Object类中的getClass()方法

    1
    2
    3
    4
    5
    6
    7
    Employee e;
    Class c1 = e.getClass(); //获取到Employee类

    /*
    此时还有一个常用的方法getName()
    */
    System.out.println(c1.getName());
  2. T.class(),此时T为任意的java类型

    1
    2
    3
    Class c1 = Random.class;
    Class c2 = int.class;
    Class c3 = Double[].class;
  3. 通过Class类中的static方法forName()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    String className = "java.util.Random";

    try{
    Class c1 = Class.forName(className);
    System.out.println(c1.getName());
    }
    catch(ClassNotFoundException e){
    e.getMessage();
    }

    /*
    1. forName()方法抛出ClassNotFoundException
    2. className必须是类名或者接口
    */

通过反射获取构造方法并使用

自定义Employee类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package reflect;

import java.time.LocalDate;

public class Employee{

//data field
private String name;
private double salary;
private LocalDate hireDay;

//constructor
public Employee() {
System.out.println("c1.getConstructor().newInstance()被执行,调用了无参构造器");
}


public Employee(String name,double salary,int year,int month,int day){
this.name = name;
this.salary = salary;
this.hireDay = LocalDate.of(year,month,day);
System.out.println("getConstructor(Class,Class....).newInstance(para,para....)被执行");
}

//method
public String getName(){
return name;
}

public double getSalary(){
return salary;
}

public LocalDate gethireDay(){
return hireDay;
}

public void raiseSalary(double byPercent){
salary += salary * byPercent/100;
}

public String toString() {
return "name : " + name + "salary : " + salary + " hireDay : " + hireDay;
}
}
获取构造方法并使用
  • 获取public构造器

    • Constructor constructors = c1.getConstructors();
  • 获取所有构造器

    • Constructor[] constructors = className.getDeclaredConstructors();
  • newIntance()方法的使用

    • 调用无参构造器

      • Constructor con = c1.getConstructor();
      • con.newInstance();
    • 调用含参构造器

      • Constructor paraCons = c1.getConstructor(String.class,double.class,int.class,int.class,int.class);

      • paraCons.newInstance(“调用有参构造器”,600,2018,7,18));

        1
        2
        3
        public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
        throws NoSuchMethodException,
        SecurityException
      • 返回值为Constructor泛型,参数个数与实际构造器一致,但参数类型为Class

获取构造方法并使用例子程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class TestConstructors {

public static void main(String[] args) {
Class c1 = null;

//获取Class对象
try {
c1 = Class.forName("d_1reflect.Employee");
}catch(ClassNotFoundException e) {
e.printStackTrace();
}

//获取构造方法
System.out.println("========获取所有构造方法========");
Constructor<Employee>[] constructors = c1.getDeclaredConstructors();

//输出构造方法
for(Constructor cons : constructors) {
System.out.println(cons);
}

System.out.println();

//使用构造方法中的无参构造器
System.out.println("======使用构造方法中的无参构造器======");

try {
Constructor<Employee> nonParaCons = c1.getConstructor();
System.out.println(nonParaCons.newInstance());
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}

System.out.println();

//使用构造方法中的含参构造器
System.out.println("======使用构造方法中的含参构造器======");
try {
Constructor<Employee> paraCons = c1.getConstructor(String.class,double.class,int.class,int.class,int.class);
System.out.println(paraCons.newInstance("调用有参构造器",600,2018,7,18));
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

通过反射获取普通方法并使用

  • 获取所有普通方法

    • Method[] method = c1.getDeclaredMethods();
  • 获取单个普通方法

    • Method m = c1.getDeclaredMethod(“getName”);

      1
      2
      public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
      throws NoSuchMethodException,SecurityException
      • 返回值为Method类型,两个参数,第一个参数为String类,即方法名称,第二个参数为Class类型
  • 使用普通方法

    1
    2
    3
    4
    public Object invoke(Object obj,Object... args)
    throws IllegalAccessException,
    IllegalArgumentException,
    InvocationTargetException
    • 返回值为Object,一共两个参数:
    • 第一个参数Object是指调用该方法的对象,第二个参数是实参

通过反射获取普通方法并使用例子程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class TestMethod {
public static void main(String[] args) {
//获取Class对象
Class c1 = null;

try {
c1 = Class.forName("d_1reflect.Employee");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}

//获取所有普通方法并输出
Method[] method = c1.getDeclaredMethods();

System.out.println("===========获取所有普通方法===========");
for(Method m : method) {
System.out.println(m);
}

///获取单个普通方法
System.out.println("===========获取单个普通方法===========");
Method m = null;
try {
m = c1.getDeclaredMethod("getName");
System.out.println(m);
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}

//使用普通方法
System.out.println("===========使用普通方法===========");
Employee emp;
try {
emp = (Employee)c1.getDeclaredConstructor(String.class,double.class,int.class,int.class,int.class).newInstance("hahaha",200,2018,7,18);
System.out.println("调用Method类中invoke(Object,para)方法后 : " + m.invoke(emp, null));
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
}
}

/*
* 在JDK1.8中输出结果为:
* -----------------------------------------------------------------------
* ===========获取所有普通方法===========
public java.lang.String d_1reflect.Employee.toString()
public java.lang.String d_1reflect.Employee.getName()
public void d_1reflect.Employee.setName(java.lang.String)
public double d_1reflect.Employee.getSalary()
public java.time.LocalDate d_1reflect.Employee.gethireDay()
public void d_1reflect.Employee.raiseSalary(double)
public java.lang.String d_1reflect.Employee.testMethod(java.lang.String)
===========获取单个普通方法===========
public java.lang.String d_1reflect.Employee.getName()
===========使用普通方法===========
调用Method类中invoke(Object,para)方法后 : hahaha
-----------------------------------------------------------------------
* */

通过反射获取成员变量

  • 获取所有成员变量信息

    • Field[] fields = c1.getDeclaredFields();
  • 获取public成员变量信息

    • Field[] fields = c1.getFields();
  • 获取单个成员变量信息

    • Field f = c1.getDeclaredField(“name”);

      1
      2
      public Field getDeclaredField(String name)
      throws NoSuchFieldException,SecurityException
    • 返回值为Field,有一个参数,为成员变量名字

  • 使用成员变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Employee emp = (Employee)c1.getConstructor().newInstance();

    f.setAccessible(true);//暴力反射,解除私有设定

    f.set(emp, "xixixi");



    public void set(Object obj,Object value)
    throws IllegalArgumentException,
    IllegalAccessException
    • 无返回值,共两个参数,第一个参数是:要设置的对象,第二个参数是要设置的值
    • 此时注意private的成员变量不可直接访问,但是
    • setAccessible()方法可解除私有设定

通过反射获取成员变量例子程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class TestField {
public static void main(String[] args) {

Class c1 = null;
//获取Class对象
try {
c1 = Class.forName("d_1reflect.Employee");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

//获取成员变量信息
Field[] fields = c1.getDeclaredFields();

System.out.println("=========获取成员变量信息=========");
for(Field field : fields) {
System.out.println(field);
}

//获取单个成员变量信息
System.out.println("=========获取单个成员信息=========");
Field f = null;
try {
f = c1.getDeclaredField("name");
System.out.println(f);
} catch (NoSuchFieldException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

//使用成员变量
Employee emp;
try {
emp = (Employee)c1.getConstructor().newInstance();
f.setAccessible(true);//暴力反射,解除私有设定
f.set(emp, "xixixi");
System.out.println("使用了set方法修改成员变量 name: " + emp.getName());
} catch (IllegalArgumentException | IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/*
* 在JDK1.8中输出结果为:
* ---------------------------------------------------------
* =========获取成员变量信息=========
private java.lang.String d_1reflect.Employee.name
private double d_1reflect.Employee.salary
private java.time.LocalDate d_1reflect.Employee.hireDay
=========获取单个成员信息=========
private java.lang.String d_1reflect.Employee.name
c1.getConstructor().newInstance()被执行,调用了无参构造器
使用了set方法修改成员变量 name: xixixi
---------------------------------------------------------
* */

反射与配置文件结合使用

  • 配置文件pro.txt,注意是要放在工程下面

    1
    2
    className = d_1reflect.Employee
    methodName = testMethod
  • 主程序:从配置文件中获取ClassName生成Class对象,并调用方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public static void main(String[] args) {

    Class c1 = null;
    //获取创建的Class对象
    try {
    c1 = Class.forName(getValue("className"));
    } catch (ClassNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }

    Method m;
    try {
    m = c1.getDeclaredMethod(getValue("methodName"));
    m.invoke((Employee)c1.getConstructor().newInstance());
    } catch (NoSuchMethodException | SecurityException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } catch (IllegalAccessException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } catch (IllegalArgumentException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } catch (InvocationTargetException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } catch (InstantiationException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }
  • 配置文件的调用与返回

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public static String getValue(String str) {
    Properties pro = new Properties(); //获取配置文件对象
    FileReader in;
    try {
    in = new FileReader("pro.txt");
    pro.load(in);
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }
    return pro.getProperty(str);
    }

    /*
    * 在JDK1.8中输出结果为:
    * --------------------------------------------------
    * c1.getConstructor().newInstance()被执行,调用了无参构造器
    testMeod()方法被执行
    --------------------------------------------------
    * */