反射机制事理图
获取字节码文件工具的三种办法
Class clazz1 = Class.forName(“全限定类名”);//通过Class类中的静态方法forName,直接获取到一个类的字节码文件工具,此时该类还是源文件阶段,并没有变为字节码文件。Class clazz2 = Person.class;//当类被加载成.class文件时,此时Person类变成了.class,在获取该字节码文件工具,也便是获取自己, 该类处于字节码阶段。Class clazz3 = p.getClass();//通过类的实例获取该类的字节码文件工具,该类处于创建工具阶段有了字节码文件工具才能得到类中所有的信息,我们在利用反射获取信息时,也要考虑利用上面哪种办法获取字节码工具合理,视不同情形而定。
反射的优缺陷:
优点:在运行时得到类的各种内容,进行反编译,对付Java这种先编译再运行的措辞,能够让我们很方便的创建灵巧的代码,这些代码可以在运行时装置,无需在组件之间进行源代码的链接,更加随意马虎实现面向工具。
缺陷:
1)反射会花费一定的系统资源,因此,如果不须要动态地创建一个工具,那么就不须要反射;2)反射调用方法时可以忽略权限检讨,因此可能会毁坏封装性而导致安全问题。反射的用场:
反编译:.class-->.java通过反射机制访问java工具的属性,方法,布局方法等当我们在利用IDE,比如Ecplise时,当我们输入一个工具或者类,并想调用他的属性和方法是,一按点号,编译器就会自动列出他的属性或者方法,这里便是用到反射。反射最主要的用场便是开拓各种通用框架。比如很多框架(Spring)都是配置化的(比如通过XML文件配置Bean),为了担保框架的通用性,他们可能须要根据配置文件加载不同的类或者工具,调用不同的方法,这个时候就必须利用到反射了,运行时动态加载须要的加载的工具。例如,在利用Strut2框架的开拓过程中,我们一样平常会在struts.xml里去配置Action<action name="login" class="org.ScZyhSoft.test.action.SimpleLoginAction" method="execute"> <result>/shop/shop-index.jsp</result> <result name="error">login.jsp</result> </action>
反射常用方法
反射常用方法
通过字节码工具创建实例工具
/ 类工具 Class的实例 JVM在加载一个类的class文件时,就会同时创建一个Class的实例,利用该实例记录加载的 类的统统信息(类名,有哪些属性,哪些方法,哪些布局器等)。并且每个被JVM加载的类都有 且只有一个Class的实例与之对应。 反射的第一步便是获取要操作的类的类工具,以便程序在运行期间得知要操作的类的统统信息 然后对其进行相应的操作。 获取一个类的类工具的常见办法: 1:类名.class 例如: Class cls = String.class; Class cls = int.class; 把稳:基本类型获取类工具只有这一种办法。 2:Class.forName(String className) 例如: Class cls = Class.forName("java.lang.String"); 这里传入的类名必须是类的完备限定名,即:包名.类名 3:还可以通过类加载器形式完成 / //获取String的类工具// Class cls = String.class;// Class cls = ArrayList.class;// Class cls = Class.forName("java.lang.String"); Scanner scanner = new Scanner(System.in); System.out.println("请输入类名:"); String className = scanner.nextLine(); /全限定名称: java.util.ArrayList、java.util.HashMap、java.lang.String/ Class cls = Class.forName(className); //获取当前类工具所表示的类的完备限定名 String name = cls.getName(); System.out.println(name); //仅获取类名(不包含包名) name = cls.getSimpleName(); System.out.println(name); / Package getPackage() 获取当前类工具所表示的类的包,返回的Package实例表示该包信息 / Package pack = cls.getPackage(); String packName = pack.getName(); System.out.println("包名"+packName); / Method[] getMethods() 获取当前类工具所表示的类中定义的所有公创办法,包含从超类继续下来的方法 java.lang.reflect.Method类,方法工具 该类的每一个实例用于表示某个类中定义的一个方法,通过它可以获取其表示的方法中的 干系信息(方法名,参数个数,参数类型,返回值类型等等,并且还可以调用这个方法) / Method[] methods = cls.getMethods(); for(Method method : methods){ System.out.println(method.getName()+"()"); }
通过公开的无参布局器实例化的功能
//1获取要实例化的类的类工具// Class cls = Class.forName("reflect.Person");Scanner scanner = new Scanner(System.in);System.out.println("请输入要实例化的类名:");String className = scanner.nextLine();Class cls = Class.forName(className);//2类工具直接供应了可以通过公开的 无参布局器 实例化的功能Object obj = cls.newInstance();System.out.println(obj);
利用有参布局器进行实例化
Class cls = Class.forName("reflect.Person");//获取Person的布局器Person(String,int)// cls.getConstructor();//不穿任何参数时获取的仍旧是无参布局器Constructor c = cls.getConstructor(String.class,int.class);//new Person("王五",55);Object obj = c.newInstance("王五",55);//实例化时要传入布局器哀求的实际参数System.out.println(obj);
利用反射机制调用方法
//1实例化Class cls = Class.forName("reflect.Person");//Object obj = new Person();Object obj = cls.newInstance();//2调用方法//2.1通过类工具获取要调用的方法Method method = cls.getMethod("sayHello");//获取的是无参的sayHello方法//2.2通过获取的方法工具来调用该方法// obj.sayHello() 由于obj指向的是及一个Person工具,因此反射机制可以调用到它的sayHello()method.invoke(obj);
调用有参方法
Class cls = Class.forName("reflect.Person");Object obj = cls.newInstance();//doSomeThing(String)Method method = cls.getMethod("doSomeThing", String.class);method.invoke(obj,"玩游戏");//p.doSomeThing("玩游戏");//doSomeThing(String,int)Method method1 = cls.getMethod("doSomeThing", String.class,int.class);method1.invoke(obj,"作业",5);
得到Class所有方法(包括私有)
Class cls = Class.forName("reflect.Person");Object obj = cls.newInstance();/ getMethod(),getMethods() 它们都是获取Class所表示的类的所有公创办法,包含从超类继续的 getDeclaredMethod(),getDeclaredMethods() 这两个方法获取的都是Class所表示的类中当前类自身定义的方法。包含私有方法 /// Method[] methods = cls.getDeclaredMethods();// for(Method method : methods){// System.out.println(method.getName());// }Method method = cls.getDeclaredMethod("dosome");method.setAccessible(true);//强行打开dosome方法的访问权限method.invoke(obj);//p.dosome()
int parameterCount() 返回方法参数个数
Class cls = Class.forName("reflect.Person");Object obj = cls.newInstance();//获取所有方法Method[] methods = cls.getMethods();for (Method method : methods){ //判断方法名称包含 say 并且 无方法参数 if(method.getName().contains("say") && method.getParameterCount()==0 ){ System.out.println("自动调用方法:"+method.getName()); method.invoke(obj); }}
自动调用本类自己定义的无参的公创办法Demo如下:
//1这里确当前目录表示的是当前ReflectDemo7这个类所在最外层包的上一级目录// File dir = new File(// ReflectDemo7.class.getClassLoader().getResource(".").toURI()// );//2这里确当前目录便是当前类所在的目录 【文件的目录】File dir = new File( ReflectDemo7.class.getResource(".").toURI());System.out.println(dir.getName());//通过当前类ReflectDemo7的类工具获取所在的包名String packageName = ReflectDemo7.class.getPackage().getName();System.out.println("ReflectDemo7类的包名是:"+packageName);//获取ReflectDemo7.class文件所在的目录中所有.class文件File[] subs = dir.listFiles(f->f.getName().endsWith(".class"));for(File sub : subs){ //获取字节码文件的文件名 String fileName = sub.getName(); System.out.println(fileName); / 由于java命名哀求,文件名必须与类名同等,以是我们可以通过文件名得知该字节码 文件中保存的类的类名 / String className = fileName.substring(0,fileName.indexOf(".")); //加载该类的类工具 Class cls = Class.forName(packageName+"."+className); Object obj = cls.newInstance(); //通过类工具获取本类定义的所有方法 Method[] methods = cls.getDeclaredMethods(); //遍历每个方法,检讨哪个方法是无参的 for(Method method : methods){ //Modifier.PUBLIC 这是一个整数 代表类中Public的方法判断 if(method.getParameterCount()==0 && method.getModifiers() == Modifier.PUBLIC){ System.out.println("自动调用"+className+"的方法:"+method.getName()); method.invoke(obj); } }}
补充:JDK5之后推出了一个特性:变长参数
该特性用于适应那些传入参数的个数不固定的利用场景,使得利用一个方法就可以办理该问题,而无需穷尽。 所有参数个数组合的重载。
public static void main(String[] args) { dosome(1,"one"); dosome(2,"one","two"); dosome(1,"one","two","three"); } / 一个方法里只能有一个变长参数,且必须是末了一个参数 @param arg / public static void dosome(int a,String... arg){ System.out.println(arg.length); System.out.println(Arrays.toString(arg)); }
学习记录,如有侵权请联系删除。