免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
關(guān)于Java你不知道的那些事之Java注解和反射

作者:輕狂書生FS

https://blog.csdn.net/LookForDream_/

前言

我們在實(shí)際的工作中,項(xiàng)目使用的框架中會經(jīng)常遇到注解和反射。但是我們大部分同學(xué)都僅僅限于知道這是注解和反射,卻不太了解它們底層的原理實(shí)現(xiàn)。對這部分知識了解有點(diǎn)淺薄和片面,所以這邊文章將會深入理解什么是注解和反射。讓我們達(dá)到“知其然,知其所以然”的目的。

什么是注解

  • Annotation是JDK5.0開始引入的新技術(shù)

  • Annotation的作用

  • 不是程序本身,可以對程序做出解釋(這一點(diǎn)和注釋沒有什么區(qū)別)

  • 可以被其它程序,比如編譯器讀取

  • Annotation的格式

  • 注解以 @注釋名 在代碼中存在的,還可以添加一些參數(shù)值

  • 例如:@SuppressWarnings(value = 'unchecked')

  • Annotation在那里使用?

  • 可以附加在package、class、method、field等上面,相當(dāng)于給他們添加了額外的輔助信息

  • 通過反射機(jī)制變成實(shí)現(xiàn)對這些元數(shù)據(jù)的控制

內(nèi)置注解

  • @Override:定義在 java.lang.Override中,此注釋只適用于修飾方法,表示一個(gè)方法聲明打算重寫超類中的另一個(gè)方法聲明

  • @Deprecated:定義在java.lang.Deprecated中,此注釋可以用于修飾方法,屬性,類,表示不鼓勵(lì)程序員使用這樣的元素,通常是因?yàn)樗芪kU(xiǎn),或者存在更好的選擇

  • @SuppressWarnings:定義在java.lang.SuppressWarnings中,用來抑制編譯時(shí)的警告信息,與前面的兩個(gè)注釋不同,你需要額外添加一個(gè)參數(shù)才能正確使用,這些參數(shù)都是已經(jīng)定義好了的,我們選擇性的使用就好了。

  • @SuppressWarnings(“all”)

  • @SuppressWarnings(“unchecked”)

  • @SuppressWarnings(value={“unchecked”, “deprecation”})

元注解

元注解的作用就是負(fù)責(zé)注解其它注解,Java定義了4個(gè)標(biāo)準(zhǔn)的meta-annotation類型,他們被用來提供對其它annotation類型作說明。

這些類型和它們所支持的類在 java.lang.annotation包可以找到 @Target 、@Retention@Documented、@Inherited

  • @Target:用于描述注解的使用范圍,即:被描述的注解可以在什么地方使用

  • @Retention:表示需要什么保存該注釋信息,用于描述注解的生命周期

  • 級別范圍:Source < Class < Runtime

  • @Document:說明該注解被包含在java doc中

  • @Inherited:說明子類可以集成父類中的注解

示例

/**
 * 元注解
 *
 * @author: 輕狂書生FS
 */

@MyAnnotation
public class MateAnnotationDemo {

}

/**
 * 定義一個(gè)注解
 */

@Target(value={ElementType.METHOD, ElementType.TYPE})  // target表示我們注解應(yīng)用的范圍,在方法上,和類上有效
@Retention(RetentionPolicy.RUNTIME)   // Retention:表示我們的注解在什么時(shí)候還有效,運(yùn)行時(shí)候有效
@Documented   // 表示說我們的注解是否生成在java doc中
@Inherited   // 表示子類可以繼承父類的注解
@interface MyAnnotation {

}

自定義注解

