Java反射,从0开始
目錄
靜態VS動態語言
Java?Reflection(反射)介紹
Java反射機制提供的功能
Java反射優點和缺點
反射相關的主要API
獲取運行時類的完整結構
反射的使用
有了Class對象,如何創建對象?
使用反射調用對象的指定方法
Object invoke(Object obj, Object[] args)
setAccessible
性能檢測
反射操作泛型
反射操作注解
靜態VS動態語言
動態語言:
? ? 是一類在運行時可以改變其結構的語言:例如新的函數、對象、甚至代碼可以被引進,已有的函數可以被刪除或是其他結構上的變化。通俗短說就是在運行時代碼可以根據某些條件改變自身結構。
? ? 主要動態語言:Object-C、C#、JavaScript、PHP、Python等。
靜態語言:
? ? 與動態語言相對應的,運行時結構不可變的語言就是靜態語言,如Java、C、C++。
? ? Java不是動態語言,但Java可以稱之為“準動態語言”。即Java有一定的動態性,我們可以利用反射機制獲得類似動態語言的特性。Java的動態性讓編程的時候更加靈活。
Java?Reflection(反射)介紹
? ? Reflection(反射)是Java被視為動態語言的關鍵,反射機制允許程序在執行期借助于Reflection?API取得任何類的內部信息,并能直接操作任意對象的內部屬性及方法。
? ? Class?c =?Class.forName("java.lang.String")
? ? 加載完類之后,在堆內存的方法區中就產生了一個Class類型的對象(一個類只有一個Class對象),這個對象就包含了完整的類的結構信息。我們可以通過這個對象看到類的結構。這個對象就像一面鏡子,透過這個鏡子看到類的結構,所以,我們形象的稱之為:反射。
Java反射機制提供的功能
? ? 在運行時判斷任意一個對象所屬的類。
? ? 在運行時構造任意一個類的對象。
? ? 在運行時判斷任意一個類所具有的成員變量和方法。
? ? 在運行時獲取泛型信息。
? ? 在運行時調用任意一個對象的成員變量和方法。
? ? 在運行時處理注解。
? ? 生成動態代理。
? ? ......
Java反射優點和缺點
? ? 優點:可以實現動態創建對象和編譯,體現出很大的靈活性。
? ? 缺點:對性能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什么并且它滿足我們的要求。這類操作總是慢于直接執行相同的操作。
反射相關的主要API
? ? java.lang.Class:代表一個類
? ? java.lang.reflect.Method:代表類的方法。
? ? java.lang.reflect.Field:代表類的成員變量。
? ? java.lang.reflect.Constructor:代表類的構造器。
? ? ......
獲取運行時類的完整結構
? ? 通過反射獲取運行時類的完整結構:Field、Method、Constructor、Superclass、Interface、Annotation。
? ? 實現的全部接口、所繼承的父類、全部的構造器、全部的方法、全部的Field、注解等等。
import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method;//獲取類的信息 public class Test06 {public static void main(String[] args) throws Exception {Class c1 = Class.forName("com.myUtils.reflectUtils.learn.MyUser");MyUser user = new MyUser();c1 = user.getClass();//獲得類的名字System.out.println(c1.getName());//包名 + 類名System.out.println(c1.getSimpleName());//獲得類名//獲得類的屬性Field[] fields = c1.getFields();//獲取所有(只能找到public的屬性)屬性fields = c1.getDeclaredFields();//獲取所有(找到全部的屬性)屬性Field id = c1.getDeclaredField("id");//獲取指定屬性//獲得類的方法Method[] methods = c1.getMethods();//獲取所有(本類及其父類public的)方法methods = c1.getDeclaredMethods();//獲取所有(本類全部的)方法Method getName = c1.getMethod("getName", null);Method setName = c1.getMethod("setName", String.class);//需要參數類型(重載問題)//獲得構造器Constructor[] constructors = c1.getConstructors();Constructor constructor = c1.getConstructor(null);//也需要參數類型} }class MyUser{public Integer id;public String name;private int age;@Overridepublic String toString() {return "MyUser{" +"id=" + id +", name='" + name + '\'' +", age=" + age +'}';}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}private int getAge() {return age;}public void setAge(int age) {this.age = age;} }反射的使用
有了Class對象,如何創建對象?
創建類的對象:調用Class對象的newInstance()方法:
? ? 1.類必須有一個無參數的構造器。
? ? 2.類的構造器的訪問權限需要足夠。
沒有無參構造器、構造器權限不足的情況創建對象:
? ? 1.通過Class類的getDeclaredConstructor(Class ... parameterTypes)取得本類的指定形參類型的構造器。
? ? 2.向構造器的形參中傳遞一個對象數組進去,里面包含了構造器中所需的各個參數。
? ? 3.通過Constructor實例化對象。
使用反射調用對象的指定方法
通過反射,調用類中的方法,通過Method類完成。
? ? 1.通過Class類的getMethod(String name,Class ... parameterTypes)方法取得一個Method對象,并設置此方法操作時所需要的參數類型。
? ? 2.之后使用Object invoke(Object obj, Object[] args)進行調用,并向方法中傳遞要設置的obj對象的參數信息。
Object invoke(Object obj, Object[] args)
? ? Object對應原方法的返回值,若原方法無返回值,此時返回null。
? ? 若原方法為靜態方法,此時形參Object?obj可為null。
? ? 若原方法形參列表為空,則Object[] args為null。
? ? 若原方法聲明為private,則需要在調用此invoke()方法前,顯式調用方法對象的setAccessible(true)方法。將可訪問private的方法。
setAccessible
? ? Method和Field、Construetor對象都有setAccessible()方法。
? ? setAccessible作用是啟動和禁用訪問安全檢查的開關。
? ? 參數值為true則指示反射的對象在使用時應該取消Java語言訪問檢查。
?? ?? ? 提高反射的效率。如果代碼中必須用反射,而該句代碼需要頻繁的被調用,那么請設置為true;
?? ?? ? 使得原本無法訪問的私有成員也可以訪問。
? ? 參數值為false則指示反射的對象應該實施Java語言訪問檢查。
import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method;//通過反射創建對象 public class Test07 {public static void main(String[] args) throws Exception {Class c1 = Class.forName("com.myUtils.reflectUtils.learn.MyUser07");//構造一個對象MyUser07 user = (MyUser07) c1.newInstance();//實際上調用了無參構造器//通過構造器創建對象Constructor constructor = c1.getDeclaredConstructor(Integer.TYPE, String.class);user = (MyUser07) constructor.newInstance(5, "張三");//通過反射調用方法Method setName = c1.getDeclaredMethod("setName", String.class);setName.invoke(user, "李四");//(對象,參數)System.out.println(user.getName());//通過反射操作屬性Field name = c1.getDeclaredField("name");name.setAccessible(true);//設置為true就可以獲取到私有屬性、方法(關閉程序的安全監測)name.set(user, "王五");System.out.println(name.get(user));} } class MyUser07{private Integer id;private String name;public MyUser07(Integer id, String name) {this.id = id;this.name = name;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;} }?
性能檢測
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;public class Test08 {public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {/*** 普通時17ms* 反射耗時3398ms* 關閉檢測耗時1657ms*/test1();test2();test3();}//普通方式public static void test1(){User08 user08 = new User08();long startTime = System.currentTimeMillis();for (int i = 0; i < 1000000000; i++) {user08.getName();}long endTime = System.currentTimeMillis();System.out.println("普通時" + (endTime - startTime) + "ms");}//反射方式public static void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {User08 user08 = new User08();Class c1 = user08.getClass();Method getName = c1.getDeclaredMethod("getName", null);long startTime = System.currentTimeMillis();for (int i = 0; i < 1000000000; i++) {getName.invoke(user08, null);}long endTime = System.currentTimeMillis();System.out.println("反射耗時" + (endTime - startTime) + "ms");}//反射方式,關閉檢測public static void test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {User08 user08 = new User08();Class c1 = user08.getClass();Method getName = c1.getDeclaredMethod("getName", null);getName.setAccessible(true);long startTime = System.currentTimeMillis();for (int i = 0; i < 1000000000; i++) {getName.invoke(user08, null);}long endTime = System.currentTimeMillis();System.out.println("關閉檢測耗時" + (endTime - startTime) + "ms");} } class User08{private Integer id;private String name;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;} }反射操作泛型
? ? Java采用泛型擦除的機制來引入泛型,Java的泛型僅僅是給編譯器javac使用的,確保數據的安全性和免去強制類型轉換問題,但是,一旦編譯完成,所有和泛型有關的類型全部擦除。
? ? 為了通過反射操作這些類型,Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType集中類型來代表不能被歸一到class類中的類型但是又和原始類型齊名的類型。
ParameterizedType:表示一種參數化類型,比如Collection<String>。
GenericArrayType:表示一種元素類型是參數化類型或者類型變量的數組類型。
TypeVariable:是各種類型變量的公共父接口。
WildcardType:代表一種通配符類型表達式。
import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import java.util.Map;//通過反射獲取泛型 public class Test09 {public void test01(Map<String, User> map, List<Integer> list){System.out.println("test01");}public Map<String, User> test02(){System.out.println("test02");return null;}public static void main(String[] args) throws NoSuchMethodException {//獲取方法參數泛型Method method = Test09.class.getMethod("test01", Map.class, List.class);Type[] genericParameterTypes = method.getGenericParameterTypes();for (Type genericParameterType : genericParameterTypes) {System.out.println("#"+genericParameterType);if(genericParameterType instanceof ParameterizedType){Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();for (Type actualTypeArgument : actualTypeArguments) {System.out.println(actualTypeArgument);}}}//獲取返回值泛型Method method2 = Test09.class.getMethod("test02", null);Type genericReturnType = method2.getGenericReturnType();//獲取返回類型System.out.println("#"+genericReturnType);if(genericReturnType instanceof ParameterizedType){Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();for (Type actualTypeArgument : actualTypeArguments) {System.out.println(actualTypeArgument);}}} }反射操作注解
import java.lang.annotation.*; import java.lang.reflect.Field;public class Test10 {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {Class c1 = Class.forName("com.myUtils.reflectUtils.learn.Student10");//通過反射獲取注解Annotation[] annotations = c1.getAnnotations();for (Annotation annotation : annotations) {System.out.println(annotation);}//獲得注解的value的值TableTest tableTest = (TableTest)c1.getAnnotation(TableTest.class);String value = tableTest.value();System.out.println(value);//db_student//獲得類指定的注解Field f = c1.getField("name");FieldTest annotation = f.getAnnotation(FieldTest.class);System.out.println(annotation.columnName());System.out.println(annotation.type());System.out.println(annotation.length());} }@TableTest("db_student") class Student10{@FieldTest(columnName = "db_id", type = "int", length = 10)private Integer id;@FieldTest(columnName = "db_name", type = "varchar", length = 20)private String name;public Student10() {}public Student10(Integer id, String name) {this.id = id;this.name = name;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;} }//類名的注解 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface TableTest{String value(); } //屬性的注解 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface FieldTest{String columnName();String type();int length(); }?
總結
以上是生活随笔為你收集整理的Java反射,从0开始的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 还在对java类、类的加载一知半解?这篇
- 下一篇: java对象创建的流程到底是什么样子的?