Java学习笔记之反射

什么是反射

反射简单来说就是根据配置文件生成相应的对象实例,在不修改源码的情况下,仅通过更改配置文件生成不同的对象。在反射中方法也视为对象。

假设有如下的配置文件:

ClassPath = com.cat

通过properties读取的配置文件,得到的返回类型是字符串,这里假设用变量A来接收字符串。此时A就是“com.Cat”这个字符串。但我们无法用new A();的方式创建cat对象,因为new后面需要跟类名而A只是一个保存了字符串的变量。所以通过properties读取配置文件我们只能隐性的得到类名对应的字符串,而无法明确类名是什么,也就无法创建对象。

同样的在调用方法时,并不知道方法名,只有一个保存了方法名的字符串变量。而反射机制就提供了可以通过“保存了方法名的变量”实例化方法的渠道(把方法看做对象),然后再通过方法的对象来调用方法。

代码写完先进行编译,然后在类加载时会自动生成一个class类型的类(类名就叫class)对应上图中间的。这个class类型的类保存了初始化类的所有完整信息,每个信息又可以看成一个单独的类。反射主要的类有:

java.long.class :此类实例化后代表,class类对应的类加载后的对象。

java.long.reflect.Methord:此类实例化后代表该反射对应类的某个方法。

java.long.reflect.Filed:此类实例化后代表反射对应类的成员变量。

java.long.reflect.Constructor:此类实例化后代表反射对应类的构造器。

class类相当于一面镜子,通过这个镜子可以看到原类的所有结构,然后通过它来创建对象、调用方法。一个类无论实例化多少个对象都只会产生一个class类型的类。

class类

class类不是new出来的是系统自动创建的。

每个对象实例都知道自己是哪个class类关联的。

class常用方法

其中创建对象实例方法在JDK18后弃用,变成cls.getDeclaredConstructor().newInstance();

获取对应class对象的方法(不同阶段不同方法)

方法一:多用于从配置文件读取了类的全路径后加载类

        直接用class的静态方法forName()获取:Class cls = Class.forName("全类名”);

方法二:已知具体类,直接通过类的Class获取,多用于参数传递,效率最高最安全可靠

        已知类Cat获取它的Class类:Class cls = Cat.class;

方法三:已知某个类的实例,直接调用该类的getClass()方法获取Class对象

        通过创建好的对象获取Class:Class cls = 对象.getClass();

方法四:通过类加载器获得Class对象,也需要类的全路径

        Class cls = classLoader.loadClass("类的全路径”)

无论获取多少个Class对象其实都是同一个Class对象。

通过反射获取类相关信息的方法

通过反射创建对象

第一步先获取类对应的Class对象。

法一:调用类中public修饰的无参构造器。

法二:调用其他构造器

主要通过Class类相关方法以及Constructor类相关方法实现:

爆破:暴力破解,使用反射可以访问private修饰的东西。XXXX.setAccessible(true);


通过反射操作属性

首先根据属性名获取Field对象:

        Field f = class对象.getDeclaredField(属性名); //可以得到所有类型的属性

        如果是private类型的可以用爆破:f.setAccessible(true);

然后用拿到的属性类去访问/设置值:

        f.set(O,值) 其中O是通过反射创建的对象

        syso(f.get(O));

        如果是静态属性,则set和get中的O可以写成null。

通过反射操作方法

根据方法名和参数列表获取Methord方法对象

        Methord m = class.getDeclaredMethord(方法名,XXX.class);得到本类所有方法,XXX是形参类型

获取对象

        Object o = class.newInstance();

爆破

        m.setAccessible(true);

访问                  

        Object returnValue = m.invoke(O,实参列表);如果是静态方法O可以是null

应该运用实例:通过反射修改已知类的属性并调用方法输出

public class Homework16 {
    public static void main(String[] args) throws Exception {
        //拿到class对象
        Class aClass = Class.forName("Reflect.PrivateTest");
        //通过调用默认构造器class对象实例化
        Constructor constructor = aClass.getDeclaredConstructor();
        Object pt = constructor.newInstance();
        //通过class对象得到属性对象
        Field name = aClass.getDeclaredField("name");
        //爆破
        name.setAccessible(true);
        //设置属性
        name.set(pt,"abc");
        //通过class对象得到方法
        Method getName = aClass.getDeclaredMethod("getName");
        //实现方法
        Object invoke = getName.invoke(pt);
        System.out.println(invoke);

    }
}
class PrivateTest {
    private String name = "Helllo Kitty" ;
    public String getName(){
        return name;
    }
}

类加载

类加载分为静态加载和动态加载

一般的类,只要在代码中写了,无论运行代码时会不会用到该类在编译时都会加载,如果相关类有问题就会报错。这称为静态加载。如在switch某个分支写了new dog();这个语句但是没有定义dog这个类时,即使运行时不会走该分支在编译时也会报错。静态加载依赖性比较强

如果使用反射创建类而不是直接new,则会动态加载。即在代码运行时如果用到这个类了才会加载,编译时并不会直接加载。同样在switch语句某一个分支用反射实例化了一个dog类,只要运行时没走到这个分支也就不会用到dog类,也就不会报错。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值