IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 通俗的讲一下Java中的反射,简单易懂 -> 正文阅读

[Java知识库]通俗的讲一下Java中的反射,简单易懂

前言:

今天简单学习了下反射,B站弹幕都说听不懂,我研究了一下,发现听不懂的原因在于内容有点多,但是又无法分开学,另外一点就是老师讲的有点抽象,所以借晚上总结的时候用容易理解的语言解释一下,加深记忆。

什么是反射

定义

反射是指在程序运行过程中动态的获取类各部分的一种技术。详细一点就是将类的各个部分封装为其他的对象

解释

| 这两句话比较抽象,需要仔细分析一下,举一个例子:我们定义一个学生类Student,类中主要的部分是 属性,构造方法,普通方法。当然也有toString。这里我定义的时候 属性,构造方法,普通方法都用了不同访问修饰符,大家稍微注意一下,后面会用到,代码简单看一下就行,直接看后面文章

package cn.itcast.person;
/**
 * 学生类
 */
public class Student {
    //属性
    public String name;
    protected int age;
    String sex;
    private int height;

    /**
     * 构造方法
     */
    public Student() {
    }

    public Student(String name, int age, String sex, int height) {

        this.name = name;
        this.age = age;
        this.sex = sex;
        this.height = height;
    }
    private Student(String name){
        this.name = name;
    }

    //普通方法
    public void study() {
        System.out.println("study");
    }
    private void sleep(){
        System.out.println("sleep");
    }
    public void eat(int i ){
        System.out.println("吃了"+i+"碗饭");
    }

    //get/set方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }


    //
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", height=" + height +
                '}';
    }
}


| 然后我们可以定义一个测试类,来创建学生对象并且调用方法。

package cn.itcast.person;

/**
 * 学生测试类
 */
public class StudentTest {
    public static void main(String[] args) {
        Student s = new Student();
        s.study();
    }
}

我们平时就是这样写代码的,其实在这两步之间还有一步,就是将我们写的类加载到内存中,是虚拟机自动完成的这一步就是大部分人不明白的地方,我们写的文件是保存为.java.类型的文件,编译完就是.class类型的文件,然后通过类加载器,将整个类变成一个Class类型对象,如下图
在这里插入图片描述

可以这样想象:有一个Class类,这个类中有各种类型的属性,如下

public class Class1 {//写Class1是应为class是关键字,
    public Field[] shuxing ;//就是属性
    public Constructor[] gouzaofangfa;//构造方方法
    public Method[] fangfa;//普通方法

    //构造方法省略。。。

    //普通方法省略。。。
}

然后创建这个类的对象studentclass,这个对象就是学生类对应的class对象,什么意思呢,

这个对象有一个field数组类型的属性: public Field[] shuxing;这个数组中放了学生类中所有的属性也即是说,学生类的所有的属性(姓名,年龄,性别)在class对象中,都是field类型的!!

也有一个constructor数组类型的属性: public Constructor[] gouzaofangfa;这个数组中放了所有学生类的构造方法也即是说,学生类的所有的构造方法,在class对象中,都是constructor类型的!!

还有一个method数组类型的属性public Method[] fangfa;这个数组放了学生类所有的普通方法同样地,方法在class对象中都是method类型的

注意,这么说是方便理解,其实并不是这样存放的,但是不这样说,这段就很难理解
这段不理解不要紧,看完如何反射在回来看可以更方便理解。

为什么要反射(反射的作用)

动态获取类的各个组成部分,框架设计的灵魂。后面会用一个案例解释为啥是灵魂

怎么反射

1获取Class对象

有三种方法,分别对应类运行的三个过程
| 1,Class.forName(“全路径名”) 对应类刚写完,还没加载进内存
| ?2,类名.class 对应加载完,还没生成对象,这是作为类的一个属性进行调用
| ?3,对象.getclass();生成对象了,用对象调用方法,代码如下

