Java基础:反射(一)

本文详细介绍了Java反射的基本概念,包括反射的定义、类对象的获取、静态和动态加载类的区别,以及反射包体系中的各种类和接口。此外,还涵盖了反射的基本操作流程,如获取类的Class、构造方法、对象实例、字段和方法,以及反射注解的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、反射

1.什么是反射

看一下官方对反射的描述

Reflection is commonly used by programs which require 
the ability to examine or modify the runtime behavior 
of applications running in the Java virtual machine.

翻译一下的意思就是:反射通常用于运行在java虚拟机上的程序,需要检查能力或者修改运行时行为的程序。

这段话有点绕,我们换个说法就是:反射可以获取一个类的所有方法和属性;并且对于任意一个对象,可以调用它的任意方法或者获取其属性。

2.类对象

2.1 什么是类对象

使用反射机制的前提就是获取类对象,类对象是虚拟机将class文件读入内存,创建的一个类对象,用于描述类有什么属性,方法等。

2.2 如何获取类对象

获取类对象有三种方式,每一种方式获取的类对象都是相等的,所以在虚拟机中一个类只会有一个类对象。

其中Class.forName不仅仅表示类的类类型,还代表了动态加载类。编译时加载是静态加载。

Class c1 = Demo.class;
Class c2 = new Demo().getClass();
Class c3 = Class.forName("com.lee.provider.controller.demo.Demo");
// true
System.out.println(c1 == c2);
// true
System.out.println(c2 == c3);

2.3 动态和静态加载类

  • 静态加载

编译时刻加载是静态加载,new创建对象是静态加载类,在编译时刻就需要加载所有的可能使用到的类。下述代码在编译的时候如果TeacherStudent类不存在的话,就直接会报错

public void method(String case) {
    if ("case1".equals(case)) {
        Teacher teacher = new Teacher();
    }
    if ("case2".equals(case)) {
        Student student = new Student();
    }
}
  • 动态加载

运行时加载是动态加载。用到哪个就加载哪个,不用就不加载。下述代码动态加载类,在编译的时候,如果classPath不存在也不会报错,只有在运行的时候才会报错

