基礎筆記13(動態性:反射,動態編譯,執行,操做)

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

相關文章
相關標籤/搜索