(Java)註解和反射

註解和反射

一. 註解

註解(Annotation)是Java提供的設置程序中元素的關聯信息和元數據(MetaData)的方法,它是一個接口,程序可以通過反射獲取指定程序中元素的註解對象,然後通過該註解對象獲取註解中的元數據信息。

1.1 元註解

import java.lang.annotation.*;

//測試原註解
@MyAnnotation
public class Test01 {

    public void test() {

    }

}

//定義一個註解
//Target 表示我們的註解可以用在哪些地方
@Target(value = {ElementType.METHOD,ElementType.TYPE})

//Retention 表示我們的註解在什麼地方有效   runtime>class>source
@Retention(value = RetentionPolicy.RUNTIME)

//Documented 表示是否將我們的註解生成在JAVAdoc中
@Documented

//Inherited  子類可以繼承父類的註解
@Inherited
@interface MyAnnotation{

}

1.2 內置註解

import java.util.ArrayList;

//內置註解
public class Test03 {

    @Override  //重寫的註解
    public String toString() {
        return "Test03{}";
    }


    @Deprecated  //不推薦使用,存在更好的
    public static void test() {
        System.out.println("Deprecated");
    }

    @SuppressWarnings("all")  //鎮壓警告
    public void test2() {
        ArrayList arrayList = new ArrayList();
    }

    public static void main(String[] args) {
        test();
    }

}

1.3 自定義註解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//自定義註解
public class Demo01 {
    //註解可以顯示賦值,如果沒有默認值,我們就必須給註解賦值
    @MyAnnotation(name="伊澤瑞爾",schools = "戰爭學院")
    public void test1(){

    }
    //一個參數的註解可以直接寫值
    @MyAnnotation1("魔法貓咪")
    public void test2(){

    }
    //設置生效類型
    @Target({ElementType.TYPE,ElementType.METHOD})
    //設置作用範圍
    @Retention(RetentionPolicy.RUNTIME)
    @interface MyAnnotation{
        String name() default "";
        int age() default 0;
        int id() default -1;
        String[] schools() default {"",""};
    }
    //設置生效類型
    @Target({ElementType.TYPE,ElementType.METHOD})
    //設置作用範圍
    @Retention(RetentionPolicy.RUNTIME)
    @interface MyAnnotation1{
        //一個參數的註解一般用value作爲參數
        String value();
    }
}

二. 反射

2.1 什麼是反射

Java c c++是靜態語言,Java可以通過反射獲得類似動態語言的特性。(準動態語言)

動態語言是指在運行階段可以改變其結構

反射 優點:

  • 可以動態創建對象和編譯,靈活

反射 缺點:

  • 對象性能有影響,總是慢於直接執行相同的操作

2.2 Class類

  • class本身也是一個類
  • 只能由系統建立對象
  • 一個加載的類在JVM中只會有一個Class實例
  • 一個Class實例對應的是一個加載到JVM中的一個.class文件
  • 每個類的實例都會記得自己是由哪個class實例生成的
  • 通過Class可以完整的得到一個類所有被加載的結構
  • Class類是Reflection反射的根源,針對任何想動態加載,運行的類,唯有先獲得相應的Class對象

2.3 創建Class類的方式

package com.company.demo11.reflection;

public class Demo01 {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person = new Student();
        System.out.println("這個人是:"+person.name);
        //1.通過對象獲得
        Class c1 = person.getClass();
        System.out.println(c1.hashCode());
        //2.forName獲得
        Class c2 = Class.forName("com.company.demo11.reflection.Student");
        System.out.println(c2.hashCode());
        //3.通過類名.class獲得
        Class c3=Student.class;
        System.out.println(c3.hashCode());
        //4.基本內置類型的包裝類都有一個Type屬性
        Class c4 = Integer.TYPE;
        System.out.println(c4);
        //獲取父類類型
        Class c5 = c1.getSuperclass();
        System.out.println(c5);
    }
}
class Person {
    String name;
    public Person() {
    }
    public Person(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}
class Student extends Person {
    public Student() {
        this.name = "學生";
    }
}
class Teacher extends Person {
    public Teacher() {
        this.name = "老師";
    }
}

在這裏插入圖片描述

2.4 所有類型的Class

package com.company.demo11.reflection;

import java.lang.annotation.ElementType;
//所有類型的Class
public class Demo02 {
    public static void main(String[] args) {
        Class c1 = Object.class;  //類
        Class c2 = Comparable.class;  //接口
        Class c3 = String[].class;   //一維數組
        Class c4 = int[][].class;   //二維數組
        Class c5 = Override.class;  //註解
        Class c6 = ElementType.class;  //枚舉
        Class c7 = Integer.class;   //基本數據類型
        Class c8 = void.class;  //void
        Class c9 = Class.class;  //Class

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);