public void method(String classPath) {
    try{
        Class clazz = Class.forName(classPath);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

二、反射包体系

反射的相关类和接口都在java.lang.reflect包下面。

1.类的继承体系

在这里插入图片描述

1.1 AccessibleObject

AccessibleObject类是FieldMethodConstructor对象的基类。它提供了将反射的对象标记为在使用时取消默认Java语言访问控制检查的能力。

对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用FieldMethodConstructor对象来设置或获得字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。

1.2 Member

Member是一个接口,在JDK描述中是用于标记一个成员(字段或者方法)或者一个构造方法。Member接口有四个方法。

  • getDeclaringClass:获取声明成员类对应的class

  • getModifiers:获取成员对应的修饰符,修饰符以累加的形式返回,如public static就返回9

    修饰符
    public1
    private2
    protected4
    static8
    final16
    synchronized32
    volatile64
    transient128
    native256
    interface512
    abstract1024
    strict2048
  • getName:获取这个成员对应的名称

  • isSynthetic:判断这个成员是不是由编译器注入的

1.3 Executable

一个共享的父类用来描述ConstructorMethod的公共方法。

1.4 Field

Field提供了动态访问类或者接口的单个字段、反射的字段可以是类(静态)字段或实例字段。

1.5 Constructor

Constructor提供了访问类的单个构造方法,一个构造方法对应一个Constructor类。

1.6 Method

Method提供访问类或者接口的单个方法,反射的方法可以是类方法或者实例方法包括抽象方法。

2.接口的继承体系

2.1 AnnotatedElement

代表运行在虚拟机中的程序的一个注解元素,此接口允许以反射的方式读取注解。在这个接口中返回的所有注解是不可变的和序列化的。调用者可以修改这个接口返回的数组,不会影响其他调用者。

2.2 AnnotatedType

AnnotatedType表示在当前虚拟机运行的程序中可能使用的注解类型、 在Java中使用的可以是任何类型,包括数组类型、参数类型、变量类型、通配符类型。

三、反射基本操作

1.反射使用流程

  • 获取类对象

  • 获取构造方法

  • 创建对象

  • 使用对象变量

  • 使用对象方法

  • 反射获取注解

2.使用反射的准备

要使用反射我们要有基本的类,这里我们准备一个类如下

package com.lee.study.basic.reflect;

public class ReflectDemo {

    public String name;

    private int age;

    public ReflectDemo(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public ReflectDemo() {

    }

    public void sayHello (String message) {
        System.out.println("说消息:" + message);
    }

    public void getMessage () {
        System.out.println("无参数");
    }

    public String getReturn () {
        return "惊喜";
    }

    private void secret() {
        System.out.println("秘密");
    }

    public String getName () {
        return this.name;
    }

    public int getAge () {
        return this.age;
    }
}

3. 使用反射

3.1 获取类的class

上面我们说了获取类的class有三种方法,这里我们使用forName方法

Class<?> clazz = Class.forName("com.lee.study.basic.reflect.ReflectDemo");

3.2 获取构造方法

  • 公共构造方法

通过getConstructors()方法可以获取公共的构造方法,如果类里面没有写构造方法,也会默认获取无参构造方法,因为无参构造方法是每个类默认有的,一旦自己添加构造方法,那就没有默认的构造方法。

Class<?> clazz = Class.forName("com.lee.study.basic.reflect.ReflectDemo");
Constructor[] constructors = clazz.getConstructors();
  • 所有构造方法

通过getDeclaredConstructors()方法获取所有的构造方法,包括私有的构造方法。

Class<?> clazz = Class.forName("com.lee.study.basic.reflect.ReflectDemo");
Constructor[] constructors = clazz.getDeclaredConstructors();
  • 获取无参构造方法

通过getConstructor方法可以获取单个无参构造方法。

Class<?> clazz = Class.forName("com.lee.study.basic.reflect.ReflectDemo");
Constructor constructor = clazz.getConstructor();
  • 获取有参构造方法

通过getConstructor方法可以获取单个有参构造方法。

Class<?> clazz = Class.forName("com.lee.study.basic.reflect.ReflectDemo");
Constructor constructor = clazz.getConstructor(String.class, int.class);

3.3 反射创建对象

上面我们通过获取class,然后获取了构造方法,通过构造方法就可以创建实例对象了

  • 无参构造方法创建对象
Class<?> clazz = Class.forName("com.lee.study.basic.reflect.ReflectDemo");
Constructor constructor = clazz.getConstructor();
ReflectDemo reflectDemo = (ReflectDemo)constructor.newInstance();
  • 有参构造方法创建对象
Class<?> clazz = Class.forName("com.lee.study.basic.reflect.ReflectDemo");
Constructor constructor = clazz.getConstructor(String.class, int.class);
ReflectDemo reflectDemo = (ReflectDemo)constructor.newInstance("张三", 19);

3.4 使用对象变量

Field类是反射字段类,用于变量反射,一个Field对应一个变量。

  • 公共变量

通过getFields()方法可以获取所有的公共成员变量,包括从父类继承和实现接口中的公共成员变量

Class<?> clazz = Class.forName("com.lee.study.basic.reflect.ReflectDemo");
Field[] fields = clazz.getFields();
  • 所有变量

通过getDeclaredFields()方法可以获取所有的成员变量,公有和私有变量,不包括父类和实现接口的成员变量。

Class<?> clazz = Class.forName("com.lee.study.basic.reflect.ReflectDemo");
Field[] fields = clazz.getDeclaredFields();
  • 反射公共字段
Class<?> clazz = Class.forName("com.lee.study.basic.reflect.ReflectDemo")
Constructor constructor = clazz.getConstructor();
ReflectDemo reflectDemo = (ReflectDemo)constructor.newInstance();
Field field = clazz.getField("name");
field.set(reflectDemo, "老六");
System.out.println(reflectDemo.name);
  • 反射私有字段
Class<?> clazz = Class.forName("com.lee.study.basic.reflect.ReflectDemo");
Constructor constructor = clazz.getConstructor();
ReflectDemo reflectDemo = (ReflectDemo)constructor.newInstance();
Field field = clazz.getDeclaredField("age");
// 设置私有字段可访问
field.setAccessible(Boolean.TRUE);
field.set(reflectDemo, 20);
System.out.println(reflectDemo.getAge());

3.5 使用对象方法

Method类,是反射方法的对象,一个成员方法就是一个Method对象。

  • 公共方法

通过getMethods方法获取到所有的公共方法,包括父类的公共方法。

Class<?> clazz = Class.forName("com.lee.study.basic.reflect.ReflectDemo");
Method[] methods = clazz.getMethods();
  • 所有方法

通过getDeclaredMethods方法获取到改类的所有方法,包括私有方法,但是不包括父类的任何方法。

Class<?> clazz = Class.forName("com.lee.study.basic.reflect.ReflectDemo");
Method[] methods = clazz.getDeclaredMethods();
  • 无参方法
Class<?> clazz = Class.forName("com.lee.study.basic.reflect.ReflectDemo");
Constructor constructor = clazz.getConstructor();
ReflectDemo reflectDemo = (ReflectDemo)constructor.newInstance();
Method method = clazz.getMethod("getMessage");
method.invoke(reflectDemo);
  • 有参方法
Class<?> clazz = Class.forName("com.lee.study.basic.reflect.ReflectDemo");
Constructor constructor = clazz.getConstructor();
ReflectDemo reflectDemo = (ReflectDemo)constructor.newInstance();
Method method = clazz.getMethod("sayHello", String.class);
method.invoke(reflectDemo, "可以的");
  • 有返回值方法
Class<?> clazz = Class.forName("com.lee.study.basic.reflect.ReflectDemo");
Constructor constructor = clazz.getConstructor();
ReflectDemo reflectDemo = (ReflectDemo)constructor.newInstance();
Method method = clazz.getMethod("getReturn");
Object obj = method.invoke(reflectDemo);
System.out.println(obj);
  • 私有方法
Class<?> clazz = Class.forName("com.lee.study.basic.reflect.ReflectDemo");
Constructor constructor = clazz.getConstructor();
ReflectDemo reflectDemo = (ReflectDemo)constructor.newInstance();
Method method = clazz.getDeclaredMethod("secret");
method.setAccessible(Boolean.TRUE);
method.invoke(reflectDemo);

4. 反射注解

4.1 使用反射注解准备

  • 用于类的注解

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyClass {
        String name();
    }
    
  • 用于字段的注解

    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @interface MyField {
    }
    
  • 用于方法的注解

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyMethod {
        int value();
    }
    
  • 被修饰的类

    MyClass(name = "反射什么的")
    public class ReflectDemo {
    
        @MyField
        public int age;
    
        @MyMethod(1)
        public void method () {
    
        }
    }
    

4.2 反射获取注解

  • 获取类上的注解

    Class<?> clazz = Class.forName("com.lee.study.basic.reflect.ReflectDemo");
    // 获取类上所有的注解  
    Annotation[] annotations = clazz.getAnnotations();
    // 获取类上指定的注解
    Annotation annotation = clazz.getAnnotation(MyClass.class);
    // 判断类上是否有该注解
    boolean isHave = clazz.isAnnotationPresent(MyClass.class);
    
  • 获取字段上的注解

    Class<?> clazz = Class.forName("com.lee.study.basic.reflect.ReflectDemo");
    Field field = clazz.getField("age");
    // 获取所有的注解
    Annotation[] annotations = field.getAnnotations();
    // 获取指定的注解
    Annotation annotation = field.getAnnotation(MyField.class);
    // 判断字段上是否有指定注解
    boolean isHave = field.isAnnotationPresent(MyField.class);
    
  • 获取方法上的注解

    Class<?> clazz = Class.forName("com.lee.study.basic.reflect.ReflectDemo");
    Method method = clazz.getMethod("method");
    // 获取方法上的所有注解
    Annotation[] annotations = method.getAnnotations();
    // 获取方法上的指定注解
    Annotation annotation = method.getAnnotation(MyMethod.class);
    // 判断方法上是否有指定注解
    boolean isHave = method.isAnnotationPresent(MyMethod.class);
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值