CHANSHIYU
GITHUBZERO
  • README
  • 時雨
    • 2017
      • 01 网站动态标题的两种方式
      • 02 RN App 外部唤醒踩坑记
    • 2018
      • 01 不一样の烟火
      • 02 Python 之禅
      • 03 Python 文件操作
    • 2019
      • 01 Aurora 食用指南
      • 02 Godaddy 域名找回记事
      • 03 一个接口的诞生
      • 04 SpringMVC 前后端传参协调
      • 05 主题集成友链访问统计
      • 06 Github Style 博客主题
      • 07 字符编码の小常识
      • 08 WSL 安装 Docker 实录
      • 09 Eriri comic reader
      • 10 Aurora 2.0
      • 11 jsDelivr 全站托管
      • 12 两年工作台变迁史
      • 13 春物
      • 14 一种优雅の笔记方式
    • 2020
      • 01 Telegram 电报机器人
      • 02 她的眼里有星辰
      • 03 文心雕龙
      • 04 软萌木子の有趣笔谈
      • 05 Telegram RSS 订阅频道
      • 06 水月雨银色飞船
      • 07 五年前旧照
    • 2021
      • 01 春宵苦短 2020
      • 02 风花
    • 2022
      • 01 小城新貌
      • 02 原神满级纪念
    • 2023
      • 01 2022 逆旅
      • 02 半透明背景图实现
      • 03 新年攒台海景房
  • 前端
    • JavaScript
      • 01 JavaScript 秘密花园
      • 02 JavaScript 正则技巧
      • 03 从浏览器解析 JS 运行机制
      • 04 Canvas 基础用法
      • 05 Blob Url And Data Url
      • 06 函数节流与函数防抖
      • 07 排序算法初探
      • 08 洗牌算法实现数组乱序
      • 09 正则匹配 match 和 exec
      • 10 正则匹配汉字
      • 11 JSX.Element vs ReactElement
      • 12 可选链与空值合并
      • 13 TypeScript 编码规范
      • 14 Typescript 中 interface 和 type 区别
      • 15 TypeScript 高级类型
      • 16 TypeScript 关键字
      • 17 TypeScript 映射类型
    • CSS
      • 01 Flex 弹性布局
      • 02 Position 定位
      • 03 CSS 逻辑属性
    • Node
      • 01 Node Tips
      • 02 七天学会 NodeJS
    • Note
      • 01 Note
      • 02 Code
      • 03 Snippets
      • 04 Git
    • React
      • 01 React Props Children 传值
      • 02 Use a Render Prop!
      • 03 React Hook
      • 04 React Hook 定时器
      • 05 Fetch data with React Hooks
      • 06 React 和 Vue 中 key 的作用
      • 07 useCallback 的正确使用方式
      • 08 useLayoutEffect 和 useEffect 的区别
      • 09 forwardRef 逃生舱
      • 10 React 条件渲染
    • Vue
      • 01 Vue Tips
      • 02 Vue 构建项目写入配置文件
      • 03 Vue 项目引入 SVG 图标
      • 04 Vue 一键导出 PDF
      • 05 动态可响应对象
      • 06 Vue 引入 SCSS
      • 07 Vue 路由权限控制
    • 实战系列
      • 01 WebSocket 心跳重连机制
      • 02 图片加解密二三事
      • 03 优雅实现 BackTop
      • 04 动态加载 JS 文件
      • 05 常用 DOM 方法比较
      • 06 AbortController 中断 fetch
      • 07 计算字符所占字节数
      • 08 Axios 自定义返回值类型
  • 后端
    • Java
      • 01 面向对象基本特征与原则
      • 02 Java 数据类型
      • 03 Java String
      • 04 Java 只有值传递
      • 05 Java final 与 static
      • 06 Java Object 通用方法
      • 07 Java 继承
      • 08 Java 反射
      • 09 Java 异常
      • 10 Java 容器
      • 11 Java 虚拟机
      • 12 Java IO
      • 13 Java HashMap
      • 14 Java List
      • 15 Java Stream
      • 16 Java 枚举
      • 17 Java 日期与时间
      • 18 Java fail fast
      • 19 Java BiFunction 和 BinaryOperator
    • 并发编程
      • 01 Java 并发
      • 02 synchronized
      • 03 volatile
      • 04 ReentrantLock
      • 05 ReadWriteLock
      • 06 StampedLock
      • 07 CompletableFuture
      • 08 ForkJoin
      • 09 ThreadLocal
      • 10 CountDownLatch
      • 11 ThreadPoolExecutor
      • 12 ExecutorService
      • 13 Atom 原子类
      • 14 BlockingQueue
    • 高效编程
      • 01 30 seconds of java8
      • 02 函数式替代 for 循环
      • 03 Java 字符串拼接
      • 04 单例模式的几种实现
      • 05 HashMap 排序
    • 理论概念
      • 01 Java Servlet
      • 02 Java 服务端分层模型
      • 03 经典排序算法
      • 04 LRU 缓存淘汰算法
      • 05 BloomFilter 判断元素存在
      • 06 Java HashMap 面试大全
      • 07 HTTP 状态码详解
      • 08 Cookie 和 Session
      • 09 基于消息队列的分布式事务解决方案
      • 10 微服务之所见
    • 实战系列
      • 01 AES CBC 加解密
      • 02 Magic 魔数获取文件类型
      • 03 获取请求 IP 地址
      • 04 Kaptcha 与数学公式验证码
      • 05 Netty 获取客户端 IP.md
      • 06 高性能无锁队列 Disruptor.md
      • 07 前后端接入阿里云盾
    • Linux
      • 01 Linux 文件权限系统
      • 02 Linux 常用软件安装
      • 03 CentOS 防火墙
    • MySQL
      • 01 MySQL
      • 02 SQL 语句 where 1=1
      • 03 truncate 和 delete
      • 04 事务
      • 05 关系模型
      • 06 Mybatis
      • 07 MySQL 查看数据库表详情
    • Nginx
      • 01 Nginx 指北
      • 02 nginx gzip 压缩
    • Note
      • 01 Vagrant
      • 02 Docker
      • 03 Lombok
      • 04 Swagger
      • 05 Redis
    • Spring
      • 01 Spring Boot
      • 02 Spring Validation
      • 03 Spring Data
      • 04 Spring 容器
      • 05 Spring AOP
      • 06 Spring Transactional 注解
      • 07 Spring Cloud Netflix
      • 08 Spring Cloud Alibaba
      • 09 Spring Security oAuth2
      • 10 Spring Boot 跨域解决方式
      • 11 Spring Boot 请求拦截
      • 12 Spring Boot 异步编程
      • 13 Spring Boot 定时任务
      • 14 Spring Boot 管理 bean
      • 15 Mybatis 逆向代码生成
      • 16 JWT
      • 17 JPA
      • 18 Apache Shiro
      • 19 Spring 异步请求
  • 书斋
    • ES6 标准入门
      • 01 变量声明与解构赋值
      • 02 语法的扩展
      • 03 数据类型与数据结构
      • 04 Proxy 和 Reflect
      • 05 异步编程 Promise
      • 06 Iterator 和 for of 循环
      • 07 Generator 函数
      • 08 Async 函数
      • 09 Class 类
    • JavaScript 设计模式
      • 01 基础知识
      • 02 设计模式(上)
      • 03 设计模式(下)
      • 04 设计原则和编程技巧
  • 纸函
    • 01 Interview
    • 02 Ceph
    • 03 动态规划
    • 04 Document.designMode
    • 2023-01-10
  • 万藏
    • 文档
      • 01 Git 文档
      • 02 Linux 命令大全
      • 03 七天学会 NodeJS
      • 04 Algorithms
    • 工具
      • 01 Nginx Config
      • 02 ProcessOn
      • 03 Flat Icon
      • 04 Regexper
      • 05 TempMail
      • 06 Carbon
