阿里P8教你Java注解與反射

Ⅰ 什么是注解

Java 注解(Annotation)又稱 Java 標(biāo)注,是 JDK5.0 引入的一種注釋機(jī)制语婴。 Java 語言中的類关筒、方法溶握、變量、參數(shù)和包等都可以被標(biāo)注蒸播。和 Javadoc 不同睡榆,Java 標(biāo)注可以通過反射獲取標(biāo)注內(nèi)容。在編譯器生成類文件時(shí)袍榆,標(biāo)注可以被嵌入到字節(jié)碼中胀屿。Java 虛擬機(jī)可以保留標(biāo)注內(nèi)容,在運(yùn)行時(shí)可以獲取到標(biāo)注內(nèi)容 包雀。 當(dāng)然它也支持自定義 Java 標(biāo)注宿崭。

注解以@注解名的形式存在于代碼中,例如@Override才写,還可以添加一些參數(shù)值葡兑,例如@Auth(value = "super")

Ⅱ 內(nèi)置注解

Java 有10個(gè)內(nèi)置 注解琅摩,6個(gè)注解是作用在代碼上的铁孵,4個(gè)注解是負(fù)責(zé)注解其他注解的(即元注解),元注解提供對(duì)其他注解的類型說明房资。

注解 作用 作用范圍
@Override 檢查該方法是否是重寫方法蜕劝。如果發(fā)現(xiàn)其父類,或者是引用的接口中并沒有該方法時(shí)轰异,會(huì)報(bào)編譯錯(cuò)誤岖沛。 作用在代碼上
@Deprecated 標(biāo)記表示過時(shí)的,不推薦使用搭独∮は鳎可以用于修飾方法,屬性牙肝,類唉俗。如果使用被此注解修飾的方法,屬性或類配椭,會(huì)報(bào)編譯警告虫溜。 作用在代碼上
@SuppressWarnings 指示編譯器去忽略注解中聲明的警告。 作用在代碼上
@SafeVarargs Java 7 開始支持股缸,忽略任何使用參數(shù)為泛型變量的方法或構(gòu)造函數(shù)調(diào)用產(chǎn)生的警告衡楞。 作用在代碼上
@FunctionalInterface Java 8 開始支持,標(biāo)識(shí)一個(gè)匿名函數(shù)或函數(shù)式接口敦姻。 作用在代碼上
@Repeatable Java 8 開始支持瘾境,標(biāo)識(shí)某注解可以在同一個(gè)聲明上使用多次歧杏。 作用在代碼上
@Retention 標(biāo)識(shí)這個(gè)注解怎么保存,是只在代碼中迷守,還是編入class文件中犬绒,或者是在運(yùn)行時(shí)可以通過反射訪問。包含關(guān)系runtime>class>source兑凿。 作用在其他注解上懂更,即元注解
@Documented 標(biāo)記這些注解是否包含在用戶文檔中。 作用在其他注解上急膀,即元注解
@Target 標(biāo)記某個(gè)注解的使用范圍沮协,例如作用方法上,類上卓嫂,屬性上等等慷暂。 作用在其他注解上,即元注解
@Inherited 說明子類可以集成父類中的此注解晨雳,默認(rèn)注解并沒有繼承于任何子類 作用在其他注解上行瑞,即元注解

Ⅲ 自定義注解

使用@interface在這里插入代碼片定義注解,而且自動(dòng)繼承java.lang.annotation.Annotation接口餐禁。

  • 格式為 public @interface 注解名 {定義內(nèi)容}
  • 其中每一個(gè)方法實(shí)際是聲明了一個(gè)參數(shù)
  • 方法的名稱就是參數(shù)的名稱
  • 返回值類型就是參數(shù)的類型血久,而且返回值類型只能是基本類型,Class帮非,String氧吐,enum。
  • 可以通過default關(guān)鍵字來聲明參數(shù)的默認(rèn)值末盔,一般會(huì)使用空字符串或者0
  • 如果只有一個(gè)參數(shù)筑舅,一般參數(shù)名為value,而且使用注解時(shí)陨舱,賦值可以不顯示寫出參數(shù)名翠拣,直接寫參數(shù)值
  • 定義了參數(shù),如果沒有默認(rèn)值游盲,就一定要顯示賦值

Ⅳ 注解案例

import java.lang.annotation.*;

/**
 * @author Mr.nobody
 * @Description 自定義注解
 * @date 2020/8/30
 */
