way-to-architect
  • 前言
  • Java
    • Java关键字
      • Java中四种修饰符的限制范围
      • static和final
    • 容器
      • 容器概述
        • 容器:综述
        • Iterator原理及实现
        • fast-fail机制
        • 比较器Comparator
        • Collections工具类
      • List
        • List综述
        • ArrayList原理分析
        • ArrayList在循环过程中删除元素的问题
        • 常用的小技巧
        • CopyOnWrite
      • Set
        • Set综述
        • HashSet
        • LinkedHashSet
        • TreeSet
      • Queue
        • Queue综述
        • ArrayBlockingQueue实现原理
        • LinkedBlockingQueue实现原理
        • 高性能无锁队列Disruptor
      • Map
        • Map综述
        • HashMap
          • HashMap实现原理
          • HashMap中的位运算
          • HashMap其他问题
        • LinkedHashMap
        • TreeMap
        • ConcurrentHashMap
          • ConcurrentHashMap实现原理JDK1.7
          • ConcurrentHashMap实现原理JDK1.8
        • ConcurrentSkipListMap
        • Map中key和value的null的问题
    • 线程
      • 线程
        • 创建线程
        • 线程状态及切换
        • 线程中断的理解
        • 几种方法的解释
        • 用户线程与守护线程
        • 线程组ThreadGroup
      • 线程池
        • 线程池工作原理及创建
        • Executor
        • 如何确保同一属性的任务被同一线程执行
      • ThreadLocal
        • ThreadLocal原理
        • ThreadLocal之父子线程传值
        • InheritableThreadLocal
      • 同步与锁
        • 线程安全与锁优化
        • synchronize关键字
        • Lock
          • 队列同步器
            • 同步状态的获取与释放
            • 使用方式
            • 示例:Mutex
            • 示例:TwinsLock
          • 重入锁和读写锁
          • LockSupport
          • Condition
          • 并发工具类
        • CAS
          • CAS的理解
          • Java中原子操作类
        • 3个经典同步问题
      • fork/join的理解
    • I/O
      • I/O概述
        • 磁盘I/O与网络I/O
        • 主要接口
        • 输入流和输出流的使用示例
        • InputStream的重复读
        • BufferdxxxxStream
        • Serailizable
        • File常用方法
        • Files和Path
        • RandomAccessFile
        • 通过零拷贝实现有效数据传输
        • 正确地处理文件
      • NIO基础
      • NIO2
      • Netty
        • Java I/O的演进之路
        • 为什么是Netty
        • 更多
      • I/O调优
    • 异常
      • 异常体系及为什么要有这种异常设计
      • 多catch的执行情况
      • try catch finally 与reture
      • 异常处理的误区
      • Preconditions:方法入参校验工具
    • 枚举
      • 常见用法
      • 枚举类在序列化中的问题
    • 注解
      • 概述
      • Spring中的组合注解的条件注解
      • 常用注解
        • JSR-330标准注解
    • 反射
      • 概述
      • 内部类的反射
      • 反射中需要注意的地方
    • 流程控制
      • switch case without break
      • Java: for(;;) vs. while(true)
    • JVM
      • JVM内存结构
      • Java内存模型
      • 垃圾收集器和内存分配策略
      • 四种引用类型区别及何时回收
      • 类文件结构
      • 类初始化顺序
      • 类加载机制
      • 虚拟机执行引擎
      • 逃逸分析
      • JVM常用配置
      • GC日志分析
      • Java8 JVM 参数解读
      • 垃圾收集器和内存分配策略
    • 面向对象
      • Object类中的方法
      • Class类中的方法
      • 值传递还是引用传递?
      • 接口和抽象类的区别
      • 深拷贝和浅拷贝
      • Integer.parseInt()与Interger.valueof()
      • hashCode()与equal()
      • String
        • String池化及intern()方法的作用
        • 关于字符串
    • 序列化
      • Java序列化的方式有哪些?
    • 新特性
      • 流 Stream
        • Stream是什么
        • Stream API详解
        • Stream进阶
        • 流编程
        • 其他事项
      • lambda表达式
      • 默认方法(Default Methods)
      • @FunctionalInterface注解
    • SPI
      • 理解SPI
    • 字节码
      • javaagent
      • 字节码操纵
      • 如何查看类编译后的字节码指令
      • 字节码指令有哪些
  • Python
    • 异常处理
  • Go
  • 数据结构与算法
    • 数据结构
      • 概述
        • 线性表
        • 栈
        • 队列
        • 串
        • 树
        • 图
      • Java的一些实现
      • 红黑树
      • 双缓冲队列
      • 跳表SkipList
    • 算法
      • 概述
      • 常见算法
        • 基本排序
        • 高级排序
        • 动态规划
  • 框架或工具
    • Spring
      • Spring基础
        • Spring整体架构
        • 什么是IoC
        • Ioc容器的基本实现
        • Spring的MainClass
          • Spring的BeanFactory
          • Spring的Register
          • Spring的Resource和ResourceLoader
          • Spring的PropertySource
          • Spring的PropertyResolver
          • Spring的PropertyEditor
          • Spring的Convert
          • Spring的BeanDefinition
          • Spring的BeanDefinitionReader
          • Spring的BeanDefiniton其他Reader
          • Spring的BeanDefinition其他Reader2
          • Spring的Aware
          • Spring的BeanFctoryPostProcessor
          • Spring的BeanPostProcessor
          • Spring的Listener
        • Xml格式的应用启动
          • Xml格式的应用启动2
          • Xml格式的应用启动3
          • Xml格式的应用启动4
          • Xml格式的应用启动5
          • Xml格式的应用启动6
          • Xml格式的应用启动7
        • Spring中的设计模式
        • 什么是AOP
        • Spring中AOP的实现
      • Spring应用
        • Spring的事务控制
        • @Transactional注解在什么情况下会失效
        • 如何在数据库事务提交成功后进行异步操作
        • Spring中定时任务的原理
    • SpringMVC
      • Controller是如何将参数和前端传来的数据一一对应的
      • 请求处理流程
    • Zookeeper
      • Zookeeper是什么
      • Zookeeper能干啥
    • Shiro
    • druid
    • Netty
    • Consul
      • Consul是什么
    • etcd
    • confd
    • Akka
      • Actor模型是什么
  • 数据库
    • 基本概念
    • MySQL
      • 基本配置
      • MySQL数据类型
      • MySQL存储引擎
      • MySQL事务
        • MySQL事务概念
      • MySQL索引
        • MySQL中的索引类型
        • B-Tree/B+Tree概述
        • 为什么使用B+Tree
        • MySQL中的B+Tree索引
        • MySQL高性能索引策略
      • MySQL查询
        • MySQL查询过程
        • MySQL查询性能优化
        • 使用EXPLAIN
      • MySQL锁
        • MySQL中锁概述
        • InnoDB的并发控制
        • MySQL乐观锁
      • MySQL分库分表
        • 分库/分表
        • 跨库JOIN
        • 跨库分页
        • 分库分表后的平滑扩容
        • 分区表
        • 分布式ID生成方法
      • MySQL实战
        • 在线表结构变更
        • MySQL优化规则
        • MySQL问题排查
        • 常见查询场景
    • Redis
    • Hbase
    • OpenTSDB
    • rrd
    • MongoDB
    • 连接池
  • 系统设计
    • 一致性Hash算法
    • 限流
      • 限流是什么
      • 限流算法
      • 应用内限流
      • 分布式限流
      • 接入层限流
        • ngx_http_limit_conn_module
        • ngx_http_limit_req_module
        • lua_resty_limit-tarffic
      • 节流
    • 降级
      • 降级详解
      • 人工降级开关的实现
      • 自动降级的实现:Hystrix
    • 负载均衡
      • 概述
      • 互联网架构下的负载均衡
      • Nginx负载均衡(七层)
      • Nginx负载均衡(四层)
      • Nginx动态配置
    • 超时与重试机制
      • 什么地方要超时与重试
      • 代理层超时与重试
      • Web容器超时
      • 中间件客户端超时与重试
      • 数据库超时
      • NoSQL客户端超时设置
      • 业务超时
      • 前端请求超时
    • 网关
    • CAP
      • 什么是CAP
      • CAP理解
    • 生产者-消费者模型
      • 使用notify/wait方式
      • 使用await/signal实现
      • 使用阻塞队列实现
      • 使用信号量实现
      • 使用管道流实现
      • 无锁队列Disruptor
      • 双缓冲队列
    • 缓存
      • 缓存概述
      • 数据库缓存
      • 应用缓存
      • 前端缓存
      • 本地缓存
    • 秒杀
    • LRU
  • 版本控制
    • Git
      • Git常用命令
      • 场景命令
    • Svn
  • 计算机操作系统
    • Linux
      • Linux中重要概念
      • 常用命令
      • 查看日志
      • 权限管理
      • 登录或传输
      • 防火墙
      • 配置ssh免密
      • 进程
      • 防火墙
    • Mac
    • 计算机基础
      • 进制
      • Java中的位运算
      • 计算机存储系统结构
  • 网络
    • TCP三次握手和四次挥手
    • 网络术语
      • 网关、路由器、交换机、IP等
      • VLAN
      • LAN
  • 设计模式
    • 设计模式概述
    • 创建型
      • 单例模式
      • 工厂模式
      • 建造者模式
      • 原型模式
      • 享元模式
    • 行为型
      • 观察者模式
      • 策略模式
      • 模板模式
      • 责任链模式
      • 命令模式
      • 外观模式
      • 迭代器模式
      • 中介者模式
        • 中介模式续
      • 状态模式
        • 状态模式实例
        • 状态模式思考
      • 访问者模式
        • 访问者实例1
        • 访问者模式续
    • 结构型
      • 组合模式
        • 组合模式续
      • 装饰模式
        • 装饰模式续
      • 代理模式
      • 备忘录模式
      • 桥接模式
        • 桥接模式实例一
  • 构建工具
    • Maven
      • 常用命令
      • Maven生命周期
      • Maven中的变量和属性
      • 不同环境的如何配置不同的变量
      • 常用插件及配置
      • 其他问题
      • dependencies与dependencyManagement的区别
    • Gradle
  • 大数据
    • Hadoop
    • Storm
    • Spark
  • 服务器
    • Tomcat
      • server.xml配置详解
      • 线程池和连接数配置
      • Maven远程部署
      • 一些小技巧
      • Tomcat类加载机制分析
      • Tomcat的日志
      • Tomcat架构
        • 概述
        • Server 的启动流程
        • 请求处理流程
    • Nginx
      • 常用命令
      • 基本配置
      • Lua
    • Tengine
  • 中间件
    • 任务调度
      • 为什么需要任务调度
    • 消息队列
      • 为什么需要消息队列
      • 消息队列关键点
      • 消息中间件需要解决的问题
      • 不同消息队列产品对比
      • RocketMQ
        • 快速入门
        • 整体架构
        • 部署方式
          • Broker部署方案
        • 客户端使用
          • 客户端使用指南
          • 快速开始
          • 简单示例
          • 有序消息示例
          • 广播消息示例
          • 定时消息示例
          • 批量消息示例
          • 过滤消息示例
          • 日志输出配置示例
        • 关键点实现
          • 顺序消息的实现
        • 最佳实践
          • Broker的最佳实践
          • 生产者最佳实践
            • 生产者最佳实践续
          • 消费者最佳实践
            • 消费者最佳实践续
          • 名称服务最佳实践
          • JVM/kernel配置的最佳实践
          • 新特性 Filter Server
          • 其他事项
      • RabbitMQ
      • Kafka
    • 分布式事务
      • 什么是分布式事务
      • 解决方案
    • 服务治理
      • RPC概念
      • RPC最简实现
      • 为什么需要服务治理
      • Dubbo
        • Dubbo整体架构
      • Java RMI
    • 分布式锁
      • 如何设计分布式锁
        • 基于zookeeper
        • 基于Redis
    • 注册中心
      • 注册中心的职责
      • 不同注册中心的比较
    • 配置中心
      • 概述
      • 配置中心的实现与选型
  • Web开发
    • Http请求类型及区别
    • 常见的content-type
    • 如何处理跨域
    • Restful最佳实践
    • HTTP状态码
    • Http下载原理
  • 测试
    • 压测:apache bench
    • 压测:Jmeter