使用 @interface自定義注解時(shí),自動繼承了 java.lang.annotation.Annotation接口

  • @interface 用來聲明一個(gè)注解,格式:public @interface 注解名 {定義內(nèi)容

  • 其中的每個(gè)方法實(shí)際上是申明了一個(gè)配置參數(shù)

  • 方法的名稱j就是參數(shù)的類型

  • 返回值類型就是參數(shù)的類型(返回值只能是基本數(shù)據(jù)類型,Class,String,enum)

  • 通過default來申明參數(shù)的默認(rèn)值

  • 如果只有一個(gè)參數(shù)成員,一般參數(shù)名為 value

  • 注解元素必須要有值,我們定義元素時(shí),經(jīng)常使用空字符串或者0作為默認(rèn)值

/**
 * 自定義注解
 *
 * @author: 輕狂書生FS
 */

public class MateAnnotationDemo {

    // 注解可以顯示賦值,如果沒有默認(rèn)值,我們就必須給注解賦值
    @MyAnnotation(schools = {'大學(xué)'})
    public void test(){

    }

}

/**
 * 定義一個(gè)注解
 */

@Target(value={ElementType.METHOD, ElementType.TYPE})  // target表示我們注解應(yīng)用的范圍,在方法上,和類上有效
@Retention(RetentionPolicy.RUNTIME)   // Retention:表示我們的注解在什么時(shí)候還有效,運(yùn)行時(shí)候有效
@Documented   // 表示說我們的注解是否生成在java doc中
@Inherited   // 表示子類可以繼承父類的注解
@interface MyAnnotation {

    // 注解的參數(shù):參數(shù)類型 + 參數(shù)名()
    String name() default '';

    int age() default 0;

    // 如果默認(rèn)值為-1,代表不存在
    int id() default -1;

    String[] schools();
}

反射機(jī)制

動態(tài)語言與靜態(tài)語言

動態(tài)語言

動態(tài)語言是一類在運(yùn)行時(shí)可以改變其結(jié)構(gòu)的語言:例如新的函數(shù),對象,甚至代碼可以被引進(jìn),已有的函數(shù)可以被刪除或是其它結(jié)構(gòu)上的變化。通俗點(diǎn)說就是在運(yùn)行時(shí)代碼可以根據(jù)某些條件改變自身結(jié)構(gòu)

主要的動態(tài)語言有:Object-c、C#、JavaScript、PHP、Python等

靜態(tài)語言

與動態(tài)語言相比,運(yùn)行時(shí)結(jié)構(gòu)不可變的語言就是靜態(tài)語言。例如Java、C、C++

Java不是動態(tài)語言,但是Java可以稱為“準(zhǔn)動態(tài)語言”。即Java有一定的動態(tài)性,我們可以利用反射機(jī)制來獲取類似于動態(tài)語言的 特性,Java的動態(tài)性讓編程的時(shí)候更加靈活。

搜索Java知音公眾號,回復(fù)“后端面試”,送你一份Java面試題寶典.pdf

Java反射機(jī)制概述

什么是反射

Java Reflection:Java反射是Java被視為動態(tài)語言的關(guān)鍵,反射機(jī)制運(yùn)行程序在執(zhí)行期借助于Reflection API 去的任何類內(nèi)部的信息,并能直接操作任意對象的內(nèi)部屬性及方法。

Class c = Class.forName('java.lang.String')

在加載完類后,在堆內(nèi)存的方法區(qū)就產(chǎn)生了一個(gè)Class類型的對象(一個(gè)類只有一個(gè)Class對象),這個(gè)對象就包含了完整的類的結(jié)構(gòu)信息,我們可以通過這個(gè)對象看到類的結(jié)構(gòu),這個(gè)對象就像一面鏡子,透過這個(gè)鏡子看到類的結(jié)構(gòu),所以我們形象的稱之為:反射

在這里插入圖片描述

tip:反射可以獲取到private修飾的成員變量和方法

反射的應(yīng)用

  • 在運(yùn)行時(shí)判斷任意一個(gè)對象所屬類

  • 在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對象

  • 在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法

  • 在運(yùn)行時(shí)獲取泛型信息

  • 在運(yùn)行時(shí)調(diào)用任意一個(gè)對象的成員變量和方法

  • 在運(yùn)行時(shí)候處理注解

  • 生成動態(tài)代理

Java反射的優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn):可以實(shí)現(xiàn)動態(tài)創(chuàng)建對象和編譯,體現(xiàn)出很大的靈活性

  • 缺點(diǎn):對性能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什么并且它滿足我們的要求,這類操作總是慢于直接執(zhí)行相同的操作。也就是說new創(chuàng)建和對象,比反射性能更高

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

  • java.lang.Class:代表一個(gè)類

  • java.lang.reflect.Method:代表類的方法

  • java.lang.reflect.Field:代表類的成員變量

  • java.lang.reflect.Constructor:代表類的構(gòu)造器

理解Class類并獲取Class實(shí)例

Class類

我們下面通過Class.forName來獲取一個(gè)實(shí)例對象

/**
 * 反射Demo
 *
 * @author: 輕狂書生FS
 */

public class ReflectionDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        // 通過反射獲取類的Class對象
        Class c1 = Class.forName('com.moxi.interview.study.annotation.User');
        Class c2 = Class.forName('com.moxi.interview.study.annotation.User');
        Class c3 = Class.forName('com.moxi.interview.study.annotation.User');
        System.out.println(c1.hashCode());
        System.out.println(c2.hashCode());
        System.out.println(c3.hashCode());


    }
}

/**
 * 實(shí)體類:pojo,entity
 */

class User {
    private String name;
    private int id;
    private int age;

