Reflect反射

反射

为什么要使用反射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//定义一个类
class Person{
public Object work(){
//假设看不到源码,无法获取真实类型
return new Date();
}

//调用work方法
@Test
public void testWork(){
Person p = new Person();
Object obj = new Person();

//返回值是一个对象,可以继续调用该对象的方法.
Date d = (Date)obj;
//打印本地的时间风格,但是却不知道应该用什么来接收(不知道返回的是date)
String str = d.toLocalString();
System.out.println(str);
}
}

Person类是别人提供好的字节码文件.看不到源码是不能直接强转的.

不爽:我都已经获取到真正的对象了,居然不能知道他的真实类型.

什么是反射:

通过字节码(Class)对象,动态的获取该字节码中的成员(构造器,方法,字段,父类,包…)

字节码对象(Class):
将多个Class类抽象出来,形成一个字节码的结构(everything is object)

当JVM加载完字节码后.会使用一个对象,来描述该字节码文件的结构.该对象包含操作字节码中成员的方法.

字节码对象,是JVM加载字节码文件的时候,给我们创建的一个对象,一份字节码,只会创建一个字节码对象.

获取字节码对象的三种方法:

1.通过Class类的forName来获取.
    public static Class<?> forName(String className):根据全限定名来获取字节码对象.
    全限定名:包名.类名.指定到唯一的一个类上.
2.所有的对象,都有一个公共的方法.getClass.
    public final Class<?> getClass(): 通过对象返回字节码对象.
3.任何类型都有一个class属性.
    int.class /  Person.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
package me.cscar.reflect.getClassObject;

import org.junit.Test;

public class GetClassTest {
@Test
public void getClassObject() throws Exception {
//1.通过forName静态方法获取,抛出异常
Class clz = Class.forName("me.cscar.reflect.getClassObject.Person");

//2.通过对象.getClass()方法获取
Object obj = new Person();
Class clz1 = obj.getClass();

//3.任何类型都有一个class属性
Class clz2 = Person.class;

//class me.cscar.reflect.getClassObject.Person
System.out.println(clz);
//true
System.out.println(clz == clz1);
//true
System.out.println(clz == clz2);

//int类型的class属性不等于integer的class属性,结果为false
System.out.println(int.class == Integer.class);
//integer的包装类型是int,结果为true
System.out.println(int.class == Integer.TYPE);
//class [Ljava.lang.String;
System.out.println(String[].class);
}
}

构造器的操作

获取构造器
获取单个构造器
parameterTypes:参数类型
想要定位到一个构造器,必须要指明构造器的参数列表.

1.获取单个构造器:
public Constructor<T> getConstructor(Class<?>... parameterTypes):获取public的指定的构造器

Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) :获取不管权限的构造器

2.获取所有构造器:
Constructor<?>[] getConstructors() :获取所有public 的构造器

Constructor<?>[] 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
32
33
34
35
36
package me.cscar.reflect.getConstructor;

import org.junit.Test;

import java.lang.reflect.Constructor;

public class GetConstructor {
@Test
public void testGetConstructor() throws Exception {
//创建字节码对象
Class clz = Class.forName("me.cscar.reflect.getConstructor.Person");
//1.获取所有的public构造器
Constructor[] cons = clz.getConstructors();
for (Constructor ele : cons) {
//System.out.println(ele);
}

//2.获取所有的构造器
cons = clz.getDeclaredConstructors();
for (Constructor ele : cons) {
//System.out.println(ele);
}

//3.获取无参数构造器
Constructor con = clz.getConstructor();
//System.out.println(con);

//4.获取带参数的public构造器
Constructor conagsP = clz.getDeclaredConstructor(Long.class, String.class, int.class);
//System.out.println(conagsP);

//5.获取带参数的构造器(暴力反射)
Constructor conage = clz.getDeclaredConstructor(String.class, int.class);
System.out.println(conage);
}
}

通过构造器创建对象

public T newInstance(Object... initargs):通过构造器,创建对象.
    initargs:实际参数.

如果想要调用私有的成员.必须要先设置可以访问.

public void setAccessible(boolean flag): 设置为true.
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
package me.cscar.reflect.createObject;

import org.junit.Test;

import java.lang.reflect.Constructor;

