編輯:關於Android編程
隨著應用復雜度增加,後期的應用更傾向於模塊化、插件化來盡量減少主程序的容量,對此有幾種方法來解決:1.使用so來封裝共同模塊,通過加載庫的形式實現代碼共享2.使用jar形式跟so一樣,不過容易被反編譯3.簡化代碼編寫可以使用反射機制和注解來實現,這篇文章來介紹這種方法4.插件,使用sharedUserId來實現共享進程的方式,這部分以後講解。
通過反射來實現簡化代碼之前我們需要了解反射機制以及如何實現反射,然後了解注解以及自定義注解,最後通過反射來解釋注解,由於內容比較多,下面會分為4部分來說明。
Java反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取信息以及獲取調用對象的方法的功能稱為Java語言的反射機制。
Java反射機制主要提供了以下的功能:
在運行時判斷任意一個對象所屬的類;在運行時構造任意一個類的對象;在運行時判斷任意一個類所具有的成員變量和方法;在運行時調用任意一個對象的方法;生成動態代理。在JDK中,主要由以下類來實現Java反射機制,這些類都位於java.lang.reflect包中。
Class類:代表一個類。Field類:代表類的成員變量(成員變量也稱為類的屬性)。Method類:代表類的方法。Constructor類:代表類的構造方法。Array類:提供了動態創建數組,以及訪問數組元素的靜態方法。在DumpMethods類演示了Reflection API的基本作用,它讀取命令行參數指定的類名,然後打印這個類所具有的方法信息:
package com.jwzhangjie.reflection; import java.lang.reflect.Method; public class DumpMethods { public static void main(String args[]) throws Exception{ //加載 並初始化命令行參數指定的類 Class classType = Class.forName(args[0]); //獲取類的所有方法 Method method[] = classType.getDeclaredMethods(); for (int i = 0; i < method.length; i++) { System.out.println(method[i].toString()); } } }
運行命令“java com/jwzhangjie/reflection/DumpMethods java.util.Stack”就會顯示java.util.Stack類所具有的方法,程序的打印結果如下:
下面我們使用ReflectTester類進一步演示Reflection API的基本使用方法。ReflectTester類有一個copy(Object object)方法,這個方法能夠創建一個和參數object同樣類型的對象,然後把object對象中的所有屬性復制到新建的對象中,並將它返回。
這個例子只能復制簡單的JavaBean,假定JavaBean的每個屬性都有public類型的getXXX()和setXXX()方法。
package com.jwzhangjie.reflection; import java.lang.reflect.Field; import java.lang.reflect.Method; public class ReflectTester { public Object copy(Object object) throws Exception{ //獲取對象類型 Class classType = object.getClass(); //獲得類的完整名字 System.out.println("Class:"+classType.getName()); //通過默認構造方法創建一個新的對象 Object objectCopy = classType.getConstructor().newInstance(); //獲取對象的所有屬性 Field fields[] = classType.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { Field field = fields[i]; String fieldName = field.getName(); String firstLetter = fieldName.substring(0, 1).toUpperCase(); //獲得和屬性對應的getXXX()方法的名字 String getMethodName = "get"+firstLetter+fieldName.substring(1); //獲得和屬性對應的setXXX()方法的名字 String setMethodName = "set"+firstLetter+fieldName.substring(1); //獲得和屬性對應的getXXX()方法 Method getMethod = classType.getMethod(getMethodName, new Class[]{}); //獲得和屬性對應的setXXX()方法 Method setMethod = classType.getMethod(setMethodName, new Class[]{field.getType()}); //調用原對象的getXXX()方法 Object value = getMethod.invoke(object, new Object[]{}); System.out.println(fieldName+": "+value); //調用復制對象的setXXX()方法 setMethod.invoke(objectCopy, new Object[]{value}); } return objectCopy; } public static void main(String[] args) throws Exception{ ReflectTester test = new ReflectTester(); Customer customer = new Customer("Tom", 21); customer.setId(new Long(1)); Customer customerCopy = (Customer)test.copy(customer); System.out.println("Copy infomation:"+customerCopy.getName()+" : "+customerCopy.getAge()); } public static class Customer{ private Long id; private String name; private int age; public Customer(){} public Customer(String name, int age){ this.name = name; this.age = age; } public Long getId(){return id;} public void setId(Long id){this.id = id;} public String getName(){return name;} public void setName(String name){this.name = name;} public int getAge(){return age;} public void setAge(int age){this.age = age;} } }
在這個例子裡需要注意幾點,例子裡面的Customer是一個內部類:
1.內部類Customer的實現,上面的例子裡面我修飾Customer使用的是static,調用內部類的另一個方式就是Customer customer = test.new Customer();使用外部類的對象.new來實例化
2.對於上面的例子如果使用外部類的對象.new來實例化Customer對象,那麼會出現一個Exception in thread "main" java.lang.NoSuchMethodException: com.jwzhangjie.reflection.ReflectTester$Customer.
下面我們來分析一下上面例子代碼。
ReflectTester類的copy(Object object)方法一次執行以下步驟。
(1)獲得對象的類型:
Class classType = object.getClass(); System.out.println("Class:"+classType.getName());
在java.lang.Object類中定義了getClass()方法,因此對於任意一個Java對象,都可以通過此方法獲得對象的類型。Class類是Reflection API中的核心類,它有以下方法。
getName():獲得類的完整名字。getFields():獲得類的public類型的屬性。getDeclaredFields():獲得類的所有屬性。getMethods():獲得類的public類型的方法。getDeclaredMethod():獲得類的所有方法。getMethod(String name, Class[] parameterTypes):獲得類的特定方法,name參數指定方法的名字,parameterTypes參數指定方法的參數類型。getConstrutors():獲得類的public類型的構造方法。getConstrutor(Class[] parameterTypes):獲得類的特定構造方法,parameterTypes參數指定構造方法的參數類型。newInstance():通過類的不帶參數的構造方法創建這個類的一個對象。(2)通過默認構造方法創建一個新的對象:
Object objectCopy = classType.getConstructor().newInstance();
以上代碼先調用Class類的getConstructor()方法獲得一個Constructor對象,它代表默認的構造方法,然後調用Constructor對象的newInstance()方法構造一個實例。
(3)獲得對象的所有屬性:
Field fields[] = classType.getDeclaredFields();
Class類的getDeclareFields()方法返回類的所有屬性,包括public、protected、默認和private訪問級別的屬性。
(4)獲得每個屬性相應的getXXX()和setXXX()方法,然後執行這些方法,把原來對象的屬性復制給新的對象中:
for (int i = 0; i < fields.length; i++) { Field field = fields[i]; String fieldName = field.getName(); String firstLetter = fieldName.substring(0, 1).toUpperCase(); //獲得和屬性對應的getXXX()方法的名字 String getMethodName = "get"+firstLetter+fieldName.substring(1); //獲得和屬性對應的setXXX()方法的名字 String setMethodName = "set"+firstLetter+fieldName.substring(1); //獲得和屬性對應的getXXX()方法 Method getMethod = classType.getMethod(getMethodName, new Class[]{}); //獲得和屬性對應的setXXX()方法 Method setMethod = classType.getMethod(setMethodName, new Class[]{field.getType()}); //調用原對象的getXXX()方法 Object value = getMethod.invoke(object, new Object[]{}); System.out.println(fieldName+": "+value); //調用復制對象的setXXX()方法 setMethod.invoke(objectCopy, new Object[]{value}); }
package com.jwzhangjie.reflection; import java.lang.reflect.Method; public class InvokeTester { public int add(int param1, int param2){ return param1+param2; } public String echo(String msg){ return "echo:"+msg; } public static void main(String[] args) throws Exception{ Class classType = InvokeTester.class; Object invokeTester = classType.newInstance(); //調用InvokeTester對象的add()方法 Method addMethod = classType.getMethod("add", int.class, int.class); Object result = addMethod.invoke(invokeTester, 100, 200); System.out.println((Integer)result); //調用InvokeTester對象的echo()方法 Method echoMethod = classType.getMethod("echo", String.class); result = echoMethod.invoke(invokeTester, "Hello"); System.out.println((String)result); } }
add()方法的兩個參數為int類型,獲得表示add()方法的Method對象的代碼如下:
Method addMethod = classType.getMethod("add", int.class, int.class);Method類的invoke(Object obj, Object args[])方法接收的參數必須為對象,如果參數為基本類型數據,比較轉換為相應的包裝類型的對象。invoke()方法的返回值總是對象,如果實際被調用的方法的返回類型是基本類型數據,那麼invoke()方法會把它轉化為相應的包裝類型的對象,再將其返回。
package com.jwzhangjie.reflection; import java.lang.reflect.Array; public class ArrayTester1 { public static void main(String args[]) throws Exception{ Class classType = Class.forName("java.lang.String"); //創建一個長度為10的字符串數組 Object array = Array.newInstance(classType, 10); //把索引位置為5的元素設為"hello" Array.set(array, 5, "hello"); //讀取索引位置為5的元素的值 String s = (String)Array.get(array, 5); System.out.println(s); } }如ArrayTester2類的main()方法創建了一個5 X 10 X 15的整形數組,並把索引位置為[3][5][10]的元素的值設為37.
package com.jwzhangjie.reflection; import java.lang.reflect.Array; public class ArrayTester2 { public static void main(String[] args) throws Exception{ int dims[] = new int[]{5, 10, 15}; Object array = Array.newInstance(Integer.TYPE, dims); //使用arrayObj引用array[3] Object arrayObj = Array.get(array, 3); Class cls = arrayObj.getClass().getComponentType(); System.out.println(cls); //使用arrayObj引用array[3][5] arrayObj = Array.get(arrayObj, 5); //把元素array[3][5][10]設為37 Array.setInt(arrayObj, 10, 37); int arrayCast[][][] = (int[][][])array; System.out.println(arrayCast[3][5][10]); } }
BottomNavigationView 很早之前就在 Material Design 中出現了,但是直到 Android Support Library 25 中才增加
今天這篇稍微增強點代碼量,可能要多花上5分鐘喽。本篇完成一個稍微顯得絢麗的菜單項,模仿優酷選擇菜單。如果想對其中的任意一項實現點擊功能,自行加入即可。現在就一步一步做出這
本博文是《第一行代碼 Android》的讀書筆記/摘錄。一、Content Provider簡介內容提供器(Content Provider)主要用於在不同的應用程序之間
Intent簡介Android中提供了Intent機制來協助應用間的交互與通訊,Intent負責對應用中一次操作的動作、動作涉及數據、附加數據進行描述,Android則根