什么是反射
反射是一個很牛的功能纫骑,能夠在程序運行時修改程序的行為蝎亚。但是反射是非常規(guī)手段,反射有風(fēng)險先馆,應(yīng)用需謹(jǐn)慎发框。
把程序代碼比作一輛車,因為Java是面向?qū)ο蟮恼Z言,車子有自己的型號梅惯、車牌號宪拥、發(fā)動機,有倒車铣减、泊車等功能她君。
正常情況下,我們需要為車子配備一個司機葫哗,然后按照行為準(zhǔn)則規(guī)范駕駛缔刹。
反射是非常規(guī)手段,正常行駛的時候劣针,車子需要司機的駕駛校镐。但是反射不需要,因為它就是車子的自動駕駛捺典。(非常規(guī)嘛)
- 反射技術(shù)通常被用來檢測和改變應(yīng)用程序在 Java 虛擬機中的行為表現(xiàn)鸟廓。它是一個相對而言比較高級的技術(shù),通常它應(yīng)用的前提是開發(fā)者本身對于 Java 語言特性有很強的理解的基礎(chǔ)上襟己。值得說明的是引谜,反射是一種強有力的技術(shù)特性,因此可以使得應(yīng)用程序突破一些藩籬擎浴,執(zhí)行一些常規(guī)手段無法企及的目的员咽。
Class
Java是面向?qū)ο蟮恼Z言,基本上是以類為基礎(chǔ)構(gòu)造了整個程序退客。反射中要求提供的規(guī)格說明書就是類的規(guī)格說明書骏融。
其中小寫的class是定義類的關(guān)鍵字。
-
Class本質(zhì)是一個類萌狂。
public final class Class<T> implements java.io.Serializable, GenericDeclaration, Type, AnnotatedElement, TypeDescriptor.OfField<Class<?>>, Constable {}
Class就是一個對象档玻,運行在Java虛擬機中的類和接口。
Class的獲取
反射的入口是Class茫藏,但是反射中Class是沒有公開的構(gòu)造函數(shù)误趴,不能通過關(guān)鍵字new來獲取Class的對象,所以就有如下3種獲取Class的方式
-
Object.getClass()
- 對于一個對象而言务傲,如果能訪問凉当,那么調(diào)用getClass()方法就可以獲取到它對應(yīng)的Class對象。
- 如下這種方法不適合基本類型 :int float
public class Car { } public class CarTest { public static void main(String[] args) { Car car = new Car(); Class aClass = car.getClass(); } }
-
通過 .class標(biāo)識
- 對于上述方法售葡,Car是一個類看杭,car是一個對象,通過car.getClass()就獲取到Car這個類的Class對象挟伙。
- 如果不想通過類的對象的getClass()方式去獲取楼雹,就可以通過 ‘.class的標(biāo)識’來獲取。
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注解修飾的類就沒有出現(xiàn)在SDK中榨咐。
- 我們要獲取到這個在當(dāng)前開發(fā)環(huán)境中不存在的類的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的內(nèi)容清單
Class的名字獲取
Class對象也有名字,涉及到API如下
- Class.getName()
- Class.getSimpleName()
- Class.getCanonicalName()
因為Class是一個入口,它代表引用孕暇、基本數(shù)據(jù)類型以及數(shù)組對象购撼,所以獲取它們的方式也是不同的。
getName
當(dāng)Class代表一個引用時
- getName放回的是一個二進(jìn)制形式的字符串 “com.dashingqi.reflect.Car”
當(dāng)Class代表是一個基本數(shù)據(jù)類型的時候
- getName返回的就是它們的關(guān)鍵字 比如 int.class的名字就是int
當(dāng)Class代表是基本數(shù)據(jù)類型的數(shù)組時(int [] [] [] 這樣的3維數(shù)組)
getName返回的是[[[I 這樣的字符串
Java在這個地方有相應(yīng)的規(guī)則亭姥,在元素類型的前面添加相應(yīng)數(shù)量的[符號,用[符號的數(shù)量來表示數(shù)組的維度,對于基本數(shù)據(jù)類型都有相應(yīng)的編碼污呼,都是大寫的字母;規(guī)則如下
元素類型 | 編碼 |
---|---|
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()); //基本數(shù)據(jù)類型 System.out.println("integerClass = " + integerClass.getName()); System.out.println("floatClass=" + floatClass.getName()); //基本數(shù)據(jù)類型的數(shù)組 System.out.println("aClass1=" + aClass1.getName()); //類或者接的數(shù)組 System.out.println("aClass2=" + aClass2.getName()); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } //運行結(jié)果如下 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); } } //運行結(jié)果 name = getSimpleName.ClassA$ClassB simpleName = ClassB
- 因為是內(nèi)部類包竹,通過getName得到是的二進(jìn)制形式的全限定類名燕酷,并且類名前面還有一個$符號。
- getSimpleName()則返回一個ClassB,去掉了包名限定周瞎。
-
當(dāng)數(shù)組的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); } } //運行結(jié)果如下 name1 = [LgetSimpleName.ClassA; simpleName1=ClassA[]
-
對于匿名內(nèi)部類來說声诸,通過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()); } } // 運行結(jié)果如下 name = getSimpleName.ClassTest$1 simple name =
getCanonicalName
Canonial 是官方酱讶、標(biāo)準(zhǔn)的意思,那么該方法就是獲取Class對象的官方名字彼乌。
這個CanonicalName是官方規(guī)定的泻肯,如果Class對象沒有canonicalName的話就返回一個null
getCanonicalName()是getName()與getSimpleName()的結(jié)合
getCanonicalName()返回的也是全限定類名,但是對于內(nèi)部類來說慰照,不用$開頭灶挟,而是用.。
getCanonicalName()對于數(shù)組的Class毒租,同getSimpleName()也是在后面加[]稚铣。
getCanonicalName()不同于getSimpleName()的是,如果不存在canonicalName就返回null而不是空字符串。
局部類和匿名內(nèi)部類不存在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); } } //運行結(jié)果 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開發(fā)中定義一個類蝌蹂,往往要通過很多修飾符來配合使用的。大致分為如下四類
- 用來限制作用域:publish曹锨、protected孤个、private
- 用來標(biāo)記為靜態(tài):static
- 注解
- 用來提示子類來復(fù)寫:abstract
Java反射提供了API去獲取這些修飾符
-
定義一個被 abstract 和 publish 修飾的類
public abstract class DemoA { }
-
我們現(xiàn)在要提取這些修飾符,只需要調(diào)用 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)); } }
-
運行結(jié)果如下
modifiers = 1025 modifiers_str = public abstract
-
Java工程師考慮到了位運算沛简,用一個int數(shù)值來記錄所有的修飾符齐鲤,然后不同的位對應(yīng)不同的修飾符,這些修飾符都定義在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):當(dāng)前 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()); } } } //運行結(jié)果如下 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
類獲取接口中的方法對應(yīng)到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()); } } } //運行結(jié)果如下 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把構(gòu)造器從Method中拎出來,用Constructor來表示
Constructor不能從父類中繼承银锻,所以就沒有辦法通過getConstructor()來獲取到父類的Constructor
-
getDeclaredConstructor():獲取任何修飾符修飾的構(gòu)造方法名字
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException }
-
getConstructor():獲取當(dāng)前Class對象中 publish修飾的構(gòu)造方法的名字
public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException{ }
-
getDeclaredConstructors():獲取所有構(gòu)造方法名字
public Constructor<?>[] getDeclaredConstructors() throws SecurityException { }
-
getConstructors():獲取所有publish修飾的構(gòu)造方法名字
public Constructor<?>[] getConstructors() throws SecurityException { }
Field的操作
我們在類中定義字段時永品,通常是這樣的
其中 c、d击纬、f腐碱、g、demo這些變量都是屬性掉弛,在反射機制中映射到Class對象中都是Field
它們要么是8中基本數(shù)據(jù)類型症见,或者是引用,所有的引用都是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() { } //兩種方法返回參數(shù)不一樣,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("======================"); } } //運行結(jié)果如下 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 更詳細(xì)
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("======================"); } //運行結(jié)果如下 getModifiers = public ====================== getModifiers = public ====================== getModifiers = public ====================== getModifiers = public ======================
Field內(nèi)容的讀取與賦值
這也是反射機制中對Filed主要的目的
Filed定義了一些列set和get方法來設(shè)置和獲取不同類型的值
-
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本身不對成員進(jìn)行存儲帖池,它只提供檢索,所以需要Field吭净、Method睡汹、Constructor對象來承載這些成員,所以針對成員的操作時寂殉,一般需要為成員指定類的實例引用囚巴,表示這個成員屬于哪個Object的,是為了精準(zhǔn)定位友扰。
-
代碼實操
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(); } } //運行結(jié)果如下 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(); } //運行結(jié)果如下 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)
-
運行結(jié)果如下
son.g = 10 reflection son.g = 10 son.g = 16
-
Method操控
Method對應(yīng)普通類的方法村怪;
一般普通類的構(gòu)成
public int add(int a, int b){
? return a+b;
}
主要包括如下幾個方面
- 方法名
- 方法的返回值類型
- 方法的參數(shù)
- 方法的修飾符
- 方法拋出的異常
Java的反射中針對Method 有相關(guān)操作API的來提取相關(guān)元素
獲取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()); } } } //運行結(jié)果如下 method name = getCarName method name = setCarName method name = getCarId method name = setCarId
Method獲取方法參數(shù)
-
通過 getParameters()來獲取
public Parameter[] getParameters() {}
-
返回是一個Parameter的數(shù)組秽浇,在反射中Parameter對象就是用來映射方法中的參數(shù),經(jīng)常使用的方法有如下
// 獲取參數(shù)名字 public String getName() {} // 獲取參數(shù)類型 public Class<?> getType() {} // 獲取參數(shù)的修飾符 public int getModifiers() {}
-
當(dāng)然有時我們不需要參數(shù)的名字甚负,只需要參數(shù)的類型就可以柬焕,通過Method下面的方法獲取
//獲取所有參數(shù)的類型 public Class<?>[] getParameterTypes() { } //獲取所有參數(shù)的類型,包括泛型 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()); //獲取到方法中的參數(shù) Parameter[] parameters = method.getParameters(); for (Parameter parameter : parameters) { //獲取到參數(shù)的名字和參數(shù)和參數(shù)的修飾符 System.out.println("parameter = " + parameter.getName() + " " + parameter.getType().getName() + " Modifiers name = " + Modifier.toString(parameter.getModifiers())); } //獲取到所有參數(shù)的類型 Class<?>[] pTypes = method.getParameterTypes(); System.out.println("method para types"); for (Class type : pTypes) { System.out.println(" type name = " + type.getName()); } //獲取到所有參數(shù)的類型梭域,包括泛型 Type[] genericParameterTypes = method.getGenericParameterTypes(); for (Type type : genericParameterTypes) { System.out.println(" generic type name = " + type.getTypeName()); } } } } //運行結(jié)果如下 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); } } } //運行結(jié)果如下 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()); } } } //運行結(jié)果如下 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)); } } } //運行結(jié)果如下 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方法的執(zhí)行
這應(yīng)該是整個反射機制的核心內(nèi)容击喂,我們很多時候運用反射目的就是為了以非常規(guī)手段執(zhí)行Method
-
Method類型,是如下這個方法
public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {}
-
Method在調(diào)用invoke()方法的時候碰辅,存在許多細(xì)節(jié)
- invoke()方法中的第一個參數(shù)obj是Method依附的Class對應(yīng)的類的實例,如果這個方法是一個靜態(tài)方法介时,那么obj為null没宾,第二個參數(shù)args對應(yīng)的是方法的參數(shù)
- invoke()方法返回的是Object,所以實際上執(zhí)行的時候要進(jìn)行強制轉(zhuǎn)換沸柔。
- 在對Method的invoke()方法調(diào)用的時候循衰,如果方法本身會拋出異常,那么這個異常就會進(jìn)過包裝褐澎,由Method統(tǒng)一拋出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); //測試靜態(tài)方法 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()); } } } // 運行結(jié)果如下 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的操控
構(gòu)造器也叫做構(gòu)造方法工三,在Java的反射機制中把Constructor與Method分離開來迁酸,單獨用Constructor這個類來表示,Constructor同Method差不多俭正,但是它的特別的地方在于它能夠創(chuàng)建一個對象奸鬓。
Java的反射機制中有兩種方法可以用來創(chuàng)建類的對象實例:Class.newInstance()和Constructor.newInstance() 官方文檔建議開發(fā)者使用第二種方法 原因如下
Class.newInstance()只能調(diào)用無參的構(gòu)造方法,而Constructor.newInstance()可以調(diào)用任意的構(gòu)造方法掸读。
Class.newInstance()通過構(gòu)造方法直接拋出異常串远,而Constructor.newInstance()可以把拋出來的異常包裝到InvocationtargetException里面去宏多,這個是和Method一致的。
Class.newInstance()要求構(gòu)造方法能夠被訪問澡罚,而Constructor.newInstance()卻能夠訪問private修飾的構(gòu)造器伸但。
-
代碼實操
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 中已經(jīng)標(biāo)注為過時的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(); } } } //運行結(jié)果如下 TestClass{name='zhangqi'} name = TestClass{name='dashingqi'}
反射中的數(shù)組
數(shù)組本質(zhì)上是一個Class,而在Class中存在一個方法用來識別它是否為一個數(shù)組
public native boolean isArray();
-
代碼實操
public class Arrays { private int[] array; private Car[] cars; } //我們定義了一個類 Arrays 內(nèi)部有兩個數(shù)組 array和cars 留搔。當(dāng)然array和cars是類Arrays的Field更胖,從Field角度來說,它們是數(shù)組類型催式。 //我們可以通過一些列的API來獲取它們的具體信息 //獲取數(shù)組的元素對應(yīng)的編碼 同F(xiàn)ield中的 getName() //該方法是獲取數(shù)組里面元素的類型 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());
}
}
}
}
//運行結(jié)果如下
Type is [I
ComponentType type is int
Type is [Loperate_array.Car;
ComponentType type is class operate_array.Car
反射中動態(tài)創(chuàng)建數(shù)組
-
是通過Array.newInstance()方法創(chuàng)建的函喉。
// Array.java public static Object newInstance(Class<?> componentType, int... dimensions) throws IllegalArgumentException, NegativeArraySizeException { return multiNewArray(componentType, dimensions); }
-
第一個參數(shù)表示數(shù)組內(nèi)元素類型,第二個參數(shù)表示相應(yīng)維度數(shù)組中的長度限制
//比如我們呢要創(chuàng)建 int [][] ints = new int[2][3]; Array.newInstance(int.class,2,3);
-
Array的讀取與賦值
對于Array整體的讀取賦值荣月,把它作為一個普通的Field管呵,調(diào)用Field中對應(yīng)的方法即可。
也就是 Field中提供的 set() 和get
ublic void set(Object obj,
Object value)
throws IllegalArgumentException,
IllegalAccessException;
public Object get(Object obj)
throws IllegalArgumentException,
IllegalAccessException;
當(dāng)數(shù)組中指定位置的元素進(jìn)行讀取與賦值哺窄,這要涉及到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 能夠創(chuàng)建一個對象 //類的實例對象的創(chuàng)建 Arrays arrays = arraysClass.newInstance(); Field fieldArray = arraysClass.getDeclaredField("array"); fieldArray.setAccessible(true); //創(chuàng)建一個數(shù)組 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(); } } } //運行結(jié)果如下 array index 0value 1 array index 1value 2 array index 2value 3
反射中的枚舉類Enum
同數(shù)組一樣捐下,枚舉本質(zhì)上也是一個Class而已,但是反射中還是把它單獨提出來了萌业。
-
定義一個枚舉類
public enum EnumTestState { IDLE, DRIVING, STOPPING, test(); int test1() { return 1; } }
枚舉跟類很相似坷襟,有自己的修飾符,方法生年,屬性字段婴程,還可以有構(gòu)造方法。
-
在Java的反射機制中抱婉,提供了3個特別的API用于操控枚舉档叔。
// 用來判定 Class 對象是不是枚舉類型 Class.isEnum() // 獲取所有的枚舉常量 Class.getEnumConstants() // 判斷一個 Field 是不是枚舉常量 java.lang.reflect.Field.isEnumConstant()
枚舉的獲取與設(shè)定
應(yīng)為等同于Class,所以枚舉的獲取與設(shè)定都可以通過Field中的get()和set()方法蒸绩。
如果枚舉要獲取里面的Field衙四、Method、Constructor可以調(diào)用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) { //獲取到枚舉對應(yīng)的Class對象 Class<EnumTestState> enumTestStateClass = EnumTestState.class; //Class中有判斷當(dāng)前獲取的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(); } } } } //運行結(jié)果如下 [IDLE, DRIVING, STOPPING, test] is Enum is Enum is Enum is Enum is not Enum enumTestState = DRIVING State current is STOPPING
總結(jié)
- Java中的反射是非常規(guī)編碼方式患亿。
- Java中的反射入口是Class文件传蹈,可通過 getClass()、.class步藕、Class.forName()三種方式獲取它惦界。
- 獲取到Class對象之后,按照需要還能獲取到Field咙冗、Constructor表锻、Method。
- Field的操作主要涉及到類別的獲取以及數(shù)值的讀取與賦值乞娄。
- Constractor:可通過Class.newInstance()和Constructor.newInstance()創(chuàng)建類的對象實例瞬逊,推薦后者显歧。
- Method算是反射的最核心的,發(fā)射都是為了調(diào)用某個Method的invoke方法确镊。
- 數(shù)組和枚舉可以看成Class對待士骤。
反射是非常規(guī)手段,它會拋棄Java虛擬機的很多優(yōu)化蕾域,所以同樣功能代碼拷肌,反射要比正常方式要慢,所以考慮到采用反射時旨巷,要考慮它的時間成本巨缘。
參考文章:細(xì)說反射,Java 和 Android 開發(fā)者必須跨越的坎