Java 反射详解

news2024/9/29 15:23:21

一、反射

1、什么是反射

反射允许对成员变量、成员方法和构造器的信息进行编程访问。
补充:暴力反射,非public修饰需要打开权限
setAccessible(boolean)

2、反射的作用

  • 利用反射创建的对象可以无视修饰符调用类里面的内容
  • 可以跟配置文件结合起来使用,把要创建的对象信息和方法写在配置文件中。
    读取到什么类,就创建什么类的对象
    读取到什么方法,就调用什么方法
    此时当需求变更的时候不需要修改代码,只要修改配置文件即可。
    简单记忆:1、获取任意一个类中的所有信息、2、结合配置文件动态创建对象。

3、学习反射到底学习什么

反射都是从class字节码文件中获取的内容。

  • 如何获取class字节码文件的对象
  • 利用反射如何获取构造方法(创建对象)
  • 利用反射如何获取成员变量(赋值,获取值)
  • 利用反射如何获取成员方法(运行)

4、获取字节码文件对象的三种方式

  • Class这个类里面的静态方法forName(“全类名”)(最常用)
  • 通过class属性获取 (一般更多的是当做参数进行传递)
  • 通过对象获取字节码文件对象 (当我们已经有了这个类的对象时,才可以使用)

代码演示:

package com.liming.myreflect;

public class ReflectDemo01 {
    public static void main(String[] args) throws ClassNotFoundException {
        /*
         * 获取class对象的三种方式
         *   1、Class.forName("全类名");
         *   2、类名.class
         *   3、对象.getClass();
         * */

        //1、方式一
        //全类名:包名+类名
        //最为常用的
        Class clazz1 = Class.forName("com.liming.myreflect.Student01");

        //2、方式二
        //一般更多的是当做参数进行传递
        Class clazz2 = Student01.class;

        //3、方式三
        //当我们已经有了这个类的对象时,才可以使用
        Student01 stu = new Student01();
        Class clazz3 = stu.getClass();

        System.out.println(clazz1 == clazz2);
        System.out.println(clazz2 == clazz3);


    }
}
package com.liming.myreflect;

public class Student01 {
    private String name;
    private int age;
    public String gender;

    public Student01() {
    }


    public Student01(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student01{name = " + name + ", age = " + age + "}";
    }

    /**
     * 获取
     * @return gender
     */
    public String getGender() {
        return gender;
    }

    /**
     * 设置
     * @param gender
     */
    public void setGender(String gender) {
        this.gender = gender;
    }
}

5、获取构造方法

规则:

​ 1、get表示获取

​ 2、Declared表示私有

​ 3、最后的s表示所有,复数形式

​ 4、如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用

在这里插入图片描述
代码演示:

public class ReflectDemo2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        //1.获得整体(class字节码文件对象)
        Class clazz = Class.forName("com.liming.myreflect.Student01");


        //2.获取构造方法对象
        //获取所有构造方法(public)
        Constructor[] constructors1 = clazz.getConstructors();
        for (Constructor constructor : constructors1) {
            System.out.println(constructor);
        }

        System.out.println("=======================");

        //获取所有构造(带私有的)
        Constructor[] constructors2 = clazz.getDeclaredConstructors();

        for (Constructor constructor : constructors2) {
            System.out.println(constructor);
        }
        System.out.println("=======================");

        //获取指定的空参构造
        Constructor con1 = clazz.getConstructor();
        System.out.println(con1);

        Constructor con2 = clazz.getConstructor(String.class,int.class);
        System.out.println(con2);

        System.out.println("=======================");
        //获取指定的构造(所有构造都可以获取到,包括public包括private)
        Constructor con3 = clazz.getDeclaredConstructor();
        System.out.println(con3);
        //了解 System.out.println(con3 == con1);
        //每一次获取构造方法对象的时候,都会新new一个。

        Constructor con4 = clazz.getDeclaredConstructor(String.class);
        System.out.println(con4);
    }
}

5.1、获取构造方法并创建对象

涉及到的方法:newInstance

代码演示:

//首先要有一个javabean类
public class Student {
    private String name;

    private int age;


    public Student() {

    }

    public Student(String name) {
        this.name = name;
    }

    private Student(String name, int age) {
        this.name = name;
        this.age = age;
    }


    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}



//测试类中的代码:
//需求1:
//获取空参,并创建对象

