------- android培训、java培训、期待与您交流! ----------
理解反射的概念
Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class。
例如:众多的人用Person类来表示,而众多的Java类就用Class类来表示,Person类的实例对象就如张三、李四这样一个个具体的人,而Class类代表java类,它的各个实例对象对应的是什么呢?
对应的是各个类在内存中的字节码,例如Person类的字节码,Demo类的字节码。一个类被加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可以分别用一个个的对象来表示,这些对象显然具有相同的类型,就是Class类型。
想要获取字节码文件对象中的成员,要先获取字节码文件对象。如何得到各个字节码对应的实例对象(Class类型)呢?
有三种方式,这三种方式得到的是同一个字节码实例对象。:
1.类型名称.class,例如:System.class。 好处:不用new对象,但仍需要使用具体的类型。
2.对象.getClass,例如:new Demo.getClass()。 弊端:必须要创建指定类的对象才可以调用该方法。
3.Class.forName("类名"),例如:Class.forName("java.util.Date")。
Class.forName("类名")的作用:返回字节码,有两种方式:
1.该字节码已经被加载到JVM里面了,那么就直接返回。
2.JVM里面还没有这份字节码,则会用类加载器去加载,把加载进来的字节码缓存到JVM里面,以后就不用再加载了。
String getName():获取此Class对象的名称。
boolean isArray():判断此Class对象是否表示一个数组类。
boolean isInterface():判断此Class对象是否表示一个接口类型。
boolean isPrimitive():判断指定的Class对象是否表示一个基本类型。
九个预定义Class实例对象(八大原始类型+void)
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。
每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该Class 对象。
基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void也表示为 Class 对象。
注意:
1、int.class == Integer.TYPE。
2、数组类型的Class实例对象Class.isArray()为true。
3、反射并不是Java 5.0的新特性。
反射技术:
动态的获取指定的类以及动态的调用类中的内容。反射就是把java类中的各种成分映射成相应的java类。
例如:一个java类中用一个Class类的对象来表示,一个类中的组成部分包括:成员变量、方法、构造方法、包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机、变速箱等等也是一个个的类。表示java类的Class类显然要提供一系列的方法,来获得其中的变量、方法、构造方法、修饰符、包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等。
反射的应用场景:
当应用程序已经写好了,后期出现的接口子类无法直接在该应用程序中用new创建对象。这时,既然子类不确定,可以通过对外提供配置文件的形式,将不确定的信息存储到配置文件中即可。而程序要提前写好如何读取配置文件信息。根据配置文件中具体的类名找到该类,并进行加载和对象的创建。这些动作要在前期定义软件时写好。没有类之前就将创建对象的动作提前做完了,这就是动态的获取指定的类,并使用类中的功能,这就是反射技术。
反射的好处:大大的提高了程序的扩展性。
一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象后,得到这些实例对象后有什么用呢?怎么用呢?这正是学习和应用反射的要点。
构造方法的反射:
Constructor类代表某个类中的一个构造方法。
通过Class类中的getConstructor方法获取一个构造方法对象,获取构造方法对象时,要指定构造方法参数类型。示例:Constructor con = cls.getConstructor(StringBuffer.class)。 获得的构造方法对象调用newInstance方法时,参数中接收的实例对象要和上面的类型相同。示例:String str = (String)con.newInstance(new StringBuffer("abc"));如果是获取空参数的构造方法对象时,可以直接使用Class类中的newInstance方法得到,省去了再找Constructor的过程。
Class clazz = Class.forName("className"):通过给定的类名,加载对应的字节码文件,并封装成字节码文件对象Class。
Object obj = clazz.newInstance():通过new创建给定类的实例。 调用该类的构造函数。
在反射中经常见到的异常:
1.InstantiationException:实例初始化异常。表示没有对应的构造函数。
2.IllegalAccessException:无效的访问异常。表示有提供,但是权限不够。
如果通过指定的构造函数初始化对象:
1.先获取字节码文件对象。
2.再获取给定的构造函数。
3.通过该构造函数初始化对象。
getConstructors():得到某个类所有的构造方法。
getConstructor(Class<?>... parameterTypes):得到某一个构造方法。
newInstance():创建此Class对象所表示的类的一个新实例。
newInstance(Object... initargs):创建该构造方法的声明类的新实例。
getDeclaringClass():得到此Constructor对象表示的构造方法所属的类。表示哪个类的构造方法。
代码演示:
成员变量的反射:
在Class类中通过getField(String name)、getDeclaredField(String name) 方法获取Field对象。该Field对象是对应到类上面的成员变量,也就是字节码上的成员变量,并不是对象上的成员变量。Filed类代表某个类中的一个成员变量。再通过Field对象的get(对象)方法获取某个对象上对应该成员变量(字段)所表示的值,示例:fieY.get(pt1)。setAccessible(boolean flag):将此对象的accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。 set(Object obj, Object value):将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
代码演示:
成员方法的反射:
Method类代表某个类中的一个成员方法。
getMethod(String name, Class<?>... parameterTypes):获得类中的某一个方法,第一个参数是方法的名称,第二个参数是参数列表(参数类型和个数)。示例:Method charAt = str.getClass().getMethod("charAt",int.class)。
调用方法:通常方式:str.charAt(2)。
反射方式:invoke(Object obj, Object... args):第一个参数是对象,第二个参数也是参数列表,和getMethod的第二个参数是对应的。示例:charAt.invoke(str,2)。 如果传递给Method对象的invoke方法的第一个参数为null,说明该Method对象对应的是一个静态方法。
代码演示:
数组的反射:
具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
代表数组的Class实例对象的getSuperclass方法返回的父类为Object类对应的Class。
基本类型的一维数组可以被当做Object类型使用,不能当做Object[]类型使用,非基本类型的一维数组既可以当做Object类型使用,也可以当做Object[]类型使用。
Class类中的getSuperclass方法可以获取该字节码对象的父类的Class。通过验证发现int[]类型的父类是Object,而String[]类型的父类也是Object。Arrays.asList方法处理int[]和String[]时的差异:数组的工具类Arrays中的方法asList()可以将数组转变成List集合,如果是一个基本类型的数组,那么会将这个数组作为元素转变成List集合,如果是一个Object类型的数组,那么会将数组中的元素转变成集合中的元素存在。
java.lang.reflect包中的Array类,提供了动态创建和访问 Java 数组的方法。该类中的方法都是静态的,因此要用类名调用。get(Object array, int index):返回指定数组对象中索引组件的值。示例:int arr[x] = Array.get(arr,x);
需求:通过反射的方式打印任意对象中的元素,如果该对象是数组,就打印数组中的每个元素。如果不是数组就直接打印该对象。
代码演示:
反射在程序中的作用——我们学习反射主要是用来做框架。
框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。框架的核心问题:因为在写程序时无法知道要被调用的类名,所以在程序中无法直接new某个类的实例对象了,而要用反射方式来做。通过读取配置文件,动态的获取类并使用类中的功能,这种方式大大的提高了程序的扩展性。