反射

127 阅读4分钟

定义

Java反射,指的是在运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。这也是Java被称为准动态语言的关键。

在Java中,反射机制相关的类在java.lang.reflect.* 包下。

之所以可以使用反射是因为一个类再堆内存中只有唯一一个java.lang.Class对象。Class对象是用来封装类再方法区内的数据结构,类的加载的最终产品是位于堆区中的 Class对象, Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。

反射机制中重要的类

1、java.lang.class:代表整个字节码。代表一个类型,代表整个类。

2、java.lang.reflect.Method:代表类中的方法。

3、java.lang.reflect.Constructor:代表类中的构造方法。

4、java.lang.reflect.Field:代表类中的成员变量(静态变量+实例变量)

获取class的三种方式

1、Class.forName("完整类名(带包名)") -- 这种方式是静态方法

Class c = Class.forName("com.test.statical.UserServiceImp");

注:会抛出ClassNotFoundException异常

Class.forName()方法也是类加载的一种方式,类加载有3中方式:

(1)命令行启动应用时候由JVM初始化加载

(2)通过Class.forName()方法动态加载

(3)通过ClassLoader.loadClass()方法动态加载

其中:Class.forName():将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块; ClassLoader.loadClass():只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。

2、对象.getClass

Class c2 = userServiceImp.getClass();

3、任何类型.class

Class c1 = UserServiceImp.class;

通过反射实例化对象

对象.newInstance()

引申创建对象的三种方式:

(1)new 对象

(2)clone

(3)反射的方式创建对象.newInstance()

.newInstance()方法内部实际上调用了无参构造方法,必须要保证class内部的无参构造方法存在。

否则会抛出java.lang.InstantiationException异常。

通过.newInstance()方法会导致类加载,类中的静态代码块会被执行。

通过反射反编译一个类的属性(映照上面4种反射类)

1、Class类方法

方法名备注
public T newInstance()创建对象
public String getName()返回完整类名带包名
public String getSimpleName()返回类名
public Field[] getFields()返回类中public修饰的属性
public Field[] getDeclaredFields()返回类中所有的属性
public Field getDeclaredField(String name)根据属性名name获取指定的属性
public Method[] getDeclaredMethods()返回类中所有的实例方法
public Method getDeclaredMethod(String name, Class<?>… parameterTypes)根据方法名name和方法形参获取指定方法
public Constructor<?>[] getDeclaredConstructors()返回类中所有的构造方法
public Constructor getDeclaredConstructor(Class<?>… parameterTypes)根据方法形参获取指定的构造方法
public native Class<? super T> getSuperclass()返回调用类的父类
public Class<?>[] getInterfaces()返回调用类实现的接口集合

2、File类方法

方法名备注
public String getName()返回属性名
public Class<?> getType()以Class类型,返回属性类型【一般配合Class类的getSimpleName()方法使用】
public void set(Object obj, Object value)设置属性值
public Object get(Object obj)读取属性值

注意:set()方法不可以访问私有属性,若想访问私有属性则需要打破封装。

方法备注
public void setAccessible(boolean flag)默认false,设置为true为打破封装

无论是使用get()方法还是set()方法都需要结合对象.属性的方式获取file。(还是需要先获得对象才能拿到属性)

3、Method类方法

方法备注
public String getName()返回方法名
public Class<?> getReturnType()以Class类型,返回方法类型【一般配合Class类的getSimpleName()方法使用】
public Class<?>[] getParameterTypes()返回方法的修饰符列表(一个方法的参数可能会有多个。)【结果集一般配合Class类的getSimpleName()方法使用】
public Object invoke(Object obj, Object… args)调用方法

用反射的方式调用方法:

方法.invoke(对象,实参)

4、Constructor类方法

方法备注
public String getName()返回构造方法名
public Class<?>[] getParameterTypes()返回构造方法的修饰符列表(一个方法的参数可能会有多个。)【结果集一般配合Class类的getSimpleName()方法使用】
public T newInstance(Object … initargs)创建对象【参数为创建对象的数据】

用反射创建对象的两种方式:

(1)调用无参构造方法

Class vipClass = Class.forName("javase.reflectBean.Vip");
Object obj1 = vipClass.newInstance();//Class类的newInstance方法

(2)调用有参构造方法

Constructor c1 = vipClass.getDeclaredConstructor(int.class, String.class, String.class, boolean.class);
Object obj2 = c1.newInstance(321, "lsi", "1999-10-11", true);//Constructor类的newInstance方法

注意

  1. 属性最重要的是名字
  2. 实例方法最重要的是名字形参列表
  3. 构造方法最重要的是形参列表

获取一个类的父类以及实现的接口

Class superclass = vipClass.getSuperclass();
Class[] interfaces = vipClass.getInterfaces();