    public User() {

    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        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;
    }

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

上面我們通過反射獲取了三個(gè)對象,我們輸出對應(yīng)對象的hashcode碼,會發(fā)現(xiàn)

1173230247
1173230247
1173230247

它們的hashcode碼是一樣的,這就說明了:

  • 一個(gè)類在內(nèi)存中只有一個(gè)Class對象

  • 一個(gè)類被加載后,類的整體結(jié)構(gòu)都會被封裝在Class對象中

在Object類中定義了以下的方法,此方法將被所有子類繼承

public final Class getClass()

以上方法的返回值的類型是一個(gè)Class類,此類是Java反射的源頭,實(shí)際上所謂反射從程序的運(yùn)行結(jié)果來看也很好理解,即:可以通過對象反射求出類的名稱。

也就是說,我們通過對象來獲取到它的Class,相當(dāng)于逆過程

通過對照鏡子我們可以得到的信息:某個(gè)類的屬性,方法和構(gòu)造器,某個(gè)類到底實(shí)現(xiàn)了那些接口。對于每個(gè)類而言,JRE都為其保留一個(gè)不變的Class類型對象,一個(gè)CLass對象包含了特定某個(gè)結(jié)構(gòu)的有關(guān)信息

  • Class本身也是一個(gè)類

  • Class對象只能由系統(tǒng)建立對象

  • 一個(gè)加載的類在JVM中只會有一個(gè)Class實(shí)例

  • 一個(gè)Class對象對應(yīng)的是一個(gè)加載到JVM中的一個(gè).class文件

  • 每個(gè)類的實(shí)例都會記得自己是由哪個(gè)Class實(shí)例所生成

  • 通過Class可以完整地得到一個(gè)類中所有被加載的結(jié)構(gòu)

  • Class類是Reflection的根源,針對任何你想動態(tài)加載、運(yùn)行的類、唯有先獲得相應(yīng)的Class對象

Class類常用的方法

  • ClassforName(String name):返回指定類name的Class對象

  • newInstance():調(diào)用缺省構(gòu)造函數(shù),返回Class對象的一個(gè)實(shí)例

  • getName():返回此Class對象所表示的實(shí)體(類,接口,數(shù)組或void)的名稱

  • getSuperClass():返回當(dāng)前Class對象的父類Class對象

  • getinterfaces():返回當(dāng)前對象的接口

  • getClassLoader():返回該類的類加載器

  • getConstructors():返回一個(gè)包含某些Constructor對象的數(shù)組

  • getMethod(String name, Class… T):返回一個(gè)Method對象,此對象的形參類型為paramsType

  • getDeclaredFields():返回Field對象的一個(gè)數(shù)組

獲取對象實(shí)例的方法

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

  • Class clazz = Person.class;

  • 已知某個(gè)類的實(shí)例,調(diào)用該實(shí)例的getClass()方法獲取Class對象

  • Class clazz = person.getClass()

  • 已經(jīng)一個(gè)類的全類名,且該類在類路徑下,可以通過Class類的靜態(tài)方法forName()獲取,HIA可能拋出ClassNotFoundException

  • Class clazz = Class.forName(“demo01.Sutdent”)

  • 內(nèi)置數(shù)據(jù)類型可以直接通過 類名.Type

  • 還可以利用ClassLoader

/**
 * Class類創(chuàng)建的方式
 *
 * @author: 輕狂書生FS
 */

class Person {
    public 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 = '學(xué)生';
    }
}

class Teacher extends Person {
    public Teacher() {
        this.name = '老師';
    }
}


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

        Person person = new Student();
        System.out.println('這個(gè)人是:' + person.name);

        // 方式1:通過對象獲得
        Class c1 = person.getClass();
        System.out.println('c1:' + c1.hashCode());

        //方式2:通過forName獲得
        Class c2 = Class.forName('com.moxi.interview.study.annotation.Student');
        System.out.println('c2:' + c2.hashCode());

        // 方式3:通過類名獲?。ㄗ顬楦咝В?/span>
        Class c3 = Student.class;
        System.out.println('c3:' + c3.hashCode());

        // 方式4:基本內(nèi)置類型的包裝類,都有一個(gè)Type屬性
        Class c4 = Integer.TYPE;
        System.out.println(c4.getName());

        // 方式5:獲取父類類型
        Class c5 = c1.getSuperclass();
    }
}

哪些類型可以有Class對象

class:外部類,成員(成員內(nèi)部類,靜態(tài)內(nèi)部類),局部內(nèi)部類,匿名內(nèi)部類

interface:接口

[]:數(shù)組

enum:枚舉

annotation:注解@interface

primitive type:基本數(shù)據(jù)類型

void

