害不懂反射吗

反射

1.1 框架

半成品软件。可以在框架的基础上进行软件开发,简化编码。学习框架并不需要了解反射,但是要是想自己写一个框架,那么就要对反射机制有很深入的了解。

1.2 反射

反射机制:将类的各个组成部分封装为其他对象,这就是反射机制。

反射的好处:

  • 1.可以在程序运行过程中,操作这些对象。
  • 2.可以解耦,提高程序的可扩展性。

Java代码在计算机中经历的三个阶段

  • (1)Source源代码阶段:.java被编译成.class字节码文件。
  • (2)Class类对象阶段:*.class字节码文件被类加载器加载进内存,并将其封装成Class对象(用于在内存中描述字节码文件),Class对象将原字节码文件中的成员变量抽取出来封装成数组Field[],将原字节码文件中的构造函数抽取出来封装成数组Construction[],在将成员方法封装成Method[]。当然Class类内不止这三个,还封装了很多,我们常用的就这三个。
  • (3)RunTime运行时阶段:创建对象的过程new。
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n7Usa4sS-1665120914129)(C:\Users\宇宙电击杀\AppData\Roaming\Typora\typora-user-images\image-20220718170902846.png)]

获取Class对象的方式

2.1 获取Class对象的三种方式对应着java代码在计算机中的三个阶段

(1)【Source源代码阶段】 Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象 * 多用于配置文件,将类名定义在配置文件中。 读取文件,加载类。

(2)【Class类对象阶段】类名.class:通过类名的属性class获取 多用于参数的传递

(3)【Runtime运行时阶段】对象.getClass():getClass()方法是定义在Objec类中的方法 多用于对象的获取字节码的方式

结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,无论通过哪一种方式获取的Class对象都是同一个。

在getDeclaredFiled方法中:可以获取所有成员变量,包括私有。以前说私有东西不可以在外面被访问到,在反射中

私有变量可以通过反射获取,如果设置私有值需要通过setAccessible(true)。

public void reflect1() throws ClassNotFoundException {
    //方式一:Class.forName("全类名");
    Class cls1 = Class.forName("com.test.domain.Person");   //Person自定义实体类
    System.out.println("cls1 = " + cls1);

    //方式二:类名.class
    Class cls2 = Person.class;
    System.out.println("cls2 = " + cls2);

    //方式三:对象.getClass();
    Person person = new Person();        
    Class cls3 = person.getClass();
    System.out.println("cls3 = " + cls3);

    // == 比较三个对象
    System.out.println("cls1 == cls2 : " + (cls1 == cls2));    //true
    System.out.println("cls1 == cls3 : " + (cls1 == cls3));    //true
    //结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,无论通过哪一种方式获取的Class对象都是同一个。
}

Class对象功能

(1)获取成员变量们

Field[] getFields() :获取所有public修饰的成员变量
Field getField(String name) 获取指定名称的 public修饰的成员变量

Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
Field getDeclaredField(String name)

  • (1)设置值 void set(Object obj, Object value)
  • (2)获取值 get(Object obj)
  • (3)忽略访问权限修饰符的安全检查 setAccessible(true):暴力反射
/**
 * 1. 获取成员变量们
 *     * Field[] getFields()
 *     * Field getField(String name)
 * @throws Exception
 */
@Test
public void reflect2() throws Exception {
    //0、获取Person的Class对象
    Class personClass = Person.class;

    //1、Field[] getFields()获取所有public修饰的成员变量
    Field[] fields = personClass.getFields();
    for(Field field : fields){
        System.out.println(field);
    }
    System.out.println("=============================");
    //2.Field getField(String name)
    Field a = personClass.getField("a");

    //获取成员变量a 的值 [也只能获取公有的,获取私有的或者不存在的字符会抛出异常]
    Person p = new Person();
    Object value = a.get(p);
    System.out.println("value = " + value);

    //设置属性a的值
    a.set(p,"张三");
    System.out.println(p);
}
/**
 *     Field[] getDeclaredFields()
 *     Field getDeclaredField(String name)
 * @throws Exception
 */
