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. Java编程风格

    来自<The Elements of Java Style>-<Java编程风格>一书,值得一读的书籍,会让你在细节上节省很多时间,合作之间更加愉快! 好处不多说了,但是有几个 ...

  2. window杀进程-----引用

    Windows平台   两步方法 :  1 查询端口占用,2 强行杀死进程 netstat -aon|findstr "8080" taskkill /pid 4136-t -f ...

  3. 破晓行动----带你总结JVM的知识大全(二)

    JVM运行时内存 + 垃圾回收与算法

  4. mybatis进行mapper.xml测试的时候发生"必须为元素类型 “mapper” 声明属性 “namespace”

    1.Caused by Caused by: org.xml.sax.SAXParseException; lineNumber: 7; columnNumber: 45; 必须为元素类型 " ...

  5. JAVA并发笔记

    重入锁的特性, 避免死锁, 如果有锁的话, 不用重新加锁, 直接增加锁的次数.. Synchronize, ReentrantLock都是重入锁. 读写锁, ReentrantReadWriteLoc ...

  6. 《Head First 设计模式》:代理模式

    正文 一.定义 代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问. 要点: 代理模式为一个对象创建了代理对象,让代理对象控制对该对象的访问.被代理的对象可以是远程的对象.创建开销大的对象 ...

  7. How to read h5 file by Matlab

    In matlab, one can use the following command to read h5 file data = h5read(filename,ds) data = h5rea ...

  8. 理解pytorch几个高级选择函数(如gather)

    目录 1. 引言 2. 维度的理解 3. gather函数 4. index_select函数 5. masked_select函数 6. nonzero函数 1. 引言   最近在刷开源的Pytor ...

  9. 探究"补阶乘大法的本质"——糖水不等式!

    废话不多说先来康一条例题: 证明: 下面给出题目的一种解法(我称之为"补阶乘大法"): 思考:为什么补上一个阶乘(准确说不是阶乘,是两个数阶乘的之商)项,放缩后再给去掉,就能达到我 ...

  10. 013 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 07 基本数据类型变量的存储

    013 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 07 基本数据类型变量的存储 变量和它的值如何在内存中进行存储的? 前面学习过:Java中的数据类型分为基本 ...