Java 反射(详细使用)

一、概述

  1. Class对象的建立

  • 类的加载:类 加 载 指 的 是 将 类 的 class 文 件 读 入 内 存 , 并 为 之 创 建 一 个 java.lang.Class对 象,也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象

  • 类加载整个过程,其他的类都是Class类的实例对象

2. 类加载器(ClassLoader)

(1)了解加载器
1. 根类加载器 :用C++编写的,是JVM自带的类 加载器,负责Java平台核心库,用来装载核心类 库。 该加载器无法直接获取。在Sun的JVM中,当执行java.exe命令 时,使用Xbootclasspath或-D选项指定sun.boot.class.path系统属性 值可以指定加载附加的类。 2. 扩展类加载器:负责jre/lib/ext目录下的jar包或 – D java.ext.dirs 指定目录下的jar包装入工作库

3. 系统类加载器:负责java –classpath 或 –D java.class.path所指的目录下的类与jar包装入工 作 ,是最常用的加载器

4. 用户类加载器:开发者也可以实现自己的 类加载器,自定义的类加载器通过继承ClassLoader来实现。

下面是在JVM中这4种加载器的层次结构

(2) 获取加载器
  • 普通类加载器

    public void getClassLoader(){
        // 1. 对于自定义类,使用系统类加载器进行加载
        ClassLoader classLoader = MyClass.class.getClassLoader();
        
        // 2.调用系统类加载器的getParent():获取扩展类加载器
        // 调用扩展类加载器的getParent():无法获取根类加载器
        ClassLoader extendClassLoader = classLoader.getParent();
    }
  • ClassLoader类的加载器

    @Test
    public void getClassloader() throws IOException {
        // 1. 获取一个系统类加载器
        ClassLoader systemClassloader = ClassLoader.getSystemClassLoader();
        System.out.println("系统类加载器=>"+systemClassloader);
        /*
        获取系统类加载路径-->通常有CLASSPATH环境变量指定
        如果系统没有指定CLASSPATH环境变量,则默认当前路径作为系统类加载类的加载路径
         */
        Enumeration<URL> eml = systemClassloader.getResources("");
        URL url = null;
        while (eml.hasMoreElements()){
            url = eml.nextElement();
        }
        System.out.println("系统类加载路径=>"+url);

        // 2. 获取系统类加载器的父类加载器,即扩展类加载器
        ClassLoader extensionClassloader = systemClassloader.getParent();
        System.out.println("扩展类加载器=>"+extensionClassloader);

        String extensionClassloaderPath =  System.getProperty("java.ext.dirs");
        System.out.println("扩展类加载路径=>"+extensionClassloaderPath);

        // 3. 获取扩展类加载器的父类加载器,即根类加载器
        ClassLoader rootClassloader = extensionClassloader.getParent();
        System.out.println("根类加载器=>"+rootClassloader);
    }
  • 总结:

  • 根类加载器并没有继承ClassLoader抽象类,所以扩展类加载器的 getParent()方法返回null。

  • 从运行结果可以看出,系统类加载器是AppClassLoader的实例扩 展类加载器PlatformClassLoader的实例。实际上,这两个类都是 URLClassLoader类的实例。

二、通过反射获取Class对象信息

下面是编写了一个测试代码,在后面的说明中 clazz是Class对象实例,obj 是Class对象实例对应类的对象实例,MyClass是一个创建的普通类的类名,用于说明

package com.bean;

public class Father<T> {
    public String fatherPublicField;
    private String fatherPrivateField;
    protected String fatherProtectedField;
    
    public void FatherPublicFun(){}
    private void FatherPrivateFun(){}
    protected void FatherProtectedFun(){}
}
public interface mother1 {
}
public interface mother2 {
}

public class MyClass extends Father<String> implements mother1,mother2{
    private int id;
    private String title;
    public String publicField;
    String defaultField;

    public MyClass() {}
    public MyClass(int id, String title) {this.id = id;this.title = title;}
    public int getId() {return id;}
    public void setId(int id) {this.id = id;}
    public String getTitle() {return title;}
    public void setTitle(String title) {this.title = title;}
    private void PrivateFun(){}
    protected void ProtectedFun(){}
}
  1. 获取Class类对象实例

  • 方式一: 使用Class类的forName(String clazzName)静态方法。必须添加完整包名