@Target(ElementType.METHOD) // 此注解只能用在方法上误墓。
@Retention(RetentionPolicy.RUNTIME) // 此注解保存在運(yùn)行時(shí),可以通過反射訪問益缎。
@Inherited // 說明子類可以集成父類中的此注解谜慌。
@Documented // 此注解包含在用戶文檔中。
public @interface CustomAnnotation {
    String value(); // 使用時(shí)需要顯示賦值
    int id() default 0; // 有默認(rèn)值链峭,使用時(shí)可以不賦值
}
/**
 * @author Mr.nobody
 * @Description 測(cè)試注解
 * @date 2020/8/30
 */
public class TestAnnotation {

    // @CustomAnnotation(value = "test") 只能注解在方法上畦娄,這里會(huì)報(bào)錯(cuò)
    private String str = "Hello World!";

    @CustomAnnotation(value = "test")
    public static void main(String[] args) {
        System.out.println(str);
    }
}

Ⅴ Java 反射機(jī)制

講解反射前又沾,我們先來談?wù)勳o態(tài)語言和動(dòng)態(tài)語言弊仪。

動(dòng)態(tài)語言是一類在運(yùn)行時(shí)可以改變其結(jié)構(gòu)的語言熙卡。例如新的函數(shù),對(duì)象励饵,甚至代碼可以被引進(jìn)驳癌,已有的函數(shù)可以被刪除或者結(jié)構(gòu)上的一些變化。簡(jiǎn)單說即是在運(yùn)行時(shí)代碼可以根據(jù)某些條件改變自身結(jié)構(gòu)役听。動(dòng)態(tài)語言主要C#颓鲜,Object-C,JavaScript典予,PHP甜滨,Python等。

靜態(tài)語言是運(yùn)行時(shí)結(jié)構(gòu)不可改變的瘤袖,例如Java衣摩,C,C++等捂敌。

Java不是動(dòng)態(tài)語言艾扮,但是她可以稱為準(zhǔn)動(dòng)態(tài)語言,因?yàn)镴ava可以利用反射機(jī)制獲得類似動(dòng)態(tài)語言的特性占婉,Java的動(dòng)態(tài)性讓它在編程時(shí)更加靈活泡嘴。

反射機(jī)制允許程序在執(zhí)行期借助于Reflection API取得任何類的內(nèi)部信息,并能直接操作任意對(duì)象的內(nèi)部屬性以及方法等逆济。 類在被加載完之后酌予,會(huì)在堆內(nèi)存的方法區(qū)中生成一個(gè)Class類型的對(duì)象,一個(gè)類只有一個(gè)Class對(duì)象奖慌,這個(gè)對(duì)象包含了類的結(jié)構(gòu)信息霎终。我們可以通過這個(gè)對(duì)象看到類的結(jié)構(gòu)。 例如可以通過如下方法獲取String類的Class對(duì)象:Class c = Class.forName("java.lang.String"); 當(dāng)然升薯,每個(gè)類都隱式繼承Object類莱褒,Object類有個(gè)getClass()方法也能獲取Class對(duì)象。

5.1 Java反射機(jī)制提供的功能

  1. 在運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類
  2. 在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象
  3. 在運(yùn)行時(shí)判斷任意一個(gè)類具有的成員變量和方法
  4. 在運(yùn)行時(shí)獲取泛型信息
  5. 在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的成員變量和方法
  6. 在運(yùn)行時(shí)處理注解
  7. 生成動(dòng)態(tài)代理
  8. ...

5.2 Java反射機(jī)制的優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn):實(shí)現(xiàn)動(dòng)態(tài)創(chuàng)建對(duì)象和編譯涎劈,有更加的靈活性广凸。
  • 缺點(diǎn):對(duì)性能有影響。使用反射基本上是一種解釋操作蛛枚,我們可以告訴JVM谅海,我們想要做什么然后它滿足我們的要求,這類操作總是慢于直接執(zhí)行相同的操作蹦浦。

5.3 Java反射相關(guān)的主要API

  • java.lang.Class:代表一個(gè)類
  • java.lang.reflect.Method:代表類的方法
  • java.lang.reflect.Field:代表類的成員變量
  • java.lang.reflect.Constructor:代表類的構(gòu)造器
  • ...

5.4 Class類

