Java反射

本文详细介绍了Java反射的概念、获取Class的不同方式、反射实例化、Field和Method的使用、Modifier的作用,以及反射对泛型的影响。强调了反射的动态性、灵活性和可扩展性,同时也讨论了其性能成本和封装破坏的问题。

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

目录

一:什么是反射?

二.Class类

1.获取Class的三种方式

1.类名.class

2.对象.getClass()

3.Class.forName(全限定类名)

2.反射实例化对象

三、反射Filed类

1.获取所有字段

2.获取单个字段

3.设置字段私有可访问(打破封装)

4.设置字段属性

5.获取字段属性值

6.获取字段属性名称

7.获取字段类型

8.获取字段修饰符:

9.综合案例题

四、反射Method类

1.获取类中的方法有以下方法

2.拿到方法返回类型

3.拿到方法名称

4.拿到方法修饰符

5.拿到方法里的所有参数

6.设置私有方法可访问

7.调用方法

8.综合案例:

五.Modifier类

1.getModifiers():

2.Modifier.toString(修饰符常量值) :

3.isXxx(修饰符常量值):

六、反射破坏泛型案例

七、总结


一:什么是反射?

        Java反射是指在运行时动态地获取和操作类的成员(属性、方法、构造方法等)的能力。通过使用反射,我们可以在运行时通过类名获取类的信息,如类的字段、方法等,并且可以在运行时动态地调用这些方法和字段,而不需要事先知道类的具体信息。

       Java反射在很多框架和库中被广泛使用,如Spring、Hibernate等。它能够在运行时动态地生成对象和调用方法,使得程序的扩展和修改更加方便。但是,由于反射操作相比直接调用方法和访问字段的性能较低,所以在性能要求较高的场景中应谨慎使用。

优点:

1.动态性:通过反射,可以在运行时动态地获取类的信息,并且可以动态地创建对象、调用方法、访问属性等。这种动态性使得程序可以适应不同的情况和需求,提高了代码的灵活性和可扩展性。

2.增强代码的灵活性:反射使得代码可以在运行时根据需要进行动态的操作,可以避免在编译时确定所有使用的具体类和方法,从而使得代码更加灵活,可以根据需要进行动态的调整和改变。

3.提高代码的可扩展性:通过反射,可以在运行时动态地创建对象,可以根据需要动态地加载和卸载类。这种可扩展性使得程序可以动态地适应不同的需求和变化,从而提高了代码的可扩展性。

缺点:

1.性能影响:使用反射会导致性能下降,因为反射调用需要动态解析方法或字段,这比直接调用静态方法或字段的速度要慢。

2.破坏了封装:使用反射可以访问和修改私有字段和方法,这可能会绕过访问控制的安全机制,导致潜在的安全问题。

3.缺乏编译时检查:反射调用的参数类型和方法签名是在运行时确定的,而不是在编译时检查的,这可能导致类型不匹配和方法调用错误的问题。

二.Class类

介绍:Class也是一个类,每个对象都会有一个Class对象,反射骚操作的前提就是要拿到对象的Class对象才能进行操作。

1.获取Class的三种方式

1.类名.class

Class<Object> objectClass = Object.class;

2.对象.getClass()

Object object=new Object();

Class<?> aClass = object.getClass();

3.Class.forName(全限定类名)

最经典的就是我们写jdbc时候的加载驱动了

Class.forName("com.mysql.jdbc.cj.Driver");

2.反射实例化对象

//通过  类名.class  获取了Object的class对象
Class<Object> objectClass = Object.class;

//通过newInstance方法新建实例,不过这个方法在9版本已经被弃用
Object object1 = objectClass.newInstance();

//推荐构造方法new对象
Object object2=objectClass.getDeclaredConstructor().newInstance();

//接下来就可以调用通过 object2 调用object的方法了

。。。

三、反射Filed类

这个类用于获取类中的字段信息以及访问这些字段的能力

反射常用Api介绍:

1.获取所有字段

//获取Integer的Class对象
Class<Integer> integerClass = Integer.class;

//获取Integer对象的所有字段
Field[] declaredFields1 = integerClass.getDeclaredFields();
//只能获取修饰符为public的所有字段
Field[] declaredFields2 = integerClass.getFields();

2.获取单个字段

//获取Integer的Class对象
Class<Integer> integerClass = Integer.class;
//通过字段名获取单个字段
Field field1 = integerClass.getDeclaredField("字段名称");
//只能获取修饰符为public的字段
Field field2 = integerClass.getField("字段名称");

3.设置字段私有可访问(打破封装)

