Java反射学习笔记

本文详细介绍了Java反射机制,包括如何获取Class对象、构造器、成员变量和方法,并展示了反射在创建对象、破坏封装性和泛型约束方面的应用。通过实例代码演示了反射在集合泛型擦除后的操作。反射在框架开发中起着重要作用,能够帮助开发者在运行时动态获取和操作类信息。

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

反射

在Java中反射是极其重要的知识,在后期接触的大量框架的底层都运用了反射技术,因此掌握反射技术将帮助我们更好地理解这些框架的底层原理,以便灵活地掌握框架的使用

反射是指对于任何一个Class类(编译后的.class文件),在“运行的时候”都可以直接得到这个类的全部成分

一个.class文件就是一个Class对象

在运行时,我们可以直接得到这个类的

  • 构造器对象 Constructor
  • 类的成员变量对象Field
  • 类的成员方法对象Method

这种运行时动态后去类信息以及动态调用类中成分的能力称为Java的反射机制

反射的关键

反射的第一步也是关键的一步就是先得到编译后的Class类对象 , 然后就可以得到Class的全部成分(因为类中的所有信息都在.class文件中)

image-20220915213820961

认识Class类

先定义一个类 ,接下来用

package com.reflect;

public class Person {
    //私有成员变量
    private String name;
    private int age;
    
    private Person(int age){
        this.age = age;
    }
    
    //构造方法
    public Person(){

    };

    public Person(String name){
        this.name = name;
    }

    public Person(String name , int age){
        this.name = name;
        this.age = age;
    }
    public void setName(String name) {
        this.name = name;
    }

    //成员方法
    public String getName(){
        return name;
    }

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

    public int getAge() {
        return age;
    }


}

获取Class类对象

image-20220915214725715

编译生成的.class文件, 将这个文件提取到内存中 , 就可以拿到这个Class对象

第一种方式

通过Class类的静态方法forName(className)来将.class文件加载到内存中,

该方法需要一个参数, 是这个类的全限名: 包名 + 类名

package com.reflect;

public class Reflect_ForName {
    public static void main(String[] args) throws ClassNotFoundException {
        //通过forName(全限名:包名 + 类名)
        Class c = Class.forName("com.reflect.Person");
        System.out.println(c);
    }
}

第二种方式

直接使用类名.class

第三种方式

Runtime运行阶段, 通过对象.getClass(String name)方法

getClass()Object的一个方法, 所以的对象都可以直接调用该方法.

当我已经有了一个确切的对象后, 我就可以直接通过此方法获得此类的Class对象(此时.class肯定已经加载到内存中了)

package com.reflect;

public class Reflect_ForName {
    public static void main(String[] args) throws ClassNotFoundException {
//        第一种方式
        //通过forName(全限名:包名 + 类名)
        Class c = Class.forName("com.reflect.Person");
        System.out.println(c);


//        第二种方式:   类名.class
        Class c1 = Person.class;
        System.out.println(c1);

//        第三种方式: 对象.getClass()
        Person p = new Person("张三");
        Class c2 = p.getClass();
        System.out.println(c2);
    }
}

反射的应用

通过反射创建对象

image-20220915221002604

  • 第一步,获取Class对象
  • 第二部, 获取构造器对象
  • 通过构造器对象创建对象
Class中与构造亲有关的API
Constructor<?>[] getConstructors();
//返回这个类的所有构造器对象数组(只能拿public的)

Constructor<?>[] getDeclaredConstructors();
//返回所有的构造器对象,存在就能拿到

Constructor<T> getConstructor(Class<?>...parameterTypes);
//返回单个构造器对象(只能拿public的)

Constructor<T> getDeclaredConstructor(Class<?>...parameterType);
//返回单个构造器对象 , 存在就能拿到    
获取构造器的几种方法

建议使用getDeclaredConstructor()getDeclaredConstructors()

package com.reflect;

import java.lang.reflect.Constructor;