Class clazz = Class.forName("com.bean.MyClass");
  • 方式二: 调用某个类的class属性来获取该类对应的Class对象

  • 该方法更加推荐,无方法调用,性能更好

  • 程序在编译阶段就可以检查需要访问的Class对象 是否存在,代码更安全 。

// 类名.class
Class clazz = MyClass.class;
  • 方式三: 调用某个对象的getClass()方法

MyClass m = new MyClass();
Class clazz = m.getClass();
  • 方式四: 使用类的加载器:ClassLoader

ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class clazz =  classLoader.loadClass("com.bean.MyClass");

  1. 创建运行时类对象(obj)

说明:通过反射创建运行时类=>首先获得该类的Class实例对象,然后通过Class实例对象创建该类

运行时类就是Class对象对应类的实例

  • 方式一: 直接调用Class对象的newInstance()

  • 类必须有一个无参数的构造器。类的构造器的访问权限需要足够。

  • 注意:这个在java5之后会报过时,可用方式二

// 获得运行时类MyClass的Class实例对象
Class<MyClass> clazz = MyClass.class; 

MyClass obj = clazz.newInstance();
  • 方式二:通过clazz.getConstructor().newInstance()

  • 无参构造,运行时类必须有无参构造器

Class<MyClass> clazz = MyClass.class;

MyClass m = (MyClass)clazz.getConstructor().newInstance();
  • 有参构造

Class<MyClass> clazz = MyClass.class;

// 调用指定参数结构的构造器,生成Constructor的实例
Constructor cons = clazz.getConstructor(形参类1.class,形参类2.class);

// 通过Constructor的实例创建对应类的对象,并初始化类属性
T t = (T)cons.newInstance(形参数据1,形参数据2);

// 下面用MyClass具体举例
Constructor cons = clazz.getConstructor(Integer.class,String.class);
MyClass m = (MyClass)cons.newInstance(1,"test");
  1. 获取Class对象的信息

(1)获取类实现的全部接口
  • Class[] Class[] getInterfaces()确定此对象所表示的类或接口实现的接口。

Class<MyClass> clazz = MyClass.class;
Class[] classes = clazz.getInterfaces();
(2)获取类继承的超类
  • Class<T> getSuperclass(): 获得超类

Class<MyClass> clazz = MyClass.class;
Class superclass = clazz.getSuperclass();
(3)获取泛型父类的泛型类型
  • Type getGenericSuperclass()

Class<MyClass> clazz = MyClass.class;
Type type = clazz.getGenericSuperclass();
// type.getName() 返回的是 类的全类名<泛型的全类名>  eg:com.bean.Father<java.lang.String>
(4)获取其他相关信息
Class<MyClass> clazz = MyClass.class;


// 1. 返回此Class对象是否表示一个注解类型(由@interface定义)
boolean result =  clazz.isAnnotation();

// 2. 返回此Class对象是否是一个匿名类
boolean result =  clazz.isAnonymousClass();

// 3. 返回此Class对象是否表示一个接口(使用interface定义)
boolean result =  clazz.isInterface();

// 4. 返回此Class对象是否表示一个数组类
boolean result =  clazz.isArray();

// 5. 返回此Class对象是否表示一个枚举 (由enum关键字定义)
boolean result =  clazz.isEnum();

// 6. 判断obj是否是此Class对象的实例
boolean result =  clazz.isInstance(obj);

// 7. 获取此类的包。
Package aPackage = clazz.getPackage();

// 8. 返回此Class对象所表示的类的名称。完整版
String className = clazz.getName(); //返回示例:java.lang.String

// 9. 返回此Class对象所表示的类的简称
String classSimpleName = clazz.getSimpleName(); // 返回示例:String

// 10. 返回该Class对象对应类里包含的全部内部类
Class[] declaredClasses = clazz. getDeclaredClasses();