public class CreateObject {
@Test
public void createObj() throws Exception {
Class<Person> clz = Person.class;

//1)调用空参构造
Constructor<Person> con = clz.getConstructor();
Person p = con.newInstance();
System.out.println(p);

//调用空参构造器方式二,字节码对象需要加泛型,直接使用字节码对象调用
p = clz.newInstance();
System.out.println(p);

//2)获取带参数构造并创建对象
Constructor<Person> conargs = clz.getConstructor(Long.class, String.class, int.class);
Person p1 = conargs.newInstance(9527L, "蛤蛤", 70);
System.out.println(p1);

//3)获取私有构造并创建对象
Constructor<Person> constructor = clz.getDeclaredConstructor(String.class, int.class);
//设置非public的成员访问许可
constructor.setAccessible(true);
Person p2 = constructor.newInstance("包包", 51);
System.out.println(p2);

}
}

操作方法

获取方法

name:方法名

parameterTypes:参数列表的类型

想要定位到一个方法,必须要指定方法签名(方法名+参数列表)

1.获取多个方法
Method[] getMethods() :获取到所有的public方法,包括继承的.

Method[] getDeclaredMethods() :获取所有的方法,不包括继承的.

2.获取单个方法
Method getMethod(String name, Class<?>... parameterTypes) :获取指定的public方法

Method getDeclaredMethod(String name, Class<?>... parameterTypes) :获取指定的方法,不管权限.
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
package me.cscar.reflect.getMethod;

import org.junit.Test;

import java.lang.reflect.Method;

public class GetMethod {
@Test
public void getMethod() throws Exception {

//使用反射创建对象
Class<Person> clz = Person.class;
Person p = clz.newInstance();

//1)执行无参无返回的方法
Method m = clz.getMethod("method1");
Object ret = m.invoke(p);
System.out.println(ret);

//2)执行有参无返回的方法
Method m1 = clz.getMethod("method2", String.class);
Object ret1 = m1.invoke(p, "蛤蛤");
System.out.println(ret1);

//3)执行无参有返回的方法
Method m2 = clz.getMethod("method3");
Object ret2 = m2.invoke(p);
System.out.println(ret2);

//4)执行有参有返回的方法
Method m3 = clz.getMethod("method4", String.class);
Object ret3 = m3.invoke(p, "蛤蛤蛤");
System.out.println(ret3);

//5)执行私有方法
Method m4 = clz.getDeclaredMethod("method5");
//设置可以访问
m4.setAccessible(true);
Object ret4 = m4.invoke(p);
System.out.println(ret4);

}
}

反射的其他API

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
63
package me.cscar.reflect.staticmethod;

import org.junit.Test;

import java.lang.reflect.Method;

public class MethodTest {

/**
* 调用静态方法
*/
@Test
public void testInvokeStaticMethod() throws Exception {
//找到静态方法
Class<?> clz = Class.forName("me.cscar.reflect.staticmethod.Person");
Method m = clz.getMethod("method6", String.class);
//调用方法.由于是static修饰,执行静态方法,不需要对象,第一个参数传递为null
m.invoke(null, "蛤蛤蛤");

}

/**
* 调用参数是基本类型数组的方法
*/
@Test
public void testInvokeIntArray() throws Exception {
//找到基本类型数组方法
Class<?> clz = Class.forName("me.cscar.reflect.staticmethod.Person");
Method m = clz.getMethod("method7", int[].class);
int[] arr = new int[]{1, 2, 3};
//person有公共的无参数构造器,可以直接通过,字节码对象去调用
m.invoke(clz.newInstance(), new Object[]{arr});

}

/**
* 调用参数是引用类型数组的方法
* 在Invoke方法中,第二个参数实际上要的是一个Object数组
* 底层将所有的实际参数放在一个Object类型的数组中
*/
@Test
public void testInvokeString() throws Exception {
Class<? extends Person> clz = new Person().getClass();
Method m = clz.getMethod("method8", String[].class);
String[] str = new String[]{"A", "B"};
//wrong number of arguments:参数个数错误
/*
如果显式的传递到Invoke方法中,已经是一个Object数组了.那么会解包
解开之后是->String[].class

如果传递的是散数据->1,2,3,会装成一个Object数组,装包之后,invoke再解包
解开之后是->int.class,int.class

基本类型数组,不是Object类型的数组,所以会装包,也会拆包.
把int类型数组装进Object数组,再拆成int类型数组

引用类型数组,已经是Object类型数组,不会装包,直接拆->
String.class,String.class不匹配方法参数的String[]数组

*/
m.invoke(clz.newInstance(), new Object[]{str});
}
}