Powered by GitBook
On this page
  • 什么是反射
  • 应用场景
  • 基本用法
  • 获取Class对象
  • 判断是否为某个类的实例
  • 创建实例
  • 获取类构造器
  • 获取类的变量(字段)信息
  • 获取方法
  • 调用方法
  • 利用反射创建数组
  • 总结
  • 参考
  1. Java
  2. 反射

概述

什么是反射

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

Oracle官方对反射的解释是

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. The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control.

通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。一般而言,程序中对象的类型都是在编译期就确定下来的,而Java反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。 反射的核心是JVM在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。

Java的反射框架主要提供以下功能:关键词是运行时(而非编译期)

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

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

  • 3.在运行时判断任意一个类所具有的成员变量和方法

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

应用场景

很多人都认为反射在实际的Java开发应用中并不广泛,其实不然。当我们在使用IDE(如Eclipse,IDEA)时,当我们输入一个对象或类并想调用它的属性或方法时,一按点号,编译器就会自动列出它的属性或方法,这里就会用到反射。

反射最重要的用途就是开发各种通用框架。

很多框架(比如Spring)都是配置化的(比如通过XML文件配置各种bean),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射——运行时动态加载需要加载的对象。

对与框架开发人员来说,反射虽小但作用非常大,它是各种容器实现的核心。而对于一般的开发者来说,不深入框架开发则用反射用的就会少一点,不过了解一下框架的底层机制有助于丰富自己的编程思想,也是很有益的。

