一,什么是反射?
1.1 概念:
反射是指在运行时获取类和对象的相关信息及操作类和对象的方法,反射用于描述该类的属性、方法和构造器等信息。通过反射机制,可以在运行时获取任何一个类的Class对象,并借助Class对象获取该类的相关信息和调用该类的方法。
2.2反射的优缺点:
2.2.1优点:
在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。
2.2.2缺点:
- 反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射
- 反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。
3.3 反射中的三个类
3.3.1描述:
Class类、Method类和Field类。Class类是Java反射机制的基础,通过该类可以获取类的名称、修饰符、属性、构造器、方法等信息;Method类和Field类分别用于描述类的方法和属性,可以动态地获取、设置和调用类的方法和属性。
二,类类
2.1 概念
Class
是一个类,代表一个Java类或接口。每个类都有一个对应的Class
对象,通过这个对象可以获取类的信息信息,如名称、修饰符、字段(成员变量)、方法、注解、构造函数等,还可以创建类的对象。当一个类被加载到内存中时,会为这个类创建一个对应的Class
对象,这个对象包含了当前类的所有信息。通过这个对象,Java程序可以在运行时获取该类的属性、方法、构造函数等信息,并可以通过反射机制实例化对象、调用方法等。
2.2 类类获取三种方式
- Class.forName(类的路径) 注意:该方法需要抛出异常Exception,不然会报错
- 类实例.getClass
- 类名.class
代码如下:
测试的StudentClass类代码:
package com.zking.LiuBing; public class Student { private String sid; private String sname; public Integer age; static{ System.out.println("加载进jvm中!"); } 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()); } }
获取StudentClass类类代码:
package com.zking.LiuBing; /** * 类类的获取及反射 * @author 兵 * */ public class Demo1 { public static void main(String[] args) throws Exception { // 类类获取方式(三种) 要记牢! // Class.forName // 类实例.getClass // 类名.class //实例一个类 Student student= new Student(); //第一种 Class.forName //调用了student中无参构造方法 底层实列类的方法就是调的无参构造方法 Class c1 = Class.forName("com.zking.LiuBing.Student"); System.out.println("第一种:"+c1); // 第二种 类实例.getClass Class c2 = student.getClass(); System.out.println("第二种:"+c2); // 第三种 类名.class Class c3= Student.class; System.out.println("第三种:"+c3); } }
运行结果:
在反射Student类的时候一定会调用无构造方法,如果在Student中没有无构造方法的话是会运行时会抛出
InstantiationException
异常,因为通过反射机制创建对象时,利用的是无参构造函数实例化对象
三,反射实例化
3.1方法:
- newInstance(); 创建对象
- getConstructor(); 获取对象的构造函数
- getDeclaredConstructor(); 获取私有化构造方法
- setAccessible(); 修改访问权限的方法 也就是打开访问私有化
注意:参数是反射类的中的方法类型.class
代码展示:
package com.zking.LiuBing; /** * 调用构造方法 * 先获取对象的构造函数getConstructor(参数类型.class); 再调用其中的方法newInstance * * * 调用构造函数 * 先获取构造方法getConstructor(类型); 再调用newInstance方法实现 * */ import java.lang.reflect.Constructor; public class Demo2 { public static void main(String[] args) throws Exception { // 获取类类 Class c1 = Student.class; // 实例化反射 在实列反射newInstance创建对象的时候调用的是无参构造方法 Student s1 = (Student) c1.newInstance(); System.out.println(s1); // Constructor底层parameterTypes它代表了参数的类型 // 调用一个参数 getConstructor获取对象的构造函数 newInstance创建一个对象 Constructor cr1 = c1.getConstructor(String.class); Student student1 = (Student) cr1.newInstance("s002"); System.out.println(student1); // 调用二个参数 Constructor cr2 = c1.getConstructor(String.class, String.class); Student student2 = (Student) cr2.newInstance("s002", "nb"); System.out.println(student2); // 调用私有化getDeclaredConstructor的方法 不能再用getConstructor它只适用于公共方法中 Constructor cr3 = c1.getDeclaredConstructor(Integer.class); // 如果想调用私有化中的方法需要打开它 cr3.setAccessible(true);// 打开 Student student3 = (Student) cr3.newInstance(1); System.out.println(student3); } }
运行结果:
四,反射动态方法调用
4.1方法:
- newInstance(); 创建对象
- getMethod(); 获取构造方法名
- getDeclaredMethod(); 获取私有化构造函数
- invoke(); 调用方法
代码展示:
package com.zking.LiuBing; /** * 调用动态方法 和调用构造方法区别: * 调用构造方法:在调用构造的函数的时候没有构造函数名 直接调用参数 * 调用动态方法:在调用构造的函数的时候前有构造函数名后接参数 * * 调用动态构造函数 * 先获取构造方法getMethod(构造函数名,类型); 再调用invoke方法 * * 私有方法: * 先获取构造函数getDeclaredMethod(构造函数名,类型); * 打开私有化 * 再调用方法 invoke3 * * getMethod是一个可变的方法 */ import java.lang.reflect.Method; public class Demo3 { public static void main(String[] args) throws Exception { //获取类类 Class c= Student.class; //实列反射 Student stu1 = (Student) c.newInstance(); //调用无参方法 // getMethod获取对象 再调用方法 Method m1= c.getMethod("hello"); //调用方法 Object invoke = m1.invoke(stu1); System.out.println(invoke); //调用有参方法 // getMethod是一个可变的方法 Method m2= c.getMethod("hello",String.class); Object invoke2 = m2.invoke(stu1,"大朋友"); System.out.println(invoke2); //调用私有方法 getDeclaredMethod调用私有的对象 Method m3= c.getDeclaredMethod("add", Integer.class,Integer.class); m3.setAccessible(true);//打开私有化 //invoke动态方法 Object invoke3 = m3.invoke(stu1,2,2); System.out.println(invoke3); } }
运行结果:
五,反射读写属性
5.1 方法
- getDeclaredFields(); 获取所有对象
- getDeclaredField();获取单个属性值
代码展示:
package com.zking.LiuBing; import java.lang.reflect.Field; /** * 反射读写属性 * * @author 兵 想要获取值是需要打开私有化的 setAccessible getDeclaredFields获取整条对象 加了s * getDeclaredField获取单个属性 不加s */ public class Demo4 { public static void main(String[] args) throws Exception { // 获取类类 Class c1 = Student.class; // 实列一个对象 Student student = new Student("002", "小朋友"); student.age = 3; // 打印 System.out.println("学生id:" + student.getSid() + "学生名称:" + student.getSname() + "年龄:" + student.age); // 修改属性值 Field snamefield = c1.getDeclaredField("sname"); snamefield.setAccessible(true);// 打开私有化 snamefield.set(student, "小黑子");// 所需要修改的值 Field[] field = c1.getDeclaredFields(); for (Field field2 : field) {// 遍历 field2.setAccessible(true);// 打开私有化 // 查看所有 System.out.println(field2.getName() + "" + field2.get(student)); } } }
运行结果: