反射(Reflection)
Java学习笔记——反射(Reflection)
关于反射
能够分析类能力的程序称之为反射(Reflection)
反射机制可以用来:
- 在运行时分析类的能力
- 在运行时检查对象,例如:编写一个适合所有类的
toString()
- 实现泛型数组操作代码
- 利用Method对象(类似C++中的函数指针)
- 用户界面生成器
Class类
在程序运行时,JRE始终为所有对象维护一个运行时类型标识(个人猜测实际上是一个结构体)。这个类型标识会跟踪记录每一个对象所属的类。虚拟机利用这些信息来保证调用正确的方法
Class
类保存了这些类型标识(Class
类是泛型类,你懂我什么意思的吧)内部的信息,因此可以使用这个特殊的类来访问这些信息。
那么何如获取这些信息呢?
Object类中有一个getClass()
,可以返回一个Class
类型实例
class cl = e.getclass();
class
类中有个方法getName()
,可以获取对象所属的类名(字符串形式),当然如果这个类在一个包中,则返回的类名也会包含包名。
var ram = new Random();
Class cl = ram.getClass();
String name = cl.getName() // name is "java.util.Random"
当然java还有一种方便的方法来获取每一个类的Class
类对象。如果T
是任意的Java类型,则T.class
代表该类的Class
类对象
Class cl = Random.class;
Class c2 = int[].class;
Class c3 = int.class;
Class对象实际上表示的是一种类型(type),这种类型可以是类,亦可以不是类,因此int.class、int[].class
是合法的。
如果我想实现动态加载加载类,或者我现在知道这个类的类名(或者接口名),则还可以使用Class
类本身的静态方法来实现类加载
String classname = "java.util.Random";
try{
Class cl = Class.forName(classname);
}catch(Exception e){
e.printStackTrace();
}
若classname
不是一个接口或者类,则会抛出检查型异常。因此捕获异常。
使用这种方法获得Class
对象,classname
对应的类会被加载,也就是说类里面的静态代码块(Static Code)会被执行。同时可以变换classname
的值来实现动态加载类。
更加准确的类型比较
JVM为每一种类型管理一个唯一的Class
类对象,也就是说父类和子类被区分为不同的Class
类型,因此可以利用==
来进行同类型对象比较
father.getClass() == son.getClass(); // 表达式为False,即便father是son的父类
使用Class类对象构造类实例
前文说过,Class
类实际上表示的是一种类型,既然如此我能不能用一个Class
类来构造一个类实例呢?答案肯定是能的
使用getConstructor()
和newInstance()
Class cl = Class.forName(classname);
Object obj = Cl.getConstructor().newInstance();
那如果我想要调用有参数的构造器来创建对象呢?
先看看getConstructor()
和newInstance()
方法的声明:
Constructor getConstructor(Class ...paramterTypes) // 生成一个构造器对象,并描述这个构造器有什么参数类型
Object newInstance(Object ...params) // 生成对象实例,params参数为构造器传进的参数
因此,可以
Class cl = Class.forName(classname);
Object obj = Cl.getConstructor(int.class).newInstance(25); // 调用classname类中带int类型的构造器,并传入参数整型25
资源
Class
类通常也用在读入资源上,例如显示一张图片等
加载资源的方法
如果资源文件和类文件放在同一个包中,则可以
- 获取资源类的
Class
对象 - 有些方法需要获取资源位置的URL则需要调用
getResource()
- 如果不想获取URL而是直接将文件的所有字节存放在输入流中的则需要调用
getResourceAsStream()
Class cl = ResourceTest.class;
URL aboutURL = c1.getResource("about.png");
Image icon = new Image(aboutURL);
InputStream stream = cl.getResourceAsStream("../Date/about.txt"); // 支持相对和绝对路径,如果没找到资源则返回null
var about = new String(stream.readAllBytes(), "UTF-8");
反射应用
利用反射分析类
反射机制中常用来做类分析的重要类:Field
、Method
、Constructor
。这些类都在java.lang.reflect
包中
接下来对这几个类用来分析的方法进行简单介绍:
Class类
String getName() // 返回该类型的类名字
String getPackageName() // 返回该类所在的包名
Field[] getFields() // 返回对象的所有公共字段,包括超类的公共字段
Field[] getDeclaredFields() // 返回对象的全部字段,如果类中没有字段,或者对象是基本类型或者数组,则返回0长度数组
Class getSuperClass() // 获取该类的父类Class对象
Method[] getMethods() // 返回对象所属类或者接口的所有公共方法,包括超类的公共方法
Method[] getDeclaredMethods() // 返回对象所属类或者接口的全部方法,不包括超类
Constructor[] getConstructors() // 返回这个类的所有公共构造器
Constructor[] getDeclaredConstructors() // 返回全部构造器
Field类
String getName() // 返回类中的字段名(属性名)的字符串
Class getType() // 返回字段的类型(int、long、Date...)
int getModifiers() // 获取字段的修饰符(public、static、final...),返回1/0的二进制标志位,可以配合reflect包中的toString()来显示具体的修饰符
Class getDeclaringClass() //获取字段所属的类对应的Class对象
Method类
String getName() // 返回类中的方法名的字符串
Class getReturnType() // 返回方法的返回值类型对应的Class对象(int、long、Date...)
int getModifiers() // 获取方法的修饰符(public、static、final...),返回1/0的二进制标志位,可以配合reflect包中的toString()来显示具体的修饰符
Class getDeclaringClass() //获取方法所属的类对应的Class对象
Class[] getParameterTypes() // 返回Class对象的数组,其中各个对象表示参数的类型
Class[] getExceptionTypes() // 返回Class对象数组,其中各个对象表示该方法所抛出的异常的类型
Constructor类
String getName() // 返回类中的构造方法的字符串
int getModifiers() // 获取构造方法的修饰符(public、static、final...),返回1/0的二进制标志位,可以配合reflect包中的toString()来显示具体的修饰符
Class getDeclaringClass() //获取构造方法所属的类对应的Class对象
Class[] getParameterTypes() // 返回Class对象的数组,其中各个对象表示参数的类型
Class[] getExceptionTypes() // 返回Class对象数组,其中各个对象表示该方法所抛出的异常的类型
Modifier类
static String toString(int modifiers)
static boolean isAbstract(int modifiers)
static boolean isFinal(int modifiers)
static boolean isInterface(int modifiers)
static boolean isNative(int modifiers)
static boolean isPrivate(int modifiers)
static boolean isProtected(int modifiers)
static boolean isPublic(int modifiers)
static boolean isStatic(int modifiers)
static boolean isStrict(int modifiers)
static boolean isSynchronized(int modifiers)
static boolean isVolatile(int modifiers)
下面将演示一个通过反射来分析一个类的demo:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.PrivateKey;
public class Test {
public static void main(String[] args) {
new Test("java.lang.Double");
}
public Test(String classname){
try {
Class c = Class.forName(classname);
printClass(c);
}catch (Exception e){
e.printStackTrace();
}
}
public void printClass(Class c){
Class sc = c.getSuperclass(); // 获取父类
String modifier = Modifier.toString(c.getModifiers());// 获取类修饰符
if(modifier.length()>0)
System.out.print(modifier + " ");
System.out.print("class " + c.getName()); // class + 类名
if(sc!=null && sc != Object.class)
System.out.print(" extends " + sc.getName()); // 继承的父类
System.out.println();
System.out.println("{");
printConstructor(c); // 获取构造器函数
System.out.println();
printField(c); // 获取字段名
System.out.println();
printMethod(c); // 获取方法名
System.out.println("}");
}
private void printField(Class c){
Field[] fields = c.getDeclaredFields(); // 获取字段名
for (Field f:fields
) {
Class type = f.getType(); // 字段类型
String name = f.getName(); // 字段名
System.out.print(" ");
String midifier = Modifier.toString(f.getModifiers());
if(midifier.length()>0)
System.out.print(midifier + " "); // 字段修饰符
System.out.println(type.getName() + " " + name + ";");
}
}
private void printConstructor(Class c){
Constructor[] constructors = c.getConstructors(); // 获取构造方法名称
for (Constructor constructor:constructors
) {
String midifier = Modifier.toString(constructor.getModifiers());
String methodName = constructor.getName(); // 获取构造方法名
Class[] Params = constructor.getParameterTypes();// 获取参数类型
Class[] exceptions = constructor.getExceptionTypes(); // 获取异常类型
System.out.print(" ");
if(midifier.length()>0)
System.out.print(midifier + " "); // 获取修饰符
System.out.print(methodName + "(");
for(int i=0; i<Params.length; i++){
if(i == Params.length - 1)
System.out.print( Params[i].getName());
else
System.out.print( Params[i].getName() + ", ");
}
System.out.print(")");
if(exceptions.length>0)
System.out.print("throws "); // 获取异常类型
for(int i=0; i<exceptions.length; i++){
if(i == exceptions.length - 1)
System.out.print( exceptions[i].getName());
else
System.out.print( exceptions[i].getName() + ", ");
}
System.out.println(";");
}
}
private void printMethod(Class c){
Method[] methods = c.getDeclaredMethods();
for (Method m:methods
) {
String midifier = Modifier.toString(m.getModifiers());
String ret = m.getReturnType().getName();
String methodName = m.getName();
Class[] Params = m.getParameterTypes();
Class[] exceptions = m.getExceptionTypes();
System.out.print(" ");
if(midifier.length()>0)
System.out.print(midifier + " ");
System.out.print(ret + " " + methodName + "(");
for(int i=0; i<Params.length; i++){
if(i == Params.length - 1)
System.out.print( Params[i].getName());
else
System.out.print( Params[i].getName() + ", ");
}
System.out.print(")");
if(exceptions.length>0)
System.out.print("throws ");
for(int i=0; i<exceptions.length; i++){
if(i == exceptions.length - 1)
System.out.print( exceptions[i].getName());
else
System.out.print( exceptions[i].getName() + ", ");
}
System.out.println(";");
}
}
}
输出信息:
public final class java.lang.Double extends java.lang.Number
{
public java.lang.Double(double);
public java.lang.Double(java.lang.String)throws java.lang.NumberFormatException;
public static final double POSITIVE_INFINITY;
public static final double NEGATIVE_INFINITY;
public static final double NaN;
public static final double MAX_VALUE;
public static final double MIN_NORMAL;
public static final double MIN_VALUE;
public static final int MAX_EXPONENT;
public static final int MIN_EXPONENT;
public static final int SIZE;
public static final int BYTES;
public static final java.lang.Class TYPE;
private final double value;
private static final long serialVersionUID;
public boolean equals(java.lang.Object);
public static java.lang.String toString(double);
public java.lang.String toString();
public int hashCode();
public static int hashCode(double);
public static double min(double, double);
public static double max(double, double);
public static native long doubleToRawLongBits(double);
public static long doubleToLongBits(double);
public static native double longBitsToDouble(long);
public volatile int compareTo(java.lang.Object);
public int compareTo(java.lang.Double);
public byte byteValue();
public short shortValue();
public int intValue();
public long longValue();
public float floatValue();
public double doubleValue();
public static java.lang.Double valueOf(double);
public static java.lang.Double valueOf(java.lang.String)throws java.lang.NumberFormatException;
public static java.lang.String toHexString(double);
public static int compare(double, double);
public java.lang.Double resolveConstantDesc(java.lang.invoke.MethodHandles$Lookup);
public volatile java.lang.Object resolveConstantDesc(java.lang.invoke.MethodHandles$Lookup)throws java.lang.ReflectiveOperationException;
public java.util.Optional describeConstable();
public static boolean isNaN(double);
public boolean isNaN();
public static boolean isInfinite(double);
public boolean isInfinite();
public static boolean isFinite(double);
public static double sum(double, double);
public static double parseDouble(java.lang.String)throws java.lang.NumberFormatException;
}
利用反射在运行时分析对象
前文讲过如何利用反射分析一个类的组成,那么对于类运行时的实例而言,能不能获取到对象实例的具体值呢?能
要做到这一点,需要用到Field
类中的get()
和set()
(同样Method
类、Constructor
类也有这个方法),例如看下面的代码:
var harry = new Employee("Harry Hacker", 50000, 10, 1, 1989);
Class cl = harry.getClass();
Field f = cl.getDeclaredField("name");
// the 'name' field of the Employee class
object v = f.get(harry); // 获取harry对象中字段为name的值
// output:“Harry Hacker”
同样更改值,可以使用:
f.set(harry, "Askia"); // 设置harry对象中字段为name的值
当然上面的get()
、set()
代码存在问题,因为name
字段修饰符是private
,因此对该字段的值进行访问会抛出illegalAccessException
。
Java安全机制允许查看一个对象有哪些字段,但是除非拥有访问权限,否则不能对这些字段进行读写。
那么就真的没有办法对这些字段进行强制修改了吗?也不是,我们可以调用setAccessible()
来覆盖java的访问控制
f.setAccessible(true);
f.set(harry, "Askia");
// now harry.name is "Askia"
通用的toString()
使用反射调用任意的方法
使用反射编写泛型数组代码
反射(Reflection)的更多相关文章
- [.net 面向对象程序设计进阶] (21) 反射(Reflection)(下)设计模式中利用反射解耦
[.net 面向对象程序设计进阶] (21) 反射(Reflection)(下)设计模式中利用反射解耦 本节导读:上篇文章简单介绍了.NET面向对象中一个重要的技术反射的基本应用,它可以让我们动态的调 ...
- [.net 面向对象程序设计进阶] (20) 反射(Reflection)(上)利用反射技术实现动态编程
[.net 面向对象程序设计进阶] (20) 反射(Reflection)(上)利用反射技术实现动态编程 本节导读:本节主要介绍什么是.NET反射特性,.NET反射能为我们做些什么,最后介绍几种常用的 ...
- [整理]C#反射(Reflection)详解
本人理解: 装配件:Assembly(程序集) 晚绑定:后期绑定 MSDN:反射(C# 编程指南) -----------------原文如下-------- 1. 什么是反射2. 命名空间与装配件的 ...
- CSharpGL(43)环境映射(Environment Mapping)-天空盒(Skybox)反射(Reflection)和折射(Refraction)
CSharpGL(43)环境映射(Environment Mapping)-天空盒(Skybox)反射(Reflection)和折射(Refraction) 开始 如图所示,本文围绕GLSL里的sam ...
- 代理(Proxy)和反射(Reflection)
前面的话 ES5和ES6致力于为开发者提供JS已有却不可调用的功能.例如在ES5出现以前,JS环境中的对象包含许多不可枚举和不可写的属性,但开发者不能定义自己的不可枚举或不可写属性,于是ES5引入了O ...
- Golang 反射reflection
反射reflection 反射可大大提高程序的灵活性,使得interface{}有更大的发挥余地 反射使用TypeOf和ValueOf函数从接口中获取目标对象信息 反射会将匿名字段作为独立字段(匿名字 ...
- C# 反射(Reflection)技术
本文参考自C#反射(Reflection)详解,纯属学习笔记,加深记忆 在介绍反射前,先介绍一个重要的知识点 .Net应用程序是由程序集(Assembly).模块(Module).类型 ...
- C#反射(Reflection)详解
1. 什么是反射2. 命名空间与装配件的关系3. 运行期得到类型信息有什么用4. 如何使用反射获取类型5. 如何根据类型来动态创建对象6. 如何获取方法以及动态调用方法7. 动态创建委托 1.什么是反 ...
- C# 反射Reflection——反射反射程序员的快乐
一.什么是反射 反射Reflection:System.Reflection,是.Net Framework提供的一个帮助类库,可以读取并使用metadata. 反射是无处不在的,MVC-Asp.Ne ...
- Laravel学习笔记之PHP反射(Reflection) (上)
Laravel学习笔记之PHP反射(Reflection) (上) laravel php reflect 2.1k 次阅读 · 读完需要 80 分钟 3 说明:Laravel中经常使用PHP的反 ...
随机推荐
- 3.CDN加速简介
什么是CDN CDN的全称是Content Delivery Network,即内容分发网络.CDN的基本原理是广泛采用各种缓存服务器,将这些缓存服务器分布到用户访问相对集中的地区或网络中,在用户访问 ...
- NGINX 负载均衡的理解
前言 NGINX是轻量级,也是当前比较流行的web服务器软件.体积小但是功能强大. 这里我按照自己的理解,记录下对NGINX负载均衡的认识.(加权均衡,最小连接) 这里参考了 [https://blo ...
- [Leetcode]585. 2016年的投资(MySQL)
题目 写一个查询语句,将 2016 年 (TIV_2016) 所有成功投资的金额加起来,保留 2 位小数. 对于一个投保人,他在 2016 年成功投资的条件是: 他在 2015 年的投保额 (TIV_ ...
- js学习笔记之作用域链和闭包
在学习闭包之前我们很有必要先了解什么是作用域链 一.作用域链 作用域链是保证对执行环境有权访问的所有变量和函数的有序访问. 这句话其实还是蛮抽象的,但是通过下面一个例子,我们就能清楚的了解到作用域链了 ...
- 解Bug之路-记一次对端机器宕机后的tcp行为
解Bug之路-记一次对端机器宕机后的tcp行为 前言 机器一般过质保之后,就会因为各种各样的问题而宕机.而这一次的宕机,让笔者观察到了平常观察不到的tcp在对端宕机情况下的行为.经过详细跟踪分析原因之 ...
- powershell中使用Get-FileHash计算文件的hash值
今天在公司一台windows服务器上.需要对两个文件进行比对,笔者首先就想到了可以使用md5校验 但是公司服务器上又不可以随意安装软件,于是笔者想到了可以试试windows自带的powershell中 ...
- Java基础一篇过(八)常见异常速查
一.引言 开发过程中可能会遇到各种各样的异常,这里还是汇总一些比较典型的异常,有些比较直观的异常如空指针这种就不写了,此文可作为异常速查用. 二.异常大军正在来袭~ IllegalArgumentEx ...
- Spring学习(四)IOC详解
一.简介 概念:控制反转是一种通过描述(在 Java 中可以是 XML 或者注解)并通过第三方(Spring)去产生或获取特定对象的方式.(被动创建) 优势: ① 降低对象之间的耦合 ② 我们不需要理 ...
- 面试可能遇到的关联式容器(map、set等)相关问题
>>>. map与set的区别是什么,各有哪些优势? map中存储的数据是以键值对(key - value)形式并且通过排序(比较key,默认以 '<' 方式排序)存在的( ...
- 小伙伴问我:如何搭建Maven私服?我连夜肝了这篇实战文章!!
写在前面 十一假期期间,也有很多小伙伴不忘学习呀,看来有很多小伙伴想通过十一长假来提升自己的专业技能!这不,就有小伙伴在微信上问我:如何搭建Maven私服?让我专门推一篇搭建Maven私服的文章.安排 ...