// 11. 返回该Class对象对应类所在的外部类。
Class declaringClass = clazz.getDeclaringClass();

  1. 获取构造器结构(Constructor)

(1)获得构造器
  • 获得单个构造器(Constructor)

  • Constructor<T> getConstructor(Class<?>...parameterTypes): 获得对应参数列表的public构造器

Class<MyClass> clazz = MyClass.class;
Constructor constructor = clazz.getConstructor(形参类1.class,形参类2.class...);
  • Constructor<T> getDeclaredConstructor(Class<?>...parameterTypes): 获得对应参数列表任意权限构造器

Class<MyClass> clazz = MyClass.class;
Constructor declaredConstructor = clazz.getDeclaredConstructor(形参类1.class,形参类2.class...);
  • 获得所有构造器(Constructors)

  • Constructor[] getConstructors():返回此 Class 对象所表示的类的所有public构造器

Constructor[] constructors = clazz.getConstructors();
  • Constructor[] getDeclaredConstructors(): 返回此 Class 对象表示的类声明的所有构造器没有权限的限制

Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
(2)获得构造器属性
// 1. 取得修饰符: public int getModifiers(); 
int modifier = constructor.getModifiers();
String modifierName = Modifier.toString(modifier); // public private defult

// 2. 取得方法名称: public String getName();
String constructorName = constructor.getName();

// 3. 取得参数的类型:public Class<?>[] getParameterTypes();
Class[] parameterTypes = constructor.getParameterTypes();
  1. 操作方法结构(Method)

(1) 获取Method
  • Method[] getMethods():获得Class对象对应类所有的public方法,还可以获得超类(直接和间接)里public方法

Class clazz = MyClass.class
Method[] methods = clazz.getMethods()
  • Method[] getDeclaredMethods():获得Class对象对应类的所有方法,无视权限。但无法获得继承的超类里的任何方法。只能获取Class对象对应类里的方法

Class clazz = MyClass.class
Method[] methods = clazz.getDeclaredMethods() // 只能获得MyCalss里有的方法
  • Method getMethod(String name, Class<?>...parameterTypes):获得对应函数名且参数类型相同的public方法,可以获得超类(间接和直接)里的public方法

Class clazz = MyClass.class
// 当获得函数属性不是public会报错
Method method = clazz.getMethod(publicFunName,形参类1.class,形参类2.class...)
  • Method getDeclaredMethod(String name, Class<?>...parameterTypes):获得对应函数名且参数相同的方法(无视权限限制),但无法获得超类里的任何方法,只能获取Class对象对应类里的方法

Class clazz = MyClass.class
// 获得指定参数的函数方法,所有权限的都能获得
Method method = clazz.getDeclaredMethod(FunName,形参类1.class,形参类2.class...)
  • 这里测试获得方法数组

    @Test
    public void getMethodTest() throws NoSuchMethodException {
        Class clazz = MyClass.class;
        System.out.println("getMethods获得public方法");
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            System.out.print(method.getName() +"  ");
        }
        System.out.println("\ngetDeclaredMethods所有方法");
        methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            System.out.print(method.getName()+"   ");
        }
    }

说明:getMethods获得的函数里FatherPublicFun是直接超类的public方法,这些里面还有Object类里面的public方法。 getDeclaredMethods获得的方法都是MyClass里面,无超类的函数

(2) 调用对象上的Method
  • Object invoke(Object obj, Object[] args):invoke()的返回值即为对应类中调用的方法的返回值。

  • 如果调用的运行时类中的方法没有返回值,则此invoke()返回null

  • 注意:private属性变量和包外default方法需要setAccessible(true)保证属性可以访问,public属性变量都可访问。

// 1 获得Class对象
Class clazz = MyClass.class;

// 2. 获得obj(Class对象对应类的对象)
MyClass obj = new MyClass(); 
// MyClass obj =(MyClass)clazz.getConstructor().newInstance();

// 3. 通过Class对象clazz获得对应变量的Field
Method method = clazz.getDeclaredMethod(funName);

// 4.保证方法属性是可访问的(重要)
method.setAccessible(true);

