整理來自互聯(lián)網(wǎng)
1谢谦,JDK:Java Development Kit,java的開發(fā)和運(yùn)行環(huán)境萝衩,java的開發(fā)工具和jre回挽。
2,JRE:Java Runtime Environment猩谊,java程序的運(yùn)行環(huán)境千劈,java運(yùn)行的所需的類庫+JVM(java虛擬機(jī))径荔。
3贷盲,配置環(huán)境變量:讓java jdk\bin目錄下的工具,可以在任意目錄下運(yùn)行,原因是宜鸯,將該工具所在目錄告訴了系統(tǒng),當(dāng)使用該工具時(shí)遮怜,由系統(tǒng)幫我們?nèi)フ抑付ǖ哪夸洝?/p>
環(huán)境變量的配置:
1)永久配置方式:JAVA_HOME=%安裝路徑%\Java\jdk
? ? path=%JAVA_HOME%\bin
2)臨時(shí)配置方式:set path=%path%;C:\Program Files\Java\jdk\bin
特點(diǎn):系統(tǒng)默認(rèn)先去當(dāng)前路徑下找要執(zhí)行的程序淋袖,如果沒有,再去path中設(shè)置的路徑下找锯梁。
classpath的配置:
1)永久配置方式:classpath=.;c:\;e:\
2)臨時(shí)配置方式:set classpath=.;c:\;e:\
注意:在定義classpath環(huán)境變量時(shí)即碗,需要注意的情況
如果沒有定義環(huán)境變量classpath,java啟動(dòng)jvm后陌凳,會在當(dāng)前目錄下查找要運(yùn)行的類文件剥懒;
如果指定了classpath,那么會在指定的目錄下查找要運(yùn)行的類文件合敦。
還會在當(dāng)前目錄找嗎初橘?兩種情況:
CLASSPATH是什么?它的作用是什么?
它是javac編譯器的一個(gè)環(huán)境變量保檐。它的作用與import耕蝉、package關(guān)鍵字有關(guān)。當(dāng)你寫下improt java.util.*時(shí)夜只,編譯器面對import關(guān)鍵字時(shí)垒在,就知道你要引入java.util這個(gè)package中的類;但是編譯器如何知道你把這個(gè)package放在哪里了呢扔亥?所以你首先得告訴編譯器這個(gè)package的所在位置场躯;如何告訴它呢?就是設(shè)置CLASSPATH啦 :) 如果java.util這個(gè)package在c:/jdk/ 目錄下砸王,你得把c:/jdk/這個(gè)路徑設(shè)置到CLASSPATH中去推盛!當(dāng)編譯器面對import java.util.*這個(gè)語句時(shí),它先會查找CLASSPATH所指定的目錄谦铃,并檢視子目錄java/util是否存在耘成,然后找出名稱吻合的已編譯文件(.class文件)。如果沒有找到就會報(bào)錯(cuò)驹闰!CLASSPATH有點(diǎn)像c/c++編譯器中的INCLUDE路徑的設(shè)置哦瘪菌,是不是?當(dāng)c/c++編譯器遇到include 這樣的語句嘹朗,它是如何運(yùn)作的师妙?哦,其實(shí)道理都差不多屹培!搜索INCLUDE路徑默穴,檢視文件!當(dāng)你自己開發(fā)一個(gè)package時(shí)褪秀,然后想要用這個(gè)package中的類蓄诽;自然,你也得把這個(gè)package所在的目錄設(shè)置到CLASSPATH中去媒吗!CLASSPATH的設(shè)定仑氛,對JAVA的初學(xué)者而言是一件棘手的事。所以Sun讓JAVA2的JDK更聰明一些闸英。你會發(fā)現(xiàn)锯岖,在你安裝之后,即使完全沒有設(shè)定CLASSPATH甫何,你仍然能夠編譯基本的JAVA程序出吹,并且加以執(zhí)行。
PATH環(huán)境變量
PATH環(huán)境變量辙喂。作用是指定命令搜索路徑捶牢,在命令行下面執(zhí)行命令如javac編譯java程序時(shí)赃额,它會到PATH變量所指定的路徑中查找看是否能找到相應(yīng)的命令程序。我們需要把jdk安裝目錄下的bin目錄增加到現(xiàn)有的PATH變量中叫确,bin目錄中包含經(jīng)常要用到的可執(zhí)行文件如javac/java/javadoc等待跳芳,設(shè)置好PATH變量后,就可以在任何目錄下執(zhí)行javac/java等工具了竹勉。
4飞盆,javac命令和java命令做什么事情呢?
要知道java是分兩部分的:一個(gè)是編譯次乓,一個(gè)是運(yùn)行吓歇。
javac:負(fù)責(zé)的是編譯的部分,當(dāng)執(zhí)行javac時(shí)票腰,會啟動(dòng)java的編譯器程序城看。對指定擴(kuò)展名的.java文件進(jìn)行編譯。 生成了jvm可以識別的字節(jié)碼文件杏慰。也就是class文件测柠,也就是java的運(yùn)行程序。
java:負(fù)責(zé)運(yùn)行的部分.會啟動(dòng)jvm.加載運(yùn)行時(shí)所需的類庫,并對class文件進(jìn)行執(zhí)行.
一個(gè)文件要被執(zhí)行,必須要有一個(gè)執(zhí)行的起始點(diǎn),這個(gè)起始點(diǎn)就是main函數(shù).
標(biāo)示符:
? ? 1)缘滥,數(shù)字不可以開頭轰胁。
? ? 2),不可以使用關(guān)鍵字朝扼。
變量的作用域和生存期:
1. 變量的作用域:作用域從變量定義的位置開始赃阀,到該變量所在的那對大括號結(jié)束;
生命周期:變量從定義的位置開始就在內(nèi)存中活了擎颖;
變量到達(dá)它所在的作用域的時(shí)候就在內(nèi)存中消失了榛斯;
數(shù)據(jù)類型:
1):基本數(shù)據(jù)類型:byte、short搂捧、int驮俗、long、float异旧、double意述、char提佣、boolean
運(yùn)算符號:
4)吮蛹、邏輯運(yùn)算符。
? ? ? ? & | ^ ! && ||
? ? ? ? 邏輯運(yùn)算符除了 ! 外都是用于連接兩個(gè)boolean類型表達(dá)式拌屏。
? ? ? ? &: 只有兩邊都為true結(jié)果是true潮针。否則就是false。
? ? ? ? |:只要兩邊都為false結(jié)果是false倚喂,否則就是true
? ? ? ? ^:異或:和或有點(diǎn)不一樣每篷。
? ? ? ? ? ? 兩邊結(jié)果一樣瓣戚,就為false。
? ? ? ? ? ? 兩邊結(jié)果不一樣焦读,就為true.
& 和 &&區(qū)別: & :無論左邊結(jié)果是什么子库,右邊都參與運(yùn)算。
&&:短路與矗晃,如果左邊為false仑嗅,那么右邊不參數(shù)與運(yùn)算。
| 和|| 區(qū)別:|:兩邊都運(yùn)算张症。
||:短路或仓技,如果左邊為true,那么右邊不參與運(yùn)算俗他。
5)脖捻、位運(yùn)算符:用于操作二進(jìn)制位的運(yùn)算符。
? ? ? ? & | ^
? ? ? ? << >> >>>(無符號右移)
? ? 練習(xí):對兩個(gè)變量的數(shù)據(jù)進(jìn)行互換兆衅。不需要第三方變量地沮。
? ? ? ? ? ? int a = 3,b = 5;-->b = 3,a = 5;
? ? ? ? 方法一:
? ? ? ? ? ? a = a + b; a = 8;
? ? ? ? ? ? b = a - b; b = 3;
? ? ? ? ? ? a = a - b; a = 5;
? ? ? ? 方法二:
? ? ? ? ? ? a = a ^ b;//
? ? ? ? ? ? b = a ^ b;//b = a ^ b ^ b = a
? ? ? ? ? ? a = a ^ b;//a = a ^ b ^ a = b;
? ? ? ? 練習(xí):高效的算出 2*8 = 2<<3;
重載的定義是:在一個(gè)類中,如果出現(xiàn)了兩個(gè)或者兩個(gè)以上的同名函數(shù)羡亩,只要它們的參數(shù)的個(gè)數(shù)诉濒,或者參數(shù)的類型不同,即可稱之為該函數(shù)重載了夕春。
如何區(qū)分重載:當(dāng)函數(shù)同名時(shí)未荒,只看參數(shù)列表。和返回值類型沒關(guān)系及志。
重寫:父類與子類之間的多態(tài)性片排,對父類的函數(shù)進(jìn)行重新定義。如果在子類中定義某方法與其父類有相同的名稱和參數(shù)速侈,我們說該方法被重寫 (Overriding)率寡。
Java內(nèi)存管理
Java內(nèi)存管理:深入Java內(nèi)存區(qū)域
Java與C++之間有一堵由內(nèi)存動(dòng)態(tài)分配和垃圾收集技術(shù)所圍成的高墻,墻外面的人想進(jìn)去倚搬,墻里面的人卻想出來冶共。
概述:
對于從事C和C++程序開發(fā)的開發(fā)人員來說,在內(nèi)存管理領(lǐng)域每界,他們既是擁有最高權(quán)力的皇帝捅僵,又是從事最基礎(chǔ)工作的勞動(dòng)人民—既擁有每一個(gè)對象的"所有權(quán)",又擔(dān)負(fù)著每一個(gè)對象生命開始到終結(jié)的維護(hù)責(zé)任眨层。
對于Java程序員來說庙楚,在虛擬機(jī)的自動(dòng)內(nèi)存管理機(jī)制的幫助下,不再需要為每一個(gè)new操作去寫配對的delete/free代碼趴樱,而且不容易出現(xiàn)內(nèi)存泄漏和內(nèi)存溢出問題馒闷,看起來由虛擬機(jī)管理內(nèi)存一切都很美好酪捡。不過,也正是因?yàn)镴ava程序員把內(nèi)存控制的權(quán)力交給了Java虛擬機(jī)纳账,一旦出現(xiàn)內(nèi)存泄漏和溢出方面的問題逛薇,如果不了解虛擬機(jī)是怎樣使用內(nèi)存的,那排查錯(cuò)誤將會成為一項(xiàng)異常艱難的工作疏虫。
運(yùn)行時(shí)數(shù)據(jù)區(qū)域
Java虛擬機(jī)在執(zhí)行Java程序的過程中會把它所管理的內(nèi)存劃分為若干個(gè)不同的數(shù)據(jù)區(qū)域金刁。這些區(qū)域都有各自的用途,以及創(chuàng)建和銷毀的時(shí)間议薪,有的區(qū)域隨著虛擬機(jī)進(jìn)程的啟動(dòng)而存在尤蛮,有些區(qū)域則是依賴用戶線程的啟動(dòng)和結(jié)束而建立和銷毀。根據(jù)《Java虛擬機(jī)規(guī)范(第2版)》的規(guī)定斯议,Java虛擬機(jī)所管理的內(nèi)存將會包括以下幾個(gè)運(yùn)行時(shí)數(shù)據(jù)區(qū)域产捞,如下圖所示:
程序計(jì)數(shù)器
程序計(jì)數(shù)器(Program Counter Register)是一塊較小的內(nèi)存空間,它的作用可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器哼御。在虛擬機(jī)的概念模型里(僅是概念模型坯临,各種虛擬機(jī)可能會通過一些更高效的方式去實(shí)現(xiàn)),字節(jié)碼解釋器工作時(shí)就是通過改變這個(gè)計(jì)數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令恋昼,分支看靠、循環(huán)、跳轉(zhuǎn)液肌、異常處理挟炬、線程恢復(fù)等基礎(chǔ)功能都需要依賴這個(gè)計(jì)數(shù)器來完成。 由于Java虛擬機(jī)的多線程是通過線程輪流切換并分配處理器執(zhí)行時(shí)間的方式來實(shí)現(xiàn)的嗦哆,在任何一個(gè)確定的時(shí)刻谤祖,一個(gè)處理器(對于多核處理器來說是一個(gè)內(nèi)核)只會執(zhí)行一條線程中的指令。因此老速,為了線程切換后能恢復(fù)到正確的執(zhí)行位置粥喜,每條線程都需要有一個(gè)獨(dú)立的程序計(jì)數(shù)器,各條線程之間的計(jì)數(shù)器互不影響橘券,獨(dú)立存儲额湘,我們稱這類內(nèi)存區(qū)域?yàn)?線程私有"的內(nèi)存。 如果線程正在執(zhí)行的是一個(gè)Java方法旁舰,這個(gè)計(jì)數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址锋华;如果正在執(zhí)行的是Natvie方法,這個(gè)計(jì)數(shù)器值則為空(Undefined)鬓梅。此內(nèi)存區(qū)域是唯一一個(gè)在Java虛擬機(jī)規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域供置。
Java虛擬機(jī)棧
與程序計(jì)數(shù)器一樣谨湘,Java虛擬機(jī)棧(Java Virtual? Machine Stacks)也是線程私有的绽快,它的生命周期與線程相同芥丧。虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個(gè)方法被執(zhí)行的時(shí)候都會同時(shí)創(chuàng)建一個(gè)棧幀(Stack Frame)用于存儲局部變量表、操作棧坊罢、動(dòng)態(tài)鏈接续担、方法出口等信息。每一個(gè)方法被調(diào)用直至執(zhí)行完成的過程活孩,就對應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中從入棧到出棧的過程物遇。
經(jīng)常有人把Java內(nèi)存區(qū)分為堆內(nèi)存(Heap)和棧內(nèi)存(Stack),這種分法比較粗糙憾儒,Java內(nèi)存區(qū)域的劃分實(shí)際上遠(yuǎn)比這復(fù)雜询兴。這種劃分方式的流行只能說明大多數(shù)程序員最關(guān)注的、與對象內(nèi)存分配關(guān)系最密切的內(nèi)存區(qū)域是這兩塊起趾。其中所指的"堆"在后面會專門講述诗舰,而所指的"棧"就是現(xiàn)在講的虛擬機(jī)棧,或者說是虛擬機(jī)棧中的局部變量表部分训裆。
局部變量表存放了編譯期可知的各種基本數(shù)據(jù)類型(boolean眶根、byte、char边琉、short属百、int、float变姨、long族扰、double)、對象引用(reference類型)定欧,它不等同于對象本身别伏,根據(jù)不同的虛擬機(jī)實(shí)現(xiàn),它可能是一個(gè)指向?qū)ο笃鹗嫉刂返囊弥羔樣嵌睿部赡苤赶蛞粋€(gè)代表對象的句柄或者其他與此對象相關(guān)的位置)和returnAddress類型(指向了一條字節(jié)碼指令的地址)厘肮。
其中64位長度的long和double類型的數(shù)據(jù)會占用2個(gè)局部變量空間(Slot),其余的數(shù)據(jù)類型只占用1個(gè)睦番。局部變量表所需的內(nèi)存空間在編譯期間完成分配类茂,當(dāng)進(jìn)入一個(gè)方法時(shí),這個(gè)方法需要在幀中分配多大的局部變量空間是完全確定的托嚣,在方法運(yùn)行期間不會改變局部變量表的大小巩检。 在Java虛擬機(jī)規(guī)范中,對這個(gè)區(qū)域規(guī)定了兩種異常狀況:如果線程請求的棧深度大于虛擬機(jī)所允許的深度示启,將拋出StackOverflowError異常兢哭;如果虛擬機(jī)棧可以動(dòng)態(tài)擴(kuò)展(當(dāng)前大部分的Java虛擬機(jī)都可動(dòng)態(tài)擴(kuò)展夫嗓,只不過Java虛擬機(jī)規(guī)范中也允許固定長度的虛擬機(jī)棧)迟螺,當(dāng)擴(kuò)展時(shí)無法申請到足夠的內(nèi)存時(shí)會拋出OutOfMemoryError異常冲秽。
本地方法棧
本地方法棧(Native Method Stacks)與虛擬機(jī)棧所發(fā)揮的作用是非常相似的,其區(qū)別不過是虛擬機(jī)棧為虛擬機(jī)執(zhí)行Java方法(也就是字節(jié)碼)服務(wù)矩父,而本地方法棧則是為虛擬機(jī)使用到的Native方法服務(wù)锉桑。虛擬機(jī)規(guī)范中對本地方法棧中的方法使用的語言、使用方式與數(shù)據(jù)結(jié)構(gòu)并沒有強(qiáng)制規(guī)定窍株,因此具體的虛擬機(jī)可以自由實(shí)現(xiàn)它民轴。甚至有的虛擬機(jī)(譬如Sun HotSpot虛擬機(jī))直接就把本地方法棧和虛擬機(jī)棧合二為一。與虛擬機(jī)棧一樣球订,本地方法棧區(qū)域也會拋出StackOverflowError和OutOfMemoryError異常后裸。
Java堆
對于大多數(shù)應(yīng)用來說,Java堆(Java Heap)是Java虛擬機(jī)所管理的內(nèi)存中最大的一塊冒滩。Java堆是被所有線程共享的一塊內(nèi)存區(qū)域轻抱,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建。此內(nèi)存區(qū)域的唯一目的就是存放對象實(shí)例旦部,幾乎所有的對象實(shí)例都在這里分配內(nèi)存祈搜。這一點(diǎn)在Java虛擬機(jī)規(guī)范中的描述是:所有的對象實(shí)例以及數(shù)組都要在堆上分配,但是隨著JIT編譯器的發(fā)展與逃逸分析技術(shù)的逐漸成熟士八,棧上分配容燕、標(biāo)量替換優(yōu)化技術(shù)將會導(dǎo)致一些微妙的變化發(fā)生,所有的對象都分配在堆上也漸漸變得不是那么"絕對"了婚度。
Java堆是垃圾收集器管理的主要區(qū)域蘸秘,因此很多時(shí)候也被稱做"GC堆"(Garbage Collected Heap,幸好國內(nèi)沒翻譯成"垃圾堆")蝗茁。如果從內(nèi)存回收的角度看醋虏,由于現(xiàn)在收集器基本都是采用的分代收集算法,所以Java堆中還可以細(xì)分為:新生代和老年代哮翘;再細(xì)致一點(diǎn)的有Eden空間颈嚼、From Survivor空間、To Survivor空間等饭寺。如果從內(nèi)存分配的角度看阻课,線程共享的Java堆中可能劃分出多個(gè)線程私有的分配緩沖區(qū)(Thread Local Allocation Buffer,TLAB)艰匙。不過限煞,無論如何劃分孝冒,都與存放內(nèi)容無關(guān)蒸辆,無論哪個(gè)區(qū)域亿汞,存儲的都仍然是對象實(shí)例盖腿,進(jìn)一步劃分的目的是為了更好地回收內(nèi)存忧风,或者更快地分配內(nèi)存荸恕。在本章中粪糙,我們僅僅針對內(nèi)存區(qū)域的作用進(jìn)行討論蛾方,Java堆中的上述各個(gè)區(qū)域的分配和回收等細(xì)節(jié)將會是下一章的主題。
根據(jù)Java虛擬機(jī)規(guī)范的規(guī)定抚官,Java堆可以處于物理上不連續(xù)的內(nèi)存空間中扬跋,只要邏輯上是連續(xù)的即可阶捆,就像我們的磁盤空間一樣凌节。在實(shí)現(xiàn)時(shí),既可以實(shí)現(xiàn)成固定大小的倍奢,也可以是可擴(kuò)展的卒煞,不過當(dāng)前主流的虛擬機(jī)都是按照可擴(kuò)展來實(shí)現(xiàn)的(通過-Xmx和-Xms控制)叼架。如果在堆中沒有內(nèi)存完成實(shí)例分配,并且堆也無法再擴(kuò)展時(shí)乖订,將會拋出OutOfMemoryError異常甜无。
方法區(qū)
方法區(qū)(Method Area)與Java堆一樣岂丘,是各個(gè)線程共享的內(nèi)存區(qū)域,它用于存儲已被虛擬機(jī)加載的類信息翩概、常量、靜態(tài)變量评姨、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)吐句。雖然Java虛擬機(jī)規(guī)范把方法區(qū)描述為堆的一個(gè)邏輯部分嗦枢,但是它卻有一個(gè)別名叫做Non-Heap(非堆)侣诺,目的應(yīng)該是與Java堆區(qū)分開來。
對于習(xí)慣在HotSpot虛擬機(jī)上開發(fā)和部署程序的開發(fā)者來說搔确,很多人愿意把方法區(qū)稱為"永久代"Permanent Generation),本質(zhì)上兩者并不等價(jià),僅僅是因?yàn)镠otSpot虛擬機(jī)的設(shè)計(jì)團(tuán)隊(duì)選擇把GC分代收集擴(kuò)展至方法區(qū)宇葱,或者說使用永久代來實(shí)現(xiàn)方法區(qū)而已。對于其他虛擬機(jī)(如BEA JRockit、IBM J9等)來說是不存在永久代的概念的年局。即使是HotSpot虛擬機(jī)本身,根據(jù)官方發(fā)布的路線圖信息,現(xiàn)在也有放棄永久代并"搬家"至Native Memory來實(shí)現(xiàn)方法區(qū)的規(guī)劃了验庙。
Java虛擬機(jī)規(guī)范對這個(gè)區(qū)域的限制非常寬松,除了和Java堆一樣不需要連續(xù)的內(nèi)存和可以選擇固定大小或者可擴(kuò)展外湃交,還可以選擇不實(shí)現(xiàn)垃圾收集痛阻。相對而言俏扩,垃圾收集行為在這個(gè)區(qū)域是比較少出現(xiàn)的,但并非數(shù)據(jù)進(jìn)入了方法區(qū)就如永久代的名字一樣"永久"存在了。這個(gè)區(qū)域的內(nèi)存回收目標(biāo)主要是針對常量池的回收和對類型的卸載彬檀,一般來說這個(gè)區(qū)域的回收"成績"比較難以令人滿意诽偷,尤其是類型的卸載深浮,條件相當(dāng)苛刻,但是這部分區(qū)域的回收確實(shí)是有必要的玄柠。在Sun公司的BUG列表中, 曾出現(xiàn)過的若干個(gè)嚴(yán)重的BUG就是由于低版本的HotSpot虛擬機(jī)對此區(qū)域未完全回收而導(dǎo)致內(nèi)存泄漏娃闲。根據(jù)Java虛擬機(jī)規(guī)范的規(guī)定,當(dāng)方法區(qū)無法滿足內(nèi)存分配需求時(shí),將拋出OutOfMemoryError異常。
運(yùn)行時(shí)常量池
運(yùn)行時(shí)常量池(Runtime Constant Pool)是方法區(qū)的一部分纯衍。Class文件中除了有類的版本、字段、方法、接口等描述等信息外鹅龄,還有一項(xiàng)信息是常量池(Constant Pool Table)扮休,用于存放編譯期生成的各種字面量和符號引用蜗搔,這部分內(nèi)容將在類加載后存放到方法區(qū)的運(yùn)行時(shí)常量池中兄渺。 Java虛擬機(jī)對Class文件的每一部分(自然也包括常量池)的格式都有嚴(yán)格的規(guī)定挂谍,每一個(gè)字節(jié)用于存儲哪種數(shù)據(jù)都必須符合規(guī)范上的要求炼绘,這樣才會被虛擬機(jī)認(rèn)可、裝載和執(zhí)行铅辞。但對于運(yùn)行時(shí)常量池苇倡,Java虛擬機(jī)規(guī)范沒有做任何細(xì)節(jié)的要求晓褪,不同的提供商實(shí)現(xiàn)的虛擬機(jī)可以按照自己的需要來實(shí)現(xiàn)這個(gè)內(nèi)存區(qū)域涣仿。不過米罚,一般來說录择,除了保存Class文件中描述的符號引用外塘秦,還會把翻譯出來的直接引用也存儲在運(yùn)行時(shí)常量池中精偿。運(yùn)行時(shí)常量池相對于Class文件常量池的另外一個(gè)重要特征是具備動(dòng)態(tài)性笔咽,Java語言并不要求常量一定只能在編譯期產(chǎn)生历造,也就是并非預(yù)置入Class文件中常量池的內(nèi)容才能進(jìn)入方法區(qū)運(yùn)行時(shí)常量池吭产,運(yùn)行期間也可能將新的常量放入池中橄霉,這種特性被開發(fā)人員利用得比較多的便是String類的intern()方法医吊。既然運(yùn)行時(shí)常量池是方法區(qū)的一部分卿堂,自然會受到方法區(qū)內(nèi)存的限制览绿,當(dāng)常量池?zé)o法再申請到內(nèi)存時(shí)會拋出OutOfMemoryError異常。
對象訪問
介紹完Java虛擬機(jī)的運(yùn)行時(shí)數(shù)據(jù)區(qū)之后诀蓉,我們就可以來探討一個(gè)問題:在Java語言中,對象訪問是如何進(jìn)行的份名?對象訪問在Java語言中無處不在,是最普通的程序行為,但即使是最簡單的訪問,也會卻涉及Java棧豌蟋、Java堆、方法區(qū)這三個(gè)最重要內(nèi)存區(qū)域之間的關(guān)聯(lián)關(guān)系往声,如下面的這句代碼:
Object obj = new Object();
假設(shè)這句代碼出現(xiàn)在方法體中听哭,那"Object obj"這部分的語義將會反映到Java棧的本地變量表中陆盘,作為一個(gè)reference類型數(shù)據(jù)出現(xiàn)妻顶。而"new Object()"這部分的語義將會反映到Java堆中,形成一塊存儲了Object類型所有實(shí)例數(shù)據(jù)值(Instance Data,對象中各個(gè)實(shí)例字段的數(shù)據(jù))的結(jié)構(gòu)化內(nèi)存,根據(jù)具體類型以及虛擬機(jī)實(shí)現(xiàn)的對象內(nèi)存布局(Object Memory Layout)的不同庞钢,這塊內(nèi)存的長度是不固定的蓝角。另外,在Java堆中還必須包含能查找到此對象類型數(shù)據(jù)(如對象類型、父類、實(shí)現(xiàn)的接口、方法等)的地址信息,這些類型數(shù)據(jù)則存儲在方法區(qū)中盖灸。
由于reference類型在Java虛擬機(jī)規(guī)范里面只規(guī)定了一個(gè)指向?qū)ο蟮囊眉叵伲]有定義這個(gè)引用應(yīng)該通過哪種方式去定位,以及訪問到Java堆中的對象的具體位置邮府,因此不同虛擬機(jī)實(shí)現(xiàn)的對象訪問方式會有所不同未檩,主流的訪問方式有兩種:使用句柄和直接指針。 如果使用句柄訪問方式,Java堆中將會劃分出一塊內(nèi)存來作為句柄池顽决,reference中存儲的就是對象的句柄地址蚓耽,而句柄中包含了對象實(shí)例數(shù)據(jù)和類型數(shù)據(jù)各自的具體地址信息鹦付,如下圖所示:
如果使用的是直接指針訪問方式,Java 堆對象的布局中就必須考慮如何放置訪問類型數(shù)據(jù)的相關(guān)信息,reference中直接存儲的就是對象地址,如下圖所示:
這兩種對象的訪問方式各有優(yōu)勢,使用句柄訪問方式的最大好處就是reference中存儲的是穩(wěn)定的句柄地址码荔,在對象被移動(dòng)(垃圾收集時(shí)移動(dòng)對象是非常普遍的行為)時(shí)只會改變句柄中的實(shí)例數(shù)據(jù)指針眉尸,而reference本身不需要被修改慢宗。使用直接指針訪問方式的最大好處就是速度更快嘴脾,它節(jié)省了一次指針定位的時(shí)間開銷樟插,由于對象的訪問在Java中非常頻繁,因此這類開銷積少成多后也是一項(xiàng)非常可觀的執(zhí)行成本煮盼。就本書討論的主要虛擬機(jī)Sun HotSpot而言短纵,它是使用第二種方式進(jìn)行對象訪問的,但從整個(gè)軟件開發(fā)的范圍來看僵控,各種語言和框架使用句柄來訪問的情況也十分常見香到。
類
匿名對象使用場景:
1:當(dāng)對方法只進(jìn)行一次調(diào)用的時(shí)候,可以使用匿名對象报破。
2:當(dāng)對象對成員進(jìn)行多次調(diào)用時(shí)悠就,不能使用匿名對象。必須給對象起名字充易。
類中怎么沒有定義主函數(shù)呢梗脾?
注意:主函數(shù)的存在,僅為該類是否需要獨(dú)立運(yùn)行盹靴,如果不需要炸茧,主函數(shù)是不用定義的。
主函數(shù)的解釋:保證所在類的獨(dú)立運(yùn)行稿静,是程序的入口梭冠,被jvm調(diào)用。
成員變量和局部變量的區(qū)別:
1:成員變量直接定義在類中自赔。
局部變量定義在方法中妈嘹,參數(shù)上,語句中绍妨。
2:成員變量在這個(gè)類中有效润脸。
局部變量只在自己所屬的大括號內(nèi)有效,大括號結(jié)束他去,局部變量失去作用域毙驯。
3:成員變量存在于堆內(nèi)存中,隨著對象的產(chǎn)生而存在灾测,消失而消失爆价。
局部變量存在于棧內(nèi)存中,隨著所屬區(qū)域的運(yùn)行而存在,結(jié)束而釋放铭段。
構(gòu)造函數(shù):用于給對象進(jìn)行初始化骤宣,是給與之對應(yīng)的對象進(jìn)行初始化,它具有針對性序愚,函數(shù)中的一種憔披。
特點(diǎn):
1:該函數(shù)的名稱和所在類的名稱相同。
2:不需要定義返回值類型爸吮。
3:該函數(shù)沒有具體的返回值芬膝。
記淄┰纭:所有對象創(chuàng)建時(shí)炫七,都需要初始化才可以使用。
注意事項(xiàng):一個(gè)類在定義時(shí),如果沒有定義過構(gòu)造函數(shù)诅福,那么該類中會自動(dòng)生成一個(gè)空參數(shù)的構(gòu)造函數(shù)咖气,為了方便該類創(chuàng)建對象,完成初始化奋救。如果在類中自定義了構(gòu)造函數(shù)背亥,那么默認(rèn)的構(gòu)造函數(shù)就沒有了盾戴。
一個(gè)類中剩膘,可以有多個(gè)構(gòu)造函數(shù)奈懒,因?yàn)樗鼈兊暮瘮?shù)名稱都相同达皿,所以只能通過參數(shù)列表來區(qū)分汤功。所以科阎,一個(gè)類中如果出現(xiàn)多個(gè)構(gòu)造函數(shù)错英。它們的存在是以重載體現(xiàn)的。
構(gòu)造代碼塊和構(gòu)造函數(shù)有什么區(qū)別?
構(gòu)造代碼塊:是給所有的對象進(jìn)行初始化良蒸,也就是說椰棘,所有的對象都會調(diào)用一個(gè)代碼塊。只要對象一建立蠕蚜。就會調(diào)用這個(gè)代碼塊邪蛔。
構(gòu)造函數(shù):是給與之對應(yīng)的對象進(jìn)行初始化污抬。它具有針對性楣责。
執(zhí)行順序:(優(yōu)先級從高到低。)靜態(tài)代碼塊>mian方法>構(gòu)造代碼塊>構(gòu)造方法。其中靜態(tài)代碼塊只執(zhí)行一次窃躲。構(gòu)造代碼塊在每次創(chuàng)建對象是都會執(zhí)行撩嚼。
靜態(tài)代碼塊的作用:比如我們在調(diào)用C語言的動(dòng)態(tài)庫時(shí)會可把.so文件放在此處蜻底。
構(gòu)造代碼塊的功能:(可以把不同構(gòu)造方法中相同的共性的東西寫在它里面)。例如:比如不論任何機(jī)型的電腦都有開機(jī)這個(gè)功能聘鳞,此時(shí)我們就可以把這個(gè)功能定義在構(gòu)造代碼塊內(nèi)薄辅。
Person p = new Person();
創(chuàng)建一個(gè)對象都在內(nèi)存中做了什么事情要拂?
1:先將硬盤上指定位置的Person.class文件加載進(jìn)內(nèi)存。
2:執(zhí)行main方法時(shí)站楚,在棧內(nèi)存中開辟了main方法的空間(壓棧-進(jìn)棧)脱惰,然后在main方法的棧區(qū)分配了一個(gè)變量p。
3:在堆內(nèi)存中開辟一個(gè)實(shí)體空間窿春,分配了一個(gè)內(nèi)存首地址值拉一。new
4:在該實(shí)體空間中進(jìn)行屬性的空間分配,并進(jìn)行了默認(rèn)初始化旧乞。
5:對空間中的屬性進(jìn)行顯示初始化蔚润。
6:進(jìn)行實(shí)體的構(gòu)造代碼塊初始化。
7:調(diào)用該實(shí)體對應(yīng)的構(gòu)造函數(shù)尺栖,進(jìn)行構(gòu)造函數(shù)初始化嫡纠。()
8:將首地址賦值給p ,p變量就引用了該實(shí)體延赌。(指向了該對象)
封 裝(面向?qū)ο筇卣髦唬菏侵鸽[藏對象的屬性和實(shí)現(xiàn)細(xì)節(jié)除盏,僅對外提供公共訪問方式。
好處:將變化隔離挫以;便于使用痴颊;提高重用性;安全性屡贺。
封裝原則:將不需要對外提供的內(nèi)容都隱藏起來,把屬性都隱藏锌杀,提供公共方法對其訪問甩栈。
this:代表對象。就是所在函數(shù)所屬對象的引用糕再。
this到底代表什么呢量没?哪個(gè)對象調(diào)用了this所在的函數(shù),this就代表哪個(gè)對象突想,就是哪個(gè)對象的引用殴蹄。
開發(fā)時(shí),什么時(shí)候使用this呢猾担?
在定義功能時(shí)袭灯,如果該功能內(nèi)部使用到了調(diào)用該功能的對象,這時(shí)就用this來表示這個(gè)對象绑嘹。
this 還可以用于構(gòu)造函數(shù)間的調(diào)用稽荧。
調(diào)用格式:this(實(shí)際參數(shù));
this對象后面跟上 . 調(diào)用的是成員屬性和成員方法(一般方法)工腋;
this對象后面跟上 () 調(diào)用的是本類中的對應(yīng)參數(shù)的構(gòu)造函數(shù)姨丈。
注意:用this調(diào)用構(gòu)造函數(shù)畅卓,必須定義在構(gòu)造函數(shù)的第一行。因?yàn)闃?gòu)造函數(shù)是用于初始化的蟋恬,所以初始化動(dòng)作一定要執(zhí)行翁潘。否則編譯失敗。
static: 關(guān)鍵字歼争,是一個(gè)修飾符拜马,用于修飾成員(成員變量和成員函數(shù))。
特點(diǎn):
1矾飞、static變量
按照是否靜態(tài)的對類成員變量進(jìn)行分類可分兩種:一種是被static修飾的變量一膨,叫靜態(tài)變量或類變量;另一種是沒有被static修飾的變量洒沦,叫實(shí)例變量豹绪。兩者的區(qū)別是:
對于靜態(tài)變量在內(nèi)存中只有一個(gè)拷貝(節(jié)省內(nèi)存),JVM只為靜態(tài)分配一次內(nèi)存申眼,在加載類的過程中完成靜態(tài)變量的內(nèi)存分配瞒津,可用類名直接訪問(方便),當(dāng)然也可以通過對象來訪問(但是這是不推薦的)括尸。
對于實(shí)例變量巷蚪,沒創(chuàng)建一個(gè)實(shí)例,就會為實(shí)例變量分配一次內(nèi)存濒翻,實(shí)例變量可以在內(nèi)存中有多個(gè)拷貝屁柏,互不影響(靈活)。
2有送、靜態(tài)方法
靜態(tài)方法可以直接通過類名調(diào)用淌喻,任何的實(shí)例也都可以調(diào)用,因此靜態(tài)方法中不能用this和super關(guān)鍵字雀摘,不能直接訪問所屬類的實(shí)例變量和實(shí)例方法(就是不帶static的成員變量和成員成員方法)裸删,只能訪問所屬類的靜態(tài)成員變量和成員方法。因?yàn)閷?shí)例成員與特定的對象關(guān)聯(lián)阵赠!這個(gè)需要去理解涯塔,想明白其中的道理,不是記憶G迨础X拜!
因?yàn)閟tatic方法獨(dú)立于任何實(shí)例轧铁,因此static方法必須被實(shí)現(xiàn)每聪,而不能是抽象的abstract。
3、static代碼塊
static代碼塊也叫靜態(tài)代碼塊,是在類中獨(dú)立于類成員的static語句塊,可以有多個(gè)仍源,位置可以隨便放,它不在任何的方法體內(nèi)真屯,JVM加載類時(shí)會執(zhí)行這些靜態(tài)的代碼塊,如果static代碼塊有多個(gè)穷娱,JVM將按照它們在類中出現(xiàn)的先后順序依次執(zhí)行它們绑蔫,每個(gè)代碼塊只會被執(zhí)行一次。
4泵额、static和final一塊用表示什么
static final用來修飾成員變量和成員方法配深,可簡單理解為"全局常量"!
對于變量嫁盲,表示一旦給值就不可修改篓叶,并且通過類名可以訪問。
對于方法羞秤,表示不可覆蓋,并且可以通過類名直接訪問俐镐。
備注:
1哺哼,有些數(shù)據(jù)是對象特有的數(shù)據(jù)取董,是不可以被靜態(tài)修飾的匹摇。因?yàn)槟菢拥脑挘赜袛?shù)據(jù)會變成對象的共享數(shù)據(jù)经窖。這樣對事物的描述就出了問題画侣。所以,在定義靜態(tài)時(shí)桑寨,必須要明確尉尾,這個(gè)數(shù)據(jù)是否是被對象所共享的。
2,靜態(tài)方法只能訪問靜態(tài)成員吆豹,不可以訪問非靜態(tài)成員。
(這句話是針對同一個(gè)類環(huán)境下的速勇,比如說,一個(gè)類有多個(gè)成員(屬性都伪,方法,字段),靜態(tài)方法A褐耳,那么可以訪問同類名下其他靜態(tài)成員,你如果訪問非靜態(tài)成員就不行)
因?yàn)殪o態(tài)方法加載時(shí)仁烹,優(yōu)先于對象存在,所以沒有辦法訪問對象中的成員。
3鳍鸵,靜態(tài)方法中不能使用this,super關(guān)鍵字。
因?yàn)閠his代表對象画切,而靜態(tài)在時(shí),有可能沒有對象,所以this無法使用耍缴。
4炼吴,主函數(shù)是靜態(tài)的荣德。
成員變量和靜態(tài)變量的區(qū)別:
1,成員變量所屬于對象署咽。所以也稱為實(shí)例變量。
靜態(tài)變量所屬于類。所以也稱為類變量台谊。
2减宣,成員變量存在于堆內(nèi)存中丰歌。
靜態(tài)變量存在于方法區(qū)中。
3,成員變量隨著對象創(chuàng)建而存在绑咱。隨著對象被回收而消失衡蚂。
靜態(tài)變量隨著類的加載而存在年叮。隨著類的消失而消失一姿。
4,成員變量只能被對象所調(diào)用 。
靜態(tài)變量可以被對象調(diào)用蜂林,也可以被類名調(diào)用霉翔。
所以子眶,成員變量可以稱為對象的特有數(shù)據(jù),靜態(tài)變量稱為對象的共享數(shù)據(jù)。
靜態(tài)代碼塊:就是一個(gè)有靜態(tài)關(guān)鍵字標(biāo)示的一個(gè)代碼塊區(qū)域。定義在類中。
作用:可以完成類的初始化身诺。靜態(tài)代碼塊隨著類的加載而執(zhí)行怠苔,而且只執(zhí)行一次(new 多個(gè)對象就只執(zhí)行一次)迫肖。如果和主函數(shù)在同一類中,優(yōu)先于主函數(shù)執(zhí)行。
final
根據(jù)程序上下文環(huán)境,Java關(guān)鍵字final有"這是無法改變的"或者"終態(tài)的"含義,它可以修飾非抽象類谓苟、非抽象類成員方法和變量斤讥。你可能出于兩種理解而需要阻止改變派草、設(shè)計(jì)或效率簸州。
final類不能被繼承搏存,沒有子類,final類中的方法默認(rèn)是final的袁滥。
final方法不能被子類的方法覆蓋腰鬼,但可以被繼承。
final成員變量表示常量初家,只能被賦值一次他托,賦值后值不再改變志笼。
final不能用于修飾構(gòu)造方法韧掩。
注意:父類的private成員方法是不能被子類方法覆蓋的费彼,因此private類型的方法默認(rèn)是final類型的。
1、final類
final類不能被繼承祭玉,因此final類的成員方法沒有機(jī)會被覆蓋,默認(rèn)都是final的。在設(shè)計(jì)類時(shí)候烫堤,如果這個(gè)類不需要有子類利诺,類的實(shí)現(xiàn)細(xì)節(jié)不允許改變,并且確信這個(gè)類不會載被擴(kuò)展,那么就設(shè)計(jì)為final類。
2悠菜、final方法
如果一個(gè)類不允許其子類覆蓋某個(gè)方法兽叮,則可以把這個(gè)方法聲明為final方法账阻。
使用final方法的原因有二:
第一规丽、把方法鎖定,防止任何繼承類修改它的意義和實(shí)現(xiàn)。
第二、高效。編譯器在遇到調(diào)用final方法時(shí)候會轉(zhuǎn)入內(nèi)嵌機(jī)制,大大提高執(zhí)行效率。
3、final變量(常量)
用final修飾的成員變量表示常量在讶,值一旦給定就無法改變战坤!
final修飾的變量有三種:靜態(tài)變量、實(shí)例變量和局部變量臀防,分別表示三種類型的常量。
從下面的例子中可以看出边败,一旦給final變量初值后,值就不能再改變了捎废。
另外,final變量定義的時(shí)候登疗,可以先聲明排截,而不給初值,這中變量也稱為final空白辐益,無論什么情況断傲,編譯器都確保空白final在使用之前必須被初始化智政。但是认罩,final空白在final關(guān)鍵字final的使用上提供了更大的靈活性,為此续捂,一個(gè)類中的final數(shù)據(jù)成員就可以實(shí)現(xiàn)依對象而有所不同垦垂,卻有保持其恒定不變的特征。
4牙瓢、final參數(shù)
當(dāng)函數(shù)參數(shù)為final類型時(shí)劫拗,你可以讀取使用該參數(shù),但是無法改變該參數(shù)的值矾克。
生成Java幫助文檔:命令格式:javadoc –d 文件夾名 –auther –version *.java
/** //格式
*類描述
*@author 作者名
*@version 版本號
*/
/**
*方法描述
*@param 參數(shù)描述
*@return 返回值描述
*/
繼 承(面向?qū)ο筇卣髦唬?/p>
java中對于繼承页慷,java只支持單繼承。java雖然不直接支持多繼承,但是可實(shí)現(xiàn)多接口酒繁。
1:成員變量滓彰。
? ? 當(dāng)子父類中出現(xiàn)一樣的屬性時(shí),子類類型的對象欲逃,調(diào)用該屬性找蜜,值是子類的屬性值。
? ? 如果想要調(diào)用父類中的屬性值稳析,需要使用一個(gè)關(guān)鍵字:super
? ? This:代表是本類類型的對象引用洗做。
? ? Super:代表是子類所屬的父類中的內(nèi)存空間引用。
? ? 注意:子父類中通常是不會出現(xiàn)同名成員變量的彰居,因?yàn)楦割愔兄灰x了诚纸,子類就不用在定義了,直接繼承過來用就可以了陈惰。
2:成員函數(shù)畦徘。
當(dāng)子父類中出現(xiàn)了一模一樣的方法時(shí),建立子類對象會運(yùn)行子類中的方法抬闯。好像父類中的方法被覆蓋掉一樣井辆。所以這種情況,是函數(shù)的另一個(gè)特性:重寫
3:構(gòu)造函數(shù)溶握。
發(fā)現(xiàn)子類構(gòu)造函數(shù)運(yùn)行時(shí)杯缺,先運(yùn)行了父類的構(gòu)造函數(shù)。為什么呢?
原因:子類的所有構(gòu)造函數(shù)中的第一行睡榆,其實(shí)都有一條隱身的語句super();
super(): 表示父類的構(gòu)造函數(shù)萍肆,并會調(diào)用于參數(shù)相對應(yīng)的父類中的構(gòu)造函數(shù)。而super():是在調(diào)用父類中空參數(shù)的構(gòu)造函數(shù)胀屿。
為什么子類對象初始化時(shí)塘揣,都需要調(diào)用父類中的函數(shù)?(為什么要在子類構(gòu)造函數(shù)的第一行加入這個(gè)super()?)
因?yàn)樽宇惱^承父類宿崭,會繼承到父類中的數(shù)據(jù)亲铡,所以必須要看父類是如何對自己的數(shù)據(jù)進(jìn)行初始化的。所以子類在進(jìn)行對象初始化時(shí)葡兑,先調(diào)用父類的構(gòu)造函數(shù)奴愉,這就是子類的實(shí)例化過程。
注意:子類中所有的構(gòu)造函數(shù)都會默認(rèn)訪問父類中的空參數(shù)的構(gòu)造函數(shù)铁孵,因?yàn)槊恳粋€(gè)子類構(gòu)造內(nèi)第一行都有默認(rèn)的語句super();
如果父類中沒有空參數(shù)的構(gòu)造函數(shù)锭硼,那么子類的構(gòu)造函數(shù)內(nèi),必須通過super語句指定要訪問的父類中的構(gòu)造函數(shù)蜕劝。
如果子類構(gòu)造函數(shù)中用this來指定調(diào)用子類自己的構(gòu)造函數(shù)檀头,那么被調(diào)用的構(gòu)造函數(shù)也一樣會訪問父類中的構(gòu)造函數(shù)轰异。
問題:
super()和this()是否可以同時(shí)出現(xiàn)的構(gòu)造函數(shù)中?
兩個(gè)語句只能有一個(gè)定義在第一行暑始,所以只能出現(xiàn)其中一個(gè)搭独。
super()或者this():為什么一定要定義在第一行?
因?yàn)閟uper()或者this()都是調(diào)用構(gòu)造函數(shù)廊镜,構(gòu)造函數(shù)用于初始化牙肝,所以初始化的動(dòng)作要先完成。
在方法覆蓋時(shí)嗤朴,注意兩點(diǎn):
1:子類覆蓋父類時(shí)配椭,必須要保證,子類方法的權(quán)限必須大于等于父類方法權(quán)限可以實(shí)現(xiàn)繼承雹姊。否則股缸,編譯失敗。(舉個(gè)例子吱雏,在父類中是public的方法敦姻,如果子類中將其降低訪問權(quán)限為private,那么子類中重寫以后的方法對于外部對象就不可訪問了歧杏,這個(gè)就破壞了繼承的含義)
2:覆蓋時(shí)镰惦,要么都靜態(tài),要么都不靜態(tài)犬绒。 (靜態(tài)只能覆蓋靜態(tài)旺入,或者被靜態(tài)覆蓋)
繼承的一個(gè)弊端:打破了封裝性。對于一些類懂更,或者類中功能,是需要被繼承急膀,或者復(fù)寫的沮协。
這時(shí)如何解決問題呢?介紹一個(gè)關(guān)鍵字卓嫂,final慷暂。
final特點(diǎn):(詳細(xì)解釋見前面)
1:這個(gè)關(guān)鍵字是一個(gè)修飾符,可以修飾類晨雳,方法行瑞,變量。
2:被final修飾的類是一個(gè)最終類餐禁,不可以被繼承血久。
3:被final修飾的方法是一個(gè)最終方法,不可以被覆蓋帮非。
4:被final修飾的變量是一個(gè)常量氧吐,只能賦值一次讹蘑。
抽象類: abstract
抽象類的特點(diǎn):
1:抽象方法只能定義在抽象類中,抽象類和抽象方法必須由abstract關(guān)鍵字修飾(可以描述類和方法筑舅,不可以描述變量)。
2:抽象方法只定義方法聲明翠拣,并不定義方法實(shí)現(xiàn)。
3:抽象類不可以被創(chuàng)建對象(實(shí)例化)畦娄。
4:只有通過子類繼承抽象類并覆蓋了抽象類中的所有抽象方法后熙卡,該子類才可以實(shí)例化杖刷。否則,該子類還是一個(gè)抽象類驳癌。
抽象類的細(xì)節(jié):
1:抽象類中是否有構(gòu)造函數(shù)滑燃?有,用于給子類對象進(jìn)行初始化颓鲜。
2:抽象類中是否可以定義非抽象方法表窘?
? ? 可以。其實(shí)甜滨,抽象類和一般類沒有太大的區(qū)別乐严,都是在描述事物,只不過抽象類在描述事物時(shí)衣摩,有些功能不具體昂验。所以抽象類和一般類在定義上,都是需要定義屬性和行為的艾扮。只不過既琴,比一般類多了一個(gè)抽象函數(shù)。而且比一般類少了一個(gè)創(chuàng)建對象的部分泡嘴。
3:抽象關(guān)鍵字abstract和哪些不可以共存甫恩?final ,? ? private , static
4:抽象類中可不可以不定義抽象方法?可以酌予。抽象方法目的僅僅為了不讓該類創(chuàng)建對象填物。
接 口:
1:是用關(guān)鍵字interface定義的纹腌。
2:接口中包含的成員,最常見的有全局常量滞磺、抽象方法升薯。
注意:接口中的成員都有固定的修飾符。
? ? 成員變量:public static final
? ? 成員方法:public abstract
interface Inter{
? ? public static final int x = 3;
? ? public abstract void show();
}
3:接口中有抽象方法击困,說明接口不可以實(shí)例化涎劈。接口的子類必須實(shí)現(xiàn)了接口中所有的抽象方法后,該子類才可以實(shí)例化阅茶。否則蛛枚,該子類還是一個(gè)抽象類。
4:類與類之間存在著繼承關(guān)系脸哀,類與接口中間存在的是實(shí)現(xiàn)關(guān)系蹦浦。
? ? 繼承用extends ;實(shí)現(xiàn)用implements 撞蜂;
5:接口和類不一樣的地方盲镶,就是,接口可以被多實(shí)現(xiàn)蝌诡,這就是多繼承改良后的結(jié)果溉贿。java將多繼承機(jī)制通過多現(xiàn)實(shí)來體現(xiàn)。
6:一個(gè)類在繼承另一個(gè)類的同時(shí)浦旱,還可以實(shí)現(xiàn)多個(gè)接口宇色。所以接口的出現(xiàn)避免了單繼承的局限性。還可以將類進(jìn)行功能的擴(kuò)展颁湖。
7:其實(shí)java中是有多繼承的宣蠕。接口與接口之間存在著繼承關(guān)系,接口可以多繼承接口甥捺。
java類是單繼承的抢蚀。classB Extends classA
java接口可以多繼承。Interface3 Extends Interface0, Interface1, interface……
不允許類多重繼承的主要原因是涎永,如果A同時(shí)繼承B和C思币,而b和c同時(shí)有一個(gè)D方法鹿响,A如何決定該繼承那一個(gè)呢羡微?
但接口不存在這樣的問題,接口全都是抽象方法繼承誰都無所謂惶我,所以接口可以繼承多個(gè)接口妈倔。
抽象類與接口:
抽象類:一般用于描述一個(gè)體系單元,將一組共性內(nèi)容進(jìn)行抽取绸贡,特點(diǎn):可以在類中定義抽象內(nèi)容讓子類實(shí)現(xiàn)盯蝴,可以定義非抽象內(nèi)容讓子類直接使用毅哗。它里面定義的都是一些體系中的基本內(nèi)容。
接口:一般用于定義對象的擴(kuò)展功能捧挺,是在繼承之外還需這個(gè)對象具備的一些功能虑绵。
抽象類和接口的共性:都是不斷向上抽取的結(jié)果。
抽象類和接口的區(qū)別:
1:抽象類只能被繼承闽烙,而且只能單繼承翅睛。
接口需要被實(shí)現(xiàn),而且可以多實(shí)現(xiàn)黑竞。
2:抽象類中可以定義非抽象方法捕发,子類可以直接繼承使用。
接口中都是抽象方法很魂,需要子類去實(shí)現(xiàn)扎酷。
3:抽象類使用的是 is a 關(guān)系。
接口使用的 like a 關(guān)系遏匆。
4:抽象類的成員修飾符可以自定義法挨。
接口中的成員修飾符是固定的。全都是public的拉岁。
多 態(tài)
多 態(tài)(面向?qū)ο筇卣髦唬汉瘮?shù)本身就具備多態(tài)性罗标,某一種事物有不同的具體的體現(xiàn)。
體現(xiàn):父類引用或者接口的引用指向了自己的子類對象循签。//Animal a = new Cat();父類可以調(diào)用子類中覆寫過的(父類中有的方法)
多態(tài)的好處:提高了程序的擴(kuò)展性劈狐。繼承的父類或接口一般是類庫中的東西,(如果要修改某個(gè)方法的具體實(shí)現(xiàn)方式)只有通過子類去覆寫要改變的某一個(gè)方法陵叽,這樣在通過將父類的應(yīng)用指向子類的實(shí)例去調(diào)用覆寫過的方法就行了狞尔!
多態(tài)的弊端:當(dāng)父類引用指向子類對象時(shí),雖然提高了擴(kuò)展性巩掺,但是只能訪問父類中具備的方法偏序,不可以訪問子類中特有的方法。(前期不能使用后期產(chǎn)生的功能胖替,即訪問的局限性)
多態(tài)的前提:
? ? 1:必須要有關(guān)系研儒,比如繼承、或者實(shí)現(xiàn)独令。
? ? 2:通常會有覆蓋操作端朵。
如果想用子類對象的特有方法,如何判斷對象是哪個(gè)具體的子類類型呢燃箭?
可以可以通過一個(gè)關(guān)鍵字 instanceof ;//判斷對象是否實(shí)現(xiàn)了指定的接口或繼承了指定的類
格式:<對象 instanceof 類型> 冲呢,判斷一個(gè)對象是否所屬于指定的類型。
Student instanceof Person = true;//student繼承了person類
-------------------------------------------------------------------------------------java.lang.Object
Object:所有類的直接或者間接父類招狸,Java認(rèn)為所有的對象都具備一些基本的共性內(nèi)容敬拓,這些內(nèi)容可以不斷的向上抽取邻薯,最終就抽取到了一個(gè)最頂層的類中的,該類中定義的就是所有對象都具備的功能乘凸。
具體方法:
boolean equals(Object obj):用于比較兩個(gè)對象是否相等厕诡,其實(shí)內(nèi)部比較的就是兩個(gè)對象地址。
2营勤,String toString():將對象變成字符串木人;默認(rèn)返回的格式:類名@哈希值 = getClass().getName() + '@' + Integer.toHexString(hashCode())
? ? 為了對象對應(yīng)的字符串內(nèi)容有意義,可以通過復(fù)寫冀偶,建立該類對象自己特有的字符串表現(xiàn)形式醒第。
? ? public String toString(){
? ? ? ? return "person : "+age;
? ? }
3,Class getClass():獲取任意對象運(yùn)行時(shí)的所屬字節(jié)碼文件對象进鸠。
4稠曼,int hashCode():返回該對象的哈希碼值。支持此方法是為了提高哈希表的性能客年。將該對象的內(nèi)部地址轉(zhuǎn)換成一個(gè)整數(shù)來實(shí)現(xiàn)的霞幅。
通常equals,toString量瓜,hashCode司恳,在應(yīng)用中都會被復(fù)寫,建立具體對象的特有的內(nèi)容绍傲。
-------------------------------------------------------------------------------------
內(nèi)部類:如果A類需要直接訪問B類中的成員扔傅,而B類又需要建立A類的對象。這時(shí),為了方便設(shè)計(jì)和訪問烫饼,直接將A類定義在B類中猎塞。就可以了。A類就稱為內(nèi)部類杠纵。內(nèi)部類可以直接訪問外部類中的成員荠耽。而外部類想要訪問內(nèi)部類,必須要建立內(nèi)部類的對象比藻。
-----------------------------------------------------
class Outer{
? ? int num = 4;? ?
? ? class Inner {
? ? ? ? void show(){
? ? ? ? ? ? System.out.println("inner show run "+num);
? ? ? ? }
? ? }
? ? public void method(){
? ? ? ? Inner in = new Inner();//創(chuàng)建內(nèi)部類的對象铝量。
? ? ? ? in.show();//調(diào)用內(nèi)部類的方法。 //內(nèi)部類直接訪問外部類成員银亲,用自己的實(shí)例對象慢叨;
? ? }? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //外部類訪問內(nèi)部類要定義內(nèi)部類的對象;
}
-------------------------------------------------------
當(dāng)內(nèi)部類定義在外部類中的成員位置上群凶,可以使用一些成員修飾符修飾 private插爹、static哄辣。
1:默認(rèn)修飾符请梢。
直接訪問內(nèi)部類格式:外部類名.內(nèi)部類名 變量名 = 外部類對象.內(nèi)部類對象;
Outer.Inner in = new Outer.new Inner();//這種形式很少用赠尾。
? ? 但是這種應(yīng)用不多見,因?yàn)閮?nèi)部類之所以定義在內(nèi)部就是為了封裝毅弧。想要獲取內(nèi)部類對象通常都通過外部類的方法來獲取气嫁。這樣可以對內(nèi)部類對象進(jìn)行控制。
2:私有修飾符够坐。
? ? 通常內(nèi)部類被封裝寸宵,都會被私有化,因?yàn)榉庋b性不讓其他程序直接訪問元咙。
3:靜態(tài)修飾符梯影。
? ? 如果內(nèi)部類被靜態(tài)修飾,相當(dāng)于外部類庶香,會出現(xiàn)訪問局限性甲棍,只能訪問外部類中的靜態(tài)成員。
? ? 注意赶掖;如果內(nèi)部類中定義了靜態(tài)成員感猛,那么該內(nèi)部類必須是靜態(tài)的。
內(nèi)部類編譯后的文件名為:"外部類名$內(nèi)部類名.java"奢赂;
為什么內(nèi)部類可以直接訪問外部類中的成員呢陪白?
那是因?yàn)閮?nèi)部中都持有一個(gè)外部類的引用。這個(gè)是引用是 外部類名.this
內(nèi)部類可以定義在外部類中的成員位置上膳灶,也可以定義在外部類中的局部位置上咱士。
當(dāng)內(nèi)部類被定義在局部位置上,只能訪問局部中被final修飾的局部變量轧钓。
匿名內(nèi)部類(對象):沒有名字的內(nèi)部類司致。就是內(nèi)部類的簡化形式。一般只用一次就可以用這種形式聋迎。匿名內(nèi)部類其實(shí)就是一個(gè)匿名子類對象脂矫。想要定義匿名內(nèi)部類:需要前提,內(nèi)部類必須繼承一個(gè)類或者實(shí)現(xiàn)接口霉晕。
匿名內(nèi)部類的格式:new 父類名&接口名(){ 定義子類成員或者覆蓋父類方法 }.方法庭再。
匿名內(nèi)部類的使用場景:
當(dāng)函數(shù)的參數(shù)是接口類型引用時(shí),如果接口中的方法不超過3個(gè)牺堰≈羟幔可以通過匿名內(nèi)部類來完成參數(shù)的傳遞。
其實(shí)就是在創(chuàng)建匿名內(nèi)部類時(shí)伟葫,該類中的封裝的方法不要過多恨搓,最好兩個(gè)或者兩個(gè)以內(nèi)。
--------------------------------------------------------
//面試
? ? ? ? //1
? ? ? ? new Object(){
? ? ? ? ? ? void show(){
? ? ? ? ? ? ? ? System.out.println("show run");? ? ? ? ? ? ? ?
? ? ? ? ? ? }
? ? ? ? }.show();? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //寫法和編譯都沒問題
? ? ? ? //2
? ? ? ? Object obj = new Object(){
? ? ? ? ? ? void show(){
? ? ? ? ? ? ? ? System.out.println("show run");
? ? ? ? ? ? }
? ? ? ? };
? ? ? ? obj.show();? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //寫法正確,編譯會報(bào)錯(cuò)
? ? ? ? 1和2的寫法正確嗎斧抱?有區(qū)別嗎常拓?說出原因。
? ? ? ? 寫法是正確辉浦,1和2都是在通過匿名內(nèi)部類建立一個(gè)Object類的子類對象弄抬。
? ? ? ? 區(qū)別:
? ? ? ? 第一個(gè)可是編譯通過,并運(yùn)行宪郊。
? ? ? ? 第二個(gè)編譯失敗掂恕,因?yàn)槟涿麅?nèi)部類是一個(gè)子類對象,當(dāng)用Object的obj引用指向時(shí)弛槐,就被提升為了Object類型懊亡,而編譯時(shí)會檢查Object類中是否有show方法,此時(shí)編譯失敗乎串。
異 常:
--java.lang.Throwable:
Throwable:可拋出的斋配。
? ? |--Error:錯(cuò)誤,一般情況下灌闺,不編寫針對性的代碼進(jìn)行處理艰争,通常是jvm發(fā)生的,需要對程序進(jìn)行修正桂对。
? ? |--Exception:異常甩卓,可以有針對性的處理方式
這個(gè)體系中的所有類和對象都具備一個(gè)獨(dú)有的特點(diǎn);就是可拋性蕉斜。
可拋性的體現(xiàn):就是這個(gè)體系中的類和對象都可以被throws和throw兩個(gè)關(guān)鍵字所操作逾柿。
throw與throws區(qū)別:
throws是用來聲明一個(gè)方法可能拋出的所有異常信息,而throw則是指拋出的一個(gè)具體的異常類型宅此。此外throws是將異常聲明但是不處理机错,而是將異常往上傳,誰調(diào)用我就交給誰處理父腕。
throw用于拋出異常對象弱匪,后面跟的是異常對象;throw用在函數(shù)內(nèi)璧亮。
throws用于拋出異常類萧诫,后面跟的異常類名,可以跟多個(gè)枝嘶,用逗號隔開帘饶。throws用在函數(shù)上。
throws格式:方法名(參數(shù))throws 異常類1群扶,異常類2及刻,.....
throw:就是自己進(jìn)行異常處理镀裤,處理的時(shí)候有兩種方式,要么自己捕獲異常(也就是try catch進(jìn)行捕捉)缴饭,要么聲明拋出一個(gè)異常(就是throws 異常~~)暑劝。
處理方式有兩種:1、捕捉茴扁;2、拋出汪疮。
對于捕捉:java有針對性的語句塊進(jìn)行處理峭火。
try {
? ? 需要被檢測的代碼;
}
catch(異常類 變量名){
? ? 異常處理代碼智嚷;
}
fianlly{
? ? 一定會執(zhí)行的代碼卖丸;
}
定義異常處理時(shí),什么時(shí)候定義try盏道,什么時(shí)候定義throws呢稍浆?
功能內(nèi)部如果出現(xiàn)異常,如果內(nèi)部可以處理猜嘱,就用try衅枫;
如果功能內(nèi)部處理不了,就必須聲明出來朗伶,讓調(diào)用者處理弦撩。使用throws拋出,交給調(diào)用者處理论皆。誰調(diào)用了這個(gè)功能誰就是調(diào)用者益楼;
自定義異常的步驟:
1:定義一個(gè)子類繼承Exception或RuntimeException,讓該類具備可拋性(既可以使用throw和throws去調(diào)用此類)点晴。
2:通過throw 或者throws進(jìn)行操作感凤。
異常的轉(zhuǎn)換思想:當(dāng)出現(xiàn)的異常是調(diào)用者處理不了的,就需要將此異常轉(zhuǎn)換為一個(gè)調(diào)用者可以處理的異常拋出粒督。
try catch finally的幾種結(jié)合方式:
1陪竿,
try
catch
finally
這種情況,如果出現(xiàn)異常屠橄,并不處理萨惑,但是資源一定關(guān)閉,所以try finally集合只為關(guān)閉資源仇矾。
記子拱:finally很有用,主要用戶關(guān)閉資源贮匕。無論是否發(fā)生異常姐仅,資源都必須進(jìn)行關(guān)閉。
System.exit(0); //退出jvm,只有這種情況finally不執(zhí)行掏膏。
注意:
如果父類或者接口中的方法沒有拋出過異常劳翰,那么子類是不可以拋出異常的,如果子類的覆蓋的方法中出現(xiàn)了異常馒疹,只能try不能throws佳簸。
如果這個(gè)異常子類無法處理,已經(jīng)影響了子類方法的具體運(yùn)算颖变,這時(shí)可以在子類方法中生均,通過throw拋出RuntimeException異常或者其子類腥刹,這樣马胧,子類的方法上是不需要throws聲明的。
多線程:
返回當(dāng)前線程的名稱:Thread.currentThread().getName()
線程的名稱是由:Thread-編號定義的衔峰。編號從0開始佩脊。
線程要運(yùn)行的代碼都統(tǒng)一存放在了run方法中。
線程要運(yùn)行必須要通過類中指定的方法開啟垫卤。start方法威彰。(啟動(dòng)后,就多了一條執(zhí)行路徑)
start方法:1)穴肘、啟動(dòng)了線程抱冷;2)、讓jvm調(diào)用了run方法梢褐。
Thread類中run()和start()方法的區(qū)別:
start():用start方法來啟動(dòng)線程旺遮,真正實(shí)現(xiàn)了多線程運(yùn)行,這時(shí)無需等待run方法體代碼執(zhí)行完畢而直接繼續(xù)執(zhí)行下面的代碼盈咳。通過調(diào)用Thread類的start()方法來啟動(dòng)一個(gè)線程耿眉,這時(shí)此線程處于就緒(可運(yùn)行)狀態(tài),并沒有運(yùn)行鱼响,一旦得到cpu時(shí)間片鸣剪,就開始執(zhí)行run()方法,這里方法run()稱為線程體丈积,它包含了要執(zhí)行的這個(gè)線程的內(nèi)容筐骇,Run方法運(yùn)行結(jié)束江滨,此線程隨即終止铛纬。
run():run()方法只是類的一個(gè)普通方法而已,如果直接調(diào)用Run方法,程序中依然只有主線程這一個(gè)線程,其程序執(zhí)行路徑還是只有一條,還是要順序執(zhí)行咏连,還是要等待run方法體執(zhí)行完畢后才可繼續(xù)執(zhí)行下面的代碼骑晶,這樣就沒有達(dá)到寫線程的目的竖共。
總結(jié):start()方法最本質(zhì)的功能是從CPU中申請另一個(gè)線程空間來執(zhí)行 run()方法中的代碼,它和當(dāng)前的線程是兩條線,在相對獨(dú)立的線程空間運(yùn)行,也就是說,如果你直接調(diào)用線程對象的run()方法,當(dāng)然也會執(zhí)行,但那是 在當(dāng)前線程中執(zhí)行,run()方法執(zhí)行完成后繼續(xù)執(zhí)行下面的代碼.而調(diào)用start()方法后,run()方法的代碼會和當(dāng)前線程并發(fā)(單CPU)或并行 (多CPU)執(zhí)行。所以請記住一句話:調(diào)用線程對象的run方法不會產(chǎn)生一個(gè)新的線程加叁,雖然可以達(dá)到相同的執(zhí)行結(jié)果,但執(zhí)行過程和執(zhí)行效率不同
創(chuàng)建線程的第一種方式:繼承Thread ,由子類復(fù)寫run方法。
步驟:
1,定義類繼承Thread類巫财;
2湖雹,目的是復(fù)寫run方法,將要讓線程運(yùn)行的代碼都存儲到run方法中橱夭;
3氨距,通過創(chuàng)建Thread類的子類對象桑逝,創(chuàng)建線程對象棘劣;
4,調(diào)用線程的start方法楞遏,開啟線程茬暇,并執(zhí)行run方法。
線程狀態(tài):
被創(chuàng)建:start()
運(yùn)行:具備執(zhí)行資格寡喝,同時(shí)具備執(zhí)行權(quán)糙俗;
凍結(jié):sleep(time),wait()—notify()喚醒;線程釋放了執(zhí)行權(quán)预鬓,同時(shí)釋放執(zhí)行資格巧骚;
臨時(shí)阻塞狀態(tài):線程具備cpu的執(zhí)行資格,沒有cpu的執(zhí)行權(quán)格二;
消亡:stop()
創(chuàng)建線程的第二種方式:實(shí)現(xiàn)一個(gè)接口Runnable劈彪。
步驟:
1,定義類實(shí)現(xiàn)Runnable接口顶猜。
2沧奴,覆蓋接口中的run方法(用于封裝線程要運(yùn)行的代碼)。
3长窄,通過Thread類創(chuàng)建線程對象滔吠;
4纲菌,將實(shí)現(xiàn)了Runnable接口的子類對象作為實(shí)際參數(shù)傳遞給Thread類中的構(gòu)造函數(shù)。
為什么要傳遞呢疮绷?因?yàn)橐尵€程對象明確要運(yùn)行的run方法所屬的對象翰舌。
5,調(diào)用Thread對象的start方法冬骚。開啟線程灶芝,并運(yùn)行Runnable接口子類中的run方法。
Ticket t = new Ticket();
? ? ? ? /*
? ? ? ? 直接創(chuàng)建Ticket對象唉韭,并不是創(chuàng)建線程對象夜涕。
? ? ? ? 因?yàn)閯?chuàng)建對象只能通過new Thread類,或者new Thread類的子類才可以属愤。
? ? ? ? 所以最終想要?jiǎng)?chuàng)建線程女器。既然沒有了Thread類的子類,就只能用Thread類住诸。
? ? ? ? */
? ? ? ? Thread t1 = new Thread(t); //創(chuàng)建線程驾胆。
? ? ? ? /*
? ? ? ? 只要將t作為Thread類的構(gòu)造函數(shù)的實(shí)際參數(shù)傳入即可完成線程對象和t之間的關(guān)聯(lián)
? ? ? ? 為什么要將t傳給Thread類的構(gòu)造函數(shù)呢?其實(shí)就是為了明確線程要運(yùn)行的代碼run方法贱呐。
? ? ? ? */
? ? ? ? t1.start();
為什么要有Runnable接口的出現(xiàn)丧诺?
1:通過繼承Thread類的方式,可以完成多線程的建立奄薇。但是這種方式有一個(gè)局限性驳阎,如果一個(gè)類已經(jīng)有了自己的父類,就不可以繼承Thread類馁蒂,因?yàn)閖ava單繼承的局限性呵晚。
可是該類中的還有部分代碼需要被多個(gè)線程同時(shí)執(zhí)行。這時(shí)怎么辦呢沫屡?
只有對該類進(jìn)行額外的功能擴(kuò)展饵隙,java就提供了一個(gè)接口Runnable。這個(gè)接口中定義了run方法沮脖,其實(shí)run方法的定義就是為了存儲多線程要運(yùn)行的代碼金矛。
所以,通常創(chuàng)建線程都用第二種方式勺届。
因?yàn)閷?shí)現(xiàn)Runnable接口可以避免單繼承的局限性驶俊。
2:其實(shí)是將不同類中需要被多線程執(zhí)行的代碼進(jìn)行抽取。將多線程要運(yùn)行的代碼的位置單獨(dú)定義到接口中涮因。為其他類進(jìn)行功能擴(kuò)展提供了前提废睦。
所以Thread類在描述線程時(shí),內(nèi)部定義的run方法养泡,也來自于Runnable接口嗜湃。
實(shí)現(xiàn)Runnable接口可以避免單繼承的局限性奈应。而且,繼承Thread购披,是可以對Thread類中的方法杖挣,進(jìn)行子類復(fù)寫的。但是不需要做這個(gè)復(fù)寫動(dòng)作的話刚陡,只為定義線程代碼存放位置惩妇,實(shí)現(xiàn)Runnable接口更方便一些。所以Runnable接口將線程要執(zhí)行的任務(wù)封裝成了對象筐乳。
-------------------------------------------------------
//面試
? ? ? ? new Thread(new Runnable(){ //匿名
? ? ? ? ? ? public void run(){
? ? ? ? ? ? ? ? System.out.println("runnable run");? ?
? ? ? ? ? ? }
? ? ? ? })
? ? ? ? {
? ? ? ? ? ? public void run(){
? ? ? ? ? ? ? ? System.out.println("subthread run");
? ? ? ? ? ? }
? ? ? ? }.start(); //結(jié)果:subthread run
---------------------------------------------------------
synchronized關(guān)鍵字(一)
一歌殃、當(dāng)兩個(gè)并發(fā)線程訪問同一個(gè)對象object中的這個(gè)synchronized(this)同步代碼塊時(shí),一個(gè)時(shí)間內(nèi)只能有一個(gè)線程得到執(zhí)行蝙云。另一個(gè)線程必須等待當(dāng)前線程執(zhí)行完這個(gè)代碼塊以后才能執(zhí)行該代碼塊氓皱。
二、然而勃刨,當(dāng)一個(gè)線程訪問object的一個(gè)synchronized(this)同步代碼塊時(shí)波材,另一個(gè)線程仍然可以訪問該object中的非synchronized(this)同步代碼塊身隐。
三廷区、尤其關(guān)鍵的是,當(dāng)一個(gè)線程訪問object的一個(gè)synchronized(this)同步代碼塊時(shí)贾铝,其他線程對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞隙轻。
四、第三個(gè)例子同樣適用其它同步代碼塊忌傻。也就是說大脉,當(dāng)一個(gè)線程訪問object的一個(gè)synchronized(this)同步代碼塊時(shí)搞监,它就獲得了這個(gè)object的對象鎖水孩。結(jié)果,其它線程對該object對象所有同步代碼部分的訪問都被暫時(shí)阻塞琐驴。
五昙读、以上規(guī)則對其它對象鎖同樣適用.
package ths;
public class Thread1 implements Runnable {
public void run() {
synchronized(this) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"synchronized loop " + i);
}
}
}
}
synchronized關(guān)鍵字(二)
synchronized 關(guān)鍵字土榴,它包括兩種用法:synchronized 方法和 synchronized 塊。
1. synchronized 方法:通過在方法聲明中加入 synchronized關(guān)鍵字來聲明 synchronized 方法。如:
public synchronized void accessVal(int newVal);
synchronized 方法控制對類成員變量的訪問:每個(gè)類實(shí)例對應(yīng)一把鎖撮奏,每個(gè) synchronized 方法都必須獲得調(diào)用該方法的類實(shí)例的鎖方能執(zhí)行,否則所屬線程阻塞炊苫,方法一旦執(zhí)行谊却,就獨(dú)占該鎖,直到從該方法返回時(shí)才將鎖釋放馍乙,此后被阻塞的線程方能獲得該鎖布近,重新進(jìn)入可執(zhí)行狀態(tài)垫释。這種機(jī)制確保了同一時(shí)刻對于每一個(gè)類實(shí)例,其所有聲明為 synchronized 的成員函數(shù)中至多只有一個(gè)處于可執(zhí)行狀態(tài)(因?yàn)橹炼嘀挥幸粋€(gè)能夠獲得該類實(shí)例對應(yīng)的鎖)撑瞧,從而有效避免了類成員變量的訪問沖突(只要所有可能訪問類成員變量的方法均被聲明為 synchronized)棵譬。
在 Java 中,不光是類實(shí)例预伺,每一個(gè)類也對應(yīng)一把鎖订咸,這樣我們也可將類的靜態(tài)成員函數(shù)聲明為 synchronized ,以控制其對類的靜態(tài)成員變量的訪問酬诀。
synchronized 方法的缺陷:若將一個(gè)大的方法聲明為synchronized 將會大大影響效率脏嚷,典型地,若將線程類的方法 run() 聲明為synchronized 瞒御,由于在線程的整個(gè)生命期內(nèi)它一直在運(yùn)行然眼,因此將導(dǎo)致它對本類任何 synchronized 方法的調(diào)用都永遠(yuǎn)不會成功。當(dāng)然我們可以通過將訪問類成員變量的代碼放到專門的方法中葵腹,將其聲明為 synchronized 高每,并在主方法中調(diào)用來解決這一問題,但是 Java 為我們提供了更好的解決辦法践宴,那就是 synchronized 塊鲸匿。
2. synchronized 塊:通過 synchronized關(guān)鍵字來聲明synchronized 塊。語法如下:
synchronized(syncObject) {
//允許訪問控制的代碼
}
synchronized 塊是這樣一個(gè)代碼塊阻肩,其中的代碼必須獲得對象 syncObject (如前所述带欢,可以是類實(shí)例或類)的鎖方能執(zhí)行,具體機(jī)制同前所述烤惊。由于可以針對任意代碼塊乔煞,且可任意指定上鎖的對象,故靈活性較高柒室。
對synchronized(this)的一些理解
一渡贾、當(dāng)兩個(gè)并發(fā)線程訪問同一個(gè)對象object中的這個(gè)synchronized(this)同步代碼塊時(shí),一個(gè)時(shí)間內(nèi)只能有一個(gè)線程得到執(zhí)行雄右。另一個(gè)線程必須等待當(dāng)前線程執(zhí)行完這個(gè)代碼塊以后才能執(zhí)行該代碼塊空骚。
二、然而擂仍,當(dāng)一個(gè)線程訪問object的一個(gè)synchronized(this)同步代碼塊時(shí)囤屹,另一個(gè)線程仍然可以訪問該object中的非synchronized(this)同步代碼塊。
三逢渔、尤其關(guān)鍵的是肋坚,當(dāng)一個(gè)線程訪問object的一個(gè)synchronized(this)同步代碼塊時(shí),其他線程對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞。
四智厌、第三個(gè)例子同樣適用其它同步代碼塊粟判。也就是說,當(dāng)一個(gè)線程訪問object的一個(gè)synchronized(this)同步代碼塊時(shí)峦剔,它就獲得了這個(gè)object的對象鎖档礁。結(jié)果,其它線程對該object對象所有同步代碼部分的訪問都被暫時(shí)阻塞吝沫。
五呻澜、以上規(guī)則對其它對象鎖同樣適用。
解決安全問題的原理:
只要將操作共享數(shù)據(jù)的語句在某一時(shí)段讓一個(gè)線程執(zhí)行完惨险,在執(zhí)行過程中羹幸,其他線程不能進(jìn)來執(zhí)行就可以解決這個(gè)問題。
如何保障共享數(shù)據(jù)的線程安全呢辫愉?
java中提供了一個(gè)解決方式:就是同步代碼塊栅受。
格式:
synchronized(對象) { //任意對象都可以。這個(gè)對象就是共享數(shù)據(jù)恭朗。
? ? 需要被同步的代碼屏镊;
}
---------------------------------------------------------------
同步:
好處:解決了線程安全問題。Synchronized
弊端:相對降低性能痰腮,因?yàn)榕袛噫i需要消耗資源而芥,產(chǎn)生了死鎖。
同步的第二種表現(xiàn)形式:? ? ? ? //對共享資源的方法定義同步
同步函數(shù):其實(shí)就是將同步關(guān)鍵字定義在函數(shù)上膀值,讓函數(shù)具備了同步性棍丐。
同步函數(shù)是用的哪個(gè)鎖呢?? ? ? ? //synchronized(this)用以定義需要進(jìn)行同步的某一部分代碼塊
通過驗(yàn)證沧踏,函數(shù)都有自己所屬的對象this歌逢,所以同步函數(shù)所使用的鎖就是this鎖。This.方法名
當(dāng)同步函數(shù)被static修飾時(shí)翘狱,這時(shí)的同步用的是哪個(gè)鎖呢秘案?
靜態(tài)函數(shù)在加載時(shí)所屬于類,這時(shí)有可能還沒有該類產(chǎn)生的對象盒蟆,但是該類的字節(jié)碼文件加載進(jìn)內(nèi)存就已經(jīng)被封裝成了對象踏烙,這個(gè)對象就是該類的字節(jié)碼文件對象。
所以靜態(tài)加載時(shí)历等,只有一個(gè)對象存在,那么靜態(tài)同步函數(shù)就使用的這個(gè)對象辟癌。
這個(gè)對象就是 類名.class
同步代碼塊和同步函數(shù)的區(qū)別寒屯?
同步代碼塊使用的鎖可以是任意對象。
同步函數(shù)使用的鎖是this,靜態(tài)同步函數(shù)的鎖是該類的字節(jié)碼文件對象寡夹。
在一個(gè)類中只有一個(gè)同步的話处面,可以使用同步函數(shù)。如果有多同步菩掏,必須使用同步代碼塊魂角,來確定不同的鎖。所以同步代碼塊相對靈活一些智绸。
-------------------------------------------------------
考點(diǎn)問題:請寫一個(gè)延遲加載的單例模式野揪?寫懶漢式;當(dāng)出現(xiàn)多線程訪問時(shí)怎么解決瞧栗?加同步斯稳,解決安全問題;效率高嗎迹恐?不高挣惰;怎樣解決?通過雙重判斷的形式解決殴边。
//懶漢式:延遲加載方式憎茂。
當(dāng)多線程訪問懶漢式時(shí),因?yàn)閼袧h式的方法內(nèi)對共性數(shù)據(jù)進(jìn)行多條語句的操作锤岸。所以容易出現(xiàn)線程安全問題唇辨。為了解決,加入同步機(jī)制能耻,解決安全問題赏枚。但是卻帶來了效率降低。
為了效率問題晓猛,通過雙重判斷的形式解決饿幅。
class Single{
? ? private static Single s = null;
? ? private Single(){}
? ? public static Single getInstance(){ //鎖是誰?字節(jié)碼文件對象戒职;
? ? ? ? if(s == null){
? ? ? ? ? ? synchronized(Single.class){
? ? ? ? ? ? ? ? if(s == null)
? ? ? ? ? ? ? ? ? ? s = new Single();
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return s;
? ? }
}
---------------------------------------------------------
等待喚醒機(jī)制:涉及的方法:
wait:將同步中的線程處于凍結(jié)狀態(tài)栗恩。釋放了執(zhí)行權(quán),釋放了資格洪燥。同時(shí)將線程對象存儲到線程池中磕秤。
notify:喚醒線程池中某一個(gè)等待線程。
notifyAll:喚醒的是線程池中的所有線程捧韵。
注意:
1:這些方法都需要定義在同步中市咆。
2:因?yàn)檫@些方法必須要標(biāo)示所屬的鎖。
? ? 你要知道 A鎖上的線程被wait了,那這個(gè)線程就相當(dāng)于處于A鎖的線程池中再来,只能A鎖的notify喚醒蒙兰。
3:這三個(gè)方法都定義在Object類中磷瘤。為什么操作線程的方法定義在Object類中?
? ? 因?yàn)檫@三個(gè)方法都需要定義同步內(nèi)搜变,并標(biāo)示所屬的同步鎖采缚,既然被鎖調(diào)用,而鎖又可以是任意對象挠他,那么能被任意對象調(diào)用的方法一定定義在Object類中扳抽。
wait和sleep區(qū)別: 分析這兩個(gè)方法:從執(zhí)行權(quán)和鎖上來分析:
wait:可以指定時(shí)間也可以不指定時(shí)間。不指定時(shí)間殖侵,只能由對應(yīng)的notify或者notifyAll來喚醒贸呢。
sleep:必須指定時(shí)間,時(shí)間到自動(dòng)從凍結(jié)狀態(tài)轉(zhuǎn)成運(yùn)行狀態(tài)(臨時(shí)阻塞狀態(tài))愉耙。
wait:線程會釋放執(zhí)行權(quán)贮尉,而且線程會釋放鎖。
sleep:線程會釋放執(zhí)行權(quán)朴沿,但不是不釋放鎖猜谚。
線程的停止:通過stop方法就可以停止線程。但是這個(gè)方式過時(shí)了赌渣。
停止線程:原理就是:讓線程運(yùn)行的代碼結(jié)束魏铅,也就是結(jié)束run方法。
怎么結(jié)束run方法坚芜?一般run方法里肯定定義循環(huán)览芳。所以只要結(jié)束循環(huán)即可。
第一種方式:定義循環(huán)的結(jié)束標(biāo)記鸿竖。
第二種方式:如果線程處于了凍結(jié)狀態(tài)沧竟,是不可能讀到標(biāo)記的,這時(shí)就需要通過Thread類中的interrupt方法缚忧,將其凍結(jié)狀態(tài)強(qiáng)制清除悟泵。讓線程恢復(fù)具備執(zhí)行資格的狀態(tài),讓線程可以讀到標(biāo)記闪水,并結(jié)束糕非。
---------< java.lang.Thread >----------
interrupt():中斷線程。
setPriority(int newPriority):更改線程的優(yōu)先級球榆。
getPriority():返回線程的優(yōu)先級朽肥。
toString():返回該線程的字符串表示形式,包括線程名稱持钉、優(yōu)先級和線程組衡招。
Thread.yield():暫停當(dāng)前正在執(zhí)行的線程對象,并執(zhí)行其他線程右钾。
setDaemon(true):將該線程標(biāo)記為守護(hù)線程或用戶線程蚁吝。將該線程標(biāo)記為守護(hù)線程或用戶線程旱爆。當(dāng)正在運(yùn)行的線程都是守護(hù)線程時(shí)舀射,Java 虛擬機(jī)退出窘茁。該方法必須在啟動(dòng)線程前調(diào)用。
join:臨時(shí)加入一個(gè)線程的時(shí)候可以使用join方法脆烟。
當(dāng)A線程執(zhí)行到了B線程的join方式山林。A線程處于凍結(jié)狀態(tài),釋放了執(zhí)行權(quán)邢羔,B開始執(zhí)行驼抹。A什么時(shí)候執(zhí)行呢?只有當(dāng)B線程運(yùn)行結(jié)束后拜鹤,A才從凍結(jié)狀態(tài)恢復(fù)運(yùn)行狀態(tài)執(zhí)行框冀。
LOCK的出現(xiàn)替代了同步:lock.lock();………lock.unlock();
Lock接口:多線程在JDK1.5版本升級時(shí),推出一個(gè)接口Lock接口敏簿。
解決線程安全問題使用同步的形式明也,(同步代碼塊,要么同步函數(shù))其實(shí)最終使用的都是鎖機(jī)制惯裕。
到了后期版本温数,直接將鎖封裝成了對象。線程進(jìn)入同步就是具備了鎖蜻势,執(zhí)行完撑刺,離開同步,就是釋放了鎖握玛。
在后期對鎖的分析過程中够傍,發(fā)現(xiàn),獲取鎖挠铲,或者釋放鎖的動(dòng)作應(yīng)該是鎖這個(gè)事物更清楚冕屯。所以將這些動(dòng)作定義在了鎖當(dāng)中,并把鎖定義成對象市殷。
所以同步是隱示的鎖操作愕撰,而Lock對象是顯示的鎖操作,它的出現(xiàn)就替代了同步醋寝。
在之前的版本中使用Object類中wait搞挣、notify、notifyAll的方式來完成的音羞。那是因?yàn)橥街械逆i是任意對象囱桨,所以操作鎖的等待喚醒的方法都定義在Object類中。
而現(xiàn)在鎖是指定對象Lock嗅绰。所以查找等待喚醒機(jī)制方式需要通過Lock接口來完成舍肠。而Lock接口中并沒有直接操作等待喚醒的方法搀继,而是將這些方式又單獨(dú)封裝到了一個(gè)對象中。這個(gè)對象就是Condition翠语,將Object中的三個(gè)方法進(jìn)行單獨(dú)的封裝叽躯。并提供了功能一致的方法 await()、signal()肌括、signalAll()體現(xiàn)新版本對象的好處点骑。
< java.util.concurrent.locks > Condition接口:await()、signal()谍夭、signalAll()黑滴;
--------------------------------------------------------
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
}
? finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
}
finally {
lock.unlock();
}
}
}
集合框架
集合框架:,用于存儲數(shù)據(jù)的容器紧索。
對于集合容器袁辈,有很多種。因?yàn)槊恳粋€(gè)容器的自身特點(diǎn)不同珠漂,其實(shí)原理在于每個(gè)容器的內(nèi)部數(shù)據(jù)結(jié)構(gòu)不同晚缩。
集合容器在不斷向上抽取過程中。出現(xiàn)了集合體系甘磨。
在使用一個(gè)體系時(shí)橡羞,原則:參閱頂層內(nèi)容。建立底層對象济舆。
------------------------------------------------------------
--< java.util >-- List接口:
List本身是Collection接口的子接口卿泽,具備了Collection的所有方法。現(xiàn)在學(xué)習(xí)List體系特有的共性方法滋觉,查閱方法發(fā)現(xiàn)List的特有方法都有索引签夭,這是該集合最大的特點(diǎn)。
List:有序(元素存入集合的順序和取出的順序一致)椎侠,元素都有索引第租。元素可以重復(fù)。
? ? |--ArrayList:底層的數(shù)據(jù)結(jié)構(gòu)是數(shù)組,線程不同步我纪,ArrayList替代了Vector慎宾,查詢元素的速度非常快浅悉。
? ? |--LinkedList:底層的數(shù)據(jù)結(jié)構(gòu)是鏈表趟据,線程不同步,增刪元素的速度非呈踅。快汹碱。
? ? |--Vector:底層的數(shù)據(jù)結(jié)構(gòu)就是數(shù)組,線程同步的荞估,Vector無論查詢和增刪都巨慢咳促。
可變長度數(shù)組的原理:
當(dāng)元素超出數(shù)組長度稚新,會產(chǎn)生一個(gè)新數(shù)組,將原數(shù)組的數(shù)據(jù)復(fù)制到新數(shù)組中跪腹,再將新的元素添加到新數(shù)組中褂删。
ArrayList:是按照原數(shù)組的50%延長。構(gòu)造一個(gè)初始容量為 10 的空列表尺迂。
Vector:是按照原數(shù)組的100%延長笤妙。
------------------------------------------------------------
--< java.util >-- Set接口:
數(shù)據(jù)結(jié)構(gòu):數(shù)據(jù)的存儲方式冒掌;
Set接口中的方法和Collection中方法一致的噪裕。Set接口取出方式只有一種,迭代器股毫。
? ? |--HashSet:底層數(shù)據(jù)結(jié)構(gòu)是哈希表膳音,線程是不同步的。無序铃诬,高效祭陷;
? ? ? ? HashSet集合保證元素唯一性:通過元素的hashCode方法,和equals方法完成的趣席。
? ? ? ? 當(dāng)元素的hashCode值相同時(shí)兵志,才繼續(xù)判斷元素的equals是否為true。
? ? ? ? 如果為true宣肚,那么視為相同元素想罕,不存。如果為false霉涨,那么存儲按价。
? ? ? ? 如果hashCode值不同,那么不判斷equals笙瑟,從而提高對象比較的速度楼镐。
|--LinkedHashSet:有序,hashset的子類往枷。
? ? |--TreeSet:對Set集合中的元素的進(jìn)行指定順序的排序框产。不同步。TreeSet底層的數(shù)據(jù)結(jié)構(gòu)就是二叉樹错洁。
對于ArrayList集合秉宿,判斷元素是否存在,或者刪元素底層依據(jù)都是equals方法墓臭。
對于HashSet集合蘸鲸,判斷元素是否存在,或者刪除元素窿锉,底層依據(jù)的是hashCode方法和equals方法酌摇。
------------------------------------------------------------
Map集合:
|--Hashtable:底層是哈希表數(shù)據(jù)結(jié)構(gòu)膝舅,是線程同步的。不可以存儲null鍵窑多,null值仍稀。
|--HashMap:底層是哈希表數(shù)據(jù)結(jié)構(gòu),是線程不同步的埂息〖寂耍可以存儲null鍵,null值千康。替代了Hashtable.
|--TreeMap:底層是二叉樹結(jié)構(gòu)享幽,可以對map集合中的鍵進(jìn)行指定順序的排序。
Map集合存儲和Collection有著很大不同:
Collection一次存一個(gè)元素拾弃;Map一次存一對元素值桩。
Collection是單列集合;Map是雙列集合豪椿。
Map中的存儲的一對元素:一個(gè)是鍵奔坟,一個(gè)是值,鍵與值之間有對應(yīng)(映射)關(guān)系搭盾。
特點(diǎn):要保證map集合中鍵的唯一性咳秉。
5,想要獲取map中的所有元素:
? ? 原理:map中是沒有迭代器的鸯隅,collection具備迭代器澜建,只要將map集合轉(zhuǎn)成Set集合,可以使用迭代器了滋迈。之所以轉(zhuǎn)成set霎奢,是因?yàn)閙ap集合具備著鍵的唯一性,其實(shí)set集合就來自于map饼灿,set集合底層其實(shí)用的就是map的方法幕侠。
把map集合轉(zhuǎn)成set的方法:
? ? Set keySet();
? ? Set entrySet();//取的是鍵和值的映射關(guān)系。
Entry就是Map接口中的內(nèi)部接口碍彭;
為什么要定義在map內(nèi)部呢晤硕?entry是訪問鍵值關(guān)系的入口,是map的入口庇忌,訪問的是map中的鍵值對舞箍。
---------------------------------------------------------
取出map集合中所有元素的方式一:keySet()方法。
可以將map集合中的鍵都取出存放到set集合中皆疹。對set集合進(jìn)行迭代疏橄。迭代完成,再通過get方法對獲取到的鍵進(jìn)行值的獲取。
Set keySet = map.keySet();
? ? ? Iterator it = keySet.iterator();
? ? ? while(it.hasNext()) {
? ? ? ? ? Object key = it.next();
? ? ? ? ? Object value = map.get(key);
? ? ? ? ? System.out.println(key+":"+value);
? ? ? }
--------------------------------------------------------
取出map集合中所有元素的方式二:entrySet()方法捎迫。
Set entrySet = map.entrySet();
? ? ? Iterator it = entrySet.iterator();
? ? ? while(it.hasNext()) {
? ? ? ? ? Map.Entry me = (Map.Entry)it.next();
? ? ? ? ? System.out.println(me.getKey()+"::::"+me.getValue());
? ? ? }
-------------------------------------------------------
將非同步集合轉(zhuǎn)成同步集合的方法:Collections中的 XXX
synchronizedXXX(XXX);
List synchronizedList(list);
Map synchronizedMap(map);
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
return new SynchronizedMap<K,V>(m);
}
原理:定義一個(gè)類晃酒,將集合所有的方法加同一把鎖后返回。
List list = Collections.synchronizedList(new ArrayList());
Map<String,String> synmap = Collections.synchronizedMap(map);
Collection 和 Collections的區(qū)別:
Collections是個(gè)java.util下的類窄绒,是針對集合類的一個(gè)工具類,提供一系列靜態(tài)方法,實(shí)現(xiàn)對集合的查找贝次、排序、替換彰导、線程安全化(將非同步的集合轉(zhuǎn)換成同步的)等操作蛔翅。
Collection是個(gè)java.util下的接口,它是各種集合結(jié)構(gòu)的父接口位谋,繼承于它的接口主要有Set和List,提供了關(guān)于集合的一些操作,如插入山析、刪除、判斷一個(gè)元素是否其成員倔幼、遍歷等盖腿。
-------------------------------------------------------
自動(dòng)拆裝箱:java中數(shù)據(jù)類型分為兩種 : 基本數(shù)據(jù)類型 引用數(shù)據(jù)類型(對象)
在 java程序中所有的數(shù)據(jù)都需要當(dāng)做對象來處理,針對8種基本數(shù)據(jù)類型提供了包裝類损同,如下:
int --> Integer
byte --> Byte
short --> Short
long --> Long
char --> Character
double --> Double
float --> Float
boolean --> Boolean
jdk5以前基本數(shù)據(jù)類型和包裝類之間需要互轉(zhuǎn):
基本---引用 Integer x = new Integer(x);
引用---基本 int num = x.intValue();
1)、Integer x = 1; x = x + 1; 經(jīng)歷了什么過程鸟款?裝箱 à 拆箱 à 裝箱膏燃;
2)、為了優(yōu)化何什,虛擬機(jī)為包裝類提供了緩沖池组哩,Integer池的大小 -128~127 一個(gè)字節(jié)的大小处渣;
3)伶贰、String池:Java為了優(yōu)化字符串操作 提供了一個(gè)緩沖池;
----------------------------------------------------------
泛型:jdk1.5版本以后出現(xiàn)的一個(gè)安全機(jī)制罐栈。表現(xiàn)格式:< >
好處:
1:將運(yùn)行時(shí)期的問題ClassCastException問題轉(zhuǎn)換成了編譯失敗黍衙,體現(xiàn)在編譯時(shí)期旧蛾,程序員就可以解決問題誓焦。
2:避免了強(qiáng)制轉(zhuǎn)換的麻煩。
泛型中的通配符:可以解決當(dāng)具體類型不確定的時(shí)候晒杈,這個(gè)通配符就是 ? 柑贞;當(dāng)操作類型時(shí)方椎,不需要使用類型的具體功能時(shí),只使用Object類中的功能钧嘶。那么可以用 ? 通配符來表未知類型棠众。
-------------------------------------------------------------------------------------------------------------------------------
反射技術(shù)
反射技術(shù):其實(shí)就是動(dòng)態(tài)加載一個(gè)指定的類,并獲取該類中的所有的內(nèi)容有决。并將字節(jié)碼文件中的內(nèi)容都封裝成對象闸拿,這樣便于操作這些成員轿亮。簡單說:反射技術(shù)可以對一個(gè)類進(jìn)行解剖。
反射的好處:大大的增強(qiáng)了程序的擴(kuò)展性胸墙。
反射的基本步驟:
1我注、獲得Class對象,就是獲取到指定的名稱的字節(jié)碼文件對象迟隅。
2但骨、實(shí)例化對象,獲得類的屬性智袭、方法或構(gòu)造函數(shù)奔缠。
3、訪問屬性吼野、調(diào)用方法校哎、調(diào)用構(gòu)造函數(shù)創(chuàng)建對象。
獲取這個(gè)Class對象瞳步,有三種方式:
1:通過每個(gè)對象都具備的方法getClass來獲取闷哆。弊端:必須要?jiǎng)?chuàng)建該類對象,才可以調(diào)用getClass方法单起。
2:每一個(gè)數(shù)據(jù)類型(基本數(shù)據(jù)類型和引用數(shù)據(jù)類型)都有一個(gè)靜態(tài)的屬性class抱怔。弊端:必須要先明確該類。
? ? 前兩種方式不利于程序的擴(kuò)展嘀倒,因?yàn)槎夹枰诔绦蚴褂镁唧w的類來完成屈留。
3:使用的Class類中的方法,靜態(tài)的forName方法测蘑。
? ? 指定什么類名灌危,就獲取什么類字節(jié)碼文件對象,這種方式的擴(kuò)展性最強(qiáng)碳胳,只要將類名的字符串傳入即可勇蝙。
// 1. 根據(jù)給定的類名來獲得 用于類加載
String classname = "cn.itcast.reflect.Person";// 來自配置文件
Class clazz = Class.forName(classname);// 此對象代表Person.class
// 2. 如果拿到了對象,不知道是什么類型 用于獲得對象的類型
Object obj = new Person();
Class clazz1 = obj.getClass();// 獲得對象具體的類型
// 3. 如果是明確地獲得某個(gè)類的Class對象 主要用于傳參
Class clazz2 = Person.class;? ?
反射的用法:
1)固逗、需要獲得java類的各個(gè)組成部分浅蚪,首先需要獲得類的Class對象,獲得Class對象的三種方式:
? ? Class.forName(classname)? ? 用于做類加載
? ? obj.getClass()? ? ? ? ? ? ? ? 用于獲得對象的類型
? ? 類名.class? ? ? ? ? ? 用于獲得指定的類型烫罩,傳參用
2)惜傲、反射類的成員方法:
? ? Class clazz = Person.class;
? ? Method method = clazz.getMethod(methodName, new Class[]{paramClazz1, paramClazz2});
? ? method.invoke();
3)、反射類的構(gòu)造函數(shù):
? ? Constructor con = clazz.getConstructor(new Class[]{paramClazz1, paramClazz2,...})
? ? con.newInstance(params...)
4)贝攒、反射類的屬性:
? ? Field field = clazz.getField(fieldName);
? ? field.setAccessible(true);
? ? field.setObject(value);
獲取了字節(jié)碼文件對象后盗誊,最終都需要?jiǎng)?chuàng)建指定類的對象:
創(chuàng)建對象的兩種方式(其實(shí)就是對象在進(jìn)行實(shí)例化時(shí)的初始化方式):
1,調(diào)用空參數(shù)的構(gòu)造函數(shù):使用了Class類中的newInstance()方法。
2哈踱,調(diào)用帶參數(shù)的構(gòu)造函數(shù):先要獲取指定參數(shù)列表的構(gòu)造函數(shù)對象荒适,然后通過該構(gòu)造函數(shù)的對象的newInstance(實(shí)際參數(shù)) 進(jìn)行對象的初始化。
綜上所述开镣,第二種方式刀诬,必須要先明確具體的構(gòu)造函數(shù)的參數(shù)類型,不便于擴(kuò)展邪财。所以一般情況下陕壹,被反射的類,內(nèi)部通常都會提供一個(gè)公有的空參數(shù)的構(gòu)造函數(shù)树埠。
------------------------------------------------------
? ? // 如何生成獲取到字節(jié)碼文件對象的實(shí)例對象糠馆。
? ? ? ? Class clazz = Class.forName("cn.itcast.bean.Person");//類加載
// 直接獲得指定的類型
? ? ? ? clazz = Person.class;
? ? ? ? // 根據(jù)對象獲得類型
? ? ? ? Object obj = new Person("zhangsan", 19);
? ? ? ? clazz = obj.getClass();
? ? ? ? Object obj = clazz.newInstance();//該實(shí)例化對象的方法調(diào)用就是指定類中的空參數(shù)構(gòu)造函數(shù),給創(chuàng)建對象進(jìn)行初始化怎憋。當(dāng)指定類中沒有空參數(shù)構(gòu)造函數(shù)時(shí)又碌,該如何創(chuàng)建該類對象呢?請看method_2();
? ? public static void method_2() throws Exception {
? ? ? ? Class clazz = Class.forName("cn.itcast.bean.Person");
? ? ? ? //既然類中沒有空參數(shù)的構(gòu)造函數(shù),那么只有獲取指定參數(shù)的構(gòu)造函數(shù),用該函數(shù)來進(jìn)行實(shí)例化绊袋。
? ? ? ? //獲取一個(gè)帶參數(shù)的構(gòu)造器毕匀。
? ? ? ? Constructor constructor = clazz.getConstructor(String.class,int.class);
? ? ? ? //想要對對象進(jìn)行初始化,使用構(gòu)造器的方法newInstance();
? ? ? ? Object obj = constructor.newInstance("zhagnsan",30);
? ? ? ? //獲取所有構(gòu)造器愤炸。
? ? ? ? Constructor[] constructors = clazz.getConstructors();//只包含公共的
? ? ? ? constructors = clazz.getDeclaredConstructors();//包含私有的
? ? ? ? for(Constructor con : constructors) {
? ? ? ? ? ? System.out.println(con);
? ? ? ? }
? ? }
------------------------------------------------------
反射指定類中的方法:
? ? //獲取類中所有的方法期揪。
? ? public static void method_1() throws Exception {
? ? ? ? Class clazz = Class.forName("cn.itcast.bean.Person");
? ? ? ? Method[] methods = clazz.getMethods();//獲取的是該類中的公有方法和父類中的公有方法。
? ? ? ? methods = clazz.getDeclaredMethods();//獲取本類中的方法规个,包含私有方法。
? ? ? ? for(Method method : methods) {
? ? ? ? ? ? System.out.println(method);
? ? ? ? }
? ? }
? ? //獲取指定方法姓建;
? ? public static void method_2() throws Exception {
? ? ? ? Class clazz = Class.forName("cn.itcast.bean.Person");
? ? ? ? //獲取指定名稱的方法诞仓。
? ? ? ? Method method = clazz.getMethod("show", int.class,String.class);
? ? ? ? //想要運(yùn)行指定方法,當(dāng)然是方法對象最清楚速兔,為了讓方法運(yùn)行墅拭,調(diào)用方法對象的invoke方法即可,但是方法運(yùn)行必須要明確所屬的對象和具體的實(shí)際參數(shù)涣狗。
? ? ? ? Object obj = clazz.newInstance();
? ? ? ? method.invoke(obj, 39,"hehehe");//執(zhí)行一個(gè)方法
? ? }
? ? //想要運(yùn)行私有方法谍婉。
? ? public static void method_3() throws Exception {
? ? ? ? Class clazz = Class.forName("cn.itcast.bean.Person");
? ? ? ? //想要獲取私有方法。必須用getDeclearMethod();
? ? ? ? Method method = clazz.getDeclaredMethod("method", null);
? ? ? ? // 私有方法不能直接訪問镀钓,因?yàn)闄?quán)限不夠穗熬。非要訪問,可以通過暴力的方式丁溅。
? ? ? ? method.setAccessible(true);//一般很少用唤蔗,因?yàn)樗接芯褪请[藏起來,所以盡量不要訪問。
? ? }
? ? //反射靜態(tài)方法妓柜。
? ? public static void method_4() throws Exception {
? ? ? ? Class clazz = Class.forName("cn.itcast.bean.Person");
? ? ? ? Method method = clazz.getMethod("function",null);
? ? ? ? method.invoke(null,null);
? ? }