反射

  • 反射指的是可以在运行时加载,使用编译期间完全未知的类

  • 程序在运行中。可以动态加载一个只有名称的类。对于任意一个已加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性

  • 一个类只有一个Class对象,在加载一次出来的Class对象也是之前的那一个

  • 加载完类之后,在堆内存里就会产生一个Class对象,类的整个结构信息会放到对应的Class对象中

获取Class对象的三种方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class ReflectionTest {
public static void main(String[] args) {
// 1.用Class.forName()获取Class对象,推荐使用这个
try {
Class clazz1 = Class.forName("gui.beantest");//Class.forName(包名+类名)
System.out.println(clazz1);
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}

// 2.用 .class 来获取Class对象
Class clazz2 = beantest.class; //类名.class
System.out.println(clazz2);

// 3.用 getClass()来获取Class对象
beantest cc = new beantest(1,2,"name");
Class clazz3 = cc.getClass(); //对象.getClass()
System.out.println(clazz3);

}
}

获取类的信息

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
@SuppressWarnings("all")
public class ReflectionTest {
public static void main(String[] args) throws Exception {
String path = "gui.beantest";
Class clazz = Class.forName(path);
System.out.println(clazz);


// 获取类的名字
System.out.println(clazz.getName()); //包名+类名
System.out.println(clazz.getSimpleName()); //类名

// 获取属性信息
Field[] files1 = clazz.getFields(); //只能获得public类型的属性
Field[] files2 = clazz.getDeclaredFields();//能获得所有类型的属性
Field f =clazz.getDeclaredField("uname");//获得这个属性的
System.out.println(files1.length);
System.out.println(files2.length);
System.out.println(f);

// 获取方法信息
Method[] method1 = clazz.getMethods(); //只能获得public类型的方法
Method[] method2 = clazz.getDeclaredMethods(); //获得所有类型的方法
Method m = clazz.getMethod("setId",int.class); //获取这个方法,没参数就写null,有参数就传参数类型的class
System.out.println(method1.length);
System.out.println(method2.length);
System.out.println(m);

// 获取构造器信息
Constructor[] constructors = clazz.getDeclaredConstructors();
Constructor c =clazz.getDeclaredConstructor(int.class,int.class,String.class); //传递不同的参数类型获取不同的构造器
for(Constructor temp:constructors){
System.out.println(temp);
}
System.out.println(c);
}
}

动态操作

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
public class Demo01 {
public static void main(String[] args) throws Exception{
Class clazz = Class.forName("test.Bean");

// 通过反射调用构造方法,构造对象
Bean u1 = (Bean) clazz.getDeclaredConstructor().newInstance();
System.out.println(u1);
Bean u2 = (Bean) clazz.getDeclaredConstructor(int.class,int.class,String.class).newInstance(1,2,"ee");
System.out.println(u2.getUname());

// 通过反射调用普通方法
Bean u3 =(Bean) clazz.getDeclaredConstructor().newInstance();
Method me = clazz.getMethod("setId", int.class);
me.invoke(u3,5); //通过Method的invoke方法来使用方法
System.out.println(u3.getId());

// 通过反射操作属性
Bean u4 =(Bean) clazz.getDeclaredConstructor().newInstance();
Field f = clazz.getDeclaredField("uname");
f.setAccessible(true);//这个属性不需要安全检查,可以直接访问,不然私有的属性不能修改
f.set(u4,"lll");
System.out.println(u4.getUname());
System.out.println(f.get(u4));

}
}

getDeclaredConstructor()这个方法会查找到所有的构造方法,然后通过后面的newInstance()方法来完成实例化,在强转就可以得到类了。如果有参数就输入参数。(注意这个Javabean中最好要有一个无参构造器,不然无参的这个实例化会报错)

反射机制的性能问题

  • setAccessible(),启用和禁用安全检查的开关,true时取消Java访问检查,但并不是true就能访问,false就不能访问

  • 禁止安全检查可以提高反射的运行速度

    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
    public class Demo02 {
    public static void main(String[] args) throws Exception{
    test01();
    test02();
    test03();
    }
    public static void test01(){
    Bean u = new Bean();
    long start = System.currentTimeMillis();
    for(int i = 0;i<1000000000L;i++){
    u.getUname();
    }
    long end = System.currentTimeMillis();
    System.out.println("调用普通方法耗时为:"+(end-start)+"ms");
    }

    public static void test02() throws Exception{
    Bean u = new Bean();
    Class clazz = u.getClass();
    Method me = clazz.getDeclaredMethod("getUname",null);
    long start = System.currentTimeMillis();
    for(int i = 0;i<1000000000L;i++){
    me.invoke(u, null);
    }
    long end = System.currentTimeMillis();
    System.out.println("运用反射调用方法耗时为:"+(end-start)+"ms");
    }

    public static void test03() throws Exception{
    Bean u = new Bean();
    Class clazz = u.getClass();
    Method me = clazz.getDeclaredMethod("getUname",null);
    me.setAccessible(true);
    long start = System.currentTimeMillis();
    for(int i = 0;i<1000000000L;i++){
    me.invoke(u, null);
    }
    long end = System.currentTimeMillis();
    System.out.println("关闭访问检查后运用反射调用方法耗时为:"+(end-start)+"ms");
    }

    }

    这里就可以知道,运用反射调用方法的时间差不多是直接调用方法的时间的10倍,而关闭了访问检查之后就下降到接近5倍的程度