【回炉重造】——反射

本文探讨了Java的反射机制,解释了反射的定义及重要性。内容包括通过反射获取构造器、属性、方法和类信息,以及相关类如Class、Field、Method和Constructor的使用。同时,文章提出了两个关于反射的问题:一是创建对象时使用new关键字还是反射,并建议通常情况下使用new以保持静态初始化;二是讨论反射是否破坏封装性,指出反射虽能访问私有成员,但不建议这样做,因此与封装性并不冲突。

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

首先,看一下反射最重要的四句代码:

package com.wu.test01;

/**
 * @author wu
 * @version 2022/1/18 10:35 PM
 */
public class AliPay implements Mtwm {
    @Override
    public void payOnline() {
        //具体的支付宝支付:
        System.out.println("我已经点了外卖,我正在使用支付宝进行支付" );
    }
}


public static void main(String... args) throws Exception {
        //定义一个字符串,用来模拟前台的支付方式:
        String str = "com.wu.test01.AliPay";

        //下面的代码就是利用反射
        Class cls = Class.forName(str);
        Object o = cls.newInstance();
        Method method = cls.getMethod("payOnline");
        method.invoke(o);
}

什么是反射?

然后,反射的定义是:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能成为java语言的反射机制。

反射可以用来做什么

反射所体现的就是一种动态性,就像一个镜子,通过反射的api获取一个类内部的所有内部信息。这也是我认为它叫反射的一个含义。

1、获取构造器和创建对象

public static void main(String... args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //获取字节码信息
        Class cls = Student.class;

        //通过字节码信息可以获取构造器
        //getConstructors只能获取当前运行时类的被public修饰的构造器
        Constructor[] c1 = cls.getConstructors();
        for (Constructor c : c1) {
            System.out.println(c);
        }

        System.out.println("------------------");
        //getDeclaredConstructors:获取运行时类的全部修饰符的构造器
        Constructor[] c2 = cls.getDeclaredConstructors();
        for(Constructor c:c2){
            System.out.println(c );
        }
        System.out.println("------------------");

        //获取指定的构造器
        //得到空构造器
        Constructor con1 = cls.getConstructor();
        System.out.println( con1);

        //得到两个参数的有参构造器
        Constructor con2 = cls.getConstructor(double.class, double.class);
        System.out.println(con2);

        //得到一个参数的有参构造器:并且是private修饰的
         Constructor con3 = cls.getDeclaredConstructor(int.class);
        System.out.println(con3);

        //有了构造器以后我就可以创建对象
        Object o1 = con1.newInstance();
        System.out.println(o1);

        Object o2 = con2.newInstance(180.5, 170.6);
        System.out.println(o2);
    }

2、获取属性和对属性赋值

public static void main(String... args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        //获取运行时类的字节码信息
        Class cls = Student.class;
        //获取属性
        //getFields:获取运行时类和父类中被public修饰的属性
        Field[] fields = cls.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("--------------");

        Field[] declaredFields = cls.getDeclaredFields();
        for (Field field : declaredFields) {
            System.out.println(field);
        }
        System.out.println("--------------");

        //获取指定的属性
        Field score = cls.getField("score");
        System.out.println(score);
        Field sno = cls.getDeclaredField("sno");
        System.out.println(sno);
        System.out.println("--------------");

        //属性的具体结构:
        //获取修饰符
        int modifiers = sno.getModifiers();
        System.out.println(modifiers);
        System.out.println(Modifier.toString(modifiers));
        System.out.println(Modifier.toString(sno.getModifiers()));
        //获取属性的数据类型
        Class clazz = sno.getType();
        System.out.println(clazz.getName());
        //获取属性的名字
        String name = sno.getName();
        System.out.println(name);
        System.out.println("--------------");
        //给属性赋值:(给属性设置值,必须要有对象)
        Field sco = cls.getField("score");
        Object obj = cls.newInstance();
        sco.set(obj, 98);
        System.out.println(obj);
    }

3、获取方法和调用方法