//1.获取整体的字节码文件对象
Class clazz = Class.forName("com.liming.myreflect.Student");
//2.获取空参的构造方法
Constructor con = clazz.getConstructor();
//3.利用空参构造方法创建对象
Student stu = (Student) con.newInstance();
System.out.println(stu);


System.out.println("=============================================");


//测试类中的代码:
//需求2:
//获取带参构造,并创建对象
//1.获取整体的字节码文件对象
Class clazz = Class.forName("com.liming.myreflect.Student");
//2.获取有参构造方法
Constructor con = clazz.getDeclaredConstructor(String.class, int.class);
//3.临时修改构造方法的访问权限(暴力反射)
con.setAccessible(true);
//4.直接创建对象
Student stu = (Student) con.newInstance("zhangsan", 23);
System.out.println(stu);

6、获取成员变量

规则:

​ 1、get表示获取

​ 2、Declared表示私有

​ 3、最后的s表示所有,复数形式

​ 4、如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用
在这里插入图片描述

获取成员变量并获取值和修改值

方法说明
void set(Object obj, Object value)赋值
Object get(Object obj)获取值
package com.liming.myreflect;

import java.lang.reflect.Field;

public class ReflectDemo03 {
    public static void main(String[] args) throws Exception {
        //1、获取类的字节码文件
        Class<?> clazz = Class.forName("com.liming.myreflect.Student01");
        //2、获取所有公共的成员变量
        Field[] fields1 = clazz.getFields();
        for (Field field : fields1) {
            System.out.println(field);
        }
        //获取所有的成员变量
        Field[] fields2 = clazz.getDeclaredFields();
        for (Field field : fields2) {
            System.out.println(field);
        }
        //获取单个公共成员变量
        Field gender = clazz.getField("gender");
        System.out.println(gender);
        //获取单个成员变量(所有权限)
        Field name = clazz.getDeclaredField("name");
        System.out.println(name);

        //获取权限修饰符
        int modifiers = name.getModifiers();
        System.out.println(modifiers);

        //获取成员变量的名字
        String n = name.getName();
        System.out.println(n);

        //获取成员变量的数据类型
        Class<?> type = name.getType();
        System.out.println(type);

        //获取成员变量记录的值
        Student01 stu = new Student01("zhangsan", 23, "男");
        name.setAccessible(true);
        String value = (String) name.get(stu);
        System.out.println(value);

        //修改对象里面记录的值
        name.set(stu, "lisi");
        System.out.println(stu);
    }
}

7、获取成员方法

规则:

​ 1、get表示获取

​ 2、Declared表示私有

​ 3、最后的s表示所有,复数形式

​ 4、如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用
在这里插入图片描述

获取成员方法并运行

Object invoke(Object obj, Object… args) :运行方法

参数一:用obj对象调用该方法

参数二:调用方法的传递的参数(如果没有就不写)

返回值:方法的返回值(如果没有就不写)

代码演示:

package com.liming.myreflect;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class ReflectDemo03 {
    public static void main(String[] args) throws Exception {
        //1、获取class字节码文件对象
        Class clazz = Class.forName("com.liming.myreflect.Student02");

        //2、获取所有公共的成员方法(包含父类中所有的公共方法)
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("======================================");
        //获取所有的成员方法(不包含父类)
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method method : declaredMethods) {
            System.out.println(method);
        }
        System.out.println("======================================");
        //获取单个公共的成员方法
        Method sleep = clazz.getMethod("sleep");
        System.out.println(sleep);
        System.out.println("======================================");
        //获取单个成员方法
        Method eat = clazz.getDeclaredMethod("eat", String.class);
        System.out.println(eat);
        System.out.println("======================================");

        //获取方法的修饰符
        int modifiers = eat.getModifiers();
        System.out.println(modifiers);

        //获取方法的名字
        String name = eat.getName();
        System.out.println(name);

        //获取方法的形参
        Parameter[] parameters = eat.getParameters();
        for (Parameter parameter : parameters) {
            System.out.println(parameter);
        }

        //获取方法抛出的异常
        Class[] exceptionTypes = eat.getExceptionTypes();
        for (Class exceptionType : exceptionTypes) {
            System.out.println(exceptionType);
        }

        //方法运行
        //参数一:用obj对象调用该方法
        //参数二:调用方法传递的参数(没有就不写)
        //返回值:方法的返回值(没有就不写)
        Student02 stu = new Student02();
        eat.setAccessible(true);
        eat.invoke(stu,"鸡腿");
    }
}
package com.liming.myreflect;