通過Class對(duì)象可以得知某個(gè)類的屬性扭吁,方法,構(gòu)造器,注解侥袜,以及實(shí)現(xiàn)了哪些接口等等信息蝌诡。對(duì)于每個(gè)類而言,JRE都為其保留一個(gè)不變的Class類型的對(duì)象枫吧。一個(gè)Class對(duì)象包含了特定的結(jié)構(gòu)(class浦旱,interface,enum九杂,annotation颁湖,private type,void例隆,[])的有關(guān)信息甥捺。

  • Class本身也是一個(gè)類
  • Class對(duì)象只能系統(tǒng)建立
  • 一個(gè)加載的類在JVM中只有一個(gè)Class對(duì)象
  • 一個(gè)Class對(duì)象一般對(duì)應(yīng)的是一個(gè)加載到JVM中的一個(gè).class文件
  • 每個(gè)類的實(shí)例都會(huì)記得自己是右哪個(gè)Class實(shí)例所生成的
  • 通過Class可以完整地得到一個(gè)類中的所有被加載的結(jié)構(gòu)
  • Class類是Reflection的根源,針對(duì)任何你想動(dòng)態(tài)加載镀层,運(yùn)行的類涎永,唯有先獲得相應(yīng)的Class對(duì)象

Class類的常用方法:

  • static forName(String name):返回指定類名name的Class對(duì)象
  • Object newInstance():調(diào)用缺省構(gòu)造方法,返回Class對(duì)象的一個(gè)實(shí)例
  • getName():返回Class對(duì)象所表示的實(shí)體(類鹿响,接口羡微,數(shù)組類或void)的名稱。
  • Class getSuperClass():返回當(dāng)前Class對(duì)象的父類的Class對(duì)象
  • Class[] getInterfaces():獲取當(dāng)前Class對(duì)象的接口
  • ClassLoader getClassLoader():獲取當(dāng)前類的類加載器
  • Constructor[] getConstructors():獲取一個(gè)包含某些Constructor對(duì)象的數(shù)組
  • Method getMethod(String name, Class.. T):返回一個(gè)Method對(duì)象,此對(duì)象的形參類型為paramType
  • Field[] getDeclaredFields():返回Field對(duì)象的一個(gè)數(shù)組

獲取運(yùn)行時(shí)類的完整結(jié)構(gòu)

通過反射可以獲取運(yùn)行時(shí)類的完整結(jié)構(gòu):

  1. Field
  2. Method
  3. Constructor
  4. Superclass
  5. Interface
  6. Annatation
  7. ...

調(diào)用指定的方法:Object invoke(Object obj, Object ... args)

  • 第一個(gè)Object對(duì)應(yīng)原方法的返回值,若原方法無返回值舀瓢,則返回null
  • 若原方法為靜態(tài)方法浩考,則參數(shù)obj可為null
  • 若原方法形參列表為空,則參數(shù)args為null
  • 若原方法聲明為private,則調(diào)用invoke方法前,需要顯示調(diào)用方法對(duì)象的setAccessible(true)方法,才可訪問private方法捧挺。
package com.nobody;

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

/**
 * @author Mr.nobody
 * @Description
 * @date 2020/9/4
 */
public class Test01 {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        Class<?> aClass = Class.forName("com.nobody.Student");

        System.out.println(aClass.getName());
        System.out.println(aClass.getSimpleName());

        System.out.println("-----------------------");
        System.out.println("獲取public的屬性");
        Field[] fields = aClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

        System.out.println("-----------------------");
        System.out.println("獲取全部的屬性");
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }

        System.out.println("-----------------------");
        System.out.println("獲取指定名稱的屬性");
        Field name = aClass.getDeclaredField("name");
        System.out.println(name);

        System.out.println("-----------------------");
        System.out.println("獲取本類和父類的全部public方法");
        Method[] methods = aClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }

        System.out.println("-----------------------");
        System.out.println("獲取本類的方法");
        Method[] declaredMethods = aClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }

        System.out.println("-----------------------");
        System.out.println("獲得指定名字的方法");
        Method getName = aClass.getDeclaredMethod("getName");
        System.out.println(getName);
        Method setName = aClass.getDeclaredMethod("setName", String.class);
        System.out.println(setName);

        System.out.println("-----------------------");
        System.out.println("獲得構(gòu)造器");
        Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }

        System.out.println("-----------------------");
        System.out.println("獲取指定的構(gòu)造器");
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, int.class);
        System.out.println(declaredConstructor);

        System.out.println("-----------------------");
        System.out.println("生成類的實(shí)例");
        Student student = (Student) aClass.newInstance();
        Student student1 = (Student) declaredConstructor.newInstance("小明", 20);
        System.out.println(student);
        System.out.println(student1);

        System.out.println("-----------------------");
        System.out.println("調(diào)用實(shí)例的方法");
        setName.invoke(student, "小花");
        System.out.println(student);

        System.out.println("-----------------------");
        System.out.println("使用實(shí)例的屬性");
        name.setAccessible(true); // 因?yàn)閟tudent的name變量是私有的,所以要加此行代碼尿瞭,關(guān)閉安全檢測(cè)
        name.set(student, "小紅");
        System.out.println(student);
    }

}

