框架的前置学习-反射

news2024/11/27 11:58:01
  • 运行java代码要经历的三个阶段

    在这里插入图片描述

  • 反射,程序框架设计的灵魂,将类的各个组成部分封装成其他对象,这就是反射机制。

  • 框架:半成品的软件,在框架的基础上进行开发,可以简化编码

  • 反射的好处:

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

    1. 对于任意一个类,都能够知道这个类的所有属性和方法
    2. 对于任意一个对象,都能够调用它的任意属性和方法
  • 与反射机制相关的类

    1. Java.lang.class :代表整个类编译得到的class文件(字节码)
    2. Java.lang.reflect.Method :代表字节码中的方法字节码
    3. Java.lang.reflect.Constructor :代表字节码中的构造器字节码
    4. Java.lang.reflect.Field :代表字节码中的属性字节码
  • 要操作一个字节码,首先要得到这个类的字节码,下面是三种得到字节码的方式

    1. static Class.forName(String className) 这里的className需要时类的全路径,这是一个静态方法

      public class reflect1 {
          public static void main(String[] args) throws ClassNotFoundException {
            //第一种方式
              Class c1=Class.forName("java.lang.String");
              System.out.println(c1);
          }
      }
      
      
    2. java中的任意一个对象.getClass() ,这个方法继承自Object

      public class reflect1 {
          public static void main(String[] args) throws ClassNotFoundException {
            //第一种方式
              Class c1=Class.forName("java.lang.String");
              System.out.println(c1);
      			//第二种方式
              String s="hello";
              Class c2=s.getClass();
              System.out.println(c2);
          }
      }
      
      
    3. java语言中的任意一种类型,包括基本数据类型,都有.class属性。

      public class reflect1 {
          public static void main(String[] args) throws ClassNotFoundException {
            //第一种方式
              Class c1=Class.forName("java.lang.String");
              System.out.println(c1);
      			//第二种方式
              String s="hello";
              Class c2=s.getClass();
              System.out.println(c2);
            //第三种方式
             	Class c3=String.class;
              System.out.println(c3);
          }
      }
      
      
    4. JVM在加载一个类的时候,会把这个类的.class字节码文件放在方法区中,栈中放入main方法进行压栈,堆中new出对象c,但是无论使用什么方法获取字节码,一样的class字节码文件只会放一份在方法区中,得到的c变量都是指向方法区中的同一份.class文件。

  • 使用反射机制来实例化对象

  1. 通过Class的newInstance() 方法可以实例化对象

  2. 但是newInstance()方法内部实际上调用了无参构造方法,必须要保证无参构造的存在才可以

        private static void test2() throws InstantiationException, IllegalAccessException {
            Object o=Student.class.newInstance();
            System.out.println(o);
        }
    
  • 反射机制的灵活性,在不修改源码的情况下,可以做到不同对象的实例化,符合OCP开闭原则:对拓展开放,对修改关闭.

    1. 下面的代码中首先利用FileReader读取配置文件,配置文件里面保存了键和值,值是类名,之后new了一个Properties对象,Properties对象的键和值都是String类型,是一种Map数据类型,之后将文件流对象传入Properties对象,再根据键得到值,然后利用反射机制传入类名新建一个对象,实现了对象的灵活创建。
        private static void test3() throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
            FileReader fr=new FileReader("Student.properties");
            Properties pro=new Properties();
            pro.load(fr);
            fr.close();
            String forName=pro.getProperty("Student");
            Class C=Class.forName(forName);
            Object o=C.newInstance();
            System.out.println(o);
        }
    
  • 如果只想要让类里面的某些代码被执行,那么可以使用静态代码和Class.forName,像下面的代码那样,每次类加载器加载一个类的时候,就会先执行静态代码块,使用Class.forName而不用创建类的实例化变量

    public class reflect1 {
        static {
            System.out.println("静态代码执行!");
        }
    }
    
        private static void test4() {
            try {
                Class.forName("reflect1");
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
    
  • 获取文件的类路径,可以通过类加载器获取,具体如下:

        private static void test4() {
            String path=Thread.currentThread().getContextClassLoader().getResource("a.txt").getPath();
            System.out.println(path);
        }
    
  • 资源绑定器

    1. 用来获取属性配置文件中的内容

    2. 属性配置文件必须要放在类路径下(src下面)

    3. 必须是properties后缀

    4. 绑定某个属性配置文件的时候,只需要填写配置文件的名字,不需要写后缀,也不能写后缀

          private static void test4() {
              ResourceBundle bundle=ResourceBundle.getBundle("Student");
              String path=bundle.getString("Student");
              System.out.println(path);
          }
      
  • 获取并操作属性字节码

    1. 首先获取整个字节码

    2. 利用字节码的getFields可以获取到public修饰的所有属性

    3. 利用字节码的getDeclaredFields可以获取到所有属性,无论什么修饰符都可以获取到

    4. 遍历属性列表

    5. 通过属性.getName()可以获取到属性的名字

    6. 通过属性.getType()可以获取到属性的完整类型,getSimpleName是获取简化的类名

    7. 通过属性.getModifiers()可以获取到属性的修饰符id,通过Modifier.toString(id)就可以拿到完整的修饰符

      import java.io.Serializable;
      
      public class Student implements Serializable {
      //    属性设置为private
          private String name;
          private int age;
          private static String room;
          //无参数构造
          public Student() {
          }
          //有参数构造
          public Student(String name, int age, String room) {
              this.name = name;
              this.age = age;
              this.room=room;
          }
          //getter方法
          public String getName() {
              return name;
          }
          public int getAge() {
              return age;
          }
      
          public static String getRoom() {
              return room;
          }
      
          //setter方法
          public void setName(String name) {
              this.name = name;
          }
          public void setAge(int age) {
              this.age = age;
          }
      
          public static void setRoom(String room) {
              Student.room = room;
          }
          //重写equals方法
          public boolean equals(Object obj){
              if(obj instanceof Student) {
                  //首先将得到的Object转为Student,否则使用不了Student特有的方法(多态的弊端)
                  Student t = (Student) obj;
                  return t.getAge() == this.getAge() && t.getName() == this.getName() && t.getRoom() == this.getRoom();
              }
              return false;
          }
      
          @Override
          public String toString() {
              return "Student{" +
                      "name='" + name + '\'' +
                      ", age=" + age +
                      '}';
          }
      }
      
      
          private static void test5() throws ClassNotFoundException {
              //获取整个类
              Class studentClass=Student.class;
              //获取public修饰的属性,一个Filed包括private static String room;
              Field[] fileds=studentClass.getFields();
              for (Field filed : fileds) {
                  System.out.println(filed);
              }
              //获取所有的属性,一个Filed包括private static String room;
              fileds=studentClass.getDeclaredFields();
              for (Field filed : fileds) {
                  //获取属性的名字
                  System.out.println("属性的名字:");
                  System.out.println(filed.getName());
      
                  //获取属性的类型
                  Class filedTypeClass=filed.getType();
                  //getName是获取完整类型,getSimpleName是获取简化的类名
                  String filedType=filedTypeClass.getSimpleName();
                  System.out.println("属性的类型");
                  System.out.println(filedType);
      
                  //获取属性的修饰符
                  int modify=filed.getModifiers();
                  String modifyName= Modifier.toString(modify);
                  System.out.println("属性的修饰符");
                  System.out.println(modifyName);
      
                  System.out.println("==================");
              }
          }
      
  • 利用反射机制访问一个对象的属性

    1. 要使用反射,当然要先获取一个类的字节码文件

    2. 利用获取到的字节码文件,再用newInstance方法创建一个对象(使用反射的类必须有无参构造)

    3. 利用字节码文件获取这个类的某个属性

    4. 利用这个属性给创建的对象的对应的属性赋值

    5. 在设置对象的属性值之前,需要在使用 set 方法之前,调用 setAccessible(true) 方法来取消对字段的访问权限检查。否则, 会报IllegalAccessException 异常。这是因为 name 字段被声明为 private,无法直接访问。但是使用反射机制绕过访问权限限制可能会导致安全问题。

          private static void test6() throws NoSuchFieldException, InstantiationException, IllegalAccessException {
              //获取整个类
              Class studentClass=Student.class;
              Field filed=studentClass.getDeclaredField("name");
              Object obj=studentClass.newInstance();
              filed.setAccessible(true); // 取消对字段的访问权限检查
              filed.set(obj,"zs");
              System.out.println(obj);
          }
      
  • 利用反射机制获取Method

    1. 获取整个类的字节码文件

    2. 通过字节码文件得到方法的字节码,并且遍历它

    3. 通过.getName()获取方法名字

    4. 通过.getReturnType().getSimpleName()获取返回值类型

    5. 通过.getModifiers()获取修饰符id,然后通过Modifier.toString(id)得到修饰符

    6. 通过.getParameterTypes()得到修饰符列表,遍历这个列表,通过.getSimpleName()得到参数名称

          private static void test7() {
              Class studentClass=Student.class;
              Method[] methods=studentClass.getDeclaredMethods();
              for (Method method : methods) {
                  //获取方法名
                  System.out.println("方法名");
                  System.out.println(method.getName());
      
                  //获取方法的返回值类型
                  System.out.println("方法的返回值类型");
                  System.out.println(method.getReturnType().getSimpleName());
      
                  //获取修饰符列表
                  System.out.println("方法的修饰符列表");
                  System.out.println(Modifier.toString(method.getModifiers()));
      
                  //方法的参数列表
                  System.out.println("方法的参数列表");
                  Class[] parametorType= method.getParameterTypes();
                  for (Class aClass : parametorType) {
                      System.out.println(aClass.getSimpleName());
                  }
      
                  System.out.println("========================");
              }
          }
      
  • 通过反射机制操纵Method

    1. 首先获取字节码

    2. 利用得到的字节码新建一个对象

    3. 利用得到的字节码获取一个指定的方法,在java里面,由于存在方法的重载,所以区分一个方法是由方法名+方法参数类型,而不仅仅是方法名,所以使用getDeclaredMethod方法需要一个方法名和若干参数的class

    4. 最后使用这个方法的invoke方法,传入要操纵的对象和实参

          private static void test8() throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
              Class studentClass=Student.class;
              Object obj=studentClass.newInstance();
              Method setName=studentClass.getDeclaredMethod("setName",String.class);
              Object returnVa=setName.invoke(obj,"zs");
              System.out.println(obj);
          }
      
  • 通过反射机制获取构造方法

    1. 获取整个类的字节码文件

    2. 通过字节码文件得到构造方法的字节码,并且遍历它

    3. 通过.getName()获取方法名字

    4. 通过.getModifiers()获取修饰符id,然后通过Modifier.toString(id)得到修饰符

    5. 通过.getParameterTypes()得到修饰符列表,遍历这个列表,通过.getSimpleName()得到参数名称

          private static void test9() {
              Class studentClass=Student.class;
              Constructor[] constructors=studentClass.getDeclaredConstructors();
              for (Constructor constructor : constructors) {
                  //获取构造方法名
                  System.out.println("构造方法名");
                  System.out.println(constructor.getName());
      
      
                  //获取修饰符列表
                  System.out.println("构造方法的修饰符列表");
                  System.out.println(Modifier.toString(constructor.getModifiers()));
      
                  //方法的参数列表
                  System.out.println("构造方法的参数列表");
                  Class[] parametorType= constructor.getParameterTypes();
                  for (Class aClass : parametorType) {
                      System.out.println(aClass.getSimpleName());
                  }
      
                  System.out.println("========================");
              }
          }
      
  • 使用反射机制的构造方法创建对象

    1. 直接利用字节码创建(已过时)

    2. 首先获取无参构造函数,然后调用无参构造函数

    3. 首先获取有参构造函数,然后调用有参构造函数

          private static void test10() throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
              //使用反射创建对象
              //1. 直接利用字节码创建
              Class studentClass=Student.class;
              Object t1=studentClass.newInstance();
              //2. 先利用字节码获取构造函数,然后再利用构造函数创建对象
              //首先是无参构造
              Constructor con1=studentClass.getDeclaredConstructor();
              Object t2=con1.newInstance();
              //最后是有参构造方法
              Constructor con2=studentClass.getDeclaredConstructor(String.class,int.class,String.class);
              Object t3=con2.newInstance("zs",18,"h111");
      
              System.out.println(t1);
              System.out.println(t2);
              System.out.println(t3);
      
          }
      
  • 使用反射获取一个类的父类以及接口

        private static void test11() {
            Class stringClass=String.class;
            //获取String的父类
            Class superClass=stringClass.getSuperclass();
            System.out.println("父类是");
            System.out.println(superClass.getSimpleName());
            //获取接口
            Class[] interface1=stringClass.getInterfaces();
            System.out.println("接口是");
            for (Class aClass : interface1) {
                System.out.println(aClass.getSimpleName());
            }
        }
    

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

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

