目录
一、反射简介
1. 什么是反射
2. 反射的作用
3. 反射的入口—Class类
二、类对象的三种获取方式
1. Class.forName(' 完整类名 ')
2. 类名.class
3. 对象.getClass()
三、反射实例化
1.通过 Class 对象的 newInstance() 方法
2. 通过 Constructor 对象的 newInstance() 方法
3. 通过 DeclarredConstructor 对象的 newInstance() 方法
四、反射动态方法调用
1. getMethod 方法
2. DeclaredMethod 方法
五、反射读写属性
六、反射工作原理
一、反射简介
1. 什么是反射
Java反射是指在运行时动态地获取和操作类的信息以及调用对象的方法和属性,而不需要在编译期间知道类名或者其成员变量和方法具体等的信息。
通过Java反射机制,可以实现动态创建对象、调用对象的方法、修改对象的属性值等操作,这为程序的灵活性和扩展性提供了很大的便利。
2. 反射的作用
- 动态创建对象
- 动态操作属性
- 动态调用方法
在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中
- Class类:代表一个类
- Constructor 类:代表类的构造方法
- Field 类:代表类的成员变量(属性)
- Method类:代表类的成员方法
注:必须先获得Class才能获取Method、Constructor、Field
3. 反射的入口—Class类
Class类是Java 反射机制的起源和入口
- 用于获取与类相关的各种信息
- 提供了获取类信息的相关方法
- Class类继承自Object类
Class类是所有类的共同的图纸
- 每个类有自己的对象,好比图纸和实物的关系
- 每个类也可看做是一个对象,有共同的图纸Class,存放类的结构信息,比如类的名字、属性、方法、构造方法、父类和接口,能够通过相应方法取出相应信息
Class类的对象称为类对象
二、类对象的三种获取方式
1. Class.forName(' 完整类名 ')
package com.reflect; / * 类对象的获取方式: * 1.Class.forName() * * @author 云村小威 * */ public class Demo1 { public static void main(String[] args) throws Exception { / * 多应用于jdbc数据库连接中 */ Class forName = Class.forName("com.reflect.Student"); System.out.println(forName); } }
2. 类名.class
package com.reflect; / * 类对象的获取方式: * 2.类实例.getClass() * * @author 云村小威 * */ public class Demo1 { public static void main(String[] args) throws Exception { / * 通用增删改 */ Student stu = new Student(); System.out.println(stu.getClass()); } }
3. 对象.getClass()
package com.reflect; / * 类对象的获取方式: * 3.类名.class * * @author 云村小威 * */ public class Demo1 { public static void main(String[] args) throws Exception { / * 通用查询 */ Class<?> c = Student.class; System.out.println(c); } }
其中类名.class、对象名.getClass()方式在编码时已经知道了要操作的类,而Class.forName()方式在操作的时候,可以知道,也可以不知道要操作的类。所以当编码时还不知道要操作的具体类,就只能使用Class.forName()方式了。
三、反射实例化
配置测试类:
package com.reflect; / * 学生实体类 * * @author 云村小威 * */ public class Student { private String sid; private String sname; public Integer age; public Student() { super(); System.out.println("调用无参构造方法创建了一个学生对象"); } public Student(String sid) { super(); this.sid = sid; System.out.println("调用带一个参数的构造方法创建了一个学生对象"); } public Student(String sid, String sname) { super(); this.sid = sid; this.sname = sname; System.out.println("调用带二个参数的构造方法创建了一个学生对象"); } @SuppressWarnings("unused") private Student(Integer age) { System.out.println("调用Student类私有的构造方法创建一个学生对象"); this.age = age; } public String getSid() { return sid; } public void setSid(String sid) { this.sid = sid; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public void hello() { System.out.println("你好!我是" + this.sname); } public void hello(String name) { System.out.println(name + "你好!我是" + this.sname); } @SuppressWarnings("unused") private Integer add(Integer a, Integer b) { return new Integer(a.intValue() + b.intValue()); } }
1.通过 Class 对象的 newInstance() 方法
package com.reflect; / * 反射实例化 * 1.通过 Class 对象的 newInstance() 方法 * * @author 云村小威 * */ public class Demo2 { public static void main(String[] args) throws Exception { // 获取类对象 Class c = new Student().getClass(); // 调用无参构造方法创建一个对象 Object object = c.newInstance(); System.out.println(object); } }
重点是:newInstance()调用的是无参构造,必须保证无参构造是存在的!
2. 通过 Constructor 对象的 newInstance() 方法
package com.reflect; import java.lang.reflect.Constructor; / * 反射实例化 * 1.通过 Constructor 对象的 newInstance() 方法 * * @author 云村小威 * */ public class Demo2 { public static void main(String[] args) throws Exception { // 获取类对象 Class c = new Student().getClass(); // 1.调用带一个参数(sid)的构造方法创建了一个学生对象 Constructor constructor1 = c.getConstructor(String.class); // 强转为了获取实体类参数值 Student object = (Student) constructor1.newInstance("s10"); System.out.println(object.getSid()); // 2.调用带两个参数(sid,sname)的构造方法创建了一个学生对象 Constructor constructor2 = c.getConstructor(String.class, String.class); Student object2 = (Student) constructor2.newInstance("s101", "ikun"); System.out.println(object2.getSid() + "--" + object2.getSname()); } }
3. 通过 DeclarredConstructor 对象的 newInstance() 方法
package com.reflect; import java.lang.reflect.Constructor; / * 反射实例化 * 3. 通过 DeclarredConstructor 对象的 newInstance() 方法 * * @author 云村小威 * */ public class Demo2 { public static void main(String[] args) throws Exception { // 获取类对象 Class c = new Student().getClass(); //调用Student类私有的构造方法创建一个构造方法 Constructor constructor = c.getDeclaredConstructor(Integer.class); //打开私有修饰符的访问权限 constructor.setAccessible(true); Student object = (Student) constructor.newInstance(18); System.out.println(object.getAge()); } }
注:不设置 setAccessible(true) 则会报错 Class com.reflect.Demo2 can not access a member of class com.reflect.Student with modifiers "private"
四、反射动态方法调用
1. getMethod 方法
package com.reflect; import java.lang.reflect.Method; / * 反射动态方法调用 * * @author 云村小威 * */ public class Demo3 { public static void main(String[] args) throws Exception { // 获取类对象 Class c = new Student().getClass(); // 调用newInstance()方法创建一个对象 Student stu = (Student) c.newInstance(); / * 1.public 的无参数方法调用 */ // 获取方法对象 // getMethod(方法名, 参数类型); Method m = c.getMethod("hello"); // invoke(类实例, 实参) Object invoke = m.invoke(stu); // 注:方法返回值是void 结果是null System.out.println(invoke); / * 2.public 的有参数方法调用 */ Method m2 = c.getMethod("hello", String.class); System.out.println(m2.invoke(stu, "小黑宝")); } }
2. DeclaredMethod 方法
package com.reflect; import java.lang.reflect.Method; / * 反射私有方法调用 * * @author 云村小威 * */ public class Demo3 { public static void main(String[] args) throws Exception { // 获取类对象 Class c = new Student().getClass(); // 调用newInstance()方法创建一个对象 Student stu = (Student) c.newInstance(); / * 私有带参数方法调用 */ Method m3 = c.getDeclaredMethod("add", Integer.class, Integer.class); // 打开私有修饰符的访问权限 m3.setAccessible(true); System.out.println("返回结果:"+m3.invoke(stu, 1, 10)); } }
五、反射读写属性
package com.reflect; import java.lang.reflect.Field; / * 反射读写属性 * * @author 云村小威 * */ public class Demo4 { public static void main(String[] args) throws Exception { // 获取类对象 Class c = new Student().getClass(); // 调用newInstance()方法创建一个对象 Student stu = (Student) c.newInstance(); / * 通过反射存值getDeclaredField(属性名); * set(对象,值) */ Field declaredField = c.getDeclaredField("sname"); //打开修饰符访问权限 declaredField.setAccessible(true); declaredField.set(stu, "小黑宝"); / * 通过反射读取值getDeclaredFields() return arr[] * get(对象) 获取值 * getName() 获取属性名 */ Field[] fields = c.getDeclaredFields(); for (Field f : fields) { //打开修饰符访问权限 f.setAccessible(true); System.out.println(f.getName()+":"+f.get(stu)); } } }
六、反射工作原理
调用反射的总体流程如下:
- 当我们编写完一个Java项目之后,每个java文件都会被编译成一个.class文件。
- 这些class文件在程序运行时会被ClassLoader加载到JVM中,当一个类被加载以后,JVM就会在内存中自动产生一个Class对象。
- 通过Class对象获取Field/Method/Construcor
- 我们一般平时是通过new的形式创建对象实际上就是通过这些Class来创建的,只不过这个class文件是编译的时候就生成的,程序相当于写死了给jvm去跑。
- 当我们的程序在运行时,需要动态的加载一些类这些类可能之前用不到所以不用加载到jvm,而是在运行时根据需要才加载。
原来使用new的时候,需要明确的指定类名,这个时候属于硬编码实现,而在使用反射的时候,可以只传入类名参数,就可以生成对象,降低了耦合性,使得程序更具灵活性。