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. MaaS系统概述

    摘要:共享经济正改变着人们的生活方式,城市公共交通系统应该顺应共享经济的潮流进行转型.近年来,西方国家提出的“出行即服务(MaaS)”理念为我国解决日益严重的城市交通拥堵问题提供了新的思路.基于Maa ...

  2. Hadoop演进与Hadoop生态

    1.了解对比Hadoop不同版本的特性,可以用图表的形式呈现. (1)0.20.0~0.20.2: Hadoop的0.20分支非常稳定,虽然看起来有些落后,但是经过生产环境考验,是 Hadoop历史上 ...

  3. 面试题:JVM在Java堆中对对象的创建、内存结构、访问方式

    一.对象创建过程 1.检查类是否已被加载 JVM遇到new指令时,首先会去检查这个指令参数能否在常量池中定位到这个类的符号引用,检查这个符号引用代表的类是否已被加载.解析.初始化,若没有,则进行类加载 ...

  4. Powershell编程基础-004-for语句的使用

    For循环在PowerShell中也称为For语句. 同其他编程语言类似 当指定条件的值为True时,此循环以代码块的形式执行语句,另,For后面接(;;)代表恒为真! 实例1:用于执行指定次数的语句 ...

  5. gRPC-go 入门(1):Hello World

    摘要 在这篇文章中,主要是跟你介绍一下gRPC这个东西. 然后,我会创建一个简单的练习项目,作为gRPC的Hello World项目. 在这个项目中,只有很简单的一个RPC函数,用于说明gRPC的工作 ...

  6. DVWA SQL-injection 附python脚本

    SQL-Injection low等级 首先我们将dvwa等级调到low 如图 接下来选择SQL Injection,并在提交框中输入正常值1,查看返回结果 接下来检测是否存在注入,分别输入 1' a ...

  7. 关于struts1与struts2

    1.Action方面 Action是整个Struts框架的核心内容,Struts1.x与Struts2的Action模型很大的区别.Struts2是一个 pull-MVC架构 ,从开发者角度看,就是说 ...

  8. 微服务实战系列(六)-网关springcloud zuul

    1. 场景描述 今天接着介绍springcloud,今天介绍下springcloud的路由网关-Zuul,外围系统或者用户通过网关访问服务,网关通过注册中心找到对应提供服务的客户端,网关也需要到注册中 ...

  9. (转载)浏览器 user-agent 字符串的故事

    本文转载自:http://www.cnblogs.com/ifantastic/p/3481231.html. 如有侵权,请联系处理!   你是否好奇标识浏览器身份的User-Agent,为什么每个浏 ...

  10. mysql-10-union

    #进阶10:联合查询 /* union联合 将多条查询语句的结果合并成一个结果 语法: 查询1 union 查询2 union 查询3 ... 应用场景:要查询的结果来自于多个表,且多个表没有直接的连 ...