写在前面
后面两节关于泛型和注解的实在是不入脑,等后续提到相关内容了再补吧。

反射机制(Reflection)

反射概述

  • 动态语言:在运行时可以改变某些自身结构

  • 静态:结构不可变

反射机制允许程序在执行期间借助Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

优点:可以实现动态创建对象和编译,灵活性高

缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么,它会满足我们的一起,这类操作总是慢于直接执行相同的操作。

public class reflection {

    //有可能会找不到类,需要抛出异常
    public static void main(String[] args) throws ClassNotFoundException{

        //获取类的class对象 如果有包名,需要在前面顺序加上
        //如Class<?>c1 = Class.forName("com.baidu.www.user");
        Class<?>c1 = Class.forName("user");
        Class<?>c2 = Class.forName("user");
        Class<?>c3 = Class.forName("user");

        //一个类在内存中只有一个class对象
        //一个类被加载后,类的整个结构都会被封装在Class对象中

        //打印的哈希值相同,说明对象相同
        System.out.println(c1.hashCode());
        System.out.println(c2.hashCode());
        System.out.println(c3.hashCode());
    }
}

class user{
    private int age;
    private String name;

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

    public user() {
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

运行结果如下:

460141958
460141958
460141958

进程已结束,退出代码为 0
  1. 反射可以获取类的class对象

  2. 一个类在内存中只有一个class对象

  3. 一个类被加载后,类的整个结构都会被封装在Class对象中

在Object类在,定义了以下方法:

public final Class getClass();

由于是在Object类中,这个方法将被所有子类继承。

以上方法的返回值是一个Class类。这个类是java反射的源头。

实际上,反射就是通过一个类的实例化对象来求出类的名称。

Class类

public class reflection {

    //有可能会找不到类,需要抛出异常
    public static void main(String[] args) throws ClassNotFoundException{

        user u1=new consumer();
        System.out.println(u1.age);

        //1.通过对象获得
        Class c1 = u1.getClass();
        System.out.println(c1.hashCode());

        //2.forname
        Class c2=Class.forName("consumer");
        System.out.println(c2.hashCode());

        //3.类名.class
        Class c3 = consumer.class;
        System.out.println(c3.hashCode());

        //4.基本内置类型的包装类都有一个type属性
        Class<Integer> c4=Integer.TYPE;
        System.out.println(c4);

        //获得父类类型
        Class c5=c1.getSuperclass();
        System.out.println(c5);
    }
}

class user{
    public int age=15;
    public String name;

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

    public user() {
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

class administrator extends user{
    public administrator(){
        this.age= 55;
    }
}

class consumer extends user{
    public consumer(){
        this.age= 20;
    }
}

所有类型的Class对象

import javax.xml.bind.Element;
import java.lang.annotation.ElementType;

public class reflectTest {
    public static void main(String[] args) {
        Class<Object> c1 = Object.class;
        Class c2= Comparable.class;
        Class c3= String[].class;
        Class c4= String.class;
        Class c5 = int [][].class;
        Class c6 = Override.class;
        Class c7= ElementType.class;
        Class c8= Integer.class;
        Class c9= void.class;

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);


    }
}

class java.lang.Object
interface java.lang.Comparable
class [Ljava.lang.String;
class java.lang.String
class [[I
interface java.lang.Override
class java.lang.annotation.ElementType
class java.lang.Integer
void

只要元素类型和维度一样,就是同一个Class对象。

Java的内存分析

测试静态代码块的执行顺序:

public class reflect02 {
    public static void main(String[] args) {
        A a= new A();
        System.out.println(A.m);

    }
}

class A{
    static {
        System.out.println("静态代码初始化");
        m=300;
    }
    static int m = 100;

    public A(){
        System.out.println("无参构造初始化");
    }
}

输出的顺序为:

静态代码初始化
无参构造初始化
100

可以看出,static部分的内容早在类的对象实例化之前就已经运行了。

屏幕截图 2025-11-12 151440

整个类的初始化的内存变化如上图。

类的初始化顺序

主动引用---一定会发生类的初始化

package reflect;

//测试类什么时候会初始化
public class reflect03 {
    public static void main(String[] args) {
        System.out.println("main类加载完成");
        
    }
}

class Father{
    static int b = 2;
    static {
        System.out.println("父类加载完成");
    }
}

class Son extends Father{
    static {
        System.out.println("子类加载完成");
        m= 300;
    }
    static int m=100;
    static final int M=1;
}

结果:

main类加载完成
package reflect;

//测试类什么时候会初始化
public class reflect03 {
    public static void main(String[] args) {
        System.out.println("main类加载完成");
        
        //主动引用
        Son s = new Son();
    }
}

class Father{
    static int b = 2;
    static {
        System.out.println("父类加载完成");
    }
}

class Son extends Father{
    static {
        System.out.println("子类加载完成");
        m= 300;
    }
    static int m=100;
    static final int M=1;
}

main类加载完成
父类加载完成
子类加载完成

此时可以看出,当主动引用子类时,即使Main函数中未进行父类的初始化,在子类初始化的过程中也会先对父类进行初始化。


package reflect;

//测试类什么时候会初始化
public class reflect03 {
    public static void main(String[] args) throws ClassNotFoundException{
        System.out.println("main类加载完成");
        
        //反射产生主动引用的情况
        Class<?> c1 = Class.forName("reflect.Son");
    }
}

class Father{
    static int b = 2;
    static {
        System.out.println("父类加载完成");
    }
}

class Son extends Father{
    static {
        System.out.println("子类加载完成");
        m= 300;
    }
    static int m=100;
    static final int M=1;
}

main类加载完成
父类加载完成
子类加载完成

可以看出,当采用反射创建类的Class对象时,父类和子类也发生了初始化

被动引用---不会发生类的初始化

  1. 访问静态域时,只有真正声明这个域的类才会被初始化。

    当子类访问父类的static变量时,子类不会被初始化。

    因为静态变量只属于定义它的类,即使子类继承了,并且可以访问,但访问的是同一个内存。

  2. 通过数组定义类引用,不会触发此类的初始化

  3. 引用常量不会触发此类的初始化

​ 引用常量是指final过的引用对象,被final修事故后的引用变量无法再指向别的对象,但它指向的对象内部内容仍然是可变的。

​ (p.s.:真正的常量是用static final修饰过的变量)

package reflect;

//测试类什么时候会初始化
public class reflect03 {
    public static void main(String[] args) throws ClassNotFoundException{
        System.out.println("main类加载完成");

        System.out.println(Son.b);
    }
}

class Father{
    static int b = 2;
    static {
        System.out.println("父类加载完成");
    }
}

class Son extends Father{
    static {
        System.out.println("子类加载完成");
        m= 300;
    }
    static int m=100;
    static final int M=1;
}

main类加载完成
父类加载完成
2
package reflect;

//测试类什么时候会初始化
public class reflect03 {
    public static void main(String[] args) throws ClassNotFoundException{
        System.out.println("main类加载完成");

        Son[] a= new Son[5];
    }
}

class Father{
    static int b = 2;
    static {
        System.out.println("父类加载完成");
    }
}

class Son extends Father{
    static {
        System.out.println("子类加载完成");
        m= 300;
    }
    static int m=100;
    static final int M=1;
}

main类加载完成

可以看出,当用数组形式新建引用对象的时候,父类和子类都未进行初始化。

package reflect;

//测试类什么时候会初始化
public class reflect03 {
    public static void main(String[] args) throws ClassNotFoundException{
        System.out.println("main类加载完成");

        System.out.println(Son.M);
    }
}

class Father{
    static int b = 2;
    static {
        System.out.println("父类加载完成");
    }
}

class Son extends Father{
    static {
        System.out.println("子类加载完成");
        m= 300;
    }
    static int m=100;
    static final int M=1;
}

main类加载完成
1

由于常量在链接过程中就已经被初始化,存入调用类的常量池中,因此无需初始化即可调用。

类加载器

类加载:把class文件字节码加载到内存,将数据转化为方法区的运行时数据结构,在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问接口。

类缓存:标准的Javase类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载一段时间。JVM垃圾回收机制可以回收这些Class对象。

加载器的分类:

  1. 引导类(负责核心库,无法直接获取)
  2. 扩展类 jre/lib/ext
  3. 系统类(最常用)java -classpath
package reflect;

public class reflect04 {
    public static void main(String[] args) throws ClassNotFoundException {

        //获取系统类的加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        //获得系统类加载器的父类加载器:扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);

        //获得扩展类加载器的父类加载器:根加载器(C/C++)
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);

        //测试当前类是哪个加载器加载的
        ClassLoader classLoader1 = Class.forName("reflect.reflect04").getClassLoader();
        System.out.println(classLoader1);

        //测试JDK内置的类是谁加载的
        ClassLoader classLoader = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader);

        //查看加载器可以应用于哪些路径
        System.out.println(System.getProperty("java.class.path"));
    }
}

sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1b6d3586
null
sun.misc.Launcher$AppClassLoader@18b4aac2
null
D:\work\java\jre\lib\charsets.jar;D:\work\java\jre\lib\deploy.jar;D:\work\java\jre\lib\ext\access-bridge-64.jar;D:\work\java\jre\lib\ext\cldrdata.jar;D:\work\java\jre\lib\ext\dnsns.jar;D:\work\java\jre\lib\ext\jaccess.jar;D:\work\java\jre\lib\ext\jfxrt.jar;D:\work\java\jre\lib\ext\localedata.jar;D:\work\java\jre\lib\ext\nashorn.jar;D:\work\java\jre\lib\ext\sunec.jar;D:\work\java\jre\lib\ext\sunjce_provider.jar;D:\work\java\jre\lib\ext\sunmscapi.jar;D:\work\java\jre\lib\ext\sunpkcs11.jar;D:\work\java\jre\lib\ext\zipfs.jar;D:\work\java\jre\lib\javaws.jar;D:\work\java\jre\lib\jce.jar;D:\work\java\jre\lib\jfr.jar;D:\work\java\jre\lib\jfxswt.jar;D:\work\java\jre\lib\jsse.jar;D:\work\java\jre\lib\management-agent.jar;D:\work\java\jre\lib\plugin.jar;D:\work\java\jre\lib\resources.jar;D:\work\java\jre\lib\rt.jar;D:\javawork\untitled\out\production\untitled;D:\apps\IntelliJ IDEA Community Edition 2024.2.1\lib\idea_rt.jar

由第三和第五行可以看出,扩展类加载器的父类,即根加载器,是无法直接获取的

通过反射获取类的信息

此部分对照代码即可

package reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class reflect05 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class<?> c1 = Class.forName("reflect.user");

        //获取类名
        System.out.println(c1.getName());
        System.out.println(c1.getSimpleName());

        //获取类的属性
        Field[] fields =c1.getFields();//只能找到公共属性(public)
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("======================");

        fields=c1.getDeclaredFields();//找到全部属性
        for (Field field1 : fields) {
            System.out.println(field1);
        }

        System.out.println("======================");

        //获取指定属性的值
        Field age = c1.getField("age");
        System.out.println(age);

        System.out.println("======================");

        //获取类的方法

        //会获取到本类及父类的全部public方法
        Method[] methods=c1.getMethods();
        for (Method method : methods) {
            System.out.println("method:"+method);
        }

        //所有方法
        methods = c1.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println("declare method:"+method);
        }

        System.out.println("======================");

        //获得指定方法
        //重载
        Method getName = c1.getMethod("getName", null);
        Method setName = c1.getMethod("setName", String.class);
        System.out.println(getName);
        System.out.println(setName);

        System.out.println("======================");

        //获得构造器
        Constructor<?>[] constructors = c1.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
        constructors=c1.getDeclaredConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println("declared:"+constructor);
        }
        System.out.println("======================");
        //获得指定构造器 指定参数就可以
        Constructor<?> constructor;
        constructor = c1.getConstructor(int.class,String.class);
        System.out.println(constructor);
    }
}