public class Student02 {
    private String name;
    private int age;

    public Student02() {
    }

    public Student02(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public void sleep(){
        System.out.println("睡觉");
    }

    private void eat(String something){
        System.out.println("在吃"+something);
    }

    public String toString() {
        return "Student02{name = " + name + ", age = " + age + "}";
    }
}

8、四道面试题

你觉得反射好不好?好,有两个方向

第一个方向:无视修饰符访问类中的内容。但是这种操作在开发中一般不用,都是框架底层来用的。
第二个方向:反射可以跟配置文件结合起来使用,动态的创建对象,动态的调用方法。

8.1、泛型擦除

集合中的泛型只在java文件中存在,当编译成class文件之后,就没有泛型了。

代码演示:

public class ReflectDemo03 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //1.创建集合对象
        ArrayList<Integer> list = new ArrayList<>();
        list.add(123);
        //list.add("asd");

        //2.利用反射运行add方法去添加字符串
        //因为反射使用的是class字节码文件

        //获取class对象
        Class clazz = list.getClass();
        //获取add方法对象
        Method add = clazz.getMethod("add", Object.class);
        //运行方法
        add.invoke(list,"asd");
        //打印集合
        System.out.println(list);
    }
}

8.2、修改字符串的内容

字符串不可以修改的原因?
字符串,在底层是一个byte类型的字节数组,名字叫做value

private final byte[] value;

真正不能被修改的原因:final和private
final修饰value表示value记录的地址值不能修改。

private修饰value而且没有对外提供getvalue和setvalue的方法。所以,在外界不能获取或修改value记录的地址值。

如果要强行修改可以用反射:

代码演示:

public class ReflectDemo04 {
    public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException {
        String s = "abc";
        String ss = "abc";
        // private final byte[] value= {97,98,99};
        // 没有对外提供getvalue和setvalue的方法,不能修改value记录的地址值
        // 如果我们利用反射获取了value的地址值,也是可以修改的
        // final修饰的value真正不可变的value数组的地址值,里面的内容利用反射还是可以修改的,比较危险

        //1.获取class对象
        Class clazz = s.getClass();
        //2.获取value成员变量(private)
        Field field = clazz.getDeclaredField("value");
        //JDK高版本已经屏蔽了这种操作,低版本还是可以的
        field.setAccessible(true);//临时修改权限

        //3.获取value记录的地址值
        byte[] value = (byte[]) field.get(s);
        value[0] = 100;

        System.out.println(s);
        System.out.println(ss);
    }
}

8.3、反射和配置文件结合动态获取的练习(重点)

需求: 利用反射根据文件中的不同类名和方法名,创建不同的对象并调用方法。

分析:

①通过Properties加载配置文件

②得到类名和方法名

③通过类名反射得到Class对象

④通过Class对象创建一个对象

⑤通过Class对象得到方法

⑥调用方法

代码演示:
properties配置文件:

classname=com.liming.mytest.Student
methodname=sleep
public class ReflectDemo01 {
    public static void main(String[] args) throws Exception{
        //1.读取配置文件的信息
        Properties prop = new Properties();
        FileInputStream fis = new FileInputStream("day15\\prop.properties");
        prop.load(fis);
        fis.close();
        System.out.println(prop);

        String classname = prop.get("classname") + "";
        String methodname = prop.get("methodname") + "";

        //2.获取字节码文件对象
        Class clazz = Class.forName(classname);

        //3.获取构造器创建这个类的对象
        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        Object o = constructor.newInstance();
        System.out.println(o);

        //4.获取方法的对象
        Method sleep = clazz.getDeclaredMethod(methodname);
        sleep.setAccessible(true);

        //5.运行方法
        sleep.invoke(o);
    }
}

8.4、利用发射保存对象中的信息(重点)

需求:对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中去

代码演示:

public class ReflectDemo02 {
    public static void main(String[] args) throws IOException, IllegalAccessException {
        /* 对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中去 */
        Student01 stu = new Student01("张三", 23, '男', 182.3, "学习");
        Teacher teacher = new Teacher("赖桑", 20000.0);
        saveObject(stu);
        saveObject(teacher);
    }