/**
 * 獲取Class的方式
 *
 * @author: 輕狂書生FS
 */

public class GetClassDemo {
    public static void main(String[] args) {
        Class c1 = Object.class// 類
        Class c2 = Comparable.class// 接口
        Class c3 = String[].class// 數(shù)組
        Class c4 = int[][].class// 二維數(shù)組
        Class c5 = Override.class// 注解
        Class c6 = ElementType.class// 枚舉
        Class c7 = Integer.class// 基本數(shù)據(jù)類型
        Class c8 = void.class// void,空數(shù)據(jù)類型
        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);
    }
}

最后運(yùn)行結(jié)果為:

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

同時(shí)需要注意,只要類型和維度一樣,那就是同一個(gè)Class對象

int [] a = new int[10];
int [] b = new int[10];
System.out.println(a.getClass().hashCode());
System.out.println(b.getClass().hashCode());

這兩個(gè)的hashcode是一樣的

Java內(nèi)存分析

java內(nèi)存分為以下三部分

  • 存放new的對象和數(shù)組

  • 可以被所有的線程共享,不會存放別的對象引用

  • 存放基本變量(會包含這個(gè)基本類型的具體數(shù)值)

  • 引用對象的變量(會存放這個(gè)引用在對堆里面的具體地址)

  • 方法區(qū)

  • 可以被所有線程共享

  • 包含了所有的class和static變量

類的加載與ClassLoader的理解

類加載過程

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

在這里插入圖片描述
  • 加載:將class文件字節(jié)碼內(nèi)容加載到內(nèi)存,并將這些靜態(tài)數(shù)據(jù)轉(zhuǎn)換成方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu),然后生成一個(gè)代表這個(gè)類的 java.lang.Class 對象。

  • 鏈接:將Java類的二進(jìn)制代碼合并到JVM的運(yùn)行狀態(tài)之中的過程。

  • 驗(yàn)證:確保加載的類信息符合JVM規(guī)范,沒有安全方面的問題

  • 準(zhǔn)備:正式為類變量(static)分配內(nèi)存并設(shè)置類變量默認(rèn)初始值的階段,這些內(nèi)存都將在方法區(qū)中進(jìn)行分配。

  • 解析:虛擬機(jī)常量池的符號引用(常量名)替換為直接引用(地址)的過程

  • 初始化:

  • 執(zhí)行類構(gòu)造器方法的過程,類構(gòu)造器 方法是由編譯期自動收集類中所有類變量的賦值動作和靜態(tài)代碼塊中的語句合并產(chǎn)生的。(類構(gòu)造器是構(gòu)造類信息的,不是構(gòu)造該類對象的構(gòu)造器)

  • 當(dāng)初始化一個(gè)類的時(shí)候,如果發(fā)現(xiàn)其父類還沒有初始化完成,則需要先觸發(fā)其父類的初始化

  • 虛擬機(jī)會保證一個(gè)類的方法在多相差環(huán)境中被正確的加鎖和同步

下面一段代碼,分別說明了static代碼塊,以及子類和父類構(gòu)造方法的執(zhí)行流程

/**
 * 類加載流程
 *
 * @author: 輕狂書生FS
 */

class SuperA {

    static {
        System.out.println('父類靜態(tài)代碼塊初始化');
    }

    public SuperA() {
        System.out.println('父類構(gòu)造函數(shù)初始化');
    }
}
class A extends SuperA{
    static {
        System.out.println('靜態(tài)代碼塊初始化');
        m = 300;
    }

    static int m = 100;

    public A() {
        System.out.println('A類的無參構(gòu)造方法');
    }

}
public class ClassLoaderDemo {

    public static void main(String[] args) {
        A a = new A();
        System.out.println(a.m);
    }
}

最后的結(jié)果為:

父類靜態(tài)代碼塊初始化
靜態(tài)代碼塊初始化
父類構(gòu)造函數(shù)初始化
A類的無參構(gòu)造方法
100

說明靜態(tài)代碼塊都是執(zhí)行的,并且父類優(yōu)先

類加載步驟

  • 加載到內(nèi)存,會產(chǎn)生一個(gè)類對應(yīng)Class對象

  • 鏈接,鏈接結(jié)束 m = 0

  • 初始化:

<clinit>() {
 syso('A類靜態(tài)方法')
 m = 300;
 m = 100;
}
在這里插入圖片描述

什么時(shí)候發(fā)生類初始化

類的主動引用(一定發(fā)生初始化)

  • 當(dāng)虛擬機(jī)啟動,先初始化main方法所有在類

  • new 一個(gè)類的對象

  • 調(diào)用類的靜態(tài)成員(除了 final常量)和靜態(tài)方法

  • 使用 java.lang.reflect包的方法對類進(jìn)行反射調(diào)用

  • 當(dāng)初始化一個(gè)類,如果其父類沒有被初始化,則會先初始化它的父類