基本用法

反射可以用于判断任意对象所属的类,获得Class对象,构造任意一个对象以及调用一个对象。这里我们介绍一下基本反射功能的实现(反射相关的类一般都在java.lang.relfect包里)。

为了操作方便,Java除抽象出Class来表示类之外,还提供了Method/Field/Constructor来分别表示方法/字段/构造器。

获取Class对象

(1)使用Class类的forName(String className)静态方法

Class<?> userClass = Class.forName("com.maxwell.learning.common.reflectexample.User");

(2)直接获取某一个对象的class:类名.class

Class<String> stringClass = String.class;
Class<HashMap> hashMapClass = HashMap.class;

(3)调用某个对象的getClass()方法

User user = new User();
Class<? extends User> userClass = user.getClass();

String s = new String("abc");
Class<? extends String> aClass = s.getClass();

判断是否为某个类的实例

一般地,我们用instanceof关键字来判断是否为某个类的实例。同时我们也可以借助反射中Class对象的isInstance()方法来判断是否为某个类的实例,它是一个Native方法:

public native boolean isInstance(Object obj);

用法示例

static class Animal { }
static class Cat extends Animal { }

@Test
public void testInstanceOf() {
    Cat cat = new Cat();
    boolean isInstanceOf = cat instanceof Animal;
    boolean isInstance = Animal.class.isInstance(cat);
}