public class ReflectTest1 {
    public static void main(String[] args) throws Exception {
        /**
         * 获取类对应的class对象
         * 1,Class.forName()
         * 2,类名.class
         * 3,对象.getclass()
         */
        //1,Class.forName()
        Class sclass1 = Class.forName("cn.itcast.person.Student");
        //2,类名.class
        Class sclass2 = Student.class;
       // 3,对象.getclass(),先创建个学生类的对象
        Student s = new Student("郭德纲", 45, "男", 170);
        Class sclass3 = s.getClass();

        System.out.println(sclass1);
        System.out.println(sclass2);
        System.out.println(sclass3);

        System.out.println(sclass1 == sclass2);
        System.out.println(sclass1 == sclass3);
}

这样我们就获取到了Student类对应的class对象,这里一定要注意,这三个对象是同一个,在内存中其实只有一个,上面5个输出分别是:

在这里插入图片描述

获取到class对象有啥用呢?我们还是没明白反射是怎么在程序运行时动态获取类的各个部分的,要知道怎么获取,首先要知道类有几个主要部分?三个,属性,构造方法,普通方法,当然也有类名啊,包名啊等等,我们说这三个主要的部分,

获取属性

| 获取属性就是要获取某个学生对象的属性值,分两步走:

1,先获取属性名

| 4种方法,以代码形式给出:

public class ReflectTest1 {
    public static void main(String[] args) throws Exception {
        /**
         * 首先获取学生类的class对象
         */

        Student s = new Student("郭德纲", 45, "男", 170);
        Class sclass3 = s.getClass();
       
        /**
         * 获取属性,用class对象调对用的方法
         * 1获取指定public的属性  getFiled()
         * 2获取全部public的属性  getFileds()
         *
         * 3获取指定声明的属性	getDeclaredField()
         * 4获取全部声明的属性  getDeclaredFields()
         */

         /*1获取指定public的属性  getFiled(),除了name,
         	其他三个会报错,因为不是public修饰的
         */
        Field name = sclass1.getField("name");
        Field age = sclass1.getField("age");
        Field sex = sclass1.getField("sex");
        Field height = sclass1.getField("height");

		//2获取全部public的属性  getFileds()
        Field[] fields = sclass1.getFields();
        
		//3获取指定声明的属性	getDeclaredField()
        Field name1 = sclass1.getDeclaredField("name");
        Field age1 = sclass1.getDeclaredField("age");
        Field sex1 = sclass1.getDeclaredField("sex");
        Field height1 = sclass1.getDeclaredField("height");
        
		//4获取全部声明的属性  getDeclaredFields()
        Field[] declaredFields = sclass1.getDeclaredFields();
		
       

解释:
getField(“属性名”)是获取指定的public修饰的属性名,不是Public修饰的不行,上面代码除了名字是public,另外三个会报错,
getFields();这个是获取所有的public修饰的属性名,返回的是一个数组,可以遍历出来。

declared意思是声明,就是学生类中有的,不论是不是私有,只要你在学生类,都能获取,所以getdeclaredField(“属性名”),可以获取私有的属性名,带s的也是返回一个数组,大家可以将他们都输出来试一下。

找指定对象对应该属性的属性值(涉及暴力反射)

我们找到了属性名,就要找某个学生对象 对应 该属性名的属性值,用获取到的属性名,调用get(对象名)方法,比如要获取姓名,


        /**
         * 获取属性值
         */
        Object o = name.get(s);
        System.out.println(o);

       //对于私有的要用暴力反射,
        age1.setAccessible(true);//忽略访问权限修饰符
        Object o1 = age1.get(s);
        System.out.println(o1);

控制台输出:
在这里插入图片描述
对于私有的,用带declared的获取到后,要加一行代码

属性名.setAccessible(true);

这样可以忽视访问权限修饰符,称为暴力反射,简称暴射。
这里就有疑问了不是带declared能获取到的私有的吗,要注意刚才获取到的是属性名,现在是找属性值,私有的属性值要暴力反射。就比如,体重是一个属性名,但是我们不能直接问女朋友你多少斤,人家的具体体重(属性值),是私有的。

大家可以尝试一输出另外的几个方法获取到的属性值。

获取构造方法

获取构造方法就是要用来创建对象

1,先获取构造器

| 4种,代码如下

  */
 		 //获取指定的public修饰的构造方法,参数可以这样写,没有参数就不写
        Constructor constructor1 = sclass1.getConstructor(String.class, int.class, String.class, int.class);
        //获取指定的声明过的构造方法(可以是私有的)
        Constructor declaredConstructor = sclass1.getDeclaredConstructor(String.class);

同样,带s的是获取全部,并且返回数组,大家可以试一下

创建对象(暴力反射)

获取到构造方法,就要创建对象了,用newInstance();方法,参数列表写想传入的参数,我用的第二个,是私有的所以用暴力反射

declaredConstructor.setAccessible(true);
        Object zzy = declaredConstructor.newInstance("zzy");
        System.out.println(zzy);

控制台输出:因为只传了名字,所以其他为空
在这里插入图片描述
大家可以试一下其他的几个方法,并且输出一下

获取方法(这个比较特殊)

获取方法目的就是要执行该方法

获取方法

| 4种,代码如下

  * 获取方法
         * 1获取指定名称的public方法
         * 2获取全部的public方法(包括继承过来的)
         *
         * 3获取指定名称的 该类声明过的方法(继承不算,实现接口的算)
         * 4获取全部声明的方法
         */
        Method study = sclass1.getMethod("study");
        Method[] methods = sclass1.getMethods();

        Method sleep = sclass1.getDeclaredMethod("sleep");
        Method[] declaredMethods = sclass1.getDeclaredMethods();

大家获取之后输出一下,会发现不带declared的那两个,输出了一大堆,这是因为,所有的类都继承Object类,继承过来的方法,也是默认有的,只不过我们看不见。
但是带declared的没有,因为declared的意思是声明的,学生类继承的Object类中的方法并没有在该类中声明,所以没有,但是,实现接口中的方法是有声明的,会存在。

从这里也不难看出,declared这个声明是什么意思,就是写出来的,看得到的,不论你什么修饰符,写出来就能获取,没写出来的就不获取

执行方法

用获取到的方法对象调用invokes()方法

  Method study = sclass1.getMethod("study");
        Method[] methods = sclass1.getMethods();

        Method sleep = sclass1.getDeclaredMethod("sleep");
        Method[] declaredMethods = sclass1.getDeclaredMethods();
        sleep.setAccessible(true);
        sleep.invoke(s);

控制台输出:
在这里插入图片描述
好了,具体获取方法都说完了,那么,这玩意有啥用,比我们平时做的还要麻烦!
下面我们做个案例,就是写一个反射,让他可以在不改变代码的情况下,创建任意类的对象,并且调用任意方法!这样就理解了

反射案例(解释为什么反射是框架的灵魂)

创建配置文件

在src下建一个pro.properties文件(file),在文件里写如下(第二张图)内容,全路径包名和想调用的方法名
在这里插入图片描述
在这里插入图片描述

获取配置文件

加载配置文件

获取配置文件的值

反射

以上四部分统一写在代码里

public class ReflectTest {
    public static void main(String[] args) throws Exception {
        /**
         * 获取配置文件,用反射类的类加载器
         */
        ClassLoader classLoader = ReflectTest.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("pro.properties");
        /**
         * 加载配置文件,用properties对象调用load方法
         */
        Properties p = new Properties();
        p.load(is);
        
        /**
         * 获取配置文件的值,
         */
        String classname = p.getProperty("classname");
        String methodname = p.getProperty("methodname");

        /**
         * 反射,4步
		        1获取Class文件
		        2 获取构造器创建对象
		        3获取方法
		        4执行方法
         */
        Class<?> aClass = Class.forName(classname);

        Constructor<?> constructor = aClass.getConstructor();
        Object o = constructor.newInstance();

        Method method = aClass.getMethod(methodname);

        method.invoke(o);
    }
}

这样,加入我要调用学生类的其他方法,只要在配置文件(pro.properties)修改就可以了,就不用变化代码了。
甚至,我新建一个老师类,想创建老师类对象,并且调用老师类中方法,也只需要修改配置文件中的classname,methodname就可以了。

以上仅代表个人观点。有帮助请您点个赞,如有错误,还请斧正。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-09-06 11:02:26  更:2021-09-06 11:03:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:32:51-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码