1. 程式人生 > 資訊 >國慶假期來臨,一起來玩io遊戲吧

國慶假期來臨,一起來玩io遊戲吧

一 註解機制

1.1 內建註解

@Override:此註解只適用於修飾方法,表示一個方法宣告打算重寫父類中的另一個方法宣告

@Deprecated:此註解可以修飾方法、屬性、類,表示不鼓勵程式設計師使用這樣的元素,通常因為它很危險或存在更好的選擇

@SuppressWarning:用來鎮壓編譯時的警告資訊

  • @SuppressWarning("all")
  • @SuppressWarning("unchencked")
  • @SuppressWarning(values={"unchecked", "deprecation"})
@SuppressWarnings("all")
public void test02(){
    // 定義了但沒有使用,預設是會出現警告的,但加了註解之後,警告消失(被鎮壓)
	LinkedList list = new LinkedList();
}

1.2 元註解

元註解的作用就是負責註解其它的註解,Java定義了4個標準的meta-annotation型別,它們被用來提供對其它的annotation型別作說明。

  • @Target:用於描述註解的使用範圍(即:被描述的註解可以用在什麼地方)
  • @Retention:表示需要在什麼級別儲存該註解,用於描述註解的生命週期
    • SOURCE < CLASS < RUNTIME(一般使用這個)
  • @Document:說明該註解將被包含在javadoc中
  • @Inherited:說明子類可以繼承父類中的註解
package zr.annotation;

import java.lang.annotation.*;

/**
 * 測試元註解
 */
public class Test01 extends Object{
    @MyAnnotation
    public void test(){

    }
}

// 定義一個註解
@Inherited
// Documented 表示是否將我們的註解生成在Javadoc中
@Documented
// Recumented 表示註解的生命週期
@Retention(value = RetentionPolicy.RUNTIME)
// Target 表示註解的使用範圍
@Target(value = {ElementType.METHOD, ElementType.TYPE})
@interface MyAnnotation{
}

1.3 自定義註解

使用@interface自動以註解時,自動繼承了java.lang.annotation.Annotation介面

  • @interface用來宣告一個註解,格式:public @interface 註解名{定義內容}
  • 其中的每一個方法實際上是聲明瞭一個配置引數
  • 方法的名稱就是引數的名稱
  • 返回值型別就是引數的型別(返回值只能是基本型別、Class、String、enum)
  • 可以通過default來宣告引數的預設值
  • 如果只有一個引數成員,一般引數名為value,在使用的時候可以把value省略掉
  • 註解元素必須要有值,我們定義註解元素時,經常使用空字串、0作為預設值

二 反射機制

2.1 反射概念

Refelection(反射)是Java被視為動態語言的關鍵,反射機制允許程式在執行期藉助於Reflection API獲取任何類的內部資訊,並能直接操作任意物件的內部屬性及方法。

Class clazz = Class.forName("java.lang.String");

類載入完之後,在堆記憶體中就產生了一個Class型別的物件(一個類只有一個Class物件),這個物件就包含了完整的類的結構資訊,我們可以通過這個物件看到類的結構。這個物件就像一面鏡子,透過這個鏡子看到類的結構,所以我們形象地稱之為:反射。

建立物件的方式:

  • 正常方式:引入需要的“包類”名稱—>通過new關鍵字例項化—>取得例項化物件
  • 反射方式:例項化物件—>getClass()方法—>得到完整的“包類”名稱

2.2 反射相關的API

  • java.lang.Class:代表一個類
  • java.lang.reflect.Method:代表類的方法
  • java.lang.reflect.Field:代表類的成員變數
  • java.lang.reflect.Constructor:代表類的構造器
  • ......

2.3 獲取Class類的例項

  • 若已知具體的類,通過類的class屬性獲取,該方法最為可靠,程式效能最高

    Class clazz = Person.class;

  • 已知某個類的例項,呼叫該例項的getClass()方法獲取Class物件

    Class clazz = person.getClass();

  • 已知一個類的全類名,且該類在類路徑下,可通過Class類的靜態方法forName()獲取

    Class clazz = Class.forName("zr.reflection.User");

  • 內建基本資料型別可以直接用類名.Type(比如Integer.Type返回的就是int)

  • 利用ClassLoader

