類加載與反射java
Java程序與JVMmysql
•無論Java程序有多麼複雜、該程序啓動了多少個線程,它們都處於該Java虛擬機進程裏。正如前面sql 介紹的,同一個JVM的全部線程、全部變量都處於同一個進程裏,它們都使用該JVM進程的內存區。數據庫 當系統出現如下幾種狀況時,JVM進程將被終止:編程
–程序運行到最後正常結束。數組
–程序運行到使用System.exit()或Runtime.getRuntime().exit()代碼結束程序。緩存
–程序執行過程當中遇到未捕獲的異常或錯誤而結束。網絡
–程序所在平臺強制結束了JVM進程。 ide 類加載測試
•當程序主動使用某個類時,若是該類還未被加載到內存中,系統會經過加載、鏈接、初始化三個步驟 來對該類進行初始化,若是沒有意外,JVM將會連續完成這三個步驟,因此有時也把這三個步驟統稱 爲類加載或類初始化。
•類加載指的是將類的class文件讀入內存,併爲之建立一個java.lang.Class對象,也就是說當程序 使用任何類時,系統都會爲之創建一個java.lang.Class對象。
類數據的來源
•經過使用不一樣的類加載器,能夠從不一樣來源加載類的二進制數據,一般有以下幾種來源: –從本地文件系統來加載class文件,這是前面絕大部分示例程序的類加載方式。 –從JAR包中加載class文件,這種方式也是很常見的,前面介紹JDBC編程時用到的數據庫驅動類就是放在JAR文件中,JVM能夠從JAR文件中直接加載該class文件。 –經過網絡在加載class文件。 –把一個Java源文件動態編譯、並執行加載。
類的鏈接
•當類被加載以後,系統爲之生成一個對應的Class對象,接着將會進入鏈接階段,鏈接階段將會負責把類的二進制數據合併到JRE中。類鏈接又可分爲以下三個階段: –驗證:驗證階段用於檢驗被加載的類是否有正確的內部結構,並和其餘類協調一致。 –準備:類準備階段則負責爲類的靜態屬性分配內存,並設置默認初始值。 –解析:將類的二進制數據中的符號引用替換成直接引用。
類的初始化
•在類的初始化階段,虛擬機負責對類進行初始化,主要就是對靜態屬性進行初始化。在Java類中對靜態屬性指定初始值有兩種方式: –(1)聲明靜態屬性時指定初始值; –(2)使用靜態初始化塊爲靜態屬性指定初始值。
JVM初始化類的步驟
•(1)假如這個類尚未被加載和鏈接,程序先加載並鏈接該類。 •(2)假如該類的直接父類尚未被初始化,則先初始化其直接父類。 •(3)假如類中有初始化語句,則系統依次執行這些初始化語句。 類的初始化時機
•建立類的實例。爲某個類建立實例的方式包括使用new操做符來建立實例,經過反射來建立實例,經過反序列化的方式來建立實例。 •調用某個類的靜態方法。 •訪問某個類或接口的靜態屬性,或爲該靜態屬性賦值。 •使用反射方式來強制建立某個類或接口對應的java.lang.Class對象。例如代碼:Class.forName("Person")。 •初始化某個類的子類,當初始化某個類的子類時,該子類的全部父類都會被初始化。 •直接使用java.exe命令來運行某個主類,當運行某個主類時,程序會先初始化該主類。 •final型的靜態屬性,若是該屬性能夠在編譯時就獲得屬性值,則可認爲該屬性可被當成編譯時常量。當程序使用編譯時常量時,系統會認爲這是對該類的被動使用,因此不會致使該類的初始化。 類加載器
•類加載器負責將.class文件(可能在磁盤上,也可能在網絡上)加載到內存中,併爲之生成對應的java.lang.Class對象。 •當JVM啓動時,會造成由三個類加載器組成的初始類加載器層次結構: –Bootstrap ClassLoader:根類加載器。 –Extension ClassLoader:擴展類加載器。 –System ClassLoader:系統類加載器。
類加載機制
•JVM的類加載機制主要有以下三種機制: –全盤負責:所謂全盤負責,就是說當一個類加載器負責加載某個Class的時候,該Class所依賴的 和引用的其餘Class也將由該類加載器負責載入,除非顯式使用另一個類加載器來載入。
–父類委託:所謂父類委託則是先讓parent(父)類加載器試圖加載該Class,只有在父類加載器 沒法加載該類時才嘗試從本身的類路徑中加載該類。
–緩存機制:緩存機制將會保證全部被加載過的Class都會被緩存,當程序中須要使用某個Class 時,類加載器先從緩存中搜尋該Class,只有當緩存中不存在該Class對象時,系統纔會讀取該類 對應的二進制數據,並將其轉換成Class對象,並存入cache。這就是爲何咱們修改了Class 後,程序必須從新啓動JVM,程序所做的修改纔會生效的緣由。
實現自定義類加載器
•ClassLoader類有以下三個關鍵方法: –loadClass(String name, boolean resolve):該方法爲ClassLoader的入口點,根據指定的二進制名稱來加載類,系統就是調用ClassLoader的該方法來獲取指定類對應的Class對象。 –findClass(String name):根據二進制名稱來查找類。 •若是須要實現自定義的ClassLoader,能夠經過重寫以上兩個方法來實現,固然咱們推薦重寫findClass()方法,而不是重寫loadClass()方法。
自定義的類加載器的常見功能
•執行代碼前自動驗證數字簽名。 •根據用戶提供的密碼解密代碼,從而能夠實現代碼混淆器來避免反編譯class文件。 •根據用戶需求來動態地加載類。 •根據應用需求把其餘數據以字節碼的形式加載到應用中。 URLClassLoader
•Java爲ClassLoader提供了一個URLClassLoader實現類,該類也是系統類加載器和擴展類加載器 的父類(此處是父類,而不是父類加載器,這裏是類與類之間的繼承關係),URLClassLoader功能 比較強大,它既能夠從本地文件系統獲取二進制文件來加載類,也能夠從遠程主機獲取二進制文件來加 載類。
•實際上應用程序中能夠直接使用URLClassLoader來加載類,URLClassLoader類提供了以下兩個 構造器:
–URLClassLoader(URL[] urls):使用默認的父類加載器建立一個ClassLoader對象,該對象 將從urls所指定的系列路徑來查詢、並加載類。
–URLClassLoader(URL[] urls, ClassLoader parent):使用指定的父類加載器建立一個ClassLoader對象,其餘功能前一個構造器相同。
經過反射獲取Class對象
•Java程序中得到Class對象一般有以下三種方式: –使用Class類的forName()靜態方法。該方法須要傳入字符串參數,該字符串參數的值是某個類的全限定類名(必須添加完整包名)。 –調用某個類的class屬性來獲取該類對應的Class對象。例如Person.class將會返回Person類對應的Class對象。 –調用某個對象的getClass()方法,該方法是java.lang.Object類中的一個方法,因此全部Java對象均可以調用該方法,該方法將會返回該對象所屬類對應的Class對象。
從Class中獲取信息
•獲取構造器 •訪問Class對應的類所包含的方法 •訪問Class對應的類所包含的屬性(Field) •訪問Class對應的類上所包含的註釋。 •訪問該Class對象對應類包含的內部類。 •訪問該Class對象對應類所在的外部類。 •訪問該Class對象所對應類所繼承的父類、所實現的接口等。 Java 8新增的方法參數反射
•Java 8在java.lang.reflect包下新增了一個Executable抽象基類,該對象表明可執行的類成員,該類派生了Constructor、Method兩個子類。 •Executable基類提供了大量方法來獲取修飾該方法或構造器的註解信息;還提供了isVarArgs()方法用於判斷該方法或構造器是否包含數量可變的形參,以及經過getModifiers()方法來獲取該方法或構造器的修飾符。除此以外,Executable提供了以下兩個方法來獲取該方法或參數的形參個數及形參名。 –int getParameterCount():獲取該構造器或方法的形參個數。 –Parameter[] getParameters():獲取該構造器或方法的全部形參。 •上面第二個方法返回了一個Parameter[]數組,Parameter也是Java 8新增的API,每一個Parameter對象表明方法或構造器的一個參數。Parameter也提供了大量方法來獲取聲明該參數的泛型信息。
經過反射執行代碼
•經過反射調用構造器建立對象。 •經過反射調用方法 •經過反射來訪問Field值 •經過反射操做數組 動態代理
•java.lang.reflect包下提供了一個Proxy類和一個InvocationHandler接口,經過使用這個類和 接口能夠生成JDK動態代理類或動態代理對象。
•Proxy 提供用於建立動態代理類和代理對象的靜態方法,它也是全部動態代理類的父類。若是咱們在 程序中爲一個或多個接口動態地生成實現類,就可使用Proxy來建立的動態代理類;若是須要爲一個 或多個接口動態地建立實例,也可使用Proxy來建立動態代理實例。
Proxy
•Proxy提供了以下兩個方法來建立動態代理類和動態代理實例: –static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces):創 建一個動態代理類所對應的Class對象,該代理類將實現interfaces所指定的多個接口。第一個 ClassLoader指定生成動態代理類的類加載器。
–static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h):直接建立一個動態代理對象,該代理對象的實現類實現了interfaces 指定的系列接口,執行代理對象的每一個方法時都會被替換執行InvocationHandler對象的invoke 方法。
動態代理和AOP
•動態代理在AOP(Aspect Orient Program,即面向切面編程)裏被稱爲AOP代理,AOP代理可 代替目標對象,AOP代理包含了目標對象的所有方法。但AOP代理中的方法與目標對象的方法存在差 異:AOP代理裏的方法能夠在執行目標方法以前、以後插入一些通用處理。
反射的泛型
•動態代理在AOP(Aspect Orient Program,即面向切面編程)裏被稱爲AOP代理,AOP代理可 代替目標對象,AOP代理包含了目標對象的所有方法。但AOP代理中的方法與目標對象的方法存在差 異:AOP代理裏的方法能夠在執行目標方法以前、以後插入一些通用處理。 動態代理和AOP
•從JDK1.5以後,Java的Class類增長了泛型功能,從而容許使用泛型來限制Class類,例如, String.class 的類型其實是Class<String>。 使用Class<T>泛型能夠避免強制類型轉換。
使用反射獲取泛型
•得到了Field對象後,就能夠很容易地得到該Field的數據類型,即便用以下代碼便可得到指定Field的類型: –//獲取Field對象f的類型 –Class<?> a = f.getType();
•經過這種方式只對普通類型的Field有效。但若是該Field的類型是有泛型限制的類型,如Map<String , Integer>類型,則不能準確的獲得該Field的泛型參數。
•爲了得到指定Field的泛型類型,應先使用以下方法來獲取指定Field的泛型類型: –//得到Field實例f的泛型類型 –Type gType = f.getGenericType();
•而後將Type對象強制類型轉換爲ParameterizedType對象,ParameterizedType表明被參數化的類型,也就是增長了泛型限制的類型。ParameterizedType類提供了兩個方法: –getRawType():返回被泛型限制的類型。 –getActualTypeArguments():返回泛型參數類型。
public class A { // 定義該類的類變量 public static int a = 6; } public class ATest1 { public static void main(String[] args) { // 建立A類的實例 A a = new A(); // 讓a實例的類變量a的值自加 a.a++; System.out.println(a.a); } } public class ATest2 { public static void main(String[] args) { // 建立A類的實例 A b = new A(); // 輸出b實例的類變量a的值 System.out.println(b.a); } } class Tester { static { System.out.println("Tester類的靜態初始化塊..."); } } public class ClassLoaderTest { public static void main(String[] args) throws ClassNotFoundException { ClassLoader cl = ClassLoader.getSystemClassLoader(); // 下面語句僅僅是加載Tester類 cl.loadClass("Tester"); System.out.println("系統加載Tester類"); // 下面語句纔會初始化Tester類 Class.forName("Tester"); } } class MyTest { static { System.out.println("靜態初始化塊..."); } // 使用一個字符串直接量爲static final的類變量賦值 static final String compileConstant = "瘋狂Java講義"; } public class CompileConstantTest { public static void main(String[] args) { // 訪問、輸出MyTest中的compileConstant類變量 System.out.println(MyTest.compileConstant); // ① } } public class Test { static { // 使用靜態初始化塊爲變量b指定出初始值 b = 6; System.out.println("----------"); } // 聲明變量a時指定初始值 static int a = 5; static int b = 9; // ① static int c; public static void main(String[] args) { System.out.println(Test.b); } } public class BootstrapTest { public static void main(String[] args) { // 獲取根類加載器所加載的所有URL數組 URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs(); // 遍歷、輸出根類加載器加載的所有URL for (int i = 0; i < urls.length; i++) { System.out.println(urls[i].toExternalForm()); } } } public class ClassLoaderPropTest { 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()); } } 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方法 protected Class<?> findClass(String name) throws ClassNotFoundException { Class clazz = null; // 將包路徑中的點(.)替換成斜線(/)。 String fileStub = name.replace(".", "/"); String javaFilename = fileStub + ".java"; String classFilename = fileStub + ".class"; File javaFile = new File(javaFilename); 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(name, raw, 0, raw.length); } catch (IOException ie) { ie.printStackTrace(); } } // 若是clazz爲null,代表加載失敗,則拋出異常 if (clazz == null) { throw new ClassNotFoundException(name); } return clazz; } // 定義一個主方法 public static void main(String[] args) throws Exception { // 若是運行該程序時沒有參數,即沒有目標類 if (args.length < 1) { System.out.println("缺乏目標類,請按以下格式運行Java源文件:"); System.out.println("java CompileClassLoader ClassName"); } // 第一個參數是須要運行的類 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); } } public class Hello { public static void main(String[] args) { for (String arg : args) { System.out.println("運行Hello的參數:" + arg); } } } 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 { System.out.println(getConn("jdbc:mysql://localhost:3306/mysql", "root", "32147")); } }
// 定義可重複註解 @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<ClassTest> 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()); } } class Test { public void replace(String str, List<String> list) { } } public class MethodParameterTest { public static void main(String[] args) throws Exception { // 獲取String的類 Class<Test> clazz = Test.class; // 獲取String類的帶兩個參數的replace()方法 Method replace = clazz.getMethod("replace", String.class, List.class); // 獲取指定方法的參數個數 System.out.println("replace方法參數個數:" + replace.getParameterCount()); // 獲取replace的全部參數信息 Parameter[] parameters = replace.getParameters(); 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()); } } } } public class ArrayTest1 { public static void main(String args[]) { try { // 建立一個元素類型爲String ,長度爲10的數組 Object arr = Array.newInstance(String.class, 10); // 依次爲arr數組中index爲五、6的元素賦值 Array.set(arr, 5, "瘋狂Java講義"); Array.set(arr, 6, "輕量級Java EE企業應用實戰"); // 依次取出arr數組中index爲五、6的元素的值 Object book1 = Array.get(arr, 5); Object book2 = Array.get(arr, 6); // 輸出arr數組中index爲五、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]); } } 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); } } public class ExtendedObjectPoolFactory { // 定義一個對象池,前面是對象名,後面是實際對象 private Map<String, Object> objectPool = new HashMap<>(); private Properties config = new Properties(); // 從指定屬性文件中初始化Properties對象 public void init(String fileName) { try (FileInputStream fis = new FileInputStream(fileName)) { config.load(fis); } catch (IOException ex) { 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("extObj.txt"); epf.initPool(); epf.initProperty(); System.out.println(epf.getObject("a")); } } 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); } } public class ObjectPoolFactory { // 定義一個對象池,前面是對象名,後面是實際對象 private Map<String, Object> objectPool = new HashMap<>(); // 定義一個建立對象的方法, // 該方法只要傳入一個字符串類名,程序能夠根據該類名生成Java對象 private Object createObject(String clazzName) throws InstantiationException, IllegalAccessException, ClassNotFoundException { // 根據字符串來獲取對應的Class對象 Class<?> clazz = Class.forName(clazzName); // 使用clazz對應類的默認構造器建立實例 return clazz.newInstance(); } // 該方法根據指定文件來初始化對象池, // 它會根據配置文件來建立對象 public void initPool(String fileName) throws InstantiationException, IllegalAccessException, ClassNotFoundException { try (FileInputStream fis = new FileInputStream(fileName)) { Properties props = new Properties(); props.load(fis); for (String name : props.stringPropertyNames()) { // 每取出一對key-value對,就根據value建立一個對象 // 調用createObject()建立對象,並將對象添加到對象池中 objectPool.put(name, createObject(props.getProperty(name))); } } catch (IOException ex) { System.out.println("讀取" + fileName + "異常"); } } public Object getObject(String name) { // 從objectPool中取出指定name對應的對象。 return objectPool.get(name); } public static void main(String[] args) throws Exception { ObjectPoolFactory pf = new ObjectPoolFactory(); pf.initPool("obj.txt"); System.out.println(pf.getObject("a")); // ① System.out.println(pf.getObject("b")); // ② } }
public interface Dog { // info方法聲明 void info(); // run方法聲明 void run(); } public class DogUtil { // 第一個攔截器方法 public void method1() { System.out.println("=====模擬第一個通用方法====="); } // 第二個攔截器方法 public void method2() { System.out.println("=====模擬通用方法二====="); } } public class GunDog implements Dog { // 實現info()方法,僅僅打印一個字符串 public void info() { System.out.println("我是一隻獵狗"); } // 實現run()方法,僅僅打印一個字符串 public void run() { System.out.println("我奔跑迅速"); } } public class MyInvokationHandler implements InvocationHandler { // 須要被代理的對象 private Object target; public void setTarget(Object target) { this.target = target; } // 執行動態代理對象的全部方法時,都會被替換成執行以下的invoke方法 public Object invoke(Object proxy, Method method, Object[] args) throws Exception { DogUtil du = new DogUtil(); // 執行DogUtil對象中的method1。 du.method1(); // 以target做爲主調來執行method方法 Object result = method.invoke(target, args); // 執行DogUtil對象中的method2。 du.method2(); return result; } } public class MyProxyFactory { // 爲指定target生成動態代理對象 public static Object getProxy(Object target) throws Exception { // 建立一個MyInvokationHandler對象 MyInvokationHandler handler = new MyInvokationHandler(); // 爲MyInvokationHandler設置target對象 handler.setTarget(target); // 建立、並返回一個動態代理 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler); } } public class Test { public static void main(String[] args) throws Exception { // 建立一個原始的GunDog對象,做爲target Dog target = new GunDog(); // 以指定的target來建立動態代理 Dog dog = (Dog) MyProxyFactory.getProxy(target); dog.info(); dog.run(); } } interface Person { void walk(); void sayHello(String name); } class MyInvokationHandler implements InvocationHandler { /* * 執行動態代理對象的全部方法時,都會被替換成執行以下的invoke方法 其中: proxy:表明動態代理對象 method:表明正在執行的方法 * args:表明調用目標方法時傳入的實參。 */ public Object invoke(Object proxy, Method method, Object[] args) { System.out.println("----正在執行的方法:" + method); if (args != null) { System.out.println("下面是執行該方法時傳入的實參爲:"); for (Object val : args) { System.out.println(val); } } else { System.out.println("調用該方法沒有實參!"); } return null; } } public class ProxyTest { public static void main(String[] args) throws Exception { // 建立一個InvocationHandler對象 InvocationHandler handler = new MyInvokationHandler(); // 使用指定的InvocationHandler來生成一個動態代理對象 Person p = (Person) Proxy.newProxyInstance( Person.class.getClassLoader(), new Class[] { Person.class }, handler); // 調用動態代理對象的walk()和sayHello()方法 p.walk(); p.sayHello("孫悟空"); } } public class CrazyitArray { // 對Array的newInstance方法進行包裝 @SuppressWarnings("unchecked") public static <T> T[] newInstance(Class<T> componentType, int length) { return (T[]) Array.newInstance(componentType, length); // ① } public static void main(String[] args) { // 使用CrazyitArray的newInstance()建立一維數組 String[] arr = CrazyitArray.newInstance(String.class, 10); // 使用CrazyitArray的newInstance()建立二維數組 // 在這種狀況下,只要設置數組元素的類型是int[]便可。 int[][] intArr = CrazyitArray.newInstance(int[].class, 5); arr[5] = "瘋狂Java講義"; // intArr是二維數組,初始化該數組的第二個數組元素 // 二維數組的元素必須是一維數組 intArr[1] = new int[] { 23, 12 }; System.out.println(arr[5]); System.out.println(intArr[1][1]); } } public class CrazyitObjectFactory { public static Object getInstance(String clsName) { try { // 建立指定類對應的Class對象 Class cls = Class.forName(clsName); // 返回使用該Class對象所建立的實例 return cls.newInstance(); } catch (Exception e) { e.printStackTrace(); return null; } } } public class CrazyitObjectFactory2 { public static <T> T getInstance(Class<T> cls) { try { return cls.newInstance(); } catch (Exception e) { e.printStackTrace(); return null; } } public static void main(String[] args) { // 獲取實例後無須類型轉換 Date d = CrazyitObjectFactory2.getInstance(Date.class); JFrame f = CrazyitObjectFactory2.getInstance(JFrame.class); } } public class GenericTest { private Map<String, Integer> score; public static void main(String[] args) throws Exception { Class<GenericTest> clazz = GenericTest.class; Field f = clazz.getDeclaredField("score"); // 直接使用getType()取出的類型只對普通類型的成員變量有效 Class<?> a = f.getType(); // 下面將看到僅輸出java.util.Map System.out.println("score的類型是:" + a); // 得到成員變量f的泛型類型 Type gType = f.getGenericType(); // 若是gType類型是ParameterizedType對象 if (gType instanceof ParameterizedType) { // 強制類型轉換 ParameterizedType pType = (ParameterizedType) gType; // 獲取原始類型 Type rType = pType.getRawType(); System.out.println("原始類型是:" + rType); // 取得泛型類型的泛型參數 Type[] tArgs = pType.getActualTypeArguments(); System.out.println("泛型信息是:"); for (int i = 0; i < tArgs.length; i++) { System.out.println("第" + i + "個泛型類型是:" + tArgs[i]); } } else { System.out.println("獲取泛型類型出錯!"); } } } |