class Student {

    private String name;

    public int age;

    public String getName() {
        return name;
    }

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

    private void testMethod01() {

    }

    public Student() {
    }

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

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

輸出結(jié)果:

com.nobody.Student
Student
-----------------------
獲取public的屬性
public int com.nobody.Student.age
-----------------------
獲取全部的屬性
private java.lang.String com.nobody.Student.name
public int com.nobody.Student.age
-----------------------
獲取指定名稱的屬性
private java.lang.String com.nobody.Student.name
-----------------------
獲取本類和父類的全部public方法
public java.lang.String com.nobody.Student.toString()
public java.lang.String com.nobody.Student.getName()
public void com.nobody.Student.setName(java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
-----------------------
獲取本類的方法
public java.lang.String com.nobody.Student.toString()
public java.lang.String com.nobody.Student.getName()
public void com.nobody.Student.setName(java.lang.String)
private void com.nobody.Student.testMethod01()
-----------------------
獲得指定名字的方法
public java.lang.String com.nobody.Student.getName()
public void com.nobody.Student.setName(java.lang.String)
-----------------------
獲得構(gòu)造器
public com.nobody.Student()
public com.nobody.Student(java.lang.String,int)
-----------------------
獲取指定的構(gòu)造器
public com.nobody.Student(java.lang.String,int)
-----------------------
生成類的實(shí)例
Student{name='null', age=0}
Student{name='小明', age=20}
-----------------------
調(diào)用實(shí)例的方法
Student{name='小花', age=0}
-----------------------
使用實(shí)例的屬性
Student{name='小紅', age=0}

獲取Class類的實(shí)例

  1. 若已知具體的類闽烙,可通過class屬性獲取,此方法最為安全可靠声搁,程序性能最高黑竞。Class clazz = User.class;
  2. 若已知某個(gè)類的實(shí)例,可調(diào)用此實(shí)例的getClass()方法獲取疏旨。Class clazz = user.getClass();
  3. 若已知一個(gè)類的全類名很魂,且此類在類路徑下,可通過Class類的靜態(tài)方法forName()方法獲取檐涝,可能會(huì)拋出ClassNotFoundException遏匆。Class clazz = Class.forName("com.nobody.User");
  4. 內(nèi)置基本數(shù)據(jù)類型可以直接通過類名.Type獲取
  5. 可以通過ClassLoader獲取
package com.nobody;

/**
 * @author Mr.nobody
 * @Description
 * @date 2020/9/2
 */
public class User extends Person {

    private String name;

    public static void main(String[] args) throws ClassNotFoundException {

        User user = new User();

        Class<User> userClass = User.class;
        Class<? extends User> aClass = user.getClass();
        Class<?> aClass1 = Class.forName("com.nobody.User");

        System.out.println(userClass.hashCode());
        System.out.println(aClass.hashCode());
        System.out.println(aClass1.hashCode());

        Class<? super User> superclass = userClass.getSuperclass();
        System.out.println(superclass);

        Class<Integer> type = Integer.TYPE;
        System.out.println(type);

    }
}

class Person {

}

輸出結(jié)果:

685325104
685325104
685325104
class com.nobody.Person
int
復(fù)制代碼

有Class對(duì)象的類型

  1. class:外部類法挨,成員(成員內(nèi)部類,靜態(tài)內(nèi)部類)幅聘,局部內(nèi)部類凡纳,匿名內(nèi)部類。
  2. interface:接口
  3. []:數(shù)組
  4. enum:枚舉
  5. annotation:注解
  6. primitive type:基本數(shù)據(jù)類型(例如Integer喊暖,Double等)
  7. void:空類型
package com.nobody;

import java.lang.annotation.Documented;
import java.util.List;

/**
 * @author Mr.nobody
 * @Description
 * @date 2020/9/2
 */
public class ClassTest {

    public static void main(String[] args) {
        Class<Object> objectClass = Object.class;
        Class<List> listClass = List.class;
        Class<String[]> aClass = String[].class;
        Class<String[][]> aClass1 = String[][].class;
        Class<Override> overrideClass = Override.class;
        Class<Documented> documentedClass = Documented.class;
        Class<Integer> integerClass = Integer.class;
        Class<Void> voidClass = void.class;
        Class<Class> classClass = Class.class;

        System.out.println(objectClass);
        System.out.println(listClass);
        System.out.println(aClass);
        System.out.println(aClass1);
        System.out.println(overrideClass);
        System.out.println(documentedClass);
        System.out.println(integerClass);
        System.out.println(voidClass);
        System.out.println(classClass);
    }
}

輸出結(jié)果:

class java.lang.Object
interface java.util.List
class [Ljava.lang.String;
class [[Ljava.lang.String;
interface java.lang.Override
interface java.lang.annotation.Documented
class java.lang.Integer
void
class java.lang.Class

Ⅵ 類加載過程

當(dāng)程序主動(dòng)使用某個(gè)類時(shí),如果此類還未被加載到內(nèi)存中撕瞧,則系統(tǒng)會(huì)通過以下三個(gè)步驟來對(duì)此類進(jìn)行初始化陵叽。

  1. 類的加載(load):將類的class文件字節(jié)碼加載到內(nèi)存中,并將這些靜態(tài)數(shù)據(jù)轉(zhuǎn)換為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)丛版,然后生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象巩掺,此過程由類加載器完成。
  2. 類的鏈接(Link):將類的二進(jìn)制代碼合并到JVM的運(yùn)行狀態(tài)之中的過程页畦。
    1. 驗(yàn)證:確保加載的類的信息符合JVM規(guī)范胖替,確保安全;
    2. 準(zhǔn)備:正式為類變量(static)分配內(nèi)存并設(shè)置變量默認(rèn)初始值的階段豫缨,這些內(nèi)存都將在方法區(qū)中進(jìn)行分配独令;
    3. 解析:虛擬機(jī)常量池內(nèi)的符號(hào)引用(常量名)替換為直接引用(地址)的過程;
  3. 類的初始化(Initialize):JVM對(duì)類進(jìn)行初始化好芭。
    1. 執(zhí)行類構(gòu)造器<clinit>()方法的過程燃箭,類構(gòu)造器<clinit>()方法是由編譯器自動(dòng)收集類中所有類變量的賦值動(dòng)作和靜態(tài)代碼塊中的語句合并產(chǎn)生的。(類構(gòu)造器是構(gòu)造類信息的舍败,不是構(gòu)造該類對(duì)象的構(gòu)造器)
    2. 當(dāng)初始化一個(gè)類的時(shí)候招狸,如果發(fā)現(xiàn)其父類還沒被初始化,則先進(jìn)行父類初始化邻薯。
    3. 虛擬機(jī)會(huì)保證一個(gè)類的<clinit>()方法在多線程環(huán)境中被正確加鎖和同步裙戏。
package com.nobody;

/**
 * @author Mr.nobody
 * @Description
 * @date 2020/9/3
 */
public class TestA {

    public static void main(String[] args) {
        Child child = new Child();
        System.out.println(">>> age = " + Child.age);
    }
}

class Child extends Father {

    static {
        System.out.println(">>> Child static block...");
        age = 20;
    }

    static int age = 18;

    public Child() {
        System.out.println(">>> Child constructor...");
    }

}

class Father {

    static {
        System.out.println(">>> Father static block...");
    }

    public Father () {
        System.out.println(">>> Father constructor...");
    }

}

輸出結(jié)果為:

>>> Father static block...
>>> Child static block...
>>> Father constructor...
>>> Child constructor...
>>> age = 18

如果Child類的靜態(tài)代碼塊和static int age = 18;語句位置交換,則最后age的值為20(0 -> 18 -> 20)厕诡。因?yàn)榫幾g器會(huì)按定義順序自動(dòng)收集類中所有類變量的賦值動(dòng)作靜態(tài)代碼塊中的語句合并產(chǎn)生類構(gòu)造器<clinit>()方法累榜。

class Child extends Father {

    static int age = 18;

    static {
        System.out.println(">>> Child static block...");
        age = 20;
    }

    public Child() {
        System.out.println(">>> Child constructor...");
    }
}

輸出結(jié)果為:

>>> Father static block...
>>> Child static block...
>>> Father constructor...
>>> Child constructor...
>>> age = 20

5.1 何時(shí)會(huì)發(fā)生類初始化

類的主動(dòng)引用(一定會(huì)發(fā)生類的初始化)

  • 當(dāng)虛擬機(jī)啟動(dòng),先初始化main方法所在的類
  • new一個(gè)類的對(duì)象
  • 調(diào)用類的靜態(tài)成員(除final常量外)和靜態(tài)方法
  • 使用java.lang.reflect包的方法對(duì)類進(jìn)行反射調(diào)用
  • 當(dāng)初始化一個(gè)類灵嫌,如果其父類未被初始化信柿,則先初始化其父類

類的被動(dòng)引用(不會(huì)發(fā)生類的初始化)

  • 當(dāng)訪問一個(gè)靜態(tài)域時(shí),只有真正聲明這個(gè)域的類才會(huì)被初始化醒第。例如渔嚷,當(dāng)通過子類引用父類的靜態(tài)變量或靜態(tài)方法,不會(huì)導(dǎo)致子類初始化稠曼。Child.age;形病,而age屬性是在父類定義的。
  • 通過數(shù)組定義類引用,也不會(huì)觸發(fā)此類的初始化漠吻。Child[] childs = new Child[5];
  • 引用常量不會(huì)觸發(fā)此類的初始化量瓜,因?yàn)槌A吭阪溄与A段就存入調(diào)用類的常量池中了。

Ⅶ 類加載器Classloader

類加載器的作用:將class文件字節(jié)碼加載到內(nèi)存中途乃,并將這些靜態(tài)數(shù)據(jù)轉(zhuǎn)換為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)绍傲,然后在堆中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象,作為方法區(qū)中類數(shù)據(jù)的訪問入口耍共。

類緩存:標(biāo)準(zhǔn)的JavaSE類加載器可以按要求查找類烫饼,但一旦某個(gè)類被加載到類加載器中,它將加載(緩存)一段時(shí)間试读。不過JVM垃圾回收機(jī)制可以回收這些Class對(duì)象杠纵。


image.png
package com.nobody;

/**
 * @author Mr.nobody
 * @Description
 * @date 2020/9/3
 */
public class TestClassLoader {

