反射
1.反射定义
-
反射是框架的灵魂,
-
什么是框架?
- 我们之前写一些功能都是通过最底层的代码去实现这些功能,为了提高开发效率,公司里面都是需要用到框架去开发,所谓java框架就是通过java语言给你封装了一个半成品,那你基于这个框架平台去开发能够节省很多的代码提高工作的效率。基本上所有的开发语言都会有不同的框架,比如学习前端我们最底层javascript,基于javascript的框架vue、jquery、bootstrap等等,java的框架比如现在流行的ssm、springboot、springcloud等等。
-
java的框架基本上都是通过反射实现的
-
反射:将类的各个组成部分封装为其他对象.这就是反射机制.
- 在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象都能够调用它的任意一个方法和属性(包括私有的方法和属性);这种动态获取信息以及动态调用对象的方法的功能就称为java的反射机制。通俗点讲,我们可以通过java反射技术,所有的类对我们来讲都是透明的,我们想要获取任意类的属性和方法都可以!
- 要想实现反射机制,就必须先获取该类的字节码文件对象(.class),通过字节码对象,就能够通过字节码的类中的方法获取到我们想要的信息(方法、属性、类名、父类名、所有实现的接口、构造方法等等);
- 每一个类对应着一个字节码文件也就对应着一个class类的对象,也就是字节码文件对象。
-
java代码在计算机中经历的三个阶段,分别是:
- 源代码阶段(编写代码时期)
- class类对象阶段(保存运行的时候首先类加载器加载编译源代码后加载到内存)
- runtime运行时阶段(运行到需要创建对象的代码的时候才会在内存开辟创建对象空间)
如图
- 反射的好处:
- 我们可以通过反射封装自己所需的框架
- 可以在程序运行的过程中操作这个对象(操作属性、方法等)
- 可以解耦合(不要让代码堆积在一块),提高程序的扩展性。
2.获取class对象(字节码对象)
要想获取到Person这个类里面所有的属性、方法、构造方法等首先要获取它的字节码对象
-
获取class有三种方式
-
方式1:class.forName(“全类名”);
Class cla1 = Class.forName("com.wu.entity.Person"); System.out.println(cla1);
多用于配置文件,将类名定义在配置文件中,读取文件加载类
-
方式2:类名.class
Class cla2 = Person.class; System.out.println(cla2);
多用于对象的传参
比如:Logger logger = Logger.getLogger(Person.class)
-
方式3:对象.getClass
Person p = new Person(); Class cla3 = p.getClass(); System.out.println(cla3);
多用于对象的获取字节码的方式
-
-
案例
-
Person类
public class Person { private String name; private int age; public String a; public String b; }
-
测试
public class Test { public static void main(String[] args) throws ClassNotFoundException { //要想获取到Person这个类里面所有的属性、方法、构造方法等首先要获取它的字节码对象 //方式1:class.forName("全类名"); Class cla1 = Class.forName("com.wu.entity.Person"); System.out.println(cla1); //方式2:Person.class Class cla2 = Person.class; System.out.println(cla2); //方式3:对象.getClass Person p = new Person(); Class cla3 = p.getClass(); System.out.println(cla3); //获取到的字节码对象(class对象)是同一个对象吗? //结论:是,因为同一个字节码文件(.class)在一次程序运行过程中,只会被加载一次, //不论通过哪一种方式获取的class对象都是同一个对象 System.out.println(cla1==cla2); System.out.println(cla2==cla3); } }
-
结论
字节码class对象只有一个! 因为同一个字节码文件(.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的class对象都是同一个对象
-
获取到了class字节码对象后,我们就可以通过class字节码对象获取到类的属性、方法、构造方法、父类等等。
-
3.通过Class对象获取类的信息
-
测试
import java.lang.reflect.Field; public class Test { public static void main(String[] args) throws Exception { Class<Person> personClass = Person.class; //通过person类型的字节码对象调用getFields()获取到person类的所有public修饰的成员变量 Field[] fields = personClass.getFields(); for (Field field : fields) { System.out.println(field); } System.out.println("==================="); //getField("a")获取Person类public修饰的属性a Field a = personClass.getField("a"); System.out.println(a); //获取到这些属性有啥用?当然是获取或者修改的值。 Person per = new Person(); //获取到Person类属性a里面的值 Object aValue = a.get(per); System.out.println(aValue); //修改Person类属性a里面的值 a.set(per, "张三"); System.out.println(per); System.out.println("=========================="); Field[] declareadFields = personClass.getDeclaredFields(); for (Field field : declareadFields) { System.out.println(field); } Field fieldName = personClass.getDeclaredField("name"); //报错:java.lang.IllegalAccessException是因此不能访问私有的属性 //解决办法,通过暴力反射 //访问之前先忽略访问修饰符的安全检查,就叫暴力反射 fieldName.setAccessible(true); String name = (String)fieldName.get(per); System.out.println("姓名是"+name); fieldName.set(per, "李四"); System.out.println(per); } }
-
结果
public java.lang.String com.wu2.entity.Person.a public java.lang.String com.wu2.entity.Person.b public java.lang.String com.wu2.entity.Person.c public java.lang.String com.wu2.entity.Person.d =================== public java.lang.String com.wu2.entity.Person.a 吴 Person [name=null, age=0, a=张三, b=null, c=null, d=null] ========================== private java.lang.String com.wu2.entity.Person.name private int com.wu2.entity.Person.age public java.lang.String com.wu2.entity.Person.a public java.lang.String com.wu2.entity.Person.b public java.lang.String com.wu2.entity.Person.c public java.lang.String com.wu2.entity.Person.d 姓名是null Person [name=李四, age=0, a=张三, b=null, c=null, d=null]
4.通过class类对象获取构造方法
-
获取构造方法的作用是创建对象
public class Test { public static void main(String[] args) throws Exception { //获取Class对象 Class personClass = Person.class; //getConstructor(String.class,int.class)获取到参数为String,int的那个构造方法 Constructor constructor = personClass.getConstructor(String.class,int.class); System.out.println(constructor); //获取构造方法作用:创建对象! Person person = (Person)constructor.newInstance("张三",18); System.out.println(person); //获取空对象 Constructor constructor2 = personClass.getConstructor(); Person person2 = (Person)constructor2.newInstance(); System.out.println(person2); //获取空对象,可以简化如下 Person person3 = (Person)personClass.newInstance(); System.out.println(person3); } }
5.通过class类对象获取成员方法
-
获取成员方法作用是执行方法
import java.lang.reflect.Method; public class Test { public static void main(String[] args) throws Exception { //获取Class对象 Class<Person> personClass = Person.class; //获取public修饰的方法 Method eat_method = personClass.getMethod("eat"); Person person = new Person(); //通过invoke()方法来执行获取到的方法 eat_method.invoke(person); Method eat_method2 = personClass.getMethod("eat",String.class); eat_method2.invoke(person, "牛排"); //getDeclaredMethod获取所有定义的方法 Method eat_method3 = personClass.getDeclaredMethod("sleep"); eat_method3.setAccessible(true); eat_method3.invoke(person); System.out.println("======================"); //获取Peraon以及父类的所有方法 Method[] methods = personClass.getMethods(); for (Method method : methods) { // System.out.println(method); String methodName = method.getName(); System.out.println("方法名"+methodName); } System.out.println("======================"); //获取所有定义的方法 Method[] methods2 = personClass.getDeclaredMethods(); for (Method method : methods2) { // System.out.println(method); String methodName = method.getName(); System.out.println("方法名"+methodName); } //获取类名 String className = personClass.getName(); System.out.println(className); } }
上机案例:
写一个"框架’,我们通过properties配置需要执行的类和方法,然后我们获取到配置信息中配置的信息通过反射机制执行。
-
实体类
public class Person { private String name; private int age; public String a = "吴"; public String b; public String c; public String d; public Person() { super(); } public Person(String name, int age) { super(); this.name = name; this.age = age; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + ", a=" + a + ", b=" + b + ", c=" + c + ", d=" + d + "]"; } public void eat() { System.out.println("eat--"); } public void eat(String food) { System.out.println("eat--"+food); } private void sleep() { System.out.println("睡觉zzzz"); } }
-
创建pro.properties文件
className=com.wu.entity.Person methodName=eat
-
测试
public class Test { public static void main(String[] args) { Properties pro = new Properties(); try { //加载pro.properties配置文件 InputStream is = Test.class.getClassLoader().getResourceAsStream("pro.properties"); pro.load(is); //1.根据键获取到pro.properties配置的值 String className = pro.getProperty("className"); String methodName = pro.getProperty("methodName"); //2.创建class字节码对象 Class<?> cls = Class.forName(className); //创建实例对象 Object obj = cls.newInstance(); //得到对象方法 Method method = cls.getMethod(methodName,String.class); method.invoke(obj, "牛排"); } catch (Exception e) { e.printStackTrace(); } } }