Java反射机制是运行状态中,对于任意一个类,都能够知道这个类的所有的属性和方法,对于任意一个对象,都能够调用它的任意方法和属性,这种动态获取信息以及动态调用对象的方法就被称为Java语言的反射机制
java程序中,所有对象都有两种类型,编译时类型和运行时类型,而很多时候对象的编译时类型和运行时类型不同
例如我们某些变量或形参的类型是Object类型,但是程序却需要调用该对象运行时类型的方法,该方法不是Object中的方法,那么如果解决呢?
在编译和运行时,多万千知道类型的具体信息,这种情况下,我们可以直接先使用instanceof运算符进行判断,再利用强制类型转换,将其转换为运行时类型的变量编译时根本无法预知该对象和类的真实信息,程序只能依靠运行时信息来发现该对象和类的真实信息,这就必须使用反射Class类:
因为加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象,(一个类只有一个Class对象)这个对象就包含了完整的类的结构信息,我们可以通过这个对象看到类的结构,这个对象就是一面镜子,通过这个镜子就能看到这个类的结构,所以我们形象的称之为反射所以最终如果想要解刨一个类,必须先获取该类的字节码对象,而解刨使用的就是Class类中的方法,所以先要获取每个类对应的Class对象。反射就是把java类中的各个组成部分映射成一个个的java对象。
Class类的实例表示正在运行的Java应用程序的类和接口,事实上,所有类型都可以表示为Class的实例对象
class:外部类,内部类interface:接口[]:数组,所具有相同元素类型和维数的数组共享同一个Class对象enum:枚举annotation:注解@interfaceprimitive type:8种基本数据类型void:是一种特殊的类型,如果要获取void表示的类型,则返回nulljava中有四种方法获取Class对象:
类名.class,适用于编译期间已知的任意类型调用任意对象的getClass()方法,可以获取该对象运行时类型的Class对象使用Class类的forName(String name)静态方法,该方法需要传入一个类的全限定名调用类加载对象的loadClass(String name)该方法需要传入一个类的全限定名 Class<?> clazz = String.class; Class<?> c2 = clazz.getClass(); Class<?> c3 = Class.forName("java.lang.String"); Class<?> c4 = ClassLoader.getSystemClassLoader().loadClass("java.lang.String");如果某个类型编译期间是已知的,优先考虑使用方式1,代码更安全,效率更高。另外基本数据类型和数组类型也只能通过该方法获得Class对象
如果某个类型编译期间是未知的,我们只能通过某种方式获取个该类型的全限定名的字符串形式,使用方式3、4,但是该方法运行期间仍然无法加载该类的话,会报ClassNotFoundExcrption。这两种方式不适用与基本数据类型和数组类型
Class<?> intC = int.class;//基本数据类型 Class<?> intsC1 = int[].class;//数组 Class<?> intsC2 = int[][].class; //维度不同的数组Class不同 System.out.println(intsC1==intsC2); Class<?> c5 = Override.class;//注解 Class<?> c6 = ElementType.class;//枚举 Class<?> c7 = Comparable.class;//接口Class类提供了大量的实例方法来获取该Class对象所对应类的详细信息
包、修饰符、类名、父类、父接口、注解、成员(属性、方法、构造器)反射相关的API主要是java.lang.Class和java.lang.reflect包的内容--------------------------------------------------了解:------------------------------------------------
JDK1.5引入泛型,为了通过反射操作这些泛型,新增加了ParameterizedType、GenericArrayType、TypeVariable、WildcardType几种类型来代表不能被归到Class中的类型但是又和原始类型齐名的类型
而在Class类、Field类、Method类等API中增加了很多关于获取泛型信息的方法,例如在Class类中就有很多,其中有一个获取泛型父类的方法: public Type getGenericSuperclass():返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的直接超类的 Type(想要通过反射获取到某个注解的信息,该注解声明时必须加@Retention(RetentionPolicy.RUNTIME)元注解,表名滞留注解信息到运行时)
通过Class对象的getFields()等方法可以获取该类所包含的Field或指定Field。而Field类除了提供了获取属性的修饰符、属性类型、属性名的方法外,我们还可以获取、设置属性值
public Object get(Object obj);返回由该Field表示的字段在指定对象obj上的值 如果是基本数据类型使用getXxx(Object obj)获取属性值 public void set(Object obj,Object value);将指定的对象参数obj由此Field对象表示的字段设置为指定的新值value 如果是基本数据类型使用setXxx(Object obj,Xxx value)设置属性值 public void setAccessible(boolean flag);启动或禁用访问安全检查的开关 true,反射对象在使用时,取消Java语言的访问检查 1、能使原本无法访问的私有成员也可以访问2、能提高反射的效率,代码需要频繁的被调用建议使用 false,反射对象在使用时,进行Java语言的访问检查(默认就是进行检查) Class<?> clazz = Class.forName("com.bdit.Student"); Object obj1 = clazz.newInstance(); System.out.println(obj1); //获取自定义类中的私有属性 Field field = clazz.getDeclaredField("name"); System.out.println(field); //不进行安全检查 field.setAccessible(true); //将对象的name属性设置值为 张三 field.set(obj1,"张三"); System.out.println(obj1);通过Class对象的getMethod()等方法获取全部Method或者指定Method后,就可以使用Method中的方法调用
public Object invoke(Object obj,Object… args);在具有指定参数args的指定对象obj上调用此对象表示的方法。 Class<?> clazz = Class.forName("com.bdit.Student"); Object obj1 = clazz.newInstance(); System.out.println(obj1); //获取Student类中的setName方法 Method method = clazz.getMethod("setName",String.class); //使用获取到的方法修改obj1中的name值 method.invoke(obj1,"李四"); System.out.println(obj1);在java.lang.reflect包下还提供了一个Array类,Array对象可以代表所有的数组。程序可以通过使用Array类来动态的创建数组,操作数组元素等。
Array类提供的方法:
public static Object newInstance(Class<?> componentType, int… dimensions):创建一个具有指定的组件类型和维度的新数组public static void setXxx(Object array,int index,xxx value):将指定array数组中[index]元素的值修改为value。此处的Xxx对应8种基本数据类型,如果该属性的类型是引用数据类型,则直接使用set(Object array,int index, Object value)方法public static xxx getXxx(Object array,int index):将指定array数组中[index]元素的值返回。此处的Xxx对应8种基本数据类型,如果该属性的类型是引用数据类型,则直接使用get(Object array,int index)方法 Object arr = Array.newInstance(String.class,5); Array.set(arr,0,"java"); Array.set(arr,1,"array"); System.out.println(Array.get(arr,0)); System.out.println(Array.get(arr,1));