Field对象.setAccessible(true);

4.设置字段属性

field对象.set(对象,值)

5.获取字段属性值

field对象.get(对象)

6.获取字段属性名称

field对象.getName()

7.获取字段类型

两者区别:根据字段类型来说,getType返回的是类似java.lang.String类型,而getType().getSimpleName()就直接返回String

field对象.getType()

field对象.getType().getSimpleName()

8.获取字段修饰符:

Modifier.toString(declaredField.getModifiers())

9.综合案例题

一、遍历Integer类中所有的字段,格式如下所示
public final class Integer {
public static final int MIN_VALUE;
public static final int MAX_VALUE;
public static final Class TYPE;
static final char[] digits;
......
}

答案

public static void main(String[] args) {

        //获取Integer的Class对象
        Class<Integer> integerClass = Integer.class;
        //获取Integer对象的所有字段
        Field[] declaredFields = integerClass.getDeclaredFields();
        //遍历字段
        for (Field declaredField : declaredFields) {

            //拿到字段类型
            String simpleName = declaredField.getType().getSimpleName();
            //拿到字段名称
            String name = declaredField.getName();
            //拿到字段修饰符
            String declaredFieldModifier = Modifier.toString(declaredField.getModifiers());
            //STR是jdk高版本语法也是字符串拼接的意思
            System.out.println(STR." \{declaredFieldModifier} \{simpleName} \{name}");
        }
    }

二、字段操作案例

// 第一:获取Student类的Class对象
Class<?> clazz = Student.class;

// 第二:创建Student对象的实例
Object obj = clazz.getConstructor().newInstance();

// 第三:获取Student类中的name字段
Field nameFidld = clazz.getDeclaredField("name");
Field PIFidld = clazz.getDeclaredField("PI");

// 注意:私有的字段,必须开放访问权限
nameFidld.setAccessible(true);
PIFidld.setAccessible(true) ;

// 第四:赋值
nameFidld.set(obj, "张三");
//PIFidld.set(obj, 3.14159) ; //常量不能重新赋值

// 第五:取值
Object name = nameFidld.get(obj);
Object pi = PIFidld.get(obj) ;

System.out.println(name);//输出 张三
System.out.println(pi);

四、反射Method类

这个类用于获取类中的方法的信息以及访问这些方法的能力

和字段方法大致相同

常用API介绍:

1.获取类中的方法有以下方法

// 1获取类中所有的公共方法,返回Method数组
Method[] methods = Class对象.getMethods() ;

// 2获取为中所有的方法(包括私有),返回Method数组
Method[] methods = Class对象.getDeclaredMethods() ;

// 3返回某个指定的公共方法
Method method = Class对象.getMethod("方法名") ;

// 4返回某个指定的方法(包括私有)
Method method = Class对象.getDeclaredMethod("方法名")  ;

2.拿到方法返回类型

Method.getReturnType().getSimpleName()

3.拿到方法名称

Method.getName()

4.拿到方法修饰符

String methodModifier = Modifier.toString(Method.getModifiers());

5.拿到方法里的所有参数

Parameter[] parameters = declaredMethod.getParameters();

//拿到参数类型
parameter.getType().getSimpleName()
//拿到参数名称(这里只会是arg0,arg1 ....)
parameter.getName()

6.设置私有方法可访问

Method.setAccessible(true) ;

7.调用方法

Method.invoke(实例对象,参数...)

8.综合案例:

第一题:

循环遍历String类中所有的方法,格式如下所示 

public boolean equals(Object arg0)

public String toString()

public int hashCode()

...

答案:

public static void main(String[] args) {
      
        //拿到String类的Class对象
        Class<String> stringClass = String.class;

        //拿到String类对象的所有方法
        Method[] declaredMethods = stringClass.getDeclaredMethods();

        //遍历方法
        for (Method declaredMethod : declaredMethods) {

            //拿到方法返回类型
            String simpleName1 = declaredMethod.getReturnType().getSimpleName();

            //拿到方法名称
            String declaredMethodName = declaredMethod.getName();

            //拿到方法修饰符
            String methodModifier = Modifier.toString(declaredMethod.getModifiers());

            //拿到所有参数(修饰符,方法名称,参数类型...)
            Parameter[] parameters = declaredMethod.getParameters();

            // 定义存储参数字符串的StringBuilder对象
            StringBuilder paramStrBuilder = new StringBuilder();

            // 遍历参数
            for (Parameter parameter : parameters) {

                // 拼接参数类型和参数名称
                String param = STR."\{parameter.getType().getSimpleName()} \{parameter.getName()}";

                paramStrBuilder.append(param).append(", ");
            }

            // 去掉最后一个逗号和空格
            String paramStr = paramStrBuilder.toString();

            if (!paramStr.isEmpty()) {

                paramStr = paramStr.substring(0, paramStr.length() - 2);
            }

            // 输出方法信息
            System.out.println(STR."\{methodModifier} \{simpleName1} \{declaredMethodName}(\{paramStr})");
        }
    }