由 GitBook 提供支持
在本页
  • 反射的概念
  • 什么是反射
  • 反射的优点
  • 反射的缺点
  • 反射的基本作用
  • 获取 Class 对象
  • 动态加载类
  • 获取类的信息
  • 方法的反射
  • 泛型的本质

这有帮助吗?

  1. 后端
  2. Java

08 Java 反射

反射的概念

什么是反射

反射 (Reflection) 是 Java 的特征之一,它允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性。

Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.

每个类都有一个 Class 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。类加载相当于 Class 对象的加载,类在第一次使用时才动态加载到 JVM 中。

反射可以提供运行时的类信息,并且这个类可以在运行时才加载进来,甚至在编译时期该类的 .class 不存在也可以加载进来。

Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库主要包含了以下三个类:

  • Field :可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段;

  • Method :可以使用 invoke() 方法调用与 Method 对象关联的方法;

  • Constructor :可以用 Constructor 创建新的对象。

Java 反射主要提供以下功能:

  • 在运行时判断任意一个对象所属的类;

  • 在运行时构造任意一个类的对象;

  • 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用 private 方法);

  • 在运行时调用任意一个对象的方法

反射的优点

  • 可扩展性:应用程序可以利用全限定名创建可扩展对象的实例,来使用来自外部的用户自定义类。

  • 类浏览器和可视化开发环境:一个类浏览器需要可以枚举类的成员。可视化开发环境(如 IDE)可以从利用反射中可用的类型信息中受益,以帮助程序员编写正确的代码。

  • 调试器和测试工具:调试器需要能够检查一个类里的私有成员。测试工具可以利用反射来自动地调用类里定义的可被发现的 API 定义,以确保一组测试中有较高的代码覆盖率。

