什么是反射
反射是一个很牛的功能,能够在程序运行时修改程序的行为。但是反射是非常规手段,反射有风险,应用需谨慎。
把程序代码比作一辆车,因为Java是面向对象的语言,车子有自己的型号、车牌号、发动机,有倒车、泊车等功能。
正常情况下,我们需要为车子配备一个司机,然后按照行为准则规范驾驶。
反射是非常规手段,正常行驶的时候,车子需要司机的驾驶。但是反射不需要,因为它就是车子的自动驾驶。(非常规嘛)
- 反射技术通常被用来检测和改变应用程序在 Java 虚拟机中的行为表现。它是一个相对而言比较高级的技术,通常它应用的前提是开发者本身对于 Java 语言特性有很强的理解的基础上。值得说明的是,反射是一种强有力的技术特性,因此可以使得应用程序突破一些藩篱,执行一些常规手段无法企及的目的。
Class
Java是面向对象的语言,基本上是以类为基础构造了整个程序。反射中要求提供的规格说明书就是类的规格说明书。
-
其中小写的class是定义类的关键字。
-
Class本质是一个类。
public final class Class<T> implements java.io.Serializable, GenericDeclaration, Type, AnnotatedElement, TypeDescriptor.OfField<Class<?>>, Constable {}
-
Class就是一个对象,运行在Java虚拟机中的类和接口。
Class的获取
反射的入口是Class,但是反射中Class是没有公开的构造函数,不能通过关键字new来获取Class的对象,所以就有如下3种获取Class的方式
-
Object.getClass()
- 对于一个对象而言,如果能访问,那么调用getClass()方法就可以获取到它对应的Class对象。
- 如下这种方法不适合基本类型 :int float
public class Car { } public class CarTest { public static void main(String[] args) { Car car = new Car(); Class aClass = car.getClass(); } }
-
通过 .class标识
- 对于上述方法,Car是一个类,car是一个对象,通过car.getClass()就获取到Car这个类的Class对象。
- 如果不想通过类的对象的getClass()方式去获取,就可以通过 ‘.class的标识’来获取。
public class CarTest { public static void main(String[] args) { Class<Car> carClass = Car.class; Class<Integer> integerClass = int.class; Class<String> stringClass = String.class; } }
-
通过 Class.forName()方法
- 在Android中,Google的Android工程师把一些类加上了@hide注解,这些被@hide注解修饰的类就没有出现在SDK中。
- 我们要获取到这个在当前开发环境中不存在的类的Class对象,就可以通过此方法来获取到。
- 如下代码中,"com.dashingqi.reflect.Car"就是Car类的全限定类名称,包括 包名+类名。
- 如果找不到该类的时候,会抛出ClassNotFoundException,因为如果这个没有加载到JVM中的时候,需要告诉我们。
public class CarTest { public static void main(String[] args) { try { Class.forName("com.dashingqi.reflect.Car"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
Class的内容清单
Class的名字获取
Class对象也有名字,涉及到API如下
- Class.getName()
- Class.getSimpleName()
- Class.getCanonicalName()
因为Class是一个入口,它代表引用、基本数据类型以及数组对象,所以获取它们的方式也是不同的。
getName
当Class代表一个引用时
- getName放回的是一个二进制形式的字符串 “com.dashingqi.reflect.Car”
当Class代表是一个基本数据类型的时候
- getName返回的就是它们的关键字 比如 int.class的名字就是int
当Class代表是基本数据类型的数组时(int [] [] [] 这样的3维数组)
-
getName返回的是[[[I 这样的字符串
-
Java在这个地方有相应的规则,在元素类型的前面添加相应数量的[符号,用[符号的数量来表示数组的维度,对于基本数据类型都有相应的编码,都是大写的字母;规则如下
元素类型 | 编码 |
---|---|
boolean | Z |
byte | B |
char | C |
Double | D |
Float | F |
int | I |
Long | L |
short | S |
类或者接口 | L类名;(是有;的哟) |
-
代码操作一下
public class CarTest { public static void main(String[] args) { try { Class<?> aClass = Class.forName("getclassinstance.Car"); Class<Integer> integerClass = int.class; Class<Float> floatClass = float.class; Class<? extends int[]> aClass1 = new int[]{}.getClass(); Class<? extends Car[]> aClass2 = new Car[]{}.getClass(); //代表一个引用 System.out.println("aClass = " + aClass.getName()); //基本数据类型 System.out.println("integerClass = " + integerClass.getName()); System.out.println("floatClass=" + floatClass.getName()); //基本数据类型的数组 System.out.println("aClass1=" + aClass1.getName()); //类或者接的数组 System.out.println("aClass2=" + aClass2.getName()); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } //运行结果如下 aClass = getclassinstance.Car integerClass = int floatClass=float aClass1=[I aClass2=[Lgetclassinstance.Car;
getSimpleName()
-
先看一个嵌套类
public class ClassA { public static class ClassB{ } } public class ClassTest { public static void main(String[] args) { ClassA.ClassB classB = new ClassA.ClassB(); String name = classB.getClass().getName(); String simpleName = classB.getClass().getSimpleName(); System.out.println("name = " + name); System.out.println("simpleName = " + simpleName); } } //运行结果 name = getSimpleName.ClassA$ClassB simpleName = ClassB
- 因为是内部类,通过getName得到是的二进制形式的全限定类名,并且类名前面还有一个$符号。
- getSimpleName()则返回一个ClassB,去掉了包名限定。
-
当数组的Class获取simpleName的时候,不同于getName,simpleName不在前面加[,而是在后面加[]
public class ClassTest { public static void main(String[] args) { Class<? extends ClassA[]> aClass = new ClassA[]{}.getClass(); String name1 = aClass.getName(); System.out.println("name1 = " + name1); String simpleName1 = aClass.getSimpleName(); System.out.println("simpleName1=" + simpleName1); } } //运行结果如下 name1 = [LgetSimpleName.ClassA; simpleName1=ClassA[]
-
对于匿名内部类来说,通过getSimpleName()获取的是一个空的字符串
public class ClassTest { public static void main(String[] args) { Runnable runnable = new Runnable() { @Override public void run() { } }; System.out.println("name = " + runnable.getClass().getName()); System.out.println("simple name = " + runnable.getClass().getSimpleName()); } } // 运行结果如下 name = getSimpleName.ClassTest$1 simple name =
getCanonicalName
Canonial 是官方、标准的意思,那么该方法就是获取Class对象的官方名字。
这个CanonicalName是官方规定的,如果Class对象没有canonicalName的话就返回一个null
getCanonicalName()是getName()与getSimpleName()的结合
-
getCanonicalName()返回的也是全限定类名,但是对于内部类来说,不用$开头,而是用.。
-
getCanonicalName()对于数组的Class,同getSimpleName()也是在后面加[]。
-
getCanonicalName()不同于getSimpleName()的是,如果不存在canonicalName就返回null而不是空字符串。
-
局部类和匿名内部类不存在canonicalName。
-
代码
public class ClassTest { public static void main(String[] args) { ClassA.ClassB classB = new ClassA.ClassB(); Runnable runnable = new Runnable() { @Override public void run() { } }; String canonicalName = classB.getClass().getCanonicalName(); String canonicalName1 = new ClassA.ClassB[][][]{}.getClass().getCanonicalName(); System.out.println("canonicalName = " + canonicalName); System.out.println("canonicalName1 = " + canonicalName1); System.out.println("run name = " + runnable.getClass().getCanonicalName()); /** * local是局部类 */ class Local { } ; String name = Local.class.getName(); String simpleName = Local.class.getSimpleName(); String canonicalName2 = Local.class.getCanonicalName(); System.out.println("local name = " + name); System.out.println("local simple name = " + simpleName); System.out.println("local canonical name = " + canonicalName2); } } //运行结果 canonicalName = getSimpleName.ClassA.ClassB canonicalName1 = getSimpleName.ClassA.ClassB[][][] run name = null local name = getSimpleName.ClassTest$1Local local simple name = Local local canonical name = null
Class获取修饰符
Java开发中定义一个类,往往要通过很多修饰符来配合使用的。大致分为如下四类
- 用来限制作用域:publish、protected、private
- 用来标记为静态:static
- 注解
- 用来提示子类来复写:abstract
Java反射提供了API去获取这些修饰符
-
定义一个被 abstract 和 publish 修饰的类
public abstract class DemoA { }
-
我们现在要提取这些修饰符,只需要调用 Class.getModifiers()
public class Demo { public static void main(String[] args) { /** * public native int getModifiers(); * 是一个native层的方法 */ int modifiers = DemoA.class.getModifiers(); System.out.println("modifiers = " + modifiers); System.out.println("modifiers_str = "+ Modifier.toString(modifiers)); } }
-
运行结果如下
modifiers = 1025 modifiers_str = public abstract
-
Java工程师考虑到了位运算,用一个int数值来记录所有的修饰符,然后不同的位对应不同的修饰符,这些修饰符都定义在Modifier类中。
public static String toString(int mod) { StringJoiner sj = new StringJoiner(" "); if ((mod & PUBLIC) != 0) sj.add("public"); if ((mod & PROTECTED) != 0) sj.add("protected"); if ((mod & PRIVATE) != 0) sj.add("private"); /* Canonical order */ if ((mod & ABSTRACT) != 0) sj.add("abstract"); if ((mod & STATIC) != 0) sj.add("static"); if ((mod & FINAL) != 0) sj.add("final"); if ((mod & TRANSIENT) != 0) sj.add("transient"); if ((mod & VOLATILE) != 0) sj.add("volatile"); if ((mod & SYNCHRONIZED) != 0) sj.add("synchronized"); if ((mod & NATIVE) != 0) sj.add("native"); if ((mod & STRICT) != 0) sj.add("strictfp"); if ((mod & INTERFACE) != 0) sj.add("interface"); return sj.toString(); } /* * Access modifier flag constants from tables 4.1, 4.4, 4.5, and 4.7 of * <cite>The Java™ Virtual Machine Specification</cite> */ /** * The {@code int} value representing the {@code public} * modifier. */ public static final int PUBLIC = 0x00000001; /** * The {@code int} value representing the {@code private} * modifier. */ public static final int PRIVATE = 0x00000002; /** * The {@code int} value representing the {@code protected} * modifier. */ public static final int PROTECTED = 0x00000004; /** * The {@code int} value representing the {@code static} * modifier. */ public static final int STATIC = 0x00000008; /** * The {@code int} value representing the {@code final} * modifier. */ public static final int FINAL = 0x00000010; /** * The {@code int} value representing the {@code synchronized} * modifier. */ public static final int SYNCHRONIZED = 0x00000020; /** * The {@code int} value representing the {@code volatile} * modifier. */ public static final int VOLATILE = 0x00000040; /** * The {@code int} value representing the {@code transient} * modifier. */ public static final int TRANSIENT = 0x00000080; /** * The {@code int} value representing the {@code native} * modifier. */ public static final int NATIVE = 0x00000100; /** * The {@code int} value representing the {@code interface} * modifier. */ public static final int INTERFACE = 0x00000200; /** * The {@code int} value representing the {@code abstract} * modifier. */ public static final int ABSTRACT = 0x00000400; /** * The {@code int} value representing the {@code strictfp} * modifier. */ public static final int STRICT = 0x00000800; // Bits not (yet) exposed in the public API either because they // have different meanings for fields and methods and there is no // way to distinguish between the two in this class, or because // they are not Java programming language keywords static final int BRIDGE = 0x00000040; static final int VARARGS = 0x00000080; static final int SYNTHETIC = 0x00001000; static final int ANNOTATION = 0x00002000; static final int ENUM = 0x00004000; static final int MANDATED = 0x00008000;
获取Class的成员
获取Filed
获取到指定名字的属性API
-
getDeclaredField()方法获取的是Class中任何修饰符修饰的属性名字,name
public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException { }
-
getField(String name):当前 name是被publish修饰 就能获取到,否则抛出异常
@CallerSensitive public Field getField(String name) }
获取到所有属性
-
getDeclaredFieleds()获取到所有属性,但不包括从父类继承下来的属性
public Field[] getDeclaredFields() throws SecurityException { }
-
getFields() 获取到自身的所有public属性,包括从父类继承下来的
public Field[] getFields() throws SecurityException { }
-
代码实操
public class MainTest { public static void main(String[] args) { Class<Son> sonClass = Son.class; try { //获取到属性名字, Field a = sonClass.getDeclaredField("d"); System.out.println("getDeclaredField name = " + a.getName()); } catch (NoSuchFieldException e) { e.printStackTrace(); System.out.println("getDeclaredField = " + e.getMessage()); } try { //获取 publish修饰的属性名字,如果自己找不到,会去查找父类中的 Field c = sonClass.getField("c"); System.out.println("getField name = " + c.getName()); } catch (NoSuchFieldException e) { e.printStackTrace(); } //获取到所有属性的名字,不包括父类的 Field[] declaredFields = sonClass.getDeclaredFields(); for (Field f : declaredFields){ System.out.println("getDeclaredFields name = "+f.getName()); } //获取到自身所有的publish修饰的属性名字,包括从父类继承下来的 Field[] fields = sonClass.getFields(); for (Field field : fields){ System.out.println("getFields name = "+field.getName()); } } } //运行结果如下 getDeclaredField name = d java.lang.NoSuchFieldException: c at java.base/java.lang.Class.getField(Class.java:2004) at get_class_filed.MainTest.main(MainTest.java:21) getDeclaredFields name = c getDeclaredFields name = d getDeclaredFields name = f getFields name = b
获取Method
类获取接口中的方法对应到Class就是Method
-
getDeclaredMethod():获取到任何修饰符修饰的方法,不包括从父类继承到的
public Method getDeclaredMethod(String name, Class<?>... parameterTypes) }
-
getMethod():获取到任何publish修饰符修饰的方法,包括从父类中继承得到的
public Method getMethod(String name, Class<?>... parameterTypes) }
-
getDeclaredMethods():获取所有方法的名字,不包括从父类继承得到的
public Method[] getDeclaredMethods() throws SecurityException { }
-
getMethods():获取所有publish修饰方法的名字,包括从父类继承的来的(包括Object)
public Method[] getMethods() throws SecurityException { }
-
代码演示
public class MainTest { public static void main(String[] args) { Class<Son> sonClass = Son.class; try { Method methodC = sonClass.getDeclaredMethod("methodF", null); System.out.println("methodC = " + methodC.getName()); } catch (NoSuchMethodException e) { e.printStackTrace(); System.out.println(e.getMessage()); } try { Method methodA = sonClass.getMethod("methodF", null); System.out.println("methodA name = " + methodA.getName()); } catch (NoSuchMethodException e) { e.printStackTrace(); System.out.println(e.getMessage()); } Method[] declaredMethods = sonClass.getDeclaredMethods(); for (Method method : declaredMethods) { System.out.println("getDeclaredMethods name = " + method.getName()); } Method[] methods = sonClass.getMethods(); for (Method method : methods) { System.out.println(" getMethods name = " + method.getName()); } } } //运行结果如下 methodC = methodF java.lang.NoSuchMethodException: get_class_filed.Son.methodF() at java.base/java.lang.Class.getMethod(Class.java:2113) at get_class_filed.MainTest.main(MainTest.java:20) get_class_filed.Son.methodF() getDeclaredMethods name = methodC getDeclaredMethods name = methodF getDeclaredMethods name = methodD getDeclaredMethods name = methodG getMethods name = methodC getMethods name = methodD getMethods name = methodA getMethods name = wait getMethods name = wait getMethods name = wait getMethods name = equals getMethods name = toString getMethods name = hashCode getMethods name = getClass getMethods name = notify getMethods name = notifyAll
Constructor
Java把构造器从Method中拎出来,用Constructor来表示
Constructor不能从父类中继承,所以就没有办法通过getConstructor()来获取到父类的Constructor
-
getDeclaredConstructor():获取任何修饰符修饰的构造方法名字
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException }
-
getConstructor():获取当前Class对象中 publish修饰的构造方法的名字
public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException{ }
-
getDeclaredConstructors():获取所有构造方法名字
public Constructor<?>[] getDeclaredConstructors() throws SecurityException { }
-
getConstructors():获取所有publish修饰的构造方法名字
public Constructor<?>[] getConstructors() throws SecurityException { }
Field的操作
我们在类中定义字段时,通常是这样的
其中 c、d、f、g、demo这些变量都是属性,在反射机制中映射到Class对象中都是Field
它们要么是8中基本数据类型,或者是引用,所有的引用都是Object的子类。
public class Son extends Father {
int c;
private String d;
protected float f;
public int g;
Demo demo;
}
Field类型的获取
-
获取Field类型有2种方法(首先获取到Class对象种的Field,通过Field的一下两种方法获取Field类型)
public Type getGenericType() { } public Class<?> getType() { } //两种方法返回参数不一样,getGenericType()可以获取到泛型类型
-
代码实操
public class Son extends Father { int c; private String d; protected float f; public int g; Demo demo; public List<Demo> mDemoList; public Map<Integer, Integer> maps; } public static void main(String[] args) { Class<Son> sonClass = Son.class; Field[] fields = sonClass.getFields(); for (Field field : fields) { System.out.println("getName == " + field.getName()); System.out.println("getType = " + field.getType()); System.out.println("getGenericType = " + field.getGenericType()); System.out.println("======================"); } } //运行结果如下 getName == g getType = int getGenericType = int ====================== getName == mDemoList getType = interface java.util.List getGenericType = java.util.List<get_class_filed.Demo> ====================== getName == maps getType = interface java.util.Map getGenericType = java.util.Map<java.lang.Integer, java.lang.Integer> ====================== getName == b getType = int getGenericType = int ====================== // 我们看到 把自己Class对象种的publish属性和父类中的publish修饰的属性都给找到了 // getGenericType()把泛型的类型都给打印出来了 ,相比较与getType 更详细
Field修饰符的获取
-
通过getModifiers()方法就可以获取
public int getModifiers() { return modifiers; }
-
代码实操
public static void main(String[] args) { Class<Son> sonClass = Son.class; Field[] fields = sonClass.getFields(); for (Field field : fields) { System.out.println("getModifiers = "+ Modifier.toString(field.getModifiers())); System.out.println("======================"); } //运行结果如下 getModifiers = public ====================== getModifiers = public ====================== getModifiers = public ====================== getModifiers = public ======================
Field内容的读取与赋值
这也是反射机制中对Filed主要的目的
Filed定义了一些列set和get方法来设置和获取不同类型的值
-
get
public Object get(Object obj); public int getInt(Object obj); public long getLong(Object obj) throws IllegalArgumentException, IllegalAccessException; public float getFloat(Object obj) throws IllegalArgumentException, IllegalAccessException; public short getShort(Object obj) throws IllegalArgumentException, IllegalAccessException; public double getDouble(Object obj) throws IllegalArgumentException, IllegalAccessException; public char getChar(Object obj) throws IllegalArgumentException, IllegalAccessException; public byte getByte(Object obj) throws IllegalArgumentException, IllegalAccessException; public boolean getBoolean(Object obj) throws IllegalArgumentException, IllegalAccessException
-
set
public void set(Object obj, Object value); public void getInt(Object obj,int value); public void getLong(Object obj,long value) throws IllegalArgumentException, IllegalAccessException; public void getFloat(Object obj,float value) throws IllegalArgumentException, IllegalAccessException; public void getShort(Object obj,short value) throws IllegalArgumentException, IllegalAccessException; public void getDouble(Object obj,double value) throws IllegalArgumentException, IllegalAccessException; public void getChar(Object obj,char value) throws IllegalArgumentException, IllegalAccessException; public void getByte(Object obj,byte b) throws IllegalArgumentException, IllegalAccessException; public void getBoolean(Object obj,boolean b) throws IllegalArgumentException, IllegalAccessException
-
其中Object就是类的实例引用,Class本身不对成员进行存储,它只提供检索,所以需要Field、Method、Constructor对象来承载这些成员,所以针对成员的操作时,一般需要为成员指定类的实例引用,表示这个成员属于哪个Object的,是为了精准定位。
-
代码实操
public class MainTest { public static void main(String[] args) { Son son = new Son(); son.g = 10; System.out.println("son.g = " + son.g); //Class本身不对成员做存储 Class<Son> sonClass = Son.class; try { Field g = sonClass.getField("g"); int anInt = g.getInt(son); System.out.println(" reflection son.g = " + anInt); g.setInt(son, 16); System.out.println("son.g = " + son.g); } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } } //运行结果如下 son.g = 10 reflection son.g = 10 son.g = 16
-
上述代码是获取publish修饰的属性,这回找个private修饰的属性 h
Son son = new Son(); son.setH(10); System.out.println("son.g = " + son.getH()); //Class本身不对成员做存储 Class<Son> sonClass = Son.class; try { Field h = sonClass.getDeclaredField("h"); int anInt = h.getInt(son); System.out.println(" reflection son.g = " + anInt); h.setInt(son, 16); System.out.println("son.g = " + son.getH()); } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } //运行结果如下 son.g = 10 java.lang.IllegalAccessException: class get_class_filed.MainTest cannot access a member of class get_class_filed.Son with modifiers "private" at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:376) at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:639) at java.base/java.lang.reflect.Field.checkAccess(Field.java:1075) at java.base/java.lang.reflect.Field.getInt(Field.java:592) at get_class_filed.MainTest.main(MainTest.java:21) //报错了
-
这是因为反射过程操作了private属性,如果要消除异常加如下代码
h.setAccessible(true)
-
运行结果如下
son.g = 10 reflection son.g = 10 son.g = 16
-
Method操控
Method对应普通类的方法;
一般普通类的构成
public int add(int a, int b){
return a+b;
}
主要包括如下几个方面
- 方法名
- 方法的返回值类型
- 方法的参数
- 方法的修饰符
- 方法抛出的异常
Java的反射中针对Method 有相关操作API的来提取相关元素
获取Method方法名
-
获取方法名字的通过getName()方法
-
代码实操
public class MainClass { public static void main(String[] args) { Class<Car> carClass = Car.class; Method[] declaredMethods = carClass.getDeclaredMethods(); for (Method method : declaredMethods) { System.out.println("method name = " + method.getName()); } } } //运行结果如下 method name = getCarName method name = setCarName method name = getCarId method name = setCarId
Method获取方法参数
-
通过 getParameters()来获取
public Parameter[] getParameters() {}
-
返回是一个Parameter的数组,在反射中Parameter对象就是用来映射方法中的参数,经常使用的方法有如下
// 获取参数名字 public String getName() {} // 获取参数类型 public Class<?> getType() {} // 获取参数的修饰符 public int getModifiers() {}
-
当然有时我们不需要参数的名字,只需要参数的类型就可以,通过Method下面的方法获取
//获取所有参数的类型 public Class<?>[] getParameterTypes() { } //获取所有参数的类型,包括泛型 public Type[] getGenericParameterTypes() { return super.getGenericParameterTypes(); }
-
代码实操
public class MainClass { public static void main(String[] args) { Class<Car> carClass = Car.class; Method[] declaredMethods = carClass.getDeclaredMethods(); for (Method method : declaredMethods) { System.out.println("method name = " + method.getName()); //获取到方法中的参数 Parameter[] parameters = method.getParameters(); for (Parameter parameter : parameters) { //获取到参数的名字和参数和参数的修饰符 System.out.println("parameter = " + parameter.getName() + " " + parameter.getType().getName() + " Modifiers name = " + Modifier.toString(parameter.getModifiers())); } //获取到所有参数的类型 Class<?>[] pTypes = method.getParameterTypes(); System.out.println("method para types"); for (Class type : pTypes) { System.out.println(" type name = " + type.getName()); } //获取到所有参数的类型,包括泛型 Type[] genericParameterTypes = method.getGenericParameterTypes(); for (Type type : genericParameterTypes) { System.out.println(" generic type name = " + type.getTypeName()); } } } } //运行结果如下 method name = toString method para types method name = test parameter = arg0 [Ljava.lang.String; Modifiers name = parameter = arg1 java.util.List Modifiers name = parameter = arg2 java.util.HashMap Modifiers name = method para types type name = [Ljava.lang.String; type name = java.util.List type name = java.util.HashMap generic type name = java.lang.String[] generic type name = java.util.List<java.lang.String> generic type name = java.util.HashMap<java.lang.String, java.lang.Integer> method name = drive method para types
获取Method的返回值类型
-
与如下两种方法
// 获取返回值类型 public Class<?> getReturnType() {} // 获取返回值类型包括泛型 public Type getGenericReturnType() {}
-
代码实操
-
getReturnType()
public class MainClass { public static void main(String[] args) { Class<Car> carClass = Car.class; Method[] declaredMethods = carClass.getDeclaredMethods(); for (Method method : declaredMethods) { System.out.println("method name = " + method.getName()); Class<?> returnTypes = method.getReturnType(); String name = returnTypes.getName(); System.out.println("return type name = " + name); } } } //运行结果如下 method name = toString return type name = java.lang.String method name = test return type name = void method name = drive return type name = java.lang.String method name = getNames return type name = java.util.List
-
getGenericReturnType();
public class MainClass { public static void main(String[] args) { Class<Car> carClass = Car.class; Method[] declaredMethods = carClass.getDeclaredMethods(); for (Method method : declaredMethods) { System.out.println("method name = " + method.getName()); Type genericReturnType = method.getGenericReturnType(); System.out.println("genericReturnType name = " + genericReturnType.getTypeName()); } } } //运行结果如下 method name = toString genericReturnType name = java.lang.String method name = test genericReturnType name = void method name = drive genericReturnType name = java.lang.String method name = getNames genericReturnType name = java.util.List<java.lang.String>
-
Method获取修饰符
-
通过如下方法获取
public int getModifiers() {}
-
代码实操
public class MainClass { public static void main(String[] args) { Class<Car> carClass = Car.class; Method[] declaredMethods = carClass.getDeclaredMethods(); for (Method method : declaredMethods) { System.out.println("method name = " + method.getName()); int modifiers = method.getModifiers(); System.out.println("modifiers name = " + Modifier.toString(modifiers)); } } } //运行结果如下 method name = toString modifiers name = public method name = test modifiers name = public method name = drive modifiers name = public method name = getNames modifiers name = private
Method获取异常类型
-
通过如下方法获取
public Class<?>[] getExceptionTypes() {} public Type[] getGenericExceptionTypes() {}
Method方法的执行
这应该是整个反射机制的核心内容,我们很多时候运用反射目的就是为了以非常规手段执行Method
-
Method类型,是如下这个方法
public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {}
-
Method在调用invoke()方法的时候,存在许多细节
- invoke()方法中的第一个参数obj是Method依附的Class对应的类的实例,如果这个方法是一个静态方法,那么obj为null,第二个参数args对应的是方法的参数
- invoke()方法返回的是Object,所以实际上执行的时候要进行强制转换。
- 在对Method的invoke()方法调用的时候,如果方法本身会抛出异常,那么这个异常就会进过包装,由Method统一抛出InvocationTargetException。而通过InvocationTargetException.getCause()可以获取真正的异常。
-
代码实操
public class TestMethod { public static void testStatic() { System.out.println("test static"); } private int add(int a, int b) { return a + b; } public void testException() throws IllegalAccessException { throw new IllegalAccessException("You have some problem"); } } public class MainTest { public static void main(String[] args) { Class<TestMethod> testMethodClass = TestMethod.class; try { Method testStatic = testMethodClass.getMethod("testStatic", null); //测试静态方法 testStatic.invoke(null, null); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } TestMethod testMethod = new TestMethod(); try { Method add = testMethodClass.getDeclaredMethod("add", int.class, int.class); //通过这行代码才能访问private修饰的method add.setAccessible(true); int addValue = (int) add.invoke(testMethod, 3, 4); System.out.println("addValue = " + addValue); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } try { Method testException = testMethodClass.getMethod("testException", null); testException.invoke(testMethod, null); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); System.out.println("testException occur some error,Error type is :" + e.getCause().getClass().getName()); System.out.println("Error message is : " + e.getCause().getMessage()); } } } // 运行结果如下 test static addValue = 7 java.lang.reflect.InvocationTargetException at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:567) at method_invoke.MainTest.main(MainTest.java:40) Caused by: java.lang.IllegalAccessException: You have some problem at method_invoke.TestMethod.testException(TestMethod.java:14) ... 5 more
Constructor的操控
构造器也叫做构造方法,在Java的反射机制中把Constructor与Method分离开来,单独用Constructor这个类来表示,Constructor同Method差不多,但是它的特别的地方在于它能够创建一个对象。
Java的反射机制中有两种方法可以用来创建类的对象实例:Class.newInstance()和Constructor.newInstance() 官方文档建议开发者使用第二种方法 原因如下
-
Class.newInstance()只能调用无参的构造方法,而Constructor.newInstance()可以调用任意的构造方法。
-
Class.newInstance()通过构造方法直接抛出异常,而Constructor.newInstance()可以把抛出来的异常包装到InvocationtargetException里面去,这个是和Method一致的。
-
Class.newInstance()要求构造方法能够被访问,而Constructor.newInstance()却能够访问private修饰的构造器。
-
代码实操
public class TestClass { private String name; public TestClass(String name) { this.name = name; } public TestClass() { name = "zhangqi"; } @Override public String toString() { return "TestClass{" + "name='" + name + '\'' + '}'; } } public class TestMain { public static void main(String[] args) { Class<TestClass> testClassClass = TestClass.class; //Class.newInstance() 测试 try { //在Java 9 中已经标注为过时的API TestClass testClass = testClassClass.newInstance(); System.out.println(testClass.toString()); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } // Constructor.newInstance() 测试 try { Constructor<TestClass> constructor = testClassClass.getConstructor(String.class); TestClass dashingqi = constructor.newInstance("dashingqi"); System.out.println("name = "+dashingqi); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } //运行结果如下 TestClass{name='zhangqi'} name = TestClass{name='dashingqi'}
反射中的数组
数组本质上是一个Class,而在Class中存在一个方法用来识别它是否为一个数组
public native boolean isArray();
-
代码实操
public class Arrays { private int[] array; private Car[] cars; } //我们定义了一个类 Arrays 内部有两个数组 array和cars 。当然array和cars是类Arrays的Field,从Field角度来说,它们是数组类型。 //我们可以通过一些列的API来获取它们的具体信息 //获取数组的元素对应的编码 同Field中的 getName() //该方法是获取数组里面元素的类型 getComponentType() array 就是 int cars就是 class operate_array.Car
public class MainTest {
public static void main(String[] args) {
Class<Arrays> arraysClass = Arrays.class;
Field[] declaredFields = arraysClass.getDeclaredFields();
for (Field field : declaredFields) {
Class<?> type = field.getType();
if (type.isArray()) {
System.out.println("Type is " + type.getName());
System.out.println("ComponentType type is " + type.getComponentType());
}
}
}
}
//运行结果如下
Type is [I
ComponentType type is int
Type is [Loperate_array.Car;
ComponentType type is class operate_array.Car
反射中动态创建数组
-
是通过Array.newInstance()方法创建的。
// Array.java public static Object newInstance(Class<?> componentType, int... dimensions) throws IllegalArgumentException, NegativeArraySizeException { return multiNewArray(componentType, dimensions); }
-
第一个参数表示数组内元素类型,第二个参数表示相应维度数组中的长度限制
//比如我们呢要创建 int [][] ints = new int[2][3]; Array.newInstance(int.class,2,3);
-
Array的读取与赋值
对于Array整体的读取赋值,把它作为一个普通的Field,调用Field中对应的方法即可。
也就是 Field中提供的 set() 和get
ublic void set(Object obj,
Object value)
throws IllegalArgumentException,
IllegalAccessException;
public Object get(Object obj)
throws IllegalArgumentException,
IllegalAccessException;
当数组中指定位置的元素进行读取与赋值,这要涉及到Array提供的一些列 setXXX()和getXXX()方法
public static native boolean getBoolean(Object array, int index)
throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
public static native byte getByte(Object array, int index)
throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
-
代码操作
public class ArrayTest { public static void main(String[] args) { Class<Arrays> arraysClass = Arrays.class; try { //Constructor 能够创建一个对象 //类的实例对象的创建 Arrays arrays = arraysClass.newInstance(); Field fieldArray = arraysClass.getDeclaredField("array"); fieldArray.setAccessible(true); //创建一个数组 Object o = Array.newInstance(int.class, 3); Array.set(o, 0, 1); Array.set(o, 1, 2); Array.set(o, 2, 3); //操作Field中的set fieldArray.set(arrays, o); int[] array = arrays.getArray(); for (int i = 0; i < array.length; i++) { System.out.println("array index "+i+"value "+array[i]); } } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } } } //运行结果如下 array index 0value 1 array index 1value 2 array index 2value 3
反射中的枚举类Enum
同数组一样,枚举本质上也是一个Class而已,但是反射中还是把它单独提出来了。
-
定义一个枚举类
public enum EnumTestState { IDLE, DRIVING, STOPPING, test(); int test1() { return 1; } }
-
枚举跟类很相似,有自己的修饰符,方法,属性字段,还可以有构造方法。
-
在Java的反射机制中,提供了3个特别的API用于操控枚举。
// 用来判定 Class 对象是不是枚举类型 Class.isEnum() // 获取所有的枚举常量 Class.getEnumConstants() // 判断一个 Field 是不是枚举常量 java.lang.reflect.Field.isEnumConstant()
枚举的获取与设定
应为等同于Class,所以枚举的获取与设定都可以通过Field中的get()和set()方法。
如果枚举要获取里面的Field、Method、Constructor可以调用Class通用的API
-
代码实操
public enum EnumTestState { IDLE, DRIVING, STOPPING, test(); int test1() { return 1; } } public class EnumTestClass { private EnumTestState state = EnumTestState.DRIVING; public EnumTestState getState() { return state; } public void setState(EnumTestState state) { this.state = state; } } public class EnumTestMain { public static void main(String[] args) { //获取到枚举对应的Class对象 Class<EnumTestState> enumTestStateClass = EnumTestState.class; //Class中有判断当前获取的Class对象是否是枚举 if (enumTestStateClass.isEnum()) { System.out.println(Arrays.asList(enumTestStateClass.getEnumConstants())); //活到枚举中所有的Field Field[] declaredFields = enumTestStateClass.getDeclaredFields(); for (Field field : declaredFields) { //判断一个Field是不是一个枚举常量 if (field.isEnumConstant()) { System.out.println("is Enum"); } else { System.out.println("is not Enum"); } } Class<EnumTestClass> enumTestClassClass = EnumTestClass.class; EnumTestClass enumTestClass = new EnumTestClass(); try { //获取到名字叫做"state"的Field Field state = enumTestClassClass.getDeclaredField("state"); state.setAccessible(true); EnumTestState enumTestState = (EnumTestState) state.get(enumTestClass); System.out.println("enumTestState = " + enumTestState); state.set(enumTestClass, EnumTestState.STOPPING); System.out.println("State current is " + enumTestClass.getState()); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } //运行结果如下 [IDLE, DRIVING, STOPPING, test] is Enum is Enum is Enum is Enum is not Enum enumTestState = DRIVING State current is STOPPING
总结
- Java中的反射是非常规编码方式。
- Java中的反射入口是Class文件,可通过 getClass()、.class、Class.forName()三种方式获取它。
- 获取到Class对象之后,按照需要还能获取到Field、Constructor、Method。
- Field的操作主要涉及到类别的获取以及数值的读取与赋值。
- Constractor:可通过Class.newInstance()和Constructor.newInstance()创建类的对象实例,推荐后者。
- Method算是反射的最核心的,发射都是为了调用某个Method的invoke方法。
- 数组和枚举可以看成Class对待。
反射是非常规手段,它会抛弃Java虚拟机的很多优化,所以同样功能代码,反射要比正常方式要慢,所以考虑到采用反射时,要考虑它的时间成本。
参考文章:细说反射,Java 和 Android 开发者必须跨越的坎
网友评论