Java學習:註解,反射,動態編譯

狂神聲明 : 文章均爲本身的學習筆記 , 轉載必定註明出處 ; 編輯不易 , 防君子不防小人~共勉 ! java

Java學習:註解,反射,動態編譯

 Annotation 註解

 什麼是註解 ?

  • Annotation是從JDK5.0開始引入的新技術 .
  • Annotation的做用 :
    • 不是程序自己 , 能夠對程序做出解釋.(這一點和註釋沒什麼區別)
    • 能夠被其餘程序(好比:編譯器等)讀取.(註解信息處理流程,是註解和註釋的重大區別.若是沒有註解信息處理流程,則註解毫無心義)
  • Annotation的格式 :
    • 註解是以"@註釋名"在代碼中存在的 , 還能夠添加一些參數值 , 例如:@SuppressWarnings(value="unchecked").
  • Annotation在哪裏使用?
    • 能夠附加在package , class , method , field 等上面 , 至關於給他們添加了額外的輔助信息,咱們能夠經過反射機制編程實現對這些元數據的訪問

內置註解

  • @Override : 定義在 java.lang.Override 中 , 此註釋只適用於修辭方法 , 表示一個方法聲明打算重寫超類中的另外一個方法聲明.
  • @Deprecated : 定義在java.lang.Deprecated中 , 此註釋能夠用於修辭方法 , 屬性 , 類 , 表示不鼓勵程序員使用這樣的元素 , 一般是由於它很危險或者存在更好的選擇 .
  • @SuppressWarnings : 定義在java.lang.SuppressWarnings中,用來抑制編譯時的警告信息.
    • 與前兩個註釋有所不一樣,你須要添加一個參數才能正確使用,這些參數都是已經定義好了的,咱們選擇性的使用就行了,參數以下 :
    • @SuppressWarnings("unchecked")
    • @SuppressWarnings(value={"unchecked","deprecation"})

