Java反射的实现原理
Java反射的概念
反射指的是程序在运行时能够获取自身的信息。在java中,只要给定类的名字,那么就可以通过反射机制来获得类的所有信息。
Class类
每一个类都有一个Class对象,如果说类是对象抽象和集合的话,那么Class类就是对类的抽象和集合。
将一个类加载到Java虚拟机中需要经历三个阶段:加载->链接(验证、准备,解析)->初始化。
- 加载:这是由类加载器(ClassLoader)执行的。通过一个类的全限定名来获取其定义的二进制字节流(Class字节码),将这个字节流所代表的静态存储结构转化为方法区的运行时数据接口,根据字节码在java堆中生成一个代表这个类的java.lang.Class对象。
- 链接:
2.1.验证:验证Class文件中的字节流包含的信息是否符合当前虚拟机的要求。
2.2.准备:为静态域分配存储空间并设置类变量的初始值(默认值),
2.3.解析:将常量池中的符号引用转化为直接引用。 - 初始化:类的初始化顺序 :父类(静态变量、静态代码块)–>子类(静态变量、静态代码块)–>父类(变量、代码块)–> 父类构造器–>子类(变量、初始化块)–>子类构造器。注意:静态代码和静态变量同级,变量和代码块同级。谁在前先执行谁。类只会初始化一次。
补充:静态初始化,是在加载类的时候初始化。而非静态初始化,是new类实例对象的时候加载。
类什么时候才被初始化: 1.创建类的实例,2.访问类或接口的静态变量,调用类的静态方法 3.反射,4.初始化一个类的子类(会首先初始化子类的父类)
测试代码
public class Test {
public static void main(String[] args) {
try {
System.out.println("-------加载类-----");
System.out.println(Class.forName("Son"));
System.out.println("---new类对象实例------");
new Son();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class Parent{
private static String a=inita();
private String b=initb();
static {
System.out.println("父类静态代码块开始执行");
}
{
System.out.println("父类代码块开始执行");
}
public static String inita(){
System.out.println("父类静态变量开始赋值");
return "父类静态变量";
}
public String initb(){
System.out.println("父类成员变量开始赋值");
return "父类成员变量";
}
public Parent(){
System.out.println("父类构造方式开始执行");
}
}
class Son extends Parent{
{
System.out.println("子类代码块开始执行");
}
static {
System.out.println("子类静态代码块开始执行");
}
private static String a=initSa();
private String sb=initSb();
public static String initSa(){
System.out.println("子类静态变量开始赋值");
return "子类静态变量";
}
public String initSb(){
System.out.println("子类成员变量开始赋值");
return "子类成员变量";
}
public Son(){
System.out.println("子类构造方式开始执行");
}
}
运行结果:-------加载类-----
父类静态变量开始赋值
父类静态代码块开始执行
子类静态代码块开始执行
子类静态变量开始赋值
class Son
—new类对象实例------
父类成员变量开始赋值
父类代码块开始执行
父类构造方式开始执行
子类代码块开始执行
子类成员变量开始赋值
子类构造方式开始执行
1、获取类的Class对象
方式 | 说明 |
---|---|
Class.forName(“类的全限定名”) | 如果类没有加载,加载类,并做类的静态初始化。 |
对象.getClass() | 对象已经存在,说明类已经初始化了。多态问题:Parent son = new Son();返回的是:对象的实际类型的Class,即class Son |
类名.class | 不会初始化类。 |
包装类.TYPE | 返回的是:对应的基本数据类型的Class对象 |
注意:基本数据类型的Class对象和包装类的Class对象是不一样的
2、获取类的Fields
方式 | 说明 |
---|---|
Field getField(String name) | 返回该类和其所有父类指定的一个公有属性,不能获取私有的属性 |
Field[] getFields() | 返回该类和其所有父类的所有公有属性,不能获取私有的属性 |
Field getDeclaredField(String name) | 返回该类声明的指定一个属性(包括私有属性)。不能获取父类声明的属性 |
Field[] getDeclaredFields() | 返回该类声明的所有属性(包括私有属性)。不能获取父类声明的属性 |
测试代码:
public class Test {
public static void main(String[] args) {
Class son = new Son().getClass();
try {
System.out.println("----返回该类和其所有父类指定的一个公有属性---");
System.out.println(son.getField("a"));
System.out.println("----返回该类声明的指定一个属性---");
System.out.println(son.getDeclaredField("sb"));
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
System.out.println("----返回该类和其所有父类的所有公有属性---");
Field[] fields = son.getFields();
for (int i=0;i<fields.length;i++){
System.out.println(fields[i]);
}
System.out.println("----返回该类声明的所有属性(包括私有属性)---");
Field[] declaredFields = son.getDeclaredFields();
for (int i=0;i<declaredFields.length;i++){
System.out.println(declaredFields[i]);
}
}
}
class Parent {
public String a;
private String b;
}
class Son extends Parent {
public String sa;
private String sb;
}
运行结果:----返回该类和其所有父类指定的一个公有属性—
public java.lang.String Parent.a
----返回该类声明的指定一个属性—
private java.lang.String Son.sb
----返回该类和其所有父类的所有公有属性—
public java.lang.String Son.sa
public java.lang.String Parent.a
----返回该类声明的所有属性(包括私有属性)—
public java.lang.String Son.sa
private java.lang.String Son.sb
补充:如果子类和父类定义了一样的属性,获得的属性是子类的属性。
3、获取类的Method
方式 | 说明 |
---|---|
Method getMethod(String name,Class<?>… parameterTypes) | 返回该类和其所有父类指定的一个公有方法,不能获取私有的方法 |
Method[] getMethods() | 返回该类和其所有父类的所有公有方法,不能获取私有的方法 |
Method getDeclaredMethod(Stringname,Class<?>… parameterTypes) | 返回该类声明的指定一个方法(包括私有方法)。不能获取父类声明的方法 |
Method[] getDeclaredMethods() | 返回该类声明的所有方法(包括私有方法)。不能获取父类声明的方法 |
补充:如果子类重写了父类的方法,获取的是子类的方法。
4、获取类的Constructor
方式 | 说明 |
---|---|
Constructor getConstructor(Class<?>… parameterTypes) | 返回该类指定的一个公有构造器,不能获取私有的构造器 |
Constructor<?>[] getConstructors() | 返回该类的所有公有构造器,不能获取私有的方法 |
Constructor getDeclaredConstructor(Class<?>… parameterTypes) | 返回该类声明的指定一个构造器(包括私有构造器) |
Constructor<?>[] getDeclaredConstructors() | 返回该类声明的所有构造器(包括私有构造器) |
补充:上述的每个方法都不能获取到父类的构造器。
代码测试
public class Test {
public static void main(String[] args) {
Class son = new Son().getClass();
try {
System.out.println("----返回该类指定的一个公有构造器---");
System.out.println(son.getConstructor(String.class));
System.out.println("----返回该类声明的指定一个构造器---");
System.out.println(son.getDeclaredConstructor(int.class));
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
System.out.println("----返回该类的所有公有构造器---");
Constructor[] constructors = son.getConstructors();
for (int i=0;i<constructors.length;i++){
System.out.println(constructors[i]);
}
System.out.println("----返回该类声明的所有构造器---");
Constructor[] declaredConstructors = son.getDeclaredConstructors();
for (int i=0;i<declaredConstructors.length;i++){
System.out.println(declaredConstructors[i]);
}
}
}
class Parent {
public double a;
private boolean b;
public Parent() {
}
public Parent(double a, boolean b) {
this.a = a;
this.b = b;
}
}
class Son extends Parent {
public String sa;
private int sb;
public Son() {
}
private Son(int sb) {
this.sb = sb;
}
public Son(String sa) {
this.sa = sa;
}
public Son(String sa, int sb) {
this.sa = sa;
this.sb = sb;
}
}
运行结果:----返回该类指定的一个公有构造器—
public Son(java.lang.String)
----返回该类声明的指定一个构造器—
private Son(int)
----返回该类的所有公有构造器—
public Son(java.lang.String,int)
public Son(java.lang.String)
public Son()
----返回该类声明的所有构造器—
public Son(java.lang.String,int)
public Son(java.lang.String)
private Son(int)
public Son()
java.lang.reflect中常用类
常用类 | 说明 |
---|---|
Field类 | 提供一个类的域的信息以及访问类的域的接口。 |
Method类 | 提供一个类的方法的信息以及访问类的方法的接口。 |
Constructor类 | 提供一个类的构造函数的信息以及访问类的构造函数的接口。 |
Proxy类 | 提供动态地生成代理类和类实例的静态方法。 |
Array类 | 该类提供动态地生成和访问JAVA数组的方法。 |
AccessibleObject类 | 该类是域(field)对象、方法(method)对象、构造函数(constructor)对象的基础类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。 |
Proxy类 | 提供动态地生成代理类和类实例的静态方法。 |
注意:上述获取类的Fields,Method,Constructor的返回值是Fields类对象(访问类的域的接口),Method类对象(访问类的方法的接口),Constructor类对象(访问类的构造函数的接口)
如何使用反射
1、通过反射创建类的实例
- 调用类的Class对象的newInstance方法,该方法会调用对象的默认构造器,如果没有默认构造器,会调用失败.
- 调用默认Constructor对象的newInstance方法
- 调用带参数Constructor对象的newInstance方法
测试代码
public class Test {
public static void main(String[] args) {
try {
System.out.print("调用类的Class对象的newInstance方法,创建类实例:");
System.out.println(Class.forName("Son").newInstance());
System.out.print("调用默认Constructor对象的newInstance方法,创建类实例:");
System.out.println(Class.forName("Son").getDeclaredConstructor().newInstance());
System.out.print("调用带参数Constructor对象的newInstance方法,创建类实例:");
System.out.println(Class.forName("Son").getDeclaredConstructor(String.class,int.class).newInstance("唐三",20));
System.out.print("调用私有Constructor对象的newInstance方法,创建类实例:");
Constructor<?> son = Class.forName("Son").getDeclaredConstructor(String.class);
son.setAccessible(true);
System.out.println(son.newInstance("小舞"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Son {
public String sa;
private int sb;
public Son() {
}
private Son(String sa) {
this.sa = sa;
}
public Son(String sa, int sb) {
this.sa = sa;
this.sb = sb;
}
}
运行结果:
调用类的Class对象的newInstance方法,创建类实例:Son@1540e19d
调用默认Constructor对象的newInstance方法,创建类实例:Son@677327b6
调用带参数Constructor对象的newInstance方法,创建类实例:Son@14ae5a5
调用私有Constructor对象的newInstance方法,创建类实例:Son@7f31245a
补充:对于私有的属性,方法,构造器的访问我们需要将setAccessible()方法设置为true。取消默认 Java 语言访问控制检查的能力。
2、调用类的函数
调用Invoke方法执行函数。invoke方法的参数,第一个是调用该方法的对象,后面是方法形参。
测试代码
public class Test {
public static void main(String[] args) {
try {
Class<Son> sonClass = (Class<Son>) Class.forName("Son");
Son son = sonClass.newInstance();
//通过反射执行私有方法
Method prm = sonClass.getDeclaredMethod("privateMethod",String.class);
prm.setAccessible(true);//取消默认 Java 语言访问控制检查的能力。
prm.invoke(son,"测试方法执行了");
//通过反射执行公有方法
Method pum = sonClass.getDeclaredMethod("publicMethod",String.class);
pum.invoke(son,"测试方法执行了");
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Son {
public String sa;
private int sb;
private void privateMethod(String a){
System.out.println("私有"+a);
}
public void publicMethod(String s){
System.out.println("公有"+s);
}
}
运行结果:私有测试方法执行了
公有测试方法执行了
3、设置/获取类的属性值
- 通过set()设置属性值
- 通过get(son)获取属性值
同样需要指明是哪个对象设置/获取类的属性值。
测试代码
public class Test {
public static void main(String[] args) {
try {
Class<Son> sonClass = (Class<Son>) Class.forName("Son");
Son son = sonClass.newInstance();
//通过反射设置获取公有属性
Field sa = sonClass.getDeclaredField("sa");
sa.set(son,"宁荣荣");//设置公有属性
System.out.println(sa.get(son));//获取公有属性
//通过反射设置获取私有属性
Field sb = sonClass.getDeclaredField("sb");
sb.setAccessible(true);
sb.set(son,18);//设置私有属性
System.out.println(sb.get(son));//获取私有属性
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Son {
public String sa;
private int sb;
}
运行结果:宁荣荣 18
参考:Java反射机制详解