一、含义:
Java反射机制是在运行状态中,对于任意一个类(class文件),都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性
这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制
动态获取类中信息,就是Java反射
可以理解为对类的解剖
反射必须要有接口和配置文件两大元素。
二、 字节码文件对象
想要对一个类文件进行解剖,只要获取到该类字节码文件对象即可
|-- 用于描述字节码的类叫class Class{
提供获取字节码文件中的内容。比如名称,字段,构造函数,一般函数
}
该类就可以获取字节码文件中的所有内容,那么反射,就是依靠该类完成的。
三、获取Class对象的三种方式
package com.FansheReflect; import com.JavaFanShe.Person; /** * @Author wufq * @Date 2020/9/8 16:38 */ public class ReflectDemo { public static void main(String[] args) throws ClassNotFoundException { // getClassobject_1(); // getClassobject_2(); getClassobject_3(); } /* 方式三:通过给定类的字符串名称就可以获取类,更为扩展 可是用Class类中的方法完成。 该方法就是forName 这种方法只需要名称即可,更方便,扩展性更强 * */ public static void getClassobject_3() throws ClassNotFoundException { String className = "com.JavaFanShe.Person";// Class classs = Class.forName(className); //以下片段返回命名为java.lang.Thread的类的运行时Class描述符,所以定义的类字符串是完整的带包名的类名称 System.out.println(classs); } /* * 方式一:获取字节码对象的方式: * 1.object类中的getClass方法 * 想要这种方式,必须要明确具体的类,并创建对象 * 麻烦!!! * */ public static void getClassobject_1(){ Person p = new Person(); Class aClass=p.getClass(); //调用person的缺省构造器 -->Person run... Person p1 = new Person(); Class p1Class= p1.getClass();//调用person的缺省构造器 -->Person run... System.out.println(aClass==p1Class); } /*方式二: * 任何数据类型都具备一个静态的属性.class来获取其对应的class对象 * 相对简单,但是还是要明确用到类中的静态成员 * */ private static void getClassobject_2() { Class classz = Person.class; Class classz1 = Person.class; System.out.println(classz==classz1); } }
四、获取Class中的构造函数
|-- 寻找类名称文件,并加载进内存,生成class对象
|-- 通过getConstructor(Class<?>... parameterTypes)方法获取指定的构造方法,返回Constructor对象
|-- 通过newInstance()方法实现对象的生成
备注:如果调用缺省构造器的话,就不需要获取指定的构造方法,,直接实现方法的生成
package com.FansheReflect; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; /** * @Author wufq * @Date 2020/9/9 15:46 */ public class ReflectDemo01 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { //creatNewObject_2(); //creatNewObject(); creatNewObject_1(); } public static void creatNewObject(){ //早期初始化对象-->缺省构造器、构造方法调用 com.JavaFanShe.Person p = new com.JavaFanShe.Person(); com.JavaFanShe.Person p1 = new com.JavaFanShe.Person("小强",23); } //现在:通过该名称类文件地址 -->缺省构造器、构造方法调用 public static void creatNewObject_1() throws ClassNotFoundException, IllegalAccessException, InstantiationException { String Naame ="com.JavaFanShe.Person"; //寻找该名称类文件,并加载进内存,并产生class对象 Class classs = Class.forName(Naame); //如何产生对象呢? Object obj =classs.newInstance(); } public static void creatNewObject_2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { String Naame ="com.JavaFanShe.Person"; //寻找该名称类文件,并加载进内存,并产生class对象 Class classs = Class.forName(Naame); //获取到了指定对象的构造方法 Constructor con =classs.getConstructor(String.class,int.class); //真实产生对象 Object obj = con.newInstance("小强",22); } } =====执行结果===== Person param run...小强::22 Person run... Person param run...小强::23 Person run...、获取Class中的构造函数 wu
五、获取Class中的字段
|-- 寻找类名称文件,并加载进内存,生成class对象
|-- class对象调用getField() -->只能获取共有的
class对象调用getDeclaredField("age"); -->只获取本类,包含私有
|-- 对私有字段的访问取消权限检查。暴力访问
field= clazz.getDeclaredField("age");
|-- 产生真实的对象:
field.setAccessible(true);
|-- 给对象进行赋值和获取
Object obj = clazz.newInstance(); field.set(obj,89); Object o = field.get(obj); System.out.println(o);
--------->完整的代码<-----------
package com.FansheReflect; import java.lang.reflect.Field; /** * @Author wufq * @Date 2020/9/15 14:21 */ public class ReflectDemo02 { public static void main(String[] args) throws NoSuchFieldException, ClassNotFoundException, InstantiationException, IllegalAccessException { getFileDemo(); } /* * 获取字节码文件中的字段*/ public static void getFileDemo() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException { Class clazz = Class.forName("com.JavaFanShe.Person"); Field field=null; /*//getField()返回公共成员字段,而Person对象里面的age是一个私有属性,所以不适合 file=clazz.getField("age"); System.out.println(file);//java.lang.NoSuchFieldException: age*/ //只能获取本类,包含私有 field= clazz.getDeclaredField("age"); //对私有字段的访问取消权限检查。暴力访问 field.setAccessible(true); //真实产生对象 Object obj = clazz.newInstance(); field.set(obj,89); Object o = field.get(obj); System.out.println(o); } } ====执行结果==== Person run... 89
六、获取Class中的方法
1、 获取无参方法
2、 获取有参方法
|-- 寻找类名称对象,并加载进内存,返回class对象
|-- 调用getMethod()方法:无参和有参的区别在于:参数用String.class int.class
|-- 生成对象
|-- 执行目标方法:invoke
package com.FansheReflect; import java.lang.reflect.Constructor; import java.lang.reflect.Method; /** * @Author wufq * @Date 2020/9/15 16:35 * 获取class类的方法 -->单独获取一个方法(无参/有参) */ public class ReflectDemo04 { public static void main(String[] args) throws Exception { // getMethodDemo_1(); getMethodDemo_2(); } //有参方法 public static void getMethodDemo_2() throws Exception { Class clazz = Class.forName("com.JavaFanShe.Person"); Method method= clazz.getMethod("paramMethod",String.class,int.class); Object obj = clazz.newInstance(); method.invoke(obj,"小强",23); } //无参方法 public static void getMethodDemo_1() throws Exception { Class clazz = Class.forName("com.JavaFanShe.Person"); Method method=clazz.getMethod("show",null);//调用无参方法 Object obj =clazz.newInstance();//产生真实的对象 method.invoke(obj,null);//用来执行某个的对象的目标方法 //调用构造方法 Constructor con= clazz.getConstructor(String.class,int.class); Object obj_1 = con.newInstance("小明",22); } }
七、反射练习
电脑运行:在电脑主板的基础上新增加声卡和网卡
思路:
|-- 有可能在电脑主板的基础上,新增加网卡、声卡....
|-- 所以需要把新增加的网卡、声卡做成接口,然后每新增加一个就继承接口,已实现接口的功能
|-- 在电脑主板上写一个方法(usePCI)实现接口
|-- 在测试时,调用usePCI接口实现声卡、网卡的功能
这样做的优势:即使新增加功能,只需要新增加的功能实现了接口就能被调用
劣势:每次添加声卡、网卡这样的设备都需要修改代码传递一个新创建的对象
问题:能不能不修改对象就可以完成这个动作
解决方法:不能new完成,通过修改配置文件,然后获取其class文件,在内部实现创建对象的动作。
----------->接口:PCI<------------
package com.FansheReflectTest; /** * @Author wufq * @Date 2020/9/15 17:34 */ public interface PCI { void open(); void close(); }
----------->声卡/网卡类:SoundCard/WangCard 继承实现接口<------------
package com.FansheReflectTest; /** * @Author wufq * @Date 2020/9/15 17:32 */ public class SoundCard implements PCI{ public void open(){ System.out.println("open...."); } public void close(){ System.out.println("close...."); } }
package com.FansheReflectTest; /** * @Author wufq * @Date 2020/9/15 17:40 */ public class WangCard implements PCI{ public void open(){ System.out.println("net open...."); } public void close(){ System.out.println("net close...."); } }
----------->主板类:Mainboard<------------
package com.FansheReflectTest; /** * @Author wufq * @Date 2020/9/15 17:18 */ public class Mainboard { public void run(){ System.out.println("main bpard run..."); } public void usePCI(PCI p){ //采用接口的好处是,即使新增加功能,只需要新增加的功能实现了接口就能被调用 if(p!=null){ p.close(); p.open(); } } }
----------->测试类:ReflectTest<------------
package com.FansheReflectTest; import java.io.File; import java.io.FileInputStream; import java.util.Properties; /** * @Author wufq * @Date 2020/9/15 17:16 * * 电脑运行 * --->采用接口的好处是,即使新增加功能,只需要新增加的功能实现了接口就能被调用 * ---> 缺点:每次添加设备都需要修改代码传递一个新创建的对象 * 能不能不修改对象就可以完成这个动作。 * --->不用new完成,而是获取其class文件,在内部实现创建对象的动作。 */ public class ReflectTest { public static void main(String[] args){ Mainboard mb = new Mainboard(); mb.run(); /*mb.usePCI(new SoundCard()); mb.usePCI(new WangCard());*/ try { File configFile = new File("pic.properties"); Properties prop = new Properties(); FileInputStream fis = new FileInputStream(configFile); prop.load(fis); for(int x=0;x<prop.size();x++){ String pciName = prop.getProperty("pci"+(x+1)); Class clazz = Class.forName(pciName); PCI pci=(PCI) clazz.newInstance(); mb.usePCI(pci); } fis.close(); } catch (Exception e) { e.printStackTrace(); } } }
try {}catch{} 整个这一段代码都可以封装
----------->配置文件:pic.properties<------------
pci1=com.FansheReflectTest.SoundCard
pci2=com.FansheReflectTest.WangCard
后面如果新增加设备,只需要在修改配置文件里面的名称即可