Java-反射机制

295 阅读8分钟

1、什么是Java反射

Java反射机制可以在Java程序但运行期动态地获取对象的信息和调用对象的方法。反射机制是Java的特有功能,是后面Spring框架的实现基础。
Java反射机制可以让我们在编译期(CompileTime)之外的运行期(RunTime)获得任何一个类的字节码,包括接口、变量、方法等信息。还可以让我们在运行期实例化对象,通过调用get/set方法获取变量的值。
使用java反射机制可以在运行时期检查java类的信息,检查java类信息往往实在使用java反射机制时做的第一件事,通过获取类的信息可以获取类的Class对象、类名、修饰符、包信息、父类、实现的接口、构造器、方法、变量、泛型等信息。

反射就是得到类的元数据的过程。在Java中,一切皆为对象,类也是对象;即每个类被加载进内存后就会变成Class对象。使用反射获取类的信息就是在运行时起得到类的Class对象(即类的元数据),JVM中使用Class对象来描述类。在运行时期可以动态地获取类的信息和类的所有成员信息(包括私有成员)。并把每个成员信息描述为一个新的类:

  • Class类型对象:描述所有类
  • Constructor类型对象:描述所有构造器
  • Method类型对象爱过你:表示所有的方法
  • Filedz类型对象:表示所有的字段

反射也可以访问私有构造器和方法,所以反射也可以调用私有构造器创建对象,但访问私有成员时必须先设置权限。通过setAccessable(true)方法设置。

但反射会损耗性能


2、Java反射API

反射中常用以下四个类:

说明
Class封装正在运行的Java程序中的类的实例
Filed提供有关类的属性信息,以及对它的动态访问的属性,它是一个封装类的属性的类
Constructor提供有关类的构造方法的信息,以及对它动态访问的权限。它是一个封装反射类的构造方法的类
Method提供关于类的方法的信心,包括抽象方法。他是用来封装反射类方法的一个类

2.1、Class类

Class类的实例表示正在运行的Java应用程序中的类和接口(在JVM中的一份份字节码)枚举是一种特殊的类,注解是一种特殊的接口。
当程序第一次使用某一个类时就会把这个类的字节码对象加载进JVM,并创建一个Class对象,此时的Class对象表示该类的字节码。Class类可以表示N个类的字节码对象。
创建Class类的对象的方式有三种:

以上clz1=clz2=clz3,同一个类在JVM中只有一个Class对象。在框架中大量使用第三种。

九大内置Class实例(预定义的Class实例):
在基本数据类型不能表示为对象,没有类的概念。但所有的基本数据类型都有class属性即
class clz=数据类型.class
九大内置Class实例即JVM中预先提供好的Class实例,即八大基本数据类型+void,合计九个。
基本数据类型不可以用作泛型,非要加泛型的话可以用包装类型;而基本数据类型的数组可以用作泛型。
数组的Class实例:数组是引用数据类型,数组其实是对象。
注意: 所有具有相同维数和相同元素类型的数组共享同一份字节码对象,和元素没有关系。

2.2、Constructor类&&获取类中的构造器

通过Class类来获取类中的构造器
Constructor类:表示类中构造器的类型;Constructor类的实例就是反射类中的某一构造器。

获取多个构造器:

public Constructor<?>[]getConstructor();//该方法只能获取当前Class所表示类的public修饰的构造器
public Constructor<?>[]getDeclaredConstructor();//获取当前Class的所有构造器与访问权限无关 

获取单个构造器:
获取当前Class所表示类中指定的一个public构造器

	public Constructor<?>getConstructor(Class<?>…parameterTypes);
	//parameterTypes表示构造器参数的Class类型

   获取当前Class所表示类中指定的一个构造器

	public Constructor<?>getDeclaredConstructor(Class<?>…parameterTypes);

示例代码:
获取指定的一个构造器:

        //1:获取构造器所在类的字节码对象
        Class<User> clz=User.class;
        //2:通过反射类的字节码对象获取所有的构造器
        //需求1:获取public User()
        Constructor<User> con=clz.getConstructor();
        //需求2:获取public User(String name)
        con=clz.getConstructor(String.class);
        //需求3:获取private User(String name,int age)
        con=getDeclaredConstructor(String.class,int.class);

  获取所有的构造器:

        //1.获取构造器所在的类(反射类)的字节码对象
        Class<User> clz=User.class;
        //2.获取反射类字节码对象clz的所有构造器
        //获取所有public的构造器
        Constructor<?>[] cs=clz.getConstructors();
        //获取所有private的构造器
        cs=clz.getDeclaredConstructors();

