一、反射概述
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;
}
@Override
public 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 block
e.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 block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.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 block
e.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 block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.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