【详细】关于Java中的反射

来源:互联网 发布:mac怎么玩qq游戏 编辑:程序博客网 时间:2024/06/03 19:57
前提,开始序号为2是因为这里摘自我的笔记
2.对象有编译类型和运行类型Object obj = new java.util.Date();编译类型:Object运行类型(obj对象真实的类型):java.util.Date需求:通过obj对象来调用java.util.Date类中的toLocaleString方法obj.toLocalString() //编译错误因为toLocalString方法不在Object类中解决方法:java.util.Date d = (java.util.Date)obj;d.toLocalString();新的问题:但,如果不知道对象的真实类型,无法做强制类型转换特别是,当某一个对象通过方法返回,看不到底层怎么办?->反射这里的问题的解答在下面的第13点3.类也是一种对象类:描述对象Class:描述类那么,Class类和Object类有什么关系?Object:表示一切对象Class:表示一切类4.到底,什么是反射?在运行时期,动态的去获取类中的成员信息(包、父类、接口、修饰符、类名、构造器、字段、方法等)Eclipse中的outline(大纲视图)就是通过反射编写的反射的API:Class:描述一切的类Constructor:描述一切的构造器Method:描述一切的方法使用反射:创建对象,调用方法然而,反射操作性能较低。但是-->Java中的框架都是基于反射编写5.Class类与Class实例Class类 :用来表示所有的类,是所有类的类型Class实例:表示在JVM中运行的一份份字节码文件Class类可以表示所有类,那如何明确是具体哪个类呢?由此,SUN公司提高了一个泛型,用来表示当前所表示的是哪一个类若当前描述String类:Class<String> clz若当前描述ArrayList类:Class<ArrayList> clz表示Class对象的三种方式1.类型.class表示一份字节码2.对象.getClass()获取当前对象的真实类型3.使用Class.forName(String className)根据类的全限定名来获取Class对象Class<java.util.Date> clz1 = java.util.Date.class;java.util.Date date = new java.util.Date();Class clz2 = date.getClass();Class clz3 = Class.forName("java.util.Date");System.out.println(clz1 + "\r\n" + clz2 + "\r\n" + clz3);/*class java.util.Date  class java.util.Date  class java.util.Date */ 6.九大内置的Class实例和数据的Class实例8大基本数据类型和void关键字都可以表示为Class的实例如:Class intClz = int.class;boolean blClz = boolean.class;问题:int和Integer是同一数据类型吗?System.out.println(int.class == Integer.class);//false在包装类中都有一个TYPE常量,返回对应的基本类型的Class对象System.out.println(int.class == Integer.TYPE); //true表示数组的Class对象方式1:数组名.getClass();Class clz1 = arr.getClass();方式2:数组类型.class;Class clz2 = int[].class;9.获取构造器Class类描述了所有的类的成员(构造函数、方法等)——>Class类具有获取某一个类的构造函数的方法步骤:1)获取被操作的类的字节码对象  2)获取构造函数获取构造器的方法:Constructor<?>[] getConstructors() 返回包含一个数组 Constructor对象反射由此表示的类的所有公共构造 类对象 Constructor<?>[] getDeclaredConstructors() 返回一个反映 Constructor对象表示的类声明的所有 Constructor对象的类  Constructor<T> getConstructor(类<?>... parameterTypes) 返回一个 Constructor对象,该对象反映 Constructor对象表示的类的指定的公共类函数 Constructor<T> getDeclaredConstructor(类<?>... parameterTypes) 返回一个 Constructor对象,该对象反映 Constructor对象表示的类或接口的指定 类函数。 10.调用构造器创建对象T newInstance(Object... initargs) 使用此 Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例。 通过构造器对象使用newInstance可以获得实例c = clz.getConstructor(String.class);obj = c.newInstance("公有、有参数");但是,对于私有的构造器,会有“非法访问”的异常c = clz.getDeclaredConstructor(String.class, int.class);//obj = c.newInstance("私有", 1);/* * Exception in thread "main" java.lang.IllegalAccessException * ... * can not access a member of class pers.honhong._02_createinstance.User with modifiers "private" */这里,需要使用setAccessible方法关闭安全检查void setAccessible(boolean flag) 将此对象的 accessible标志设置为指示的布尔值。c.setAccessible(true);obj = c.newInstance("私有", 1);11.获取类中的方法|--获取所有方法Method[] getMethods() 返回包含一个数组方法对象反射由此表示的类或接口的所有【公共】方法类对象包括那些由类或接口和那些从超类和超接口继承的声明Method[] getDeclaredMethods() 返回包含一个数组方法对象反射的类或接口的所有声明的方法,通过此表示类对象包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。//获取User类中所有public的方法(包括继承)Method[] methods = clz.getMethods();for(Method method : methods) {System.out.println(method);}//获取User类中所有的方法(不包括继承)methods = clz.getDeclaredMethods();for (Method method : methods) {System.out.println(method);}|--获取单个方法Method getMethod(String name, 类<?>... parameterTypes) 返回一个方法对象它反映此表示的类或接口的指定公共成员方法 类对象 String name:方法名类<?>... parameterTypes:参数的Class类型//获取public void fun1()Method method = clz.getMethod("fun1");System.out.println(method);//获取public void fun2(String name)method = clz.getMethod("fun2", String.class);System.out.println(method);//获取private void fun3(String name, int age)method = clz.getDeclaredMethod("fun3", String.class, int.class);System.out.println(method);12.使用反射调用方法Object invoke(Object obj, Object... args) 在具有指定参数的Method对象上调用此Method对象表示的底层方法。 参数 obj - 从底层方法被调用的对象 args - 用于方法调用的参数//调用方法public void fun1()Method m = clz.getMethod("fun1");m.invoke(clz.newInstance());//public void fun1()//调用方法public void fun2(String name)m = clz.getMethod("fun2", String.class);m.invoke(clz.newInstance(), "公有、有参数");//public void fun2(String name)但是,对于私有的方法,即便我们可以通过getDeclaredMethod方法获取,我们也没有权限使用。于是,需要使用java.lang.reflect.AccessibleObject中的setAccessible方法关闭安全检查//调用方法private void fun3(String name, int age)m = clz.getDeclaredMethod("fun3", String.class, int.class);m.setAccessible(true);m.invoke(clz.newInstance(), "私有", 1);//private void fun3(String name, int age)13.解答第2点留下的问题,获得某一个以Object为编译类型(不知道运行类型)的对象时,如何使用它的运行类型所特有的方法//现在有一个日期对象,其编译类型是ObjectObject o = new java.util.Date();//现在不通过强转调用Date类中的toLocaleString方法//利用反射Method m = o.getClass().getMethod("toLocaleString");System.out.println(m.invoke(o));14.使用反射调用静态方法和可变数组参数如果底层方法是静态的,则可以忽略指定的obj参数,传入null!例如://调用public static void staticMethod1()Method m = c.getMethod("staticMethod1");m.invoke(null);//输出:public static void staticMethod1()如果底层方法是可变数组参数,则分为:基本数据类型例如://调用public static void staticMethod2(int ... arr)m = c.getMethod("staticMethod2", int[].class);//m.invoke(null, 1, 2, 3);运行时错误//这里需要把1,2,3封装成数组m.invoke(null, new int[]{1, 2, 3});//输出:[1, 2, 3]引用数据类型//调用public static void staticMethod3(String ... str_arr)m = c.getMethod("staticMethod3", String[].class);//m.invoke(null, new String[]{"a", "b", "c"});//运行时错误这里,为什么会出现错误呢?查看APIdoc,invoke方法下有这么一句话:个别参数自动解包以匹配原始形式参数,原始参考参数和参考参数都需要进行方法调用转换。再看看invoke方法的声明:public Object invoke(Object obj, Object... args)哦,需要使用Obejct的一维数组将其打包m.invoke(null, new Object[]{new String[]{"a", "b", "c"}});//输出:[a, b, c]实际,对于其它非引用数据类型数组,也可以自己进行打包,如:m.invoke(null, new Object[]{new int[]{1, 2, 3}});m.invoke(null, new Object[]{1});15.使用反射操作字段获取字段getField();getFields();设置字段void setXX(Object obj, Object value);16.其它API获取修饰符getModifiers();获取类的全限定名getName();获取类的简单名称getSimpleName();17.单例设计模式在应用中某一个类(工具类0),有且只有一个实例0单例模式的写法有多种编写规则1):私有化构造器2):在类中创建一个自身对象3):向外暴露一个公共的静态方法用于返回该对象使用反射机制之后,传统的单例设计,不安全-->建议:使用枚举来做单例Spring框架创建的对象默认就是单例的public enum Util {INSTANCE;}


原创粉丝点击