// 5.执行obj对象名称为funName的方法;
method.invoke(obj,参数列表);
(3) 获取Method的属性
a. 获得方法名
// public String getName() 获取方法名
String fieldName = field.getName(); // 返回变量名 例如name,title
b. 获得方法修饰符
// public int getModifiers() 取得修饰符
int modifier = method.getModifiers();

// modifierName的值会有public,private,defult,public final等
String modifierName = Modifier.toString(method.getModifiers());
c. 获得返回值类型
// public Class<?> getReturnType() 取得全部的返回值类型
Class type = method.getRetrunType();
String typeName = method.getRetrunType().getName();
d. 获得全部参数类型
// public Class<?>[] getParameterTypes() 取得全部的参数
Class[] parameterTypes = method.getParameterTypes();
e. 获取异常信息
// public Class<?>[] getExceptionTypes() 取得异常信息
Class[] exceptionTypes = m.getExceptionTypes();
  1. 操作变量结构(Field)

(1) 获取field
  • Field getField(String name):可以获得Class对象对应类以及它继承的超类里面public的变量结构

Class clazz = MyClass.class

Field field = clazz.getField(FieldName);
  • Field getDeclaredField(String name): 可以获得Class对象对应类里面的变量结构(不受权限的限制),但不能获得超类里的任何变量结构

Class clazz = MyClass.class

Field field = clazz.getDeclaredField(FieldName);
  • FIeld[] getFields():以获得Class对象对应类以及它的超类里面所有public变量

Class clazz = MyClass.calss;

Field[] fields = clazz.getFields();
  • Field[] getDeclaredFields():可以获得Class对象对应类里面的所有变量结构(不受权限的限制),不能获得超类里的任何变量结构

Class clazz = MyClass.calss;

Field[] fields = clazz.getDeclaredFields();
  • 测试获得所有成员变量

    @Test
    public void fieldTest(){
        Class<MyClass> clazz = MyClass.class;
        System.out.println("getFields获得的变量");
        Field[] fields = clazz.getFields();
        for (Field field : fields) {
            System.out.println(field.getName());
        }
        System.out.println("\ngetDeclaredFields获得的变量");
        fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getName());
        }
    }

说明:getFields获得的变量有超类的public变量,也有Class对象对应类的public变量。 getDeclaredFields获得是Class对象对应类里面的所有变量

(2) 获取设置对象上field的值
  • Object get(Object obj):取得指定对象obj上此Field的属性内容,得到是Object。

  • 注意:private属性变量和包外default属性变量需要setAccessible(true)保证属性可以访问,public属性变量都可访问。

// 1 获得Class对象
Class clazz = MyClass.class;

// 2. 获得obj(Class对象对应类的对象)
MyClass obj = new MyClass(id,title); 
// MyClass obj =(MyClass)clazz.getConstructor().newInstance();

// 3. 通过Class对象clazz获得对应变量的Field
Field field = clazz.getField("id"); // 获得clazz中id变量属性

// 4.保证当前属性是可访问的(重要)
field.setAccessible(true);

// 5. 获得是obj对象上id的值
int id = field.get(obj);
  • void set(Object obj,Object Value):设置指定对象obj上此Field的属性内容

// 1 获得Class对象
Class clazz = MyClass.class;

// 2. 获得obj(Class对象对应类的对象)
MyClass obj = new MyClass(id,title); 
// MyClass obj =(MyClass)clazz.getConstructor().newInstance();

// 3. 通过Class对象clazz获得对应变量的Field
Field field = clazz.getField("id"); // 获得clazz中id变量属性

// 4.保证当前属性是可访问的(重要)
field.setAccessible(true);

// 5.给obj赋值
field.set(obj,2); // 现在obj的id值被设置为2
(3) 获得field的信息

下面的变量都是Field对应的变量

a. 获得变量名
// 方法名:public String getName() 
// 返回Field的名称。
String fieldName = field.getName();
b. 获得变量修饰符
// 方法名;public int getModifiers() 
// 以整数形式返回此Field的修饰符
int modifier = field.getModifiers();
String modifierName = Modifier.toString(modifier);
c. 获得变量属性类型
// 方法名:public Class<?> getType() 
// 得到Field的属性类型
Class type = field.getType();
  1. 获取结构注解

