本章重點介紹java.lang.reflect包下的介面和類 當程式使用某個類時,如果該類還沒有被載入到記憶體中,那麼系統會通過載入,連接,初始化三個步驟來對該類進行初始化. 類的載入時指將類的class文件讀入記憶體,併為之創建一個java.lang.class對象,也就是說,當程式中使用任何類時,系 ...
本章重點介紹java.lang.reflect包下的介面和類
當程式使用某個類時,如果該類還沒有被載入到記憶體中,那麼系統會通過載入,連接,初始化三個步驟來對該類進行初始化.
類的載入時指將類的class文件讀入記憶體,併為之創建一個java.lang.class對象,也就是說,當程式中使用任何類時,系統都會為之建立一個java.lang.Class對象.(幾乎所有的類都是java.lang.Class的實例);
所以JVM最先初始化的總是java.long.Object類.
在java中,一個類用其全限定類名(包括包名和類名)作為標識;但在JVM中,一個類用其全限定類名和類載入器作為其唯一標識.
列印根類載入器:
public class BootstrapTest { public static void main(String[] args) { // 獲取根類載入器所載入的全部URL數組 for (URL url : sun.misc.Launcher.getBootstrapClassPath().getURLs()) { // 遍歷、輸出根類載入器載入的全部URL System.out.println(url.toExternalForm()); } } } ---------------------------------------------------------------------- file:/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home/jre/lib/resources.jar file:/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home/jre/lib/rt.jar file:/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home/jre/lib/jsse.jar file:/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home/jre/lib/jce.jar file:/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home/jre/lib/charsets.jar file:/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home/jre/lib/jfr.jar
擴展類載入器,這個可以加入自己的jar包,挺好玩的.
系統類載入器:這個就是我們平常自定義類的父載入器了
開發者實現自定義的類載入器需要通過繼承ClassLoader來實現.
public static void main(String[] args) throws IOException { // 獲取系統類載入器 ClassLoader systemLoader = ClassLoader.getSystemClassLoader(); System.out.println("系統類載入器:" + systemLoader); /* 獲取系統類載入器的載入路徑——通常由CLASSPATH環境變數指定 如果操作系統沒有指定CLASSPATH環境變數,預設以當前路徑作為 系統類載入器的載入路徑 */ Enumeration<URL> em1 = systemLoader.getResources(""); while(em1.hasMoreElements()) { System.out.println(em1.nextElement()); } // 獲取系統類載入器的父類載入器:得到擴展類載入器 ClassLoader extensionLader = systemLoader.getParent(); System.out.println("擴展類載入器:" + extensionLader); System.out.println("擴展類載入器的載入路徑:" + System.getProperty("java.ext.dirs")); System.out.println("擴展類載入器的parent: " + extensionLader.getParent()); }
自定義類載入器的例子:
由於java8.0.51的URLClassLoader里重寫了ClassLoader這個類里的下麵這個方法,
protected Class<?> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }
所以,現在還沒弄明白原因,如果我們自己重寫findClass(String name),結果就是程式會先調用URLClassLoader里的findClass方法,如果這個方法找不到類,才會調用我們自己寫的findClass(String name).
比如我們要動態載入某個類(如果記憶體存在這個類,那麼閑從記憶體取;如果記憶體中不存在,那麼載入那個類的java文件並編譯(我不確定是不是要先檢查是否有class文件,看源碼和自己的測試結果是沒有檢查)).
例子
public class Hello { public static void main(String[] args) { System.out.println("tes22t2"); for (String arg : args) { System.out.println("運行Hello的參數:" + arg); } } }
import java.io.*; import java.lang.reflect.*; import java.net.URL; import java.security.CodeSigner; import java.security.CodeSource; import java.util.jar.Manifest; import sun.misc.Resource; import sun.misc.URLClassPath; import com.sun.xml.internal.bind.annotation.OverrideAnnotationOf; /** * Description: * <br/>網站: <a href="http://www.crazyit.org">瘋狂Java聯盟</a> * <br/>Copyright (C), 2001-2016, Yeeku.H.Lee * <br/>This program is protected by copyright laws. * <br/>Program Name: * <br/>Date: * @author Yeeku.H.Lee [email protected] * @version 1.0 */ public class CompileClassLoader extends ClassLoader { // 讀取一個文件的內容 private byte[] getBytes(String filename) throws IOException { File file = new File(filename); long len = file.length(); byte[] raw = new byte[(int)len]; try( FileInputStream fin = new FileInputStream(file)) { // 一次讀取class文件的全部二進位數據 int r = fin.read(raw); if(r != len) throw new IOException("無法讀取全部文件:" + r + " != " + len); return raw; } } // 定義編譯指定Java文件的方法 private boolean compile(String javaFile) throws IOException { System.out.println("CompileClassLoader:正在編譯 " + javaFile + "..."); // 調用系統的javac命令 Process p = Runtime.getRuntime().exec("javac " + javaFile); try { // 其他線程都等待這個線程完成 p.waitFor(); } catch(InterruptedException ie) { System.out.println(ie); } // 獲取javac線程的退出值 int ret = p.exitValue(); // 返回編譯是否成功 return ret == 0; } // 重寫ClassLoader的findClass方法 @Override protected Class<?> findClass(String tmpName) throws ClassNotFoundException { System.out.println(tmpName); Class clazz = null; // 將包路徑中的點(.)替換成斜線(/); String className = tmpName.replace("." , "/").replace("1" , "");
// 這裡因為我是在eclipse里編輯的,而java文件統一放到src里,class文件統一放到了bin里, 所以我需要加首碼, String javaFilename = "src/"+className + ".java"; String classFilename = "bin/"+className + ".class"; File javaFile = new File(javaFilename); System.out.println(javaFile.getAbsolutePath()); File classFile = new File(classFilename); // 當指定Java源文件存在,且class文件不存在、或者Java源文件 // 的修改時間比class文件修改時間更晚,重新編譯 if(javaFile.exists() && (!classFile.exists() || javaFile.lastModified() > classFile.lastModified())) { try { // 如果編譯失敗,或者該Class文件不存在 if(!compile(javaFilename) || !classFile.exists()) { throw new ClassNotFoundException( "ClassNotFoundExcetpion:" + javaFilename); } } catch (IOException ex) { ex.printStackTrace(); } } // 如果class文件存在,系統負責將該文件轉換成Class對象 if (classFile.exists()) { try { // 將class文件的二進位數據讀入數組 byte[] raw = getBytes(classFilename); // 調用ClassLoader的defineClass方法將二進位數據轉換成Class對象 clazz = defineClass(className,raw,0,raw.length); } catch(IOException ie) { ie.printStackTrace(); } } // 如果clazz為null,表明載入失敗,則拋出異常 if(clazz == null) { throw new ClassNotFoundException(className); } return clazz; } // 定義一個主方法 public static void main(String[] args) throws Exception { // 如果運行該程式時沒有參數,即沒有目標類 args = new String[]{"Hello","java瘋狂講義w"}; // 第一個參數是需要運行的類 String progClass = args[0]; // 剩下的參數將作為運行目標類時的參數, // 將這些參數複製到一個新數組中 String[] progArgs = new String[args.length-1]; System.arraycopy(args , 1 , progArgs , 0 , progArgs.length); CompileClassLoader ccl = new CompileClassLoader(); // 載入需要運行的類 Class<?> clazz = ccl.loadClass(progClass); // 獲取需要運行的類的主方法 Method main = clazz.getMethod("main" , (new String[0]).getClass()); Object[] argsArray = {progArgs}; main.invoke(null,argsArray); } }
輸出結果是
tes22t2
運行Hello的參數:java瘋狂講義w
打斷點可見,並沒有調用我們自定義的findClass(String tmpName)方法.
當我們把 args = new String[]{"Hello","java瘋狂講義w"}; 改為 args = new String[]{"Hello1","java瘋狂講義w"}; 時,由於URLClassLoader並沒有找到被載入的類Hello1,所以最後會調用我們自定義的findClass方法. 當然,更規範的是把 Class<?> clazz = ccl.loadClass(progClass); 改為 Class<?> clazz = ccl.findClass(progClass);
輸出結果:
Hello1 /Users/liuxin/work/workspace2/learnJava/src/Hello.java tes22t2 運行Hello的參數:java瘋狂講義w
這裡我們可以看到,如果要動態載入某個類,不用自己覆寫findClass方法,只要如下代碼就好:
import java.io.*; import java.lang.reflect.*; import java.net.URL; import java.security.CodeSigner; import java.security.CodeSource; import java.util.jar.Manifest; import sun.misc.Resource; import sun.misc.URLClassPath; import com.sun.xml.internal.bind.annotation.OverrideAnnotationOf; public class CompileClassLoader extends ClassLoader { public static void main(String[] args) throws Exception { // 如果運行該程式時沒有參數,即沒有目標類 args = new String[]{"Hello","java瘋狂講義w"}; // 第一個參數是需要運行的類 String progClass = args[0]; // 剩下的參數將作為運行目標類時的參數, // 將這些參數複製到一個新數組中 String[] progArgs = new String[args.length-1]; System.arraycopy(args , 1 , progArgs , 0 , progArgs.length); CompileClassLoader ccl = new CompileClassLoader(); // 載入需要運行的類 Class<?> clazz = ccl.loadClass(progClass); // 獲取需要運行的類的主方法 Method main = clazz.getMethod("main" , (new String[0]).getClass()); Object[] argsArray = {progArgs}; main.invoke(null,argsArray); } } ----------------輸出結果-------------------- tes22t2 運行Hello的參數:java瘋狂講義w
18.2.4 URLClassLoader類
java為ClassLoader提供了一個URLClassLoader實現類,該類也是系統類載入器和擴展類載入器的父類(此處的父類,就是指類與類之間的繼承關係).URLClassLoader功能強大,可以從本地或遠程主機獲取二進位文件來載入類.
下麵是一個例子
import java.sql.*; import java.util.*; import java.net.*; /** * Description: * <br/>網站: <a href="http://www.crazyit.org">瘋狂Java聯盟</a> * <br/>Copyright (C), 2001-2016, Yeeku.H.Lee * <br/>This program is protected by copyright laws. * <br/>Program Name: * <br/>Date: * @author Yeeku.H.Lee [email protected] * @version 1.0 */ public class URLClassLoaderTest { private static Connection conn; // 定義一個獲取資料庫連接方法 public static Connection getConn(String url , String user , String pass) throws Exception { if (conn == null) { // 創建一個URL數組 URL[] urls = {new URL( "file:mysql-connector-java-5.1.30-bin.jar")}; // 以預設的ClassLoader作為父ClassLoader,創建URLClassLoader URLClassLoader myClassLoader = new URLClassLoader(urls); // 載入MySQL的JDBC驅動,並創建預設實例 Driver driver = (Driver)myClassLoader. loadClass("com.mysql.jdbc.Driver").newInstance(); // 創建一個設置JDBC連接屬性的Properties對象 Properties props = new Properties(); // 至少需要為該對象傳入user和password兩個屬性 props.setProperty("user" , user); props.setProperty("password" , pass); // 調用Driver對象的connect方法來取得資料庫連接 conn = driver.connect(url , props); } return conn; } public static void main(String[] args)throws Exception { Connection temconn = getConn("jdbc:mysql://localhost:3306/mybatis" , "root" , "password"); try{ String sql = "INSERT INTO `mybatis`.`users` (`NAME`, `age`) VALUES ('java8', '2')"; java.sql.PreparedStatement stmt = temconn.prepareStatement(sql); stmt.execute(); stmt.close(); String sql2 = "select * from `mybatis`.`users`"; java.sql.PreparedStatement stmt2 = temconn.prepareStatement(sql2); ResultSet rs = stmt2.executeQuery(); ResultSetMetaData m=rs.getMetaData(); //顯示列,表格的表頭 int columns=m.getColumnCount(); for(int i=1;i<=columns;i++) { System.out.print(m.getColumnName(i)); System.out.print("\t\t"); } System.out.println(); //顯示表格內容 while(rs.next()) { for(int i=1;i<=columns;i++) { System.out.print(rs.getString(i)); System.out.print("\t\t"); } System.out.println(); } stmt2.close(); }catch(Exception e){ e.printStackTrace(); }finally{ temconn.close(); } } }
所以前面的動態載入類,可以用URLClassLoader改寫如下:(為了弄明白路徑,把Hello.java放到了learnJava包里去了)
public static void main(String[] args) throws Exception { args = new String[]{"learnJava.Hello","java瘋狂講義w"}; String progClass = args[0]; String[] progArgs = new String[args.length-1]; System.arraycopy(args , 1 , progArgs, 0 , progArgs.length); URL[] urls = {new URL("file:")}; Class<?> clazz = (new URLClassLoader(urls)).loadClass(progClass); // 獲取需要運行的類的主方法 Method main = clazz.getMethod("main" , (new String[0]).getClass()); Object[] argsArray = {progArgs}; main.invoke(null,argsArray); }
18.3 通過反射來查看類的信息
什麼時候會用到反射?
從Class中獲取信息,方法分以下幾類:
1.獲取構造器
2.獲取方法
3.獲取屬性
上面這三類方法,每一類都分4個方法,例如:(單個,多個) * (按許可權,不顧許可權)
4.獲取註解,這個太多:
5.獲取內部類
Class<?>[] getDeclaredClasses():返回該Class對象對應類包含的全部內部類
6.獲取外部類
Class<?>[] getDeclaringClasse():返回該Class對象對應類所在的外部類
7.獲取介面
Class<?>[] getInterfaces():返回該Class對象對應類所實現的全部介面
8.其他的如下:
例子:
// 定義可重覆註解 @Repeatable(Annos.class) @interface Anno {} @Retention(value=RetentionPolicy.RUNTIME) @interface Annos { Anno[] value(); } // 使用4個註解修飾該類 @SuppressWarnings(value="unchecked") @Deprecated // 使用重覆註解修飾該類 @Anno @Anno public class ClassTest { // 為該類定義一個私有的構造器 private ClassTest() { } // 定義一個有參數的構造器 public ClassTest(String name) { System.out.println("執行有參數的構造器"); } // 定義一個無參數的info方法 public void info() { System.out.println("執行無參數的info方法"); } // 定義一個有參數的info方法 public void info(String str) { System.out.println("執行有參數的info方法" + ",其str參數值:" + str); } // 定義一個測試用的內部類 class Inner { } public static void main(String[] args) throws Exception { // 下麵代碼可以獲取ClassTest對應的Class Class<?> clazz = ClassTest.class; // 獲取該Class對象所對應類的全部構造器 Constructor[] ctors = clazz.getDeclaredConstructors(); System.out.println("ClassTest的全部構造器如下:"); for (Constructor c : ctors) { System.out.println(c); } // 獲取該Class對象所對應類的全部public構造器 Constructor[] publicCtors = clazz.getConstructors(); System.out.println("ClassTest的全部public構造器如下:"); for (Constructor c : publicCtors) { System.out.println(c); } // 獲取該Class對象所對應類的全部public方法 Method[] mtds = clazz.getMethods(); System.out.println("ClassTest的全部public方法如下:"); for (Method md : mtds) { System.out.println(md); } // 獲取該Class對象所對應類的指定方法 System.out.println("ClassTest裡帶一個字元串參數的info()方法為:" + clazz.getMethod("info" , String.class)); // 獲取該Class對象所對應類的上的全部註解 Annotation[] anns = clazz.getAnnotations(); System.out.println("ClassTest的全部Annotation如下:"); for (Annotation an : anns) { System.out.println(an); } System.out.println("該Class元素上的@SuppressWarnings註解為:" + Arrays.toString(clazz.getAnnotationsByType(SuppressWarnings.class))); System.out.println("該Class元素上的@Anno註解為:" + Arrays.toString(clazz.getAnnotationsByType(Anno.class))); // 獲取該Class對象所對應類的全部內部類 Class<?>[] inners = clazz.getDeclaredClasses(); System.out.println("ClassTest的全部內部類如下:"); for (Class c : inners) { System.out.println(c); } // 使用Class.forName方法載入ClassTest的Inner內部類 Class inClazz = Class.forName("ClassTest$Inner"); // 通過getDeclaringClass()訪問該類所在的外部類 System.out.println("inClazz對應類的外部類為:" + inClazz.getDeclaringClass()); System.out.println("ClassTest的包為:" + clazz.getPackage()); System.out.println("ClassTest的父類為:" + clazz.getSuperclass()); } }
18.3.3 java8 新增的方法參數反射
關於反射方法的參數的名字,這個比較麻煩,如下的例子
class Test { public void replace(String str, List<String> list){} } public class MethodParameterTest { public static void main(String[] args)throws Exception { // 獲取Tesy的類 Class<Test> clazz = Test.class; // 獲取Test類的帶兩個參數的replace()方法 Method replace = clazz.getMethod("replace" , String.class, List.class); // 獲取指定方法的參數個數 System.out.println("replace方法參數個數:" + replace.getParameterCount()); // 獲取replace的所有參數信息 Parameter[] parameters = replace.getParameters(); System.out.println((new File("")).getAbsolutePath()); int index = 1; // 遍歷所有參數 for (Parameter p : parameters) { if (p.isNamePresent()) { System.out.println("---第" + index++ + "個參數信息---"); System.out.println("參數名:" + p.getName()); System.out.println("形參類型:" + p.getType()); System.out.println("泛型類型:" + p.getParameterizedType()); } } } }
所以,上面這個例子在用eclipse編譯時,總是找不到參數名.沒找到設置的地方,只能用javac編譯
編譯命令如下: javac -parameters -encoding GBK -d . MethodParameterTest.java 因為瘋狂java講義里的源碼都是GBK編碼,而一般的電腦預設utf-8,所以需要指定編碼方式 運行: java MethodParameterTest ----------------------結果-------------------------- replace方法參數個數:2 /Users/liuxin/work/workspace2/learnJava/src ---第1個參數信息--- 參數名:str 形參類型:class java.lang.String 泛型類型:class java.lang.String ---第2個參數信息--- 參數名:list 形參類型:interface java.util.List 泛型類型:java.util.List<java.lang.String>
18.4 使用反射生成並操作對象
例子:這個個別地方沒理解,但是這是一個反射的典型應用,需要好好理解
import java.util.*; import java.io.*; import java.lang.reflect.*; /** * Description: * <br/>網站: <a href="http://www.crazyit.org">瘋狂Java聯盟</a> * <br/>Copyright (C), 2001-2016, Yeeku.H.Lee * <br/>This program is protected by copyright laws. * <br/>Program Name: * <br/>Date: * @author Yeeku.H.Lee [email protected] * @version 1.0 */ public class ExtendedObjectPoolFactory { // 定義一個對象池,前面是對象名,後面是實際對象 private Map<String ,Object> objectPool = new HashMap<>(); private Properties config = new Properties(); // 從指定屬性文件中初始化Properties對象 public void init(String fileName) { // File tmpFi = new File(fileName); // System.out.println(tmpFi.getAbsolutePath()); try(FileInputStream fis = new FileInputStream(fileName)) { config.load(fis); } catch (IOException ex) { ex.printStackTrace(); System.out.println("讀取" + fileName + "異常"); } } // 定義一個創建對象的方法, // 該方法只要傳入一個字元串類名,程式可以根據該類名生成Java對象 private Object createObject(String clazzName) throws InstantiationException , IllegalAccessException , ClassNotFoundException { // 根據字元串來獲取對應的Class對象 Class<?> clazz =Class.forName(clazzName); // 使用clazz對應類的預設構造器創建實例 return clazz.newInstance(); } // 該方法根據指定文件來初始化對象池, // 它會根據配置文件來創建對象 public void initPool()throws InstantiationException ,IllegalAccessException , ClassNotFoundException { for (String name : config.stringPropertyNames()) { // 每取出一對key-value對,如果key中不包含百分號(%) // 這就標明是根據value來創建一個對象 // 調用createObject創建對象,並將對象添加到對象池中 if (!name.contains("%")) { objectPool.put(name , createObject(config.getProperty(name))); } } } // 該方法將會根據屬性文件來調用指定對象的setter方法 public void initProperty()throws InvocationTargetException ,IllegalAccessException,NoSuchMethodException { for (String name : config.stringPropertyNames()) { // 每取出一對key-value對,如果key中包含百分號(%) // 即可認為該key用於控制調用對象的setter方法設置值, // %前半為對象名字,後半控制setter方法名 if (name.contains("%")) { // 將配置文件中key按%分割 String[] objAndProp = name.split("%"); // 取出調用setter方法的參數值 Object target = getObject(objAndProp[0]); // 獲取setter方法名:set + "首字母大寫" + 剩下部分 String mtdName = "set" + objAndProp[1].substring(0 , 1).toUpperCase() + objAndProp[1].substring(1); // 通過target的getClass()獲取它實現類所對應的Class對象 Class<?> targetClass = target.getClass(); // 獲取希望調用的setter方法 Method mtd = targetClass.getMethod(mtdName , String.class); // 通過Method的invoke方法執行setter方法, // 將config.getProperty(name)的值作為調用setter的方法的參數 mtd.invoke(target , config.getProperty(name)); } } } public Object getObject(String name) { // 從objectPool中取出指定name對應的對象。 return objectPool.get(name); } public static void main(String[] args) throws Exception { ExtendedObjectPoolFactory epf = new ExtendedObjectPoolFactory(); epf.init("src/extObj.txt"); epf.initPool(); epf.initProperty(); System.out.println(epf.getObject("a")); } }
例子二:使用指定的構造器來構造對象.
public class CreateJFrame { public static void main(String[] args) throws Exception { // 獲取JFrame對應的Class對象 Class<?> jframeClazz = Class.forName("javax.swing.JFrame"); // 獲取JFrame中帶一個字元串參數的構造器 Constructor ctor = jframeClazz .getConstructor(String.class); // 調用Constructor的newInstance方法創建對象 Object obj = ctor.newInstance("測試視窗"); // 輸出JFrame對象 System.out.println(obj); } }
18.4.3 訪問成員變數值
例子:
class Person { private String name; private int age; public String toString() { return "Person[name:" + name + " , age:" + age + " ]"; } } public class FieldTest { public static void main(String[] args) throws Exception { // 創建一個Person對象 Person p = new Person(); // 獲取Person類對應的Class對象 Class<Person> personClazz = Person.class; // 獲取Person的名為name的成員變數 // 使用getDeclaredField()方法表明可獲取各種訪問控制符的成員變數 Field nameField = personClazz.getDeclaredField("name"); // 設置通過反射訪問該成員變數時取消訪問許可權檢查 nameField.setAccessible(true); // 調用set()方法為p對象的name成員變數設置值 nameField.set(p , "Yeeku.H.Lee"); // 獲取Person類名為age的成員變數 Field ageField = personClazz.getDeclaredField("age"); // 設置通過反射訪問該成員變數時取消訪問許可權檢查 ageField.setAccessible(true); // 調用setInt()方法為p對象的age成員變數設置值 ageField.setInt(p , 30); System.out.println(p); } }
用java.lang.reflect包下的Array類操作數組
例子:
public class ArrayTest1 { public static void main(String args[]) { try { // 創建一個元素類型為String ,長度為10的數組 Object arr = Array.newInstance(String.class, 10); // 依次為arr數組中index為5、6的元素賦值 Array.set(arr, 5, "瘋狂Java講義"); Array.set(arr, 6, "輕量級Java EE企業應用實戰"); // 依次取出arr數組中index為5、6的元素的值 Object book1 = Array.get(arr , 5); Object book2 = Array.get(arr , 6); // 輸出arr數組中index為5、6的元素 System.out.println(book1); System.out.println(book2); } catch (Throwable e) { System.err.println(e); } } }
操作多維數組的例子:
public class ArrayTest2 { public static void main(String args[]) { /* 創建一個三維數組。 根據前面介紹數組時講的:三維數組也是一維數組, 是數組元素是二維數組的一維數組, 因此可以認為arr是長度為3的一維數組 */ Object arr = Array.newInstance(String.class, 3, 4, 10); // 獲取arr數組中index為2的元素,該元素應該是二維數組 Object arrObj = Array.get(arr, 2); // 使用Array為二維數組的數組元素賦值。二維數組的數組元素是一維數組, // 所以傳入Array的set()方法的第三個參數是一維數組。 Array.set(arrObj , 2 , new String[] { "瘋狂Java講義", "輕量級Java EE企業應用實戰" }); // 獲取arrObj數組中index為3的元素,該元素應該是一維數組。 Object anArr = Array.get(arrObj, 3); Array.set(anArr , 8 , "瘋狂Android講義"); // 將arr強制類型轉換為三維數組 String[][][] cast = (String[][][])arr; // 獲取cast三維數組中指定元素的值 System.out.println(cast[2][3][8]); System.out.println(cast[2][2][0]); System.out.println(cast[2][2][1]); } }
18.5 使用反射生成JDK動態代理
在java的java.lang.reflect包下提供了一個Proxy類和一個InvocationHandler介面,通過使用這個類和介面,可生成JDK動態代理或動態代理對象.
動態代理的例子:
interface Person { void walk(); void sayHello(String name); }