可以分析类能力的程序称之为反射(Reflection)java
反射机制能够用来:数组
toString()
在程序运行时,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
类来构造一个类实例呢?答案确定是能的
使用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
对象getResource()
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");
反射机制中经常使用来作类分析的重要类:Field
、Method
、Constructor
。这些类都在java.lang.reflect
包中
接下来对这几个类用来分析的方法进行简单介绍:
String getName() // 返回该类型的类名字 String getPackageName() // 返回该类所在的包名 Field[] getFields() // 返回对象的全部公共字段,包括超类的公共字段 Field[] getDeclaredFields() // 返回对象的所有字段,若是类中没有字段,或者对象是基本类型或者数组,则返回0长度数组 Class getSuperClass() // 获取该类的父类Class对象 Method[] getMethods() // 返回对象所属类或者接口的全部公共方法,包括超类的公共方法 Method[] getDeclaredMethods() // 返回对象所属类或者接口的所有方法,不包括超类 Constructor[] getConstructors() // 返回这个类的全部公共构造器 Constructor[] getDeclaredConstructors() // 返回所有构造器
String getName() // 返回类中的字段名(属性名)的字符串 Class getType() // 返回字段的类型(int、long、Date...) int getModifiers() // 获取字段的修饰符(public、static、final...),返回1/0的二进制标志位,能够配合reflect包中的toString()来显示具体的修饰符 Class getDeclaringClass() //获取字段所属的类对应的Class对象
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对象数组,其中各个对象表示该方法所抛出的异常的类型
String getName() // 返回类中的构造方法的字符串 int getModifiers() // 获取构造方法的修饰符(public、static、final...),返回1/0的二进制标志位,能够配合reflect包中的toString()来显示具体的修饰符 Class getDeclaringClass() //获取构造方法所属的类对应的Class对象 Class[] getParameterTypes() // 返回Class对象的数组,其中各个对象表示参数的类型 Class[] getExceptionTypes() // 返回Class对象数组,其中各个对象表示该方法所抛出的异常的类型
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()