Java反射机制
Java反射机制概述
- Reflection(反射)是被视为==动态语言==的关键,反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息,并能直接==操作任意对象的内部属性及方法==。
- 加载完类之后,在堆内存的方法区中就产生了一个Class类类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。==我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。==
正常方式:引入需要的“包类”名称—>通过new实例化—>取得实例化对象
反射方式:实例化对象—>getClass()方法—>得到完整的“包类”名称
反射相关的主要API
- ==java.lang.Class==:代表一个类
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Constructor:代表类的构造器
- ……
反射简单示例
定义一个person类
package com.miao.java;
public class Person { private String name; public int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } private Person(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } public void show(){ System.out.println("hello,world"); } private String nation(String nation){ System.out.println("我的国籍是:"+nation); return nation; } }
|
定义一个测试类
package com.miao.java; import org.junit.jupiter.api.Test; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class ReflectionTest1 { @Test public void test1(){ Person p = new Person("miao",22); p.age=11; System.out.println(p.toString()); p.show(); } @Test public void test2() throws Exception{ Class clazz = Person.class; Constructor constructor = clazz.getConstructor(String.class, int.class); Object obj = constructor.newInstance("tom", 12); Person p = (Person) obj; System.out.println(p.toString()); Field age = clazz.getDeclaredField("age"); age.set(p,10); System.out.println(p.toString()); Method show = clazz.getDeclaredMethod("show"); show.invoke(p); Constructor constructor1 = clazz.getDeclaredConstructor(String.class); constructor1.setAccessible(true); Person p1 = (Person) constructor1.newInstance("ttom"); System.out.println(p1.toString()); Field name = clazz.getDeclaredField("name"); name.setAccessible(true); name.set(p1,"jjru"); System.out.println(p1); Method nation = clazz.getDeclaredMethod("nation", String.class); nation.setAccessible(true); String nation1 = (String) nation.invoke(p1,"中国"); System.out.println(nation1); } }
|
test2()的结果
理解Class类并获取Class实例
关于java.lang.Class类的理解
- 类的加载过程
- 程序经过javac.exe命令,生成一个或多个字节码文件(.class结尾)
- 使用java.exe命令对某个字节码文件解释运行,相当于将字节码文件加载到内存中。(==此过程称为类的加载==)
- ==加载到内存中的类,称为运行时类,作为Class的一个实例==
- Class的实例对应着一个运行时类
- 加载到内存中的运行时类,会缓存一定的时间,在此时间,我们可以通过不同的方式来获取此运行时类
获取Class实例的方式
@Test public void test3() throws ClassNotFoundException { Class clazz = Person.class; System.out.println(clazz); Person p = new Person(); Class clazz1 = p.getClass(); System.out.println(clazz1); Class clazz2 = Class.forName("com.miao.java.Person"); System.out.println(clazz2);
System.out.println("====================="); System.out.println(clazz == clazz1); System.out.println(clazz == clazz2); ClassLoader classLoader = ReflectionTest1.class.getClassLoader(); Class clazz4 = classLoader.loadClass("com.miao.java.Person"); System.out.println(clazz4); System.out.println(clazz == clazz4); }
|
类的加载与ClassLoader的理解
类的加载过程
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过以下步骤对该类进行初始化
- 类的加载(Load)
- 将类的class文件读入内存,并为之创建一个java.lang.Class对象,==此过程由类加载器完成==
- 类的链接(Link)
- 类的初始化(Initialize)
- 源程序(*.java文件)
- 经过Java编译器
- 字节码(*.class文件)
- 类装载器
- 字节码效验器
- 解释器
- 操作系统平台
了解ClassLoader
类加载器的作用
类加载器的作用:用来把类(class)装载进内存的,JVM规范定义了如下的类的加载器
Bootstap ClassLoader
引导类加载器:
JVM自带的类加载器,负责java平台核心库
负责加载Java安装目录下的/jre/lib类库(核心类库)至JVM中,
不继承java.lang.ClassLoader,不可以被Java程序直接调用,
本身是用C++写的
Extension ClassLoader
扩展类加载器:
负责加载Java安装目录下的/jre/lib/ext类库(扩展类库)至JVM
Java程序可直接调用
Application ClassLoader
应用程序类加载器:
负责加载CLASSPATH路径下的类库,
我们写的类就是通过这个加载器完成加载,
可以通过 ClassLoader.getSystemClassLoader()来获取这个加载器
自定义类加载器
@Test public void test(){ ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); System.out.println(classLoader); ClassLoader parent = classLoader.getParent(); System.out.println(parent); ClassLoader parent1 = parent.getParent(); System.out.println(parent1); }
|
读取配置文件
方式一
@Test public void test1() throws Exception { Properties pros = new Properties(); FileInputStream fil = new FileInputStream("src/main/resources/jdbc.properties"); pros.load(fil);
String user = pros.getProperty("user"); String password = pros.getProperty("password"); System.out.println("user:"+user+"password:"+password);
}
|
@Test public void test2() throws IOException { Properties pros = new Properties(); ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); InputStream is = classLoader.getResourceAsStream("jdbc.properties"); pros.load(is);
String user = pros.getProperty("user"); String password = pros.getProperty("password"); System.out.println("user:"+user+"----password:"+password); }
|
创建运行时类的对象
通过反射创建运行时类的对象
@Test public void test() throws InstantiationException, IllegalAccessException { Class clazz = Person.class;
Object obj = clazz.newInstance(); System.out.println(obj);
}
|
简单了解反射的动态性
@Test public void test1(){ for (int i = 0;i<100;i++){ int num = new Random().nextInt(3); String classPath = ""; switch (num){ case 0: classPath="java.util.Date"; break; case 1: classPath="java.lang.Object"; break; case 2: classPath="com.miao.java.Person"; break; } try { Object obj = getInstance(classPath); System.out.println(obj); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
public Object getInstance(String classPath) throws ClassNotFoundException, InstantiationException, IllegalAccessException { Class clazz = Class.forName(classPath); return clazz.newInstance(); }
|
结果
获取运行时类的完整结构
待续…
调用运行时类的指定结构
反射的应用:动态代理