使用反射调用构造器创建对象
构造器最大的作用是创建对象

使用反射创建对象:

  • a、找到构造器所在类的字节码对象
  • b、获取构造器对象
  • c、使用反射创建对象

Constructor类:表示类中构造器的类型,Constructor就是某一个类中的某一个构造器。
常用方法:

public T newInstance(Object… initargs) 如果调用带参数的构造器只能调用该方式
参数:initargs表示调用构造器的实际参数
返回创建的实例,T表示Class类所表示类的实例
如果一个类中的构造器是外界可以直接访问,同时没有参数,那么可以直接使用Class类中的newInstance方法创建对象。
public Object newInstance()相当于New 类名(); Class类中的方法。 反射也可以访问私有成员,所以反射也能够调用私有的构造器创建对象。
访问私有成员时必须先设置可访问权限。通过setAccessible(true);方法设置。
示例代码:

        Class<Person> clz=Person.class;
        //调用public Person()
        //获取构造器
        Constructor<Person> con=clz.getConstructor();
        //调用构造器的newInstance()方法来创建对象
        Person person1=con.newInstance();//无参构造器
        con=clz.getConstructor(String.class);
        Person person2=con.newInstance("Hello");//有参数的构造器
        con=clz.getDeclaredConstructor(String.class,int.class);
        con.setAccessible(true);//设置权限以调用私有构造器
        Person person3=con.newInstance("hello",11);//私有构造器

2.3、Method类&&获取类中的方法

使用反射获取类中的方法:

1、获取方法所在类的字节码对象
2、获取方法

Class类中常用方法
public Method[] getMethods(); 获取包括自身和继承过来的所有public方法
public Method[] getDeclaredMethods(); 获取自身类中所有的方法(不包括继承的、且与访问权限无关)

public Method getMethod(StringmethodName,Class<?>…parameterTypes); 表示调用一个指定的公共方法为(包括继承的)methodName表示的调用方法的名字, parameterTypes表示被调用方法的参数的Class类型,如String.class。
public Method getDeclaredMethod(String methodName,Class<?>… parameterTypes);表示调用指定的一个本类中的方法(不包括继承的)。

使用反射调用方法:
在Method类中有方法:

public Object invoke(Object obj,Object… args);
表示调用当前Method所表示的方法 . 参数:
obj:表示被调用方法基层所属对象,即方法所在类的对象;
args:表示所传递的实际参数
返回:底层方法所返回的结果
调用私有方法:可以调用setAccessable()方法设为可访问。

  使用反射调用静态方法与数组参数:

静态方法不属于任何对象,静态方法属于类本身;此时把invoke方法的第一个参数设为null即可。
使用反射调用数组参数(可变参数):
&emsp:调用方法的时候把实际参数统统作为object数组的元素即可: Method对象.invoke(方法底层所属对象,new Object[]{所有实参});

2.4、Filed类&&获取类中的字段

java.lang.reflect.Filed类,提供了有关类或者接口属性,以及对它的动态访问权限。反射的可能是一个类属性或者实例属性,封装了反射类的属性,提供了当前成员变量的类型和重新设置值的方法。

2.5、反射的其他API

Class类中:

方法说明
int getModifiers();获得修饰符
String getName();返回类的全限定名
Package getPackage();获得该类的包
String getSimpleName();获得该类的简单名
Class getSuperclass();获得该类的父类
boolean isArray();判断该class是否是数组
boolean isEnum();判断该实例是否是数组

2.6、补充(注)

当有了反射后,发现不用new也能创建对象,java有5中创建爱对象的方法:

  • 1、使用new关键字。
  • 2、使用Class.newInstance() 用Class对象创建实例(在java9中已过时)。
  • 3、使用Class.getConstructor().newInstance() 用Clss对象获取构造方法对象创建对象。
  • 4、使用Clone()方法克隆对象时也会创建新的对象。
  • 5、使用反序列化。