反射机制及其优缺点

[TOC]

概念

Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。

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

1
2
每个类都有一个 Class 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内
容保存着 Class 对象。也可以使用Class.forName("com.mysql.jdbc.Driver") 这种方式来控制类的加载,该方法会返回一个 Class 对象。

类对象 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库主要包含了以下三个类:
Field :可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段;
Method :可以使用 invoke() 方法调用与 Method 对象关联的方法;
Constructor :可以用 Constructor 创建新的对象。

优缺点

优点:

可扩展性 :应用程序可以利用全限定名创建可扩展对象的实例,来使用来自外部的用户自定义类。

方便性: 可以写在配置文件里,直接修改配置文件就行了

spring.txt

1
2
class=reflection.Service1
method=doService1
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
package reflection;

import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Properties;

public class Test {

@SuppressWarnings({ "rawtypes", "unchecked" })
public static void main(String[] args) throws Exception {

//从spring.txt中获取类名称和方法名称
File springConfigFile = new File("e:\\project\\j2se\\src\\spring.txt");
Properties springConfig= new Properties();
springConfig.load(new FileInputStream(springConfigFile));
String className = (String) springConfig.get("class");
String methodName = (String) springConfig.get("method");

//根据类名称获取类对象
Class clazz = Class.forName(className);
//根据方法名称,获取方法对象
Method m = clazz.getMethod(methodName);
//获取构造器
Constructor c = clazz.getConstructor();
//根据构造器,实例化出对象
Object service = c.newInstance();
//调用对象的指定方法
m.invoke(service);

}
}

缺点:

性能开销 :反射涉及了动态类型的解析,所以 JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些
非反射操作低得多。

内部暴露 :由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使
用反射可能会导致意料之外的副作用,这可能导致代码功能失调并破坏可移植性。

获取类对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package reflection;

import charactor.Hero;

public class TestReflection {

public static void main(String[] args) {
String className = "charactor.Hero";
try {
Class pClass1=Class.forName(className);
Class pClass2=Hero.class;
Class pClass3=new Hero().getClass();
System.out.println(pClass1==pClass2);
System.out.println(pClass1==pClass3);
} catch (ClassNotFoundException 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
22
23
24
25
26
27
28
package reflection;
import java.lang.reflect.Constructor;
import charactor.Hero;
public class TestReflection {

public static void main(String[] args) {
//传统的使用new的方式创建对象
Hero h1 =new Hero();
h1.name = "teemo";
System.out.println(h1);

try {
//使用反射的方式创建对象
String className = "charactor.Hero";
//类对象
Class pClass=Class.forName(className);
//构造器
Constructor c= pClass.getConstructor();
//通过构造器实例化
Hero h2= (Hero) c.newInstance();
h2.name="gareen";
System.out.println(h2);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

访问属性

对于private修饰的成员,需要使用setAccessible(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
package reflection;

import java.lang.reflect.Field;

import charactor.Hero;

public class TestReflection {

public static void main(String[] args) {
Hero h =new Hero();
//使用传统方式修改name的值为garen
h.name = "garen";
try {
//获取类Hero的名字叫做name的字段
Field f1= h.getClass().getDeclaredField("name");
//修改这个字段的值
f1.set(h, "teemo");
//打印被修改后的值
System.out.println(h.name);

} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
1
2
3
4
getField和getDeclaredField的区别
这两个方法都是用于获取字段
getField 只能获取public的,包括从父类继承来的字段。
getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。 (注: 这里只能获取到private的字段,但并不能访问该private字段的值,除非加上setAccessible(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
35
package charactor;

public class Hero {
public String name;
public float hp;
public int damage;
public int id;

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Hero(){

}
public Hero(String string) {
name =string;
}

@Override
public String toString() {
return "Hero [name=" + name + "]";
}
public boolean isDead() {
// TODO Auto-generated method stub
return false;
}
public void attackHero(Hero h2) {
// TODO Auto-generated method stub

}

}
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
package reflection;

import java.lang.reflect.Method;

import charactor.Hero;

public class TestReflection {

public static void main(String[] args) {
Hero h = new Hero();

try {
// 获取这个名字叫做setName,参数类型是String的方法
Method m = h.getClass().getMethod("setName", String.class);
// 对h对象,调用这个方法
m.invoke(h, "盖伦");
// 使用传统的方式,调用getName方法
System.out.println(h.getName());

} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}