反射原理
一、反射原理的概念
反射:將類中的各個成分映射成不同的對象
? ?反射原理舉例:
java.lang.Class:將java中的任何一個類都可以統一使用
? ?反射的深層原理:
? ? 如果對于一個類,在編譯期間不知道其中的結構,而希望在運行期間去探索、得知內部的結構,則需要使用反射原理
? ? 如果要使用反射原理去探索,則需要先加載這個類
獲取Class對象的方式
? ? Class.forName("類的全局限定名")
? ? 類名.class
? ? 調用對象實例中的getClass方法
? ? 通過類加載器獲取
注意:
? ? ?方法一不執行靜態塊和動態構造塊
? ? ?方法二執行靜態快、不執行動態構造塊
? ? ?方法三需要創建對象,靜態快和動態快都會執行
? ? ?四種方式得到的Class對象都是同一個對象,因為類只會被加載一次,因此要判斷是否為某一類型,用==比較即可
?
java反射原理中有哪些核心類?分別有什么作用?
通配符和泛型
? ?無限定通配符?
? ?使用一個問號表示類型參數,雖然我們可以在用到的使用再指定具體的類型,但是如果在用到的時候還不能確定具體的類型,就需要依靠通配符來解決。即引入了通配符,使得泛型變成協變的。
上邊界限定通配符? extends …… ? ? ?
? ? List<? extends Number> list1 = new ArrayList<Number>(); ? ?
? ? List<? extends Number> list2 = new ArrayList<Integer>(); ? ?
? ? List<? extends Number> list3 = new ArrayList<Long>();
下邊界限定通配符? super …… ? ? ?
? ? List<? super Integer> list4 = new ArrayList<Integer>(); ? ?
? ? List<? super Integer> list5 = new ArrayList<Number>();
? ? T、E、V、K等
? ? ? 以List或Map為例,其中的泛型參數定義為<E>或者<K,V>,類中的方法中甚至將E、K、V等作為參數類型使用。此處和?最大的區別在于,這個類型其實是一個確定的類型,調用時指定什么類型就是什么類型,例如List<String>,編譯期間會將E自動擦除,替換成String,類中所有出現E的地方,也替換成String。
? ? ? 這一類參數相當于實現了類中一些類型的同統一(包括參數、返回值等)
/**** extends** super*/ public class Demo02 {public static void main(String[] args) { // List<Integer> list1=new ArrayList<Integer>(); // List<Double> list2=new ArrayList<Double>();//? extends Number:類型可以是Number類型或其子類型List<? extends Number> list1 = new ArrayList<Number>();List<? extends Number> list2 = new ArrayList<Integer>();List<? extends Number> list3 = new ArrayList<Double>();//List<? extends Number> list4=new ArrayList<String>();//? super Integer:Integer類型本身或其父類List<? super Number> list5 = new ArrayList<Number>();List<? super Integer> list6 = new ArrayList<Number>();} }? ?
import java.util.LinkedList; import java.util.List;/*** T/E/V/K:泛型參數,*/ public class Demo03 {public static void main(String[] args) {MyStack<Integer> myStack = new MyStack<>();myStack.add(1);myStack.add(2);myStack.add(3);myStack.print();MyStack<String> myStack2 = new MyStack<>();myStack2.add("1");myStack2.add("2");myStack2.add("3");myStack2.print();} }/*** 表示一個棧*/ class MyStack<E> {/*** E:表示的是一個泛型,類中使用的泛型要在類上聲明出來* 泛型參數在運行期間是不存在的,在編譯成字節碼的時候,會使用具體的類型來擦除泛型參數* 泛型參數其實是一種固定的數據類型*/private List<E> list = new LinkedList<>();// private List<F> list2 = new LinkedList<>();public void add(E o) {//泛型參數不能當做類去使用,也不能直接實例化//E e=new E();list.add(o);}/*** 該方法實現的操作:打印出當前集合中的元素值* <p>* 如果元素是整數則,每個+5* 如果是字符串,則前面加上str前綴*/public void print() {for (E ele : list) {if (ele.getClass() == Integer.class) {int num=(Integer) ele;System.out.println(num+5);}if (ele.getClass() == String.class) {String str=(String) ele;System.out.println("str"+str);}}}/*** 如果泛型參數只是某個方法用到,可以不用在類中聲明這個泛型參數* <p>* 可以直接在方法中聲明這個泛型參數** @param param1*/public <T, R, S> S method(T param1, R param2) {return null;}}java.lang.Class:將java中的任何一個類都可以統一使用Class類型來表示,表示的是一個類的完整信息,所有反射操作的開始都是在這里開始的。
import com.sy.reflect.entity.Emp;/*** java.lang.Class:當一個類被虛擬機加載以后,會得到一個Class對象* Class對象是反射中其它操作的起源,通過Class對象可以得到類中的屬性、方法、構造器等對象*/ public class Demo01 {public static void main(String[] args) {try {//Class.forName("類的全局限定名")//全局限定名=類所在的包+類名//注意:不能使用具體的類型作為泛型,應該使用?//?:類型不確定的時候,使用?解決//通過字符串指定類名,虛擬機不知道最后會得到什么類型Class<?> clazz1 = Class.forName("com.sy.reflect.entity.Emp");System.out.println(clazz1);//類名.classClass<Emp> clazz2 = Emp.class;System.out.println(clazz2);//對象實例.getClass();//由于在編譯期間還沒產生對象,虛擬機也不知道會產生什么對象//所以類型不確定 // Class<?> clazz3 = new Emp().getClass(); // System.out.println(clazz3);//由于Class對象在第一次加載以后會進行緩存,所以//重復加載不會得到新的對象System.out.println(clazz1 == clazz2); // System.out.println(clazz3 == clazz3); // System.out.println(clazz1 == clazz3);} catch (ClassNotFoundException e) {e.printStackTrace();}} }? ?
import com.sy.reflect.entity.Emp;import java.lang.reflect.Modifier; import java.util.Arrays;/*** Class對象的用法*/ public class Demo04 {public static void main(String[] args) {try {Class<?> clazz = Class.forName("com.sy.reflect.entity.Emp");//獲取訪問修飾符//(1)獲取的是訪問修飾符對應的整數值System.out.println(clazz.getModifiers());//(2)可以將訪問修飾符對應的數值轉換為字符串System.out.println(Modifier.toString(clazz.getModifiers()));//獲取類名//完整類名=包名+類名System.out.println(clazz.getName());//類名System.out.println(clazz.getSimpleName());//獲得了Class對象以后,可以調用Class對象的newInstance方法來創建對象//權限足夠的前提之下,調用的其實是類的無參構造方法Object emp = clazz.newInstance();System.out.println(emp);//判斷clazz是否為基本數據類型System.out.println(Integer.class.isPrimitive());System.out.println(int.class.isPrimitive());System.out.println(void.class.isPrimitive());//判斷是否為數組類型System.out.println(new int[]{1, 2, 3}.getClass().isArray());//isArray()方法不能用于集合System.out.println(Arrays.asList(1, 2, 3).getClass().isArray());} catch (ClassNotFoundException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();}} }java.lang.reflect.Field:將類中的屬性映射成該類型
import com.sy.reflect.entity.Emp;import java.lang.reflect.Field; import java.lang.reflect.Modifier;/*** java.lang.reflect.Field:表示類中的屬性* 通過Field對象可以獲取屬性的信息,也可以對屬性的值進行修改*/ public class Demo06 {public static void main(String[] args) {try {Class<?> clazz = Class.forName("com.sy.reflect.entity.Emp");//獲取所有的屬性//clazz.getFields():只返回公有屬性Field[] fields = clazz.getFields();for (Field field : fields) {System.out.println(field);}System.out.println("========================");//clazz.getDeclaredFields():返回所有的屬性fields = clazz.getDeclaredFields();for (Field field : fields) {System.out.println(field);}System.out.println("========================");//獲取某一個屬性//clazz.getField只能獲取公有的屬性Field field = clazz.getField("gender");System.out.println(field);//clazz.getDeclaredField可以返回任何一個屬性field = clazz.getDeclaredField("name");System.out.println(field);System.out.println("========================");//Field對象的用法//獲取訪問修飾符System.out.println(Modifier.toString(field.getModifiers()));//獲取屬性的數據類型,返回的是Class<?>System.out.println(field.getType());//獲取屬性名System.out.println(field.getName());//也可以使用Filed對象對屬性進行賦值操作 // Emp emp=new Emp(); // emp.name="";//emp.屬性名=值;//第一個參數:要被修改屬性的對象,第二個參數:要修改進去的屬性值Object obj=clazz.newInstance();System.out.println(obj);//打破封裝,訪問私有屬性field.setAccessible(true);field.set(obj,"Smith" );//用完后,還原封裝field.setAccessible(false);System.out.println(obj);//Emp emp=new Emp();//emp.屬性名="";//emp.setxxx();} catch (ClassNotFoundException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();}} }java.lang.reflect.Construtor:將類中的構造器映射成該類型
import com.sy.reflect.entity.Emp;import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier;/*** java.lang.reflect.Constructor:* 構造器對象,獲取構造器對象以后,可以對類進行實例化操作*/ public class Demo05 {public static void main(String[] args) {try {Class<?> clazz = Class.forName("com.sy.reflect.entity.Emp");//獲取所有的構造器//getConstructors:獲取所有的公有構造方法Constructor<?>[] constructors = clazz.getConstructors();for (Constructor constructor : constructors) {System.out.println(constructor);}System.out.println("=================================");//getDeclaredConstructors:獲取所有的構造方法constructors = clazz.getDeclaredConstructors();for (Constructor constructor : constructors) {System.out.println(constructor);}System.out.println("=================================");//獲取某一個構造器//getConstructor方法的參數應該要執行構造器的參數列表的類型Constructor<?> constructor = clazz.getConstructor();System.out.println(constructor);constructor = clazz.getConstructor(String.class, int.class, char.class, String.class, double.class);System.out.println(constructor);//getConstructor只能獲取公有的構造方法 // constructor = clazz.getConstructor(String.class); // System.out.println(constructor);//getDeclaredConstructor();獲取任何修飾詞修飾的構造方法constructor = clazz.getDeclaredConstructor(String.class);System.out.println(constructor);System.out.println("==================");//獲取構造器的訪問修飾符System.out.println(Modifier.toString(constructor.getModifiers()));//通過構造器進行實例化操作constructor = clazz.getConstructor(String.class, int.class, char.class, String.class, double.class);//通過newInstance進行實例化,方法參數就是構造器中要傳入的屬性的初始值//調用public構造Object obj = constructor.newInstance("Tom", 1001, 'F', "Java開發", 6000);System.out.println(obj);System.out.println("============================");//調用非public構造constructor = clazz.getDeclaredConstructor(String.class);//打破封裝,使得原來權限不夠的構造方法可以被調用constructor.setAccessible(true);obj = constructor.newInstance("Jack");//用完以后,再將封裝還原constructor.setAccessible(false);System.out.println(obj);} catch (ClassNotFoundException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}} }java.lang.reflect.Method:將類中的成員方法映射成該類型
import com.sy.reflect.entity.Emp;import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier;/*** java.lang.reflect.Method:用來表示成員方法* 可以用來獲取方法信息,通過Method對象可以調用類中的方法*/ public class Demo07 {public static void main(String[] args) {try {Class<?> clazz = Class.forName("com.sy.reflect.entity.Emp");//獲取類中的所有方法//獲取當前類的所有公有方法 clazz.getMethods();Method[] methods = clazz.getMethods();for (Method method : methods) {System.out.println(method);}System.out.println("=========================");//獲取當前類中無論何種修飾符的方法methods = clazz.getDeclaredMethods();for (Method method : methods) {System.out.println(method);}System.out.println("=========================");// getMethod(本類及父類中public的)//一個類中同名方法可能有多個(重載)//所以在指定方法名的同時要指定方法的參數列表Method method = clazz.getMethod("method01", int.class, String.class);System.out.println(method); // getDeclaredMethod(本類中無論何種訪問修飾符)method = clazz.getDeclaredMethod("method01", Integer.class, String.class);System.out.println(method);System.out.println("=========================");//獲取的方法的訪問修飾符System.out.println(Modifier.toString(method.getModifiers()));//獲取方法的返回類型Class<?> returnType = method.getReturnType();System.out.println(returnType.getName());System.out.println(returnType.getSimpleName());//獲取方法名System.out.println(method.getName());//獲取方法的參數類型Class<?>[] parameterTypes = method.getParameterTypes();for (Class<?> parameterType : parameterTypes) {System.out.println(parameterType);}//通過Method對象調用方法 // Emp emp=new Emp(); // emp.method01(, );//emp.方法名(參數1,參數2,參數3,……,參數N);Object obj = clazz.newInstance();//invoke返回的結果就是方法的返回值//打破封裝后,調用私有方法method.setAccessible(true);Object result = method.invoke(obj, 3, "Hello");//封裝還原method.setAccessible(false);System.out.println(result);System.out.println("==========================");//注意:void返回方法的invoke返回的值method = clazz.getDeclaredMethod("method03", int.class);System.out.println(method);result = method.invoke(obj, 1);//空參方法的invoke返回為nullSystem.out.println(result);//需要區分是帶返回類型的方法返回null還是方法本身就是void方法if (method.getReturnType() == void.class) {System.out.println("方法返回類型為void");} else {System.out.println("方法有指定返回類型");}} catch (ClassNotFoundException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}} }類加載器的緩存機制:
? ? 類裝載器還使用了Cache(緩存)機制,如果緩存中有這個Class則直接返回它,如果沒有,則從文件中讀取并轉換成Class對象,同時再將它Cache起來。這樣做的目的是保證Class對象只被包裝一次。這也就是為什么在修改了java代碼之后,需要重新啟動才會生效的原因。
? ? 結合同步代碼塊中的類名.class全局鎖? ? ? ? ? ? ? ?記憶。
?
?
總結
- 上一篇: 使用websocketpp编写webso
- 下一篇: MathType如何输入空格