    public static void main(String[] args) throws ClassNotFoundException {

        // 獲取系統(tǒng)類的加載器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        // 獲取系統(tǒng)類加載器的父加載器,也就是擴(kuò)展類加載器
        ClassLoader parent = systemClassLoader.getParent();
        // 獲取擴(kuò)展類加載器的父加載器钩骇,也就是根加載器比藻,用C或C++編寫
        ClassLoader parent1 = parent.getParent();
        System.out.println(systemClassLoader);
        System.out.println(parent);
        System.out.println(parent1);

        System.out.println("----------------------------------------");

        // 測(cè)試我們定義的類是哪個(gè)類加載器加載的
        ClassLoader classLoader = Class.forName("com.nobody.TestClassLoader").getClassLoader();
        // 測(cè)試JDK內(nèi)置的的類是哪個(gè)類加載器加載的
        ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader);
        System.out.println(classLoader1);

        // 獲取系統(tǒng)類加載器可以加載的路徑
       System.out.println(System.getProperty("java.class.path"));
    }

}

輸出結(jié)果:

sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1b6d3586
null  // 根加載器(引導(dǎo)類加載器)我們獲取不到,所以是null
----------------------------------------
sun.misc.Launcher$AppClassLoader@18b4aac2
null

C:\Program Files\Java\jdk1.8.0_202\jre\lib\charsets.jar;
C:\Program Files\Java\jdk1.8.0_202\jre\lib\deploy.jar;
C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\access-bridge-64.jar;
C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\cldrdata.jar;
C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\dnsns.jar;
C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\jaccess.jar;
C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\jfxrt.jar;
C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\localedata.jar;
C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\nashorn.jar;
C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\sunec.jar;
C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\sunjce_provider.jar;
C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\sunmscapi.jar;
C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\sunpkcs11.jar;
C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\zipfs.jar;
C:\Program Files\Java\jdk1.8.0_202\jre\lib\javaws.jar;
C:\Program Files\Java\jdk1.8.0_202\jre\lib\jce.jar;
C:\Program Files\Java\jdk1.8.0_202\jre\lib\jfr.jar;
C:\Program Files\Java\jdk1.8.0_202\jre\lib\jfxswt.jar;
C:\Program Files\Java\jdk1.8.0_202\jre\lib\jsse.jar;
C:\Program Files\Java\jdk1.8.0_202\jre\lib\management-agent.jar;
C:\Program Files\Java\jdk1.8.0_202\jre\lib\plugin.jar;
C:\Program Files\Java\jdk1.8.0_202\jre\lib\resources.jar;
C:\Program Files\Java\jdk1.8.0_202\jre\lib\rt.jar;
D:\IdeaProjects\nobody\project02\out\production\hello-world;
D:\devTools\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar

