反射是指java在运行过程中,能够获取并调用任意类的所有属性和方法;
如同是类加载的过程,将类的各个组成部分封装成其他对象(Field,Method,Constructor),这便完成了一次反射
反射.png
(系统自带的画图做这种图挺顺手的

为什么使用反射?

在反射面前,没有任何权限修饰的概念,运用反射可以在程序运行过程中操作任意对象;同时可以解耦,提高程序的可扩展性,代码的灵活性。

使用反射

获取Class对象

在进行操作之前,我们需要获取Class对象

  • 获取Class对象的方式
    1
    2
    3
    1.Class.forName("类名"); 将字节码文件加载进内存,并返回Class对象(详细包路径)
    2.类名.class; 通过类名的class属性来获取Class对象
    3.Object.getClass(); 通过继承Object类下的getClass()来获取Class对象

抽象了一个测试用的Book类

1
2
3
4
5
6
7
8
public class Book {
String name;
private int price;
public String author;
public Book(){}
public Book(String name){}
....
}

使用如下代码来获取Class对象

1
2
Class bookClass = Book.class;
System.out.println(bookClass);

此时控制台打印

class reflect.Book

不同的获取Class方法适用于不同的使用场景,但需知一个类只会产生一个class对象.

获取Field

在获取Class对象后,便可以调用其中的成员变量了
Class使用Field描述成员变量,以下是获取方法

1
2
3
4
1.Field[] getFields(); 获取所有public修饰的成员变量
2.Field getField(String name); 根据成员变量名获取
3.Field[] getDeclaredFields(); 获取所有成员变量(包括私有变量)
4.Field getDeclaredField(String name); 根据成员变量名获取(包括私有变量)
  • 在获取成员变量Field后,我们可以进行获取与设置值操作
    1
    2
    set(Object obj,Object value); 设置值
    get(Object obj); 获取值

测试代码

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
public static void main(String[] args) throws Exception {
Class bookClass = Book.class;
System.out.println(bookClass);
Book book = new Book("精灵宝钻","托尔金",80);
//获取所有public修饰的成员变量
Field[] fields = bookClass.getFields();
for(Field field : fields){
System.out.println(field);
}
System.out.println("---------");
Field author = bookClass.getField("author");
//获取book对象成员变量author的值
Object value = author.get(book);
System.out.println(value);
System.out.println("---------");
//设置book对象成员变量author的值
author.set(book,"约翰·罗纳德·瑞尔·托尔金");
System.out.println(value);
//获取私有成员变量Price
Field priceField = bookClass.getDeclaredField("price");
//忽略访问权限修饰符的安全检查(暴力反射)
priceField.setAccessible(true);
Object value2 = priceField.get(book);
System.out.println(value2);
}

控制台输出

class reflect.Book
public java.lang.String reflect.Book.author
/———
托尔金
/———
约翰·罗纳德·瑞尔·托尔金
80

需要注意的是,在访问私有成员变量(暴力反射)的时候,需要先使用setAccessible(boolean bool)来忽略权限修饰符

获取Constructor

Constructor用以描述类构造器,使用以下几种方式来获取Constructor对象

1
2
3
4
5
6
1.Constructor<?>[] getConstructors()  
2.Constructor<T> getConstructor(类<?>... parameterTypes)

3.Constructor<?>[] getDeclaredConstructors()
4.Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
方法的描述与上文中Field的获取相同,故不再赘述
  • Constructor下有几种方法用以创建新的类对象
    1
    2
    T newInstance(Object... initargs);
    值的留意的是,当使用空参构造方法创建对象时,直接使用Class下的newInstace()即可

测试代码

1
2
3
4
5
6
7
8
9
public static void main(String[] args) throws Exception {
Class bookClass = Book.class;
Book book = new Book("精灵宝钻","托尔金",80);
//获取构造器对象constructor
Constructor<Book> constructor = bookClass.getConstructor(String.class);
//使用构造器创建新的对象
Book newBook = constructor.newInstance("魔戒");
System.out.println(newBook.getName());
}

控制台输出

魔戒

获取Method

Method代表成员方法,使用以下几种方式获取

1
2
3
4
5
1.Method[] getMethods()  
2.Method getMethod(String name, 类<?>... parameterTypes)

3.Method[] getDeclaredMethods()
4.Method getDeclaredMethod(String name, 类<?>... parameterTypes)
  • Method下的方法
    1
    Object invoke(Object obj, Object... args); 执行方法

测试代码

1
2
3
4
5
6
7
8
9
public static void main(String[] args) throws Exception {
Class bookClass = Book.class;
Book book = new Book("精灵宝钻","托尔金",80);
//获取Method
Method method = bookClass.getMethod("setName",String.class);
//执行Method方法
method.invoke(book,"贝伦与露西恩");
System.out.println(book.getName());
}

控制台输出

贝伦与露西恩