- 前言
-
一开始是不知道反射的,然后找了很多资料,看了一些视屏,基本上第一句话是,反射都是在运行状态才知道是什么对象/类,然后可以获取对象/类的构造方法,属性,等等....说的,感觉这东西什么都能拿到似的,很厉害,但在编译期却感觉什么都拿不到似的,连我作为一个开发者都不知道运行了哪个对象。所以搞得我特别混乱,什么是我看的见摸不着,但这东西却会时刻发出音响。搞了很久,差点就放弃了,后来看了一个视屏,跟着写了下,觉得理解了一点。ps:在1997年2月的时候,发布jdk1.1提出了内部类,反射,jar文件格式。搞得我现在还在学习以前人的思想!!
-
- 什么是反射?
- 什么是反射?这东西我前面都提到了吧,来,按照专业术语我跟你讲讲:
- 对于任意一个对象,都能够调用它的任意一个方法和属性
- 对于任意一个类,都能够知道这个类的所有属性方法
- 反射机制是在运行状态中
- 说了这么多,很神奇,反正我到现在都还是有点懵懵懂懂的,现在咱们把这个理念来抛弃,往下走。
- 什么是反射?这东西我前面都提到了吧,来,按照专业术语我跟你讲讲:
- 反射能干啥
- 很多人都说,反射是一个非常重要的知识点,不懂,你以后什么都做不了,什么动态代理,什么增删改查,等等,你还真都做不了。听到这,我感觉十分的恐慌,心想:这么叼!不行,我得要去学学,不然工作咋搞。然后找啊找,找啊找,咦,发现了一些专业术语。来,咱们看看:
- 生成动态代理
- 在运行时调用任意一个对象的方法
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个对象所属的类
- 哇!这么多专业术语!把我搞得很慌啊,但是仔细一看可以总结成一句话:在运行时期我们可以对任意属性或者类进行操作。好!我很是不信,就开始我的掉头发之旅了(敲代码)
- 很多人都说,反射是一个非常重要的知识点,不懂,你以后什么都做不了,什么动态代理,什么增删改查,等等,你还真都做不了。听到这,我感觉十分的恐慌,心想:这么叼!不行,我得要去学学,不然工作咋搞。然后找啊找,找啊找,咦,发现了一些专业术语。来,咱们看看:
- 代码模块
- 首先,在我们写反射的代码之前,我们得要先写一个入口吧!不然,你连这个门都进不了。但很奇怪的是,不像我们之前new对象,然后调用方法。反射是直接获取Class对象,然后进行操作,这是为什么呢?来,我们往下看:
package com.qsk.myInterface; public interface MyInterface { void interfaceMethod(); } //接口2 package com.qsk.myInterface; public interface MyInterface2 { void interfaceMethod2(); }
- 创建两个接口
-
//接口1 package com.qsk.myInterface; public interface MyInterface { void interfaceMethod(); } //接口2 package com.qsk.myInterface; public interface MyInterface2 { void interfaceMethod2(); }
- 创建两个类
-
package com.qsk.entity; import com.qsk.myInterface.MyInterface; import com.qsk.myInterface.MyInterface2; public class Person implements MyInterface,MyInterface2 { private int id; private String name; private int age; public String message; public int getId() { return id; } public void setId(int id) { this.id = id; } 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; } public Person(int id, String name, int age) { this.id = id; this.name = name; this.age = age; } private Person(String name) { super(); this.name = name; } public Person(int id) { this.id = id; } public Person() { } private void privateMethod(){ System.out.println("私有"); } private void privateTry(int id,String name){ System.out.println(id+"====="+name); } public static void statiMehod() { System.out.println("Static Method 。。。。"); } public void interfaceMethod() { // TODO Auto-generated method stub System.out.println("Interface1 Method。。。。"); } public void interfaceMethod2() { // TODO Auto-generated method stub System.out.println("Interface2 Method。。。。"); } }
package com.qsk.entity; import com.qsk.myInterface.MyInterface; import com.qsk.myInterface.MyInterface2; public class Person implements MyInterface,MyInterface2 { private int id; private String name; private int age; public String message; public int getId() { return id; } public void setId(int id) { this.id = id; } 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; } public Person(int id, String name, int age) { this.id = id; this.name = name; this.age = age; } private Person(String name) { super(); this.name = name; } public Person(int id) { this.id = id; } public Person() { } private void privateMethod(){ System.out.println("私有"); } private void privateTry(int id,String name){ System.out.println(id+"====="+name); } public static void statiMehod() { System.out.println("Static Method 。。。。"); } public void interfaceMethod() { // TODO Auto-generated method stub System.out.println("Interface1 Method。。。。"); } public void interfaceMethod2() { // TODO Auto-generated method stub System.out.println("Interface2 Method。。。。"); } }
- 好!现在咱们开始操作了,上面说了,咱们得要Class对象,不然连反射的门都进不了,然而得到一个类/属性的Class对象有3种方式,来让我们看看有哪三种吧!
-
// 获取反射对象(反射入口):Class public static void getReClass() { // 1. Class.forName(全类名‘包名加类名’) try { Class<?> perClass1 = Class.forName("com.qsk.entity.Person"); System.out.println(perClass1); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 2. 类名.class Class<?> perClass2 = Person.class; System.out.println(perClass2); // 3.对象.getClass() Person person = new Person(); Class<?> perClass3 = person.getClass(); System.out.println(perClass3); }
- 实验一下,看这三个获取的是什么吧!
- 哈!不出我所料,全部都是一样的,但有的人会说,第二个和第三个都知道,但第一个为什么要包名加类名呢?对于这个问题,咱们可以看看控制台输出的内容。全部都是包名加类名!所以第一个是要完整类名去查找Class,有的话就输出,没有就异常,所以有一个try/catch。
- 好!知道了Class怎么获取就好办了,咱们可以根据这个Class获取方法属性等等,接下来试试先
- ·获取方法
-
// 获取方法 public static void getReMethod() { // Class入口 Class<?> perClass = null; try { perClass = Class.forName("com.qsk.entity.Person"); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 获取所有的公共的方法 // 1.本类 以及 父类、接口中的所有方法 // 2.符合访问修饰符规律 Method[] methods = perClass.getMethods(); for (Method method : methods) { System.out.println(method); } System.out.println("==================="); // 获取当前类的所有方法 // 1.只能是当前类 // 2.忽略访问修饰符限制 Method[] declaredMethods = perClass.getDeclaredMethods(); for (Method method : declaredMethods) { System.out.println(method); } }
- 获取所有接口
-
// 获取所有接口 public static void getInterface() { // Class入口 Class<?> perClass = null; try { perClass = Class.forName("com.qsk.entity.Person"); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } Class<?>[] interfaceClasses = perClass.getInterfaces(); for (Class<?> class1 : interfaceClasses) { System.out.println(class1); } }
- 获取所有的父类
-
// 获取所有的父类 public static void getSuperClass() { // Class入口 Class<?> perClass = null; try { perClass = Class.forName("com.qsk.entity.Person"); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } Class<?> superClass = perClass.getSuperclass(); System.out.println(superClass); }
- 获取所有的构造方法
-
// 获取所有的构造方法 public static void getConstructer() { // Class入口 Class<?> perClass = null; try { perClass = Class.forName("com.qsk.entity.Person"); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } Constructor<?>[] constructers = perClass.getConstructors(); for (Constructor<?> constructor : constructers) { System.out.println(constructor); } }
- 获取属性
-
// 获取属性 public static void getAllField() { // Class入口 Class<?> perClass = null; try { perClass = Class.forName("com.qsk.entity.Person"); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 获取所有公共的属性 Field[] fields = perClass.getFields(); for (Field field : fields) { System.out.println(field); } System.out.println("==================="); // 获取所有属性 // 1.只能是当前类 // 2.忽略访问修饰符限制 Field[] declaredFields = perClass.getDeclaredFields(); for (Field field : declaredFields) { System.out.println(field); } }
- 获取当前反射所代表类(接口)的对象(实例)
-
// 获取当前反射所代表类(接口)的对象(实例) public static void getInstance() throws InstantiationException, IllegalAccessException { // Class入口 Class<?> perClass = null; try { perClass = Class.forName("com.qsk.entity.Person"); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } Object perInstance = perClass.newInstance(); Person per = (Person) perInstance; per.interfaceMethod(); }
- 呼~敲了这么多,感觉有点小累,但是想了想,拿起键盘开始总结:
- 我们可以看到获取一个类的所有方法/属性,和当前类的所有方法/属性是不同的,而不同点在:Declared(公告的,公然的,宣布) 这个单词上。在没加这个单词之前,我们可以获取所有的 public 方法/属性,甚至最大的类 Object 中的东西全给你搞过来了。但是加了 Declared 之后,我们只能获取本类的方法/属性,但直接忽略了访问修饰符的限制,连 private 的方法/属性都给你打印出来了。所以,如果我们需要本类的所有方法/属性的话,咱们就加上 Declared ,否则就不加。
- 像获取所有构造方法、父类、接口,我就不一 一详解了,否则得要敲死我,大同小异,主要来看看:newInstance(),这个东西我们后面主要用的是这个,这个和new不太同,在使用newInstance()方法的时候,必须保证这个类已经加载并且已经连接了。而且,new关键字能调用任何构造方法,newInstance()只能调用无参构造方法。最重要的是:如果我王者荣耀是王者段位,我要:ShenZhuanZi szz = new WangZhe(),但是呢,过了这个赛季,我就变成钻石了,我又要:ShenZhuanZi szz = new ZuanShi(),这样不贼麻烦,到时候我又上了个星耀又要改,不贼麻烦。但如果我搞了一个传子工厂的话,也就是工厂模式,我上王者,我就是王者,如果我掉到钻石了,我就是钻石了,不用去改,只需要改下我段位就行了:ShenZhuanZi szz = (wangZhe) szzClass.newInstance() ,这样,我就可以在不改变代码的情况下,换成另一个段位,到时候把东西放到配置文件里,再配合依赖注入的方法,就提高了软件的可伸缩性、可扩展性。
- 好!说了这么多东西,咱们就可以开始简单的操作一个类了!比如
- 操作类的属性
-
// 操作属性 public static void demo02() throws InstantiationException, IllegalAccessException, SecurityException, NoSuchFieldException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException { Class<?> perClass = null; try { perClass = Class.forName("com.qsk.entity.Person"); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } Person per = (Person) perClass.newInstance(); Field idField = perClass.getDeclaredField("id"); // 访问的是private修饰的id,但是private是私有的 // 修改属性访问权限 // 使用反射时,如果因为访问修饰符限制造成异常,可以通过 // Field/Method/Constructor.setAccessible(true);打开权限 idField.setAccessible(true); // 对象+设置的值 idField.set(per, 1); System.out.println(per.getId()); System.out.println("====================="); // 方法名,方法参数 Method declaredMethod = perClass.getDeclaredMethod("privateMethod", null); declaredMethod.setAccessible(true); // 方法的调用:invoke() // 对象+参数 declaredMethod.invoke(per, null); Method declaredMethod2 = perClass.getDeclaredMethod("privateTry", int.class, String.class); declaredMethod2.setAccessible(true); declaredMethod2.invoke(per, 2, "我我我"); }
- 这里咱们可以看到两个新的东西,一个是Accessible,一个是invoke。首先可以谈谈
- Accessible 是个什么东西,这是一个类似打开权限的玩意,前面我们也说了,我们可以通过 Declared 来获取本类所有的 private 的方法/属性,但是私处给你看了(手动脸红)你就别动我了,但是,作为一个广大男性,咱们说过蹭蹭不进去,哪几个办的到的!这时候,为了为所欲为,咱们就可以Accessible(易接近,易相处)来打开你的心扉,设置为true(给点好处),然后我们就可以对私有的地方(属性/方法)为所欲为了。
- invoke 没有 Accessible那么屌,invoke就相当于 set一样,第一个为类型,后面都是传过去的参数。
- 好,接下来我们可以写一个简单的txt文件,从里面进行反射,然后获取对象/方法
-
classname=com.qsk.entity.Person methodname=interfaceMethod
-
// 动态加载类名和方法 public static void demo04() throws FileNotFoundException, IOException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException { Properties prop = new Properties(); prop.load(new FileReader("class.txt")); String classname = prop.getProperty("classname"); String methodname = prop.getProperty("methodname"); Class<?> perClass = null; try { perClass = Class.forName(classname); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } Method method = perClass.getMethod(methodname); method.invoke(perClass.newInstance()); }
- 来看看运行结果:
- 如果我们在txt中改成其他的呢,比如一开始写的 Studen,会怎么样呢
-
classname=com.qsk.entity.Student methodname=sayHi
- 看,现在变成了这个了,我根本没有在代码里面改任何东西,就输出成了这样,所以,我们在编译的时候,我们完全不知道我们要给什么实现类,我们只有在运行的时候,看到了输出的东西,或者其他,我们才知道,哦!我们调用了这个类
- 接下来,再看一个例子
-
public static void demo05() throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException { ArrayList<Integer> list = new ArrayList<Integer>(); list.add(1); Class<?> listClass = list.getClass(); Method method = listClass.getMethod("add", Object.class); method.invoke(list, "张三..."); System.out.println(list); } 输出:
- 输出:
- 这里我们发现了反射可以越过泛型检查,管你之前是什么类型,在我这都可以插进去。虽然可以通过反射访问private等访问修饰符不允许访问的属性/方法,也可以忽略泛型的约束;但实际开发不建议这样使用,因此可能造成程序的混乱。就比如,某某是女的,我硬是把你当男的,要跟你睡,那别人不得报警啊
- 最后,我们可以通过反射写一个工具类,就是不管你是什么字段,给我类型,字段名,和插入的值,我就可以给你进行赋值
-
package com.qsk.util; import java.lang.reflect.Field; public class PropertyUtil { //per.setXxx(value) public static void setProperty(Object obj, String propertyName, Object value) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { Class<?> class1 = obj.getClass(); Field filed = class1.getDeclaredField(propertyName); filed.setAccessible(true); filed.set(obj, value); } }
- 测试
-
public static void demo06() throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException { Person per = new Person(); PropertyUtil.setProperty(per, "name", "zs"); PropertyUtil.setProperty(per, "age", 23); Student stu = new Student(); PropertyUtil.setProperty(stu, "score", 10); System.out.println(per.getName()); System.out.println(stu.getScore()); }
- 结果
- 看,我们成功的进行赋值操作,还可以读出来。
- 首先,在我们写反射的代码之前,我们得要先写一个入口吧!不然,你连这个门都进不了。但很奇怪的是,不像我们之前new对象,然后调用方法。反射是直接获取Class对象,然后进行操作,这是为什么呢?来,我们往下看:
- 总结
- 其实我看了好多文章,我自己总结一下:写了这么多,我相信看了这个的,就可以自己总结了,反正我是总结不出来的
JAVA反射
最新推荐文章于 2023-07-20 15:13:23 发布