反射(Reflection)

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

关于反射

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

反射机制能够用来:数组

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

Class类

在程序运行时,JRE始终为全部对象维护一个运行时类型标识(我的猜想其实是一个结构体)。这个类型标识会跟踪记录每个对象所属的类。虚拟机利用这些信息来保证调用正确的方法安全

Class类保存了类型标识Class类是泛型类,你懂我什么意思的吧)内部的信息,所以能够使用这个特殊的类来访问这些信息。dom

那么何如获取这些信息呢?

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是合法的。code

若是我想实现动态加载加载类,或者我如今知道这个类的类名(或者接口名),则还能够使用Class类自己的静态方法来实现类加载orm

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()

使用反射调用任意的方法

使用反射编写泛型数组代码

相关文章
相关标签/搜索