        //只要元素類型與維度一樣,就是同一個Class
        int[] a = new int[10];
        int[] b = new int[100];
        System.out.println(a.getClass().hashCode());
        System.out.println(b.getClass().hashCode());
    }

}

在這裏插入圖片描述

2.5 類加載機制內存分析

JVM的類加載分爲5個階段:加載,驗證,準備,解析,初始化。在類初始化完成後就可以使用該類的信息,在一個類不再被需要時可以從JVM中卸載。如圖所示:

在這裏插入圖片描述
代碼測試

public class Test {
    public static void main(String[] args) {
        A a = new A();
        System.out.println(a.m);
        /**
         * 1.加載到內存,會產生一個類對應的Class對象
         * 2.鏈接,鏈接結束後m=0  鏈接:把Java的二進制代碼合併到JVM運行狀態之中的過程
         * 3.初始化
         * <clinit>(){
         *      System.out.println("A類靜態代碼塊初始化");
         *      m = 100;
         *      m = 200;
         * }
         * m = 200
         */
    }

}
class A{
    static {
        System.out.println("A類的靜態代碼塊初始化");
        m=100;
    }
    static int m = 200;

    public A() {
        System.out.println("A類的無參構造方法初始化");
    }
}

在這裏插入圖片描述
內存分析如圖
在這裏插入圖片描述

2.6 類的初始化

主要通過執行類構造器的方法爲類進行初始化。方法是在編譯階段由編譯器自動收集類中靜態語句塊和變量的賦值操作組成的。JVM規定,只有在父類的方法都執行成功後,子類中的方法纔可以被執行。在一個類中既沒有靜態變量賦值操作也沒有靜態語句塊時,編譯器不會爲該類生成方法。

在發生以下幾種情況時,JVM不會執行類的初始化流程。

  • 常量在編譯時會將其常量值存入使用該常量的類的常量池中,該過程不需要調用常量所在的類,因此不會觸發該常量類的初始化。
  • 在子類引用父類的靜態字段時,不會觸發子類的初始化,只會觸發父類的初始化。
  • 定義對象數組,不會觸發該類的初始化,只是開闢了一塊空間。
  • 在使用類名獲取Class對象時不會觸發類的初始化。
  • 在使用Class.forName加載指定的類時,可以通過initialize參數設置是否需要對類進行初始化。
  • 在使用ClassLoader默認的loadClass方法加載類時不會觸發該類的初始化
public class Test01 {
    static {
        System.out.print("main類被加載 ");
    }

    public static void main(String[] args) throws ClassNotFoundException {
        //在主動引用時,會初始化
        //1.主動引用,父類沒被引用,會先加載他的父類
//       Son son = new Son();//main類被加載 父類被加載 子類被加載
        //2.反射也會產生主動引用
//        Class.forName("com.company.demo11.reflection.Son");//main類被加載 父類被加載 子類被加載
        //不會產生類的引用方法
        //1.子類引用父類的靜態字段時,不會觸發子類的初始化,只會觸發父類的初始化
//        System.out.println(Son.b);//main類被加載 父類被加載 2
        //2.定義對象數組,不會觸發該類的初始化,只是開闢了一塊空間
//        Son[] sons = new Son[8];//main類被加載
        //3.常量調用不會初始化
//        System.out.println(Son.M);//main類被加載 1
        //4.在使用類名獲取Class對象時不會觸發類的初始化
//        Class c = Son.class;//main類被加載
        
    }
}
class Father{
    static int b =2;
    static {
        System.out.print("父類被加載 ");
    }
}
class Son extends Father{

    static {
        System.out.print("子類被加載 ");
        m=100;
    }
    static int m = 200;
    static final int M = 1;
}

2.7 類加載器

JVM提供了3種類加載器,分別是啓動類加載器,擴展類加載器和應用程序類加載器。如圖:
在這裏插入圖片描述

