不了解java反射机制,怎么看懂框架源码

一、理解反射的基础(重要指数五颗星爆表)

1.Class文件

为什么有

  • 实现跨平台,一次编译,到处运行
  • 一次编译后的字节码文件(class文件),可以在安装了java虚拟机的各个操作系统上运行

是什么

  • java源代码编译后的字节码文件(.class)
  • 任何一个Class文件都对应唯一的一个类或接口的定义信息

注意:

  • Class文件应当看作是一串二进制字节流(画外音:记住这句话,切记)
  • 包括但不限于磁盘文件、网络、数据库、内存或动态产生

2.Java虚拟机的类加载机制

类加载就是程序执行时,加载Class文件的

类加载机制包括5个阶段:

  • 加载—>验证—>准备—>解析—>初始化(画外音:知道即可,暂不需要)
  • 重要的是类加载机制的加载阶段

图解类加载的加载阶段(太重要了,老铁,好好看)

如:实例化一个A类时
先将A类编译成A.class文件,放在主机硬盘上
当执行 A a = new A(),步骤在下面
在这里插入图片描述
步骤如下:对应上图序号

  • 1.类加载器(classLoad)通过一个类的全限定类名在主机硬盘找到A.class文件
  • 2.转换为A字节码:将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构(包含字段方法等)
  • 3.生成A的class对象:在堆中生成一个代表这个类的java.lang.Class对象,可以通过这个对象访问方法区的A字节码(字段方法等)

假如不通过new对象的方式,直接拿到A的class对象是不是就可以操作A对象的字段方法呢,答案是肯定的,这个就是反射

二、反射——框架设计的灵魂

2.1是什么

  • java的反射机制就是在程序运行过程中,动态调用对象的属性和方法。

用上面的图翻译:

  • java的反射机制就是在程序运行过程中,拿到类的字节码文件对应的class对象,从而访问属性方法的过程

2.2与反射相关的类

相关类 含义
java.lang.Class 对象代表一个字节码文件,一个类
java.lang.reflect.Constructor 对象代表字节码中构造方法,类中的构造方法
java.lang.reflect.Field 对象代表字节码中的属性字节码,类中属性
java.lang.reflect.Method 对象代表字节码中的方法,类中的方法

总结:JAVA反射将类的各个部分映射成各个部分对应的对象

三、反射的使用

3.1获得Class对象的三种方式

再写一次:Class对象对应某个类

1.Class.forName()静态方法
使用格式

  • 参数是一个字符串
  • 字符串是一个类的全限定类名,包括包名
Class c1 = Class.forName("java.lang.String");

说明:

  • c1(Class对象)对应String类
  • java.lang.String就是String类的全限定类名
  • 此方式在反射中最常用

2.对象的getClass方法

String s = "abc";
Class x = s.getClass();

说明

  • x(Class对象)对应String类
  • s的getClass方法继承于Object
  • 此方法不常用,因为对象已经出来了,还要反射去调用对象方法嘛,直接用对象调用就可以

3.class属性

Class a = String.class

说明

  • a(Class对象)对应String类
  • 此方法不常用,因为使用时需要导入相关的类,依赖性强

3.2反射调用构造方法并使用

Vip类

public class Vip {
    int no;
    String name;
    //无参构造方法
    public Vip(){}
    
    //有参构造方法
    public Vip(int no, String name) {
        this.no = no;
        this.name = name;
    }
    
	//重写toString()方法
    @Override
    public String toString() {
        return "Vip{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }
}

测试类

    public static void main(String[] args) throws Exception {
        //不使用反射机制创建对象
        Vip v1 = new Vip();
        Vip V2 = new Vip(110,"zhangsan");

        //使用反射机制创建对象
        Class c = Class.forName("com.thinkcoder.java.bean.Vip");
        
        //调用无参构造
        Object obj = c.newInstance();
        System.out.println(obj);//Vip{no=0, name='null'}
        
        //调用有参构造方法
        //第一步:先获取构造方法
        Constructor con = c.getDeclaredConstructor(int.class,String.class);
        //第二步: 调用构造方法new对象
        Object newObj = con.newInstance(111,"lisi");
        System.out.println(newObj);//Vip{no=111, name='lisi'}
    }

说明

