大家肯定都使用过流行的开源框架,那么这些框架的实现原理呢,一般来说它们都是基于反射来实现的,因此,反射对于我们来说至关重要。这边文章主要讲解反射的基本原理以及使用。
本文学习自(https://blog.csdn.net/sinat_38259539/article/details/71799078),非常感谢作者
1.概述
JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
众所周知Java有个Object 类,是所有Java 类的继承根源,其内声明了数个应该在所有Java 类中被改写的方法:hashCode()、equals()、clone()、toString()、getClass()等。其中getClass()返回一个Class 对象。
Class 类十分特殊。它和一般类一样继承自Object,其实体用以表达Java程序运行时的classes和interfaces,也用来表达enum、array、primitive Java types(boolean, byte, char, short, int, long, float, double)以及关键词void。当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM 便自动产生一个Class 对象。如果您想借由“修改Java标准库源码”来观察Class 对象的实际生成时机(例如在Class的constructor内添加一个println()),这样是行不通的!因为Class并没有public constructor。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.例如:一个实体类中有String name;int age两个属性,那他所对应的字节码文件对象含String.class,int.class
小结
反射就是将类中的成分一个个映射为Java对象
例如:一个对象中含有成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)
如图是类的正常加载过程:反射的原理在与class对象。
熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。
2.Class类在api中的解释
见 https://docs.oracle.com/javase/7/docs/api/
3.反射获取成员方法、属性、构造
- 获取Class对象,三种方式:
Class clazz = Student.class
Class clazz = new Student().getClass()
Class clazz = Class.forName(“完整路径的包名”) - 通过以下方法获取
方法 | 作用 |
---|---|
clazz.getConstructors() | 获取所有**公有 **构造函数 |
clazz.getDeclaredConstructors() | 获取所有 构造函数 |
clazz.getConstructor(parameterTypes) | 获取单个公有 对应参数类型的构造函数 |
clazz.getDeclaredConstructor(parameterTypes) | 获取单个对应参数类型构造函数 |
clazz.getMethods() | 获取所有公有 成员方法 |
clazz.getDeclaredMethods() | 获取所有 成员方法 |
clazz.getMethod(name,parameterTypes) | 获取单个 对应名字对应参数类型公有 成员方法 |
clazz.getDeclaredMethod(name,parameterTypes) | 获取单个 对应名字对应参数类型成员方法 |
clazz.getFields() | 获取所有公有 成员属性 |
clazz.getDeclaredFields() | 获取所有 成员属性 |
clazz.getField(name) | 获取单个 对应名字公有 成员属性 |
clazz.getDeclaredField(name) | 获取单个 对应名字成员属性 |
案例:
1.获取构造函数
实体类
package main.pojo;
/**
* @program: reflect
* @description: 用来测试反射获取构造函数的实体类
* @author: wdl
* @create: 2018-11-12 17:34
*/
public class Student {
private String name;
private int age;
private boolean isMan;
public int hair;
public static void main(String...args){
System.out.println("main方法执行了。。。。。。");
}
public Student(String name, int age, boolean isMan) {
this.name = name;
this.age = age;
this.isMan = isMan;
}
//默认的构造方法
Student(String name) {
System.out.println("默认的构造方法 name = " + name);
this.name = name;
}
//无参公有构造方法
public Student() {
System.out.println("无参公有构造方法执行");
}
//受保护的构造方法
protected Student(int age) {
System.out.println("受保护的构造方法 age = " + age);
this.age = age;
}
//私有的构造方法
private Student(boolean isMan) {
System.out.println("私有的构造方法 isMan = " + isMan);
this.isMan = isMan;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isMan() {
return isMan;
}
public void setMan(boolean man) {
isMan = man;
}`在这里插入代码片`
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", isMan=" + isMan +
'}';
}
}
实现:
package main;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* @program: reflect
* @description: 通过反射获取构造方法
* @author: wdl
* @create: 2018-11-12 17:53
* <p>
* 1.批量获取构造函数
* Class.getConstructors 所有公有构造方法
* Class.getDeclaredConstructors 所有构造函数(私有、受保护、默认、公有)
* 2.获取单个构造函数
* Class.getConstructor(parameterTypes)入的是参数的类型,返回一个描述这个无参构造函数的类对象,公有
* Class.getDeclaredConstructor(parameterTypes)入的是参数的类型,返回一个描述这个无参构造函数的类对象,所有
*/
public class ReflectConstructors {
public static void main(String... args) {
try {
//加载Class对象
Class clx = Class.forName("main.pojo.Student");
//获取所有公有构造方法
Constructor[] publicConstructors = clx.getConstructors();
System.out.println("**********************所有公有构造方法*********************************");
for (Constructor constructor : publicConstructors) {
System.out.println(constructor);
}
//Declared 包括所有
//获取所有构造方法---包括public private protected
Constructor[] allConstructors = clx.getDeclaredConstructors();
System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
for (Constructor constructor : allConstructors) {
System.out.println(constructor);
}
//注意 : 传入的是参数的类型,返回一个描述这个无参构造函数的类对象
//Constructor constructor = clx.getConstructor(String.class,int.class,boolean.class);
//parameterTypes
Constructor constructor = clx.getConstructor(null);
System.out.println("************公有、无参构造方法***************");
//通过反射获取的无参构造方法实例化对象
Object object = constructor.newInstance();
//Object object = constructor.newInstance("wdl",5,false);
Constructor privateConstructor = clx.getDeclaredConstructor(boolean.class);
System.out.println("************私有构造方法***************");
//暴力访问 忽略掉访问修饰符
privateConstructor.setAccessible(true);
Object object1 = privateConstructor.newInstance(true);
System.out.println(object1.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
Object object = constructor.newInstance();
通过反射获取的构造方法实例化对象
2.获取成员变量
实体类
package main.pojo;
/**
* @program: reflect
* @description: 用来测试反射获取成员属性的实体类
* @author: wdl
* @create: 2018-11-12 18:28
*/
public class Book extends Student{
//私有
private String name;
//公有
public double price;
//受保护
protected int count;
String press;
public Book() {
}
public void show(int count){
System.out.println("调用有参公有方法 count = "+count);
}
protected void show1(){
System.out.println("调用无参受保护方法");
}
private int show2(int price){
System.out.println("调用有参数私有方法 price = "+price);
return price;
}
void show3(String name){
System.out.println("调用有参默认方法 name = "+name);
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", price=" + price +
", count=" + count +
", press='" + press + '\'' +
'}';
}
}
实现:
package main;
import main.pojo.Book;
import java.lang.reflect.Field;
/**
* @program: reflect
* @description: 反射获取成员属性或者赋值
* @author: wdl
* @create: 2018-11-12 18:31
* <p>
* 获取成员变量并调用
* 1.Field[] -->Class.getFields() 获取所有公有成员变量,包括父类的公有成员变量
* 2.Field[] -->Class.getDeclaredFields() 获取所有成员变量(包括私有 公有 受保护 默认)
* 3.Field -->Class.getField(String propertyName) 获取单个公有成员变量,传入参数名,包括父类的公有成员变量
* 4.Field -->Class.getDeclaredFields(String propertyName) 获取单个成员变量,传入参数名(包括私有 公有 受保护 默认)
*/
public class ReflectFiled {
public static void main(String... args) {
try {
Class clx = Class.forName("main.pojo.Book");
//获取所有公有成员变量
System.out.println("*******************获取所有公有成员变量******************");
Field[] fields = clx.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("*******************获取所有成员变量(包括公有,默认,私有,受保护)******************");
fields = clx.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("*******************获取单个公有成员变量,并设置值******************");
//获取成员属性
Field field = clx.getField("price");
System.out.println(field);
//通过构造实例化
Object object = clx.getConstructor().newInstance();
//设置值
field.set(object, 50.2);
//获取成员变量值
System.out.println(field.get(object));
Book book = (Book) object;
System.out.println(book.price);
System.out.println("*******************获取单个成员变量(私有,公有,保护,默认),并设置值******************");
//获取成员属性
field = clx.getDeclaredField("name");
//暴力反射,解除私有权限
field.setAccessible(true);
System.out.println(field);
//设置值
field.set(object, "wdl");
System.out.println(book);
} catch (Exception e) {
e.printStackTrace();
}
}
}
//暴力反射,解除私有权限 Object object = clx.getConstructor().newInstance(); field.setAccessible(true); //设置值 field.set(object, "wdl");
3.获取成员方法
实现
package main;
import java.lang.reflect.Method;
/**
* @program: reflect
* @description: 反射获取成员方法
* @author: wdl
* @create: 2018-11-12 19:10
* 与反射获取构造 属性类似
*/
public class ReflectMethod {
public static void main(String...args){
try {
Class clx = Class.forName("main.pojo.Book");
Method[] methods = clx.getMethods();
System.out.println("****************获取所有公有成员方法****************");
for (Method method : methods) {
System.out.println(method);
}
methods = clx.getDeclaredMethods();
System.out.println("****************获取所有成员方法****************");
for (Method method : methods) {
System.out.println(method);
}
//第一个为方法名,第二个为参数类型
Method method = clx.getMethod("show",int.class);
System.out.println("****************获取单个公有成员方法****************");
System.out.println(method);
//构建实例
Object object = clx.getConstructor().newInstance();
//通过invoke给此实例的这个方法设置参数
method.invoke(object,50);
//第一个为方法名,第二个为参数类型
method = clx.getDeclaredMethod("show2",int.class);
System.out.println("****************获取单个成员方法,包括私有等****************");
System.out.println(method);
method.setAccessible(true); //解除私有限定
Object o = method.invoke(object,20);
System.out.println(o);
} catch (Exception e) {
e.printStackTrace();
}
}
}
总结:
- 获取构造函数时,constructor = clazz.getDeclaredConstructor(parameterTypes);通过constructor.newInstance(对应参数)可实例化对象。
- 获取成员变量后,通过field.set(object,“value”),可设置成员变量值;通过field.get(object)获取成员变量值.
- 获取成员方法后,通过method.invoke(“methodName”,参数值)可调用成员方法
- 在通过Declared获取私有信息时,必须调用xx.setAccessible(true),暴力反射解除私有限制,否则无法生效。
- parameterTypes代表的是参数类型,比如获取的成员方法为void show(int age,String name),则获取时应该为clazz.getDeclaredMethod(“show”,
int.class,String.class
);
关键字 | 意义 |
---|---|
getFields() | 获取所有公有属性,包括父类 |
get**Declared **Fields() | 获取所有属性,不包括父类 |
Demo链接