说明:对于clazz,field,method获得注解都是相同的方法,所以在这里进行统一的介绍

注意:对于只能在源代码上保留的注解,使用运行时获得的Class对象 无法访问到该注解对象(例如@SuppressWarnings

// 提前获取结构
Class<MyClass> clazz = MyClass.class; // Class对象
Method method = clazz.getMethod("getId"); // getId的方法
Field field = clazz.getDeclaredField("id"); // id变量的结构
(1)获取指定的注解
  • Annotation getAnnotation(Class<A> annotationClass):尝试获取对应结构上存在的、指 定类型的Annotation;如果该类型的注解不存在,则返回null

// Class对象获取指定注解
Annotation annotation = clazz.getAnnotation(注解类.class);

// 获取方法结构上的指定注解
Annotation annotation = method.getAnnotation(注解类.class);

// 获取变量属性上的指定注解
Annotation annotation = field.getAnnotation(注解类.class);
  • Annotation getDeclaredAnnotation(Class<A> annotationClass): 该方法尝试获取直 接修饰对应结构上、指定类型的Annotation;如果该 类型的注解不存在,则返回nul

// Class对象获取指定注解
Annotation declaredAnnotation = clazz.getDeclaredAnnotation(注解类.class);

// 获取方法结构上的指定注解
Annotation declaredAnnotation = method.getDeclaredAnnotation(注解类.class);

// 获取变量属性上的指定注解
Annotation declaredAnnotation = field.getDeclaredAnnotation(注解类.class);
(2)获取所有注解
  • Annotation[] getAnnotations(): 返回修饰对应结构上存在的所有Annotation。

// 获取Class对象上所有注解
Annotation[] annotations = clazz.getAnnotations();

// 获取方法结构上的所有注解
Annotation[] annotations = method.getAnnotations();

// 获取变量属性上的所有注解
Annotation[] annotations = field.getAnnotations();
  • Annotation[] getDeclaredAnnotations(): 返回直接修饰对应结构上的所有Annotation。

// 获取Class对象上所有注解
Annotation[] declaredAnnotations = clazz.getDeclaredAnnotations();

// 获取Class对象上所有注解
Annotation[] declaredAnnotations = clazz.getDeclaredAnnotations();

// 获取Class对象上所有注解
Annotation[] declaredAnnotations = clazz.getDeclaredAnnotations();
(3)获取重复注解

说明:在java8中加入了重复注解的功能,通过getAnnotation(),无法获取同类的多个注解

@run(times=200) 
@run
public class test{}
  • Annotation[] getAnnotationsByType(Class<A> annotationClass):获取修饰对应结构上的、 指定类型的多个 Annotation

// 获取Class对象上指定注解类的全部注解
Annotation[] annotations =  clazz.getAnnotationsByType(注解类.class);

// 获取方法结构method上指定注解类的全部注解
Annotation[] annotations =  method.getAnnotationsByType(注解类.class); 

// 获取变量结构field上指定注解类的全部注解
Annotation[] annotations =  field.getAnnotationsByType(注解类.class);
  • Annotation[] getDeclaredAnnotationsByType(Class<A> annotationClass):获取直接修饰对应结构上指定类型的多个Annotation

// 获取Class对象上指定注解类的全部注解
Annotation[] declaredAnnotations =  clazz.getDeclaredAnnotationsByType(注解类.class);

// 获取方法结构method上指定注解类的全部注解
Annotation[] declaredAnnotations =  method.getDeclaredAnnotationsByType(注解类.class); 

// 获取变量结构field上指定注解类的全部注解
Annotation[] declaredAnnotations =  field.getDeclaredAnnotationsByType(注解类.class);
(4)判断注解是否被使用
  • boolean isAnnotationPresent(): 判断对应的结构上是否使用了Annotation修饰。

boolean isAnnotation = clazz.isAnnotationPresent(注解类.class);

boolean isAnnotation = method.isAnnotationPresent(注解类.class);

boolean isAnnotation = field.isAnnotationPresent(注解类.class);

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值