  • 1.java.lang.reflect.Constructor类的对象代表一个类的构造函数
  • 2.Constructor类的newInstance方法可以调用这个类的构造函数创建此类对象

3.3反射获取属性并赋值

Student类

public class Student {
    //属性采用不同的访问修饰符
    public int no;  
    private String name;
}

测试类

    public static void main(String[] args) throws Exception {
        //反射机制获取一个类
        Class  studentClass = Class.forName("com.thinkcoder.java.bean.Student");
        Object obj = studentClass.newInstance();//obj代表Student对象

        //获取公开属性no
        Field noField = studentClass.getDeclaredField("no");

        //给obj对象的name属性赋值
        /*
        * 给属性赋值三要素
        *   1.obj对象
        *   2.no属性
        *   3.值为1
        * */
        noField.set(obj,1);
        System.out.println(noField.get(obj));//1

        //获取私有属性name
        Field nameField = studentClass.getDeclaredField("name");

        //打破封装,使私有属性可以访问
        nameField.setAccessible(true);
        nameField.set(obj,"zhangsan");
        System.out.println(nameField.get(obj));//zhangsan
    }

说明

  • 1.java.lang.reflect.Field类对象代表一个类的属性
  • 2.Field中的get(),set()方法可以获取一个类的属性并赋值
  • 3.set和get方法只能访问公开属性,访问私有属性需要setAccessible方法打破封装访问

3.4反射调用方法

com.thinkcoder.java.service.UserService类

public class UserService {
	//实现简单登录验证
    public boolean login(String name,String pwd){
        if("admin".equals(name) && "123".equals(pwd)){
            return true;
        }
        return false;
    }
}

测试类

    public static void main(String[] args) throws Exception {
        //不使用反射机制调方法
        //UserService userService = new UserService();
        //userService.login("admin","123");

        //使用反射机制调用方法
        Class userService = Class.forName("com.thinkcoder.java.service.UserService");
        //创建对象
        Object obj = userService.newInstance();
        //获取method
        Method method =  userService.getDeclaredMethod("login", String.class, String.class);
        //调用方法用invoke
        /*
        * 要素1:对象obj
        * 要素2:方法名method
        * 要素3:实参列表"admin""123"
        * 要素4:返回值retValue
        * */
        Object retValue= method.invoke(obj,"admin","123");
        System.out.println(retValue);//true
    }

说明

  • 1.java.lang.reflect.Method类对象代表一个类中的方法
  • 2.Method类中的invoke方法调用这个类的方法

3.5总结

正如之上总结,java反射将类和其各个组成部分映射成各个对应的对象,通过对应的对象调用相关的属性方法

四、反射+配置文件

com.thinkcoder.java.service.UserService类

public class UserService {
	//实现简单登录验证
    public boolean login(String name,String pwd){
        if("admin".equals(name) && "123".equals(pwd)){
            return true;
        }
        return false;
    }
}

classinfo1配置文件内容

className=com.thinkcoder.java.service.UserService

测试类

public class ResouceBundleTest {
    public static void main(String[] args) {
        //资源绑定器,只能绑定xxx.propertities文件,写路径时,扩展名不能写
        ResourceBundle bundle = ResourceBundle.getBundle("classinfo1");

        String className = bundle.getString("className");
        System.out.println(className);//com.thinkcoder.java.service.UserService
    }
}

老铁当你获取了一个类的全限定类名之后,按照上边的操作,可以对该类各个组成部分进行操作
注意:

  • java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容
  • 使用以下这种方式时,属性配置文件xxx.properties必须放到类路径下,即src路径下

五、总结

反射为什么叫反射呢?
就是将一个类和其组成部分映射(反射)到java.reflect包下对应的类

完结,反射的内容大致如此,以后看到源码部分时,或者学习反射更多相关东西,将补充

原创文章 233 获赞 129 访问量 11万+

猜你喜欢

转载自blog.csdn.net/shang_0122/article/details/105862366