1:動態語言:java
在運行時,可以改變程序結構和類型。(java不行,如:python,js,ruby)
c,c++,java卻能夠經過反射,操做字節碼得到相似動態的特性。
2.反射機制:python
能夠在運行時,加載,探索,使用編譯期間未知的類型。
在運行時,加載一個只知道類名的類,便能知道這個類的屬性和方法,能夠用來生成對象,對於對象能夠調用其方法和屬性。
(加載類,其實就是在堆內存中生成一個Class類型的對象(jvm經過加載器),而且每一個類型都只會有一個Class對象。因此Class對象是反射的根源)
3.獲取Class:spring
Class string = Class.forName("com.me.test.reflect.User");//通常用類全名(即包括包名)(之所能夠不用,是import了 Class string2 = "stirng".getClass();//經過對象獲取 Class string3 = String.class;//經過類型獲取
a.數組是不一樣的維度對應不一樣的Class
b.像class interface enu void type private等這些關鍵詞表明的意義也是Class類型apache
4.功能做用:編程
特別注意對於一些方法對於可變參數(即數組)而言,數組參數要轉成Object,若是參數自己就是數組,不轉換會該參數的元素被誤認爲,是多個參數的封裝到數組了。數組
Object object = f.invoke(null, (Object) new String[] { "a", "b" },(Object) new String[] { "aa", "ab" });
獲取屬性,方法 ,構造器的時候,帶declared的方法名才能獲取全部範圍的,不然只能獲取public修飾的。好比:declaredField()和getField()。安全
Class userClass =Class.forName("com.me.test.reflect.User"); 1.// 獲取屬性,只能是public修飾的 Field field = userClass.getField("id");// 不然找不到,異常 // 能夠是任意範圍 Field declaredField = userClass.getDeclaredField("pId"); // public int com.me.test.reflect.User.id----private int // com.me.test.reflect.User.pId System.out.println(field + "----" + declaredField); // 只能獲取public屬性 Field[] fields = userClass.getFields(); // 全部屬性 Field[] declaredF = userClass.getDeclaredFields(); // [public int com.me.test.reflect.User.id] System.out.println(Arrays.toString(fields)); // [private int com.me.test.reflect.User.pId, // public int com.me.test.reflect.User.id, // protected java.lang.String com.me.test.reflect.User.proName] System.out.println(Arrays.toString(declaredF)); 2.//獲取無參方法能夠不寫參數類型 Method getName = userClass.getMethod("getName"); Method setName = userClass.getMethod("setName", String.class); Method declaredsetName = userClass.getDeclaredMethod("setName", String.class); Method[] methods = userClass.getMethods(); Method[] declaredMethods = userClass.getDeclaredMethods(); 3.//得到構造器 Constructor[] constructors = userClass.getConstructors(); Constructor[] declaredconstructors2 = userClass.getDeclaredConstructors(); Constructor<User> constructor = userClass.getConstructor(int.class,String.class); Constructor<User> declaredconstructor2 = userClass.getDeclaredConstructor(int.class,String.class); 4.//建立有參數的對象須要先得到構造器 User newInstance = userClass.newInstance();//無參的字節碼對象直接建立 User newInstance2 = constructor.newInstance(1,"大王"); 5.//設置用屬性,首先得到屬性對象,以及屬性所屬的對象 field.setAccessible(true);//true表示不進行安全檢查, 對於私有屬性和方法才能操做 field.set(newInstance,11 ); 6.//調用方法,首先得到方法對象,以及調用方法的對象 declaredsetName.invoke(newInstance, "三王");
5.反射執行效率低於正常執行,好比執行10億次getName()方法ruby
普通大約:2258ms 1倍
反射大約:62687ms 30倍
不安全檢查的反射:14305ms 6倍
6.反射獲取泛型:java的泛型只存在編譯期,因此爲了獲得泛型,java提供了一些不屬於Class的類型來獲取泛型。下面類型和Class類型同屬Type的子類型數據結構
public void test1(Map<String, User> map, List<User> l) {} public Map<String, User> test2() {return new HashMap<String, User>();} Method test1 = ReflectTest.class.getMethod("test1", Map.class, List.class); // 得到方法的參數類型數組 Type[] genericParameterTypes = test1.getGenericParameterTypes(); // [java.util.Map<java.lang.String, com.me.test.reflect.User>, // java.util.List<com.me.test.reflect.User>] System.out.println(Arrays.toString(genericParameterTypes)); for (Type gType : genericParameterTypes) { // 判斷是不是泛型參數 if (gType instanceof ParameterizedType) { // 得到泛型參數中的泛型類型 Type[] actualTypeArguments = ((ParameterizedType) gType).getActualTypeArguments(); // 第一次循環: [class java.lang.String, class com.me.test.reflect.User] // 第二次循環: [class com.me.test.reflect.User] System.out.println(Arrays.toString(actualTypeArguments)); } } Method test2 = ReflectTest.class.getMethod("test2"); // 得到返回類型 Type genericReturnType = test2.getGenericReturnType(); // [java.util.Map<java.lang.String, com.me.test.reflect.User>, // java.util.List<com.me.test.reflect.User>] System.out.println(genericReturnType); // 返回類型是不是泛型 if (genericReturnType instanceof ParameterizedType) { // 得到泛型參數中的泛型類型 Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments(); // 輸出: [class java.lang.String, class com.me.test.reflect.User] System.out.println(Arrays.toString(actualTypeArguments)); }
7.反射獲取註解:框架
8.動態編譯:動態的加載一些類文件進行編譯。
java1.6引入了動態編譯機制以前的相似效果的方式:
Runtime runtime = Runtime.getRuntime(); runtime.exec("javac -cp d:/mytest/ Hello.java");//編譯 Process process = runtime.exec("java -cp d:/mytest/ Hello");//運行 //能夠從輸入流中讀取到打印的信息 InputStream inputStream = process.getInputStream();
java6引入了javaCompile類(若是想編譯一個字符串的程序,能夠考慮先弄成.java文件。)
JavaCompiler systemJavaCompiler = ToolProvider.getSystemJavaCompiler(); // 四個參數in, out, err, arguments,前三個默認是標準輸入,輸出,錯誤輸出 // 會在同位生成字節碼文件,返回0表示成功 int run = systemJavaCompiler.run(null, null, null, "d:/mytest/ Hello.java");
能夠經過反射運行字節碼:
URL[] urls = new URL[] { new URL("file:/" + "d:/mytest/") };//url不只能夠封裝連接,還能夠是文件目錄(感受用的少) URLClassLoader classLoader = URLClassLoader.newInstance(urls);// 得到加載器 Class<?> hello = classLoader.loadClass("Hello");// 加載字節碼生成Class對象 Method f = hello.getDeclaredMethod("f", String[].class);// void f(String[] s){..} // 對於靜態方法能夠所屬對象能夠是null // 特別注意爲何要轉成Object,對於可變參數(即數組)而言, // 若是參數自己就是數組,不轉換會該參數的元素被誤認爲,是多個參數的封裝到數組了。 Object object = f.invoke(null, (Object) new String[] { "a", "b" });
9.java腳步引擎(1.6引入),經過一套固定的接口,實現和腳步引擎交互,從而能夠將一些複雜業務邏輯交給腳本語言處理。好比用js運行字符串「12+3-3*/5=」的計算。
java6中將Rhino(由java語言編寫能夠實現js)引擎集成了(來源v215暫略)
10.字節碼操做:java動態性通常由反射和字節碼實現的。
1.操做字節碼能夠在運行時,動態生成新的類,改變類的結構(增刪改屬性,方法)。
2.效率比反射高,
10.1一些字節碼框架:(一些應用級別的框架都用到他們,如spring hibernate ...)(比上面8自帶的強大)
BCEL:byte code engineering Library,apache項目的一部分,能夠深刻jvm彙編語言操做類細節,擁有指令集級別的操做。 ASM:輕量級別的字節碼操做,直接涉及jvm底層的操做和指令。 CGLIB:code generation library,基於ASM的代碼生成庫框架 Javassist:一個開源的分析,編輯,建立字節碼的類庫,性能和CGLIB差很少,但使用更加靈活。
10.2 Javassist能夠用來面向切面編程,也能夠實現反射效果。(使用前須要首先倒入此包)(略)
11.類加載過程:jvm把class文件加載到內存,並對數據進行校驗,解析,初始化,最後可以使用該類型的過程。目的:瞭解jvm運行過程,java動態性:熱部署,動態加載,加強程序靈活性。
11.1 加載:將.class字節碼內容加載到內存,並將靜態數據轉換成方法區中運行時數據結構,在堆中生成一個表明這個類的class對象。做爲方法區類數據訪問的入口(須要類加載器的參與)
連接:
初始化:
11.2不初始化類的狀況:
a.調用類的常量時不會初始化類(final static)
b.子類調用父類的靜態屬性不會初始化子類。
12.類加載器
類加載器的做用:
類加載器的機制:
獲取加載器的對象:
System.out.println(System.getProperty("java.class.path")); System.out.println("自定義類的加載器:"+User.class.getClassLoader()); System.out.println(ClassLoader.getSystemClassLoader()); System.out.println(ClassLoader.getSystemClassLoader().getParent()); System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());// 對於引導加載器不能獲取到
輸出:
E:\workspace2\test\bin 自定義類的加載器:sun.misc.Launcher$AppClassLoader@addbf1 sun.misc.Launcher$AppClassLoader@addbf1 sun.misc.Launcher$ExtClassLoader@42e816 null
線程上下文類加載器
osgi
equinox