package zr.reflection;
public class Test01 {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person = new Student();
        System.out.println("這個人是: " + person.name);

        // 方式一:通過物件獲得
        Class c1 = person.getClass();
        System.out.println(c1.hashCode());

        // 方式二:forName獲得
        Class c2 = Class.forName("zr.reflection.Student");
        System.out.println(c2.hashCode());

        // 方式三:通過類名.class獲得
        Class c3 = Student.class;
        System.out.println(c3.hashCode());

        // 獲取父類型別
        Class c4 = c1.getSuperclass();
        System.out.println(c4);
    }
}
class Person{
    public String name;
    public Person(){}
    public Person(String name) {
        this.name = name;
    }
}
class Student extends Person{
    public Student(){
        this.name = "學生";
    }
}
package zr.reflection;
import java.lang.annotation.ElementType;
// 所有的型別的Class物件
public class Test02 {
    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;
        // void
        Class c8 = void.class;
        // Class本身
        Class c9 = 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());
    }
}
// 輸出結果:
class java.lang.Object
interface java.lang.Comparable
class [Ljava.lang.String;
class [[I
interface java.lang.Override
class java.lang.annotation.ElementType
class java.lang.Integer
void
class java.lang.Class
356573597
356573597

2.4 什麼時候會發生類初始化

類的載入過程:載入—>連結—>初始化

2.4.1 類的主動引用

  • 當虛擬機器啟動,先初始化main方法中的所有宣告的類
  • new一個類的物件
  • 呼叫類的靜態成員(除了final static靜態常量)和靜態方法
  • 使用java.lang.reflection包的方法對類進行反射呼叫
  • 當初始化一個類,如果其父類沒有被初始化,則先會初始化它的父類

2.4.2 類的被動呼叫(不會發生類的初始化)

  • 當訪問一個靜態域,只有真正宣告這個域的類才會被初始化。如:子類引用父類的靜態變數,不會導致子類的初始化
  • 通過陣列定義類引用
  • 引用常量(因為常量在連結階段就已經存入呼叫類的常量池中了)

2.5 類載入器

類載入器的作用:將class位元組碼檔案內容載入到記憶體中,並將這些靜態資料轉換成方法區的執行時資料結構,然後在堆中生成一個代表這個類的一個java.lang.Class物件,作為方法去中類資料的訪問入口。

雙親委派模型:從下到上檢測類是否被載入,從上到下嘗試載入類

package zr.reflection;

public class Test03 {
    public static void main(String[] args) throws ClassNotFoundException {
        // 獲取系統類的載入器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        // 獲取系統類載入器的父類載入器——>擴充套件類載入器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);

        // 獲取擴充套件類載入器的父類——>根載入器(C/C++寫的,所以一般獲得null)
        ClassLoader boot = parent.getParent();
        System.out.println(boot);

        // 測試自定義類是由誰載入的(由系統類載入器載入)
        ClassLoader loader = Class.forName("zr.reflection.Test01").getClassLoader();
        System.out.println(loader);

        // 測試JDK內部類是由誰載入的(由boot根載入器載入)
        loader = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(loader);

        // 檢視系統類載入器可以載入的路徑
        System.out.println(System.getProperty("java.class.path"));
        /*
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\charsets.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\deploy.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\access-bridge-64.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\cldrdata.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\dnsns.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\jaccess.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\jfxrt.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\localedata.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\nashorn.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunec.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunjce_provider.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunmscapi.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunpkcs11.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\zipfs.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\javaws.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\jce.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\jfr.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\jfxswt.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\jsse.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\management-agent.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\plugin.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\resources.jar;
        D:\Program Files\Java\jdk1.8.0_171\jre\lib\rt.jar;
        E:\idea\out\production\idea;
        E:\idea\lib\mysql-connector-java-8.0.21.jar;
        D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2020.2.2\lib\idea_rt.jar
        */
    }
}

// 輸出結果
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1540e19d
null
sun.misc.Launcher$AppClassLoader@18b4aac2
null

2.6 獲得類的執行時結構

package zr.reflection;

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

// 獲得類的資訊
public class Test04 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class clazz = Class.forName("zr.reflection.Student");

        // 獲得類的名字
        System.out.println(clazz.getName());       // 獲得包名 + 類名
        System.out.println(clazz.getSimpleName()); // 獲得類名

        // 獲得類的屬性
        Field[] fields = clazz.getFields();  // 只能找到public屬性
        fields = clazz.getDeclaredFields();  // 能獲得所有宣告的屬性
        for(Field field : fields)
            System.out.println(field);

        // 獲得類的方法
        Method[] methods = clazz.getDeclaredMethods();  // 獲取本類的所有方法(私有的也行)
        for(Method method : methods)
            System.out.println();

        // 獲得類的構造器
        Constructor[] constructors = clazz.getConstructors();
        for(Constructor constructor : constructors)
            System.out.println(constructor);
    }
}

2.7 獲得Class物件後的操作

package zr.reflection;

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

// 獲得類的資訊
public class Test04 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        // 獲得Class物件
        Class clazz = Class.forName("zr.reflection.User");
        // 構造一個物件
        User user = (User)clazz.newInstance();   // 本質上是呼叫了類的無參構造器
        System.out.println(user);

        // 通過構造器建立物件,這樣就可以間接地去使用User的有參構造器
        Constructor constructor =  clazz.getDeclaredConstructor(String.class, int.class, int.class);
        User xiaozeng = (User)constructor.newInstance("曾潤", 24, 99);
        System.out.println(xiaozeng);

        // 通過反射呼叫普通方法
        Method setName = clazz.getDeclaredMethod("setName", String.class);
        setName.invoke(xiaozeng, "範春春");
        System.out.println(xiaozeng);

        // 通過反射操作屬性
        Field name = clazz.getDeclaredField("name");
        // 首先需要設定許可權,防止private修飾的變數不能修改
        name.setAccessible(true);
        name.set(xiaozeng, "曾益達");  // 相當於賦值語句: name = "曾益達";
        System.out.println(xiaozeng);
    }
}
// 輸出
User{name='null', age=0, grade=0}
User{name='曾潤', age=24, grade=99}
User{name='範春春', age=24, grade=99}
User{name='曾益達', age=24, grade=99}

