文章参考:学习网站 how2java.cn
参考博客:(敬业的小码哥)https://blog.csdn.net/sinat_38259539/article/details/71799078
(青色的画轴)https://www.cnblogs.com/yrstudy/p/6500982.html
(chris.wu)https://www.cnblogs.com/buoge/p/9285142.html
1.反射的概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
Java反射是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public、static等)、superclass(例如Object)、实现之interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于运行时改变fields内容或唤起methods。
2.为什么要用反射
Java中编译类型有两种:
(1)静态编译:在编译时确定类型,绑定对象,即通过
在你敲代码的时候,就已经确定你要用的是哪个类的对象,new一个出来,使用
(2)动态编译:运行时确定类型,绑定对象。动态编译最大限度地发挥了Java的灵活性,体现了多态的应用,可以减低类之间的耦合性。
在你运行之前,你只知道你要用的类的名称,而不知道其具体有哪些属性,哪些方法,却可以在运行后操作它
因此,Reflection可以在运行时加载、探知、使用编译期间完全未知的classes。即Java程序可以加载一个运行时才得知名称的class,获取其完整构造,并生成其对象实体、或对其fields设值、或唤起其methods。
例如: 我们要创造两个类似的类的对象
传统方法:
interface fruit{ public abstract void eat(); } class Apple implements fruit{ @Override public void eat() { System.out.println("eat Apple"); } } class Orange implements fruit{ @Override public void eat() { System.out.println("eat Orange"); } } class Factory{ public static fruit getInstance(String fname){ fruit f = null; if(fname.equals("Apple")) f = new Apple(); if(fname.equals("Orange")) f = new Orange(); return f; } }
因此,当我们有了更多水果时,就需要重写工厂类,来添加更多选择,这不仅是个重复的工作,且代码的耦合度很高。
反射机制:
class Factory{ public static fruit getInstance(String fname){ fruit f = null; try{ f=(fruit)Class.forName(fname).newInstance(); }catch (ClassNotFoundException e){ e.printStackTrace(); } return f; } }
而使用了反射机制的工厂类,则可以无需修改代码,只要你实现了一个类,应用了fruit接口,就可以用这个工厂类创造水果(实例)。
实际上,反射可以做的还有很多,这只是冰山一角。例如在Spring框架中的依赖注入,反转控制等。反射有着十分强大的功能。熟练的使用可以实现非常丰富的功能。
3.怎么用反射
实现Java反射机制的类都位于java.lang.reflect包中:
1、Class类:代表一个类
2、Field类:代表类的成员变量(类的属性)
3、Method类:代表类的方法
4、Constructor类:代表类的构造方法
5、Array类:提供了动态创建数组,以及访问数组的元素的静态方法
(1)Class类: 类对象,当获取到一个类对象后,你就能够进一步获取这个类的更多信息
获取类对象的三种方法:forName() , ClassName.class , instance.getClass() (ClassName指类名称,instance是指一个对象实例)
String className = "Hero"; //获取类对象的三种方法 try{ Class hClass1 = Class.forName(className); Class hClass2 = Hero.class; Class hClass3 = new Hero().getClass(); }catch (ClassNotFoundException e){ e.printStackTrace(); }
当我们获取到类对象时,会导致类中的属性被初始化,例如在类中加一个静态代码块,看在获取类时会不会初始化
public class Person { private String name; private int id; static { System.out.println("初始化Person类"); } }
(2)Constructor类:代表类的构造方法 我们可以用它来获取类的对象
String className = "ReflectTest.Person"; //通过Constructor获取一个对象 try{ Class hClass1 = Class.forName(className); Constructor c = hClass1.getConstructor(); Person p = (Person)c.newInstance(); p.setName("bilibili"); System.out.println(p); }catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e){ e.printStackTrace(); }
(3)Field类:代表类的成员变量(类的属性) 通过它获取类的变量
try{ Person p = new Person(); p.setName("lala"); p.setId(15); p.age=15; Class pClass = p.getClass(); Field f1 = pClass.getDeclaredField("age"); f1.set(p,15); System.out.println(p); }catch (NoSuchFieldException | IllegalAccessException e){ e.printStackTrace(); }
这里的age是public的,因此可以直接修改,而对于private修饰的属性,应该如下操作:
Class pClass = p.getClass(); //获取Person类的所有属性 Field[] fields = pClass.getDeclaredFields(); //对所有属性设置访问权限 AccessibleObject.setAccessible(fields,true); //对单个属性设置访问权限 Field f1 = pClass.getDeclaredField("name"); f1.setAccessible(true); f1.set(p,"Baolan");
(4)Method类:代表类的方法 通过它可以调用类中的方法
为此我先在Person类中添加了两个方法
public void run(){ System.out.println(name+" is Running"); }
private void feel(String f){
System.out.println(name+" is Felling "+f);
}
然后我们来通过反射调用
Class pClass = p.getClass(); //获取类中叫做run的方法 Method mrun = pClass.getMethod("run"); mrun.invoke(p);
同样的,对于私有的方法,要设置访问权限,同时要注意feel是一个有参数的方法,需要在调用时导入参数
Class pClass = p.getClass(); //获取类中叫做feel的方法,并导入其参数的类型 Method mfeel = pClass.getDeclaredMethod("feel",String.class); mfeel.setAccessible(true); //在使用feel方法时,加入feel方法需要的参数"Good" mfeel.invoke(p,"Good");
4.测试用例
这里我们用配置文件来进行两种方法的切换调用
首先我们定义两个类,实现两个不同的方法
public class DoSomething { public void doSome1(){ System.out.println("Do Great"); } }
public class DoSomething2 { public void doSome2(){ System.out.println("Do Bad"); } }
然后将其中一个的信息保存到一个txt文件中
class=ReflectTest.DoSomething method=doSome1
然后通过配置文件获取其内容,调用文件中保存的方法
try{ //从保存类名称和方法名称的文件中获取信息 File configfile = new File("D:\\IDEAworkspace\\JavaBasick\\methodtxt.txt"); Properties config = new Properties(); config.load(new FileInputStream(configfile)); String className = (String)config.get("class"); String methodName = (String)config.get("method"); //根据类名称获取类对象 Class dclass = Class.forName(className); //根据方法名称获取方法 Method method = dclass.getMethod(methodName); //获取构造器 Constructor c = dclass.getConstructor(); //实例化 Object dosomething = c.newInstance(); //调用指定方法 method.invoke(dosomething); }catch (Exception e){ e.printStackTrace(); }
可以看到,在上面的代码中,我们并没有指出要用什么类,以及什么方法,这些都是只保存在methodtxt文件中的
因此只要我们修改文件中的内容,就可以调用不同类的不同方法了,如
class=ReflectTest.DoSomething2 method=doSome2
原文:https://www.cnblogs.com/wqq-blog/p/10561956.html