创建实例

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

User user = User.class.newInstance();

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

//假如User类中有如下构造器
public User(String name, int age) {
        this.name = name;
        this.age = age;
}

//则可以这样来
Constructor<User> constructor = User.class.getConstructor(String.class, int.class);
User user = constructor.newInstance("xiaoming", 18);

获取类构造器

  • getConstructor(Class<?>... parameterTypes) :根据入参类型和个数,获取对应public的构造器

  • getDeclaredConstructor(Class<?>... parameterTypes) :根据入参类型和个数,获取对应构造器,不能获取到父类的

  • getConstructors():获取所有public的构造器

  • getDeclaredConstructors():获取所有构造,不能获取到父类的

此外,Constructor类有一个newInstance方法可以创建一个对象实例。

public T newInstance(Object ... initargs)

此方法可以根据传入的参数(没有则不传)来调用对应的Constructor创建对象实例。

获取类的变量(字段)信息

  • getFiled(String filedName): 根据字段名获取public成员变量

  • getDeclaredField(String filedName):根据字段名获取已声明的变量,但不能得到其父类的变量

  • getFileds():获取所有的pulbic变量

  • getDeclaredFields():获取所有的已声明的变量,但不能得到其父类的public变量

如果是私有变量,必须设置field.setAccessible(true)之后才可以访问,否则将抛出java.lang.NoSuchFieldException异常。

//假如有如下类
public class User {

    private String name;

    private int age;
}
//下面将会报错:java.lang.NoSuchFieldException
Field nameFiled = user.getClass().getField("name");