reflect.user
user
public int reflect.user.age
public java.lang.String reflect.user.name
======================
public int reflect.user.age
public java.lang.String reflect.user.name
======================
public int reflect.user.age
======================
method:public java.lang.String reflect.user.getName()
method:public void reflect.user.setName(java.lang.String)
method:public int reflect.user.getAge()
method:public void reflect.user.setAge(int)
method:public final void java.lang.Object.wait() throws java.lang.InterruptedException
method:public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
method:public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
method:public boolean java.lang.Object.equals(java.lang.Object)
method:public java.lang.String java.lang.Object.toString()
method:public native int java.lang.Object.hashCode()
method:public final native java.lang.Class java.lang.Object.getClass()
method:public final native void java.lang.Object.notify()
method:public final native void java.lang.Object.notifyAll()
declare method:public java.lang.String reflect.user.getName()
declare method:public void reflect.user.setName(java.lang.String)
declare method:public int reflect.user.getAge()
declare method:public void reflect.user.setAge(int)
======================
public java.lang.String reflect.user.getName()
public void reflect.user.setName(java.lang.String)
======================
public reflect.user(int,java.lang.String)
public reflect.user()
declared:public reflect.user(int,java.lang.String)
declared:public reflect.user()
======================
public reflect.user(int,java.lang.String)

