0. 序言
这里只讲解关于反射的基础知识,以后会补充更多的扩展知识,毕竟是基础系列。
1. 类的加载概述
- 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
- 加载 就是指将class文件读入内存,并为之创建一个Class对象。任何类被使用时系统都会建立一个Class对象。
- 连接
- 验证 是否有正确的内部结构,并和其他类协调一致
- 准备 负责为类的静态成员分配内存,并设置默认初始化值
- 解析 将类的二进制数据中的符号引用替换为直接引用
- 初始化
2. 类的加载时机
- 创建类的实例
- 访问类的静态变量,或者为静态变量赋值
- 调用类的静态方法
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
3. 类加载器的概述和分类
- 3.1 类加载器的概述
负责将.class文件加载到内存中,并为之生成对应的Class对象。虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。 - 3.2 类加载器的分类
- Bootstrap ClassLoader 根类加载器
- Extension ClassLoader 扩展类加载器
- Sysetm ClassLoader 系统类加载器
- 3.3 类加载器的作用
- Bootstrap ClassLoader 根类加载器
- 也被称为引导类加载器,负责Java核心类的加载
- 比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
- Extension ClassLoader 扩展类加载器
- 负责JRE的扩展目录中jar包的加载。
- 在JDK中JRE的lib目录下ext目录
- Sysetm ClassLoader 系统类加载器
- 负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径
- Bootstrap ClassLoader 根类加载器
4. 反射概述
- 在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
- 对于任意一个对象,都能够调用它的任意一个方法和属性;
- 这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
5. 反射的三种方式
try {
Class clazz_01 = Class.forName("test.Person");
Class clazz_02 = Person.class;
Person person = new Person("Mr.Fu", 20);
Class clazz_03 = person.getClass();
System.out.println(clazz_01==clazz_02);
System.out.println(clazz_02==clazz_03);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
6. Class.forName()获取字节码对象和字节码对象通过newInstance创建对象实例
- 定义一个榨汁机Juicer,每次我想喝苹果汁就new Apple(),每次我想喝橘子汁就new Orange()
public class test01 {
public static void main(String[] args) {
Juicer juicer = new Juicer();
juicer.run(new Apple());
juicer.run(new Orange());
}
}
class Juicer {
public void run(Fruit fruit) {
fruit.squeeze();
}
}
interface Fruit {
void squeeze();
}
class Apple implements Fruit {
public void squeeze() {
System.out.println("榨出一杯苹果汁");
}
}
class Orange implements Fruit {
public void squeeze() {
System.out.println("榨出一杯橘子汁");
}
}
榨出一杯苹果汁
榨出一杯橘子汁
- 为了不修改代码:创建conifg.properties.txt文件
test.Apple //类名
或
test.Orange
- 修改并读取配置文件即可
public class test01 {
public static void main(String[] args) {
Juicer juicer = new Juicer();
try {
// 创建输入流对象,关联配置文件
BufferedReader reader = new BufferedReader(new FileReader("config.properties.txt"));
try {
//读取配置文件一行内容,获取该类的字节码对象
Class clazz = Class.forName(reader.readLine());
try {
//通过字节码对象创建实例对象
Fruit fruit = (Fruit) clazz.newInstance();
fruit.squeeze();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
榨出一杯橘子汁
7. getConstructor获取构造方法并使用
class Person {
private String mName;
private int mAge;
public Person(String mName, int mAge) {
this.mName = mName;
this.mAge = mAge;
}
public String getmName() {
return mName;
}
public int getmAge() {
return mAge;
}
}
public class test01 {
public static void main(String[] args) {
try {
Class clazz = Class.forName("test.Person");
try {
Person person = (Person) clazz.newInstance();
System.out.println(person);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
java.lang.InstantiationException: test.Person
at java.lang.Class.newInstance(Class.java:427)
at test.test01.main(test01.java:8)
- 以上发现通过newInstance无法创建字节码对象的实例,那是因为newInstance获取的是类的无参构造,但是Person中没有无参构造,所以这个时候要使用getConstructor获取有参构造,通过有参构造创建字节码对象实例:
Class clazz = null;
try {
clazz = Class.forName("test.Person");
Constructor constructor = null;
try {
//因为还处于反射阶段,所以参数是字节码对象
constructor = clazz.getConstructor(String.class, int.class);
Person person = (Person) constructor.newInstance("张三", 28);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
8. getField和getDeclaredField获取字段
Class clazz = null;
try {
clazz = Class.forName("test.Person");
Constructor constructor = clazz.getConstructor(String.class, int.class);
Person person = (Person) constructor.newInstance("张三", 15);
Field field = clazz.getField("mName");
field.set(person,"李四");
System.out.println(person);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
java.lang.NoSuchFieldException: mName
at java.lang.Class.getField(Class.java:1703)
at test.test01.main(test01.java:16)
- 以上发现报错,没有mName字段,原因在于我们的字段是私有的,所以我们要用getDeclaredField
java.lang.IllegalAccessException: Class test.test01 can not access a member of class test.Person with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
at java.lang.reflect.Field.set(Field.java:761)
at test.test01.main(test01.java:17)
- 以上发现又报错,说没有权限去修改因为字段是私有的,那只要去除权限了:
field.setAccessible(true);
9. getMethod或getDeclaredMethod获取方法
无参:
Method eat = clazz.getMethod("eat"); //获取
eat.invoke(person); //执行
我在人民广场吃炸鸡
有参:
Method eat = clazz.getMethod("eat",int.class);//获取
eat.invoke(person,10); //执行
10. 通过反射越过泛型检查
- 向ArrayList<Integer> list 集合中添加字符串
ArrayList<Integer> list = new ArrayList<>();
list.add(10);
list.add(20);
Class clazz = Class.forName("java.util.ArrayList");
Method add = clazz.getMethod("add", Object.class);
add.invoke(list, "abc");
System.out.println(list);
[10, 20, abc]
11. 写一个通用的设置某个对象的某个属性为指定的值
public class Tool {
public void setProperty(Object o, String property, Object value) {
try {
Class clazz = o.getClass(); //获取字节码对象
Field declaredField = clazz.getDeclaredField(property); //暴力反射获取字段
declaredField.setAccessible(true); //去除权限
declaredField.set(o, value);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
Person person = new Person("张三",10);
Tool tool = new Tool();
tool.setProperty(person,"mName","李四");
System.out.println(person.getmName());