public class getConstructor {
    public static void main(String[] args) throws NoSuchMethodException {
//        1. 获取Class对象
        Class c = Person.class;
//        获取所有的pubic 构造方法
        Constructor[] constructors = c.getConstructors();
        for (Constructor sc:constructors) {
            System.out.println(sc.getName() + "---" + sc.getParameterCount());
        }
//        获取特定的构造方法
        Constructor noPara = c.getConstructor();
        System.out.println(noPara.getName() + "----" + noPara.getParameterCount());
//      定位某个有参构造器
        Constructor twoPara = c.getDeclaredConstructor(String.class,int.class);
        System.out.println(twoPara.getName() + "----" + twoPara.getParameterCount());
    }
}
通过构造器获取对象

通过ConstructornewInstance()方法 ,通过此方法得到的对象是一个Object , 需要强制转换为目标对象类型

package com.reflect;

import com.sun.org.apache.bcel.internal.Const;

import java.lang.reflect.Constructor;

public class getObject {
    public static void main(String[] args) throws Exception {
        Class c = Person.class;
//        获取public无参构造方法
        Constructor constructor = c.getDeclaredConstructor();
        Person p = (Person) constructor.newInstance();
        System.out.println(p);

//        获取有参构造方法
        Constructor paraconst = c.getDeclaredConstructor(String.class, int.class);
//        此时通过有参构造器创建对象 , 需要传参数
        Person ps = (Person)  paraconst.newInstance("张三",18);
        System.out.println(ps.getAge());

//        加入构造方法私有, 我们也可以构造对象, 暴力反射 ,但是不建议这么做
        Constructor privatecon = c.getDeclaredConstructor(int.class);
        privatecon.setAccessible(true);//强制打开权限
        Person pp = (Person) privatecon.newInstance(22);
        System.out.println(pp.getAge());
    }
}

如果类中的成分是private,不能直接使用

可以使用setAccessible(true)强制打开权限,暴力反射, 适用于类中的所有成分

获取成员变量

image-20220915233827359

Class中与Field有关的API
Field[] getFields();
//返回所有成员变量的数组(只返回public)

Field[] getDeclaredFields();
//返回所有成员变量对象的数组,存在就能拿到

Field getField(String name);
//返回单个成员变量(只能public)

Field getDeclaredField(String name);
//返回单个成员变量 , 存在就能拿到

常量也是成员变量

获取成员变量

package com.reflect;

import java.lang.reflect.Field;

public class getField {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("com.reflect.Person");
//        获取所有属性
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getName() + "---" + field.getType());
        }
//        定位某个属性
        Field field = c.getDeclaredField("age");
        System.out.println(field.getName() + "----" + field.getType());
    }
}

给成员变量赋值, 因为成员变量不能孤立存在, 所以需要指定一个对象

成员方法和属性的调用都需要结合具体的对象

//Field类中的方法

set(Object obj, Object value);
//给成员变量赋值

get(Object obj);
//获取成员变量的值
获取成员方法

image-20220916151329900

Class中与Method相关的API

Method[] getMethods();
//返回所有成员方法对象的数组(只能拿public)
//可以拿到父类的方法, 例如Object的

Methode[] getDeclaredMethods();
//返回所有成员方法的数组,存在就能拿到

Method getMethod(String name,Class<?>...parameterTypes);
//返回单个成员方法(只能public)

Method getDeclaredMethod();
//返回单个成员方法对象,存在就能拿到

拿到方法对象

package com.reflect;

import java.lang.reflect.Method;

public class getMethod{
    public static void main(String[] args) throws NoSuchMethodException {
        Class c = Person.class;
        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println("方法名称" + method.getName() + "方法返回值类型" + method.getReturnType() + "参数数量" + method.getParameterCount());
        }

//        拿到指定的方法
//        通过方法名称获取指定方法
        Method md = c.getMethod("getName");
//        通过方法名和参数类型获取
        Method mx = c.getMethod("getAge", int.class);
        
    }
}