元註解

  • 元註解的做用就是負責註解其餘註解 , Java定義了4個標準的meta-annotation類型,他們被用來提供對其餘annotation類型做說明 .
  • 這些類型和它們所支持的類在java.lang.annotation包中能夠找到 .( @Target , @Retention , @Documented , @Inherited )
  • @Target : 用於描述註解的使用範圍(即:被描述的註解能夠用在什麼地方)
    • @Target(value=ElementType.TYPE)
  • @Retention : 表示須要在什麼級別保存該註釋信息 , 用於描述註解的生命週期

 自定義註解

  •  使用 @interface自定義註解時 , 自動繼承了java.lang.annotation.Annotation接口
    package com.test.annotation;
      
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
      
    @Target(value={ElementType.METHOD,ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Annotation01 {
     
        String studentName() default "";
        int age() default 0;
        int id() default -1;   //String indexOf("abc")  -1
     
        String[] schools() default {"清華大學","北京大學"};
     
    }
  •  要點 :
    • @ interface用來聲明一個註解
    • 其中的每個方法其實是聲明瞭一個配置參數.
      • 方法的名稱就是參數的名稱
      • 返回值類型就是參數的類型 ( 返回值只能是基本類型,Class , String , enum ).
      • 能夠經過default來聲明參數的默認值
      • 若是隻有一個參數成員 , 通常參數名爲value
      • 註解元素必需要有值 , 咱們定義註解元素時 , 常用空字符串,0做爲默認值.
      • 也常用負數(-1)表示不存在的含義
  • package com.test.annotation;
      
    /**
     * 測試自定義註解的使用
     */
    public class Demo02 {
     
    @Annotation01(age=22,studentName="狂神",id=10001,schools={"北京大學","清華大學"})
    public void test(){}
    }

 註解做業

  • 實現一個小Demo利用反射讀取註解的信息
  • 什麼是ORM ? -->對象Object 關係relationship 映射Mapping
    • 類和表結構對應
    • 屬性和字段對應
    • 對象和記錄對應
  • @AnnoTable("tb_student")
    public class Student {
    
        @AnnoField(columnName = "id",type="int",length = 10)
        private int id;
        @AnnoField(columnName = "studentName",type="varchar",length = 10)
        private String studentName;
        @AnnoField(columnName = "age",type="int",length = 3)
        private int age;
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getStudentName() {
            return studentName;
        }
    
        public void setStudentName(String studentName) {
            this.studentName = studentName;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Field;
    
    //使用反射讀取註解的信息,模擬處理註解信息的流程
    public class ReadAnno {
        public static void main(String[] args) {
            try {
                Class c =  Class.forName("Student");
    
                //得到類的全部有效註解
                Annotation[] annotations = c.getAnnotations();
                for (Annotation a:annotations){
                    System.out.println(a);
                }
    
                //得到類的指定註解
                AnnoTable at = (AnnoTable)c.getAnnotation(AnnoTable.class);
                System.out.println(at.value());
    
                //得到類的屬性的註解
                Field f = c.getDeclaredField("studentName");
                AnnoField af = f.getAnnotation(AnnoField.class);
                System.out.println(af);
                System.out.println(
                        af.columnName()+"--"+
                        af.length()+"--"+
                        af.type()
                );
    
                //根據得到的表名,字段的信息,拼出DDL語句,而後
                //使用JDBC執行這個SQL,在數據庫中生成相關的表
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
    
        }
    }
  • import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(value = {ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AnnoField {
        String columnName();
        String type();
        int length();
    }
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(value = {ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AnnoTable {
        String value();
    }

 反射機制 reflection

 java的動態性程序員

  • 反射機制
  • 動態編譯
  • 動態執行JavaScript 代碼
  • 動態字節碼操做

動態語言數據庫

  • 程序運行時 ,能夠改變程序結構或變量類型 , 典型的語言 :
    • Python , Ruby , JavaScript等...
  • C , C++ , Java不是動態語言 , 可是Java具備必定的動態性 , 咱們能夠利用反射機制 , 字節碼操做得到相似動態語言的特性 . Java的動態性讓編程的時候更加靈活 !

反射機制

  • 指的是能夠於運行時加載 , 探知 , 使用編譯期間徹底未知的類 .
  • 程序在運行狀態中 , 能夠動態加載一個只有名稱的類 , 對於任意一個已加載的類 , 都可以知道這個類的全部屬性和方法 , 對於任意一個對象 , 都可以調用它的任意一個方法和屬性 ;
  • Class c = Class.forName("com.kuangstudy.User")

     加載完類以後 , 在堆內存中 , 就產生了一個Class類型的對象(一個類只有一個Class對象) , 這個對象就包含了完整的類的結構信息 . 咱們能夠經過這個對象看到類的結構 , 這個對象就像一面鏡子 , 透過這個鏡子看到類的結構 , 因此咱們形象的稱之爲 : 反射 .編程

Class類介紹數組

  • java.lang.Class類十分特殊 , 用來表示 java 中類型 ( class / interface / enum / annotation / primitive type / void ) 自己
  • Class類的對象包含了某個被加載類的結構 , 一個被加載的類對應一個Class對象 .
  • 當一個class被加載 , 或當加載器( class loader ) 的defineClass() 被JVM調用 , JVM便自動產生一個Class對象 .
  • Class類是Reflection(反射)的根源 .
  • 針對任何您想動態加載 , 運行的類 . 惟有先得到相應的Class對象 ,

Class類的對象如何獲取 ?瀏覽器

  • 運用getClass()
  • 運用Class.forName() -->最常被使用
  • 運用.class 語法
    //測試各類類型(class,interface,enum,annotation,primitive type,void)對應的java.lang.Class對象的獲取方式
    
    @SuppressWarnings("all")
    public class Demo01 {
     
        public static void main(String[] args) {
            String path = "com.test.bean.User";
     
            try {
     
                Class clazz = Class.forName(path);
                //對象是表示或封裝一些數據。  一個類被加載後,JVM會建立一個對應該類的Class對象,類的整個結構信息會放到對應的Class對象中。
                //這個Class對象就像一面鏡子同樣,經過這面鏡子我能夠看到對應類的所有信息。
                System.out.println(clazz.hashCode());
     
                Class clazz2 = Class.forName(path); //一個類只對應一個Class對象
                System.out.println(clazz2.hashCode());
     
                Class strClazz = String.class;
                Class strClazz2 = path.getClass(); 
                System.out.println(strClazz==strClazz2);
     
                Class intClazz =int.class;
     
                int[] arr01 = new int[10];
                int[][] arr02 = new int[30][3];
                int[] arr03 = new int[30];
                double[] arr04 = new double[10];
     
                System.out.println(arr01.getClass().hashCode());
                System.out.println(arr02.getClass().hashCode());
                System.out.println(arr03.getClass().hashCode());
                System.out.println(arr04.getClass().hashCode());
     
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    } 

反射機制的常見做用

  • 動態加載類 , 動態獲取類的信息(屬性,方法,構造器)
  • 動態構造對象
  • 動態調用類和對象的任意方法 , 構造器
  • 動態調用和處理屬性
  • 獲取泛型信息
  • 處理註解
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    /**
     * 應用反射的API,獲取類的信息(類的名字、屬性、方法、構造器等)
     */
    public class Demo01 {
        public static void main(String[] args) {
            String path = "com.test.bean.User";
    
            try {
                Class clazz = Class.forName(path);
    
                //獲取類的名字
                System.out.println(clazz.getName());//得到包名+類名:com.bjsxt.test.bean.User
                System.out.println(clazz.getSimpleName()); //獲的類名:User
    
                //獲取屬性信息
                //   Field[] fields = clazz.getFields(); //只能得到public的field
                Field[] fields = clazz.getDeclaredFields();//得到全部的field
                Field f = clazz.getDeclaredField("uname");
                System.out.println(fields.length);
                for(Field temp:fields){
                    System.out.println("屬性:"+temp);
                }
                //獲取方法信息
                Method[] methods = clazz.getDeclaredMethods();
                Method m01 = clazz.getDeclaredMethod("getUname", null);
                //若是方法有參,則必須傳遞參數類型對應的class對象
                Method m02 = clazz.getDeclaredMethod("setUname", String.class);
                for(Method m:methods){
                    System.out.println("方法:"+m);
                }
    
                //得到構造器信息
                Constructor[] constructors = clazz.getDeclaredConstructors();
                Constructor c = clazz.getDeclaredConstructor(int.class,int.class,String.class);
                System.out.println("得到構造器:"+c);
                for(Constructor temp:constructors){
                    System.out.println("構造器:"+temp);
                }
                
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    /**
     * 經過反射API動態的操做:構造器、方法、屬性
     */
    public class Demo01 {
        public static void main(String[] args) {
    
            String path = "com.test.bean.User";
    
            try {
                Class<User> clazz = (Class<User>) Class.forName(path);
    
                //經過反射API調用構造方法,構造對象
                User u = clazz.newInstance(); //實際上是調用了User的無參構造方法
                System.out.println(u);
    
                Constructor<User> c = clazz.getDeclaredConstructor(int.class,int.class,String.class);
                User u2 = c.newInstance(1001,18,"測試一");
                System.out.println(u2.getUname());
    
                //經過反射API調用普通方法
                User u3 = clazz.newInstance();
                Method method = clazz.getDeclaredMethod("setUname", String.class);
                method.invoke(u3, "測試三");   //u3.setUname("測試三");
                System.out.println(u3.getUname());
    
                //經過反射API操做屬性
                User u4 = clazz.newInstance();
                Field f = clazz.getDeclaredField("uname");
                f.setAccessible(true); //這個屬性不須要作安全檢查了,能夠直接訪問
                f.set(u4, "測試四");  //經過反射直接寫屬性
                System.out.println(u4.getUname()); //經過反射直接讀屬性的值
                System.out.println(f.get(u4));
                
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

 反射機制性能問題安全

  •  setAccessible
    • 啓用和禁用訪問安全檢查的開關 , 值爲true 則指示反射的對象在使用時應該取消Java語言訪問檢查, 值爲false 則指示反射的對象應該實施Java語言訪問檢查,並非爲true就能訪問爲false就不能訪問
    • 禁止安全檢查 , 能夠提升反射的運行速度
  • 能夠考慮使用 : cglib / javaassist字節碼操做

 反射操做泛型(Generic)

  •  Java採用泛型擦除的機制來引入泛型 , Java中的泛型僅僅是給編譯器javac使用的 , 確保數據的安全性和免去強制類型轉換的麻煩, 可是 , 一旦編譯完成 , 全部的和泛型有關的類型所有擦除
  • 爲了經過反射操做這些類型以迎合實際開發的需求 , Java新增了ParameterizedType,GenericArrayType , TypeVariable和WildcardType幾種類型來表明不能被歸一到Class類中的類型可是又和原始類型齊名的類型.
    • ParameterizedType:表示一種參數化的類型 , 好比Collection<String>
    • GenericArrayType : 表示一種元素類型是參數化類型或者類型變量的數組類型
    • TypeVariable : 是各類類型變量的公共父接口
    • WildcardType: 表明一種通配符類型的表達式 , 好比 ?.? extends Number , ? super Integer 
  • import java.lang.reflect.Method;
    import java.lang.reflect.ParameterizedType;
    import java.lang.reflect.Type;
    import java.util.List;
    import java.util.Map;
    
    /**
     * 經過反射獲取泛型信息
     */
    public class Demo01 {
        public void test01(Map<String,User> map,List<User> list){
            System.out.println("Demo01.test01()");
        }
        public Map<Integer,User> test02(){
            System.out.println("Demo01.test02()");
            return null;
        }
        public static void main(String[] args) {
            try {
                //得到指定方法參數泛型信息
                Method m = Demo01.class.getMethod("test01", Map.class,List.class);
                Type[] t = m.getGenericParameterTypes();
                for (Type paramType : t) {
                    System.out.println("#"+paramType);
                    if(paramType instanceof ParameterizedType){
                        Type[] genericTypes = ((ParameterizedType) paramType).getActualTypeArguments();
                        for (Type genericType : genericTypes) {
                            System.out.println("泛型類型:"+genericType);
                        }
                    }
                }
                //得到指定方法返回值泛型信息
                Method m2 = Demo01.class.getMethod("test02", null);
                Type returnType = m2.getGenericReturnType();
                if(returnType instanceof ParameterizedType){
                    Type[] genericTypes = ((ParameterizedType) returnType).getActualTypeArguments();
    
                    for (Type genericType : genericTypes) {
                        System.out.println("返回值,泛型類型:"+genericType);
                    }
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

 反射操做註解(annotation)服務器

  •  能夠經過反射API : getAnnotations , getAnnotation得到相關的註解信息 .
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Field;
    
    /**
     * 經過反射獲取註解信息
     */
    public class Demo01 {
        public static void main(String[] args) {
            try {
                Class clazz = Class.forName("com.test.annotation.Student");
                //得到類的全部有效註解
                Annotation[] annotations=clazz.getAnnotations();
                for (Annotation a : annotations) {
                    System.out.println(a);
                }
                //得到類的指定的註解
                tTable st = (tTable) clazz.getAnnotation(tTable.class);
                System.out.println(st.value());
    
                //得到類的屬性的註解
                Field f = clazz.getDeclaredField("studentName");
                tField tField = f.getAnnotation(tField.class);
                System.out.println(tField.columnName()+"--"+tField.type()+"--"+tField.length());
    
                //根據得到的表名、字段的信息,拼出DDL語句,而後,使用JDBC執行這個SQL,在數據庫中生成相關的表
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

     

 動態編譯

  •  Java 6.0 引入了動態編譯機制
  • 動態編譯的應用場景 :
    • 能夠作一個瀏覽器端編寫java代碼 , 上傳服務器編譯和運行的在線評測系統
    • 服務器動態加載某些類文件進行編譯
  • 動態編譯的兩種作法 :
    • 經過Runtime調用javac , 啓動新的進程去操做
      Runtime run = Runtime.getRuntime();
      Process process = run.exec("javac -cp d:/java/ Hello.java");
    • 經過JavaCompiler動態編譯
      • 第一個參數 : 爲Java編譯器提供參數(inputStream)
      • 第二個參數 : 獲得Java編譯器的輸出信息(outputStream)
      • 第三個參數 : 接收編譯器的錯誤信息(outputStream)
      • 第四個參數 : 可變參數(是一個String數組)能傳入一個或多個Java源文件
      • 返回值 : 0表示編譯成功 , 非0表示編譯失敗
    • import javax.tools.JavaCompiler;
      import javax.tools.ToolProvider;
      import java.lang.reflect.Method;
      import java.net.URLClassLoader;
      
      public class Demo01 {
          public static void main(String[] args) throws Exception {
      
      //經過IO流操做,將字符串存儲成一個臨時文件(Hi.java),而後調用動態編譯方法!
              String str = "public class Hi {public static void main(String[] args){System.out.println(\"HaHa!\");}}";
      
              JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
              int result = compiler.run(null, null, null, "c:/myjava/HelloWorld.java");
              System.out.println(result==0?"編譯成功":"編譯失敗");
      
      
      //經過Runtime調用執行類
      //  Runtime run = Runtime.getRuntime();  
      //        Process process = run.exec("java -cp  c:/myjava    HelloWorld");
      //
      //        InputStream in = process.getInputStream();
      //        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
      //  String info = "";
      //  while((info=reader.readLine())!=null){
      //   System.out.println(info);
      //  }
      
              try {
                  URL[] urls = new URL[] {new URL("file:/"+"C:/myjava/")};
                  URLClassLoader loader = new URLClassLoader(urls);
                  Class c = loader.loadClass("HelloWorld");
                  //調用加載類的main方法
                  Method m = c.getMethod("main",String[].class);
                  m.invoke(null, (Object)new String[]{});
                  //因爲可變參數是JDK5.0以後纔有。
                  //m.invoke(null, (Object)new String[]{});會編譯成:m.invoke(null,"aa","bb"),就發生了參數個數不匹配的問題。
                  //所以,必需要加上(Object)轉型,避免這個問題。
                  //public static void main(String[] args)
      
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }
相關文章
相關標籤/搜索