Java学习笔记——反射(Reflection)

关于反射

能够分析类能力的程序称之为反射(Reflection)

反射机制可以用来:

  1. 在运行时分析类的能力
  2. 在运行时检查对象,例如:编写一个适合所有类的toString()
  3. 实现泛型数组操作代码
  4. 利用Method对象(类似C++中的函数指针)
  5. 用户界面生成器

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");

反射应用

利用反射分析类

反射机制中常用来做类分析的重要类:FieldMethodConstructor。这些类都在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)的更多相关文章

  1. [.net 面向对象程序设计进阶] (21) 反射(Reflection)(下)设计模式中利用反射解耦

    [.net 面向对象程序设计进阶] (21) 反射(Reflection)(下)设计模式中利用反射解耦 本节导读:上篇文章简单介绍了.NET面向对象中一个重要的技术反射的基本应用,它可以让我们动态的调 ...

  2. [.net 面向对象程序设计进阶] (20) 反射(Reflection)(上)利用反射技术实现动态编程

    [.net 面向对象程序设计进阶] (20) 反射(Reflection)(上)利用反射技术实现动态编程 本节导读:本节主要介绍什么是.NET反射特性,.NET反射能为我们做些什么,最后介绍几种常用的 ...

  3. [整理]C#反射(Reflection)详解

    本人理解: 装配件:Assembly(程序集) 晚绑定:后期绑定 MSDN:反射(C# 编程指南) -----------------原文如下-------- 1. 什么是反射2. 命名空间与装配件的 ...

  4. CSharpGL(43)环境映射(Environment Mapping)-天空盒(Skybox)反射(Reflection)和折射(Refraction)

    CSharpGL(43)环境映射(Environment Mapping)-天空盒(Skybox)反射(Reflection)和折射(Refraction) 开始 如图所示,本文围绕GLSL里的sam ...

  5. 代理(Proxy)和反射(Reflection)

    前面的话 ES5和ES6致力于为开发者提供JS已有却不可调用的功能.例如在ES5出现以前,JS环境中的对象包含许多不可枚举和不可写的属性,但开发者不能定义自己的不可枚举或不可写属性,于是ES5引入了O ...

  6. Golang 反射reflection

    反射reflection 反射可大大提高程序的灵活性,使得interface{}有更大的发挥余地 反射使用TypeOf和ValueOf函数从接口中获取目标对象信息 反射会将匿名字段作为独立字段(匿名字 ...

  7. C# 反射(Reflection)技术

    本文参考自C#反射(Reflection)详解,纯属学习笔记,加深记忆 在介绍反射前,先介绍一个重要的知识点         .Net应用程序是由程序集(Assembly).模块(Module).类型 ...

  8. C#反射(Reflection)详解

    1. 什么是反射2. 命名空间与装配件的关系3. 运行期得到类型信息有什么用4. 如何使用反射获取类型5. 如何根据类型来动态创建对象6. 如何获取方法以及动态调用方法7. 动态创建委托 1.什么是反 ...

  9. C# 反射Reflection——反射反射程序员的快乐

    一.什么是反射 反射Reflection:System.Reflection,是.Net Framework提供的一个帮助类库,可以读取并使用metadata. 反射是无处不在的,MVC-Asp.Ne ...

  10. Laravel学习笔记之PHP反射(Reflection) (上)

    Laravel学习笔记之PHP反射(Reflection) (上) laravel php reflect 2.1k 次阅读  ·  读完需要 80 分钟 3 说明:Laravel中经常使用PHP的反 ...

随机推荐

  1. JWT理论理解

    什么是JWT Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的 ...

  2. 14个Spring MVC顶级技巧,随时用随时爽,一直用一直爽~

    通常,在Spring MVC中,我们编写一个控制器类来处理来自客户端的请求.然后,控制器调用业务类来处理与业务相关的任务,然后将客户端重定向到逻辑视图名称,该名称由Spring的调度程序Servlet ...

  3. mxgraph浅入

    mxgraph浅入 参考文献:https://www.cnblogs.com/xuxg/articles/3246206.html 1.了解 (1)如何判断需要引入mxgraph产品:过程图.工作流和 ...

  4. @RequestParam,@RequestBody,@ResponseBody,@PathVariable注解的一点小总结

    一.前提知识: http协议规定一次请求对应一次响应,根据不同的请求方式,请求的内容会有所不同: 发送GET请求是没有请求体的,参数会直接拼接保留到url后一并发送: 而POST请求是带有请求体的,带 ...

  5. Filecoin: 挖矿流程扫盲

    前言 IPFS以及FileCoin的白皮书知识量比较大,需要花费一些时间成本来学习. 下面先针对挖矿相关的知识进行研究,总结一下Filecoin中挖矿的流程以及相关概念. What is Fileco ...

  6. 深入总结Javascript原型及原型链

    本篇文章给大家详细分析了javascript原型及原型链的相关知识点以及用法分享,具有一定的参考价值,对此有需要的朋友可以参考学习下.如有不足之处,欢迎批评指正. 我们创建的每个函数都有一个 prot ...

  7. 内存操作【memset】【memcpy】

    void *memset(void *s, int c, unsigned long n); 将指针变量 s 所指向的前 n 字节的内存单元用一个"整数" c 替换,注意 c 是 ...

  8. 零基础学习Kmeans聚类算法的原理与实现过程

    内容导入: 聚类是无监督学习的典型例子,聚类也能为企业运营中也发挥者巨大的作用,比如我们可以利用聚类对目标用户进行群体分类,把目标群体划分成几个具有明显特征区别的细分群体,从而可以在运营活动中为这些细 ...

  9. 【SCOI2016】背单词

    P3294[SCOI2016]背单词 [提示] 这道题大概是告诉我们,让我们用一堆n个单词安排顺序,如果当前位置为x,当前单词的后缀没在这堆单词出现过,代价就为x,这里的后缀是原意,但不算自己(不算本 ...

  10. 第一次面试linux后台岗位

    今天给大家分享前段时间面试linux后台的面试题目,我从里面挑了几道大家比较陌生的题目,而且要那种手写代码的题目,这方面肯定很多人在实际面试时最怕的题目! 1.请说出如何用tcp服务实现文件的断点续传 ...