一、反射概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
反射机制的功能
- 在运行时判断任意一个对象所属的类。
- 在运行时构造任意一个类的对象。
- 在运行时判断任意一个类所具有的成员变量和方法。
- 在运行时调用任意一个对象的方法。
- 生成动态代理
二、反射的原理
先说下java类的执行需要经历以下过程:
- 编译:.java文件编译后生成.class字节码文件
- 加载:类加载器负责根据一个类的全限定名来读取此类的二进制字节流到JVM内部,并存储在运行时内存区的方法区,然后将其转换为一个与目标类型对应的java.lang.Class对象实例
- 连接:细分三步
- 验证:格式(class文件规范) 语义(final类是否有子类) 操作
- 准备:静态变量赋初值和内存空间,final修饰的内存空间直接赋原值,此处不是用户指定的初值。
- 解析:符号引用转化为直接引用,分配地址
- 初始化:有父类先初始化父类,然后初始化自己;将static修饰代码执行一遍,如果是静态变量,则用用户指定值覆盖原有初值;如果是代码块,则执行一遍操作。
Java的反射就是利用上面第二步加载到jvm中的.class文件来进行操作的。.class文件中包含java类的所有信息,当你不知道某个类具体信息时,可以使用反射 获取class,然后进行各种操作。
二、反射机制的相关类
Java的反射主要涉及java.lang和java.lang.reflect包下的类。
-
Class类:代表类的实体,在运行的Java应用程序中表示类和接口。
-
Field类:代表类的成员变量(成员变量也称为类的属性)。
-
Method类:代表类的方法。
-
Constructor类:代表类的构造方法。
-
Array类:提供了动态创建数组,以及访问数组的元素的静态方法。
1、Class类
Class代表类的实体,在运行的Java应用程序中表示类和接口。在这个类中提供了很多有用的方法,这里对他们简单的分类介绍。
获得类相关的方法
方法 | 用途 |
---|---|
asSubclass(Class<U> clazz) | 把传递的类的对象转换成代表其子类的对象 |
Cast | 把对象转换成代表类或是接口的对象 |
getClassLoader() | 获得类的加载器 |
getClasses() | 返回一个数组,数组中包含该类中所有公共类和接口类的对象 |
getDeclaredClasses() | 返回一个数组,数组中包含该类中所有类和接口类的对象 |
forName(String className) | 根据类名返回类的对象 |
getName() | 获得类的完整路径名字 |
newInstance() | 创建类的实例 |
getPackage() | 获得类的包 |
getSimpleName() | 获得类的名字 |
getSuperclass() | 获得当前类继承的父类的名字 |
getInterfaces() | 获得当前类实现的类或是接口 |
获得类中构造器相关的方法
方法 | 用途 |
---|---|
getConstructor(Class...<?> parameterTypes) | 获得该类中与参数类型匹配的公有构造方法 |
getConstructors() | 获得该类的所有公有构造方法 |
getDeclaredConstructor(Class...<?> parameterTypes) | 获得该类中与参数类型匹配的构造方法 |
getDeclaredConstructors() | 获得该类所有构造方法 |
获得类中方法相关的方法
方法 | 用途 |
---|---|
getMethod(String name, Class...<?> parameterTypes) | 获得该类某个公有的方法 |
getMethods() | 获得该类所有公有的方法 |
getDeclaredMethod(String name, Class...<?> parameterTypes) | 获得该类某个方法 |
getDeclaredMethods() | 获得该类所有方法 |
获得类中属性相关的方法
方法 | 用途 |
---|---|
getField(String name) | 获得某个公有的属性对象 |
getFields() | 获得所有公有的属性对象 |
getDeclaredField(String name) | 获得某个属性对象 |
getDeclaredFields() | 获得所有属性对象 |
获得类中注解相关的方法
方法 | 用途 |
---|---|
getAnnotation(Class<A> annotationClass) | 返回该类中与参数类型匹配的公有注解对象 |
getAnnotations() | 返回该类所有的公有注解对象 |
getDeclaredAnnotation(Class<A> annotationClass) | 返回该类中与参数类型匹配的所有注解对象 |
getDeclaredAnnotations() | 返回该类所有的注解对象 |
类中其他重要的方法
isAnnotation() | 如果是注解类型则返回true |
isAnnotationPresent(Class<? extends Annotation> annotationClass) | 如果是指定类型注解类型则返回true |
isAnonymousClass() | 如果是匿名类则返回true |
isArray() | 如果是一个数组类则返回true |
isEnum() | 如果是枚举类则返回true |
isInstance(Object obj) | 如果obj是该类的实例则返回true |
isInterface() | 如果是接口类则返回true |
isLocalClass() | 如果是局部类则返回true |
isMemberClass() | 如果是内部类则返回true |
2、Field类
Field代表类的成员变量(成员变量也称为类的属性)。
方法 | 用途 |
---|---|
equals(Object obj) | 属性与obj相等则返回true |
get(Object obj) | 获得obj中对应的属性值 |
set(Object obj, Object value) | 设置obj中对应属性值 |
3、Method类
Method代表类的方法。
方法 | 用途 |
---|---|
invoke(Object obj, Object... args) | 传递object对象及参数调用该对象对应的方法 |
4、Constructor类
Constructor代表类的构造方法。
方法 | 用途 |
---|---|
newInstance(Object... initargs) | 根据传递的参数创建类的对象 |
三、反射的使用
1、先新建Books实体类
package com.example.pojo;public class Books {public String name;private String author;private int price;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAuthor() {return author;}public void setAuthor(String author) {this.author = author;}public int getPrice() {return price;}public void setPrice(int price) {this.price = price;}@Overridepublic String toString() {return "Books [name=" + name + ", author=" + author + ", price=" + price + "]";}//构造方法public Books(String name, String author, int price) {super();this.name = name;this.author = author;this.price = price;System.out.println("公有有参构造方法");}public Books() {super();System.out.println("公有无参构造方法");}protected Books(boolean n) {super();System.out.println("受保护的构造方法:"+n);}private Books(int n) {super();System.out.println("私有的构造方法:"+n);}public Books(char c) {super();System.out.println("一个参数的构造方法:"+c);}//成员方法public void show1(String s){System.out.println("调用公有的成员方法:" + s);}protected void show2(){System.out.println("调用受保护的无参的成员方法");}void show3(){System.out.println("调用了默认的无参的成员方法");}private String show4(int price){System.out.println("调用了私有的,并且有返回值的成员方法:" + price);return "show";}public static void main(String[] args) {System.out.println("反射main方法成功");}
}
2、获取Class对象的三种方式
/*** 获取Class对象的三种方式* Object ——> getClass();* 任何数据类型(包括基本数据类型)都有一个“静态”的class属性* 通过Class类的静态方法:forName(String className)(常用)*/public static void getClassObject() {//方法一:Books book = new Books("《活着》","余华",56);//这一new 产生一个Books对象,一个Class对象。Class<? extends Books> bookClass = book.getClass();//获取Class对象System.out.println(bookClass.getName());//方法二:Class<? extends Books> bookClass2 = Books.class;System.out.println(bookClass == bookClass2);//判断第一种方式获取的Class对象和第二种方式获取的是否是同一个//方法三:try {Class<?> bookClass3 = Class.forName("com.example.pojo.Books");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名System.out.println(bookClass3 == bookClass2);//判断三种方式是否获取的是同一个Class对象} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
注意:在运行期间,一个类,只有一个Class对象产生。
三种方式常用第三种,第一种对象都有了还要反射干什么。第二种需要导入类的包,依赖太强,不导包就抛编译错误。一般都第三种,一个字符串可以传入也可写在配置文件中等多种方法。
控制台打印:
3、获取构造方法并使用
/*** 获取构造方法并使用* 1.获取构造方法:* 1).批量的方法:* public Constructor[] getConstructors():所有"公有的"构造方法public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)* 2).获取单个的方法,并调用:* public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:* public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;* * 3).调用构造方法:* Constructor-->newInstance(Object... initargs)* newInstance是 Constructor类的方法(管理构造函数的类)api的解释为:newInstance(Object... initargs)使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。它的返回值是T类型,所以newInstance是创建了一个构造方法的声明类的新实例对象。并为之调用* * @throws ClassNotFoundException* @throws SecurityException * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalArgumentException * @throws IllegalAccessException * @throws InstantiationException */public static void getClassConstructors() throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {//1、加载Class对象Class<?> bookClass = Class.forName("com.example.pojo.Books");//2、获取所有公有构造方法System.out.println("-----------------------获取所有公有构造方法--------------------------");Constructor<?>[] constructors = bookClass.getConstructors();for(Constructor<?> c :constructors) {System.out.println(c);}//3、所有的构造方法(包括:私有、受保护、默认、公有)System.out.println("---------------所有的构造方法(包括:私有、受保护、默认、公有)-------------------");Constructor<?>[] declaredConstructors = bookClass.getDeclaredConstructors();for(Constructor<?> d:declaredConstructors) {System.out.println(d);}//4、获取公有、无参的构造方法System.out.println("---------------获取公有、无参的构造方法--------------------");try {Constructor con = bookClass.getConstructor(null);//1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型//2>、返回的是描述这个无参构造函数的类对象System.out.println(con);//调用实例化con.newInstance();} catch (NoSuchMethodException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (SecurityException e) {// TODO Auto-generated catch blocke.printStackTrace();}//5、获取私有构造方法,并调用System.out.println("------------------获取私有构造方法,并调用---------------------");Constructor cons = bookClass.getDeclaredConstructor(int.class);System.out.println(cons);}
控制台打印:
4、获取成员变量并调用
/*** 获取成员变量并调用* 1.批量的* 1).Field[] getFields():获取所有的"公有字段"* 2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;* 2.获取单个的:* 1).public Field getField(String fieldName):获取某个"公有的"字段;* 2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)* * 3.设置字段的值:* Field --> public void set(Object obj,Object value):* 参数说明:* 1.obj:要设置的字段所在的对象;* 2.value:要为字段设置的值;* @throws ClassNotFoundException * @throws SecurityException * @throws NoSuchFieldException * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalArgumentException * @throws IllegalAccessException * @throws InstantiationException */public static void getClassField() throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException {//1、加载Class对象Class<?> bookClass = Class.forName("com.example.pojo.Books");//2、获取所有公有的字段System.out.println("-----------------获取所有公有的字段---------------------");Field[] fields = bookClass.getFields();for(Field f:fields) {System.out.println(f);}//3、获取所有的字段(包括私有、受保护、默认的)System.out.println("------------------获取所有的字段(包括私有、受保护、默认的)-----------------");Field[] declaredFields = bookClass.getDeclaredFields();for(Field f:declaredFields) {System.out.println(f);}//4、获取公有字段**并调用System.out.println("-------------------获取公有字段**并调用-------------------------");Field field = bookClass.getField("name");System.out.println(field);//获取一个对象Object instance = bookClass.getConstructor().newInstance();//为字段设置值field.set(instance,"悲惨世界");Books book = (Books) instance;System.out.println("验证:"+book.name);//5、获取私有字段****并调用System.out.println("------------------获取私有字段****并调用---------------------");Field declaredField = bookClass.getDeclaredField("author");System.out.println(declaredField);declaredField.setAccessible(true);//暴力反射,解除私有限定declaredField.set(instance, "维克多·雨果");System.out.println("验证:"+book.getAuthor());}
控制台打印:
5、获取成员方法并调用
/*** 获取成员方法并调用* 1.批量的:* public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)* public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)* 2.获取单个的:* public Method getMethod(String name,Class<?>... parameterTypes):* 参数:* name : 方法名;* Class ... : 形参的Class类型对象* public Method getDeclaredMethod(String name,Class<?>... parameterTypes)* * 3.调用方法:* Method --> public Object invoke(Object obj,Object... args):* 参数说明:* obj : 要调用方法的对象;* args:调用方式时所传递的实参;* @throws ClassNotFoundException * @throws SecurityException * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalArgumentException * @throws IllegalAccessException * @throws InstantiationException */public static void getClassMethod() throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {//1、加载Class对象Class<?> bookClass = Class.forName("com.example.pojo.Books");//2、获取所有公有方法System.out.println("------------------获取所有公有方法-------------------------");Method[] methods = bookClass.getMethods();for(Method m :methods) {System.out.println(m);}//3、获取所有的方法,包括私有的System.out.println("-------------------获取所有的方法,包括私有的------------------");Method[] declaredMethods = bookClass.getDeclaredMethods();for(Method m :methods) {System.out.println(m);}//4、获取公有的show1()方法Method method = bookClass.getMethod("show1", String.class);System.out.println(method);//实例化一个对象Object instance = bookClass.getConstructor().newInstance();method.invoke(instance, "战争与和平");//5、获取私有的show4()方法System.out.println("----------------获取私有的show4()方法-----------------");Method method1 = bookClass.getDeclaredMethod("show4", int.class);System.out.println(method1);method1.setAccessible(true);//解除私有限定Object invoke = method1.invoke(instance, 65);System.out.println("返回值:"+invoke);}
控制台打印:
6、反射main方法
/*** 反射main方法* @throws ClassNotFoundException* @throws NoSuchMethodException* @throws SecurityException*/public static void getClassMain() throws ClassNotFoundException, NoSuchMethodException, SecurityException {//1、加载Class对象Class<?> bookClass = Class.forName("com.example.pojo.Books");//2、获取main方法Method methodMain = bookClass.getMethod("main", String[].class);//第一个参数:方法名称,第二个参数:方法形参的类型//3、调用main方法try {//methodMain.invoke(null, new String[]{"a","b","c"});//第一个参数,对象类型,因为方法是static静态的,所以为null可以,第二个参数是String数组,这里要注意在jdk1.4时是数组,jdk1.5之后是可变参数//这里拆的时候将 new String[]{"a","b","c"} 拆成3个对象。。。所以需要将它强转。methodMain.invoke(null, (Object)new String[]{"a","b","c"});//方式一//methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});//方式二} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
控制台打印:
7、通过反射运行配置文件内容
/*** 通过反射运行配置文件内容* @throws ClassNotFoundException * @throws SecurityException * @throws NoSuchMethodException * @throws InstantiationException * @throws InvocationTargetException * @throws IllegalArgumentException * @throws IllegalAccessException */public static void getPropertiesValue() throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {String className = getValue("className");String methodName = getValue("methodName");//1、加载Class对象Class<?> bookClass = Class.forName(className);//2、获取main方法Method method = bookClass.getMethod(methodName, String.class);//第一个参数:方法名称,第二个参数:方法形参的类型//3、实例化一个对象Object instance = bookClass.getConstructor().newInstance();//4、调用show1()方法method.invoke(instance, "平凡世界");}public static String getValue(String key) {Properties properties = new Properties();try {InputStream in = Test.class.getClassLoader().getResourceAsStream("config.properties");properties.load(in);} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}return properties.getProperty(key);}
config.properties文件:
className = com.example.pojo.Books
methodName = show1
控制台打印:
8、通过反射越过泛型检查
/*** 通过反射越过泛型检查(泛型用在编译期,编译过后泛型擦除(消失掉)。所以是可以通过反射越过泛型检查的)* * 例如:有一个String泛型的集合,怎样能向这个集合中添加一个Integer类型的值?* @throws SecurityException * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalArgumentException * @throws IllegalAccessException */public static void jeapGeneric() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {List<String> list = new ArrayList<>();list.add("aaa");list.add("bbb");//获取ArrayList的Class对象,反向的调用add()方法,添加数据Class<? extends List> listClass = list.getClass();//获取add()方法Method method = listClass.getMethod("add", Object.class);method.invoke(list, 100);//遍历集合for(Object obj : list){System.out.println(obj);}}
控制台打印:
参考链接:
https://blog.csdn.net/hyly_zhard/article/details/89152624
https://blog.csdn.net/sinat_38259539/article/details/71799078
https://www.jianshu.com/p/9be58ee20dee