Ⅷ 性能對(duì)比分析

Method倘屹,F(xiàn)ield银亲,Constructor都由setAccessible()方法,它的作用是開啟或禁用訪問安全檢查纽匙。如果代碼中用到反射群凶,而且此代碼被頻繁調(diào)用,為了提高反射效率哄辣,則最好禁用訪問安全檢查请梢,即設(shè)置為true。

package com.nobody;

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

/**
 * @author Mr.nobody
 * @Description
 * @date 2020/9/5
 */
public class Test02 {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        test01();
        test02();
        test03();
    }

    public static void test01() {
        Teacher t = new Teacher();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            t.getName();
        }
        long end = System.currentTimeMillis();
        System.out.println("普通方式執(zhí)行10億次消耗:" + (end - start) + "ms");
    }

    public static void test02() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Teacher t = new Teacher();
        Class<?> aClass = Class.forName("com.nobody.Teacher");
        Method getName = aClass.getDeclaredMethod("getName");
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(t, null);
        }
        long end = System.currentTimeMillis();
        System.out.println("反射方式執(zhí)行10億次消耗:" + (end - start) + "ms");
    }

    public static void test03() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Teacher t = new Teacher();
        Class<?> aClass = Class.forName("com.nobody.Teacher");
        Method getName = aClass.getDeclaredMethod("getName");
        getName.setAccessible(true);
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(t, null);
        }
        long end = System.currentTimeMillis();
        System.out.println("關(guān)閉安全檢查反射方式執(zhí)行10億次消耗:" + (end - start) + "ms");
    }

}