@Test
public void reflect3() throws Exception {
    Class personClass = Person.class;

    //Field[] getDeclaredFields():获取所有的成员变量,不考虑修饰符
    Field[] declaredFields = personClass.getDeclaredFields();
    for(Field filed : declaredFields){
        System.out.println(filed);
    }
    System.out.println("===================================");
    //Field getDeclaredField(String name)
    Field d = personClass.getDeclaredField("d");     //private String d;
    Person p = new Person();

    //Object value1 = d.get(p);    //会抛出异常
    //System.out.println("value1 = " + value1);    //对于私有变量虽然能会获取到,但不能直接set和get

    //忽略访问权限修饰符的安全检查
    d.setAccessible(true);//暴力反射
    Object value2 = d.get(p);
    System.out.println("value2 = " + value2);
    
    d.set(person,"ddd");
        System.out.println(person);
    
    
}

(2)获取构造方法

getConstructor

get Constructors

getDeclaredConstructor

getDeclaredConstructors

创建对象:newInstance(Object initargs)

如果使用空参构造方法创建对象,操作可以简化:Class对象的newInstance

构造器也有setAccessible方法,暴力反射

//getConstructor

		Class personClass = Person.class;
        //有参构造方法
        Constructor constructor = personClass.getConstructor(String.class, int.class);
        System.out.println(constructor);
        //创建对象
        Object a = constructor.newInstance("a", 4);
        System.out.println(a);
        System.out.println("--------");
        //空参构造方法
        Constructor constructor1 = personClass.getConstructor();
        Object o = constructor1.newInstance();
        System.out.println(o);
        //使用Class对象的newInstance,可以省略Class对象的getConstructor方法
          Object o1 = personClass.newInstance();

获取方法

getmethod

getmethods

getDeclaredMethod

getDeclaredmethods

method.getname//获取方法名字

执行方法 invoke(Object obj,args)

获取方法名:method.getname

		Class personClass = Person.class;
        //获取无参方法
        Method toeat = personClass.getMethod("toeat");//无参方法参数只有方法名
        Person person = new Person();
        //执行方法:
        toeat.invoke(person);
        //获取有参方法
        //第一个参数为方法名,第二个参数为参数类型.class
        Method toeat1 = personClass.getMethod("toeat", String.class);
        toeat1.invoke(person,"oo");//第一个参数为实体类,第二个参数为参数
        //获取多个方法
        Method[] methods = personClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
            //获取方法名
            String name = method.getName();
            System.out.println(name);
        }

简单框架

 		Properties properties = new Properties();
        //获取类加载器以加载配置文件
        ClassLoader classLoader = ReflectTest.class.getClassLoader();
        //类加载器加载配置文件
        InputStream is = classLoader.getResourceAsStream("pro.properties");
        //pro对象load配置文件流
        properties.load(is);
        //通过配置文件获取类路径名、方法名
        String classname = properties.getProperty("classname");
        String methodName = properties.getProperty("methodName");
        //通过类路径名获取class对象
        Class aClass = Class.forName(classname);
        //通过class对象创建对象,使用空参构造方法创建对象,操作可以简化:Class对象的newInstance
        Object o = aClass.newInstance();
//        //使用class对象得到方法。
        Method method = aClass.getMethod(methodName);
        //invoke方法,传入实体类对象
        method.invoke(o);

好处

我们这样做有什么好处呢,对于框架来说,是人家封装好的,我们拿来直接用就可以了,而不能去修改框架内的代码。但如果我们使用传统的new形式来实例化,那么当类名更改时我们就要修改Java代码,这是很繁琐的。修改Java代码以后我们还要进行测试,重新编译、发布等等一系列的操作。而如果我们仅仅只是修改配置文件,就来的简单的多,配置文件就是一个实实在在的物理文件。

此外使用反射还能达到解耦的效果。假设我们使用的是new这种形式进行对象的实例化,此时如果在项目的某一个小模块中我们的一个实例类丢失了,那么在编译期间就会报错,以导致整个项目无法启动。而对于反射创建对象Class.forName(“全类名”);这种形式,我们在编译期需要的仅仅只是一个字符串(全类名),在编译期不会报错,这样其他的模块就可以正常的运行,而不会因为一个模块的问题导致整个项目崩溃。这就是Spring框架中IOC控制反转的本质。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值