    public static void saveObject(Object obj) throws IllegalAccessException, IOException {
        //1.获取字节码文件的对象
        Class clazz = obj.getClass();

        //2. 创建IO流
        BufferedWriter bw = new BufferedWriter(new FileWriter("day15" + File.separator + "a.txt", true));

        //3. 获取所有的成员变量
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            //获取成员变量的名字
            String name = field.getName();
            //获取成员变量的值
            Object value = field.get(obj);
            //写出数据
            bw.write(name + "=" + value);
            bw.newLine();
        }
        bw.close();
    }
}

运行结果: 在a.txt文件中

name=赖桑
salary=20000.0
name=张三
age=23
gender=男
height=182.3
hobby=学习
name=赖桑
salary=20000.0

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/339045.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Jest】Jest单元测试环境搭建

文章目录前言1、项目环境搭建初始化仓库安装ts环境安装jest环境2、初始化项目文件夹前言 今天开始&#xff01;&#xff01;&#xff01;学习vue源码&#xff0c;那么要学习源码前首先要了解Jest。 https://www.jestjs.cn/ 官网自带中文非常友好&#xff01; 1、项目环境搭建…

【C++面试问答】搞清楚深拷贝与浅拷贝的区别

问题 深拷贝和浅拷贝的区别是面试中的常见问题之一&#xff0c;对于不同的编程语言&#xff0c;这个问题的回答可能稍有差别&#xff0c;下面我们就来探索一下它们之间的异同吧。 先来看看在JavaScript对象的深拷贝与浅拷贝的区别&#xff1a; 浅拷贝&#xff1a;只是复制了…

Postgresql 根据单列或几列分组去重row_number() over() partition by

Postgresql 根据单列或几列分组去重row_number() over() partition by 一般用于单列或者几列需要去重后进行计算值的 count(distinct(eid)) 可以 比如有个例子&#xff0c;需要根据名称&#xff0c;城市去筛选覆盖的道路长度&#xff0c;以月因为建立了唯一索引是ok的&#…

前端项目集成Vite配置一览无余

Vite配置文件 默认指定&#xff1a;vite.config.js。自定义指定&#xff1a;vite --config 自定义名称.js。 Vite相关命令 查看Vite有哪些命令&#xff1a;npx vite -help。 --host [host]// 指定域名 --port <port>// 指定端口 --https // 使用 TLSHTTP/2 --cors //…

Spring 中 ApplicationContext 和 BeanFactory 的区别

文章目录类图包目录不同国际化强大的事件机制&#xff08;Event&#xff09;底层资源的访问延迟加载常用容器类图 包目录不同 spring-beans.jar 中 org.springframework.beans.factory.BeanFactoryspring-context.jar 中 org.springframework.context.ApplicationContext 国际…

HTTP 和 HTTPS 的区别

文章目录前言一、HTTP 与 HTTPS 的基本概念HTTPHTTPS二、HTTP 和 HTTPS协议的区别前言 浏览网站时&#xff0c;我们会发现网址有两种格式&#xff0c;一种以http://开头&#xff0c;一种https://开头。好像这两种格式差别不大&#xff0c;只多了一个s&#xff0c;实际上他们有…

Java零基础教程——数组

目录数组静态初始化数组数组的访问数组的动态初始化元素默认值规则&#xff1a;数组的遍历数组遍历-求和冒泡排序数组的逆序交换数组 数组就是用来存储一批同种类型数据的容器。 20, 10, 80, 60, 90 int[] arr {20, 10, 80, 60, 90}; //位置 0 1 2 3 4数组的…

死锁的原因及解决方法

❣️关注专栏&#xff1a; JavaEE 死锁☘️1.什么是死锁☘️2.死锁的三个典型情况☘️2.1情况一☘️2.2情况二☘️2.2.1死锁的代码展示☘️2.3多个线程多把锁☘️3死锁产生的必要条件☘️3.1互斥性☘️3.2不可抢占☘️3.3请求和保持☘️3.4循环等待☘️4如何避免死锁☘️4.1避免…

【Spark分布式内存计算框架——Spark Core】6. RDD 持久化

3.6 RDD 持久化 在实际开发中某些RDD的计算或转换可能会比较耗费时间&#xff0c;如果这些RDD后续还会频繁的被使用到&#xff0c;那么可以将这些RDD进行持久化/缓存&#xff0c;这样下次再使用到的时候就不用再重新计算了&#xff0c;提高了程序运行的效率。 缓存函数 可以…