第二题:

获取类的构造方法,实例化对象,调用show方法,显示学生信息

public Student(String name,int age,Double weight)

答案:

//拿到Student的Class对象
 Class<Student> studentClass = Student.class;

//实例化对象
Student student =studentClass.getDeclaredConstructor(String.class, int.class, Double.class)
                             .newInstance("张三", 18, 90.5);
//反射获取show方法
Method show = studentClass.getDeclaredMethod("show");

//调用方法
show.invoke(student);

五.Modifier类

        由于上面字段与方法,都用到了Modifier类,因为Modifier这个类存放了所有修饰符的常量值,以及通过这些常量值判断是哪种修饰符

1.getModifiers():

返回修改符的常量值(int)

2.Modifier.toString(修饰符常量值) :

返回修饰符常量值对应的字符串表示

3.isXxx(修饰符常量值):

判断修饰符是否为Xxx

int modifier = Class对象.getModifiers() ;

int modifider = Field对象. getModifiers() ;

int modifider = Method对象. getModifiers() ;

int modifider = Constructor对象. getModifiers() ;

// ...

以上返回的是修饰符对应的整型常数,可以通过Modifier.toString(修饰符常量值)转为字符串表示。

//public class Student
Class<?> clazz = Student.class ;
Constructor<?> ct = clazz.getConstructor() ;
int mod = ct.getModifiers() ;

//输出:1
System.out.println(mod);

//输出:true
System.out.println(Modifier.isPublic(mod));

//输出:public
System.out.println(Modifier.toString(mod));

六、反射破坏泛型案例

        List<String> list = new ArrayList<String>();

        // 第一:获取List对象的Class对象
        Class<?> listClass = list.getClass();

        // 第二:获取List对象的add方法的Method对象
        Method addMethod = listClass.getMethod("add", Object.class);

        // 第三:执行add方法
        addMethod.invoke(list, "AAA");
        addMethod.invoke(list, 100);
        addMethod.invoke(list, true);

        // 第四:输出[AAA, 100, true]
        System.out.println(list);

      我们知道,泛型String的List集合应该是只能存字符串,其他都会报错(编译时报错),反射就不会编译时报错,他只在运行时有效,所以反射的不好的点就体现出来了,破坏了泛型约束

七、总结

       Java反射是指在运行时动态获取类的信息并操作类的方法和属性的能力。它可以在运行时动态地获取类的信息(包括类名、属性、方法等)并进行相关的操作,比如创建对象、调用方法、修改属性值等。

Java反射的主要用途有:

  1. 动态创建对象:使用反射可以在运行时动态地创建类的对象,而不需要在编译时知道类的具体信息。

  2. 获取类的信息:使用反射可以获取类的信息,比如类的名称、继承关系、接口信息、方法信息等。

  3. 调用方法和修改属性:使用反射可以调用类的方法和修改类的属性,即使方法和属性是私有的。

  4. 动态代理:使用反射可以实现动态代理,即在运行时动态地生成代理类并在代理类中实现对目标对象的调用。

使用Java反射需要使用Java.lang.reflect包中的相关类,主要包括以下几个重要的类和接口:

  1. Class类:代表一个类的信息,可以获取类的名称、方法、属性等信息。

  2. Constructor类:代表一个构造方法的信息,可以获取构造方法的参数、修饰符等信息。

  3. Method类:代表一个方法的信息,可以获取方法的名称、参数、返回值等信息。

  4. Field类:代表一个字段的信息,可以获取字段的名称、类型、修饰符等信息。

使用反射的步骤大致如下:

  1. 获取类的Class对象:可以使用类的.class属性或者Class.forName()方法来获取Class对象。

  2. 获取类的构造方法:可以使用Class对象的getConstructor()方法或者getConstructors()方法来获取类的构造方法。

  3. 创建对象:通过构造方法对象的newInstance()方法来创建类的对象。

  4. 访问方法和字段:可以使用Class对象的getMethod()方法或者getMethods()方法来获取类的方法,使用Field类的get()和set()方法来获取和设置类的字段。

OK,今天笔记到处结束,哪里有错误请指出谢谢~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值