<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 13796字,需瀏覽 28分鐘

           ·

          2020-12-20 03:09


          作者:輕狂書生FS

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

          前言

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

          什么是注解

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

          • Annotation的作用

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

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

          • Annotation的格式

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

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

          • Annotation在那里使用?

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

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

          內(nèi)置注解

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

          • @Deprecated:定義在java.lang.Deprecated中,此注釋可以用于修飾方法,屬性,類,表示不鼓勵程序員使用這樣的元素,通常是因為它很危險,或者存在更好的選擇

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

          • @SuppressWarnings(“all”)

          • @SuppressWarnings(“unchecked”)

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

          元注解

          元注解的作用就是負責注解其它注解,Java定義了4個標準的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?{

          }

          /**
          ?*?定義一個注解
          ?*/

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

          }

          自定義注解

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

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

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

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

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

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

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

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

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

          public?class?MateAnnotationDemo?{

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

          ????}

          }

          /**
          ?*?定義一個注解
          ?*/

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

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

          ????int?age()?default?0;

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

          ????String[]?schools();
          }

          反射機制

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

          動態(tài)語言

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

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

          靜態(tài)語言

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

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

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

          Java反射機制概述

          什么是反射

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

          Class?c?=?Class.forName("java.lang.String")

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

          在這里插入圖片描述

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

          反射的應(yīng)用

          • 在運行時判斷任意一個對象所屬類

          • 在運行時構(gòu)造任意一個類的對象

          • 在運行時判斷任意一個類所具有的成員變量和方法

          • 在運行時獲取泛型信息

          • 在運行時調(diào)用任意一個對象的成員變量和方法

          • 在運行時候處理注解

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

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

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

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

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

          • java.lang.Class:代表一個類

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

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

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

          理解Class類并獲取Class實例

          Class類

          我們下面通過Class.forName來獲取一個實例對象

          /**
          ?*?反射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());


          ????}
          }

          /**
          ?*?實體類: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?+
          ????????????????'}';
          ????}
          }

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

          1173230247
          1173230247
          1173230247

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

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

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

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

          public?final?Class?getClass()

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

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

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

          • Class本身也是一個類

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

          • 一個加載的類在JVM中只會有一個Class實例

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

          • 每個類的實例都會記得自己是由哪個Class實例所生成

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

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

          Class類常用的方法

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

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

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

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

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

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

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

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

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

          獲取對象實例的方法

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

          • Class clazz = Person.class;

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

          • Class clazz = person.getClass()

          • 已經(jīng)一個類的全類名,且該類在類路徑下,可以通過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?=?"學生";
          ????}
          }

          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("這個人是:"?+?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)置類型的包裝類,都有一個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);
          ????}
          }

          最后運行結(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

          同時需要注意,只要類型和維度一樣,那就是同一個Class對象

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

          這兩個的hashcode是一樣的

          Java內(nèi)存分析

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

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

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

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

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

          • 方法區(qū)

          • 可以被所有線程共享

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

          類的加載與ClassLoader的理解

          類加載過程

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

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

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

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

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

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

          • 初始化:

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

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

          • 虛擬機會保證一個類的方法在多相差環(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)生一個類對應(yīng)Class對象

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

          • 初始化:

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

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

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

          • 當虛擬機啟動,先初始化main方法所有在類

          • new 一個類的對象

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

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

          • 當初始化一個類,如果其父類沒有被初始化,則會先初始化它的父類

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

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

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

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

          類加載器的作用

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

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

          在這里插入圖片描述

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

          在這里插入圖片描述

          代碼如下:

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

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

          ????????//當前類是哪個加載器
          ????????ClassLoader?loader?=?ClassLoaderTypeDemo.class.getClassLoader();
          ????????System.out.println(loader);

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

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

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

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

          運行結(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),類在加載的時候,都是有自己的加載區(qū)域的,而不是任何地方的類都能夠被加載。

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

          獲取運行時候類的完整結(jié)構(gòu)

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

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

          • 所繼承的父類

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

          • 全部的方法

          • 全部的Field

          • 注解

          /**
          ?*?獲取運行時類信息
          ?*?@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?????????????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ù)也丟進去,這樣因為避免方法重載,而造成不知道加載那個方法
          ????????Method?method2?=?clazz.getDeclaredMethod("setName",?String.class);

          ????}
          }

          雙親委派機制

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

          因為類在加載的時候,會逐級往上

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

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

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

          在這里插入圖片描述

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

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

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

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

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

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

          步驟如下:

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

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

          • 通過Constructor實例化對象

          調(diào)用指定方法

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

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

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

          Invoke方法

          • Object invoke(Object obj, Object … args)

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

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

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

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

          setAccessible方法

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

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

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

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

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

          • 參數(shù)值為false則指示反射的對象應(yīng)該實行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)造一個對象,newInstance調(diào)用的是無參構(gòu)造器,如果沒有無參構(gòu)造器的話,本方法會出錯
          //????????User?user?=?(User)clazz.newInstance();

          ????????//?獲取class的有參構(gòu)造器
          ????????Constructor?constructor?=?clazz.getDeclaredConstructor(String.class,?int.class,?int.class);
          ????????User?user2?=?(User)?constructor.newInstance("小溪",?10,?10);
          ????????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)限檢測,這樣才能直接修改字段,因為?set方法不能直接操作私有變量
          ????????age.setAccessible(true);
          ????????age.set(user3,?10);
          ????????System.out.println(user3);

          ????}
          }

          運行結(jié)果

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

          反射性能對比

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

          /**
          ?*?反射性能
          ?*
          ?*?@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的時間:"?+?(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的時間:"?+?(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的時間:"?+?(endTime?-?startTime)?+?"?ms");
          ????}
          ????public?static?void?main(String[]?args)?throws?Exception?{
          ????????test01();
          ????????test02();
          ????????test03();
          ????}
          }

          運行結(jié)果:

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

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

          反射操作泛型

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

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

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

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

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

          • WildcardType:代表一種通配符類型的表達式

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

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

          public?class?GenericityDemo?{

          ????public?void?test01(Map?map,?List?list)?{
          ????????System.out.println("test01");
          ????}

          ????public?Map?test02()?{
          ????????System.out.println("test02");
          ????????return?null;
          ????}

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

          ????????Method?method?=?GenericityDemo.class.getMethod("test01",?Map.class,?List.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
          class?java.lang.String
          class?com.moxi.interview.study.annotation.User
          ?#?java.util.List
          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());
          ????}
          }

          更多好文章

          1. Java高并發(fā)系列(共34篇)
          2. MySql高手系列(共27篇)
          3. Maven高手系列(共10篇)
          4. Mybatis系列(共12篇)
          5. 聊聊db和緩存一致性常見的實現(xiàn)方式
          6. 接口冪等性這么重要,它是什么?怎么實現(xiàn)?
          7. 泛型,有點難度,會讓很多人懵逼,那是因為你沒有看這篇文章!

          瀏覽 28
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  躁BBB躁BBB躁BBBBBB日 | 青青草视频免费观看 | 日本很黄的视频免费在线观看视频 | 国产精品嫩草视频 | 国产射精视频 |