1.反射机制是什么?
反射机制是
在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法; 对于任意一个对象,都能够调用它的任意一个方法和属性
. 这种动态的获取信息以及调用对象方法的功能称为Java语言的反射机制.
2.反射机制能做什么?
- 获得类的定义信息
- 包名、类名
- 成员变量定义信息
- 构造方法定义信息
- 方法定义信息
- 在运行时创建对象
- 在运行时调用成员
3.反射应用场景
-
框架中对象的构建(例如mybatis中的resultType,resultMap,spring中的bean)
-
框架中方法的调用(例如对象set方法,get方法,spring mvc 控制层方法,..)
总之:反射不能预知未来,但可驾驭未来,通过反射可以更好构建一些编程框架,
以实现通用性编程,从而达到简化代码编写。
4.反射机制API
4.1 获取Class对象(字节码对象):可理解为反射应用的入口
- 类名.class
- Class.forName("包名.类名")
- 实例对象.getClass()
数组对象是通过 实例对象.getClass().getComponentType()
说明:返回数组中元素的Class对象,如果不是Class对象那么返回null
非数组类型不能通过**getComponentType()**方法获得元素的Class对象类型
4.2 获取包名类名(c为Class对象实例)
- c.getPackage.getName()
包名
- c.getName()
完整类名
- c.getSimpleName()
简写类名
4.3 成员变量的定义信息
-
getFields()
获得所有可见的变量,包含从父类继承的变量 -
getDeclaredFields()
获得本类定义的变量,包括私有,不包含继承的变量 -
getField(变量名)
-
getDeclaredField(变量名)
4.4 构造方法的定义信息
-
getConstructors()
获得所有可见的构造方法 -
getDeclaredConstructors()
获得所有构造方法,包括私有 -
getConstructor(参数类型列表)
-
getDeclaredConstructor(参数类型列表)
4.5 方法的定义信息
-
getMethods()
获得所有可见的方法,包含从父类继承的方法 -
getDeclaredMethods()
获得所有本类定义的方法,包括私有,不包含继承的方法 -
getMethod(方法名,参数类型列表)
-
getDeclaredMethod(方法名,参数类型列表)
4.6 反射创建对象
-
执行无参构造
Object obj = c.newInstance()
-
执行有参构造
-
获得构造方法
Constructor t = c.getDeclaredConstructor(int.class, String.class);
-
创建对象,并执行这个构造方法
Object obj = t.newInstrance(5, "sdfsd");
-
4.7 反射调用成员变量
-
Field f = c.getDeclaredField("name");
获取变量 -
f.setAccessible(true)
使私有变量,也能被访问 -
f.set(对象, "张三");
为变量赋值第一个参数,指定对象,为指定对象的变量赋值
静态变量,第一个参数给 null -
f.get(对象)
访问变量的值
4.8 反射调用方法
-
获取方法
Method t = c.getDeclaredMethod("f1",int.class,String.class);
-
使私有方法可以被调用
t.setAccessible(true);
-
反射调用方法
Object r = t.invoke(对象, 5, "abc")
静态方法,第一个参数给 null
如果没有返回值,得到 null
5. 反射应用实例
5.1 在泛型为Integer的ArrayList中存放一个String类型的对象
import java.lang.reflect.Method;
import java.util.ArrayList;
/**
* ClassName: Test1
* Function: 在泛型为Integer的ArrayList中存放一个String类型的对象
* date: 2020/01/20
* author Luzy
* version V1.0
*/
public class Test1 {
public static void main(String[] args) throws Exception {
ArrayList<Integer> list = new ArrayList<>();
list.add(123);
list.add(520);
Method method = list.getClass().getMethod("add", Object.class);
method.invoke(list, "Java反射机制实例");
System.out.println(list.toString());
}
}
输出结果:
[123, 520, Java反射机制实例]
5.2 通过反射取得并修改数组的信息
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Arrays;
/**
* ClassName: Test2
* Function: 通过反射修改String
* date: 2020/01/20
* author Luzy
* version V1.0
*/
public class Test2 {
public static void main(String[] args) throws Exception {
String str = new String("恭喜发财!");//
System.out.println("输出原本str的内容:" + str);
//1、获取String类对应的Class
Class<? extends String> clazz = str.getClass();
//2、通过clazz获取类中的value属性
Field value = clazz.getDeclaredField("value");
//3、需要设置私有属性可以被操作后才能操作属性值
value.setAccessible(true);
//4、获取value属性里面的值(内存地址)
//由于 String里面的值是存放在char[] 数组里面的,所以需要用一个char[]接收
char[] temp = (char[])value.get(str);
//5、通过temp的地址引用 找到真实String对象中的数组,修改数组内的每一个元素
//由于此处是char类型,所以要用单引号
temp[0] = '新';
temp[1] = '年';
temp[2] = '快';
temp[3] = '乐';
System.out.println("反射操作过后的str内容:"+str);
}
}
输出结果:
输出原本str的内容:恭喜发财!
反射操作过后的str内容:新年快乐!
5.3 反射机制应用于工厂模式
/**
* ClassName: Test3
* Function: 反射机制应用于工厂模式
* date: 2020/01/20
* author Luzy
* version V1.0
*/
interface Fruit {
public abstract void eat();
}
class Apple implements Fruit {
public void eat() {
System.out.println("Apple");
}
}
class Orange implements Fruit {
public void eat() {
System.out.println("Orange");
}
}
class Factory {
public static Fruit getInstance(String ClassName) {
Fruit f = null;
try {
f = (Fruit) Class.forName(ClassName).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return f;
}
}
/**
* 对于普通的工厂模式当我们在添加一个子类的时候,就需要对应的修改工厂类。 当我们添加很多的子类的时候,会很麻烦。
*
* 现在我们利用反射机制实现工厂模式,可以在不修改工厂类的情况下添加任意多个子类。
*
* 但是有一点仍然很麻烦,就是需要知道完整的包名和类名,这里可以使用properties配置文件来完成。
*
* java 读取 properties 配置文件 的方法可以自行百度
*
*
*/
public class Test3 {
public static void main(String[] args) throws Exception {
Fruit f = Factory.getInstance("Apple");
if (f != null) {
f.eat();
}
}
}
输出结果:
Apple