反射的缺点

  • 性能开销:反射涉及了动态类型的解析,所以 JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。

  • 安全限制:使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如 Applet,那么这就是个问题了。

  • 内部暴露:由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用,这可能导致代码功能失调并破坏可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。

反射的基本作用

获取 Class 对象

首先理解 Java 对象的两个概念:

  1. Java 语言中,万事万物皆对象,但普通数据类型和静态的成员不是对象。

  2. 类是对象,类是 java.lang.Class 类的实例对象。There is a class named Class。

任何一个类都是 Class 的实例对象,这个实例对象有三种表示方式。

  • 直接获取类对象的 class,Foo.class

  • 调用类实例对象的 foo.getClass() 方法

  • 使用 Class 类的 Class.forName() 静态方法

package com.chanshiyu;

public class Hello {
  public static void main(String[] args) {
    // Foo 的实例对象
    Foo foo1 = new Foo();

    // Foo 这个类也是 Class 的实例对象,有三种表示方式
    // 1. 第一种表示方式:任何一个类都有一个隐含的静态成员变量 class
    Class c1 = Foo.class;

    // 2.第二种表示方式:已知该类的实例对象,使用 getClass 方法
    Class c2 = foo1.getClass();

    // 3.第三种表示方式
    Class c3 = null;
    try {
      c3 = Class.forName("com.chanshiyu.Foo");
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    }

    // c1、c2、c3 代表了 Foo 类的类类型(class type)
    // c1 == c2 == c3,一个类只能 Class 类的一个实例对象
    // 可以通过 c1、c2、c3 来创建实例对象
    try {
      Foo foo2 = (Foo)c1.newInstance();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

class Foo {}

动态加载类

编译时加载类是静态加载类,运行时加载类是动态加载类。Class.forName("类的全称") 不仅代表了类类型(class type),还代表了动态加载类。

new 创建对象是静态加载类,在编译时刻就需要加载所有可能用到的类。通过动态加载类可以实现按需加载类。

通过动态加载类有两种创建实例对象方式:

  • 使用 Class 对象的 newInstance() 方法来创建 Class 对象对应类的实例

  • 先通过 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance() 方法来创建实例。这种方法可以用指定的构造器构造类的实例。

package com.chanshiyu;

public class Office {
  public static void main(String[] args) {
    try {
      // 动态加载类
      Class c = Class.forName(args[0]);
      // 通过类类型创建对象
      OfficeAble oa = (OfficeAble) c.newInstance();
      oa.start();

      //获取指定参数的构造器
      //Constructor constructor = c.getConstructor(String.class);
      //根据构造器创建实例
      //OfficeAble oa = (OfficeAble) constructor.newInstance("hello");
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

class Word implements OfficeAble {
  @Override
  public void start() {
    System.out.println("wold start");
  }
}

interface OfficeAble {
  public void start();
}

获取类的信息

反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制。

获取类类型,再通过类类型获取类名称:

Class c1 = int.class; // int 的类类型
Class c2 = String.class; // String 的类类型
Class c3 = void.class; // void 的类类型
System.out.println(c1.getName()); // int
System.out.println(c2.getName()); // java.lang.String
System.out.println(c2.getSimpleName()); // String
System.out.println(c3.getName()); // void

工具类获取类的信息:

package com.chanshiyu;

import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.lang.reflect.Constructor;

public class ClassUtil {
  /**
   * 打印类的信息,包括成员变量与成员函数
   * @param obj 该对象所属类的信息
   */
  public static void printClassMessage(Object obj) {
    // 首先获取类类型
    Class c = obj.getClass();
    // 获取类的名称
    System.out.println("类的名称:" + c.getName());

    /**
     * Method 类,方法对象。一个成员方法就是一个 Method 对象
     * getMethods() 方法获取所有 public 方法,包括父类继承而来的
     * getDeclaredMethods() 方法获取该类自己申明的方法,不问访问权限
     */
    Method[] ms = c.getMethods();
    for (Method m: ms) {
      // 获取方法名
      System.out.print(m.getName());
      // 获取方法返回值类型
      Class returnType = m.getReturnType();
      System.out.print("方法返回值类型:" + returnType.getName());
      // 获取参数类型
      System.out.print("  参数类型:");
      Class[] paramTypes = m.getParameterTypes();
      for (Class c1: paramTypes) {
        System.out.print(c1.getName() + ",");
      }
      System.out.println();
    }

    /**
     * 获取成员变量:java.lang.reflect.Field
     * 成员变量也是对象,Field 类封装了成员变量的操作
     * getFileds() 方法获取所有 public 变量,包括父类继承而来的
     * getDeclaredFileds() 方法获取该类自己声明的变量,不问访问权限
     */
    Field[] fs = c.getDeclaredFields();
    for(Field field: fs) {
      // 获取成员变量的名称
      String fieldName = field.getName();
      // 获取成员变量类型
      Class fieldType = field.getType();
      String typeName = fieldType.getName();
      System.out.println(fieldName + " " + typeName);
    }

    /**
     * 获取构造函数:java.lang.reflect.Constructor
     * 构造函数也是对象
     * getConstructors() 方法获取所有 public 构造函数
     * getDecleardConstructors() 获取该类自己申明的构造函数
     */
    Constructor[] cs = c.getConstructors();
    for (Constructor constructor: cs) {
      // 获取构造函数名称
      System.out.print("构造函数:" + constructor.getName() + "(");
      // 获取构造函数参数列表
      Class[] paramTypes = constructor.getParameterTypes();
      for (Class c1: paramTypes) {
        System.out.print(c1.getName() + ",");
      }
      System.out.println(")");
    }
  }
}

方法的反射

通过反射操作可以获取类的方法然后调用。

package com.chanshiyu;

import java.lang.reflect.Method;

public class Demo1 {
  public static void main(String[] args) {
    A a = new A();
    Class c = a.getClass();

    // 获取方法 通过名称和参数列表决定
    try {
      // 反射操作调用方法
      // Method m1 = c.getMethod("print", int.class, int.class);
      // m1.invoke(a, 10, 20);

      Method m1 = c.getMethod("print",new Class[]{int.class, int.class});
      m1.invoke(a, new Object[]{10, 20});
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

class A {
  public void print(int a, int b) {
    System.out.println(a + b);
  }

  public void print(String a, String b) {
    System.out.println(a.toUpperCase() + "," + b.toUpperCase());
  }
}

泛型的本质

泛型是参数化类型的应用,操作的数据类型不限定于特定类型,可以根据实际需要设置不同的数据类型,以实现代码复用。

Java 源代码里面类型提供实现泛型功能,而编译后 Class 文件类型就变成原生类型(即类型被擦除掉),而在引用处插入强制类型转换以实现 JVM 对泛型的支持。本质是 Java 泛型只是 Java 提供的一个语法糖。Java 中集合的泛型是防止错误输入的,只在编译阶段有效,可以通过方法的反射来绕过编辑阶段检测。

package com.chanshiyu;

import java.lang.reflect.Method;
import java.util.ArrayList;

public class Demo2 {
  public static void main(String[] args) {
    ArrayList list1 = new ArrayList();
    ArrayList<String> list2 = new ArrayList<String>();
    list2.add("hello"); // ok
    // list2.add(20); 错误

    Class c1 = list1.getClass();
    Class c2 = list2.getClass();
    System.out.println(c1 == c2); // true

    /**
     * c1 == c2 返回 true 说明编译之后集合的泛型是去泛型话的
     * Java 中集合的泛型是防止错误输入的,只在编译阶段有效
     * 验证:可以通过方法的反射来绕过编辑阶段检测
     */
    try {
      Method m = c2.getMethod("add", Object.class);
      m.invoke(list2, 20); // 可以添加成功
      System.out.println("list2 length:" + list2.size());
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
上一页07 Java 继承下一页09 Java 异常

最后更新于2年前

这有帮助吗?