通过反射调用类的指定结构

package reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class reflect06 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        // 获得Class对象
        Class c1 = Class.forName("reflect.user");

        // 构造一个对象
        user user = (user) c1.newInstance();    // 本质上调用了类的无参构造器
        System.out.println(user);

        // 通过构造器创建对象
        Constructor constructor = c1.getDeclaredConstructor(int.class,String.class);
        user user1 = (user) constructor.newInstance(1,"光荣时代");
        System.out.println(user1);

        // 通过反射调用普通方法
        user user2 = (user) c1.newInstance();
        // 通过反射获取一个方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        // invoke:激活
        // (对象,"方法值")
        setName.invoke(user2, "some");
        System.out.println(user2.getName());

        // 通过反射操作属性
        user user3 = (user) c1.newInstance();
        Field name = c1.getDeclaredField("name");

        // 不能直接操作私有属性,我们需要关闭程序的安全检测,属性或方法的setAccessible(true)
        // 设置安全检测
        name.setAccessible(true);

        name.set(user3, "some2");
        System.out.println(user3.getName());
    }
}

reflect.user@1b6d3586
reflect.user@4554617c
some
some2

创建对象

  1. 通过反射(Class.forName())创建Class对象,然后调用newInstance函数以及强制类型转换来新建一个类的对象。

  2. 调用getDeclaredConstructor(参数)来获取构造器,再调用 constructor.newInstance(参数)来新建对象。

