反射

反射概述

反射是Java中最强大的技术之一

JAVA反射机制是在运行状态中,对于任意一个,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

举例,扎小人。B扎的是对象A的稻草人,但是反射的是A这个人,A会疼痛。B就是调用者。一个东西能够映射到A对象,那个东西就叫做反射对象(稻草人)。而这个反射对象在Java中有一个对应的类叫做Class。我们首先要捏小人,捏小人这个过程,就是我们创建Class对象的过程。

在这里插入图片描述

反射就是说,我们已经能拿到Class对象了,拿到后我们可以指定操作某一个类里面的成员变量、成员方法、构造方法等。前提就是我们必须先获得Class对象。

获取Class类的三种方式

▪ 当用户想要获取任何一个Class类有三种方式:
1、通过Class.forName方法来获取对象
2、通过类名.class来获取
3、通过对象的getClass()来获取
4、如果是一个基本数据类型,那么可以通过Type的方式来获取Class对象

▪ 前三种方式的对比
– 第一种常用,只需要传入一个类的完全限定名即可
– 第二种需要导入对应的包,依赖太强
– 第三种已经创建对象,就意味着已经产生了Class类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//1、通过Class.forName方法来获取对象
// Class clazz = Class.forName("entity.Emp");
//2、通过类名.class来获取
// Class<Emp> clazz = Emp.class;
//3、通过对象的getClass()来获取
/**
* 每次在加载我们对象的时候,只要类加载器一旦加载我们class文件了,自然而然就会创建好一个Class对象。
* 面new了一个新对象,而我已经有了一个Class对象了,相当于充分利用过程。所以不太推荐。
*/
Class clazz = new Emp().getClass();
System.out.println(clazz.getPackage());
System.out.println(clazz.getName());//获取完整名称包名+类名entity.Emp
System.out.println(clazz.getSimpleName()); //Emp
System.out.println(clazz.getCanonicalName());//entity.Emp
// 4、如果是一个基本数据类型
Class<Integer> type1 = Integer.TYPE;
System.out.println(clazz.getName());// int
System.out.println(clazz.getCanonicalName());//int

对于大部分来说,getName()getCanonicalName()相同。前者返回由类对象表示的实体,后者返回由Java语言规范定义的基础类的规范名称。少部分时候不同,如果是数组就不同。如下:

1
2
3
4
5
6
7
Class type = args.getClass();
System.out.println(type.getName()); // [Ljava.lang.String;
System.out.println(type.getCanonicalName()); // java.lang.String[]

Class type1 = Integer[].class;
System.out.println(type1.getName());// [Ljava.lang.Integer;
System.out.println(type1.getCanonicalName());// java.lang.Integer[]

反射基本用途

反射的常用api

获取类的成员变量
获取成员变量,包括子类及父类,同时只能包含公共的类型 getField
此方法返回的是当前类(不包括父类)的所有属性,不仅仅是局限于公共访问修饰符,所有的访问修饰符都可以拿到 getDeclaredFields

获取类的成员方法
获取该对象的普通方法,包含的方法范围是当前对象及父类对象的所有公共方法 getMethods
获取当前类中所有的方法,无论什么访问修饰符 getDeclaredMethods

获取类的构造方法
构造方法比较特殊,它不能进行一个继承。
 只能获取公有的构造方法 getConstructors
 获取所有构造方法,无论是私有还是公有 getDeclaredConstructors

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
Class<?> clazz = Class.forName("reflect.Student");
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field);
System.out.println(field.getName());
System.out.println(field.getType());
System.out.println(field.getModifiers()); // 访问修饰符 public在常量表中对应public
System.out.println("--------");
}
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField.getName());
}

Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod.getName());
}

Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor.getName());
}
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor.getName());
}

反射 私有获取和调用

注意

  1. 反射在一定程度上破坏了封装性,需要合理使用
  2. 如果把Student中无参构造方法省略掉,会报错。因为new instance会调用无参构造方法

Field
1.获取到属性
2.创建一个对象
3.把属性放到对象中
4.强转后直接打印

1
2
3
4
5
6
7
8
9
10
Field address = clazz.getDeclaredField("address");
//设置该属性是否能被访问,true能。破坏了封装性
address.setAccessible(true); //如果设置允许访问,就可以进行下面操作,不会报错
System.out.println(address.getName());
Object o = clazz.newInstance();
address.set(o,"北京市");
System.out.println(((Student)o).getAddress());

//address
//北京市

Method

1
2
3
4
5
6
Method add = clazz.getDeclaredMethod("add", int.class, int.class);
add.setAccessible(true);
Object o1 = clazz.newInstance();
add.invoke(o1,123,123);

//246

constructor
如何调用私有的构造方法?

1
2
3
4
5
6
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(String.class, int.class, String.class);
declaredConstructor.setAccessible(true);
Student o2 = (Student)declaredConstructor.newInstance("msb",23,"java");
System.out.println(o2);

//Student{className='java', address='null', name='msb', age=23}