相关文章

通过easyui实现动态控制表格字段显示、导出表格数据

前言 学过layui前端框架的都知道,layui默认帮我们实现了控制表格字段显示以及数据的导出功能。 1、控制表格字段显示 2、数据导出 3、导出为pdf:导出按钮的右边那个按钮就是打印pdf的 那么,easyui要怎么实现这些功能呢?这篇文章就…

matlab计算基础

目录 1. 创建矩阵和向量 2. 矩阵的基本运算 2.1 数乘 2.2 转秩 2.3 求逆 2.4 点积 2.5 拼接 3. 复数 4. 矩阵元素的引用 5.工作区中数据的保存和使用 1. 创建矩阵和向量 向量包括行向量和列向量,向量就是个特殊的矩阵,向量可看作C语言中的一维…

华为OD机试之报文回路(Java源码)

题目描述 IGMP 协议中响应报文和查询报文,是维系组播通路的两个重要报文,在一条已经建立的组播通路中两个相邻的 HOST 和 ROUTER,ROUTER 会给 HOST 发送查询报文,HOST 收到查询报文后给 ROUTER 回复一个响应报文,以维持…

网络:从socket编程的角度说明UDP和TCP的关系,http和tcp的区别

尝试从编程的角度解释各种网络协议。 UDP和TCP的关系 从Python的socket编程角度出发,UDP(User Datagram Protocol)和TCP(Transmission Control Protocol)是两种不同的传输协议。 TCP是一种面向连接的协议&#xff0c…