public class Test02 {
    public static void main(String[] args) throws ClassNotFoundException {
        //獲取應用程序類加載器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
        //獲取應用程序類加載器的父類加載器擴展類加載器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);//sun.misc.Launcher$ExtClassLoader@4554617c
        //獲取擴展類加載器的父類加載器啓動類加載器---->無法訪問,返回null
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);//null
        //測試當前類是那個類加載器加載的
        ClassLoader classLoader = Class.forName("com.company.demo11.reflection.Test02").getClassLoader();
        System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
        //測試JDK內置的類是誰加載的
        ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader1);//null
        //如何獲得系統類加載器可以加載的路徑 如果類不在這些地方則讀取不到
        System.out.println(System.getProperty("java.class.path"));
        //雙親委派機制:多重檢測,保證安全性
        // 比如自己定義一個java.lang.String類,要去加載,它先看用戶類加載器有沒有--->擴展類加載器--->根加載器
        //如果有自己寫的就無效

        /**
         D:\work\java\jdk1.8\jre\lib\charsets.jar;
         D:\work\java\jdk1.8\jre\lib\deploy.jar;
         D:\work\java\jdk1.8\jre\lib\ext\access-bridge-64.jar;
         :\work\java\jdk1.8\jre\lib\ext\cldrdata.jar;
         D:\work\java\jdk1.8\jre\lib\ext\dnsns.jar;
         D:\work\java\jdk1.8\jre\lib\ext\jaccess.jar;
         D:\work\java\jdk1.8\jre\lib\ext\jfxrt.jar;
         D:\work\java\jdk1.8\jre\lib\ext\localedata.jar;
         D:\work\java\jdk1.8\jre\lib\ext\nashorn.jar;
         D:\work\java\jdk1.8\jre\lib\ext\sunec.jar;
         D:\work\java\jdk1.8\jre\lib\ext\sunjce_provider.jar;
         D:\work\java\jdk1.8\jre\lib\ext\sunmscapi.jar;
         D:\work\java\jdk1.8\jre\lib\ext\sunpkcs11.jar;
         D:\work\java\jdk1.8\jre\lib\ext\zipfs.jar;
         D:\work\java\jdk1.8\jre\lib\javaws.jar;
         D:\work\java\jdk1.8\jre\lib\jce.jar;
         D:\work\java\jdk1.8\jre\lib\jfr.jar;
         D:\work\java\jdk1.8\jre\lib\jfxswt.jar;
         D:\work\java\jdk1.8\jre\lib\jsse.jar;
         D:\work\java\jdk1.8\jre\lib\management-agent.jar;
         D:\work\java\jdk1.8\jre\lib\plugin.jar;
         D:\work\java\jdk1.8\jre\lib\resources.jar;
         D:\work\java\jdk1.8\jre\lib\rt.jar;
         E:\註解和反射\out\production\註解和反射;
         D:\work\idea\lib\idea_rt.jar
         */

    }
}

2.7.1 雙親委派機制

JVM通過雙親委派機制對類進行加載。
雙親委派機制是指一個類在收到類加載請求後不會嘗試自己加載這個類,而是把類加載請求向上委派給其父類去完成,其父類在接收到該類加載請求後又會將其委派給自己的父類,以此類推,這樣所有的的類加載請求都被向上委派到啓動類加載器中。
若父類加載器在接收到類加載請求後發現自己也無法加載該類(通常原因是該類的Class文件在父類的類加載器路徑中不存在),則父類會將該信息反饋給子類並向下委派子類加載器加載該類,直到類被成功加載,若找不到該類,則JVM會拋出ClassNotFound異常。

雙親委派類加載機制的類加載流程如圖:
在這裏插入圖片描述

2.9 獲取運行時類的完整結構

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Demo03 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class c1 = Class.forName("com.company.demo11.reflection.User");
        //獲取類的名字
        System.out.println(c1.getName());//com.company.demo11.reflection.User
        System.out.println(c1.getSimpleName());//User
        System.out.println("--------------------------------------");
        //獲得類的屬性
        Field[] fields = c1.getFields();//只能找到public屬性
        fields = c1.getDeclaredFields();//找到全部屬性
        for (Field field : fields) {
            System.out.println(field);
            /*
            private java.lang.String com.company.demo11.reflection.User.name
            private int com.company.demo11.reflection.User.id
            private int com.company.demo11.reflection.User.age
             */
        }
        System.out.println("--------------------------------------");
        //獲取指定屬性的值
        System.out.println(c1.getDeclaredField("name"));//private java.lang.String com.company.demo11.reflection.User.name
        System.out.println("--------------------------------------");
        //獲取類的方法
        Method[] methods = c1.getMethods();//獲取本類及其父類的全部public方法
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("--------------------------------------");
        Method[] declaredMethods = c1.getDeclaredMethods();//獲取本類的所有方法
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }
        System.out.println("--------------------------------------");
        //獲得指定的方法
        Method getName = c1.getMethod("getName", null);
        Method setName = c1.getMethod("setName", String.class);
        System.out.println(getName);//public java.lang.String com.company.demo11.reflection.User.getName()
        System.out.println(setName);//public void com.company.demo11.reflection.User.setName(java.lang.String)
        System.out.println("--------------------------------------");
        //獲取構造器
        Constructor[] constructors = c1.getConstructors();//獲取public的構造器
        for (Constructor constructor : constructors) {
            System.out.println("public構造器:"+constructor);
            /*
            public com.company.demo11.reflection.User(java.lang.String,int,int)
            public com.company.demo11.reflection.User()
             */
        }

        Constructor[] declaredConstructors = c1.getDeclaredConstructors();//獲取全部的構造器
        for (Constructor declaredConstructor : declaredConstructors) {
            System.out.println("全部構造器:"+declaredConstructor);
             /*
            public com.company.demo11.reflection.User(java.lang.String,int,int)
            public com.company.demo11.reflection.User()
             */
        }
        //獲取指定的構造器
        Constructor constructor = c1.getConstructor(String.class, int.class,int.class);
        System.out.println("指定構造器"+constructor);//public com.company.demo11.reflection.User(java.lang.String,int,int)

    }
}