调用方法

Class对象调用:getDeclaredMethod(参数),再用methodName.invoke(参数);激活并使用。

修改属性

Class对象调用getDeclaredField(参数)

对于私有属性,需要赋予权限:参数.setAccessible(true);

再运用参数.set(实例化对象,内容)

性能比较

package reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class reflect07 {
    public static void test01() {
        user user = new user();
        long startTime = System.currentTimeMillis();

        for (int i = 0; i < 1000000000; i++) {
            user.getName();
        }

        long endTime = System.currentTimeMillis();
        System.out.println("普通方式执行10亿次:" + (endTime - startTime) + "ms");
    }

    // 反射方式调用
    public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        user user = new user();
        Class c1 = user.getClass();
        Method getName;
        getName = c1.getDeclaredMethod("getName", null);
        long startTime = System.currentTimeMillis();

        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user,null);
        }

        long endTime = System.currentTimeMillis();
        System.out.println("反射方式执行10亿次:" + (endTime - startTime) + "ms");
    }

    // 反射方式调用,关闭检测
    public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        user user = new user();
        Class c1 = user.getClass();
        Method getName = c1.getDeclaredMethod("getName", null);
        getName.setAccessible(true);
        long startTime = System.currentTimeMillis();

        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user,null);
        }

        long endTime = System.currentTimeMillis();
        System.out.println("反射方式执行10亿次,关闭检测:" + (endTime - startTime) + "ms");
    }

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        test01();
        test02();
        test03();
    }
}

普通方式执行10亿次:4ms
反射方式执行10亿次:1606ms
反射方式执行10亿次,关闭检测:968ms
Posted on 2025-11-13 15:43  miloandmicah  阅读(3)  评论(0)    收藏  举报