2022深圳杯C题思路解析

题目描述: 继续更新 再更问题三 继续更新第一问、第四问 1.2 问题重述 在制定电动车调度方案时,必须考虑充、换电池的时间成本,从而提出了新 的车辆运输选址及调度问题。 1) 已知自动驾驶电动物料车在取料点 P 和卸货点 D …

STM32CubeMX+VSCODE+EIDE+RT-THREAD 工程创建

Eide环境搭建暂且不表,后续补充。主要记录下Vscode环境下 创建Rt-thread工程的过程。分别介绍STM32CubeMX添加rtt支持包的方式和手动添加rtt kernel方式。STM32CubeMX生成工程的时候有"坑",防止下次忘记,方便渡一下有缘人&#xff…

MYSQL进阶-事务的基础知识

1.什么是数据库事务? 就是把好几个sql语句打包成一个整体执行,要么全部成功,要么全部失败!!! 事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执 行的结果必…

【RabbitMQ】golang客户端教程3——发布订阅(使用fanout交换器)

发布订阅 在上一个教程中,我们创建了一个工作队列。工作队列背后的假设是每个任务只传递给一个工人。在这一部分中,我们将做一些完全不同的事情——我们将向多个消费者传递一个消息。这就是所谓的“订阅/发布模式”。 为了说明这种模式,我们…