class Teacher {

    private String name;

    public String getName() {
        return name;
    }
}

輸出結(jié)果:

普通方式執(zhí)行1億次消耗:6ms
反射方式執(zhí)行1億次消耗:4294ms
關(guān)閉安全檢查反射方式執(zhí)行1億次消耗:1963ms

Ⅸ 反射操作泛型

Java采用泛型擦除的機(jī)制來引入泛型力穗,Java中的泛型僅僅是給編譯器javac使用的毅弧,確保數(shù)據(jù)的安全性和免去強(qiáng)制類型轉(zhuǎn)換問題,但是一旦編譯完成当窗,所有和泛型有關(guān)的類型全部擦除够坐。

為了通過反射操作這些類型,Java新增了ParameterizedType崖面,GenericArrayType元咙,TypeVariableWildcardType幾種類型來代表不能被歸到Class類中的類型但是又和原始類型齊名的類型。

  • ParameterizedType:表示一種參數(shù)化類型巫员,比如Collection<String>
  • GenericArrayType:表示種元素類型是參數(shù)化類型或者類型變量的數(shù)組類型
  • TypeVariable:是各種類型變量的公共父接口
  • WildcardType:代表種通配符類型表達(dá)式
package com.nobody;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;

/**
 * @author Mr.nobody
 * @Description
 * @date 2020/9/5
 */
public class Test03 {

    public void test01(Map<String, Integer> map, Person person) {

    }

    public Map<String, Student> test02() {
        return null;
    }

    public static void main(String[] args) throws NoSuchMethodException {
        Method test01 = Test03.class.getDeclaredMethod("test01", Map.class, Person.class);
        // 獲取方法test01的參數(shù)類型
        Type[] genericParameterTypes = test01.getGenericParameterTypes();
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println("<<< " + genericParameterType);
            // 如果參數(shù)類型等于參數(shù)化類型
            if (genericParameterType instanceof ParameterizedType) {
                // 獲得真實(shí)參數(shù)類型
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);
                }
            }
        }

        Method test02 = Test03.class.getDeclaredMethod("test02", null);
        // 獲取方法test02的返回值類型
        Type genericReturnType = test02.getGenericReturnType();
        System.out.println("<<< " + genericReturnType);
        // 如果參數(shù)類型等于參數(shù)化類型
        if (genericReturnType instanceof ParameterizedType) {
            // 獲得真實(shí)參數(shù)類型
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument);
            }
        }

    }
}

