小言_互联网的博客

一文让你懂——Java反射机制

256人阅读  评论(0)

来源:https://www.toutiao.com/i6737996758422389251/

 

概述

JAVA反射机制:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

Java

反射(Reflection)被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

反射研究与应用

Java语言类的理解:

  1. 类用于描述现实生活中的一类事物,类中有属性方法;

  2. 若需要到某一个事物,通过new关键字创建对象。然后操作其属性、调用其方法;

  3. 在编译时能够确定创建一个什么类对象,调用什么属性和方法。

Java运行分为两种状态:

  1. 编译时:通过 javac 命令,生成一个或多个.class字节码文件,(每个.class字节码文件对应一个类);

  2. 运行时:通过 java 命令,将一个或多个.class字节码文件加载到内存中。(由JVM提供的类加载器完成)。

反射机制提供的功能:

  1. 在运行时判断任意一个对象所属的类;

  2. 在运行时构造任意一个类的对象;

  3. 在运行时判断任意一个类所具有的成员变量和方法;

  4. 在运行时调用任意一个对象的成员变量和方法;

  5. 生成动态代理。

Java反射相关的主要API:

java.lang.Class:代表一个类

java.lang.reflect.Method:代表类的方法

java.lang.reflect.Field:代表类的成员变量

java.lang.reflect.Constructor:代表类的构造方法

Class类:Class是开启反射的源头;

  1. 在Object类中定义了以下的方法,此方法将被所有子类继承:

public final Class getClass();

2. 以上的方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程 序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。

示意图

Class常用方法:

  1. Class本身也是一个类;

  2. Class 对象只能由系统建立对象;

  3. 一个类在 JVM 中只会有一个Class实例;

  4. 一个Class对象对应的是一个加载到JVM中的一个.class文件;

  5. 每个类的实例都会记得自己是由哪个 Class 实例所生成;

  6. 通过Class可以完整地得到一个类中的完整结构;

class类常用方法

// 测试Demo

String str = "test4.Person";

Class clazz = Class.forName(str);

Object obj = clazz.newInstance();

Field field = clazz.getField("name");

field.set(obj, "Peter");

Object obj2 = field.get(obj);

System.out.println(obj2);

注:test4.Person是test4包下的Person类

实例化Class类对象(四种方法):

  • 若已知具体的类,通过类的class属性获取,该方法 最为安全可靠,程序性能最高;

Class clazz = String.class;

  • 已知某个类的实例,调用该实例的getClass()方法获取Class对象;

Person person = new Person();

Class clazz = person.getClass();

  • 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛ClassNotFoundException;

String className = “java.lang.String”;

Class clazz = Class.forName(className);

  • 通过类加载器来获取;

ClassLoader cl = this.getClass().getClassLoader();

Class clazz = cl.loadClass(“类的全类名”);

类加载过程:

示意图1

 

ClassLoader(类加载器):

classloader

classloader 代码

通过类加载器加载对应的属性文件:【重要】

类加载器加载属性文件

使用反射获取类结构

创建类对象:调用Class对象的new instance() 方法;

要求:

  1. 类必须有一个无参数的构造器;

  2. 类的构造器的访问权限需要足够。

注意:无构造器也可以创建对象,其步骤如下

  1. 通过Class类的getDeclaredConstructor(Class … parameterTypes)取得本类的指定形参类型的构造器;

  2. 向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数;

  3. 在Constructor中存在一个方法 public T newInstance(Oject...initargs)。

public class createClassObject{
 // 1.根据全类名获取对应的Class对象
 String name = “com.assuse.Person";
 Class clazz = Class.forName(name);
 // 2.调用指定参数结构的构造器,生成Constructor的实例
 Constructor con = clazz.getConstructor(String.class,Integer.class);
 // 3.通过Constructor的实例创建对应类的对象,并初始化类属性
 Person p2 = (Person)con.newInstance("Peter",20);
 System.out.println(p2);
}

通过反射调用类的完整结构:

  • 涉及到的相关类及接口:

Field

Method

Constructor

Superclass

Interface

Annotation

  • 实现的全部接口:

// 确定此对象所表示的类或接口实现的接口。

public Class<?>[] getInterfaces()

  • 所继承的父类:

// 返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的 Class。

public Class<? Super T> getSuperclass()

  • 全部的构造器:

// 返回此 Class 对象所表示的类的所有public构造方法。

public Constructor<T>[] getConstructors()

// 返回此 Class 对象表示的类声明的所有构造方法。

public Constructor<T>[] getDeclaredConstructors()

Constructor类中:

取得修饰符: public int getModifiers();

取得方法名称: public String getName();

取得参数的类型:public Class<?>[] getParameterTypes();

  • 全部方法:

// 返回此Class对象所表示的类或接口的全部方法

public Method[] getDeclaredMethods()

// 返回此Class对象所表示的类或接口的public的方法

public Method[] getMethods()

Method类中:

public Class<?> getReturnType()取得全部的返回值

public Class<?>[] getParameterTypes()取得全部的参数

public int getModifiers()取得修饰符

public Class<?>[] getExceptionTypes()取得异常信息

  • 全部的Field

// 返回此Class对象所表示的类或接口的public的Field。

public Field[] getFields()

// 返回此Class对象所表示的类或接口的全部Field。

public Field[] getDeclaredFields()

Field方法中:

public int getModifiers() 以整数形式返回此Field的修饰符

public Class<?> getType() 得到Field的属性类型

public String getName() 返回Field的名称。

  • Annotation相关:

get Annotation(Class<T> annotationClass)

getDeclaredAnnotations()

  • 泛型相关:

获取父类泛型类型:Type getGenericSuperclass()

泛型类型:ParameterizedType

获取实际的泛型类型参数数组:getActualTypeArguments()

  • 类所在的包

Package getPackage();

Demo:

demo1

demo2

使用反射生成并操作对象

通过反射调用类中的指定方法、指定属性:

  • 调用指定方法:

通过反射调用类中的方法,通过Method类完成,步骤:

  1. 通过Class类的getMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。

  2. 之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。

示意图

Object invoke(Object obj, Object … args);

说明:

Object 对应原方法的返回值,若原方法无返回值,此时返回null;

若原方法若为静态方法,此时形参Object obj可为null;

若原方法形参列表为空,则Object[] args为null;

若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的法。

  • 调用指定属性

  1. 在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和get()方法就可以完成设置和取得属性内容的操作。

// 返回此Class对象表示的类或接口的指定的public的Field。

public Field getFields(String name) ;

// 返回此Class对象表示的类或接口的指定的Field。

public Field getDeclaredField(String name);

2. 在Field中:

// 取得指定对象obj上此Field的属性内容

public Object get(Object obj);

// 设置指定对象obj上此Field的属性内容

public void set(Object obj,Object value);

说明:

在类中属性都设置为private的前提下,在使用set()和get()方法时,首先要使用Field类中的setAccessible(true)方法将需要操作的属性设置为可以被外部访问。

public void setAccessible(true)访问私有属性时,让这个属性可见。

 


转载:https://blog.csdn.net/iyoly/article/details/101296338
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场