反射技術java
其實就是動態加載一個指定的類,並獲取該類中的全部的內容。c++
並且將字節碼文件封裝成對象,並將字節碼文件中的內容都封裝成對象,它容許程序在運行時(注意不是編譯的時候)來進行自我檢查而且對內部的成員進行操做。sql
簡單說:反射技術能夠對一個類進行解剖。數組
反射的好處:大大的加強了程序的擴展性。app
反射的基本步驟:框架
得到Class對象,就是獲取到指定的名稱的字節碼文件對象。函數
實例化對象,得到類的屬性、方法或構造函數。this
訪問屬性、調用方法、調用構造函數建立對象。編碼
類類型 class Class spa
用於描述程序中的各個類屬於同一類事物的Java類,它封裝了類的不少信息。
查看JDK中的源碼:
public final class Class<T> implements java.io.Serializable, java.lang.reflect.GenericDeclaration, java.lang.reflect.Type, java.lang.reflect.AnnotatedElement { private static final int ANNOTATION= 0x00002000; private static final int ENUM = 0x00004000; private static final int SYNTHETIC = 0x00001000; private static native void registerNatives(); static { registerNatives(); } /* * Constructor. Only the Java Virtual Machine creates Class * objects. */ private Class() {}
發現:Class類有構造器,而且它的構造方法是private的(多是爲了禁止開發者去本身建立Class類的實例)。
看到註釋咱們知道,這個類是JVM來建立的。若是咱們拿到一個類的類型信息,就能夠利用反射獲取其各類成員以及方法了。
那麼咱們怎麼拿到一個類的信息呢?【獲取Class對象能夠有3種方式】
若是沒有對象實例的時候,主要有兩種辦法能夠獲取類類型:
Class cls1 = Show.class;//每個數據類型(基本數據類型和引用數據類型)都有一個靜態的屬性class。弊端:必需要先明確該類。 Class cls2 = Class.forName("Show");//【推薦這種方法】這種方式的擴展性最強,只要將類名的字符串傳入便可。若是類是在某個包中,要帶上包名。
public static void main(String[] args) throws Exception { Class<Show> cls1 = Show.class; Object obj1 = cls1.newInstance(); System.out.println(obj1); Class<?> cls2 = Class.forName("Show"); Object obj2 = cls2.newInstance(); System.out.println(obj2); }
這樣就建立了一個對象,缺點是:
第一,咱們只能利用默認構造函數,由於Class的newInstance是不接受參數的;
第二,若是類的構造函數是private的,好比Class,咱們仍舊不能實例化其對象。
若是有對象實例的話,除了上面的兩種方法來獲取類的信息外,還有第三種方法:對象.getClass()。弊端:必需要建立該類對象,才能夠調用getClass方法。
反射就是把Java類中的各類成分映射成相應的Java類:
例如,一個Java類用一個Class類的對象來表示,一個類中的組成部分:成員變量,方法,構造方法,包等等信息也用一個個的Java類來表示【就像汽車是一個類,汽車中的發動機,變速箱也是一個個的類】。表示Java類的Class類中提供了一系列的方法來獲取其中的變量(Field),方法(Method),構造方法(Contructor),修飾符(Modifiers),包(Package)等信息。
反射的用法
1.須要得到java類的各個組成部分,首先須要得到類的Class對象,得到Class對象的三種方式:
2.反射類的成員方法:
3.反射類的構造函數:
發現帶上Declared的都是得到全部的構造方法,包括私有,這下咱們就能夠調用本來不容許調用的私有構造器了^_^
eg: Class cls1 = Show.class; // 獲取全部的構造方法集合 Constructor[] con1 = cls1.getDeclaredConstructors(); con1[1].setAccessible(true);// 設置可訪問的權限 Object obj1 = con1[1].newInstance(new Object[] { "adanac" }); System.out.println(obj1); // 指定參數列表獲取特定的方法 Constructor con = cls1.getDeclaredConstructor(new Class[] { String.class }); con.setAccessible(true); Object obj2 = con.newInstance(new Object[] { "lfz" }); System.out.println(obj2);
4.獲取類的成員變量:
Class cls1 = Show.class; // 獲取全部的構造方法集合 Constructor[] con1 = cls1.getDeclaredConstructors(); con1[1].setAccessible(true);// 設置可訪問的權限 Object obj1 = con1[1].newInstance(new Object[] { "adanac" }); Field nameField = cls1.getDeclaredField("name"); nameField.setAccessible(true); System.out.println(nameField.get(obj1));// 打印結果:adanac
如今私有變量也能夠訪問到了哈~~
獲取了字節碼文件對象後,最終都須要建立指定類的對象,建立對象的兩種方式(其實就是對象在進行實例化時的初始化方式):
注意:第二種方式必需要先明確具體的構造函數的參數類型,不便於擴展。因此通常狀況下,被反射的類,內部一般都會提供一個公有的空參數的構造函數。
Object obj = clazz.newInstance();//該實例化對象的方法調用就是指定類中的空參數構造函數,給建立對象進行初始化。
當指定類中沒有空參數構造函數時,該如何建立該類對象呢?
Class<?> clazz = Class.forName("heimablog.Person"); // 既然類中沒有空參數的構造函數,那麼只有獲取指定參數的構造函數,用該函數來進行實例化。 // 獲取一個帶參數的構造器。 Constructor<?> constructor = clazz.getConstructor(String.class, int.class); // 想要對對象進行初始化,使用構造器的方法newInstance(); Object obj = constructor.newInstance("zhagnsan", 30); // 獲取全部構造器。 Constructor[] constructors = clazz.getConstructors();// 只包含公共的 constructors = clazz.getDeclaredConstructors();// 包含私有的 for (Constructor<?> con : constructors) { System.out.println(con); }
【案例】
經過class取得一個類的所有框架:
package heimablog; import java.lang.reflect.Field; import java.lang.reflect.Modifier; class hello { public static void main(String[] args) { Class<?> demo = null; try { demo = Class.forName("heimablog.Person"); } catch (Exception e) { e.printStackTrace(); } System.out.println("===============本類屬性========================"); // 取得本類的所有屬性 Field[] field = demo.getDeclaredFields(); for (int i = 0; i < field.length; i++) { // 權限修飾符 int mo = field[i].getModifiers();//返回此類或接口以整數編碼的 Java 語言修飾符。 String priv = Modifier.toString(mo); // 屬性類型 Class<?> type = field[i].getType(); System.out.println(priv + " " + type.getName() + " " + field[i].getName() + ";"); } System.out.println("===============實現的接口或者父類的屬性========================"); // 取得實現的接口或者父類的屬性 Field[] filed1 = demo.getFields(); for (int j = 0; j < filed1.length; j++) { // 權限修飾符 int mo = filed1[j].getModifiers(); String priv = Modifier.toString(mo); // 屬性類型 Class<?> type = filed1[j].getType(); System.out.println(priv + " " + type.getName() + " " + filed1[j].getName() + ";"); } } }
經過反射操做屬性:
package heimablog; import java.lang.reflect.Field; class hello { public static void main(String[] args) throws Exception { Class<?> cls1 = Class.forName("heimablog.Person"); Object object = cls1.newInstance(); Field field = cls1.getDeclaredField("name"); field.setAccessible(true); field.set(object, "男"); System.out.println(field.get(object));//打印結果:男 } }
經過反射取得並修改數組的信息:
package heimablog; import java.lang.reflect.Array; class hello { public static void main(String[] args) throws Exception { int[] temp={1,2,3,4,5}; Class<?>demo=temp.getClass().getComponentType(); System.out.println("數組類型: "+demo.getName()); System.out.println("數組長度 "+Array.getLength(temp)); System.out.println("數組的第一個元素: "+Array.get(temp, 0)); Array.set(temp, 0, 100); System.out.println("修改以後數組第一個元素爲: "+Array.get(temp, 0)); } } /** * 數組類型: int *數組長度 5 *數組的第一個元素: 1 *修改以後數組第一個元素爲: 100 */
經過反射修改數組大小:
package heimablog; import java.lang.reflect.Array; class hello{ public static void main(String[] args) { int[] temp={1,2,3,4,5,6,7,8,9}; int[] newTemp=(int[])arrayInc(temp,12); print(newTemp); System.out.println("^^^^^"); String[] atr={"a","b","c"}; String[] str1=(String[])arrayInc(atr,5); print(str1); } /** * 修改數組大小 * */ public static Object arrayInc(Object obj,int len){ Class<?>arr=obj.getClass().getComponentType(); Object newArr=Array.newInstance(arr, len); int co=Array.getLength(obj); System.arraycopy(obj, 0, newArr, 0, co); return newArr; } /** * 打印 * */ public static void print(Object obj){ Class<?>c=obj.getClass(); if(!c.isArray()){ return; } System.out.println("數組長度爲: "+Array.getLength(obj)); for (int i = 0; i < Array.getLength(obj); i++) { System.out.print(Array.get(obj, i)+" "); } } } /** *數組長度爲: 10 *1 2 3 4 5 6 7 8 9 0 ^^^^^ *數組長度爲: 5 *a b c null null */
得到類加載器:
package heimablog; class test{ } class hello{ public static void main(String[] args) { test t=new test(); System.out.println("類加載器 "+t.getClass().getClassLoader().getClass().getName()); } } //類加載器 sun.misc.Launcher$AppClassLoader
反射小例子:
package heimablog; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.sql.Timestamp; import java.util.Calendar; public class ClassTest { public static void main(String[] args) { Calendar birthday = Calendar.getInstance(); birthday.set(1966, 9, 11, 0, 0, 0); Student s1 = new Student("007", "Jack Brawer", true, new Timestamp( birthday.getTimeInMillis()), 1.80); ClassInfo classInfo = new ClassInfo(); System.out.println(classInfo.getClassInfo(s1)); } } // 該類能夠獲取任何類的元數據信息 class ClassInfo { public String getClassInfo(Object obj) { StringBuffer result = new StringBuffer(); // 獲得參數類變量的Class引用變量 Class cls = obj.getClass(); // 獲得參數類變量的屬性信息 Field[] fields = cls.getDeclaredFields(); // 獲得參數類變量的完整類名(含有包的名稱) String fullName = cls.getName(); // 獲得去除包名稱的類名 String className = fullName.substring(fullName.lastIndexOf('.') + 1); // 若是有包的定義,能夠獲得包的名稱 int packagePosition = fullName.lastIndexOf('.'); String packageName = null; if (packagePosition < 0) packageName = ""; else packageName = fullName.substring(0, fullName.lastIndexOf('.')); // 輸出包名和類名 result.append("包的名稱爲:" + packageName + "\r\n"); result.append("類的名稱爲:" + className + "\r\n"); // 輸出類中全部的屬性信息 for (Field field : fields) { // 容許訪問私有成員 field.setAccessible(true); try { // 輸出私有屬性信息 if (field.getModifiers() == Modifier.PRIVATE) result.append("私有屬性" + field.getName() + ":值爲" + field.get(obj) + "\n"); // 輸出受保護屬性信息 if (field.getModifiers() == Modifier.PROTECTED) result.append("受保護屬性" + field.getName() + ":值爲" + field.get(obj) + "\n"); } catch (Exception e) { System.out.println(e.getMessage()); } } return result.toString(); } } // 學生類 class Student { // 學號 private String number = null; // 姓名 private String name = null; // 性別 private boolean sex = false; // 生日 private Timestamp birthday = null; // 身高 private double height = 0; // 默認構造函數 public Student() { } // 該構造函數能夠對學生的屬性進行初始化 public Student(String number, String name, boolean sex, Timestamp birthday, double height) { setNumber(number); setName(name); setSex(sex); setBirthday(birthday); setHeight(height); } // 學號的讀取函數 public String getNumber() { return number; } // 學號的設置函數 public void setNumber(String number) { this.number = number; } // 姓名的讀取函數 public String getName() { return name; } // 姓名的設置函數 public void setName(String name) { this.name = name; } // 性別的讀取函數 public boolean isSex() { return sex; } // 性別的設置函數 public void setSex(boolean sex) { this.sex = sex; } // 生日的讀取函數 public Timestamp getBirthday() { return birthday; } // 生日的設置函數 public void setBirthday(Timestamp birthday) { this.birthday = birthday; } // 身高的讀取函數 public double getHeight() { return height; } // 身高的設置函數 public void setHeight(double height) { this.height = height; } // 格式化字符信息輸出 public String toString() { return "學號:" + number + "/n姓名:" + name + "/n是否爲男生:" + sex + "/n生日:" + birthday + "/n身高:" + height; } } /* 包的名稱爲:heimablog 類的名稱爲:Student 私有屬性number:值爲007 私有屬性name:值爲Jack Brawer 私有屬性sex:值爲true 私有屬性birthday:值爲1966-10-11 00:00:00.267 私有屬性height:值爲1.8 */
在java中有三種類類加載器:
Bootstrap ClassLoader 此加載器採用c++編寫,通常開發中不多見。
Extension ClassLoader 用來進行擴展類的加載,通常對應的是jre\lib\ext目錄中的類
AppClassLoader 加載classpath指定的類,是最經常使用的加載器。同時也是java中默認的加載器。