當程序主動使用某個類時,若是該類還未被加載到內存中,則系統會經過加載、鏈接、初始化三個步驟來對該類初始化。java
類加載指的是將類的class文件讀入內存,併爲之建立一個java.lang.Class對象。類的加載由類加載器完成,加載器由JVM提供。數組
當類被加載以後,系統會生成一個對應的Class對象,接着就會進入鏈接階段,鏈接階段負責把類的二進制數據合併到JRE中。類的鏈接步驟:學習
JVM負責對類進行初始化,主要就是對類變量進行初始化。在類中對類變量指定初始化值有兩種方式:測試
1,聲明類變量時指定初始化值;code
2,使用靜態初始化塊爲類變量指定初始值。orm
JVM初始化一個類包含如下幾個步驟:對象
1,假如這個類尚未被加載和鏈接,則程序先加載並鏈接該類。繼承
2,假如該類的直接父類尚未被初始化,則先初始化其直接父類。內存
3,假如類中有初始化語句,則系統依次執行這些初始化語句。文檔
java程序中許多對象在運行時都會出現兩種類型:A a=new B();這種代碼將會生成一個a變量,其中A爲編譯時類型,B爲運行時類型。必須時B繼承A
爲了解決這些問題,程序須要在運行時發現對象和和類的真實信息,有以下兩種作法:
1,假設在編譯時和運行時發現對象和類的信息,咱們可使用instanceof運算符賴判斷,再利用強制類型轉化成其運行時類型。
2,沒法知道該對象和類的信息,這裏就要使用反射了。
每一個類被加載都會生成一個Class對象,經過該Class對象就能夠訪問到JVM中的這個類。能夠經過一下三種方式得到程序中的Class對象。
1,使用Class類的forName(String clazzName)靜態方法,clazzName是某個類的全限定名(完整包名)。
2,調用某個類的class屬性來獲取該類對應的Class對象,好比A.class將會返回A類的Class對象。
3,調用某個對象的getClass方法,該方法屬於Object對象,因此全部的類均可以調用,返回當前對象所屬類對應的Class對象。
public class InstanceofTest { public static void main(String[] args) throws Exception { InstanceofA p = new InstanceofB(); System.out.println(p instanceof InstanceofA); if (p instanceof InstanceofB) { System.out.println("1:" + Class.forName("www.com.lining.test1.InstanceofA")); System.out.println("2:" + "InstanceofA.class"); System.out.println("3:" + p.getClass()); } else { System.out.println(p.getClass()); } } }
輸出
1:class www.com.lining.test1.InstanceofA 2:InstanceofA.class 3:class www.com.lining.test1.InstanceofB
運行時和編譯時類型不同可是有繼承關係,使用getClass方法獲取此時的Class對象都是運行時對象
獲取class對象以後就能夠獲取class對象中的信息了,好比獲取此類的全部public構造器、全部的public的方法等,能夠參照幫助文檔中Class類的方法來學習。
Class對象能夠得到類的方法,構造器,成員變量(實例變量),這三個類都位於java.lang.reflect包下。
1,使用Class對象的newInstance()方法來建立該Class對象對應類的實例,這種方式要求該Class對象的對應類有默認構造器,使用newInstance()來建立實例其實就是利用了默認的構造器。
2,先使用Class對象獲取指定的Constructor對象,再調用Constructor對象的newInstance()方法來建立該Class對象對應的類的實例,這種方式能夠選擇使用指定的構造器來建立實例。(java9以後就廢棄了直接使用newInstance()來建立實例,而是使用getDeclaredConstructor().newInstance()建立)
package www.com.lining.test1; import java.io.FileInputStream; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Properties; import javax.swing.JFrame; /* * 經過反射來建立未知的類的對象,利用流來讀取配置文件而後解析爲k-v模式,而後獲取該value,利用value建立對象 */ public class ObjectPoolFactory { // 定義一個對象池,前面是對象名,後面是實際對象 private Map<String, Object> pool = new HashMap<>(); // 定義一個建立對象的方法 // 該方法只要傳入一個字符串類名(權限定名)程序玖能夠根據該類名生成java對象 @SuppressWarnings("unused") private Object createObject(String clazzName) throws Exception { Class<?> clazz = Class.forName(clazzName);// 獲取class對象 return clazz.getDeclaredConstructor().newInstance();// 建立該class對應的對象的實例 } /* @SuppressWarnings("unused") private Object createObject(String clazzName) throws Exception { //使用帶參數的構造器建立對象 Class<?> clazz = Class.forName(clazzName);// 獲取class對象 Constructor ctor = clazz.getConstructor(String.class); return ctor.newInstance("20190607");// 建立該class對應的對象的實例 } */ //該方法根據指定文件來初始化對象池,根據配置文件來建立對象 public void initPool(String filename) { try (FileInputStream input = new FileInputStream(filename);) { Properties props = new Properties(); props.load(input);// 從流中讀取屬性列表,解析爲k-v的屬性列表 for (String name : props.stringPropertyNames()) {// 返回此屬性列表的鍵集k-v pool.put(name, createObject(props.getProperty(name))); } } catch (Exception e) { e.printStackTrace(); } } public Object getObject(String name) { return pool.get(name);// 獲取map集合中name對應的值 } public static void main(String[] args) { ObjectPoolFactory fact = new ObjectPoolFactory(); fact.initPool("index");//這裏的路徑是放在工程下的文件,直接在工程下建立。 Date date = new Date(); System.out.println(date); System.out.println(fact.getObject("a")); JFrame jf = new JFrame(); System.out.println(jf); System.out.println(fact.getObject("b")); } }
輸出
Fri Sep 27 15:51:34 CST 2019 Fri Sep 27 15:51:33 CST 2019 javax.swing.JFrame[frame0,0,0,0x0,invalid,hidden,layout=java.awt.BorderLayout,title=,resizable,normal,defaultCloseOperation=HIDE_ON_CLOSE,rootPane=javax.swing.JRootPane[,0,0,0x0,invalid,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true] javax.swing.JFrame[frame1,0,0,0x0,invalid,hidden,layout=java.awt.BorderLayout,title=,resizable,normal,defaultCloseOperation=HIDE_ON_CLOSE,rootPane=javax.swing.JRootPane[,0,0,0x0,invalid,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true]
index文件:
a=java.util.Date b=javax.swing.JFrame
注意:這裏用帶參數的構造器建立實例時,.newInstance(xx)x表明一個或多個參數,這裏的參數個數類型順序要和建立的類的構造方法參數相對應,否則會報異常java.lang.reflect.InvocationTargetException
當得到某個類對應的Class對象後,就能夠經過getMethods()方法返回數組或者getMethod()方法返回對象來獲取所有或者指定的方法。java.lang.reflect.Method裏包含一個invoke方法。
package www.com.lining.test1; import java.io.FileInputStream; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.Properties; public class MethodPoolFactory { private Map<String, Object> pool = new HashMap<>();// 對象池,放置建立後的對象k-v private Properties pro = new Properties();// 建立一個持久的屬性集對象 // 從指定文件中初始化Properties對象 public void init(String filename) { try (FileInputStream input = new FileInputStream(filename))// 建立讀取配置文件的流 { pro.load(input);// 讀取流裏的數據解析爲k-v } catch (Exception e) { e.printStackTrace(); } } //定義一個建立對象的方法 public Object createObject(String name) throws Exception { Class<?> clazz = Class.forName(name); return clazz.getDeclaredConstructor().newInstance(); } // 根據讀取的文件來初始化對象池,建立對象 public void initPool() throws Exception { for (String name : pro.stringPropertyNames()) {// 返回此屬性列表的鍵值集 if (!name.contains("%")) { pool.put(name, createObject(pro.getProperty(name))); } } } // 根據屬性文件來調用指定對象的xx方法,xx能夠是建立對象中的任何一個實例方法,好比setter public void initProperty() throws Exception { for (String name : pro.stringPropertyNames()) { if (name.contains("%")) { String[] objAndProp = name.split("%");// 將配置文件按照「%」來分割 Object target = getObject(objAndProp[0]);// 獲取建立的對象 String mtdName = "set" + objAndProp[1].substring(0, 1).toUpperCase() + objAndProp[1].substring(1); Class<?> targetClazz = target.getClass();// 獲取該對象的Class實例 Method mtd = targetClazz.getMethod(mtdName, String.class);// 經過該實例獲取該方法 mtd.setAccessible(true); mtd.invoke(target, pro.getProperty(name));// 調用該方法 } } } public Object getObject(String name) { return pool.get(name); } public static void main(String[] args) throws Exception { MethodPoolFactory method = new MethodPoolFactory(); method.init("index1"); method.initPool(); method.initProperty(); System.out.println(method.getObject("b")); } }
輸出
123 %12345% 8 9ddddddd kjfjf www.com.lining.test1.Test@1f32e575
index1文件
b=www.com.lining.test1.Test b%title=123,%12345%,,8,9ddddddd,kjfjf
反射調用的類(能夠本身建立也能夠運用jdk中的)
package www.com.lining.test1; public class Test { public void setTitle(String sb) { for (String s : sb.split(",")) { System.out.println(s); } } }
注意:
1,調用的方法有可能在類中有重載,因此要注意參數的個數類型。
2,setAccessible(boolean flg)方法不屬於Method,而是屬於AccessibleObject類,因此根據繼承關係能夠知道File,Method,Constructor均可以調用這個方法。該方法能夠實現對private等權限的調用。
1,經過Class對象的getFields()或getField()方法能夠獲取該類所包括的所有成員變量或指定成員變量。File提供了以下兩組方法來讀取或設置成員變量值。
package www.com.lining.test1; public class FilePoolFactory { private String name; private int age; public String toString() { return "FilePoolFactory[name:" + name + ",age:" + age + "]"; } }
測試類:
package www.com.lining.test1; import java.lang.reflect.Field; public class FilePoolTest { public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { FilePoolFactory f = new FilePoolFactory();// 建立一個FilePoolFactory的對象 Class<FilePoolFactory> clazz = FilePoolFactory.class;// 獲取對應的class對象 Field nameFiled = clazz.getDeclaredField("name");// 經過class對象獲取成員變量 nameFiled.setAccessible(true);// 設置獲取的權限 nameFiled.set(f, "jessica");// 設置對象f的成員變量name的值。 Field ageFiled = clazz.getDeclaredField("age"); ageFiled.setAccessible(true); ageFiled.set(f, 19); System.out.println(f.toString()); } }
輸出:
FilePoolFactory[name:jessica,age:19]
注意:這裏必定要設置訪問權限setAccessible(boolean flg),若是權限是字段是private權限,那麼就須要使用該方法了,否則會報 java.lang.IllegalAccessException。