拿到这些方法是为了调用这些方法, 接下里就是调用的方法

Method中用于触发执行的方法

Object invoke(Object obj, Object...args);
//第一个参数:obj是对象,通过此对象来调用方法
//第二个参数:方法的参数,如果没有可以不写
//返回值: 就是运行方法后的返回值,如果方法没有返回值, 则该方法的返回值为null
package com.reflect;

import java.lang.reflect.Method;

public class getMethod{
    public static void main(String[] args) throws Exception {
        Class c = Person.class;
        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println("方法名称" + method.getName() + "方法返回值类型" + method.getReturnType() + "参数数量" + method.getParameterCount());
        }

//        拿到指定的方法
//        通过方法名称获取指定方法
        Method mget = c.getMethod("getName");
//        通过方法名和参数类型获取
        Method mset = c.getMethod("setName",String.class);
//      触发方法
        Person p = new Person();
        mset.invoke(p,"张三");
        String rValue = (String) mget.invoke(p);
        System.out.println(rValue);
    }
}

反射的作用

  • 可以在运行阶段得到一个类的全部成分然后操作
  • 可以破坏封装性
  • 也可以破坏泛型的约束性
  • 最主要的用途: 做Java高级框架
绕过编译阶段为集合添加数据
  • 反射是作用在运行时的技术, 此时集合的泛型将不能产生约束了 , 此时是可以为集合存入其他任意类型的元素
ArrayList<Integer> list = new ArrayList<>();
list.add(100);
//list.add("zhang");
list.add(99);
  • 泛型只是在编译阶段可以约束集合, 只能操作某种数据类型, 在编译成Class文件进入运行阶段 时, 其真实类型都是ArrayList, 泛型相当于被擦除了

演示一波,

package com.reflect;

import java.util.ArrayList;

public class ArrayListAdd {
    public static void main(String[] args) {
        /*
        验证不同集合是调用同一个Class文件
        * */
        ArrayList<String> arrayList1 = new ArrayList<>();
        ArrayList<Integer> arrayList2 = new ArrayList<>();

        System.out.println(arrayList1.getClass());
        System.out.println(arrayList2.getClass());
        System.out.println(arrayList1.getClass() ==  arrayList2.getClass());
    }
}

从运行结果可以看出, 这两个集合的泛型不同, 但是都对应同一个Class文件, 就可以证明: 集合在运行时, 泛型被擦除.

package com.reflect;

import java.lang.reflect.Method;
import java.util.ArrayList;

public class ArrayListAdd {
    public static void main(String[] args) throws Exception{
        /*
        验证不同集合是调用同一个Class文件
        * */
        ArrayList<String> arrayList1 = new ArrayList<>();
        ArrayList<Integer> arrayList2 = new ArrayList<>();

        System.out.println(arrayList1.getClass());
        System.out.println(arrayList2.getClass());
        System.out.println(arrayList1.getClass() ==  arrayList2.getClass());

        System.out.println("================");
//        通过反射向泛型集合中强行注入其他类型元素
        ArrayList<Integer> list3 = new ArrayList<>();
        list3.add(13);
        list3.add(17);
        Class c = list3.getClass();
//        定位集合的add方法
        Method addString = c.getDeclaredMethod("add",Object.class);
//        运行add方法
//        add方法的返回值是一个Boolean, 用来反映是否添加成功
        Boolean rs = (Boolean) addString.invoke(list3,"嘿嘿嘿");
        System.out.println(rs);
        System.out.println(list3);
    }
}

其实, 还有一个简单的方法, 来向集合中添加其他类型元素

ArrayList list4 = list3;
list4.add(false);
list4.add("哈哈哈");
System.out.println(list3);
反射做通用框架的底层原理

后期接触到框架再说

参考视频:
作者:黑马程序员
https://www.bilibili.com/video/BV1Cv411372m?p=188&vd_source=46e82c24af5db4922a4bbca7090ecda3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值