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

          大三Java后端暑期實(shí)習(xí)面經(jīng)總結(jié)

          共 30533字,需瀏覽 62分鐘

           ·

          2021-05-28 10:58

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

            作者 |  Baret-H

          來(lái)源 |  urlify.cn/6RVNRr

          1. JDK、JRE、JVM的區(qū)別和聯(lián)系


          JDK(java程序開(kāi)發(fā)包)=JRE +Tools

          JRE=JVM(虛擬機(jī))+API


          2. 采用字節(jié)碼的好處

          Java中引入了jvm,即在機(jī)器和編譯程序之間加了一層抽象的虛擬機(jī)器,這臺(tái)機(jī)器在任何平臺(tái)上都提供給編譯程序一個(gè)共同的接口。

          • 編譯程序只需要面向虛擬機(jī),生成虛擬機(jī)能夠理解的代碼,然后由解釋器來(lái)將虛擬機(jī)代碼轉(zhuǎn)換為特定系統(tǒng)的機(jī)器碼來(lái)執(zhí)行。在Java中,這種供虛擬機(jī)理解的代碼叫做字節(jié)碼(.class),它不面向任何特定的處理器,只面向虛擬機(jī)

          • 每一種平臺(tái)的解釋器是不同的,但是實(shí)現(xiàn)的虛擬機(jī)是相同的。Java源程序通過(guò)編譯器進(jìn)行編譯后轉(zhuǎn)換為字節(jié)碼,字節(jié)碼在虛擬機(jī)上執(zhí)行,虛擬機(jī)將每一條要執(zhí)行的字節(jié)碼送給解釋器,解釋器將其翻譯成特定機(jī)器上的機(jī)器碼,然后在特定的機(jī)器上運(yùn)行。這也解釋了Java的編譯與解釋共存的特點(diǎn)。

          Java源代碼-->編譯器-->jvm可執(zhí)行的java字節(jié)碼-->jvm中解釋器-->機(jī)器可執(zhí)行的二進(jìn)制機(jī)器碼-->程序運(yùn)行

          Java語(yǔ)言采用字節(jié)碼的方式,一定程度上解決了傳統(tǒng)解釋型語(yǔ)言執(zhí)行效率低(運(yùn)行需要解釋環(huán)境,速度比編譯的要慢,占用資源也要多一些)的問(wèn)題,同時(shí)又保留了解釋型語(yǔ)言可移植的特點(diǎn),所以Java程序運(yùn)行時(shí)很高效,此外,由于字節(jié)碼不針對(duì)一種特定的機(jī)器,因此Java源程序無(wú)需重新編譯即可在不同的計(jì)算機(jī)上運(yùn)行,實(shí)現(xiàn)一次編譯,多次運(yùn)行


          3. 接口和抽象類的區(qū)別

          1?? 從語(yǔ)法上來(lái)說(shuō)

          • 抽象類可以存在普通成員函數(shù),而接口中只能存在public abstract方法。

          • 抽象類中的成員變量可以是各種類型的,而接口中的成員變量只能是public static final類型的。

          • 抽象類只能繼承一個(gè),接口可以實(shí)現(xiàn)多個(gè)

          2?? 從設(shè)計(jì)目的來(lái)說(shuō)

          • 接口是用來(lái)對(duì)

            類的形為進(jìn)行約束

            。也就是提供了一種機(jī)制,可以強(qiáng)制要求所有的類具有相同的形為,只約束了行為的有無(wú),不限制形為的具體實(shí)現(xiàn)

          • 抽象類是為了

            代碼復(fù)用

            。當(dāng)不同的類具有相同的行為A,且其中一部分形為B的實(shí)現(xiàn)方式一致時(shí),可以讓這些類都派生于一個(gè)抽象類,這個(gè)抽象類中實(shí)現(xiàn)了B,避免讓所有的子類來(lái)實(shí)現(xiàn)B,以此來(lái)達(dá)到代碼復(fù)用的目的。而A-B的部分,交給各個(gè)子類自己實(shí)現(xiàn),正是由于這里A-B的行為沒(méi)有實(shí)現(xiàn),所以抽象類不允許實(shí)例化

          3?? 從本質(zhì)上來(lái)說(shuō)

          • 接口是

            對(duì)行為的抽象

            ,表達(dá)的是like a的關(guān)系,比如 Bird like a Aircraft(鳥(niǎo)像飛行器一樣可以飛);接口的核心是定義行為,即接口的實(shí)現(xiàn)類可以做什么,至于實(shí)現(xiàn)類如何實(shí)現(xiàn),主體是誰(shuí),接口并不關(guān)心

          • 抽象類是 

            對(duì)類本質(zhì)的抽象

            ,表達(dá)的是is a的關(guān)系,比如 BaoMa is a Car(寶馬是一輛車);抽象類包含并實(shí)現(xiàn)子類的通用特性,將子類存在差異化的特性進(jìn)行抽象,交給子類去實(shí)現(xiàn)

          總結(jié)

          • 當(dāng)你關(guān)注一個(gè)事物的本質(zhì)的時(shí)候,用抽象類;當(dāng)你關(guān)注一個(gè)操作的時(shí)候,用接口。

          • 抽象類的功能要遠(yuǎn)超過(guò)接口,但是,定義抽象類的代價(jià)高。因?yàn)楦呒?jí)語(yǔ)言來(lái)說(shuō)(從實(shí)際設(shè)計(jì)上來(lái)說(shuō)也是)每個(gè)類只能繼承一個(gè)類。在這個(gè)類中,你必須繼承或編寫出其所有子類的所有共性。雖然接口在功能上會(huì)弱化許多,但是它只是針對(duì)一個(gè)動(dòng)作的描述。而且你可以在一個(gè)類中同時(shí)實(shí)現(xiàn)多個(gè)接口。在設(shè)計(jì)階段會(huì)降低難度


          4. 面向?qū)ο蟮乃拇筇匦?/span>

          1?? 抽象

          將一類對(duì)象的共同特征總結(jié)出來(lái)構(gòu)造類的過(guò)程

          2?? 封裝

          將過(guò)程和數(shù)據(jù)包圍起來(lái),對(duì)數(shù)據(jù)的訪問(wèn)只能通過(guò)特定的接口(例如私有變量的get/set方法)

          3?? 繼承

          從現(xiàn)有類派生出新類的過(guò)程

          4?? 多態(tài)

          • 編譯時(shí)多態(tài):同一方法可根據(jù)對(duì)象的不同產(chǎn)生不同的效果,也就是 方法的重載

          • 運(yùn)行時(shí)多態(tài):父類的引用指向子類對(duì)象,一個(gè)對(duì)象的實(shí)際類型確定,但是指向其的引用類型可以有很多


          5. 面向?qū)ο蠛兔嫦蜻^(guò)程

          面向過(guò)程(Procedure Oriented)和面向?qū)ο?Object Oriented,OO)都是對(duì)軟件分析、設(shè)計(jì)和開(kāi)發(fā)的一種思想,它指導(dǎo)著人們以不同的方式去分析、設(shè)計(jì)和開(kāi)發(fā)軟件。早期先有面向過(guò)程思想,隨著軟件規(guī)模的擴(kuò)大,問(wèn)題復(fù)雜性的提高,面向過(guò)程的弊端越來(lái)越明顯的顯示出來(lái),出現(xiàn)了面向?qū)ο笏枷氩⒊蔀槟壳爸髁鞯姆绞健烧叨钾灤┯谲浖治觥⒃O(shè)計(jì)和開(kāi)發(fā)各個(gè)階段,對(duì)應(yīng)面向?qū)ο缶头謩e稱為面向?qū)ο蠓治?OOA)、面向?qū)ο笤O(shè)計(jì)(OOD)和面向?qū)ο缶幊?OOP)。C語(yǔ)言是一種典型的面向過(guò)程語(yǔ)言,Java是一種典型的面向?qū)ο笳Z(yǔ)言。

          面向?qū)ο蠛兔嫦蜻^(guò)程是兩種不同的處理問(wèn)題角度

          • 面向過(guò)程注重事情的每一步以及順序

          • 面向過(guò)程諸眾事情有哪些參與者(對(duì)象),以及各自需要做什么

          比如:洗衣機(jī)洗衣服

          • 面向過(guò)程會(huì)將任務(wù)拆解成一系列的步驟(函數(shù)):

            1、打開(kāi)洗衣機(jī)–>2、放衣服–>3、放洗衣粉–>4、清洗–>5、烘干

          • 面向?qū)ο髸?huì)拆出人和洗衣機(jī)兩個(gè)對(duì)象:

            人:打開(kāi)洗衣機(jī)放衣服放洗衣粉

            洗衣機(jī):清洗烘干

          由此可見(jiàn),面向過(guò)程比較直接高效,而面向?qū)ο蟾子趶?fù)用、擴(kuò)展和維護(hù)

          面向?qū)ο蠛兔嫦蜻^(guò)程的總結(jié)

          1. 都是解決問(wèn)題的思維方式,都是代碼組織的方式。

          2. 解決簡(jiǎn)單問(wèn)題可以使用面向過(guò)程

          3. 解決復(fù)雜問(wèn)題:宏觀上使用面向?qū)ο蟀盐眨⒂^處理上仍然是面向過(guò)程。

          4. 面向?qū)ο缶哂腥筇卣鳎悍庋b性、繼承性和多態(tài)性,而面向過(guò)程沒(méi)有繼承性和多態(tài)性,并且面向過(guò)程的封裝只是封裝功能,而面向?qū)ο罂梢苑庋b數(shù)據(jù)和功能。所以面向?qū)ο髢?yōu)勢(shì)更明顯


          6. 靜態(tài)綁定&動(dòng)態(tài)綁定

          在Java方法調(diào)用的過(guò)程中,JVM是如何知道調(diào)用的是哪個(gè)類的方法源代碼呢?這就涉及到程序綁定,程序綁定指的是一個(gè)方法的調(diào)用與方法所在的類(方法主體)關(guān)聯(lián)起來(lái)。

          對(duì)Java來(lái)說(shuō),綁定分為靜態(tài)綁定動(dòng)態(tài)綁定,或者叫做前期綁定和后期綁定。

          1?? 靜態(tài)綁定

          針對(duì)Java,可以簡(jiǎn)單地理解為程序編譯期的綁定

          這里特別說(shuō)明一點(diǎn),Java當(dāng)中的方法只有finalstaticprivate構(gòu)造方法是靜態(tài)綁定。

          # 關(guān)于final,static,private和構(gòu)造方法是前期綁定的理解:
           對(duì)于private的方法,首先一點(diǎn)它不能被繼承,既然不能被繼承那么就沒(méi)辦法通過(guò)它子類的對(duì)象來(lái)調(diào)用,而只能通過(guò)這個(gè)類自身的對(duì)象來(lái)調(diào)用。因此就可以說(shuō)private方法和定義這個(gè)方法的類綁定在了一起。
           final方法雖然可以被繼承,但不能被重寫(覆蓋),雖然子類對(duì)象可以調(diào)用,但是調(diào)用的都是父類中所定義的那個(gè)final方法,(由此我們可以知道將方法聲明為final類型,一是為了防止方法被覆蓋,二是為了有效地關(guān)閉java中的動(dòng)態(tài)綁定)。
           構(gòu)造方法也是不能被繼承的(網(wǎng)上也有說(shuō)子類無(wú)條件地繼承父類的無(wú)參數(shù)構(gòu)造函數(shù)作為自己的構(gòu)造函數(shù),不過(guò)個(gè)人認(rèn)為這個(gè)說(shuō)法不太恰當(dāng),因?yàn)槲覀冎雷宇愂峭ㄟ^(guò)super()來(lái)調(diào)用父類的無(wú)參構(gòu)造方法,來(lái)完成對(duì)父類的初始化, 而我們使用從父類繼承過(guò)來(lái)的方法是不用這樣做的,因此不應(yīng)該說(shuō)子類繼承了父類的構(gòu)造方法),因此編譯時(shí)也可以知道這個(gè)構(gòu)造方法到底是屬于哪個(gè)類。
           對(duì)于static方法,具體的原理我也說(shuō)不太清。不過(guò)根據(jù)網(wǎng)上的資料和我自己做的實(shí)驗(yàn)可以得出結(jié)論:static方法可以被子類繼承,但是不能被子類重寫(覆蓋),但是可以被子類隱藏。(這里意思是說(shuō)如果父類里有一個(gè)static方法,它的子類里如果沒(méi)有對(duì)應(yīng)的方法,那么當(dāng)子類對(duì)象調(diào)用這個(gè)方法時(shí)就會(huì)使用父類中的方法。而如果子類中定義了相同的方法,則會(huì)調(diào)用子類的中定義的方法。唯一的不同就是,當(dāng)子類對(duì)象上轉(zhuǎn)型為父類對(duì)象時(shí),不論子類中有沒(méi)有定義這個(gè)靜態(tài)方法,該對(duì)象都會(huì)使用父類中的靜態(tài)方法。因此這里說(shuō)靜態(tài)方法可以被隱藏而不能被覆蓋。這與子類隱藏父類中的成員變量是一樣的。隱藏和覆蓋的區(qū)別在于,子類對(duì)象轉(zhuǎn)換成父類對(duì)象后,能夠訪問(wèn)父類被隱藏的變量和方法,而不能訪問(wèn)父類被覆蓋的方法)
           由上面我們可以得出結(jié)論,如果一個(gè)方法不可被繼承或者繼承后不可被覆蓋,那么這個(gè)方法就采用的靜態(tài)綁定。

          2?? 動(dòng)態(tài)綁定

          在運(yùn)行時(shí)根據(jù)具體對(duì)象的類型進(jìn)行綁定。也就是說(shuō),編譯器此時(shí)依然不知道對(duì)象的類型,但方法調(diào)用機(jī)制能自己去調(diào)查,找到正確的方法主體

          動(dòng)態(tài)綁定的過(guò)程

          1. 虛擬機(jī)提取對(duì)象的實(shí)際類型的方法表;

          2. 虛擬機(jī)搜索方法簽名

          3. 調(diào)用方法


          7. 重載和重寫

          重寫:發(fā)生在父子類中,方法名、參數(shù)列表必須相同;子類的返回值范圍小于等于父類,拋出異常范圍小于等于父類,訪問(wèn)修飾符范圍大于等于父類;如果父類方法訪問(wèn)修飾符為private則子類不能重寫該方法。

          重載:發(fā)生在同一個(gè)類中,參數(shù)類型不同、個(gè)數(shù)不同、順序不同都可以構(gòu)成重載;

          • 重載方法的返回值可以不同,但是不能僅僅返回值不同,否則編譯時(shí)報(bào)錯(cuò)


          • 重載方法的訪問(wèn)控制符也可以不同,但是不能僅僅訪問(wèn)控制符不同,否則編譯時(shí)報(bào)錯(cuò)



          8. Java異常體系

          Java中的所有異常都來(lái)自頂級(jí)父類Throwable,Throwable有兩個(gè)子類ExceptionError

          • Error是程序無(wú)法處理的錯(cuò)誤,一旦出現(xiàn)錯(cuò)誤,則程序?qū)⒈黄韧V惯\(yùn)行

          • Exception不會(huì)導(dǎo)致程序停止,又分為RunTimeExceptionCheckedException

          • //除0錯(cuò)誤:ArithmeticException
            //錯(cuò)誤的強(qiáng)制類型轉(zhuǎn)換錯(cuò)誤:ClassCastException
            //數(shù)組索引越界:ArrayIndexOutOfBoundsException
            //使用了空對(duì)象:NullPointerException

          • CheckedException常常發(fā)生在程序編譯過(guò)程中,會(huì)導(dǎo)致程序編譯不通過(guò)

                    例如:打開(kāi)不存在的文件

          9. final關(guān)鍵字

          1.作用

          • 修飾類:表示類不可被繼承

          • 修飾方法:表示方法不可被子類覆蓋,但是可以重載

          • 修飾變量:表示變量一旦被賦值就不可以更改它的值

          2.修飾不同變量的區(qū)別

          1?? 修飾成員變量

          • 如果final修飾的是類變量,只能在靜態(tài)初始化塊中指定初始值或者聲明該類變量時(shí)指定初始值。

          • 如果final修飾的是成員變量,可以在非靜態(tài)初始化塊、聲明該變量或者構(gòu)造器中執(zhí)行初始值。

          2?? 修飾局部變量

          系統(tǒng)不會(huì)為局部變量進(jìn)行初始化,局部變量必須由程序員顯示初始化。因此使用final修飾局部變量時(shí),即可以在定義時(shí)指定默認(rèn)值(后面的代碼不能對(duì)變量再賦值),也可以不指定默認(rèn)值,而在后面的代碼中對(duì)final變量賦初值(僅一次)

          3?? 修飾基本數(shù)據(jù)類型和引用類型數(shù)據(jù)

          • 如果是基本數(shù)據(jù)類型的變量,則其數(shù)值一旦在初始化之后便不能更改

          • 如果是引用類型的變量,則在對(duì)其初始化之后便不能再讓其指向另一個(gè)對(duì)象。但是引用的值可變

          2.為什么局部?jī)?nèi)部類和匿名內(nèi)部類只能訪問(wèn)局部final變量

          局部?jī)?nèi)部類或匿名內(nèi)部類編譯之后會(huì)產(chǎn)生兩個(gè)class文件:Test.classTest$1.class,一個(gè)是類class,一個(gè)是內(nèi)部類class

          局部?jī)?nèi)部類:

          首先需要知道的一點(diǎn)是:內(nèi)部類和外部類是處于同一個(gè)級(jí)別的,內(nèi)部類不會(huì)因?yàn)槎x在方法中就會(huì)隨著方法的執(zhí)行完畢就被銷毀。

          這里就會(huì)產(chǎn)生問(wèn)題:當(dāng)外部類的方法結(jié)束時(shí),局部變量就會(huì)被銷毀了,但是內(nèi)部類對(duì)象可能還存在(只有沒(méi)有人再引用它時(shí),才會(huì)死亡),這里就出現(xiàn)了一個(gè)矛盾:內(nèi)部類對(duì)象訪問(wèn)了一個(gè)不存在的變量。為了解決這個(gè)問(wèn)題,就將局部變量復(fù)制了一份作為內(nèi)部類的成員變量,這樣當(dāng)局部變量死亡后,內(nèi)部類仍可以訪問(wèn)它,實(shí)際訪問(wèn)的是局部變量的"copy".這樣就好像延長(zhǎng)了局部變量的生命周期

          將局部變量復(fù)制為內(nèi)部類的成員變量時(shí),必須保證這兩個(gè)變量是一樣的,也就是如果我們?cè)趦?nèi)部類中修改了成員變量,方法中的局部變量也得跟著改變,怎么解決問(wèn)題呢?

          就將局部變量設(shè)置為final,對(duì)它初始化后,我就不讓你再去修改這個(gè)變量,就保證了內(nèi)部類的成員變量和方法的局部變量的一致性。這實(shí)際上也是一種妥協(xié)。使得局部變量與內(nèi)部類內(nèi)建立的拷貝保持一致。


          10. String、StringBuilder、StringBuffer

          • String底層是final修飾的char[]數(shù)組,不可變,每次操作都會(huì)產(chǎn)生新的String對(duì)象

          • StringBuffer和StringBuilder都是在原對(duì)象上操作

          • StringBuffer線程安全(所有方法都用synchronized修飾),StringBuilder線程不安全

          性能:StringBuilder>StringBuffer>String

          使用場(chǎng)景:經(jīng)常需要改變字符串內(nèi)容時(shí)使用后面兩個(gè),優(yōu)先使用 StringBuilder,多線程使用共享變量時(shí)使用 StringBuffer


          11. 單例模式

          徹底玩轉(zhuǎn)單例模式


          12. 工廠模式和建造者模式的區(qū)別

          • 工廠模式一般都是創(chuàng)建一個(gè)產(chǎn)品,注重的是把這個(gè)產(chǎn)品創(chuàng)建出來(lái),而不關(guān)心這個(gè)產(chǎn)品的組成部分。從代碼上看,工廠模式就是一個(gè)方法,用這個(gè)方法來(lái)生產(chǎn)出產(chǎn)品

          • 建造者模式也是創(chuàng)建一個(gè)產(chǎn)品,但是不僅要把這個(gè)產(chǎn)品創(chuàng)建出來(lái),還要關(guān)心這個(gè)產(chǎn)品的組成細(xì)節(jié),組成過(guò)程。從代碼上看,建造者模式在創(chuàng)建產(chǎn)品的時(shí)候,這個(gè)產(chǎn)品有很多方法,建造者模式會(huì)根據(jù)這些相同的方法按不同的執(zhí)行順序建造出不同組成細(xì)節(jié)的產(chǎn)品


          13. 深拷貝和淺拷貝

          • 淺拷貝:復(fù)制對(duì)象時(shí)只復(fù)制對(duì)象本身,包括基本數(shù)據(jù)類型的屬性,但是不會(huì)復(fù)制引用數(shù)據(jù)類型屬性指向的對(duì)象,即拷貝對(duì)象的與原對(duì)象的引用數(shù)據(jù)類型的屬性指向同一個(gè)對(duì)象

            淺拷貝沒(méi)有達(dá)到完全復(fù)制,即原對(duì)象與克隆對(duì)象之間有關(guān)系,會(huì)相互影響

          • 深拷貝:復(fù)制一個(gè)新的對(duì)象,引用數(shù)據(jù)類型指向?qū)ο髸?huì)拷貝新的一份,不再指向原有引用對(duì)象的地址

            深拷貝達(dá)到了完全復(fù)制的目的,即原對(duì)象與克隆對(duì)象之間不會(huì)相互影響


          14. 泛型知識(shí)

          Java泛型深度解析以及面試題_周將的博客-CSDN博客

          Java泛型是在JDK5引入的新特性,它提供了編譯時(shí)類型安全檢測(cè)機(jī)制。該機(jī)制允許程序員在編譯時(shí)檢測(cè)到非法的類型,泛型的本質(zhì)是參數(shù)類型。

          1?? 使用泛型的好處

          1. 泛型可以增強(qiáng)編譯時(shí)錯(cuò)誤檢測(cè),減少因類型問(wèn)題引發(fā)的運(yùn)行時(shí)異常。

          2. 泛型可以避免類型轉(zhuǎn)換。

          3. 泛型可以泛型算法,增加代碼復(fù)用性。

          2?? Java中泛型的分類

          1. 泛型類:它的定義格式是class name<T1, T2, ..., Tn>,如下, 返回一個(gè)對(duì)象中包含了code和一個(gè)data, data是一個(gè)對(duì)象,我們不能固定它是什么類型,這時(shí)候就用T泛型來(lái)代替,大大增加了代碼的復(fù)用性。



          2. public class Result<T> {
                private T data;
                private int code;

                public T getData() {
                    return data;
                }

                public void setData(T data) {
                    this.data = data;
                }
            }

          3. 泛型接口:和泛型類使用相似

          4. 泛型方法:它的定義是[public] [static] <T> 返回值類型 方法名(T 參數(shù)列表),只有在前面加<T>這種的才能算是泛型方法,比如上面的setData方法雖然有泛型,但是不能算泛型方法

          3?? 常見(jiàn)的泛型參數(shù)

          • K 鍵

          • V 值

          • N 數(shù)字

          • T 類型

          • E 元素

          • S, U, V 等,泛型聲明的多個(gè)類型

          4?? 鉆石運(yùn)算符Diamond

          鉆石操作符是在 java 7 中引入的,可以讓代碼更易讀,但它不能用于匿名的內(nèi)部類。在 java 9 中, 它可以與匿名的內(nèi)部類一起使用,從而提高代碼的可讀性。

          • JDK7以下版本需要 Box<Integer> box = new Box<Integer>();

          • JDK7及以上版本 Box<Integer> integerBox1 = new Box<>();

          5?? 受限類型參數(shù)

          • 它的作用是對(duì)泛型變量的范圍作出限制,格式:

            單一限制:<U extends Number>

            多種限制:<U extends A & B & C>

          • 多種限制的時(shí)候,類必須寫在第一個(gè)

          6?? 通配符

          通配符用?標(biāo)識(shí),分為受限制的通配符和不受限制的通配符,它使代碼更加靈活,廣泛運(yùn)用于框架中。

          比如List<Number>List<Integer>是沒(méi)有任何關(guān)系的。如果我們將print方法中參數(shù)列表部分的List聲明為List<Number> list, 那么編譯是不會(huì)通過(guò)的,但是如果我們將List定義為List<? extends Number> list或者List<?> list,那么在編譯的時(shí)候就不會(huì)報(bào)錯(cuò)了

          • 受限制的通配符:語(yǔ)法為<? extends XXX>,它可以擴(kuò)大兼容的范圍(XXX以及它的子類)

            比如上面例子中print中如果改為L(zhǎng)ist<Number>,雖然它能存儲(chǔ)Integer和Double等類型的元素,但是作為參數(shù)傳遞的時(shí)候,它只能接受List<Number>這一種類型。如果聲明為L(zhǎng)ist<? extends Number> list就不一樣了,相當(dāng)于擴(kuò)大了類型的范圍,使得代碼更加的靈活,代碼復(fù)用性更高。

            <? super T>和extends一樣,只不過(guò)extends是限定了上限,而super是限定了下限

          • 非受限制的通配符:不適用關(guān)鍵字extends或者super。比如上面print參數(shù)列表聲明為L(zhǎng)ist<?> list也可以解決問(wèn)題。?代表了未知類型。所以所有的類型都可以理解為L(zhǎng)ist<?>的子類。它的使用場(chǎng)景一般是泛型類中的方法不依賴于類型參數(shù)的時(shí)候,比如list.size(), 遍歷集合等,這樣的話并不關(guān)心List中元素的具體類型。

          7?? 泛型中的PECS原則

          PECS原則的全拼是"Producer Extends Consumer Super"。

          • 當(dāng)需要

            頻繁取值,而不需要寫值

            則使用上界通配符? extends T作為數(shù)據(jù)結(jié)構(gòu)泛型。=

          • 相反,當(dāng)需要

            頻繁寫值,而不需要取值

            則使用下屆通配符? super T作為數(shù)據(jù)結(jié)構(gòu)泛型。

          案例分析:創(chuàng)建Apple,F(xiàn)ruit兩個(gè)類,其中Apple是Fruit的子類

          public class PECS {

              ArrayList<? extends Fruit> exdentFurit;
              ArrayList<? super Fruit> superFurit;
              Apple apple = new Apple();

              private void test() {
                  Fruit a1 = exdentFurit.get(0);
                  Fruit a2 = superFurit.get(0); //Err1

                  exdentFurit.add(apple); //Err2
                  superFurit.add(apple);
              }
          }


          其中Err1和Err2行處報(bào)錯(cuò),因?yàn)檫@些操作并不符合PECS原則,逐一分析:

          • Err1
            使用
            ? super T規(guī)定泛型的數(shù)據(jù)結(jié)構(gòu),其存儲(chǔ)的值是T的父類,而這里superFruit.get()的對(duì)象為Fruit的父類對(duì)象,而指向該對(duì)象的引用類型為Fruit,父類缺少子類中的一些信息,這顯然是不對(duì)的,因此編譯器直接禁止在使用? super T泛型的數(shù)據(jù)結(jié)構(gòu)中進(jìn)行取值,只能進(jìn)行寫值,正是開(kāi)頭所說(shuō)的CS原則。

          • Err2
            使用
            ? extends T規(guī)定泛型的數(shù)據(jù)結(jié)構(gòu),其存儲(chǔ)的值是T的子類,這里exdentFruit.add()也就是向其中添加Fruit的子類對(duì)象,而Fruit可以有多種子類對(duì)象,因此當(dāng)我們進(jìn)行寫值時(shí),我們并不知道其中存儲(chǔ)的到底是哪個(gè)子類,因此寫值操作必然會(huì)出現(xiàn)問(wèn)題,所以編譯器接禁止在使用? extends T泛型的數(shù)據(jù)結(jié)構(gòu)中進(jìn)行寫,只能進(jìn)行取值,正是開(kāi)頭所說(shuō)的PE原則。

          8?? 類型擦除

          • 類型擦除作用:因?yàn)镴ava中的泛型實(shí)在JDK1.5之后新加的特性,為了兼容性,在虛擬機(jī)中運(yùn)行時(shí)是不存在泛型的,所以Java泛型是一種偽泛型,類型擦除就保證了泛型不在運(yùn)行時(shí)候出現(xiàn)。

          • 場(chǎng)景:編譯器會(huì)把泛型類型中所有的類型參數(shù)替換為它們的上(下)限,如果沒(méi)有對(duì)類型參數(shù)做出限制,那么就替換為Object類型。因此,編譯出的字節(jié)碼僅僅包含了常規(guī)類,接口和方法。

            • 在必要時(shí)插入類型轉(zhuǎn)換以保持類型安全。

            • 生成橋方法以在擴(kuò)展泛型時(shí)保持多態(tài)性

          • Bridge Methods 橋方法
            當(dāng)編譯一個(gè)擴(kuò)展參數(shù)化類的類,或一個(gè)實(shí)現(xiàn)了參數(shù)化接口的接口時(shí),編譯器有可能因此要?jiǎng)?chuàng)建一個(gè)合成方法,名為橋方法。它是類型擦除過(guò)程中的一部分。下面對(duì)橋方法代碼驗(yàn)證一下:


          • public class Node<T> {
                T t;
                public Node(T t) {
                    this.t = t;
                }
                public void set(T t) {
                    this.t = t;
                }
            }

            class MyNode extends Node<String> {
                public MyNode(String s) {
                    super(s);
                }
                @Override
                public void set(String s) {
                    super.set(s);


          • 上面Node<T>是一個(gè)泛型類型,沒(méi)有聲明上下限,所以在類型擦除后會(huì)變?yōu)镺bject類型。而MyNode類已經(jīng)聲明了實(shí)際類型參數(shù)為String類型,這樣在調(diào)用父類set方法的時(shí)候就會(huì)出現(xiàn)不匹配的情況,所以虛擬機(jī)在編譯的時(shí)候?yàn)槲覀兩闪艘粋€(gè)橋方法,我們通過(guò)javap -c MyNode.class查看字節(jié)碼文件,看到確實(shí)為我們生成了一個(gè)橋方法


          15. Java泛型的原理?什么是泛型擦除機(jī)制?

          • Java的泛型是JDK5新引入的特性,為了向下兼容,虛擬機(jī)其實(shí)是不支持泛型,所以Java實(shí)現(xiàn)的是一種偽泛型機(jī)制,也就是說(shuō)Java在編譯期擦除了所有的泛型信息,這樣Java就不需要產(chǎn)生新的類型到字節(jié)碼,所有的泛型類型最終都是一種原始類型,在Java運(yùn)行時(shí)根本就不存在泛型信息。

          • 類型擦除其實(shí)在類常量池中保存了泛型信息,運(yùn)行時(shí)還能拿到信息,比如Gson的TypeToken的使用。

          • 泛型算法實(shí)現(xiàn)的關(guān)鍵:利用受限類型參數(shù)。


          16. Java編譯器具體是如何擦除泛型的

          1. 檢查泛型類型,獲取目標(biāo)類型

          2. 擦除類型變量,并替換為限定類型

            • 如果泛型類型的類型變量沒(méi)有限定,則用Object作為原始類型

            • 如果有限定,則用限定的類型作為原始類型

            • 如果有多個(gè)限定(T extends Class1&Class2),則使用第一個(gè)邊界Class1作為原始類

          3. 在必要時(shí)插入類型轉(zhuǎn)換以保持類型安全

          4. 生成橋方法以在擴(kuò)展時(shí)保持多態(tài)性


          17. Array數(shù)組中可以用泛型嗎?

          不能,簡(jiǎn)單的來(lái)講是因?yàn)槿绻梢詣?chuàng)建泛型數(shù)組,泛型擦除會(huì)導(dǎo)致編譯能通過(guò),但是運(yùn)行時(shí)會(huì)出現(xiàn)異常。所以如果禁止創(chuàng)建泛型數(shù)組,就可以避免此類問(wèn)題。


          18. PESC原則&限定通配符和非限定通配符

          • 如果你只需要從集合中獲得類型T , 使用<? extends T>通配符

          • 如果你只需要將類型T放到集合中, 使用<? super T>通配符

          • 如果你既要獲取又要放置元素,則不使用任何通配符。例如List<String>

          • <?> 非限定通配符既不能存也不能取, 一般使用非限定通配符只有一個(gè)目的,就是為了靈活的轉(zhuǎn)型。其實(shí)List<?> 等于 List<? extends Object>。


          19. Java中List<?>和List<Object>的區(qū)別

          雖然他們都會(huì)進(jìn)行類型檢查,實(shí)質(zhì)上卻完全不同。List<?> 是一個(gè)未知類型的List,而List<Object>其實(shí)是任意類型的List。你可以把List<String>, List<Integer>賦值給List<?>,卻不能把List<String>賦值給List<Object>。


          20. for循環(huán)和forEach效率問(wèn)題

          == 遍歷ArrayList測(cè)試==

          這里向ArrayList中插入10000000條數(shù)據(jù),分別用for循環(huán)和for each循環(huán)進(jìn)行遍歷測(cè)試

          package for循環(huán)效率問(wèn)題;

          import java.util.ArrayList;

          public class Test {
              public static void main(String[] args) {
                  ArrayList<Integer> arrayList = new ArrayList<>();
                  for (int i = 0; i < 10000000; i++) {
                      arrayList.add(i);
                  }
                  int x = 0;
                  //for循環(huán)遍歷        
                  long forStart = System.currentTimeMillis();
                  for (int i = 0; i < arrayList.size(); i++) {
                      x = arrayList.get(i);
                  }
                  long forEnd = System.currentTimeMillis();
                  System.out.println("for循環(huán)耗時(shí)" + (forEnd - forStart) + "ms");
                  //for-each遍歷        
                  long forEachStart = System.currentTimeMillis();
                  for (int i : arrayList) {
                      x = i;
                  }
                  long forEachEnd = System.currentTimeMillis();
                  System.out.println("foreach耗時(shí)" + (forEachEnd - forEachStart) + "ms");
              }
          }


          根據(jù)執(zhí)行結(jié)果,可以看到for循環(huán)速度更快一點(diǎn),但是差別不太大

          我們反編譯class文件看看

          package for循環(huán)效率問(wèn)題;
          import java.util.ArrayList;
          import java.util.Iterator;
          public class Test {
              public Test() {    
              }    
              
              public static void main(String[] args) {
                  ArrayList<Integer> arrayList = new ArrayList();
                  int x;        
                  for(x = 0; x < 10000000; ++x) {            
                      arrayList.add(x);        
                  }        
                  int x = false;        
                  long forStart = System.currentTimeMillis();        
                  for(int i = 0; i < arrayList.size(); ++i) {            
                      x = (Integer)arrayList.get(i);        
                  }        
                  long forEnd = System.currentTimeMillis();        
                  System.out.println("for循環(huán)耗時(shí)" + (forEnd - forStart) + "ms");        
                  long forEachStart = System.currentTimeMillis();        
                  int i;        
                  for(Iterator var9 = arrayList.iterator(); 
                      var9.hasNext(); 
                      i = (Integer)var9.next()) {        
                  }        
                  long forEachEnd = System.currentTimeMillis();        
                  System.out.println("foreach耗時(shí)" + (forEachEnd - forEachStart) + "ms");    
              }
          }


          可以看到增強(qiáng)for循環(huán)本質(zhì)上就是使用iterator迭代器進(jìn)行遍歷

          == 遍歷LinkedList測(cè)試==

          這里向LinkedList中插入測(cè)試10000條數(shù)據(jù)進(jìn)行遍歷測(cè)試,實(shí)驗(yàn)中發(fā)現(xiàn)如果循環(huán)次數(shù)太大,for循環(huán)直接卡死;

          package for循環(huán)效率問(wèn)題;

          import java.util.LinkedList;

          public class Test2 {
              public static void main(String[] args) {
                  LinkedList<Integer> linkedList = new LinkedList<>();
                  for (int i = 0; i < 10000; i++) {
                      linkedList.add(i);
                  }
                  int x = 0;
                  //for循環(huán)遍歷 
                  long forStart = System.currentTimeMillis();
                  for (int i = 0; i < linkedList.size(); i++) {
                      x = linkedList.get(i);
                  }
                  long forEnd = System.currentTimeMillis();
                  System.out.println("for循環(huán)耗時(shí)" + (forEnd - forStart) + "ms");
                  //for-each遍歷
                  long forEachStart = System.currentTimeMillis();
                  for (int i : linkedList) {
                      x = i;
                  }
                  long forEachEnd = System.currentTimeMillis();
                  System.out.println("foreach耗時(shí)" + (forEachEnd - forEachStart) + "ms");
              }
          }


          根據(jù)結(jié)果可以看到,遍歷LinkedList時(shí)for each速度遠(yuǎn)遠(yuǎn)大于for循環(huán)速度

          反編譯class文件的源碼

           Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//
          package for循環(huán)效率問(wèn)題;

          import java.util.Iterator;
          import java.util.LinkedList;

          public class Test2 {
              public Test2() {
              }

              public static void main(String[] args) {
                  LinkedList<Integer> linkedList = new LinkedList();
                  int x;
                  for (x = 0; x < 10000; ++x) {
                      linkedList.add(x);
                  }
                  int x = false;
                  long forStart = System.currentTimeMillis();
                  for (int i = 0; i < linkedList.size(); ++i) {
                      x = (Integer) linkedList.get(i);
                  }
                  long forEnd = System.currentTimeMillis();
                  System.out.println("for循環(huán)耗時(shí)" + (forEnd - forStart) + "ms");
                  long forEachStart = System.currentTimeMillis();
                  int i;
                  for (Iterator var9 = linkedList.iterator(); var9.hasNext(); i = (Integer) var9.next()) {
                  }
                  long forEachEnd = System.currentTimeMillis();
                  System.out.println("foreach耗時(shí)" + (forEachEnd - forEachStart) + "ms");
              }
          }


          == 總結(jié) ==

          1?? 區(qū)別

          • for 循環(huán)就是按順序遍歷,隨機(jī)訪問(wèn)元素

          • for each循環(huán)本質(zhì)上是使用iterator迭代器遍歷,順序鏈表訪問(wèn)元素;

          2?? 性能比對(duì)

          • 對(duì)于arraylist底層為數(shù)組類型的結(jié)構(gòu),使用for循環(huán)遍歷比使用foreach循環(huán)遍歷稍快一些,但相差不大

          • 對(duì)于linkedlist底層為單鏈表類型的結(jié)構(gòu),使用for循環(huán)每次都要從第一個(gè)元素開(kāi)始遍歷,速度非常慢;使用foreach可以直接讀取當(dāng)前結(jié)點(diǎn),速度比f(wàn)or快很多

          3?? 原理接釋

          • ArrayList數(shù)組類型結(jié)構(gòu)對(duì)隨機(jī)訪問(wèn)比較快,而for循環(huán)中的get()方法,采用的即是隨機(jī)訪問(wèn)的方法,因此在ArrayList里,for循環(huán)較快


          • # 順序表a[3]
            - 用for循環(huán),從a[0]開(kāi)始直接讀到元素,接著直接讀a[1](順序表的優(yōu)點(diǎn),隨機(jī)訪問(wèn))
            - 用foreach,得到a[0]-a[2]的全部地址放入隊(duì)列,按順序取出隊(duì)里里的地址來(lái)訪問(wèn)元素


          • LinkedList鏈表形結(jié)構(gòu)對(duì)順序訪問(wèn)比較快,iterator中的next()方法,采用的即是順序訪問(wèn)的方法,因此在LinkedList里,使用iterator較快


            # 單鏈表b[3]
            - 用for循環(huán),從a[0]開(kāi)始讀元素、然后通過(guò)a[0]的next讀到a[1]元素、通過(guò)a[0]的next的next讀到a[2]元素,以此類推,性能影響較大,慎用!
            - 用foreach,得到a[0]-a[2]的全部地址放入隊(duì)列,按順序取出隊(duì)里里的地址來(lái)訪問(wèn)元素;



          16. NIO、BIO、AIO

          (1條消息) Netty_youthlql的博客-CSDN博客

          尚硅谷Netty教程(B站最火,人氣最高,好評(píng)如潮)_嗶哩嗶哩 (゜-゜)つロ 干杯~-bilibili

          阻塞IO 和 非阻塞IO

          IO操作分為兩個(gè)部分,即發(fā)起IO請(qǐng)求和實(shí)際IO操作,阻塞IO和非阻塞IO的區(qū)別就在于第二個(gè)步驟是否阻塞

          • 若發(fā)起IO請(qǐng)求后請(qǐng)求線程一直等待實(shí)際IO操作完成,則為阻塞IO

          • 若發(fā)起IO請(qǐng)求后請(qǐng)求線程返回而不會(huì)一直等待,則為非阻塞IO

          同步IO 和 異步IO

          IO操作分為兩個(gè)部分,即發(fā)起IO請(qǐng)求和實(shí)際IO操作,同步IO和異步IO的區(qū)別就在于第一個(gè)步驟是否阻塞

          • 若實(shí)際IO操作阻塞請(qǐng)求進(jìn)程,即請(qǐng)求進(jìn)程需要等待或輪詢查看IO操作是否就緒,則為同步IO

          • 若實(shí)際IO操作不阻塞請(qǐng)求進(jìn)程,而是由操作系統(tǒng)來(lái)進(jìn)行實(shí)際IO操作并將結(jié)果返回,則為異步IO

          NIO、BIO、AIO

          BIO表示同步阻塞式IO,服務(wù)器實(shí)現(xiàn)模式為一個(gè)連接一個(gè)線程,即客戶端有連接請(qǐng)求時(shí)服務(wù)器端就需要啟動(dòng)一個(gè)線程進(jìn)行處理,如果這個(gè)連接不做任何事情會(huì)造成不必要的線程開(kāi)銷,當(dāng)然可以通過(guò)線程池機(jī)制改善。

          NIO表示同步非阻塞IO,服務(wù)器實(shí)現(xiàn)模式為一個(gè)請(qǐng)求一個(gè)線程,即客戶端發(fā)送的連接請(qǐng)求都會(huì)注冊(cè)到多路復(fù)用器上,多路復(fù)用器輪詢到連接有I/O請(qǐng)求時(shí)才啟動(dòng)一個(gè)線程進(jìn)行處理。

          AIO表示異步非阻塞IO,服務(wù)器實(shí)現(xiàn)模式為一個(gè)有效請(qǐng)求一個(gè)線程,客戶端的I/O請(qǐng)求都是由操作系統(tǒng)先完成IO操作后再通知服務(wù)器應(yīng)用來(lái)啟動(dòng)線程進(jìn)行處理。


          17. 什么是反射

          反射是在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意一個(gè)方法和屬性;這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為 Java 語(yǔ)言的反射機(jī)制。

          反射實(shí)現(xiàn)了把java類中的各種結(jié)構(gòu)法、屬性、構(gòu)造器、類名)映射成一個(gè)個(gè)的Java對(duì)象

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

          缺點(diǎn):對(duì)性能有影響,使用反射本質(zhì)上是一種接釋操作,慢于直接執(zhí)行java代碼

          應(yīng)用場(chǎng)景

          1. JDBC中,利用反射動(dòng)態(tài)加載了數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序。

          2. Web服務(wù)器中利用反射調(diào)用了Sevlet的服務(wù)方法。

          3. Eclispe等開(kāi)發(fā)工具利用反射動(dòng)態(tài)刨析對(duì)象的類型與結(jié)構(gòu),動(dòng)態(tài)提示對(duì)象的屬性和方法。

          4. 很多框架都用到反射機(jī)制,注入屬性,調(diào)用方法,如Spring。


          18. 序列化&反序列化

          Java基礎(chǔ)學(xué)習(xí)總結(jié)——Java對(duì)象的序列化和反序列化 - 孤傲蒼狼 - 博客園 (cnblogs.com)

          1?? 什么是序列化

          序列化是指將Java對(duì)象轉(zhuǎn)化為字節(jié)序列的過(guò)程,而反序列化則是將字節(jié)序列轉(zhuǎn)化為Java對(duì)象的過(guò)程

          2?? 為什么需要序列化

          我們知道不同線程/進(jìn)程進(jìn)行遠(yuǎn)程通信時(shí)可以相互發(fā)送各種數(shù)據(jù),包括文本圖片音視頻等,Java對(duì)象不能直接傳輸,所以需要轉(zhuǎn)化為二進(jìn)制序列傳輸,所以需要序列化

          3?? 序列化的用途

          1. 把對(duì)象的字節(jié)序列永久地保存到硬盤上,通常存放在一個(gè)文件中

            在很多應(yīng)用中,需要對(duì)某些對(duì)象進(jìn)行序列化,讓它們離開(kāi)內(nèi)存空間,入住物理硬盤,以便長(zhǎng)期保存。比如最常見(jiàn)的是Web服務(wù)器中的Session對(duì)象,當(dāng)有10萬(wàn)用戶并發(fā)訪問(wèn),就有可能出現(xiàn)10萬(wàn)個(gè)Session對(duì)象,內(nèi)存可能吃不消,于是Web容器就會(huì)把一些seesion先序列化到硬盤中,等要用了,再把保存在硬盤中的對(duì)象還原到內(nèi)存中

          2. 在網(wǎng)絡(luò)上傳送對(duì)象的字節(jié)序列

            當(dāng)兩個(gè)進(jìn)程在進(jìn)行遠(yuǎn)程通信時(shí),彼此可以發(fā)送各種類型的數(shù)據(jù)。無(wú)論是何種類型的數(shù)據(jù),都會(huì)以二進(jìn)制序列的形式在網(wǎng)絡(luò)上傳送。發(fā)送方需要把這個(gè)Java對(duì)象轉(zhuǎn)換為字節(jié)序列,才能在網(wǎng)絡(luò)上傳送;接收方則需要把字節(jié)序列再恢復(fù)為Java對(duì)象

          4?? JDK類庫(kù)中的序列化API

          • java.io.ObjectOutputStream代表對(duì)象輸出流,它的writeObject(Object obj)方法可對(duì)參數(shù)指定的obj對(duì)象進(jìn)行序列化,把得到的字節(jié)序列寫到一個(gè)目標(biāo)輸出流中

          • java.io.ObjectInputStream代表對(duì)象輸入流,它的readObject()方法從一個(gè)源輸入流中讀取字節(jié)序列,再把它們反序列化為一個(gè)對(duì)象,并將其返回

          只有實(shí)現(xiàn)了SerializableExternalizable接口的類的對(duì)象才能被序列化。Externalizable接口繼承自Serializable接口,實(shí)現(xiàn)Externalizable接口的類完全由自身來(lái)控制序列化的行為,而僅實(shí)現(xiàn)Serializable接口的類可以 采用默認(rèn)的序列化方式

          對(duì)象序列化包括如下步驟:

          1. 創(chuàng)建一個(gè)對(duì)象輸出流,它可以包裝一個(gè)其他類型的目標(biāo)輸出流,如文件輸出流

          2. 通過(guò)對(duì)象輸出流的writeObject()方法寫對(duì)象

          對(duì)象反序列化的步驟如下:

          1. 創(chuàng)建一個(gè)對(duì)象輸入流,它可以包裝一個(gè)其他類型的源輸入流,如文件輸入流

          2. 通過(guò)對(duì)象輸入流的readObject()方法讀取對(duì)象

          5?? serialVersionUID的作用

          serialVersionUID: 字面意思上是序列化的版本號(hào),凡是實(shí)現(xiàn)Serializable接口的類都有一個(gè)表示序列化版本標(biāo)識(shí)符的靜態(tài)變量

          如果實(shí)現(xiàn)Serializable接口的類如果類中沒(méi)有添加serialVersionUID,那么就會(huì)出現(xiàn)警告提示

          serialVersionUID有兩種生成方式:

          1. 采用Add default serial version ID方式生成的serialVersionUID是1L,例如:


            private static final long serialVersionUID = 1L;


          2.采用Add generated serial version ID這種方式生成的serialVersionUID是根據(jù)類名,接口名,方法和屬性等來(lái)生成的,例如:


          private static final long serialVersionUID = 4603642343377807741L;



          19. 動(dòng)態(tài)代理是什么?有哪些應(yīng)用?

          當(dāng)想要給實(shí)現(xiàn)了某個(gè)接口的類中的方法,加一些額外的處理。比如說(shuō)加日志,加事務(wù)等。可以給這個(gè)類創(chuàng)建一個(gè)代理,故名思議就是創(chuàng)建一個(gè)新的類,這個(gè)類不僅包含原來(lái)類方法的功能,而且還在原來(lái)的基礎(chǔ)上添加了額外處理的新類。這個(gè)代理類并不是定義好的,是動(dòng)態(tài)生成的。具有解耦意義,靈活,擴(kuò)展性強(qiáng)。

          應(yīng)用

          • Spring的AOP

          • 加事務(wù)

          • 加權(quán)限

          • 加日志


          20. 怎么實(shí)現(xiàn)動(dòng)態(tài)代理

          在java的java.lang.reflect包下提供了一個(gè)Proxy類和一個(gè)InvocationHandler接口,通過(guò)這個(gè)類和這個(gè)接口可以生成JDK動(dòng)態(tài)代理類和動(dòng)態(tài)代理對(duì)象

          • java.lang.reflect.Proxy是所有動(dòng)態(tài)代理的父類。它通過(guò)靜態(tài)方法newProxyInstance()來(lái)創(chuàng)建動(dòng)態(tài)代理的class對(duì)象和實(shí)例。

          • 每一個(gè)動(dòng)態(tài)代理實(shí)例都有一個(gè)關(guān)聯(lián)的InvocationHandler。通過(guò)代理實(shí)例調(diào)用方法,方法調(diào)用請(qǐng)求會(huì)被轉(zhuǎn)發(fā)給InvocationHandler的invoke方法。

          1. 首先定義一個(gè)IncocationHandler處理器接口實(shí)現(xiàn)類,實(shí)現(xiàn)其invoke()方法

          2. 通過(guò)Proxy.newProxyInstance生成代理類對(duì)象

          package demo3;

          import java.lang.reflect.InvocationHandler;
          import java.lang.reflect.Method;
          import java.lang.reflect.Proxy;

          public class ProxyInvocationHandler implements InvocationHandler {
              //定義真實(shí)角色
              private Rent host;

              //真實(shí)角色set方法
              public void setHost(Rent host) {
                  this.host = host;
              }

              /**
               生成代理類方法
               1. 類加載器,為當(dāng)前類即可
               2. 代理類實(shí)現(xiàn)的接口
               3. 處理器接口對(duì)象
              **/
              public Object getProxy() {
                  return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                          host.getClass().getInterfaces(), this);
              }

              //處理代理實(shí)例,并返回結(jié)果
              //方法在此調(diào)用
              public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                  //調(diào)用真實(shí)角色方法,相當(dāng)于調(diào)用rent()方法
                  Object result = method.invoke(host, args);
                  //附加方法
                  seeHouse();
                  contract();
                  fare();
                  return result;
              }

              //看房
              public void seeHouse() {
                  System.out.println("中介帶你看房");
              }

              //簽合同
              public void contract() {
                  System.out.println("租賃合同");
              }

              //收中介費(fèi)
              public void fare() {
                  System.out.println("收中介費(fèi)");
              }
          }


          package demo3;

          public class Client {
              public static void main(String[] args) {
                  //真實(shí)角色:房東
                  Host host = new Host();
                  //處理器接口對(duì)象
                  ProxyInvocationHandler handler = new ProxyInvocationHandler();
                  //設(shè)置要代理的真實(shí)角色
                  handler.setHost(host);
                  //動(dòng)態(tài)生成代理類
                  Rent proxy = (Rent) handler.getProxy();
                  //調(diào)用方法
                  proxy.rent();
              }
          }



          21. 如何實(shí)現(xiàn)對(duì)象克隆?

          有兩種方式:

                   1.、實(shí)現(xiàn)Cloneable接口并重寫Object類中的clone()方法;

          1. protected Object clone() throws CloneNotSupportedException {
                test_paper paper = (test_paper) super.clone();
                paper.date = (Date) date.clone();
                return paper;
            }

          2. 2、實(shí)現(xiàn)Serializable接口,通過(guò)對(duì)象的序列化和反序列化實(shí)現(xiàn)克隆,可以實(shí)現(xiàn)真正的深度克隆


          3. import java.io.ByteArrayInputStream;
            import java.io.ByteArrayOutputStream;
            import java.io.ObjectInputStream;
            import java.io.ObjectOutputStream;
            import java.util.Date;

            @SuppressWarnings("all")
            public class Client {
                public static void main(String[] args) throws Exception {
                    Date date = new Date();
                    String name = "zsr";
                    test_paper paper1 = new test_paper(name, date);
                    //通過(guò)序列化和反序列化來(lái)實(shí)現(xiàn)深克隆
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    ObjectOutputStream obs = new ObjectOutputStream(bos);
                    obs.writeObject(paper1);
                    byte a[] = bos.toByteArray();
                    ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(a));
                    test_paper paper3 = (test_paper) ois.readObject();//獲取到新對(duì)象
                    paper3.getDate().setDate(1000);//改變非基本類型屬性
                    System.out.println(paper1);
                    System.out.println(paper3);
                }
            }








          瀏覽 90
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  亚欧成人网站 | 高清无码国产在线观看 | 一级黄色日逼片 | 青青草在线视频免费播放 | 天天操天天日天天干男 |