Kubernetes集群-部署Java项目

Kubernetes集群-部署Java项目&#xff08;SSG&#xff09; k8s部署项目java流程图 第一步 打包制作镜像 打包 java源码&#xff1a; application.properties #在有pom.xml的路径下执行 mvn clean package制作镜像&#xff1a; 将刚才打包后的文件夹传到&#xff0c;装有dock…

(考研湖科大教书匠计算机网络)第三章数据链路层-第十一节:虚拟局域网VLAN概述和实现机制

获取pdf&#xff1a;密码7281专栏目录首页&#xff1a;【专栏必读】考研湖科大教书匠计算机网络笔记导航 文章目录一&#xff1a;VLAN概述&#xff08;1&#xff09;分割广播域&#xff08;2&#xff09;虚拟局域网VLAN二&#xff1a;VLAN实现机制&#xff08;1&#xff09;IEE…

LeetCode题目笔记——15. 三数之和

文章目录题目描述题目链接题目难度——中等方法一&#xff1a;暴力&#xff08;参考&#xff09;代码/Python 参考方法二&#xff1a;哈希代码/Python参考方法三&#xff1a;排序双指针代码/Python代码/C总结题目描述 龙门阵&#xff1a;这个n个数之和是不是有什么深意啊&#…

Python中的类和对象(6)

文章目录1.多态2.类继承的多态3.自定义函数的多态4.鸭子类型5.思维导图1.多态 多态在编程中是一个非常重要的概念&#xff0c;它是指同一个运算符、函数或对象在不同的场景下&#xff0c;具有不同的作用效果&#xff0c;这么一个技能。 我们知道加号&#xff08;&#xff09;…

加载sklearn新闻数据集出错 fetch_20newsgroups() HTTPError: HTTP Error 403: Forbidden解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。喜欢通过博客创作的方式对所学的知识进行总结与归纳,不仅形成深入且独到的理…

大数据框架之Hadoop:入门(五)Hadoop编译源码(面试重点)

5.1 前期准备工作 1.CentOS联网 配置CentOS能连接外网。Linux虚拟机ping www.baidu.com 是畅通的 注意&#xff1a;采用root角色编译&#xff0c;减少文件夹权限出现问题 2.jar包准备(hadoop源码、JDK8、maven、ant 、protobuf) &#xff08;1&#xff09;hadoop-2.7.7-sr…

【TCP的拥塞控制】基于窗口的拥塞控制

TCP的拥塞窗口CWND大小和传输轮次n的关系如下所示。&#xff08;本题10分&#xff09; cwnd12481632333435363738394041422122232425261248N1234567891011121314151617181920212223242526 问题&#xff1a; &#xff08;1&#xff09;慢开始阶段的时间间隔&#xff1f;&#…

NFC enable NFC使能流程

同学,别退出呀,我可是全网最牛逼的 WIFI/BT/GPS/NFC分析博主,我写了上百篇文章,请点击下面了解本专栏,进入本博主主页看看再走呗,一定不会让你后悔的,记得一定要去看主页置顶文章哦。 NFC enable NFC使能流程 认识nfc系统如何工作,最好的方法就是了解nfc的各个流程,…

linux系统下SVN服务器搭建

linux新手&#xff0c;整了好几天才搞好&#xff0c;做下笔记以备后续使用&#xff1a; 1、下载svn服务器 yum -y install subversion 2、创建仓库 svnadmin create /opt/svn/pro/respos1 svnadmin create /opt/svn/pro/respos2 3、配置用户以及权限 1:cd到仓库目录下&#…

py3常用返回规则字符串的函数+ascii与char的转换

文章目录py3常用返回规则字符串的函数字符转ascii以及ascii转字符的方法为&#xff1a;py3常用返回规则字符串的函数 注明原来的网址为&#xff1a;https://docs.python.org/3.8/library/string.html string.ascii_letters 返回所有的大写、小写字母 string.ascii_lowercase 返…

寒假安全作业nginx-host绕过实例复现

1.测试环境搭建 LNMP架构的话&#xff0c;肯定就是linux、nginx、mysql、php四大组件。在后面的复现中我们还会用到https的一部分知识&#xff0c;故这里的nginx就需要使用虚拟主机并且配置https证书&#xff0c;且具有php解析功能。 1.1 基础nginx配置 #1.创建web目录 mkdir …