2.8 通過反射操作泛型

package zr.reflection;

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 Test05 {
    public static void test01(Map<String, User> map, List<User> list){
        System.out.println("test01");
    }
    public static Map<String, User> test02(){
        System.out.println("test02");
        return null;
    }
    public static void main(String[] args) throws NoSuchMethodException {
        Class clazz = Test05.class;
        Method method = clazz.getDeclaredMethod("test01", Map.class, List.class);
        // 獲得泛型的引數資訊
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        for(Type genericParameterType : genericParameterTypes){
            System.out.println("# " + genericParameterType);
            Type[] actualyTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
            for(Type actualyTypeArgument : actualyTypeArguments)
                System.out.println(actualyTypeArgument);
        }
    }
}
// 輸出結果
# java.util.Map<java.lang.String, zr.reflection.User>
class java.lang.String
class zr.reflection.User
# java.util.List<zr.reflection.User>
class zr.reflection.User

2.9 通過反射操作註解

package zr.reflection;

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

public class Test06{
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class clazz = Class.forName("zr.reflection.Student1");
        // 通過反射獲取類的註解
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
        // 獲得註解的value值
        Tablerun tablerun = (Tablerun) clazz.getAnnotation(Tablerun.class);
        String value = tablerun.value();
        System.out.println(value);

        // 獲得類指定的註解
        Field name = clazz.getDeclaredField("name");
        Fieldrun annotation = name.getAnnotation(Fieldrun.class);
        System.out.println(annotation.columnName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());
    }
}

@Tablerun("db_student")
class Student1{
    @Fieldrun(columnName = "db_id", type = "int", length = 10)
    private int id;
    @Fieldrun(columnName = "db_age", type = "int", length = 10)
    private int age;
    @Fieldrun(columnName = "db_name", type = "varchar", length = 3)
    private String name;
    public Student1(){}
    public Student1(int id, int age, String name){
        this.id = id;
        this.age = age;
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Student1{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

// 類名的註解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Tablerun{
    String value();
}

// 屬性的註解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Fieldrun{
    String columnName();
    String type();
    int length();
}

// 輸出結果
@zr.reflection.Tablerun(value=db_student)
db_student
db_name
varchar
3