public static void main(String... args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        //获取字节码信息
        Class cls = Student.class;
        //获取方法:
        //getMethods:获取运行时类的方法还有所有父类中的方法(被public修饰)
        Method[] methods = cls.getMethods();
        for (Method m : methods) {
            System.out.println(m);
        }
        System.out.println("----------------" );
        //:获取运行时类中的所有方法
        Method[] declaredMethods = cls.getDeclaredMethods();
        for (Method m : declaredMethods) {
            System.out.println(m);
        }
        System.out.println("----------------");
        //获取指定的方法:
        Method showInfo1 = cls.getMethod("showInfo");
        System.out.println(showInfo1);
        Method showInfo2 = cls.getMethod("showInfo", int.class, int.class);
        System.out.println(showInfo2);
        Method work = cls.getDeclaredMethod("work",int.class);
        System.out.println(work);
        System.out.println("----------------");

        //获取方法的具体结构:
        /*
        @注解
        修饰符 返回值类型 方法名(参数列表) throws XXXX{}
         */
        //名字
        System.out.println(work.getName());
        //修饰符
        int modifiers = work.getModifiers();
        System.out.println(Modifier.toString(modifiers));
        //返回值
        System.out.println(work.getReturnType());
        //参数列表
        Class[] parameterTypes = work.getParameterTypes();
        for (Class c:parameterTypes){
            System.out.println(c);
        }

        //获取注解
        Method myMethod = cls.getMethod("myMethod");
        Annotation[] annotations=myMethod.getAnnotations();
        for (Annotation a:annotations){
            System.out.println(a);
        }

        //获取异常
        Class[] exceptionTypes = myMethod.getExceptionTypes();
        for (Class c:exceptionTypes){
            System.out.println(c);
        }

        //调用方法
        Object o=cls.newInstance();
        myMethod.invoke(o);
        System.out.println(showInfo2.invoke(o,12,45));
    }

4、获取类的接口,所在包,注解

public static void main(String... args) {
        //获取字节码信息
        Class cls = Student.class;
        //获取运行时类的接口
        Class[] interfaces = cls.getInterfaces();
        for (Class c : interfaces) {
            System.out.println(c);
        }

        //得到父类的接口
        //先得到父类的字节码信息
        Class superclass = cls.getSuperclass();

        //得到接口
        Class[] interfaces1 = superclass.getInterfaces();
        for (Class c : interfaces1) {
            System.out.println(c);
        }

        //获取运行时类所在的包
        Package aPackage = cls.getPackage();
        System.out.println(aPackage);
        System.out.println(aPackage.getName());

        //获取运行类的注解
        Annotation[] annotations = cls.getAnnotations();
        for (Annotation a : annotations) {
            System.out.println(a);
        }
    }

反射相关类

Class类

方法说明
getClassLoader()获得类的加载器
forName(String className)根据类名返回类的对象
getName()获得类的完整路径名字
newInstance()创建类的实例
getPackage()获得类的包
getSuperclass()获得当前类继承的父类的名字
getInterfaces()获得当前类实现的类或是接口

Field类

方法说明
getField(String name)获得公有的属性对象
getFields()获得所有公有的属性对象
getDeclaredFields()获得所有属性对象
getDeclaredField(String name)获得某个属性对象

Method类

方法说明
getAnnotation(Class<A> annotationClass)返回该类中与参数类型匹配的公有注解对象
getAnnotations()返回该类所有的公有注解对象
getDeclaredAnnotation(Class<A> annotationClass)返回该类中与参数类型匹配的所有注解对象
getDeclaredAnnotations()返回该类所有的注解对象

Constructor类

方法说明
getConstructor(Class...<?> parameterTypes)获得该类中与参数类型匹配的公有构造方法
getConstructors()获得该类的所有公有构造方法
getDeclaredConstructor(Class...<?> parameterTypes)获得该类中与参数类型匹配的构造方法
getDeclaredConstructors()获得该类所有构造方法

反射通过字节码信息获取类,获取公共方法属性的,还可以获取私有、保护等信息。那么就引出两个问题:

两个问题

1、创建一个对象,是用new Person() 还是利用反射?

建议使用new的方式,因为这样在编译的时候就确定了要初始化的对象,而不是等到运行时。那我们什么时候用反射呢?就是不确定要new哪个对象的时候,在运行时来决定,这就体现了反射的动态性。

2、反射是否破坏了封装性?

通过上面的反射访问一个类的内部私有属性,方法等,貌似与封装性想违背,封装性有私有和共有来保证代码的安全性,在外部调用共有方法。反射最大的特点是动态性,解决的是能不能调用的问题,可以调用私有方法,但是平时不建议调用。所以两者并不冲突。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值