一.反射與操做類
在反射機制的處理過程之中不只僅只是一個實例化對象的處理操做,更多的狀況下還有類的組成的操做,任何一個類的基本組成結構:父類(父接口),包,屬性,方法(構造方法,普通方法)
--獲取類的基本信息
一個類的基本信息主要包括的是所在的包名稱,父類的定義,父接口的定義.
--範例:定義一個程序類java
1 package 反射.反射與操做類; 2 3 /** 4 * @author : S K Y 5 * @version :0.0.1 6 */ 7 public interface IMessageStrvice { //消息服務 8 void send(); 9 }
1 package 反射.反射與操做類; 2 3 /** 4 * @author : S K Y 5 * @version :0.0.1 6 */ 7 public interface IChannelService { 8 boolean connect(); 9 }
1 package 反射.反射與操做類; 2 3 /** 4 * @author : S K Y 5 * @version :0.0.1 6 */ 7 public class AbstractBase { 8 }
1 package 反射.反射與操做類; 2 3 /** 4 * @author : S K Y 5 * @version :0.0.1 6 */ 7 public class Person extends AbstractBase implements IMessageStrvice, IChannelService { 8 @Override 9 public boolean connect() { 10 return true; 11 } 12 13 @Override 14 public void send() { 15 if (this.connect()) { 16 System.out.println("消息發送"); 17 } 18 } 19 }
--若是此時要想獲取一些類的基礎信息則能夠經過class類中的以下方法:
獲取包名稱: public Package getPackage()
獲取繼承父類: public Class<? super T> getClass();
獲取實現父接口: public Class<?>[] getInterfaces() c++
1 public class PersonDemo { 2 public static void main(String[] args) { 3 Class<Person> personClass = Person.class; //獲取指定類的class對象 4 Package aPackage = personClass.getPackage(); //獲取指定類的包定義 5 System.out.println("獲取包名稱: " + aPackage.getName()); 6 Class<? super Person> superclass = personClass.getSuperclass(); //獲取父類 7 System.out.println("Person獲取父類: " + superclass.getName()) ; 8 System.out.println("superclass獲取父類: " + superclass.getSuperclass().getName()); 9 Class<?>[] interfaces = personClass.getInterfaces(); //獲取父接口 10 for (int i = 0; i < interfaces.length; i++) { 11 System.out.println("獲取父接口" + (i + 1) + ": " + interfaces[i].getName()); 12 } 13 } 14 }
--運行結果設計模式
獲取包名稱: 反射.反射與操做類 Person獲取父類: 反射.反射與操做類.AbstractBase superclass獲取父類: java.lang.Object 獲取父接口1: 反射.反射與操做類.IMessageStrvice 獲取父接口2: 反射.反射與操做類.IChannelService Process finished with exit code 0
-獲取一個類的Class對象以後就覺得這這個對象能夠獲取類之中的一切繼承結構信息,所以咱們能夠知道Class能夠描述普通類,抽象類,接口安全
二.反射調用構造方法app
在一個類之中除了有繼承的關係以外,最爲重要的操做就是類中的結構處理了,類中的中首先須要關注的就是構造方法的使用問題,實際上在以前經過反射實例化對象的使用就已經接觸到了構造方法的問題了,及newInstance()方法,全部類的構造方法的獲取均可以直接經過Class類來完成,該類中定義有以下的幾個方法:
獲取指定構造方法(根據參數類型): Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
獲取全部構造方法: Constructor<?>[] getDeclaredConstructors()
獲取全部構造方法: Constructor<?>[] getConstructors()
獲取指定構造方法: Constructor<T> getConstructor(Class<?>... parameterTypes) ide
1 package 反射.反射與操做類; 2 3 /** 4 * @author : S K Y 5 * @version :0.0.1 6 */ 7 public class AbstractBase { 8 public AbstractBase(){} 9 public AbstractBase(String name){} 10 }
--修改Person類的定義工具
1 package 反射.反射與操做類; 2 3 /** 4 * @author : S K Y 5 * @version :0.0.1 6 */ 7 public class Person extends AbstractBase implements IMessageStrvice, IChannelService { 8 public Person() { 9 } 10 11 public Person(String name, int age) { 12 } 13 14 @Override 15 public boolean connect() { 16 return true; 17 } 18 19 @Override 20 public void send() { 21 if (this.connect()) { 22 System.out.println("消息發送"); 23 } 24 } 25 }
--範例:獲取所有構造方法this
1 class ConstructorDemo{ 2 public static void main(String[] args) { 3 Class<Person> personClass = Person.class; //獲取指定類的class對象 4 Constructor<?>[] constructors = personClass.getDeclaredConstructors(); //獲取所有構造 5 for (Constructor<?> constructor : constructors) { 6 System.out.println(constructor); 7 } 8 System.out.println("========================"); 9 constructors = personClass.getConstructors(); 10 for (Constructor<?> constructor : constructors) { 11 System.out.println(constructor); 12 } 13 } 14 }
--運行結果spa
public 反射.反射與操做類.Person() public 反射.反射與操做類.Person(java.lang.String,int) ======================== public 反射.反射與操做類.Person() public 反射.反射與操做類.Person(java.lang.String,int) Process finished with exit code 0
--此時獲取的是類之中的所有構造方法,可是也能夠獲取一個指定參數的構造設計
1 package 反射.反射與操做類; 2 3 /** 4 * @author : S K Y 5 * @version :0.0.1 6 */ 7 public class Person extends AbstractBase implements IMessageStrvice, IChannelService { 8 private String name; 9 private int age; 10 public Person() { 11 } 12 13 public Person(String name, int age) { 14 this.name = name; 15 this.age = age; 16 } 17 18 @Override 19 public boolean connect() { 20 return true; 21 } 22 23 @Override 24 public void send() { 25 if (this.connect()) { 26 System.out.println("消息發送"); 27 } 28 } 29 30 @Override 31 public String toString() { 32 return "Person{" + 33 "name='" + name + '\'' + 34 ", age=" + age + 35 '}'; 36 } 37 }
--須要調用類的有參構造方法,那麼咱們應該傳入參數,觀察java.lang.reflect.Constructor類的方法
實例化方法: public T newInstance(Object... initargs)throws InstantiationException,IllegalAccessException,IllegalArgumentException,InvocationTargetException
--範例:調用指定構造實例化對象
1 class OneConstructorDemo { 2 public static void main(String[] args) throws Exception { 3 Class<Person> personClass = Person.class; //獲取指定類的class對象 4 Constructor<Person> constructor = personClass.getConstructor(String.class, int.class); 5 Person person = constructor.newInstance("張三", 56); 6 System.out.println(person); 7 } 8 }
--運行結果
Person{name='張三', age=56} Process finished with exit code 0
--雖然程序代碼自己容許開發者調用有參構造處理,可是若是從實際開發來說,全部的類中最好提供有無參構造(特別是使用反射的類中),這樣的實例化可以達到很高的統一性
--咱們觀察Constructor類的結構發如今JDK1.8以後提供了一個Executable類做爲Constructor類的父類對象
三.反射調用普通方法
在進行反射處理的時候,也能夠經過反射獲取類中的所有方法,可是須要提醒的是,若是要想經過反射調用這些方法,必須有一個前提條件,類之中要提供有實例化對象
--在Class類中提供有以下的操做能夠獲取方法對象:
獲取所有方法: public Method[] getMethods()throws SecurityException
獲取指定方法: Method getMethod(String name, Class<?>... parameterTypes)
獲取本類所有方法:Method[] getDeclaredMethods()
獲取本類指定方法: Method getDeclaredMethod(String name, Class<?>... parameterTypes)
--範例:獲取所有方法
1 class MethodDemo{ 2 public static void main(String[] args) { 3 Class<Person> personClass = Person.class; //獲取指定類的class對象 4 { //獲取所有方法 5 Method[] methods = personClass.getMethods(); 6 for (Method method : methods) { 7 System.out.println(method); 8 } 9 } 10 } 11 }
--運行結果
public java.lang.String 反射.反射與操做類.Person.toString() public boolean 反射.反射與操做類.Person.connect() public void 反射.反射與操做類.Person.send() public final void java.lang.Object.wait() throws java.lang.InterruptedException public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public boolean java.lang.Object.equals(java.lang.Object) public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll() Process finished with exit code 0
--能夠發現將父類Object中的方法也獲取到了,若是隻是想獲取本地的方法
1 class MethodDemo{ 2 public static void main(String[] args) { 3 Class<Person> personClass = Person.class; //獲取指定類的class對象 4 { //獲取所有方法 5 Method[] methods = personClass.getMethods(); 6 for (Method method : methods) { 7 System.out.println(method); 8 } 9 } 10 System.out.println("========================="); 11 { //獲取所有方法 12 Method[] methods = personClass.getDeclaredMethods(); 13 for (Method method : methods) { 14 System.out.println(method); 15 } 16 } 17 } 18 }
--運行結果
public java.lang.String 反射.反射與操做類.Person.toString() public boolean 反射.反射與操做類.Person.connect() public void 反射.反射與操做類.Person.send() public final void java.lang.Object.wait() throws java.lang.InterruptedException public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public boolean java.lang.Object.equals(java.lang.Object) public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll() ========================= public java.lang.String 反射.反射與操做類.Person.toString() public boolean 反射.反射與操做類.Person.connect() public void 反射.反射與操做類.Person.send() Process finished with exit code 0
--可是須要注意的是這個時候的方法信息的獲取時依靠Method類提供的toString()方法來完成的,不少時候也能夠由用戶本身拼湊方法的輸出
1 class MyMethodDemo { 2 public static void main(String[] args) { 3 Class<Person> personClass = Person.class; //獲取指定類的class對象 4 Method[] methods = personClass.getMethods(); 5 for (Method method : methods) { 6 String modifier = Modifier.toString(method.getModifiers());//獲取訪問修飾符 7 String returnType = method.getReturnType().getName(); //獲取方法的返回類型 8 Class<?>[] parameterTypes = method.getParameterTypes(); //獲取參數類型 9 StringBuffer buffer = new StringBuffer(); 10 buffer.append("("); 11 for (int i = 0; i < parameterTypes.length; i++) { 12 buffer.append(parameterTypes[i].getName()); 13 buffer.append(" arg").append(i); 14 if (i < parameterTypes.length - 1) { 15 buffer.append(","); 16 } 17 } 18 buffer.append(")"); 19 String parameterType = buffer.toString(); 20 Class<?>[] exceptionTypes = method.getExceptionTypes(); //獲取異常類型 21 buffer = new StringBuffer(); 22 if (exceptionTypes.length > 0) { 23 buffer.append(" throws "); 24 for (int i = 0; i < exceptionTypes.length; i++) { 25 buffer.append(exceptionTypes[i].getName()); 26 buffer.append(" e").append(i); 27 if (i < exceptionTypes.length - 1) { 28 buffer.append(","); 29 } 30 } 31 } 32 String exceptionType = buffer.toString(); 33 System.out.println(modifier + " " + returnType + " " + method.getName() + parameterType + "" + exceptionType); 34 } 35 } 36 }
--運行結果
public java.lang.String toString() public boolean connect() public void send() public final void wait() throws java.lang.InterruptedException e0 public final void wait(long arg0,int arg1) throws java.lang.InterruptedException e0 public final native void wait(long arg0) throws java.lang.InterruptedException e0 public boolean equals(java.lang.Object arg0) public native int hashCode() public final native java.lang.Class getClass() public final native void notify() public final native void notifyAll() Process finished with exit code 0
--在Method類中有一個重要的方法: public Object invoke(Object obj,Object... args)throws IllegalAccessException,IllegalArgumentException,InvocationTargetException,用於反射調用方法,在Person類中有name屬性追加有setter與getter方法:
1 package 反射.反射與操做類; 2 3 /** 4 * @author : S K Y 5 * @version :0.0.1 6 */ 7 public class Person extends AbstractBase implements IMessageStrvice, IChannelService { 8 private String name; 9 private int age; 10 public Person() { 11 } 12 13 public Person(String name, int age) { 14 this.name = name; 15 this.age = age; 16 } 17 18 @Override 19 public boolean connect() { 20 return true; 21 } 22 23 @Override 24 public void send() { 25 if (this.connect()) { 26 System.out.println("消息發送"); 27 } 28 } 29 30 public String getName() { 31 return name; 32 } 33 34 public void setName(String name) { 35 this.name = name; 36 } 37 38 @Override 39 public String toString() { 40 return "Person{" + 41 "name='" + name + '\'' + 42 ", age=" + age + 43 '}'; 44 } 45 }
--經過反射機制來實現person類之中的setter與getter方法的調用處理,而且不進行person類的包導入操做
1 class GetterAndSetterDemo { 2 public static void main(String[] args) throws Exception { 3 //在不導入開發包的狀況下實現屬性的配置 4 Class<?> aClass = Class.forName("反射.反射與操做類.Person");//獲取指定類的class對象 5 String attribute = "name"; //要操做的類屬性 6 String value = "張三"; //要設置的屬性內容 7 //1.任何狀況寫想要調用類中的屬性或者調用類中的方法都必須保證存在有實例化對象 8 Object obj = aClass.newInstance(); //調用無參構造實例化 9 //2.若是想要進行方法的調用,必定要獲取方法的名稱 10 String setMethodName = "setName"; 11 Method method = aClass.getDeclaredMethod(setMethodName, String.class); 12 method.invoke(obj, value); //等價於 person.setName("小強"); 13 System.out.println("toString: " + obj); 14 String getMethodName = "getName"; 15 Method getName = aClass.getDeclaredMethod(getMethodName); 16 System.out.println("getName: " + getName.invoke(obj)); //等價於 person.getName(); 17 } 18 }
--運行結果
toString: Person{name='張三', age=0} getName: 張三 Process finished with exit code 0
--利用此類操做總體的形式不會有任何的明確的類對象產生,這樣的處理避免了某一個類的耦合問題.
四.反射調用成員
類結構之中的最後一個核心的組成就是成員(Field),大部分狀況下都會將其稱爲成員屬性,對成員屬性的獲取也是經過Class類來完成的,在這個類中提供有以下兩組操做方法:
獲取本類所有成員: Field[] getDeclaredFields()
獲取本類指定成員: Field getDeclaredField(String name)
獲取父類所有成員: Field[] getFields()
獲取父類指定成員: Field getField(String name)
--修改AbstractBase類
1 package 反射.反射與操做類; 2 3 /** 4 * @author : S K Y 5 * @version :0.0.1 6 */ 7 public class AbstractBase { 8 public static final String BASE = "123456789"; 9 private String info = "hello world"; 10 protected static final String ABC = "888"; 11 public AbstractBase() { 12 } 13 14 public AbstractBase(String name) { 15 } 16 }
1 package 反射.反射與操做類; 2 3 /** 4 * @author : S K Y 5 * @version :0.0.1 6 */ 7 public class Person extends AbstractBase implements IMessageStrvice, IChannelService { 8 public static final String NAME = "小張三"; 9 private String name; 10 private int age; 11 public Person() { 12 } 13 14 public Person(String name, int age) { 15 this.name = name; 16 this.age = age; 17 } 18 19 @Override 20 public boolean connect() { 21 return true; 22 } 23 24 @Override 25 public void send() { 26 if (this.connect()) { 27 System.out.println("消息發送"); 28 } 29 } 30 31 public String getName() { 32 return name; 33 } 34 35 public void setName(String name) { 36 this.name = name; 37 } 38 39 @Override 40 public String toString() { 41 return "Person{" + 42 "name='" + name + '\'' + 43 ", age=" + age + 44 '}'; 45 } 46 }
--範例:獲取類中的成員
1 class GetFieldDemo{ 2 public static void main(String[] args) throws Exception { 3 Class<?> aClass = Class.forName("反射.反射與操做類.Person");//獲取指定類的class對象 4 Field[] fields = aClass.getFields(); 5 for (Field field : fields) { 6 System.out.println(field); 7 } 8 System.out.println("======================"); 9 fields = aClass.getDeclaredFields(); 10 for (Field field : fields) { 11 System.out.println(field); 12 } 13 } 14 }
--運行結果
public static final java.lang.String 反射.反射與操做類.Person.NAME public static final java.lang.String 反射.反射與操做類.AbstractBase.BASE ====================== public static final java.lang.String 反射.反射與操做類.Person.NAME private java.lang.String 反射.反射與操做類.Person.name private int 反射.反射與操做類.Person.age Process finished with exit code 0
--從上述結果咱們能夠得知getFields()能夠得到父類及子類的公有成員,而getDeclaredFields()能夠得到子類的全部成員,可是在Field類中最爲重要的操做形式並非獲取所有的成員,而是以下的三個方法:
設置屬性內容: public void set(Object obj,Object value)throws IllegalArgumentException,IllegalAccessException
獲取屬性內容: public Object get(Object obj)throws IllegalArgumentException,IllegalAccessException
解除封裝: public static void setAccessible(AccessibleObject[] array,boolean flag)throws SecurityException
全部的成員都是在對象實例化以後進行空間分配的,因此此時必定要先有實例化對象以後,才能夠進行成員的操做.
--範例:直接調用person類中的name私有成員:
1 class UseNameDemo{ 2 public static void main(String[] args) throws Exception { 3 Class<?> aClass = Class.forName("反射.反射與操做類.Person");//獲取指定類的class對象 4 Object obj = aClass.getConstructor().newInstance(); 5 Field name = aClass.getDeclaredField("name"); 6 name.setAccessible(true); //解除封裝 7 name.set(obj,"張三"); 8 System.out.println(name.get(obj)); 9 } 10 }
--運行結果
張三 Process finished with exit code 0
--經過一系列的分析能夠發現,類之中的構造方法,成員屬性均可以經過反射實現調用,可是對於成員的反射調用不多這樣直接處理,大部分操做都應該經過getter和setter處理,因此對於以上的代碼只是可以說明反射具備這樣的能力,而對於Field類在實際的開發之中只有一個方法最爲經常使用:
獲取成員類型: public Class<?> getType()
--範例:獲取Person類中的name成員的類型
1 class UseNameDemo{ 2 public static void main(String[] args) throws Exception { 3 Class<?> aClass = Class.forName("反射.反射與操做類.Person");//獲取指定類的class對象 4 Object obj = aClass.getConstructor().newInstance(); 5 Field name = aClass.getDeclaredField("name"); 6 System.out.println(name.getType().getName()); //獲取完整類名稱 7 System.out.println(name.getType().getSimpleName()); 8 } 9 }
--運行結果
java.lang.String String Process finished with exit code 0
--在開發中進行反射處理的時候,每每會利用Field與Method類實現類中的setter方法的調用.
五.Unsafe工具類
反射是java的第一大特色,一旦打開了反射的大門,就能夠有更加豐富的類的設計形式,除了JVM自己支持的反射處理以外,在java裏面也提供有一個Unsafe(不安全操做),這個類的主要特色是能夠利用反射來獲取對象,而且直接使用底層的c++來代替JVM執行,便可以繞過JVM的相關的對象的管理機制,若是項目中使用了Unsafe類,那麼項目之中將沒法進行JVM的內存管理機制以及垃圾回收處理.
--若是要想使用Unsafe類首先就須要確認一下這個類之中定義的構造方法與常量問題
構造方法: private Unsafe(){}
私有常量: private static final Unsafe theUnsafe;
可是須要注意的是在這個Unsafe的類中並無提供static的方法,即不能經過相似與傳統的單例設計模式中提供的樣式進行操做,若是要想得到這個類的對象,就必須利用反射機制來完成:
1 class GetUnsafeDemo{ 2 public static void main(String[] args) throws Exception{ 3 Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); 4 theUnsafe.setAccessible(true); //解除封裝處理 5 Unsafe unsafe = (Unsafe) theUnsafe.get(null); //static屬性不須要傳遞實例化對象 6 } 7 }
--在傳統的開發之中,一個程序類必需要經過實例化對象後,才能夠調用類中的普通方法,尤爲以單例設計模式爲例:
1 class Singleton { 2 private static final Singleton INSTANCE = new Singleton(); 3 4 private Singleton() { 5 } 6 7 public static Singleton getInstance() { 8 return INSTANCE; 9 } 10 11 public void print() { 12 System.out.println("實例化對象輸出內容"); 13 } 14 }
--利用Unsafe類繞過JVM的管理機制使用print()方法:
1 class GetUnsafeDemo { 2 public static void main(String[] args) throws Exception { 3 Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); 4 theUnsafe.setAccessible(true); //解除封裝處理 5 Unsafe unsafe = (Unsafe) theUnsafe.get(null); //static屬性不須要傳遞實例化對象 6 Singleton singleton = (Singleton) unsafe.allocateInstance(Singleton.class); 7 singleton.print(); 8 } 9 } 10 11 class Singleton { 12 private static final Singleton INSTANCE = new Singleton(); 13 private Singleton(){ 14 System.out.println("構造方法"); 15 } 16 public void print() { 17 System.out.println("實例化對象輸出內容"); 18 } 19 }
--運行結果
構造方法 實例化對象輸出內容 Process finished with exit code 0
--Uusafe只能說爲開發提供了一些更加方便的處理機制,可是這種操做因爲不受JVM的管理因此若是不是必須的狀況下不建議使用