Vulnhub: DriftingBlues: 6靶机

kali:192.168.111.111 靶机:192.168.111.180 信息收集 端口扫描 nmap -A -sC -v -sV -T5 -p- --scripthttp-enum 192.168.111.180 查看robots.txt发现存在目录:/textpattern/textpattern 访问后发现是textpattern cms 目录爆破发现文件sp…

尚品汇总结三:商城首页(面试专用)

目录 首页商品分类实现 1、封装数据接口 2、页面静态化: 什么是页面静态化 为什么要使用静态化 首页商品分类实现 前面做了商品详情,我们现在来做首页分类,我先看看京东的首页分类效果,我们如何实现类似效果: 思路…

2020年06月《全国青少年软件编程等级考试》Python一级真题解析

一、单选题 第1题 以下哪种输入结果不可能得到以下反馈: 重要的事情说三遍:安全第一!安全第一!安全第一! A:print(“重要事情说三遍:”“安全第一!”*3) B:print(“重…

扩散模型实战(一):基本原理介绍

扩散模型(Diffusion Model)是⼀类⼗分先进的基于物理热⼒学中的扩散思想的深度学习⽣成模型,主要包括前向扩散和反向扩散两个过程。⽣成模型除了扩散模型之外,还有出现较早的VAE(Variational Auto-Encoder,…

【TypeScript】初识TypeScript和变量类型介绍

TypeScript 1,TypeScript是什么?2,类型的缺失带来的影响3,Ts搭建环境-本博主有专门的文章专说明这个4,使用tsc对ts文件进行编译5,TS运行初体验简化Ts运行步骤解决方案1解决方案2(常见) 开始学习…

【探索Linux】—— 步步学习强大的命令行工具 P.1(Linux简介)

目录 前言 一、Linux简介 二、linux的不同发行版本 三、Linux的开源性质 四、Linux的特点 五、Linux代码演示(仅供参考) 总结 前言 前面我们讲了C语言的基础知识,也了解了一些数据结构,并且讲了有关C的一些知识&#xff…

如何将本地的conda算法库打包到无网络的服务器用于部署

如何将本地的conda算法库打包到无网络的服务器用于部署 1、先安装conda-pack库,2、将本地的虚拟环境进行打包3、登录远程服务器,切换到conda安装目录,将本地生成的tar文件复制到该目录下4、新建文件夹,例如yus_env,这个…

并查集练习 —岛屿数量(解法一)

题目: 给定一个二维数组matrix(char[][]),里面的值不是1就是0,上、下、左、右相邻的1认为是一片岛。返回matrix中岛的数量。 本题共有2种解法,本篇先介绍最快的一种解法—递归。 分析: 递归的方…

65 # 实现 http-server 里的 gzip 压缩

用 zlib 来实现 gzip 压缩 服务端优化都是:压缩 缓存 前端可以通过 webpack 插件进行压缩 gzip 根据替换来实现的,重复率越高,压缩后的结果越小 const zlib require("zlib"); const fs require("fs"); const path …

K8S kubeadm搭建

kubeadm搭建整体步骤 1)所有节点进行初始化,安装docker引擎和kubeadm kubelet kubectl 2)生成集群初始化配置文件并进行修改 3)使用kubeadm init根据初始化配置文件生成K8S的master控制管理节点 4)安装CNI网络插件&am…

分页Demo

目录 一、分页对象封装 分页数据对象 分页查询实体类 实体类用到的utils ServiceException StringUtils SqlUtil BaseMapperPlus,> BeanCopyUtils 二、示例 controller service dao 一、分页对象封装 分页数据对象 import cn.hutool.http.HttpStatus; import com.…

EditPlus连接Linux系统远程操作文件

EditPlus是一套功能强大的文本编辑器! 1.File ->FTP->FTP Settings; 2.Add->Description->FTP server->Username->Password->Subdirectory->Advanced Options 注意:这里的Subdirectory设置的是以后上传文件的默认…