輸出結(jié)果:

<<< java.util.Map<java.lang.String, java.lang.Integer>
class java.lang.String
class java.lang.Integer
<<< class com.nobody.Person
<<< java.util.Map<java.lang.String, com.nobody.Student>
class java.lang.String
class com.nobody.Student

Ⅹ 反射操作注解

通過反射我們可以獲取代碼中的注解庶香,并且獲取注解的屬性值,下面演示如何獲取類和屬性的注解简识,解析和數(shù)據(jù)庫映射的相關(guān)信息赶掖。

package com.nobody;

import java.lang.annotation.*;

/**
 * @author Mr.nobody
 * @Description
 * @date 2020/9/5
 */
public class Test04 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class<?> aClass = Class.forName("com.nobody.Book");
        // 獲得Book類的注解
        Annotation[] annotations = aClass.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
        // 獲取類的指定注解感猛,并且獲取注解的值
        Table annotation = aClass.getAnnotation(Table.class);
        String value = annotation.value();
        System.out.println("Book類映射的數(shù)據(jù)庫表名:" + value);

        java.lang.reflect.Field bookName = aClass.getDeclaredField("bookName");
        Field annotation1 = bookName.getAnnotation(Field.class);
        System.out.println("bookName屬性映射的數(shù)據(jù)庫字段屬性 - 列名:" + annotation1.colName()
                + ",類型:" + annotation1.type() + ",長度:" + annotation1.length());
        java.lang.reflect.Field price = aClass.getDeclaredField("price");
        Field annotation2 = price.getAnnotation(Field.class);
        System.out.println("price屬性映射的數(shù)據(jù)庫字段屬性 - 列名:" + annotation2.colName()
                + ",類型:" + annotation2.type() + ",長度:" + annotation2.length());
    }
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table {
    String value();
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Field {
    String colName();

    String type();

    int length();
}

@Table("t_book")
class Book {
    @Field(colName = "name", type = "varchar", length = 15)
    String bookName;
    @Field(colName = "price", type = "int", length = 10)
    int price;

}

輸出結(jié)果:

@com.nobody.Table(value=t_book)
Book類映射的數(shù)據(jù)庫表名:t_book
bookName屬性映射的數(shù)據(jù)庫字段屬性 - 列名:name,類型:varchar,長度:15
price屬性映射的數(shù)據(jù)庫字段屬性 - 列名:price,類型:int,長度:10

我是陳皮,一個(gè)在互聯(lián)網(wǎng) Coding 的 ITer奢赂,微信搜索「陳皮的JavaLib」第一時(shí)間閱讀最新文章喔陪白!

本次分享到此結(jié)束啦~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市膳灶,隨后出現(xiàn)的幾起案子咱士,更是在濱河造成了極大的恐慌,老刑警劉巖轧钓,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件序厉,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡聋迎,警方通過查閱死者的電腦和手機(jī)脂矫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門枣耀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來霉晕,“玉大人,你說我怎么就攤上這事捞奕∥撸” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵颅围,是天一觀的道長伟葫。 經(jīng)常有香客問我,道長院促,這世上最難降的妖魔是什么筏养? 我笑而不...
    開封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮常拓,結(jié)果婚禮上渐溶,老公的妹妹穿的比我還像新娘。我一直安慰自己弄抬,他們只是感情好茎辐,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著掂恕,像睡著了一般拖陆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上懊亡,一...
    開封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天依啰,我揣著相機(jī)與錄音,去河邊找鬼店枣。 笑死孔飒,一個(gè)胖子當(dāng)著我的面吹牛灌闺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播坏瞄,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼桂对,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了鸠匀?” 一聲冷哼從身側(cè)響起蕉斜,我...
    開封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎缀棍,沒想到半個(gè)月后宅此,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡爬范,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年父腕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片青瀑。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡璧亮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出斥难,到底是詐尸還是另有隱情枝嘶,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布哑诊,位于F島的核電站群扶,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏镀裤。R本人自食惡果不足惜竞阐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望暑劝。 院中可真熱鬧骆莹,春花似錦、人聲如沸铃岔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽毁习。三九已至智嚷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間纺且,已是汗流浹背盏道。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留载碌,地道東北人猜嘱。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓衅枫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親朗伶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子弦撩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容