2.10 通過反射動態的創建對象

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Demo04 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException, NoSuchFieldException {
        //獲得Class對象
        Class c1 = Class.forName("com.company.demo11.reflection.User");
        //創建一個對象,此操作必須要有無參構造方法
        User user1 = (User) c1.newInstance();
        System.out.println(user1);//調用無參構造方法 User{name='null', id=0, age=0}
        //通過構造器創建對象
        Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        User user2 = (User) constructor.newInstance("周星馳", 007, 18);
        System.out.println(user2);//User{name='周星馳', id=7, age=18}
        //通過反射調用普通方法
        User user3 = (User) c1.newInstance();
        //通過反射獲得一個方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        //invoke 激活 (對象,方法的值)
        setName.invoke(user3,"伊澤瑞爾");
        System.out.println(user3.getName());//伊澤瑞爾
        //通過反射操作屬性
        User user4 = (User) c1.newInstance();
        Field name = c1.getDeclaredField("name");
        //不能直接操作私有屬性,關閉安全檢測.
        name.setAccessible(true);
        name.set(user4,"魔法貓咪");
        System.out.println(user4.getName());//魔法貓咪

    }
}

在這裏插入圖片描述

2.11 反射操作泛型

/通過反射獲取泛型
public class Test11 {

    public void test01(Map<String,User> map, List<User> list) {
        System.out.println("test01");
    }

    public Map<String,User> test02() {
        System.out.println("test02");
        return null;
    }


    public static void main(String[] args) throws NoSuchMethodException {
        /*
        #java.util.Map<java.lang.String, com.lcy.reflection.User>
        class java.lang.String
        class com.lcy.reflection.User
        #java.util.List<com.lcy.reflection.User>
        class com.lcy.reflection.User
        */
        //加載的方法和參數
        Method method = Test11.class.getMethod("test01", Map.class, List.class);
        //獲得泛型的參數類型
        Type[] genericParameterTypes = method.getGenericParameterTypes();

        for (Type type :genericParameterTypes) {  //打印泛型
            System.out.println("#"+type);
            if (type instanceof ParameterizedType) {  //想知道里面的參數類型
                //強轉獲得真實的泛型參數信息
                Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();
                for (Type temp :typeArguments) {
                    System.out.println(temp);
                }
            }
        }

        /*
        class java.lang.String
        class com.lcy.reflection.User
         */
        method = Test11.class.getMethod("test02",null);
        //獲得返回值類型
        Type genericReturnType = method.getGenericReturnType();
        if (genericReturnType instanceof  ParameterizedType) {
            Type[] types = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            for (Type temp :types) {
                System.out.println(temp);
            }
        }

    }



}

2.12 反射操作註解(重點)

import java.lang.annotation.*;
import java.lang.reflect.Field;

public class Demo05 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        //獲取Class對象
        Class c1 = Class.forName("com.company.demo11.reflection.Student1");
        //通過反射獲取註解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);//找到了外面的註解@com.company.demo11.reflection.Table(value=db_stu)
        }
        //獲取註解value的值
        Table annotation = (Table) c1.getAnnotation(Table.class);
        System.out.println(annotation.value());//db_stu
        //獲取類指定的註解
        Field f = c1.getDeclaredField("name");
        FieldZh annotation1 = f.getAnnotation(FieldZh.class);
        System.out.println(annotation1.columName());
        System.out.println(annotation1.type());
        System.out.println(annotation1.length());

    }

}
@Table("db_stu")
class Student1{
    @FieldZh(columName = "id",type = "int",length = 10)
    int id;
    @FieldZh(columName = "name",type = "varchar",length = 10)
    String name;
    @FieldZh(columName = "age",type = "int",length = 3)
    int age;

    @Override
    public String toString() {
        return "Student1{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public Student1() {
    }

    public Student1(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
//創建表的註解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
    String value();
}
//創建屬性的註解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldZh{
    String columName();
    String type();
    int length();

}

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章