類的被動引用(不會發(fā)生初始化)

  • 當(dāng)訪問一個(gè)靜態(tài)域時(shí),只有真正的申明這個(gè)域的類才會被初始化,如:當(dāng)通過子類引用父類的靜態(tài)變量,不會導(dǎo)致子類初始化

  • 通過數(shù)組定義類引用,不會觸發(fā)此類的初始化

  • 引用常量不會觸發(fā)此類的初始化(常量在鏈接階段就存入調(diào)用類的常量池了)

類加載器的作用

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

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

在這里插入圖片描述

類加載器作用是用來把類(Class)裝載進(jìn)內(nèi)存的,JVM規(guī)范定義了如下類型的類的加載器

在這里插入圖片描述

代碼如下:

/**
 * 類加載器的種類
 *
 * @author: 輕狂書生FS
 * @create: 2020-09-29-11:51
 */

public class ClassLoaderTypeDemo {
    public static void main(String[] args) {

        //當(dāng)前類是哪個(gè)加載器
        ClassLoader loader = ClassLoaderTypeDemo.class.getClassLoader();
        System.out.println(loader);

        // 獲取系統(tǒng)類加載器
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        System.out.println(classLoader);

        // 獲取系統(tǒng)類加載器的父類加載器 -> 擴(kuò)展類加載器
        ClassLoader parentClassLoader = classLoader.getParent();
        System.out.println(parentClassLoader);

        // 獲取擴(kuò)展類加載器的父類加載器 -> 根加載器(C、C++)
        ClassLoader superParentClassLoader = parentClassLoader.getParent();
        System.out.println(superParentClassLoader);

        // 測試JDK內(nèi)置類是誰加載的
        ClassLoader loader2 = Object.class.getClassLoader();
        System.out.println(loader2);
    }
}

運(yùn)行結(jié)果:我們發(fā)現(xiàn),根加載器我們無法獲取到

sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@45ee12a7
null
null

獲取類加載器能夠加載的路徑

// 如何獲取類加載器可以加載的路徑
System.out.println(System.getProperty('java.class.path'));

最后輸出結(jié)果為:

        // 如何獲取類加載器可以加載的路徑
        System.out.println(System.getProperty('java.class.path'));

        /*
        E:\Software\JDK1.8\Java\jre\lib\charsets.jar;
        E:\Software\JDK1.8\Java\jre\lib\deploy.jar;
        E:\Software\JDK1.8\Java\jre\lib\ext\access-bridge-64.jar;
        E:\Software\JDK1.8\Java\jre\lib\ext\cldrdata.jar;
        E:\Software\JDK1.8\Java\jre\lib\ext\dnsns.jar;
        E:\Software\JDK1.8\Java\jre\lib\ext\jaccess.jar;
        E:\Software\JDK1.8\Java\jre\lib\ext\jfxrt.jar;
        E:\Software\JDK1.8\Java\jre\lib\ext\localedata.jar;
        E:\Software\JDK1.8\Java\jre\lib\ext\nashorn.jar;
        E:\Software\JDK1.8\Java\jre\lib\ext\sunec.jar;
        E:\Software\JDK1.8\Java\jre\lib\ext\sunjce_provider.jar;
        E:\Software\JDK1.8\Java\jre\lib\ext\sunmscapi.jar;
        E:\Software\JDK1.8\Java\jre\lib\ext\sunpkcs11.jar;
        E:\Software\JDK1.8\Java\jre\lib\ext\zipfs.jar;
        E:\Software\JDK1.8\Java\jre\lib\javaws.jar;
        E:\Software\JDK1.8\Java\jre\lib\jce.jar;
        E:\Software\JDK1.8\Java\jre\lib\jfr.jar;
        E:\Software\JDK1.8\Java\jre\lib\jfxswt.jar;
        E:\Software\JDK1.8\Java\jre\lib\jsse.jar;
        E:\Software\JDK1.8\Java\jre\lib\management-agent.jar;
        E:\Software\JDK1.8\Java\jre\lib\plugin.jar;
        E:\Software\JDK1.8\Java\jre\lib\resources.jar;
        E:\Software\JDK1.8\Java\jre\lib\rt.jar;
        C:\Users\Administrator\Desktop\LearningNotes\校招面試\JUC\Code\target\classes;
        C:\Users\Administrator\.m2\repository\org\projectlombok\lombok\1.18.10\lombok-1.18.10.jar;
        C:\Users\Administrator\.m2\repository\cglib\cglib\3.3.0\cglib-3.3.0.jar;
        C:\Users\Administrator\.m2\repository\org\ow2\asm\asm\7.1\asm-7.1.jar;
        E:\Software\IntelliJ IDEA\IntelliJ IDEA 2019.1.2\lib\idea_rt.jar
         */