//解决方法:field.setAccessible(true)
User user = new User("xiaoming", 18);
Class<? extends User> userClass = user.getClass();
Field[] fields = userClass.getDeclaredFields();
for (Field field : fields){
    field.setAccessible(true);
    System.out.println(String.format("属性[%s], 值[%s]", field.getName(), field.get(user)));
}
//输出如下
属性[name], 值[xiaoming]
属性[age], 值[18]

获取方法

获取某个Class对象的方法集合,主要有以下几个方法:

  • getDeclaredMethods():返回所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法

  • getMethods():返回所有public方法,包括其继承类的公用方法

  • getMethod(String name, Class<?>... parameterTypes):第一个参数为方法名称,后面的参数为方法的参数对应的Class对象

  • getDeclaredMethod(String name, Class<?>... parameterTypes):可以获取到私有的,但不能获取到父类的

调用方法

当我们从类中获取了一个方法后,我们就可以用invoke()方法来调用这个方法:

public Object invoke(Object obj, Object... args)

使用示例

public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
}
@Test
public void testInvokeMethod() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
    Calculator calculator = new Calculator();
    Class<? extends Calculator> calculatorClass = calculator.getClass();
    Method method = calculatorClass.getMethod("add", int.class, int.class);
    int res = (int)method.invoke(calculator, 1, 2);
    System.out.println(res);
}
//输出如下
3

利用反射创建数组

数组在Java里是比较特殊的一种类型,可以使用java.lang.reflect.Array来进行创建。

@Test
public void testArray() throws ClassNotFoundException {
    Class<?> clazz = Class.forName("java.lang.String");
    Object array = Array.newInstance(clazz,25);
    Array.set(array,0,"aaa");
    Array.set(array,1,"bbb");
    Array.set(array,2,"ccc");
    Array.set(array,3,"ddd");
    Array.set(array,4,"eee");
    String  res = (String)Array.get(array, 3);
    System.out.println(res);
}
//输出如下
ddd

总结

我们可以发现这样的规则:

  • getDeclaredXxxx:获取到自身的方法/变量/构造器等,包括public/protected/private,但不包含父类的

  • getXxxx:获取自己及父类的方法/变量/构造器等,但只能是public的。

对于私有构造器/字段/方法,即使通过getDeclaredXxxx方法获取到,但在使用之前,也需要使用setAccessible(true)来设置访问权限。

反射到底慢在哪

反射所花费的时间大约是正常情况下的2倍(只是某种测试用例下,没办法下这种结论,只是有这么一种概念)。那么到底慢在哪里呢?

使用反射,则在运行时会停下来做类加载(包含很多步),加载可以缓存,但是有可能引发雪崩,方法调用还会多步寻址,虽然寻址后还是可以缓存,但这些过程jit没法插手,你可以认为java变成了php。

TODO:翻译

Reflection is slow for a few obvious reasons:

  1. The compiler can do no optimization whatsoever as it can have no real idea about what you are doing. This probably goes for theJITas well

  2. Everything being invoked/created has to be discovered(i.e. classes looked up by name, methods looked at for matches etc)

  3. Arguments need to be dressed up via boxing/unboxing, packing into arrays,Exceptionswrapped in

    InvocationTargetExceptions and re-thrown etc.

  4. All the processing that

    .

Just because something is 100x slower _does not mean it is too slow for you _assuming that reflection is the "right way" for you to design your program. For example, I imagine that IDEs make heavy use of reflection and my IDE is mostly OK from a performance perspective.

After all, the overhead of reflection is likely to pale into insignificance when compared with, say, parsing XML or accessing a database!

参考

Previous反射Next内部类的反射

Last updated 6 years ago

Another point to remember is that micro-benchmarks are a notoriously flawed mechanism for determining how fast something is in practice. As well as, the JVM takes time to "warm up", the JIT can re-optimize code hotspots on-the-fly etc.

:文字部分大多来源于此,略有改动

_Jon Skeet_mentions here
_Tim Bender's_remarks
深入解析Java反射(1) - 基础
Reflections中的getDeclared**与get**的区别
Java 反射到底慢在哪里?
Java Reflection: Why is it so slow?