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. python 中简单的输出语句

    1 python 中简单的输出语句 #coding:utf-8#输出的是整数,得到的也是整数,用raw_inputusername=raw_input('请输入用户名:')#输出的是整数或者字符串,得 ...

  2. 5.Scalal语法03 - 函数

  3. session深入探讨

    简介 session(会话),其实是一个容易让人误解的词.它总跟web系统的会话挂钩,利用session,javaweb项目实现了登录状态的控制.坊间流传,关闭浏览器,就是关闭了web系统的会话. 其 ...

  4. 一分钟理解Redo Undo

    数据库中有一种特殊的"日志文件"叫 Redo(重做) Undo(撤销),传统意义上的日志文件是记录系统运行状态的,主要用于系统工程师或者程序员排错.而 Reod/Undo 文件是数 ...

  5. GEKCTF2020-web

    GEKCTF [GKCTF2020]CheckIN97 <title>Check_In</title> <?php highlight_file(__FILE__); c ...

  6. ios7.1发布企业证书测试包的问题

    关于升级了ios7.1之后发布企业版证书的测试包不能下载的问题,这个苹果也挺坑的,什么都不说,也不警告一下,直接就不能用了 用xcode的organizer里面的console里发现安装的时候提示这个 ...

  7. Lua设计与实现--读书笔记

    目录 lua简介 一种通用的数据类型:lua_TValue 字符串 Table lua实现一个队列 lua简介 C++底层核心模块,暴露核心接口给lua脚本层,网络的收发都在c++层完成,本书简述lu ...

  8. 理解vue与MVVM三要素

    MVVM到底是什么,跟Jquery有什么区别? MVVM理解,跟MVC区别 Model View Controller,一般是用户操作view视图按钮,触发controller内方法,cotrolle ...

  9. GTA5整合版

    GTA5mod整合版游戏介绍 GTA5mod整合版游戏是一款完美破解的游戏,玩家能够在游戏中享受最爽快的角色动作扮演玩法,在这里你将是一名强大的黑帮分子,在这个都市中,你将体验最真实的黑帮社会玩法.G ...

  10. Optimisation

    https://www.cnblogs.com/wuyudong/p/writing-efficient-c-and-code-optimization.html 1 不要过多使用 stack ,尽量 ...