我們能夠發(fā)現(xiàn),類在加載的時(shí)候,都是有自己的加載區(qū)域的,而不是任何地方的類都能夠被加載。

搜索Java知音公眾號,回復(fù)“后端面試”,送你一份Java面試題寶典.pdf

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

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

  • 實(shí)現(xiàn)的全部接口

  • 所繼承的父類

  • 全部的構(gòu)造器

  • 全部的方法

  • 全部的Field

  • 注解

/**
 * 獲取運(yùn)行時(shí)類信息
 * @author: 輕狂書生FS
 */

public class GetClassInfo {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class clazz = Class.forName('com.moxi.interview.study.annotation.User');

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

        // 獲取類屬性
        System.out.println('================');
        // 只能找到public屬性
        Field [] fields = clazz.getFields();

        // 找到全部的屬性
        Field [] fieldAll = clazz.getDeclaredFields();

        for (int i = 0; i < fieldAll.length; i++) {
            System.out.println(fieldAll[i]);
        }

        // 獲取指定屬性的值
        Field name = clazz.getDeclaredField('name');

        // 獲取方法
        Method [] methods = clazz.getDeclaredMethods(); // 獲取本類和父類的所有public方法
        Method [] methods2 = clazz.getMethods(); // 獲取本類所有方法

        // 獲得指定方法
        Method method = clazz.getDeclaredMethod('getName'null);

        // 獲取方法的時(shí)候,可以把參數(shù)也丟進(jìn)去,這樣因?yàn)楸苊夥椒ㄖ剌d,而造成不知道加載那個(gè)方法
        Method method2 = clazz.getDeclaredMethod('setName', String.class);

    }
}

雙親委派機(jī)制

如果我們想定義一個(gè):java.lang.string 包,我們會發(fā)現(xiàn)無法創(chuàng)建

因?yàn)轭愒诩虞d的時(shí)候,會逐級往上

也就是說當(dāng)前的系統(tǒng)加載器,不會馬上的創(chuàng)建該類,而是將該類委派給 擴(kuò)展類加載器,擴(kuò)展類加載器在委派為根加載器,然后引導(dǎo)類加載器去看這個(gè)類在不在能訪問的路徑下,發(fā)現(xiàn) sring包已經(jīng)存在了,所以就無法進(jìn)行,也就是我們無法使用自己自定義的string類,而是使用初始化的stirng類

當(dāng)一個(gè)類收到了類加載請求,他首先不會嘗試自己去加載這個(gè)類,而是把這個(gè)請求委派給父類去完成,每一個(gè)層次類加載器都是如此,因此所有的加載請求都應(yīng)該傳送到啟動類加載其中,只有當(dāng)父類加載器反饋?zhàn)约簾o法完成這個(gè)請求的時(shí)候(在它的加載路徑下沒有找到所需加載的Class),子類加載器才會嘗試自己去加載。

采用雙親委派的一個(gè)好處是比如加載位于rt.jar 包中的類java.lang.Object,不管是哪個(gè)加載器加載這個(gè)類,最終都是委托給頂層的啟動類加載器進(jìn)行加載,這樣就保證了使用不同的類加載器最終得到的都是同樣一個(gè)Object 對象

在這里插入圖片描述

有了Class對象,我們能夠做什么?

創(chuàng)建類的對象:通過調(diào)用Class對象的newInstance()方法

  • 類必須有一個(gè)無參數(shù)的構(gòu)造器

  • 類的構(gòu)造器的權(quán)限需要足夠

如果沒有無參構(gòu)造器就不能創(chuàng)建對象?

只要在操作的時(shí)候明確的調(diào)用類中的構(gòu)造器,并將參數(shù)傳遞進(jìn)去之后,才可以實(shí)例化操作。

步驟如下:

  • 通過Class類的getDeclaredConstructor(Class … parameterTypes)取得本類的指定形參類型的構(gòu)造器

  • 向構(gòu)造器的形參中,傳遞一個(gè)對象數(shù)組進(jìn)去,里面包含了構(gòu)造器中所需的各個(gè)參數(shù)

  • 通過Constructor實(shí)例化對象

調(diào)用指定方法

通過反射,調(diào)用類中的方法,通過Method類完成。

  • 通過Class類的getMethod方法取得一個(gè)Method對象,并設(shè)置此方法操作是所需要的參數(shù)類型

  • 之后使用Object invoke進(jìn)行調(diào)用,并向方法中傳遞要設(shè)置的obj對象的參數(shù)信息

Invoke方法

  • Object invoke(Object obj, Object … args)

  • Object對應(yīng)原方法的返回值,若原方法無返回值,此時(shí)返回null

  • 若原方法為靜態(tài)方法,此時(shí)形參Object 可以為null

  • 若原方法形參列表為空,則Object[] args 為 null

  • 若原方法聲明private,則需要在調(diào)用此invoke() 方法前,顯示調(diào)用方法對象的setAccessible(true)方法,將可訪問private的方法

setAccessible方法

  • Method和Field、Constructor對象都有setAccessible()方法

  • setAccessible作用是啟動和禁用訪問安全檢查的開關(guān)

  • 參數(shù)值為true則指示反射對象再使用時(shí)應(yīng)該取消Java語言訪問檢查

  • 提高反射效率,如果代碼中必須使用反射,而這句代碼需要頻繁被嗲用,那么設(shè)置成true

  • 使得原本無法訪問的私有成員也可以訪問

  • 參數(shù)值為false則指示反射的對象應(yīng)該實(shí)行Java語言訪問檢查

在這里插入圖片描述

完整代碼:

/**
 * 通過反射獲取對象
 *
 * @author: 輕狂書生FS
 */

public class GetObjectByReflectionDemo {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {

        // 獲取Class
        Class clazz = Class.forName('com.moxi.interview.study.annotation.User');

        // 構(gòu)造一個(gè)對象,newInstance調(diào)用的是無參構(gòu)造器,如果沒有無參構(gòu)造器的話,本方法會出錯(cuò)
//        User user = (User)clazz.newInstance();

        // 獲取class的有參構(gòu)造器
        Constructor constructor = clazz.getDeclaredConstructor(String.classint.classint.class);
        User user2 = (User) constructor.newInstance('小溪'1010);
        System.out.println(user2);


        // 通過反射調(diào)用普通構(gòu)造方法
        User user3 = (User)clazz.newInstance();
        // 獲取setName 方法
        Method setName = clazz.getDeclaredMethod('setName', String.class);
        // 執(zhí)行setName方法,傳入對象 和 參數(shù)
        setName.invoke(user3, '小白');
        System.out.println(user3);

        System.out.println('============');
        Field age = clazz.getDeclaredField('age');
        // 關(guān)閉權(quán)限檢測,這樣才能直接修改字段,因?yàn)?nbsp;set方法不能直接操作私有變量
        age.setAccessible(true);
        age.set(user3, 10);
        System.out.println(user3);

    }
}

運(yùn)行結(jié)果

User{name='小溪', id=10, age=10}
User{name='小白', id=0, age=0}
============
User{name='小白', id=0, age=10}

反射性能對比

下面我們編寫代碼來具體試一試,使用反射的時(shí)候和不適用反射,在執(zhí)行方法時(shí)的性能對比

/**
 * 反射性能
 *
 * @author: 輕狂書生FS
 */

public class ReflectionPerformance {

    /**
     * 普通方式調(diào)用
     */

    public static void test01() {
        User user = new User();
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            user.getName();
        }
        long endTime = System.currentTimeMillis();

        System.out.println('普通方式執(zhí)行10億次getName的時(shí)間:' + (endTime - startTime) + ' ms');
    }

    /**
     * 反射方式調(diào)用
     */

    public static void test02() throws Exception {
        Class clazz = Class.forName('com.moxi.interview.study.annotation.User');
        Method getName = clazz.getDeclaredMethod('getName'null);
        User user = (User) clazz.newInstance();
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user, null);
        }
        long endTime = System.currentTimeMillis();

        System.out.println('反射方式執(zhí)行10億次getName的時(shí)間:' + (endTime - startTime) + ' ms');
    }

    /**
     * 反射方式調(diào)用,關(guān)閉權(quán)限檢查
     */

    public static void test03() throws Exception {
        Class clazz = Class.forName('com.moxi.interview.study.annotation.User');
        Method getName = clazz.getDeclaredMethod('getName'null);
        User user = (User) clazz.newInstance();
        long startTime = System.currentTimeMillis();
        getName.setAccessible(true);
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user, null);
        }
        long endTime = System.currentTimeMillis();

        System.out.println('反射方式執(zhí)行10億次getName的時(shí)間:' + (endTime - startTime) + ' ms');
    }
    public static void main(String[] args) throws Exception {
        test01();
        test02();
        test03();
    }
}

運(yùn)行結(jié)果:

普通方式執(zhí)行10億次getName的時(shí)間:3 ms
反射方式執(zhí)行10億次getName的時(shí)間:2554 ms
反射方式執(zhí)行10億次getName的時(shí)間:1365 ms

我們上面分別是執(zhí)行了 10億次 getName的方法,從里面可以看出,通過直接實(shí)例化對象后,調(diào)用getName耗時(shí)最短,同時(shí)關(guān)閉了 權(quán)限檢查后的比不關(guān)閉能提高一倍的性能。

反射操作泛型

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

為了通過反射操作這些類型,Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType幾種類型來代表不能被歸一到Class類中的類型但是有何原始類型齊名的類型。

  • ParameterizedType:表示一種參數(shù)化類型,比如Collection

  • GenericArrayType:表示一種元素類型是參數(shù)化類型或者類型變量的數(shù)組類型

  • TypeVariable:是各種類型變量的公共父接口

  • WildcardType:代表一種通配符類型的表達(dá)式

下面我們通過代碼來獲取方法上的泛型,包括參數(shù)泛型,以及返回值泛型

/**
 * 通過反射獲取泛型
 *
 * @author: 輕狂書生FS
 */

public class GenericityDemo {

    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 Exception{

        Method method = GenericityDemo.class.getMethod('test01', Map.classList.class);

        // 獲取所有的泛型,也就是參數(shù)泛型
        Type[] genericParameterTypes = method.getGenericParameterTypes();

        // 遍歷打印全部泛型
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println(' # ' +genericParameterType);
            if(genericParameterType instanceof ParameterizedType) {
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);
                }
            }
        }

        // 獲取返回值泛型
        Method method2 = GenericityDemo.class.getMethod('test02', null);
        Type returnGenericParameterTypes = method2.getGenericReturnType();

        // 遍歷打印全部泛型
        if(returnGenericParameterTypes instanceof ParameterizedType) {
            Type[] actualTypeArguments = ((ParameterizedType) returnGenericParameterTypes).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument);
            }
        }

    }
}

得到的結(jié)果

 # java.util.Map<java.lang.String, com.moxi.interview.study.annotation.User>
class java.lang.String
class com.moxi.interview.study.annotation.User
 # java.util.List<com.moxi.interview.study.annotation.User>
class com.moxi.interview.study.annotation.User
###################
class java.lang.String
class com.moxi.interview.study.annotation.User

反射操作注解

通過反射能夠獲取到 類、方法、字段。。。等上的注解

  • getAnnotation

  • getAnnotations

ORM對象關(guān)系映射

ORM即為:Object relationship Mapping,對象關(guān)系映射

  • 類和表結(jié)構(gòu)對應(yīng)

  • 屬性和字段對應(yīng)

  • 對象和記錄對應(yīng)

在這里插入圖片描述

下面使用代碼,模擬ORM框架的簡單使用

/**
 * ORMDemo
 *
 * @author: 輕狂書生FS
 */

@TableKuang('db_student')
class Student2 {
    @FieldKuang(columnName = 'db_id', type='int', length = 10)
    private int id;

    @FieldKuang(columnName = 'db_age', type='int', length = 10)
    private int age;

    @FieldKuang(columnName = 'db_name', type='varchar', length = 10)
    private String name;

    public Student2() {
    }

    public Student2(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 'Student2{' +
                'id=' + id +
                ', age=' + age +
                ', name='' + name + '\'' +
                '}';
    }
}

/**
 * 自定義注解:類名的注解
 */

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

/**
 * 自定義注解:屬性的注解
 */

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldKuang {
    String columnName();
    String type();
    int length() default 0;
}
public class ORMDemo {

    public static void main(String[] args) throws Exception{
        // 獲取Student 的 Class對象
        Class c1 = Class.forName('com.moxi.interview.study.annotation.Student2');

        // 通過反射,獲取到全部注解
        Annotation [] annotations = c1.getAnnotations();

        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }

        // 獲取注解的value值
        TableKuang tableKuang = (TableKuang)c1.getAnnotation(TableKuang.class);
        String value = tableKuang.value();
        System.out.println(value);

        // 獲得類指定的注解
        Field f = c1.getDeclaredField('name');
        FieldKuang fieldKuang = f.getAnnotation(FieldKuang.class);
        System.out.println(fieldKuang.columnName());
        System.out.println(fieldKuang.type());
        System.out.println(fieldKuang.length());
    }
}

END

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
110_注解和反射
Java學(xué)習(xí)-注解和反射
紀(jì)念我曾經(jīng)的 JAVA 姿勢
各大框架都在使用注解,淺談注解的使用及原理以及類加載器
詳解Java反射各種應(yīng)用
Java基礎(chǔ)增強(qiáng)-反射機(jī)制
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服