Java基礎(chǔ)總結(jié),超級(jí)全的面試題
1. static關(guān)鍵字是什么意思?Java 中是否可以覆蓋(override)一個(gè) private 或者是 static 的方法?是否可以在 static 環(huán)境中訪問(wèn)非static 變量?
static關(guān)鍵字表明一個(gè)成員變量或者是成員方法可以在沒(méi)有所屬的類(lèi)的實(shí)例變量的情況下被訪問(wèn)。??
Java中static方法不能被覆蓋,因?yàn)?strong style="box-sizing: border-box;">方法覆蓋(override)是基于運(yùn)行時(shí)動(dòng)態(tài)綁定的,而?static 方法是編譯時(shí)靜態(tài)綁定的。static 方法跟類(lèi)的任何實(shí)例都不相關(guān),所以概念上不適用。??
不可以在static 環(huán)境中訪問(wèn)非static 變量,static變量在Java中是屬于類(lèi)的,它在所有的實(shí)例中的值是一樣的。當(dāng)類(lèi)被 Java 虛擬機(jī)載入的時(shí)候,會(huì)對(duì) static 變量進(jìn)行初始化(與類(lèi)同生死)。非static變量,只有類(lèi)被創(chuàng)建時(shí),才會(huì)分配空間(如果是方法內(nèi)的局部變量,那么在方法被調(diào)用的時(shí)候才創(chuàng)建)。
2. Java中接口和抽象類(lèi)的區(qū)別
接口比抽象類(lèi)更加抽象,因?yàn)槌橄箢?lèi)中可以定義構(gòu)造器,可以有抽象方法和具體方法,而接口中不能定義構(gòu)造器而且其中的方法全部都是抽象方法。 類(lèi)可以實(shí)現(xiàn)很多個(gè)接口,但是只能繼承一個(gè)抽象類(lèi)。一個(gè)類(lèi)如果繼承了某個(gè)抽象類(lèi)或者實(shí)現(xiàn)了某個(gè)接口都需要對(duì)其中的抽象方法全部進(jìn)行實(shí)現(xiàn), 否則該類(lèi)仍然需要被聲明為抽象類(lèi)。 抽象類(lèi)可以在不提供接口方法實(shí)現(xiàn)的情況下實(shí)現(xiàn)接口。 Java 接口中聲明的變量默認(rèn)都是final的。抽象類(lèi)可以包含非final的變量。 Java 接口中的成員函數(shù)默認(rèn)是 public 的。抽象類(lèi)的成員函數(shù)可以是 private,protected 或者是 public。 接口是絕對(duì)抽象的,不可以被實(shí)例化。抽象類(lèi)也不可以被實(shí)例化,但是,如果它包含 main方法的話是可以被調(diào)用的。(抽象類(lèi)和接口都不能夠?qū)嵗梢远x抽象類(lèi)和接口類(lèi)型的引用) 接口可以繼承接口。抽象類(lèi)可以實(shí)現(xiàn)(implements)接口**,抽象類(lèi)可繼承具體類(lèi),但前提是具體類(lèi)必須有明確的構(gòu)造函數(shù)**。 有抽象方法的類(lèi)必須被聲明為抽象類(lèi),而抽象類(lèi)未必要有抽象方法。?接口可以繼承接口,而且支持多重繼承。抽象類(lèi)可以實(shí)現(xiàn)(implements)接口,抽象類(lèi)可繼承具體類(lèi)也可以繼承抽象類(lèi)。
3. 進(jìn)程和線程
進(jìn)程是程序的一次動(dòng)態(tài)執(zhí)行過(guò)程,每個(gè)進(jìn)程都有自己獨(dú)立的內(nèi)存空間。一個(gè)應(yīng)用程序可以同時(shí)啟動(dòng)多個(gè)進(jìn)程(比如瀏覽器可以開(kāi)多個(gè)窗口,每個(gè)窗口就是一個(gè)進(jìn)程),進(jìn)程是執(zhí)行著的應(yīng)用程序,而線程是進(jìn)程內(nèi)部的一個(gè)執(zhí)行序列。一個(gè)進(jìn)程可以有多個(gè)線程。線程又叫做輕量級(jí)進(jìn)程。 多進(jìn)程操作系統(tǒng)能夠運(yùn)行多個(gè)進(jìn)程,每個(gè)進(jìn)程都能夠循環(huán)利用所需要的CPU時(shí)間片,使的所有進(jìn)程看上去像在同時(shí)運(yùn)行一樣。 線程是進(jìn)程的一個(gè)執(zhí)行流程,是CPU調(diào)度和分 派的基本單位。一個(gè)進(jìn)程可以由多個(gè)線程組成,也就是一個(gè)進(jìn)程可以同時(shí)運(yùn)行多個(gè)不同的線程,每個(gè)線程完成不同的任務(wù)。 線程的并發(fā)運(yùn)行:就是一個(gè)進(jìn)程內(nèi)若干個(gè)線程同時(shí)運(yùn)行。(比如:word的拼寫(xiě)檢查功能和首字母自動(dòng)大寫(xiě)功能是word進(jìn)程中的線程) 線程和進(jìn)程的關(guān)系是一個(gè)局部和整體的關(guān)系,每個(gè)進(jìn)程都由操作系統(tǒng)分配獨(dú)立的內(nèi)存地址空間,而同一進(jìn)程的所有線程都在同一地址空間工作。(進(jìn)程在執(zhí)行時(shí)通常擁有獨(dú)立的內(nèi)存單元,而線程之間可以實(shí)現(xiàn)內(nèi)存共享) 雖然使用多線程的編程通常能夠帶來(lái)更好的性能和用戶(hù)體驗(yàn),但是多線程占用更多的CPU資源,對(duì)于其他的進(jìn)程并不友好(可能占用了更多的CPU 資源),也不是線程越多, 程序的性能就越好,因?yàn)榫€程之間的調(diào)度和切換也會(huì)浪費(fèi)CPU 時(shí)間。
4. 線程的生命周期
一個(gè)線程的完整生命周期要經(jīng)歷5中狀態(tài):新建、就緒、運(yùn)行、阻塞、死亡。
新建狀態(tài):使用new和某種線程的構(gòu)造方法來(lái)創(chuàng)建線程對(duì)象,該線程就會(huì)進(jìn)入新建狀態(tài),系統(tǒng)為該線程對(duì)象分配內(nèi)存空間。處于新建狀態(tài)的線程可以通過(guò)調(diào)用**start()**方法進(jìn)入就緒狀態(tài)。 就緒狀態(tài)(Runnable):此時(shí)線程已經(jīng)具備了運(yùn)行的條件,進(jìn)入了線程隊(duì)列,等待系統(tǒng)分配CPU資源,一旦獲得CPU資源,該線程就會(huì)進(jìn)入運(yùn)行狀態(tài)。(線程準(zhǔn)備運(yùn)行,不一定立馬就能開(kāi)始執(zhí)行) 運(yùn)行狀態(tài)(Running):進(jìn)入運(yùn)行在狀態(tài),線程會(huì)執(zhí)行自己的**run()**方法中的代碼。就是進(jìn)程正在執(zhí)行線程的代碼。 阻塞狀態(tài)(Blocked):一個(gè)正在執(zhí)行的線程,如果執(zhí)行了suspend、join或sleep方法,或等待io設(shè)備的使用權(quán),那么該線程將會(huì)讓出自己的CUP控制權(quán)并暫時(shí)中止自己的執(zhí)行,進(jìn)入阻塞狀態(tài)。阻塞的線程,不能夠進(jìn)入就緒隊(duì)列,只有當(dāng)阻塞原因被消除的時(shí)候,線程才能進(jìn)入就緒狀態(tài),重新進(jìn)入線程隊(duì)列中排隊(duì)等待CPU資源,然后繼續(xù)執(zhí)行。 死亡狀態(tài)(Dead):一個(gè)線程完成了全部工作或者被提前強(qiáng)制性的中止,該線程就處于死亡狀態(tài)。 睡眠狀態(tài)(Sleeping):線程通過(guò) Thread.sleep(線程睡眠)、Object.wait(線程等待)、LockSupport.park(線程暫停)三種方式 使線程進(jìn)入休眠狀態(tài)。?同步阻塞(Blocked on Synchronization):sleep( ) 使線程在一定的時(shí)間內(nèi)進(jìn)入阻塞狀態(tài)(不會(huì)釋放鎖資源)、wait( ) 使線程進(jìn)入阻塞狀態(tài),(釋放自己占有的鎖資源,搭配notify( )使用)、suspend( ) 使線程進(jìn)入阻塞狀態(tài)(必須其對(duì)應(yīng)的resume( )被調(diào)用,才能使線程重新進(jìn)入可執(zhí)行狀態(tài))
5. 線程同步以及線程調(diào)度相關(guān)的方法
wait():使一個(gè)線程處于等待(阻塞)狀態(tài),并且釋放所持有的對(duì)象的鎖;?sleep():使一個(gè)正在運(yùn)行的線程處于睡眠狀態(tài),是一個(gè)靜態(tài)方法,調(diào)用此方法要處理InterruptedException異常;?notify():?jiǎn)拘岩粋€(gè)處于等待狀態(tài)的線程,當(dāng)然在調(diào)用此方法的時(shí)候,并不能確切的喚醒某一個(gè)等待狀態(tài)的線程,而是由JVM 確定喚醒哪個(gè)線程,而且與優(yōu)先級(jí)無(wú)關(guān);?notityAll():?jiǎn)拘阉刑幱诘却隣顟B(tài)的線程,該方法并不是將對(duì)象的鎖給所有線程,而是讓它們競(jìng)爭(zhēng),只有獲得鎖的線程才能進(jìn)入就緒狀態(tài);
6. Thread類(lèi)的sleep()方法和對(duì)象的wait()方法有什么區(qū)別?線程的sleep()方法和yield()方法有什么區(qū)別?
Thread類(lèi)的sleep()方法和對(duì)象的wait()方法
sleep()**方法(休眠)是**線程類(lèi)( Thread)**的**靜態(tài)方法,調(diào)用此方法讓當(dāng)前線程暫停執(zhí)行指定的時(shí)間, 將執(zhí)行機(jī)會(huì)( CPU)讓給其他線程, 但是**
對(duì)象的鎖依然保持,因此休眠時(shí)間結(jié)束后會(huì)自動(dòng)恢復(fù)(線程回到就緒狀態(tài))。*wait()*?方法是Object 類(lèi)的方法**,調(diào)用對(duì)象的wait()方法導(dǎo)致當(dāng)前線程?放棄對(duì)象的鎖(線程暫停執(zhí)行), 進(jìn)入對(duì)象的等待池(wait pool),只有調(diào)用對(duì)象的notify()方法(或notifyAll()方法)時(shí)才能喚醒等待池中的線程進(jìn)入等鎖池(lock pool), 如果線程重新獲得對(duì)象的鎖就可以進(jìn)入就緒狀態(tài)。線程的sleep()方法和yield()方法
1)sleep()方法給其他線程運(yùn)行機(jī)會(huì)時(shí)不考慮線程的優(yōu)先級(jí),因此會(huì)給低優(yōu)先級(jí)的線程以運(yùn)行的機(jī)會(huì);yield()方法只會(huì)給相同優(yōu)先級(jí)或更高優(yōu)先級(jí)的線程以運(yùn)行的機(jī)會(huì);2)線程執(zhí)行sleep()方法后轉(zhuǎn)入阻塞( blocked)狀態(tài), 而執(zhí)行yield()方法后轉(zhuǎn)入就緒( ready)狀態(tài);3)sleep()方法聲明拋出InterruptedException,而yield()方法沒(méi)有聲明任何異常;4)sleep()方法比yield()方法(跟操作系統(tǒng)CPU調(diào)度相關(guān))具有更好的可移植性。
7. 什么是線程池(thread pool)
在面向?qū)ο缶幊讨校瑒?chuàng)建和銷(xiāo)毀對(duì)象是很費(fèi)時(shí)間的,因?yàn)閯?chuàng)建一個(gè)對(duì)象要獲取內(nèi)存資源或者其它更多資源。在Java 中,虛擬機(jī)將試圖跟蹤每一個(gè)對(duì)象,以便能夠在對(duì)象銷(xiāo)毀后進(jìn)行垃圾回收。所以提高服務(wù)程序效率的一個(gè)手段就是盡可能減少創(chuàng)建和銷(xiāo)毀對(duì)象的次數(shù),特別是一些很耗資源的對(duì)象創(chuàng)建和銷(xiāo)毀,這就是**?
池化資源技術(shù)**產(chǎn)生的原因。線程池顧名思義就是事先創(chuàng)建若干個(gè)可執(zhí)行的線程放入一個(gè)池(容器)中,需要的時(shí)候從池中獲取線程不用自行創(chuàng)建, 使用完畢不需要銷(xiāo)毀線程而是放回池中,從而減少創(chuàng)建和銷(xiāo)毀線程對(duì)象的開(kāi)銷(xiāo)。Java 5+中的Executor接口定義一個(gè)執(zhí)行線程的工具。它的子類(lèi)型即線程池接口是ExecutorService。工具類(lèi)Executors面提供了一些靜態(tài)工廠方法,生成一些常用的線程池。工具類(lèi)Executors生成線程池的常用方法:1)newSingleThreadExecutor:創(chuàng)建一個(gè)單線程的線程池。這個(gè)線程池只有一個(gè)線程在工作,也就是相當(dāng)于單線程串行執(zhí)行所有任務(wù)。如果這個(gè)唯一的線程因?yàn)楫惓=Y(jié)束,那么會(huì)有一個(gè)新的線程來(lái)替代它。此線程池保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行。2)?newFixedThreadPool:創(chuàng)建固定大小的線程池。每次提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線程,直到線程達(dá)到線程池的最大大小。線程池的大小一旦達(dá)到最大值就會(huì)保持不變,如果某個(gè)線程因?yàn)閳?zhí)行異常而結(jié)束,那么線程池會(huì)補(bǔ)充一個(gè)新線程(返回到線程池中)。【推薦使用】?3)newCachedThreadPool:創(chuàng)建一個(gè)可緩存的線程池。如果線程池的大小超過(guò)了處理任務(wù)所需要的線程,那么就會(huì)回收部分空閑(60 秒不執(zhí)行任務(wù))的線程,當(dāng)任務(wù)數(shù)增加時(shí),此線程池又可以智能的添加新線程來(lái)處理任務(wù)。此線程池不會(huì)對(duì)線程池大小做限制,線程池大小完全依賴(lài)于操作系統(tǒng)(或者說(shuō)JVM)能夠創(chuàng)建的最大線程大小。4)newScheduledThreadPool:創(chuàng)建一個(gè)大小無(wú)限的線程池。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求。5)newSingleThreadExecutor:創(chuàng)建一個(gè)單線程的線程池。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求。
如果希望在服務(wù)器上使用線程池,強(qiáng)烈建議使用newFixedThreadPool方法來(lái)創(chuàng)建線程池,這樣能獲得更好的性能。
8. Java中守護(hù)線程和本地線程區(qū)別
java 中的線程分為兩種:守護(hù)線程( Daemon)和用戶(hù)線程( User)。??j任何線程都可以設(shè)置為守護(hù)線程和用戶(hù)線程,通過(guò)方法
Thread.setDaemon(boolon);true則把該線程設(shè)置為守護(hù)線程,false則設(shè)置為用戶(hù)線程。Thread.setDaemon() 必須在Thread.start()之前調(diào)用,否則運(yùn)行時(shí)會(huì)拋出異常。守護(hù)線程和本地線程兩者的區(qū)別
唯一的區(qū)別是判斷虛擬機(jī)(JVM)何時(shí)離開(kāi),守護(hù)線程Daemon是為其他線程提供服務(wù),如果全部的用戶(hù)現(xiàn)場(chǎng)Thread 已經(jīng)撤離, Daemon 沒(méi)有可服務(wù)的線程,JVM 撤離。也可 以理解為守護(hù)線程是JVM 自動(dòng)創(chuàng)建的線程( 但不一定),用戶(hù)線程是程序創(chuàng)建的線程;比如JVM 的垃圾回收線程是一個(gè)守護(hù)線程,當(dāng)所有線程已經(jīng)撤離,不再產(chǎn)生垃圾,守護(hù)線程自然就沒(méi)事可干了,當(dāng)垃圾回收線程是Java 虛擬機(jī)上僅剩的線 程時(shí),Java 虛擬機(jī)會(huì)自動(dòng)離開(kāi)。
9. synchronized關(guān)鍵字的用法,當(dāng)一個(gè)線程進(jìn)入一個(gè)對(duì)象的synchronized方法A之后,其它線程是否可進(jìn)入此對(duì)象的synchronized方法B?
1)synchronized 關(guān)鍵字可以將對(duì)象或者方法標(biāo)記為同步,以實(shí)現(xiàn)對(duì)對(duì)象和方法的互斥訪問(wèn),可以用
synchronized(對(duì)象) { }定義同步代碼塊,或者在聲明方法時(shí)將synchronized作為方法的修飾符。2)不能進(jìn)入。??其它線程只能訪問(wèn)該對(duì)象的非同步方法,同步方法則不能進(jìn)入。因?yàn)榉庆o態(tài)方法上的synchronized修飾符要求執(zhí)行方法時(shí)要獲得對(duì)象的鎖,如果已經(jīng)進(jìn)入A方法說(shuō)明對(duì)象鎖已經(jīng)被取走,那么試圖進(jìn)入B 方法的線程就只能在等鎖池( 注意不是等待池)中等待對(duì)象的鎖。
10. 簡(jiǎn)述synchronized和java.util.concurrent.locks.Lock的異同
Lock是JDK1.5 以后引入的新的API,和關(guān)鍵字synchronized 的異同:相同點(diǎn):Lock 能完成synchronized 所實(shí)現(xiàn)的所有功能;主要不同點(diǎn):Lock有比synchronized 更精確的線程語(yǔ)義和更好的性能,而且不強(qiáng)制性的要求一定要獲得鎖。synchronized會(huì)自動(dòng)釋放鎖,而Lock一定要求程序員手工釋放,并且最好在finally 塊中釋放(這是釋放外部資源的最好的地方)。
11. 死鎖、活鎖、饑餓?產(chǎn)生死鎖的必要條件是什么?如何確保 N 個(gè)線程可以訪問(wèn)N 個(gè)資源同時(shí)又不導(dǎo)致死鎖?
死鎖、活鎖、饑餓?的概念,之間的區(qū)別 1)死鎖:兩個(gè)進(jìn)程都在等待對(duì)方執(zhí)行完畢才能繼續(xù)往下執(zhí)行的時(shí)候就發(fā)生了死鎖。結(jié)果就是兩個(gè)進(jìn)程都陷入了無(wú)限的等待中。因爭(zhēng)奪資源而造成的一種互相等待的現(xiàn)象,若無(wú)外力作用,它們都將無(wú)法推進(jìn)下去?2)活鎖:任務(wù)或者執(zhí)行者沒(méi)有被阻塞,由于某些條件沒(méi)有滿(mǎn)足,導(dǎo)致一直重復(fù)嘗試,失敗,嘗試, 失敗。?3)饑餓:一個(gè)或者多個(gè)線程因?yàn)榉N種原因無(wú)法獲得所需要的資源,導(dǎo)致一直無(wú)法執(zhí)行的狀態(tài)。
活鎖和死鎖的區(qū)別:??處于活鎖的實(shí)體是在不斷的改變狀態(tài),所謂的“ 活”, 而 處于死鎖的實(shí)體表現(xiàn)為等待;活鎖有可能自行解開(kāi),死鎖則不能。
Java 中導(dǎo)致饑餓的原因:1)高優(yōu)先級(jí)線程吞噬所有的低優(yōu)先級(jí)線程的CPU 時(shí)間。2)線程被永久堵塞在一個(gè)等待進(jìn)入同步塊的狀態(tài),因?yàn)槠渌€程總是能在它之前 持續(xù)地對(duì)該同步塊進(jìn)行訪問(wèn)。3)線程在等待一個(gè)本身也處于永久等待完成的對(duì)象(比如調(diào)用這個(gè)對(duì)象的wait 方 法),因?yàn)槠渌€程總是被持續(xù)地獲得喚醒。
產(chǎn)生死鎖的必要條件: 1)互斥條件:所**謂互斥就是進(jìn)程在某一時(shí)間內(nèi)獨(dú)占資源。2)**請(qǐng)求與保持條件:一個(gè)進(jìn)程因請(qǐng)求資源而阻塞時(shí),對(duì)已獲得的資源保持不放。3)不剝奪條件:進(jìn)程已獲得資源,在末使用完之前,不能強(qiáng)行剝奪。4)循環(huán)等待條件:若干進(jìn)程之間形成一種頭尾相接的循環(huán)等待資源關(guān)系。
如何確保 N 個(gè)線程可以訪問(wèn)N 個(gè)資源同時(shí)又不導(dǎo)致死鎖 使用多線程的時(shí)候,一種非常簡(jiǎn)單的避免死鎖的方式就是:指定獲取鎖的順序,并強(qiáng)制線程按照指定的順序獲取鎖。因此,如果所有的線程都是以同樣的順序加鎖和釋放鎖,就不會(huì)出現(xiàn)死鎖了。
多線程的上下文
多線程會(huì)共同使用一組計(jì)算機(jī)上的CPU,而線程數(shù)大于給程序分配的CPU 數(shù)量時(shí),為了讓各個(gè)線程都有執(zhí)行的機(jī)會(huì),就需要輪轉(zhuǎn)使用CPU。不同的線程切換使用CPU發(fā)生的切換數(shù)據(jù)等就是上下文切換。
12. 內(nèi)存中的棧(stack)、堆(heap)和方法區(qū)(method area)的用法
堆:存放方法對(duì)象(通過(guò)new 關(guān)鍵字和構(gòu)造器創(chuàng)建的對(duì)象) 棧:存放方法以及方法中的局部變量 方法區(qū):(也叫共享區(qū))存放代碼片段、靜態(tài)屬性、常量池(比如常量池中存放字符串的值) 通常我們定義一個(gè)基本數(shù)據(jù)類(lèi)型的變量,一個(gè)對(duì)象的引用,函數(shù)調(diào)用的現(xiàn)場(chǎng)保存都使用JVM 中的棧空間;而通過(guò)new 關(guān)鍵字和構(gòu)造器創(chuàng)建的對(duì)象則放在堆空間,堆是垃圾收集器管理的主要區(qū)域,由于現(xiàn)在的垃圾收集器都采用分代收集算法,所以堆空間還可以細(xì)分為新生代和老生代,再具體一點(diǎn)可以分為Eden、Survivor(又可分為From Survivor 和To Survivor)、Tenured;方法區(qū)和堆都 ??是各個(gè)線程共享的內(nèi)存區(qū)域,用于存儲(chǔ)已經(jīng)被JVM 加載的類(lèi)信息、常量、靜態(tài)變 量、JIT 編譯器編譯后的代碼等數(shù)據(jù);程序中的字面量(literal)如直接書(shū)寫(xiě)的100、” hello” 和常量都是放在常量池中, 常量池是方法區(qū)的一部分。棧空間操作起來(lái) 最快但是棧很小,通常大量的對(duì)象都是放在堆空間,棧和堆的大小都可以通過(guò)JVM 的啟動(dòng)參數(shù)來(lái)進(jìn)行調(diào)整,棧空間用光了會(huì)引發(fā)StackOverflowError,而堆和常量 池空間不足則會(huì)引發(fā)OutOfMemoryError。?
String str = new String("HelloWorld");,這個(gè)代碼中,變量str 存放在棧上,用new 創(chuàng)建出來(lái)的字符串對(duì)象放在堆上,”HelloWorld” 這個(gè)字面量(String)是放在方法區(qū)中。
13. switch 的參數(shù)類(lèi)型
JDK1.5 以前,switch()的參數(shù)中,只能是
byte、short、char、int。?從JDK1.5 開(kāi)始, Java 中引入了枚舉類(lèi)型與byte short char int的包裝類(lèi)。從JDK 1.7 開(kāi)始,參數(shù)還可以是字符串( String)類(lèi)型,但是長(zhǎng)整型( long)在目前所有的版本中都是不可以的。?也就是現(xiàn)在switch支持byte、short、char、int、String、枚舉JDK1.5,對(duì)四個(gè)包裝類(lèi)的支持是因?yàn)閖ava編譯器在底層手動(dòng)進(jìn)行拆箱,而對(duì)枚舉類(lèi)的支持是因?yàn)槊杜e類(lèi)有一個(gè)ordinal方法,該方法實(shí)際上是一個(gè)int類(lèi)型的數(shù)值。jdk1.7開(kāi)始支持String類(lèi)型,但實(shí)際上String類(lèi)型有一個(gè)hashCode算法,結(jié)果也是int類(lèi)型.而byte short char類(lèi)型可以在不損失精度的情況下向上轉(zhuǎn)型成int類(lèi)型,所以總的來(lái)說(shuō),可以認(rèn)為switch中只支持int.
14. 兩個(gè)對(duì)象值相同(x.equals(y) == true),但卻可有不同的hash code,這句話對(duì)不對(duì)?【***】
不對(duì),如果兩個(gè)對(duì)象x 和y 滿(mǎn)足x.equals(y) == true,它們的哈希碼(hash code) 應(yīng)當(dāng)相同。Java 對(duì)于eqauls 方法和hashCode 方法是這樣規(guī)定的:
1)如果兩個(gè)對(duì)象相同(equals 方法返回true),那么它們的hashCode 值一定要相同;2)如果兩個(gè)對(duì)象的hashCode 相同,它們并不一定相同。當(dāng)然,你未必要按照要求去做,但是(重寫(xiě)hashCode)如果你違背了上述原則就會(huì)發(fā)現(xiàn)在使用容器時(shí),相同的對(duì)象可以出現(xiàn)在Set 集合中,同時(shí)增加新元素的效率會(huì)大大下降(對(duì)于使用哈希存儲(chǔ)的系統(tǒng),如果哈希碼頻繁的沖突將會(huì)造成存取性能急劇下降)。
關(guān)于equals方法,有以下內(nèi)容必須滿(mǎn)足
首先equals 方法必須滿(mǎn)足 1)自反性( x.equals(x)必須返回true)?2)對(duì)稱(chēng)性(?x.equals(y)返回true 時(shí), y.equals(x)也必須返回true) 3)傳遞性(x.equals(y)和y.equals(z)都返回true 時(shí), x.equals(z)也必須返回true) 4)一致性(當(dāng)x和y引用的對(duì)象信息沒(méi)有被修改時(shí),多次調(diào)用x.equals(y)應(yīng)該得到同樣的返回值) ,而且對(duì)于任何非null值的引用x,x.equals(null)必須返回false。
實(shí)現(xiàn)高質(zhì)量的equals 方法的訣竅包括
1)使用==操作符檢查”參數(shù)是否為這個(gè)對(duì)象的引用”;2)使用instanceof 操作符檢查”參數(shù)是否為正確的類(lèi)型”;3) 對(duì)于類(lèi)中的關(guān)鍵屬性,檢查參數(shù)傳入對(duì)象的屬性是否與之相匹配;4)重寫(xiě)equals方法后,問(wèn)自己它是否滿(mǎn)足自反性、對(duì)稱(chēng)性、傳遞性、一致性;5) 重寫(xiě)equals 時(shí)總是要重寫(xiě)hashCode;6) 不要將equals 方法參數(shù)中的Object 對(duì)象替換為其他的類(lèi)型,在重寫(xiě)時(shí)不要忘掉@Override 注解。
15. String 和StringBuilder、StringBuffer 的區(qū)別【***】
Java平臺(tái)提供了兩種類(lèi)型的字符串:?String 和StringBuffer/StringBuilder,它們可以?xún)?chǔ)存和操作字符串。其中String 是只讀字符串,也就意味著String 引用的字符串內(nèi)容是不能被改變的。而StringBuffer/StringBuilder類(lèi)表示的字符串對(duì)象可以直接進(jìn)行修改。StringBuilder 是JDK 1.5 中引入的,它和StringBuffer 的方法完全相同, 區(qū)別在于StringBuffer因?yàn)楸籹ynchronized 修飾,是線程安全的而StringBuilder是線程不安全的(所有方面都沒(méi)有被synchronized 修飾)?因?yàn)镾tringBuilder沒(méi)有被synchronized同步修飾,所以StringBuilder
【面試題】什么情況下用+運(yùn)算符進(jìn)行字符串連接比調(diào)用StringBuffer/StringBuilder 對(duì)象的append 方法連接字符串性能更好?
首先要清楚以下兩點(diǎn):1)String 對(duì)象的intern方法會(huì)得到字符串對(duì)象在常量池中對(duì)應(yīng)的版本的引用(如果常量池中有一個(gè)字符串與String 對(duì)象的equals結(jié)果是true),如果常量池中沒(méi)有對(duì)應(yīng)的字符串,則該字符串將被添加到常量池中, 然后返回常量池中字符串的引用;2)字符串的+操作其本質(zhì)是創(chuàng)建了StringBuilder對(duì)象進(jìn)行append操作,然后將拼接后的StringBuilder 對(duì)象用toString方法處理成String 對(duì)象。
16. 重載(Overload)和重寫(xiě)(Override)的區(qū)別。重載的方法能否根據(jù)返回類(lèi)型進(jìn)行區(qū)分?
方法的重載和重寫(xiě)都是實(shí)現(xiàn)多態(tài)的方式,區(qū)別在于重載實(shí)現(xiàn)的是編譯時(shí)的多態(tài)性,而重寫(xiě)實(shí)現(xiàn)的是運(yùn)行時(shí)的多態(tài)性。??所謂的重載是編譯時(shí)多態(tài)性,就是根據(jù)實(shí)際的參數(shù)列表,在編譯的時(shí)候就能夠確定執(zhí)行重載方法中的哪一個(gè)了。??所謂的重寫(xiě)是運(yùn)行時(shí)多態(tài)性,就比如父類(lèi)對(duì)象引用子類(lèi)實(shí)例時(shí),調(diào)用方法只有在運(yùn)行時(shí)才知道到底執(zhí)行了哪個(gè)方法。?重載發(fā)生在一個(gè)類(lèi)中,同名的方法如果有不同的參數(shù)列表( 參數(shù)類(lèi)型不同、參數(shù)個(gè)數(shù)不同或者二者都不同)則視為重載;(重載對(duì)返回類(lèi)型沒(méi)有特殊的要求,只考慮參數(shù)列表)?重寫(xiě)發(fā)生在子類(lèi)與父類(lèi)之間(需要繼承),重寫(xiě)要求子類(lèi)被重寫(xiě)方法與父類(lèi)被重寫(xiě)方法有相同的返回類(lèi)型,比父類(lèi)被重寫(xiě)方法更好訪問(wèn),不能比父類(lèi)被重寫(xiě)方法聲明更多的異常( 里氏代換原則)。
17. JVM是什么?為什么 Java 被稱(chēng)作是“平臺(tái)無(wú)關(guān)的編程語(yǔ)言”?JVM 加載class 文件的原理機(jī)制【***】
Java 虛擬機(jī)是一個(gè)可以執(zhí)行 Java 字節(jié)碼的虛擬機(jī)進(jìn)程。Java 源文件被編譯成能被 Java 虛擬機(jī)執(zhí)行的字節(jié)碼文件。
Java 被設(shè)計(jì)成允許應(yīng)用程序可以運(yùn)行在任意的平臺(tái),而不需要程序員為每一個(gè)平臺(tái)單獨(dú)重寫(xiě)或者是重新編譯。Java 虛擬機(jī)讓這個(gè)變?yōu)榭赡埽驗(yàn)樗赖讓佑布脚_(tái)的指令長(zhǎng)度和其他特性。
Java代碼在JVM中的執(zhí)行流程
JVM的類(lèi)加載原理圖
JVM 中類(lèi)的裝載是由類(lèi)加載器(?
ClassLoader)和它的子類(lèi)來(lái)實(shí)現(xiàn)的, Java 中的類(lèi)加載器是一個(gè)重要的Java 運(yùn)行時(shí)系統(tǒng)組件,它負(fù)責(zé)在運(yùn)行時(shí)查找和裝入類(lèi)文件中的類(lèi)。由于Java 的跨平臺(tái)性,經(jīng)過(guò)編譯的Java 源程序并不是一個(gè)可執(zhí)行程序,而是一個(gè)或多個(gè)類(lèi)class文件。當(dāng)Java 程序需要使用某個(gè)類(lèi)時(shí),JVM 會(huì)確保這個(gè)類(lèi)已經(jīng)被加載、連接( 驗(yàn)證、準(zhǔn)備和解析)和初始化。類(lèi)的加載是指把類(lèi)的.class 文件中的數(shù)據(jù)讀入到內(nèi)存中,通常是創(chuàng)建一個(gè)字節(jié)數(shù)組讀入.class 文件,然后產(chǎn)生與所加載類(lèi)對(duì)應(yīng)的Class 對(duì)象。加載完成后,Class 對(duì)象還不完整,所以此時(shí)的類(lèi)還不可用。當(dāng)類(lèi)被加載后就進(jìn)入連接階段,這一階段包括驗(yàn)證、準(zhǔn)備( 為靜態(tài)變量分配內(nèi)存并設(shè)置默認(rèn)的初始值)和解析( 將符號(hào)引用替換為直接引用)三個(gè)步驟。最后JVM 對(duì)類(lèi)進(jìn)行初始化,包括:1)如果類(lèi)存在直接的父類(lèi)并且這個(gè)類(lèi)還沒(méi)有被初始化,那么就先初始化父類(lèi);?2)如果類(lèi)中存在初始化語(yǔ)句, 就依次執(zhí)行這些初始化語(yǔ)句。類(lèi)的加載是由類(lèi)加載器完成的 類(lèi)加載器包括:根加載器(BootStrap)、擴(kuò)展加載器(Extension)、系統(tǒng)加載器( System)和用戶(hù)自定義類(lèi)加載器(java.lang.ClassLoader 的子類(lèi))?。從Java 2( JDK 1.2)開(kāi)始, 類(lèi)加載過(guò)程采取了父親委托機(jī)制(PDM)。PDM 更好的保證了Java 平臺(tái)的安全性,在該機(jī)制中, JVM 自帶的Bootstrap 是根加載器, 其他的加載器都有且僅有一個(gè)父類(lèi)加載器。類(lèi)的加載首先請(qǐng)求父類(lèi)加載器加載,父類(lèi)加載器無(wú)能為力時(shí)才由其子類(lèi)加載器自行加載。JVM 不會(huì)向Java 程序提供對(duì)Bootstrap 的引用。
Bootstrap:一般用本地代碼實(shí)現(xiàn),負(fù)責(zé)加載JVM 基礎(chǔ)核心類(lèi)庫(kù)(rt.jar);Extension:從java.ext.dirs 系統(tǒng)屬性所指定的目錄中加載類(lèi)庫(kù),它的父加載器是Bootstrap;System:又叫應(yīng)用類(lèi)加載器,其父類(lèi)是Extension。它是應(yīng)用最廣泛的類(lèi)加載器。它從環(huán)境變量classpath 或者系統(tǒng)屬性java.class.path 所指定的目錄中記載類(lèi),是用戶(hù)自定義加載器的默認(rèn)父加載器。
18. Java 中會(huì)存在內(nèi)存泄漏嗎?
理論上,Java因?yàn)橛欣厥諜C(jī)制( GC)不會(huì)存在內(nèi)存泄露問(wèn)題( 這也是Java 被廣泛使用于服務(wù)器端編程的一個(gè)重要原因);然而在實(shí)際開(kāi)發(fā)中,可能會(huì)存在
無(wú)用但可達(dá)的對(duì)象,這些對(duì)象不能被GC 回收,因此也會(huì)導(dǎo)致內(nèi)存泄露的發(fā)生。?例如:Hibernate 的Session( 一級(jí)緩存)中的對(duì)象屬于持久態(tài),垃圾回收器是不會(huì)回收這些對(duì)象的,然而這些對(duì)象中可能存在無(wú)用的垃圾對(duì)象,如果不及時(shí)關(guān)閉(close)或清空( flush)一級(jí)緩存就可能導(dǎo)致內(nèi)存泄露。
//實(shí)現(xiàn)了一個(gè)棧(先進(jìn)后出(FILO))結(jié)構(gòu)代碼
import?java.util.Arrays;
import?java.util.EmptyStackException;
public?class?MyStack<T>?{
?private?T[]?elements;
?private?int?size?=?0;
?private?static?final?int?INIT_CAPACITY?=?16;
?
?public?MyStack()?{
??elements?=?(T[])?new?Object[INIT_CAPACITY];
?}
?
?public?void?push(T?elem)?{
??ensureCapacity();
??elements[size++]?=?elem;
?}
?
?public?T?pop()?{
??if(size?==?0)
???throw?new?EmptyStackException();
???return?elements[--size];
??}
?private?void?ensureCapacity()?{
??if(elements.length?==?size)?{
???elements?=?Arrays.copyOf(elements,?2?*?size?+?1);
??}
?}
}
123456789101112131415161718192021222324252627282930
上述代碼是實(shí)現(xiàn)了一個(gè)棧的先進(jìn)后出(FILO)結(jié)構(gòu),但是其中的pop 方法卻存在 內(nèi)存泄露的問(wèn)題?當(dāng)我們用pop 方法彈出棧中的對(duì)象時(shí),該對(duì)象不會(huì)被當(dāng)作垃圾回收,即使使用棧的程序不再引用這些對(duì)象, 因?yàn)闂?nèi)部維護(hù)著對(duì)這些對(duì)象的過(guò)期引用(obsolete reference)。在支持垃圾回收的語(yǔ)言中,內(nèi)存泄露是很隱蔽的,這種內(nèi)存泄露其實(shí)就是無(wú)意識(shí)的對(duì)象保持。如果一個(gè)對(duì)象引用被無(wú)意識(shí)的保留起來(lái)了,那么垃圾回收器不會(huì)處理這個(gè)對(duì)象,也不會(huì)處理該對(duì)象引用的其他對(duì)象,即使這樣的對(duì)象只有少數(shù)幾個(gè),也可能會(huì)導(dǎo)致很多的對(duì)象被排除在垃圾回收之外,從而對(duì)性能造成重大影響,極端情況下會(huì)引發(fā)Disk Paging( 物理內(nèi)存與硬盤(pán)的虛擬內(nèi)存交換數(shù)據(jù)),甚至造成OutOfMemoryError。
19. 抽象方法(abstract)是否可以被靜態(tài)的(static)、本地方法(native)和同步(synchronized)修飾。
答案是都不能。1)抽象方法需要子類(lèi)重寫(xiě),而靜態(tài)的方法是無(wú)法被重寫(xiě)的,因此二者是矛盾的。2)本地方法是由本地代碼(如C代碼)實(shí)現(xiàn)的方法,而抽象方法是沒(méi)有實(shí)現(xiàn)的,也是矛盾的。3)synchronized 和方法的實(shí)現(xiàn)細(xì)節(jié)有關(guān),抽象方法不涉及實(shí)現(xiàn)細(xì)節(jié), 因此也是相互矛盾的。
20. 靜態(tài)變量和實(shí)例變量的區(qū)別
靜態(tài)變量是被static修飾符修飾的變量,也稱(chēng)為類(lèi)變量, 它屬于類(lèi)(與類(lèi)同生死),不屬于類(lèi)的任何一個(gè)對(duì)象,一個(gè)類(lèi)不管創(chuàng)建多少個(gè)對(duì)象, 靜態(tài)變量在內(nèi)存中有且僅有一個(gè)拷貝;靜態(tài)變量可以實(shí)現(xiàn)讓多個(gè)對(duì)象共享內(nèi)存。實(shí)例變量必須依存于某一實(shí)例,需要先創(chuàng)建對(duì)象然后通過(guò)對(duì)象才能訪問(wèn)到它。(在Java 開(kāi)發(fā)中, 上下文類(lèi)和工具類(lèi)中通常會(huì)有大量的靜態(tài)成員。)
21. 實(shí)現(xiàn)對(duì)象克隆的2種方法【***】
對(duì)象是引用數(shù)據(jù)類(lèi)型,對(duì)象的賦值僅僅是吧對(duì)象的引用賦值給另一個(gè)對(duì)象,他們的堆空間是相同的。克隆就是需要有一個(gè)完全相同的對(duì)象,而且兩者之間完全互不影響,克隆對(duì)象和原對(duì)象是兩個(gè)完全對(duì)立的對(duì)象。(java.lang.Object類(lèi)的clone()方法,對(duì)象克隆就是對(duì)象的復(fù)制,即完整的復(fù)制出一個(gè)對(duì)象。)
1)實(shí)現(xiàn)
Cloneable接口并重寫(xiě)Object 類(lèi)中的clone()方法;(淺克隆對(duì)象的克隆默認(rèn)是淺度克隆,也就是包含在對(duì)象內(nèi)部的對(duì)象是不會(huì)被克隆的),【通過(guò)clone()實(shí)現(xiàn)深度克隆:讓克隆對(duì)象所包含的類(lèi)也實(shí)現(xiàn)clone功能(實(shí)現(xiàn)cloneable接口,重載clone()方法)】 2)實(shí)現(xiàn)Serializable接口,通過(guò)對(duì)象的序列化和反序列化實(shí)現(xiàn)克隆,可以實(shí)現(xiàn)真正的深度克隆基于序列化和反序列化(
Serializable)實(shí)現(xiàn)的克隆不僅僅是深度克隆, 更重要的是通過(guò)泛型限定,可以檢查出要克隆的對(duì)象是否支持序列化,這項(xiàng)檢查是編譯器完成的,不是在運(yùn)行時(shí)拋出異常,這種是方案明顯優(yōu)于使用Object 類(lèi)的clone 方法克隆對(duì)象。讓問(wèn)題在編譯的時(shí)候暴露出來(lái)總是好過(guò)把問(wèn)題留到運(yùn)行時(shí)。
22. Java 中如何實(shí)現(xiàn)序列化Serializable,有什么意義?
Serializable序列化就是一種用來(lái)處理對(duì)象流的機(jī)制,所謂對(duì)象流也就是將對(duì)象的內(nèi)容進(jìn)行流化。可以對(duì)流化后的對(duì)象進(jìn)行讀寫(xiě)操作,也可將流化后的對(duì)象傳輸于網(wǎng)絡(luò)之間。序列化是為了解決對(duì)象流讀寫(xiě)操作時(shí)可能引發(fā)的問(wèn)題( 如果不進(jìn)行序列化可能會(huì)存在數(shù)據(jù)亂序的問(wèn)題) 。要實(shí)現(xiàn)序列化,需要讓一個(gè)類(lèi)實(shí)現(xiàn)
Serializable接口,該接口是一個(gè)標(biāo)識(shí)性接口,標(biāo)注該類(lèi)對(duì)象是可被序列化的,然后使用一個(gè)輸出流來(lái)構(gòu)造一個(gè)對(duì)象輸出流并通過(guò)writeObject(Object)方法就可以將實(shí)現(xiàn)對(duì)象寫(xiě)出(即保存其狀態(tài));如果需要反序列化則可以用一個(gè)輸入流建立對(duì)象輸入流,然后通過(guò)readObject 方法從流中讀取對(duì)象。序列化除了能夠?qū)崿F(xiàn)對(duì)象的持久化之外,還能夠用于對(duì)象的深度克隆(上面所說(shuō)的)。
23. GC垃圾回收器,為什么需要它。【***】
java中由JVM的垃圾回收管理器(GC)來(lái)負(fù)責(zé)回收不需要再使用的堆內(nèi)存和棧內(nèi)存。GC 功能可以自動(dòng)監(jiān)測(cè)對(duì)象是否超過(guò)作用域從而達(dá)到自動(dòng)回收內(nèi)存的目的。JVM的垃圾回收采用動(dòng)態(tài)存儲(chǔ)管理技術(shù),它能夠自己檢測(cè)內(nèi)存的使用情況,自動(dòng)地釋放不再被程序引用的對(duì)象,按照特定的垃圾收集算法來(lái)實(shí)現(xiàn)內(nèi)存資源自動(dòng)回收功能。JVM垃圾回收管理器處理釋放沒(méi)用的對(duì)象外,還可以清除內(nèi)存記錄的碎片(因?yàn)閯?chuàng)建對(duì)象和垃圾收集齊全不斷釋放對(duì)其對(duì)象所占用的空間,因此內(nèi)存會(huì)出現(xiàn)碎片)。碎片整理將所占用的堆內(nèi)存移動(dòng)到堆的一端,JVM將整理出的內(nèi)存分配給新的對(duì)象。JVM垃圾回收器有一個(gè)潛在的缺點(diǎn) :增加了系統(tǒng)的開(kāi)銷(xiāo),影響了程序的性能。因?yàn)镴VM必須追蹤運(yùn)行程序中有用的對(duì)象,到最終釋放沒(méi)用的對(duì)象,這個(gè)過(guò)程需要花費(fèi)處理器的時(shí)間。一個(gè)對(duì)象如果不再被任何棧內(nèi)存所引用,則該對(duì)象成為垃圾對(duì)象。垃圾收集器對(duì)垃圾對(duì)象的收集時(shí)間是不確定的,也可以直接使用
System.gc()方法Runtime.getRuntime().gc()回收垃圾對(duì)象,但是這種強(qiáng)制執(zhí)行垃圾回收程序?qū)ο到y(tǒng)性能會(huì)產(chǎn)生負(fù)面影響。(JVM 可以屏蔽掉顯示的垃圾回收調(diào)用,就是顯示調(diào)用方法,JVM的GC會(huì)自動(dòng)進(jìn)行管理)雖然你可以調(diào)用System.gc() 或者Runtime.gc(),但是沒(méi)有辦法保證GC的執(zhí)行。垃圾回收可以有效的防止內(nèi)存泄露, 有效的使用可以使用的內(nèi)存。垃圾回收器通常是作為一個(gè)單獨(dú)的低優(yōu)先級(jí)的線程運(yùn)行,不可預(yù)知的情況下對(duì)內(nèi)存堆中已經(jīng)死亡的或者長(zhǎng)時(shí)間沒(méi)有使用的對(duì)象進(jìn)行清除和回收,程序員不能實(shí)時(shí)的調(diào)用垃圾回 收器對(duì)某個(gè)對(duì)象或所有對(duì)象進(jìn)行垃圾回收。在Java 誕生初期,垃圾回收是Java 最大的亮點(diǎn)之一,因?yàn)榉?wù)器端的編程需要有效的防止內(nèi)存泄露問(wèn)題,然而時(shí)過(guò) 境遷,如今Java 的垃圾回收機(jī)制已經(jīng)成為被詬病的東西。移動(dòng)智能終端用戶(hù)通常 覺(jué)得iOS 的系統(tǒng)比Android 系統(tǒng)有更好的用戶(hù)體驗(yàn),其中一個(gè)深層次的原因就在 于Android 系統(tǒng)中垃圾回收的不可預(yù)知性。
補(bǔ)充:垃圾回收機(jī)制有很多種,包括:分代復(fù)制垃圾回收、標(biāo)記垃圾回收、增量 垃圾回收等方式。標(biāo)準(zhǔn)的Java 進(jìn)程既有棧又有堆。棧保存了原始型局部變量,堆 保存了要?jiǎng)?chuàng)建的對(duì)象。Java 平臺(tái)對(duì)堆內(nèi)存回收和再利用的基本算法被稱(chēng)為標(biāo)記和 清除,但是Java 對(duì)其進(jìn)行了改進(jìn),采用“ 分代式垃圾收集”。這種方法會(huì)跟Java 對(duì)象的生命周期將堆內(nèi)存劃分為不同的區(qū)域, 在垃圾收集過(guò)程中,可能會(huì)將對(duì)象 移動(dòng)到不同區(qū)域:
24. String和基本數(shù)據(jù)類(lèi)型之間的轉(zhuǎn)換
1)將字符串轉(zhuǎn)換為基本數(shù)據(jù)類(lèi)型 ??調(diào)用基本數(shù)據(jù)類(lèi)型對(duì)應(yīng)的包裝類(lèi)中的方法
parseXXX(String)或valueOf(String)即可返回相應(yīng)基本類(lèi)型;2)將基本數(shù)據(jù)類(lèi)型轉(zhuǎn)換為字符串 ??一種方法是將基本數(shù)據(jù)類(lèi)型與空字符串?""?通過(guò)?+?連接即可獲得其所對(duì)應(yīng)的字符串;??另一種方法是調(diào)用String 類(lèi)中的valueOf()方法返回相應(yīng)字符串。String.valueOf()
25. Java和JavaScript區(qū)別
Java和JavaScript介紹
JavaScript 與Java 是兩個(gè)公司開(kāi)發(fā)的不同的兩個(gè)產(chǎn)品。Java?是原Sun Microsystems 公司推出的面向?qū)ο蟮某绦蛟O(shè)計(jì)語(yǔ)言,特別適合于互聯(lián)網(wǎng)應(yīng)用程序開(kāi)發(fā);而JavaScript?是Netscape 公司的產(chǎn)品,為了擴(kuò)展Netscape瀏覽器的功能而開(kāi)發(fā)的一種可以嵌入Web 頁(yè)面中運(yùn)行的基于對(duì)象和事件驅(qū)動(dòng)的解釋性語(yǔ)言。JavaScript 的前身是LiveScript;而Java 的前身是Oak 語(yǔ)言。
Java和JavaScript的區(qū)別
1)Java是面向?qū)ο蟮亩鳭avaScript是基于對(duì)象的。??Java 是一種真正的面向?qū)ο蟮恼Z(yǔ)言,即使是開(kāi)發(fā)簡(jiǎn)單的程序,必須設(shè)計(jì)對(duì)象;JavaScript 是種腳本語(yǔ)言,它可以用來(lái)制作與網(wǎng)絡(luò)無(wú)關(guān)的,與用戶(hù)交互作用的復(fù)雜軟件。它是一種基于對(duì)象(Object-Based)和事件驅(qū)動(dòng)(Event-Driven)的編程語(yǔ)言,因而它本身提供了非常豐富的內(nèi)部對(duì)象供設(shè)計(jì)人員使用。2)Java是編譯運(yùn)行,而JavaScript是解釋執(zhí)行的。??Java 的源代碼在執(zhí)行之前,必須經(jīng)過(guò)編譯。JavaScript 是一種解釋性編程語(yǔ)言,其源代碼不需經(jīng)過(guò)編譯,由瀏覽器解釋執(zhí)行。(目前的瀏覽器幾乎都使用了JIT(即時(shí)編譯)技術(shù)來(lái)提升JavaScript 的運(yùn)行效率 3)Java變量必須先聲明后使用,JavaScript是弱類(lèi)型語(yǔ)言,直接使用。??Java采用強(qiáng)類(lèi)型變量檢查,即所有變量在編譯之前必須作聲明;JavaScript 中變量是弱類(lèi)型的,甚至在使用變量前可以不作聲明,JavaScript 的解釋器在運(yùn)行時(shí)檢查推斷其數(shù)據(jù)類(lèi)型。4)Java是靜態(tài)語(yǔ)言,JavaScript是動(dòng)態(tài)語(yǔ)言。
26. try{}里有一個(gè)return 語(yǔ)句,finally是否執(zhí)行,執(zhí)行位置。
try{}里有一個(gè)return語(yǔ)句返回,那么緊跟在這個(gè)try后的finally{}里的代碼也是會(huì)被執(zhí)行的,是在方法返回調(diào)用前(return前執(zhí)行)?如果存在finally代碼塊,try中的return語(yǔ)句不會(huì)立馬返回調(diào)用者,而是記錄下返回值待finally代碼塊執(zhí)行完畢之后再向調(diào)用者返回其值(執(zhí)行return)。在finally中改變返回值的做法是不好的,如果在finally 中修改了返回值,就會(huì)返回修改后的值。在finally 中返回或者修改返回值會(huì)對(duì)程序造成很大的困擾,C#中直接用編譯錯(cuò)誤的方式來(lái)阻止程序員干這種齷齪的事情,Java 中也可以通過(guò)提升編譯器的語(yǔ)法檢查級(jí)別來(lái)產(chǎn)生警告或錯(cuò)誤(idea、eclipse也能自己設(shè)置)
27. 運(yùn)行時(shí)異常和受檢查異常
異常表示程序運(yùn)行過(guò)程中可能出現(xiàn)的非正常狀態(tài)。?運(yùn)行時(shí)異常:表示虛擬機(jī)的通常操作中可能遇到的異常,是一種常見(jiàn)運(yùn)行錯(cuò)誤,只要程序設(shè)計(jì)得沒(méi)有問(wèn)題通常就不會(huì)發(fā)生。(在運(yùn)行是拋出)?受檢查異常跟程序運(yùn)行的上下文環(huán)境有關(guān),即使程序設(shè)計(jì)無(wú)誤, 仍然可能因使用的問(wèn)題而引發(fā)。Java 編譯器要求方法必須聲明拋出可能發(fā)生的受檢查異常,但是并不要求必須聲明拋出未被捕獲的運(yùn)行時(shí)異常。常見(jiàn)的運(yùn)行時(shí)異常:
ArithmeticException(算術(shù)異常) ClassCastException (類(lèi)轉(zhuǎn)換異常) IllegalArgumentException (非法參數(shù)異常) IndexOutOfBoundsException (下標(biāo)越界異常) NullPointerException (空指針異常) SecurityException (安全異常)
28. final、finally、finalize 的區(qū)別
final關(guān)鍵字
一個(gè)基本數(shù)據(jù)類(lèi)型聲明為final,就只能進(jìn)行一次賦值(初始化),編譯器會(huì)自動(dòng)檢查代碼,如果需要修改final的初始化,就會(huì)編譯報(bào)錯(cuò)。final聲明的引用數(shù)據(jù)類(lèi)型,當(dāng)前引用是不能改變的,但是可以改變引用指向的內(nèi)存空間的值。final一般和static搭配使用作為常量。final關(guān)鍵字的三種用法:1)修飾類(lèi):表示該類(lèi)不能被繼承;2)修飾方法:表示方法不能被重寫(xiě);3)修飾變量:表示變量只能一次賦值以后值不能被修改(常量)(final修飾成員變量必須在聲明時(shí)初始化或者再構(gòu)造器中初始化,否則報(bào)編譯錯(cuò)誤,而且final修飾的變量通常都是常量,常量名全部大寫(xiě)) ??如果一個(gè)類(lèi)被聲明為final,意味著它不能再派生出新的子類(lèi),即不能被繼承,因此它和abstract 是反義詞。將變量聲明為final,可以保證它們?cè)谑褂弥胁槐桓淖儯宦暶鳛閒inal 的變量必須在聲明時(shí)給定初值,而在以后的引用中只能讀取不可修改。被聲明為final 的方法也同樣只能使用,不能在子類(lèi)中被重寫(xiě)。
final優(yōu)勢(shì):1)final關(guān)鍵字可以提高性能,JVM和Java應(yīng)用都會(huì)緩存final變量。?2)final變量可以在安全的多線程環(huán)境下進(jìn)行資源共享,而不需要額外的同步開(kāi)銷(xiāo)。
finally
通常放在try…catch…的后面構(gòu)造總是執(zhí)行代碼塊,這就意味著程序無(wú)論正常執(zhí)行還是發(fā)生異常,這里的代碼只要JVM 不關(guān)閉都能執(zhí)行,可以將釋放外部資源的代碼寫(xiě)在finally 塊中(關(guān)閉數(shù)據(jù)庫(kù)連接、釋放資源)。
finalize
Object 類(lèi)中定義的方法,Java 中允許使用finalize()方法在垃圾收集器將對(duì)象從內(nèi)存中清除出去之前做必要的清理工作。這個(gè)方法是由垃圾收集器在銷(xiāo)毀對(duì)象時(shí)調(diào)用的,通過(guò)重寫(xiě)finalize()方法可以整理系統(tǒng)資源或者執(zhí)行其他清理工作。
29. List、Map、Set 三個(gè)接口存取元素時(shí),各有什么特點(diǎn)?
List以特定索引來(lái)存取元素,可以有重復(fù)元素。?Set?不能存放重復(fù)元素(用對(duì)象的**equals()*方法來(lái)區(qū)分元素是否重復(fù))。**Map**保存*鍵值對(duì)( key-value)映射,映射關(guān)系可以是一對(duì)一或多對(duì)一。(Map不支持一對(duì)多,但是可以用
Map這種格式來(lái)達(dá)到一對(duì)多的系,多對(duì)多類(lèi)似)Set和Map容器都有基于哈希存儲(chǔ)和排序樹(shù)的兩種實(shí)現(xiàn)版本,基于哈希存儲(chǔ)的版本理論存取時(shí)間復(fù)雜度為O(1),而基于排序樹(shù)版本的實(shí)現(xiàn)在插入或刪除元素時(shí)會(huì)按照元素或元素的鍵( key)構(gòu)成排序樹(shù)從而達(dá)到排序和去重的效果。
30. ArrayList、Vector、LinkedList 的存儲(chǔ)性能和特性【***】
ArrayList和Vector都是使用數(shù)組方式存儲(chǔ)數(shù)據(jù),此數(shù)組元素?cái)?shù)大于實(shí)際存儲(chǔ)的數(shù)據(jù)以便增加和插入元素,它們都允許直接按序號(hào)索引元素,但是插入元素要涉及數(shù)組元素移動(dòng)等內(nèi)存操作,所以索引查詢(xún)數(shù)據(jù)快而插入數(shù)據(jù)慢,?Vector 中的方法由于添加了synchronized 修飾,因此Vector 是線程安全的容器,但性能上較ArrayList 差,因此已經(jīng)是Java 中的遺留容器。?LinkedList 使用
雙向鏈表實(shí)現(xiàn)存儲(chǔ)( 將內(nèi)存中零散的內(nèi)存單元通過(guò)附加的引用關(guān)聯(lián)起來(lái),形成一個(gè)可以按序號(hào)索引的線性結(jié)構(gòu),這種鏈?zhǔn)酱鎯?chǔ)方式與數(shù)組的連續(xù)存儲(chǔ)方式相比, 內(nèi)存的利用率更高) ,按序號(hào)索引數(shù)據(jù)需要進(jìn)行前向或后向遍歷,但是插入數(shù)據(jù)時(shí)只需要記錄本項(xiàng)的前后項(xiàng)即可,所以插入速度較快。Vector 屬于遺留容器(Java 早期的版本中提供的容器, 除此之外,Hashtable、Dictionary、BitSet、Stack、Properties都是遺留容器),已經(jīng)不推薦使用, 但是由于**ArrayList和LinkedListed都是非線程安全的**, 如果遇到多個(gè)線程操作同一個(gè)容器的場(chǎng)景,則可以通過(guò)工具Collections 中的synchronizedList 方法將其轉(zhuǎn)換成線程安全的容器后再使用( 這是對(duì)裝潢模式的應(yīng)用, 將已有對(duì)象傳入另一個(gè)類(lèi)的構(gòu)造器中創(chuàng)建新的對(duì)象來(lái)增強(qiáng)實(shí)現(xiàn))。
31. Collection 和Collections 的區(qū)別,Collections 工具類(lèi)中的sort()方法如何比較元素,TreeMap和TreeSet 在排序時(shí)如何比較元素
Collection是一個(gè)接口, 它是Set、List 等容器的父接口。Collections是個(gè)一個(gè)工具類(lèi),提供了一系列的靜態(tài)方法來(lái)輔助容器操作,這些方法包括對(duì)容器的搜索、排序、線程安全化等等。
Collections工具類(lèi)的sort方法有兩種重載的形式,第一種要求傳入 的待排序容器中存放的對(duì)象比較實(shí)現(xiàn)Comparable接口以實(shí)現(xiàn)元素的比較;第二種不強(qiáng)制性的要求容器中的元素必須可比較, 但是要求傳入第二個(gè)參數(shù), 參數(shù)是Comparator接口的子類(lèi)型(需要重寫(xiě)compare 方法實(shí)現(xiàn)元素的比較),相當(dāng)于一個(gè)臨時(shí)定義的排序規(guī)則,其實(shí)就是通過(guò)接口注入比較元素大小的算法, 也是對(duì)回調(diào)模式的應(yīng)用( Java 中對(duì)函數(shù)式編程的支持)。
TreeSet要求存放的對(duì)象所屬的類(lèi)必須實(shí)現(xiàn)
Comparable接口,該接口提供了比較元素的compareTo()方法,當(dāng)插入元素時(shí)會(huì)回調(diào)該方法比較元素的大小。?TreeMap要求存放的鍵值對(duì)映射的鍵必須實(shí)現(xiàn)Comparable 接口從而根據(jù)鍵對(duì)元素進(jìn)行排序。
32. XML 文檔定義有幾種形式?它們之間有何本質(zhì)區(qū)別?解析XML 文檔有哪幾種方式?
1)XML文檔定義分為DTD和Schema兩種形式,二者都是對(duì)XML語(yǔ)法的約束。2)DTD和Schema兩種形式的本質(zhì)區(qū)別在于Schema本身也是一個(gè)XML文件,可以被XML 解析器解析,而且可以為XML 承載的數(shù)據(jù)定義類(lèi)型,約束能力較之DTD更強(qiáng)大。3)對(duì)XML的解析主要有DOM(文檔對(duì)象模型,Document Object Model)、SAX( Simple API forXML)和StAX(JDK1.6 中引入的新的解析XML的方式,Streaming API for XML)。
其中DOM處理大型文件時(shí)其性能下降的非常厲害,這個(gè)問(wèn)題是由DOM 樹(shù)結(jié)構(gòu)占用的內(nèi)存較多造成的,而且DOM 解析方式必須在解析文件之前把整個(gè)文檔裝入內(nèi)存,適合對(duì)XML 的隨機(jī)訪問(wèn)( 典型的用空間換取時(shí)間的策略);?SAX是事件驅(qū)動(dòng)型的XML解析方式,它順序讀取XML 文件,不需要一次全部裝載整個(gè)文件。當(dāng)遇到像文件開(kāi)頭,文檔結(jié)束,或者標(biāo)簽開(kāi)頭與標(biāo)簽結(jié)束時(shí),它會(huì)觸發(fā)一個(gè)事件,用戶(hù)通過(guò)事件回調(diào)代碼來(lái)處理XML文件,適合對(duì)XML 的順序訪問(wèn);顧名思義,?StAX 把重點(diǎn)放在流上,實(shí)際上StAX與其他解析方式的本質(zhì)區(qū)別就在于應(yīng)用程序能夠把XML作為一個(gè)事件流來(lái)處理。SAX 也是這樣做的,但不同之處在于StAX 允許應(yīng)用程序代碼把這些事件逐個(gè)拉出來(lái),而不用提供在解析器方便時(shí)從解析器中接收事件的處理程序。
33. XML的主要作用
XML的主要作用有兩個(gè)方面:數(shù)據(jù)交換和信息配置。在做數(shù)據(jù)交換時(shí),XML將數(shù)據(jù)用標(biāo)簽組裝成起來(lái), 然后壓縮打包加密后通過(guò)網(wǎng)絡(luò)傳送給接收者,接收解密與解壓縮后再?gòu)腦ML文件中還原相關(guān)信息進(jìn)行處理,XML曾經(jīng)是異構(gòu)系統(tǒng)間交換數(shù)據(jù)的事實(shí)標(biāo)準(zhǔn),但此項(xiàng)功能幾乎已經(jīng)被JSON( JavaScript Object Notation)取而代之。當(dāng)然,目前很多軟件仍然使用XML 來(lái)存儲(chǔ)配置信息,我們?cè)诤芏囗?xiàng)目中 通常也會(huì)將作為配置信息的硬代碼寫(xiě)在XML 文件中,Java 的很多框架也是這么做的, 而且這些框架都選擇了dom4j 作為處理XML 的工具,(因?yàn)镾un 公司的官方API 實(shí)在不怎么好用。)
34. JDBC操作數(shù)據(jù)庫(kù)的步驟(MySQL)
//1.?加載JDBC驅(qū)動(dòng)程序(加載MySQL驅(qū)動(dòng)類(lèi))
Class.forName("com.mysql.jdbc.Driver");
//2.?提供JDBC連接的URL來(lái)創(chuàng)建連接
?//databaseName數(shù)據(jù)庫(kù)名稱(chēng),useUnicode=true:表示使用Unicode字符集,characterEncoding=UF-8字符編碼方式utf-8, udrtnsmr和password是mysql連接用戶(hù)名和密碼
Connection?con?=?DriverManager.getConnection("jdbc:mysql://localhost:3306/databaseName?useUnicode=true&characterEncoding=UF-8;",username,password);
?//?也可以將mysql驅(qū)動(dòng)寫(xiě)到DriverManager.getConnection()中
//3.?PreparedStatement預(yù)編譯sql
String?sql?=?"select?*?from?dept?where?id?=????and?name?=??";
PreparedStatement?ps?=?con.prepareStatement(sql);
ps.setInt(1,?10);
ps.setInt(2,?"研究員");
//4.?執(zhí)行SQL語(yǔ)句
ResultSet?rs?=?ps.executeQuery();
//5.?處理結(jié)果
while(rs.next())?{
?System.out.println(rs.getInt("id"));
?System.out.println(rs.getInt("name"));
}
//6.?在finally里面進(jìn)行釋放資源(關(guān)閉外部資源的順序應(yīng)該和打開(kāi)的順序相反)
if(rs?!=null){
?rs.close;
}
if(ps?!=?null){
?ps.close();
}
if(con?!=?null)?{
?con.close();
}
1234567891011121314151617181920212223242526272829
35. Statement和PreparedStatement哪個(gè)性能更好?
PreparedStatement性能更好?1)PreparedStatement接口代表預(yù)編譯的語(yǔ)句,它主要的優(yōu)勢(shì)在于可以減少SQL 的編譯錯(cuò)誤并增加SQL的安全性(減少SQL 注射攻擊的可能性) 2) PreparedStatement 中的SQL 語(yǔ)句是可以帶參數(shù)的,避免了用字符串連接拼接SQL 語(yǔ)句的麻煩和不安全;3)當(dāng)批量處理SQL 或頻繁執(zhí)行相同的查詢(xún)時(shí),PreparedStatement 有明顯的性能上的優(yōu)勢(shì),由于數(shù)據(jù)庫(kù)可以將編譯優(yōu)化后的SQL 語(yǔ)句緩存起來(lái),下次執(zhí)行相同結(jié)構(gòu)的語(yǔ)句時(shí)就會(huì)很快(不用再次編譯和生成執(zhí)行計(jì)劃)。
36. JDBC 能否處理Blob 和Clob
Blob是指二進(jìn)制大對(duì)象(Binary Large Object)?Clob是指大字符對(duì)象(Character Large Objec)?Blob是為存儲(chǔ)大的二進(jìn)制數(shù)據(jù)而設(shè)計(jì)的,而Clob 是為存儲(chǔ)大的文本數(shù)據(jù)而設(shè)計(jì)的。JDBC 的PreparedStatement和 ResultSet都提供了相應(yīng)的方法來(lái)支持Blob和Clob操作(流的操作)。
37. 數(shù)據(jù)庫(kù)編程時(shí),連接池的優(yōu)勢(shì)
由于創(chuàng)建連接和釋放連接都有很大的開(kāi)銷(xiāo)(尤其是數(shù)據(jù)庫(kù)服務(wù)器不在本地時(shí),每次建立連接都需要進(jìn)行TCP的三次握手,釋放連接需要進(jìn)行四次揮手,造成很大的開(kāi)銷(xiāo)),為了提升系統(tǒng)訪問(wèn)數(shù)據(jù)庫(kù)的性能,可以事先創(chuàng)建若干連接置于連接池中,需要時(shí)直接從連接池獲取, 使用結(jié)束時(shí)歸還連接池而不必關(guān)閉連接,從而避免頻繁創(chuàng)建和釋放連接所造成的開(kāi)銷(xiāo),這是典型的用空間換時(shí)間的策略(浪費(fèi)了空間存儲(chǔ)連接,但節(jié)省了創(chuàng)建和釋放連接的時(shí)間)。?池化技術(shù)在Java 開(kāi)發(fā)中是很常見(jiàn)的,在使用線程時(shí)創(chuàng)建線程池的道理與此相同。基于Java 的開(kāi)源數(shù)據(jù)庫(kù)連接池主要有:?
C3P0、Proxool、DBCP、BoneCP、Druid等。其實(shí)大型網(wǎng)站性能優(yōu)化的一個(gè)關(guān)鍵就是使用緩存,,而緩 存和連接池非常類(lèi)似, 也是使用空間換時(shí)間的策略。可以將熱點(diǎn)數(shù)據(jù)置于緩存中,當(dāng)用戶(hù)查詢(xún)這些數(shù)據(jù)時(shí)可以直接從緩存中得到, 避免頻繁的訪問(wèn)數(shù)據(jù)庫(kù)造成大量的開(kāi)銷(xiāo)(現(xiàn)在主要用Redis實(shí)現(xiàn)緩存)
38. Java分層,Dao模式是什么?
DAO( Data Access Object)是一個(gè)為數(shù)據(jù)庫(kù)或其他持久化機(jī)制提供了抽象接口的對(duì)象,在不暴露底層持久化方案實(shí)現(xiàn)細(xì)節(jié)的前提下提供了各種數(shù)據(jù)訪問(wèn)操作。在實(shí)際的開(kāi)發(fā)中,應(yīng)該將所有對(duì)數(shù)據(jù)源的訪問(wèn)操作進(jìn)行抽象化后封裝在一個(gè)公共API 中。用程序設(shè)計(jì)語(yǔ)言來(lái)說(shuō), 就是建立一個(gè)接口,接口中定義了此應(yīng)用程序中將會(huì)用到的所有事務(wù)方法。在這個(gè)應(yīng)用程序中,當(dāng)需要和數(shù)據(jù)源進(jìn)行交互的時(shí)候則使用這個(gè)接口,并且編寫(xiě)一個(gè)單獨(dú)的類(lèi)來(lái)實(shí)現(xiàn)這個(gè)接口,在邏輯上該類(lèi)對(duì)應(yīng)一個(gè)特定的數(shù)據(jù)存儲(chǔ)。DAO 模式實(shí)際上包含了兩個(gè)模式,一是Data Accessor(數(shù)據(jù)訪問(wèn)器),二是Data Object(數(shù)據(jù)對(duì)象),前者要解決如何訪問(wèn)數(shù)據(jù)的問(wèn)題,而后者要解決的是如何用對(duì)象封裝數(shù)據(jù)。
39. 事務(wù)的特性ACID,事務(wù)隔離級(jí)別,事務(wù)的并發(fā)問(wèn)題【****】
事務(wù)具有ACID四個(gè)特性:
1)原子性(Atomicity):事務(wù)是一個(gè)不可分割的工作單位,事務(wù)中的操作要么都發(fā)生,要么都不發(fā)生?2)一致性(Consistency):事務(wù)在完成后數(shù)據(jù)的完整性必須保持一致 3)隔離性(Isolation):多個(gè)用戶(hù)并發(fā)訪問(wèn)數(shù)據(jù)庫(kù)時(shí),一個(gè)用戶(hù)的事務(wù)不能被其他用戶(hù)的事務(wù)所干擾,多個(gè)并發(fā)事務(wù)之間的數(shù)據(jù)要相互隔離 4)持久性(Durability):一個(gè)事務(wù)一旦被提交,它對(duì)數(shù)據(jù)庫(kù)中數(shù)據(jù)的改變應(yīng)該是永久性的,即使數(shù)據(jù)庫(kù)發(fā)生故障也不應(yīng)該對(duì)其有任何影響
如果整個(gè)事務(wù)執(zhí)行過(guò)程中,有任何一個(gè)地方出現(xiàn)異常/錯(cuò)誤,那么都會(huì)進(jìn)行事務(wù)回滾,回滾之后數(shù)據(jù)的狀態(tài)將和事務(wù)執(zhí)行之前完全一致。
事務(wù)的隔離級(jí)別:
數(shù)據(jù)庫(kù)為用戶(hù)提供了自動(dòng)鎖機(jī)制,只要用戶(hù)指定會(huì)話的事務(wù)隔離級(jí)別, 數(shù)據(jù)庫(kù)就會(huì)通過(guò)分析SQL語(yǔ)句然后為事務(wù)訪問(wèn)的資源加上合適的鎖。隔離級(jí)別是指若干個(gè)并發(fā)的事務(wù)之間的隔離程度。TransactionDefinition 接口中定義有五個(gè)表示隔離級(jí)別的常量(用于解決并發(fā)問(wèn)題)一般情況下使用中間兩種就行。
| TransactionDefinition 接口事務(wù)隔離級(jí)別 | 描述 |
|---|---|
| READ_UNCOMMITTED | 該隔離級(jí)別表示一個(gè)事務(wù)可以讀取另一個(gè)事務(wù)修改但還沒(méi)有提交的數(shù)據(jù)。該級(jí)別不能防止臟讀,不可重復(fù)讀和幻讀,因此很少使用該隔離級(jí)別。 |
| READ_COMMITTED | 系統(tǒng)默認(rèn)值,該隔離級(jí)別表示一個(gè)事務(wù)只能讀取另一個(gè)事務(wù)已經(jīng)提交的數(shù)據(jù)。可以防止臟讀,是大多數(shù)情況下的推薦值。 |
| REPEATABLE_READ | 該隔離級(jí)別表示一個(gè)事務(wù)在整個(gè)過(guò)程中可以多次重復(fù)執(zhí)行某個(gè)查詢(xún),并且每次返回的記錄都相同。該級(jí)別可以防止臟讀和不可重復(fù)讀。 |
| SERIALIZABLE | 所有的事務(wù)依次逐個(gè)執(zhí)行,事務(wù)之間就完全不可能產(chǎn)生干擾。該級(jí)別可以防止臟讀、不可重復(fù)讀以及幻讀。但是這將嚴(yán)重影響程序的性能。通常情況下也不會(huì)用到該級(jí)別。 |
| 隔離級(jí)別(√:允許出現(xiàn) ×:不允許出現(xiàn)) | 臟讀 | 不可重復(fù)讀 | 幻讀 | 第一類(lèi)丟失更新 | 第二類(lèi)丟失更新 |
|---|---|---|---|---|---|
| READ_UNCOMMITTED | √ | √ | √ | × | √ |
| READ_COMMITTED | × | √ | √ | × | √ |
| REPEATABLE_READ | × | × | √ | × | × |
| SERIALIZABLE | × | × | × | × | × |
事務(wù)隔離級(jí)別和數(shù)據(jù)訪問(wèn)的并發(fā)性是對(duì)立的,事務(wù)隔離級(jí)別越高并發(fā)性就越差。所以要根據(jù)具體的應(yīng)用來(lái)確定合適的事務(wù)隔離級(jí)別,這個(gè)地方?jīng)]有萬(wàn)能的原則。
事務(wù)的并發(fā)問(wèn)題
首先要知道,只有存在并發(fā)數(shù)據(jù)訪問(wèn)時(shí)才需要事務(wù)(提交/回滾就結(jié)束事務(wù)),當(dāng)多個(gè)事務(wù)訪問(wèn)同一數(shù)據(jù)時(shí),可能會(huì)存在5類(lèi)并發(fā)問(wèn)題,包括3 類(lèi)數(shù)據(jù)讀取問(wèn)題( 臟讀、不可重復(fù)讀和幻 讀和2類(lèi)數(shù)據(jù)更新問(wèn)題(第1 類(lèi)丟失更新和第2 類(lèi)丟失更新) 3類(lèi)數(shù)據(jù)讀取問(wèn)題
1)臟讀(Dirty Read):一個(gè)事務(wù)讀到了另一個(gè)事務(wù)的還沒(méi)有提交數(shù)據(jù). (比如A事務(wù)讀取B事務(wù)尚未提交的數(shù)據(jù)并在此基礎(chǔ)上操作,而B(niǎo)事務(wù)執(zhí)行回滾,那么A 讀取到的數(shù)據(jù)就是臟數(shù)據(jù)。)(很?chē)?yán)重的行為,必須處理,不然可能有很大的影響,比如轉(zhuǎn)賬事務(wù)) 2)不可重復(fù)讀(Unrepeatable Read):一個(gè)事務(wù)中多次讀到的數(shù)據(jù)不一致,一個(gè)事務(wù)讀到了另一個(gè)事務(wù)修改后的數(shù)據(jù)。(不可重復(fù)讀,保證數(shù)據(jù)修改后,不會(huì)出現(xiàn)兩次一樣的數(shù)據(jù)) 3)?幻讀(虛讀Phantom Read):一個(gè)事務(wù)讀到了另一個(gè)事務(wù)insert提交的數(shù)據(jù)。(比如事務(wù)A 重新執(zhí)行一個(gè)查詢(xún),返回一系列符合查詢(xún)條件的行,發(fā)現(xiàn)其中插入了被事務(wù)B 提交的行)(不可能出現(xiàn)在MySQL中,只會(huì)出現(xiàn)在Oracle中)
兩類(lèi)丟失更新問(wèn)題
1)第一類(lèi)丟失更新:??事務(wù)A撤銷(xiāo)時(shí), 把已經(jīng)提交的事務(wù)B的更新數(shù)據(jù)覆蓋了(比如 取款事務(wù)A開(kāi)啟事務(wù)查詢(xún)余額1000元,轉(zhuǎn)賬事務(wù)B開(kāi)啟事務(wù)轉(zhuǎn)賬100給A,A取出100,提交事務(wù)之后,再查詢(xún)余額還是1000元) 2)事務(wù)A覆蓋事務(wù) 已經(jīng)提交的數(shù)據(jù),造成事務(wù)B 所做的操作丟失(比如:取款事務(wù)A和轉(zhuǎn)賬B先后開(kāi)啟事務(wù),先后查詢(xún)余額都是1000元,取款事務(wù)A取出100,余額變成900,提交事務(wù),但是此時(shí)轉(zhuǎn)賬事務(wù)B存入100,將余額修改為1100元,提交事務(wù),然后再查詢(xún)帳戶(hù)余額就是1100,取款事務(wù)A的操作丟失)
JDBC如何進(jìn)行事務(wù)處理:
Connection提供了事務(wù)處理的方法,通過(guò)調(diào)用setAutoCommit(false)可以設(shè)置手動(dòng)提交事務(wù)。當(dāng)事務(wù)完成后用commit()顯式提交事務(wù);如果在事務(wù)處理過(guò)程中發(fā)生異常則通過(guò)rollback()進(jìn)行事務(wù)回滾。除此之外, 從JDBC 3.0 中還引入了Savepoint( 保存點(diǎn))的概念,允許通過(guò)代碼設(shè)置保存點(diǎn)并讓事務(wù)回滾到指定的保存點(diǎn)。
40. 正則表達(dá)式是什么,Java中如何支持正則表達(dá)式。
在編寫(xiě)處理字符串的程序時(shí),經(jīng)常會(huì)有查找符合某些復(fù)雜規(guī)則的字符串的需要。正則表達(dá)式就是用于描述這些規(guī)則的工具。換句話說(shuō), 正則表達(dá)式就是記錄文本規(guī)則的代碼。正則表達(dá)式就是在進(jìn)行字符串匹配和處理的時(shí)候最為強(qiáng)大的工具,絕大多數(shù)語(yǔ)言都提供了對(duì)正則表達(dá)式的支。??Java中的String類(lèi)提供了支持正則表達(dá)式操作的方法,包括:?
matches()、replaceAll()、replaceFirst()、split()?此外,Java 中可以用Pattern類(lèi)表示正則表達(dá)式對(duì)象, 它提供了豐富的API 進(jìn)行各種正則表達(dá)式操作。
41. 獲取Class對(duì)象的三種方法,通過(guò)反射創(chuàng)建對(duì)象的方法
獲取class對(duì)象的三種方法:
1)每個(gè)類(lèi)通過(guò)class屬性獲取。【
類(lèi)名.class】 2) 每個(gè)對(duì)象通過(guò)getClass()方法獲取。【對(duì)象.getClass()】 3)通過(guò)**Class.forName(“類(lèi)的完整名”)**獲取,需要捕獲異常。【Class.forName("類(lèi)的完整名")】第一種方式,
類(lèi)名.class?不會(huì)將類(lèi)加載到內(nèi)存,第三種Class.forName()會(huì)將類(lèi)加載到內(nèi)存,對(duì)象.getClass()(創(chuàng)建對(duì)象了)會(huì)加載到內(nèi)存中
通過(guò)反射創(chuàng)建對(duì)象的兩種方法:
1)通過(guò)類(lèi)對(duì)象(class)調(diào)用1newInstance()1方法,例如創(chuàng)建String對(duì)象:?
String.class.newInstance()?2)通過(guò)類(lèi)對(duì)象(class)的getConstructor()getDeclaredConstructor()方法獲得構(gòu)造器(Constructor)對(duì)象并調(diào)用其newInstance()方法創(chuàng)建對(duì)象,例如:·String.class.getConstructor(String.class).newInstance(“Hello”);·
42. 23種經(jīng)典設(shè)計(jì)模式
| 類(lèi)型 | 設(shè)計(jì)模式 |
|---|---|
| 創(chuàng)建型 | 工廠方法模式(FactoryMethod)、抽象工廠模式(AbstractFactory)、建造者模式(Builder)、原型模式(Prototype)、單例模式(Singleton) |
| 結(jié)構(gòu)型 | 適配器模式(Adapter)、橋接模式(Bridge)、組合模式(Composite)、裝飾器模式(Decorator)、門(mén)面模式(Facade)、享元模式(Flyweight)、代理模式(Proxy) |
| 行為型 | 解釋器模式(Interpreter)、模板方法模式(TemplateMethod)、責(zé)任鏈模式(ChainofResponsibility)、命令模式(Command)、迭代器模式(Iterator)、調(diào)解者模式(Mediator)、備忘錄模式(Memento)、觀察者模式(Observer)、狀態(tài)模式(State)、策略模式(Strategy)、訪問(wèn)者模式(Visitor) |
單例模式:指一個(gè)類(lèi)只有一個(gè)實(shí)例,且該類(lèi)能自行創(chuàng)建這個(gè)實(shí)例的一種模式。(懶漢式、餓漢式寫(xiě)法)?原型模式:用一個(gè)已經(jīng)創(chuàng)建的實(shí)例作為原型,通過(guò)復(fù)制該原型對(duì)象來(lái)創(chuàng)建一個(gè)和原型相同或相似的新對(duì)象。?策略模式:定義了一系列算法,并將每個(gè)算法封裝起來(lái),使它們可以相互替換,且算法的變化不會(huì)影響使用算法的客戶(hù)。策略模式屬于對(duì)象行為模式,它通過(guò)對(duì)算法進(jìn)行封裝,把使用算法的責(zé)任和算法的實(shí)現(xiàn)分割開(kāi)來(lái),并委派給不同的對(duì)象對(duì)這些算法進(jìn)行管理。?工廠模式:定義一個(gè)創(chuàng)建產(chǎn)品對(duì)象的工廠接口,將產(chǎn)品對(duì)象的實(shí)際創(chuàng)建工作推遲到具體子工廠類(lèi)當(dāng)中。工廠類(lèi)可以根據(jù)條件生成不同的子類(lèi)實(shí)例,這些子類(lèi)有一個(gè)公共的抽象父類(lèi)并且實(shí)現(xiàn)了相同的方法,但是這些方法針對(duì)不同的數(shù)據(jù)進(jìn)行了不同的操作(多態(tài)方法)。當(dāng)?shù)玫阶宇?lèi)的實(shí)例后,開(kāi)發(fā)人員可以調(diào)用基類(lèi)中的方法而不必考慮到底返回的是哪一個(gè)子類(lèi)的實(shí)例。?代理模式:給一個(gè)對(duì)象提供一個(gè)代理對(duì)象,并由代理對(duì)象控制原對(duì)象的引用。實(shí)際開(kāi)發(fā)中,按照使用目的的不同,代理可以分為:遠(yuǎn)程代理、虛擬代理、保護(hù)代理、Cache 代理、防火墻代理、同步化代理、智能引用代理。?適配器模式:把一個(gè)類(lèi)的接口變換成客戶(hù)端所期待的另一種接口,從而使原本因接口不匹配而無(wú)法在一起使用的類(lèi)能夠一起工作。?模板方法模式:提供一個(gè)抽象類(lèi),將部分邏輯以具體方法或構(gòu)造器的形式實(shí)現(xiàn),然后聲明一些抽象方法來(lái)迫使子類(lèi)實(shí)現(xiàn)剩余的邏輯。不同的子類(lèi)可以以不同的方式實(shí)現(xiàn)這些抽象方法(多態(tài)實(shí)現(xiàn)),從而實(shí)現(xiàn)不同的業(yè)務(wù)邏輯。?狀態(tài)模式:對(duì)有狀態(tài)的對(duì)象,把復(fù)雜的“判斷邏輯”提取到不同的狀態(tài)對(duì)象中,允許狀態(tài)對(duì)象在其內(nèi)部狀態(tài)發(fā)生改變時(shí)改變其行為。?裝飾者模式:指在不改變現(xiàn)有對(duì)象結(jié)構(gòu)的情況下,動(dòng)態(tài)地給該對(duì)象增加一些職責(zé)(即增加其額外功能)的模式,它屬于對(duì)象結(jié)構(gòu)型模式。了解其他的可以從這里面看:23種設(shè)計(jì)模式詳解
43. 面向?qū)ο蟮钠叽笤O(shè)計(jì)原則
| 原則 | 描述 |
|---|---|
| 開(kāi)閉原則(OCP) | 軟件實(shí)體應(yīng)當(dāng)對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉 |
| 里氏替換原則(LSP) | 闡述了有關(guān)繼承的一些原則(什么時(shí)候使用繼承),里氏替換原是繼承復(fù)用的基礎(chǔ),反映了基類(lèi)與子類(lèi)之間的關(guān)系,是對(duì)開(kāi)閉原則的補(bǔ)充,是對(duì)實(shí)現(xiàn)抽象化的具體步驟的規(guī)范。可以這么理解:子類(lèi)可以擴(kuò)展父類(lèi)的功能,但不能改變父類(lèi)原有的功能,也就是子類(lèi)繼承父類(lèi)時(shí),除添加新的方法完成新增功能外,盡量不要重寫(xiě)父類(lèi)的方法。 |
| 依賴(lài)倒置原則(DIP) | 核心思想是:要面向接口編程,不要面向?qū)崿F(xiàn)編程。(抽象層相對(duì)穩(wěn)定,因此以抽象為基礎(chǔ)搭建起來(lái)的架構(gòu)要比以細(xì)節(jié)(實(shí)現(xiàn)類(lèi))為基礎(chǔ)搭建起來(lái)的架構(gòu)要穩(wěn)定得多) |
| 單一職責(zé)原則(SRP) | 提出對(duì)象不應(yīng)該承擔(dān)太多職責(zé)。單一職責(zé)同樣也適用于方法。一個(gè)方法應(yīng)該盡可能做好一件事情。如果一個(gè)方法處理的事情太多,其顆粒度會(huì)變得很粗,不利于重用。 |
| 接口隔離原則(ISP) | 要求程序員盡量將臃腫龐大的接口拆分成更小的和更具體的接口,讓接口中只包含客戶(hù)感興趣的方法。接口要小而專(zhuān),絕不能大而全。 |
| 迪米特法則(LoD) | 又叫作最少知識(shí)原則(LKP),一個(gè)對(duì)象應(yīng)當(dāng)對(duì)其他對(duì)象有盡可能少的了解。如果兩個(gè)軟件實(shí)體無(wú)須直接通信,那么就不應(yīng)當(dāng)發(fā)生直接的相互調(diào)用,可以通過(guò)第三方轉(zhuǎn)發(fā)該調(diào)用。其目的是降低類(lèi)之間的耦合度,提高模塊的相對(duì)獨(dú)立性。 |
| 合成復(fù)用原則(CRP) | 叫組合/聚合復(fù)用原則(CARP)要求在軟件復(fù)用時(shí),要盡量先使用組合或者聚合等關(guān)聯(lián)關(guān)系來(lái)實(shí)現(xiàn),其次才考慮使用繼承關(guān)系來(lái)實(shí)現(xiàn)。 |
概述:開(kāi)閉原則是總綱,它告訴我們要對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉;里氏替換原則告訴我們不要破壞繼承體系;依賴(lài)倒置原則告訴我們要面向接口編程;單一職責(zé)原則告訴我們實(shí)現(xiàn)類(lèi)要職責(zé)單一;接口隔離原則告訴我們?cè)谠O(shè)計(jì)接口的時(shí)候要精簡(jiǎn)單一;迪米特法則告訴我們要降低耦合度;合成復(fù)用原則告訴我們要優(yōu)先使用組合或者聚合關(guān)系復(fù)用,少用繼承關(guān)系復(fù)用。
44. UML概念,常用UML圖
UML是統(tǒng)一建模語(yǔ)言( Unified Modeling Language)的縮寫(xiě),它發(fā)表于1997年, 綜合了當(dāng)時(shí)已經(jīng)存在的面向?qū)ο蟮慕UZ(yǔ)言、方法和過(guò)程, 是一個(gè)支持模型化和軟件系統(tǒng)開(kāi)發(fā)的圖形化語(yǔ)言,為軟件開(kāi)發(fā)的所有階段提供模型化和可視化支持。使用UML 可以幫助溝通與交流,輔助應(yīng)用設(shè)計(jì)和文檔的生成,還能夠闡釋系統(tǒng)的結(jié)構(gòu)和行為。?常用UML圖?用例圖(use case diagram)、類(lèi)圖(class diagram)、時(shí)序圖(sequencediagram)、協(xié)作圖(collaboration diagram)、狀態(tài)圖(statechart diagram)、活動(dòng)圖(activity diagram)、構(gòu)件圖(component diagram)、部署圖(deploymentdiagram) 用例圖:用來(lái)捕獲需求,描述系統(tǒng)的功能,通過(guò)該圖可以迅速的了解系統(tǒng)的功能模塊及其關(guān)系 類(lèi)圖:描述類(lèi)以及類(lèi)與類(lèi)之間的關(guān)系,通過(guò)該圖可以快速了解系統(tǒng) 時(shí)序圖:描述執(zhí)行特定任務(wù)時(shí)對(duì)象之間的交互關(guān)系以及執(zhí)行順序, 通過(guò)該圖可以了解對(duì)象能接收的消息也就是說(shuō)對(duì)象能夠向外界提供的服務(wù)。
45. HashMap實(shí)現(xiàn)原理
JDK1.8中,HashMap采用**
位桶+鏈表+紅黑樹(shù)**實(shí)現(xiàn),當(dāng)鏈表長(zhǎng)度超過(guò)閾值(8)時(shí),將鏈表轉(zhuǎn)換為紅黑樹(shù),這樣大大減少了查找時(shí)間。HashCode是jdk根據(jù)對(duì)象的地址或字符串或者數(shù)字利用hash算法計(jì)算出的int類(lèi)型的數(shù)值。HashMap實(shí)現(xiàn)原理:首先有一個(gè)每個(gè)元素都是鏈表的數(shù)組,當(dāng)添加一個(gè)元素(key-value)時(shí),就首先計(jì)算元素key的hash值,以此確定插入數(shù)組中的位置,但是可能存在同一hash值的元素已經(jīng)被放在數(shù)組同一位置了,這時(shí)就添加到同一hash值的元素的后面,他們?cè)跀?shù)組的同一位置,但是形成了鏈表,同一各鏈表上的Hash值是相同的,所以說(shuō)數(shù)組存放的是鏈表。而當(dāng)鏈表長(zhǎng)度太長(zhǎng)時(shí),鏈表就轉(zhuǎn)換為紅黑樹(shù),這樣大大提高了查找的效率。在jdk8中,HashMap處理“碰撞”增加了紅黑樹(shù)這種數(shù)據(jù)結(jié)構(gòu),當(dāng)碰撞結(jié)點(diǎn)較少時(shí),采用鏈表存儲(chǔ),當(dāng)較大時(shí)(>8個(gè)),采用紅黑樹(shù)(特點(diǎn)是查詢(xún)時(shí)間是O(logn))存儲(chǔ)(有一個(gè)閥值控制,大于閥值(8個(gè)),將鏈表存儲(chǔ)轉(zhuǎn)換成紅黑樹(shù)存儲(chǔ))
紅黑樹(shù)是一種自平衡二叉查找樹(shù)
紅黑樹(shù)是每個(gè)節(jié)點(diǎn)都帶有顏色屬性的二叉查找樹(shù),顏色或紅色或黑色。對(duì)于任何有效的紅黑樹(shù)都有以下要求:1)節(jié)點(diǎn)是紅色或黑色。2)根節(jié)點(diǎn)是黑色。3)每個(gè)葉節(jié)點(diǎn)是黑色的。4)每個(gè)紅色節(jié)點(diǎn)的兩個(gè)子節(jié)點(diǎn)都是黑色。(從每個(gè)葉子到根的所有路徑上不能有兩個(gè)連續(xù)的紅色節(jié)點(diǎn)) 5從任一節(jié)點(diǎn)到其每個(gè)葉子的所有路徑都包含相同數(shù)目的黑色節(jié)點(diǎn)。
46. 同步和異步
如果系統(tǒng)中存在臨界資源(資源數(shù)量少于競(jìng)爭(zhēng)資源的線程數(shù)量的資源), 例如正在寫(xiě)的數(shù)據(jù)以后可能被另一個(gè)線程讀到,或者正在讀的數(shù)據(jù)可能已經(jīng)被另一個(gè)線程寫(xiě)過(guò)了,那么這些數(shù)據(jù)就必須進(jìn)行同步存取(數(shù)據(jù)庫(kù)操作中的排他鎖就是最好的例子)。當(dāng)應(yīng)用程序在對(duì)象上調(diào)用了一個(gè)需要花費(fèi)很長(zhǎng)時(shí)間來(lái)執(zhí)行的方法,并且不希望讓程序等待方法的返回時(shí), 就應(yīng)該使用異步編程,在很多情況下采用異步途徑往往更有效率。事實(shí)上,所謂的同步就是指阻塞式操作, 而異步就是非阻塞式操作。(同步必須等待返回結(jié)果才能繼續(xù)執(zhí)行,異步就是瀏覽器發(fā)送請(qǐng)求,不管服務(wù)器是否返回結(jié)果,都可以繼續(xù)執(zhí)行)
47. Servlet的生命周期
Web容器加載Servlet并將其實(shí)例化后,Servlet生命周期開(kāi)始,容器運(yùn)行其init()方法進(jìn)行Servlet的初始化;請(qǐng)求到達(dá)時(shí)調(diào)用Servlet的service方法,service方法會(huì)調(diào)用與請(qǐng)求對(duì)應(yīng)的doGet或doPost等方法;當(dāng)服務(wù)器關(guān)閉會(huì)項(xiàng)目被卸載時(shí)服務(wù)器會(huì)將Servlet實(shí)例銷(xiāo)毀,此時(shí)會(huì)調(diào)用Servlet的destroy方法。
48. get和post請(qǐng)求的區(qū)別
1)get請(qǐng)求用來(lái)從服務(wù)器上獲得資源,而post是用來(lái)向服務(wù)器提交數(shù)據(jù) 2)get將表單中數(shù)據(jù)按照name=value的形式,添加到action 所指向的URL 后面,并且兩者使用“?”連接,而各個(gè)變量之間使用“&”連接;post是將表單中的數(shù)據(jù)放在HTML頭部(header),傳遞到action所指向URL 3)get傳輸?shù)臄?shù)據(jù)要受到URL長(zhǎng)度限制(1024字節(jié));而post可以傳輸大量的數(shù)據(jù),上傳文件只能使用post方式 4)使用get時(shí)參數(shù)會(huì)顯示在地址欄上,如果這些數(shù)據(jù)不是敏感數(shù)據(jù),那么可以使用get;對(duì)于敏感數(shù)據(jù)還是應(yīng)用使用post
49. HttpServlet容器響應(yīng)Web客戶(hù)請(qǐng)求流程?
1)Web客戶(hù)向Servlet容器發(fā)出Http請(qǐng)求;2)Servlet容器解析Web客戶(hù)的Http請(qǐng)求;3)Servlet容器創(chuàng)建一個(gè)HttpRequest對(duì)象,在這個(gè)對(duì)象中封裝Http請(qǐng)求信息;4)Servlet容器創(chuàng)建一個(gè)HttpResponse對(duì)象;5)Servlet容器調(diào)用HttpServlet的service方法,這個(gè)方法中會(huì)根據(jù)request的Method來(lái)判斷具體是執(zhí)行doGet還是doPost,把HttpRequest和HttpResponse對(duì)象作為service方法的參數(shù)傳給HttpServlet對(duì)象;6)HttpServlet調(diào)用HttpRequest的有關(guān)方法,獲取HTTP請(qǐng)求信息;7)HttpServlet調(diào)用HttpResponse的有關(guān)方法,生成響應(yīng)數(shù)據(jù);8)Servlet容器把HttpServlet的響應(yīng)結(jié)果傳給Web客戶(hù)
50. 什么是Callable 和Future?
Callable 接口類(lèi)似于Runnable,但是Runnable 不會(huì)返回結(jié)果,并且無(wú)法拋出返回結(jié)果的異常,而Callable 功能更強(qiáng)大一些,被線程執(zhí)行后,可以返回值,這個(gè)返回值可以被Future 拿到,也就是說(shuō),F(xiàn)uture 可以拿到異步執(zhí)行任務(wù)的返回值。可以認(rèn)為是帶有回調(diào)的Runnable。Future 接口表示異步任務(wù),是還沒(méi)有完成的任務(wù)給出的未來(lái)結(jié)果。所以說(shuō)Callable用于產(chǎn)生結(jié)果,F(xiàn)uture 用于獲取結(jié)果。
JAVA一些題目和Java的一些方法
Math.round(11.5) 等于多少?Math.round(-11.5)等于多少?
Math.round(11.5)的返回值是12, Math.round(-11.5)的返回值是-11。四舍五 入的原理是在參數(shù)上加0.5 然后進(jìn)行下取整。
short s1 = 1; s1 = s1 + 1;有錯(cuò)嗎??short s1 = 1; s1 += 1;有錯(cuò)嗎?
1)
short s1 = 1; s1 = s1 + 1;???編譯錯(cuò)誤。由于1 是int 類(lèi)型,因此s1+1運(yùn)算結(jié)果也是int型, 需要強(qiáng)制轉(zhuǎn)換類(lèi)型才能賦值給short 型。2)short s1 = 1; s1 += 1;???編譯正確,因?yàn)閟1+= 1;相當(dāng)于s1 = (short)(s1 + 1);其中有隱含的強(qiáng)制類(lèi)型轉(zhuǎn)換(自增長(zhǎng)自動(dòng)轉(zhuǎn)換,除非越界)
用最有效率的方法計(jì)算2乘以8(運(yùn)算符 <<和>>的概念)
2 << 3(左移3 位相當(dāng)于乘以2 的3 次方,右移3 位相當(dāng)于除以2 的3 次方。關(guān)于運(yùn)算符
<<和>>?x >> n就是?先將x轉(zhuǎn)成二進(jìn)制,不讀后面的n位?x << n就是?先將x轉(zhuǎn)成二進(jìn)制,往二進(jìn)制數(shù)據(jù)后面加n個(gè)0案例:
int x = 16;?x >> 1?輸出x=8(先將x轉(zhuǎn)成二進(jìn)制 10000, 不讀最后一位0, 輸出 1000, 轉(zhuǎn)為10進(jìn)制即為8)?x << 1?輸出x=32(先將x轉(zhuǎn)成二進(jìn)制 10000,,往最后再讀取一位0( 或根據(jù)是否已經(jīng)有移位), 輸出 100000, 轉(zhuǎn)為10進(jìn)制即為32)
數(shù)組有沒(méi)有l(wèi)ength()方法?String 有沒(méi)有l(wèi)ength()方法?
數(shù)組沒(méi)有l(wèi)ength()方法,只有有l(wèi)ength 的屬性。String 有l(wèi)ength()方法。但是在JavaScript中 ,獲得字符串的長(zhǎng)度是通過(guò)length 屬性得到的(非常容易和Java搞混)
當(dāng)一個(gè)對(duì)象被當(dāng)作參數(shù)傳遞到一個(gè)方法后,此方法可改變這個(gè)對(duì)象的屬性,并可返回變化后的結(jié)果,那么這里到底是值傳遞還是引用傳遞?
是值傳遞。Java 語(yǔ)言的方法調(diào)用只支持參數(shù)的值傳遞。當(dāng)一個(gè)對(duì)象實(shí)例作為一個(gè)參數(shù)被傳遞到方法中時(shí),參數(shù)的值就是對(duì)該對(duì)象的引用。對(duì)象的屬性可以在被調(diào) 用過(guò)程中被改變,但對(duì)對(duì)象引用的改變是不會(huì)影響到調(diào)用者的。C++和C#中可以 通過(guò)傳引用或傳輸出參數(shù)來(lái)改變傳入的參數(shù)的值。在C#中可以編寫(xiě)如下所示的代 碼, 但是在Java 中卻做不到。
String str = new String(“xyz”);創(chuàng)建了幾個(gè)字符串對(duì)象?
兩個(gè)對(duì)象,一個(gè)是靜態(tài)區(qū)(方法區(qū)常量池)的”xyz“字符串,一個(gè)是用new 創(chuàng)建在堆上的對(duì)象str。
Java 中有幾種類(lèi)型的流,它們的關(guān)系
Java中主要是字節(jié)流和字符流。字節(jié)流繼承于InputStream、OutputStream,字符流繼承于Reader、Writer。在java.io 包中還有許多其他的流,主要是為了提高性能和使用方便 關(guān)于Java 的I/O 需要注意的有兩點(diǎn):
1)兩種對(duì)稱(chēng)性( 輸入和輸出的對(duì)稱(chēng)性,字節(jié)和字符的對(duì)稱(chēng)性) 2)兩種設(shè)計(jì)模式(適配器模式和裝潢模式)。
編程實(shí)現(xiàn)文件拷貝的兩種方式
public?static?void?fileCopy(String?source,?String?target)?throws
IOException?{
?try?(InputStream?in?=?new?FileInputStream(source))?{
??try?(OutputStream?out?=?new?FileOutputStream(target))?{
???byte[]?buffer?=?new?byte[4096];
???int?bytesToRead;
???while((bytesToRead?=?in.read(buffer))?!=?-1)?{
????out.write(buffer,?0,?bytesToRead);
???}
??}
?}
}
123456789101112
NIO方式
public?static?void?fileCopyNIO(String?source,?String?target)?throws
IOException?{
?try?(FileInputStream?in?=?new?FileInputStream(source))?{
??try?(FileOutputStream?out?=?new?FileOutputStream(target))?{
???FileChannel?inChannel?=?in.getChannel();
???FileChannel?outChannel?=?out.getChannel();
???ByteBuffer?buffer?=?ByteBuffer.allocate(4096);
???while(inChannel.read(buffer)?!=?-1)?{
????buffer.flip();
????outChannel.write(buffer);
????buffer.clear();
???}
??}
?}
}
12345678910111213141516
寫(xiě)一個(gè)方法,輸入一個(gè)文件名和一個(gè)字符串,統(tǒng)計(jì)這個(gè)字符串在這個(gè)文件中出現(xiàn)的次數(shù)。
//統(tǒng)計(jì)這個(gè)字符串在這個(gè)文件中出現(xiàn)的次數(shù)
public?static?int?countWordInFile(String?filename,?String?word)?{
?int?counter?=?0;?//統(tǒng)計(jì)數(shù)
?try?(FileReader?fr?=?new?FileReader(filename))?{
??try?(BufferedReader?br?=?new?BufferedReader(fr))?{
???String?line?=?null;
???while?((line?=?br.readLine())?!=?null)?{
????int?index?=?-1;
????while?(line.length()?>=?word.length()?&&?(index?=?line.indexOf(word))?>=?0)?{
?????counter++;
?????line?=?line.substring(index?+?word.length());
????}
???}
??}
?}?catch?(Exception?ex)?{
??ex.printStackTrace();
?}
?return?counter;
}
12345678910111213141516171819
如何用Java 代碼列出一個(gè)目錄下所有的文件?1)只列出當(dāng)前文件夾下的文件
public?static?void?main(String[]?args)?{
?File?f?=?new?File("/Users/Downloads");
?for(File?temp?:?f.listFiles())?{
??if(temp.isFile())?{
???System.out.println(temp.getName());
??}
?}
}
12345678
2)對(duì)于當(dāng)前文件夾下的文件夾繼續(xù)展開(kāi)顯示所有文件
private?static?void?queryDirectory(File?f,?int?level)?{
?if(f.isDirectory())?{
??for(File?temp?:?f.listFiles())?{
???queryDirectory(temp,?level?+?1);?//遞歸
??}
?}else?{
??for(int?i?=?0;?i?1;?i++)?{
???System.out.print("\t");?//\t四個(gè)空格
??}
??System.out.println(f.getName());??//輸出文件名
?}
}
public?static?void?main(String[]?args)?{
?queryDirectory(new?File("/Users/Downloads"),0);
}
12345678910111213141516
JDK1.7之后可以使用NIO.2的API實(shí)現(xiàn)
public?static?void?main(String[]?args)?throws?IOException?{
?Path?initPath?=?Paths.get("/Users/Downloads");
?Files.walkFileTree(initPath,?new?SimpleFileVisitor()?{
??@Override
??public?FileVisitResult?visitFile(Path?file,?BasicFileAttributesattrs)?throws?IOException?{
???System.out.println(file.getFileName().toString());
???return?FileVisitResult.CONTINUE;
??}
?});
}
12345678910
TCP編程通過(guò)Socket套接字編程實(shí)現(xiàn)服務(wù)端向客戶(hù)端發(fā)送信息 服務(wù)端:
import?java.io.IOException;
import?java.io.PrintStream;
import?java.net.ServerSocket;
import?java.net.Socket;
import?java.util.Scanner;
//socket,TCP編程,服務(wù)端程序
public?class?Server?{
????public?static?void?serverInfo(){
????????ServerSocket?server?=?null;
????????Socket?client?=?null;
????????PrintStream?out?=?null;
????????try?{
????????????//在服務(wù)器8000端口等待客戶(hù)連接
????????????server?=?new?ServerSocket(8000);
????????????System.out.println("服務(wù)器正在等待客戶(hù)端的連接......");
????????????//程序阻塞,等待客戶(hù)端的連接
????????????client?=?server.accept();
????????????System.out.println("連接客戶(hù)端成功!!");
????????????//實(shí)例化打印流對(duì)象,用于向客戶(hù)端發(fā)送輸出信息
????????????out?=?new?PrintStream(client.getOutputStream());
????????????System.out.println("請(qǐng)輸入您要向客戶(hù)端發(fā)送的信息:");
????????????Scanner?scan?=?new?Scanner(System.in);?//獲取輸入流
????????????//準(zhǔn)備向客戶(hù)端發(fā)送的信息
????????????String?info?=?"服務(wù)端向客戶(hù)端發(fā)送信息:"?+?scan.nextLine();
????????????//輸出信息
????????????out.println(info);
???scan.close();???//關(guān)閉輸入流
????????????out.close();????//關(guān)閉輸出打印流
????????????client.close();?//關(guān)閉客戶(hù)端
????????????server.close();?//關(guān)閉服務(wù)器端的練級(jí)
????????}?catch?(IOException?e)?{
????????????e.printStackTrace();
????????}
????}
????public?static?void?main(String[]?args)?{
????????serverInfo();//開(kāi)啟服務(wù)端
????}
}
12345678910111213141516171819202122232425262728293031323334353637383940414243444546
客戶(hù)端:
import?java.io.BufferedReader;
import?java.io.IOException;
import?java.io.InputStreamReader;
import?java.net.Socket;
//socket編程,客戶(hù)端,要與服務(wù)端的端口號(hào)一致
public?class?Client?{
????public?static?void?clientInfo(){
????????//聲明socket對(duì)象
????????Socket?client?=?null;
????????try?{
????????????//實(shí)例化socket對(duì)象,指定連接的主機(jī)名稱(chēng)和端口號(hào)
????????????client?=?new?Socket("localhost",8000);
????????????System.out.println("客戶(hù)端連接成功");
????????????//聲明緩存字符流,用來(lái)接收信息
????????????BufferedReader?buf?=?new?BufferedReader(new?InputStreamReader(client.getInputStream()));
????????????//讀取信息
????????????String?info?=?buf.readLine();
????????????//輸出讀取的信息
???????????System.out.println("客戶(hù)端收到服務(wù)器("+?client.getInetAddress()?+")端發(fā)來(lái)的信息:【"?+?info?+?"】");
????????????client.close();
????????????buf.close();
????????}?catch?(IOException?e)?{
????????????e.printStackTrace();
????????}
????}
????public?static?void?main(String[]?args)?{
????????clientInfo();
????}
}
123456789101112131415161718192021222324252627282930313233343536
運(yùn)行:
1、 開(kāi)啟服務(wù)端Server
Server:服務(wù)器正在等待客戶(hù)端的連接…
2.、開(kāi)啟客戶(hù)端Client
Server:(輸出內(nèi)容) ??服務(wù)器正在等待客戶(hù)端的連接… ??連接客戶(hù)端成功!!??請(qǐng)輸入您要向客戶(hù)端發(fā)送的信息:Client:(輸出內(nèi)容) ??客戶(hù)端連接成功
3、Server輸入內(nèi)容HelloWorld
客戶(hù)端Client接收到信息:??客戶(hù)端收到服務(wù)器(localhost/127.0.0.1)端發(fā)來(lái)的信息:【服務(wù)端向客戶(hù)端發(fā)送信息:Hello World!!!】
餓漢式單例模式和懶漢式單例模式
1) 將構(gòu)造器私有,不允許外界通過(guò)構(gòu)造器 創(chuàng)建對(duì)象;2) 通過(guò)公開(kāi)的靜態(tài)方法向外界返回類(lèi)的唯一實(shí)例
餓漢式單例模式:
//餓漢式??不延遲實(shí)例化的做法,直接靜態(tài)實(shí)例化創(chuàng)建?保證線程安全
public?class?HungarySingleton?{
????private?static?HungarySingleton?instance=new?HungarySingleton();
????private?HungarySingleton(){???
????}
????public?static?HungarySingleton?getInstance(){
????????return?instance;//返回唯一實(shí)例
????}
}
123456789
懶漢式單例模式:
public?class?LazySingleton?{
?private?static?LazySingleton?instance?=?null;
?private?Singleton()?{}
?public?static?synchronized?LazySingleton?getInstance(){
??if?(instance?==?null)?instance?=?new?LazySingleton?();
??return?instance;
?}
}
12345678
volatile關(guān)鍵字,DCL機(jī)制實(shí)現(xiàn)懶漢式,提高性能
//懶漢式?DCL機(jī)制
//T1先從主存拷貝數(shù)據(jù)到自己的線程內(nèi)存,進(jìn)行處理數(shù)據(jù),處理完數(shù)據(jù)更新到主存
//T2從主存拷貝數(shù)據(jù)到自己的線程內(nèi)存
public?class?LazySingleton?{
????private?static?volatile?LazySingleton?instance?=?null;
????private?LazySingleton(){
????}
????//unlock?happen-before?lock?語(yǔ)義
????//T1,T2?---可見(jiàn)性??T1?happen-before?T2
????/**
?????*?A->B,B->C===A->C
?????*?T2調(diào)用getInstance()和調(diào)用getName()
?????*?T1調(diào)用getInstance()
?????*?a++
?????*?T1?->T2
?????*?構(gòu)造器內(nèi)存操作->T1?->T2
?????*/
????public?static??LazySingleton?getInstance(){
??????//T2
???????if(instance==null){
????????????//T1??同步化?所有為null的只能進(jìn)入一個(gè),等待執(zhí)行
????????????synchronized(LazySingleton.class){
????????????????if(instance==null){
????????????????????instance=new?LazySingleton();
????????????????????//JSR-133
????????????????????//1.開(kāi)辟空間
????????????????????//2.初始化對(duì)象
????????????????????//3.把地址給isntance變量---CPU
????????????????}
????????????}
????????}
????????return?instance;
????}
}
1234567891011121314151617181920212223242526272829303132333435
能將int強(qiáng)制轉(zhuǎn)換為byte類(lèi)型的變量嗎?如果該值 大于byte 類(lèi)型的范圍,將會(huì)出現(xiàn)什么現(xiàn)象?
可以做強(qiáng)制轉(zhuǎn)換,但是Java中int 是32位(4byte)的,而byte 是8位(1byte)的,所以,如果強(qiáng)制轉(zhuǎn)化是,int 類(lèi)型的高24 位將會(huì)被丟棄,byte 類(lèi)型的范圍是從-128 到128。
哪個(gè)類(lèi)包含clone 方法?是Cloneable 還是Object?
java.lang.Cloneable 是一個(gè)標(biāo)示性接口,不包含任何方法, clone 方法在object 類(lèi)中定義。并且clone() 方法是一個(gè)本地方法,這意味著它是由c 或c++ 或其他本地語(yǔ)言實(shí)現(xiàn)。
Java 中++ (- -)操作符是線程安全的嗎?
不是線程安全的操作。它涉及到多個(gè)指令,如讀取變量值,增加/減少,然后存儲(chǔ)回內(nèi)存,這個(gè)過(guò)程可能會(huì)出現(xiàn)多個(gè)線程交差。
a = a + b?與a += b?的區(qū)別
+=隱式的將加操作的結(jié)果類(lèi)型強(qiáng)制轉(zhuǎn)換為持有結(jié)果的類(lèi)型(比如a += b?會(huì)將運(yùn)算結(jié)果a+b轉(zhuǎn)換為需要a對(duì)應(yīng)的類(lèi)型)。如果兩這個(gè)整型相加,如byte、short 或者int,首先會(huì)將它們提升到int 類(lèi)型,然后在執(zhí)行加法操作,最后再轉(zhuǎn)換為接收的數(shù)據(jù)的類(lèi)型。而?a = a + b不會(huì)進(jìn)行類(lèi)型轉(zhuǎn)換,如果越界就拋出異常
//?byte取值?-128到128,超過(guò)就越界要轉(zhuǎn)換類(lèi)型
byte?a?=?120;
byte?b?=?120;
a?=?a?+?b;?//編譯報(bào)錯(cuò):cannot convert from int to byte,因?yàn)椴粫?huì)將結(jié)果(a+b)自動(dòng)強(qiáng)制類(lèi)型轉(zhuǎn)換
a?+=?b;??//編譯正確,b?=?-16
//隱式的將加操作的結(jié)果類(lèi)型強(qiáng)制轉(zhuǎn)換為持有結(jié)果的類(lèi)型(a+b的時(shí)候隱式轉(zhuǎn)換為int相加
//結(jié)果強(qiáng)制轉(zhuǎn)換為接收結(jié)果a的類(lèi)型byte,a+b=240(int類(lèi)型)轉(zhuǎn)換為a的類(lèi)型byte=-16)
1234567
int 和Integer 哪個(gè)會(huì)占用更多的內(nèi)存
Integer包裝類(lèi)會(huì)占用更多的內(nèi)存。Integer 是一個(gè)對(duì)象,需要存儲(chǔ)對(duì)象的元數(shù)據(jù)。但是int 是一個(gè)原始類(lèi)型的數(shù)據(jù),所以占用的空間更少
“a==b”和”a.equals(b)”有什么區(qū)別?
如果a和b都是對(duì)象,則a==b 是比較兩個(gè)對(duì)象的引用,只有當(dāng)a和b指向的是堆中的同一個(gè)對(duì)象才會(huì)返回true,a.equals(b) 是進(jìn)行邏輯比較,所以通常需要重寫(xiě)該方法來(lái)提供邏輯一致性的比較。例如,String 類(lèi)重寫(xiě)equals() 方法, 所以可以用于兩個(gè)不同對(duì)象,但是包含的字母相同的比較
a.hashCode() 有什么用?與a.equals(b) 有什么關(guān)系?
hashCode()方法是相應(yīng)對(duì)象整型的hash值。它常用于基于hash 的集合類(lèi),如Hashtable、HashMap、LinkedHashMap 等等。它與equals() 方法關(guān)系特別緊密。根據(jù)Java 規(guī)范,兩個(gè)使用equal() 方法來(lái)判斷相等的對(duì)象,必須具有相同的hash code。
Java集合框架
poll()方法和remove()方法的區(qū)別
poll() 和remove() 都是從隊(duì)列中取出一個(gè)元素,但是poll() 在獲取元素失敗的時(shí)候會(huì)返回空,但是remove() 失敗的時(shí)候會(huì)拋出異常。
LinkedHashMap 和PriorityQueue 的區(qū)別
PriorityQueue保證最高或者最低優(yōu)先級(jí)的的元素總是在隊(duì)列頭部,但是LinkedHashMap 維持的順序是元素插入的順序。當(dāng)遍歷一個(gè)PriorityQueue時(shí),沒(méi)有任何順序保證,但是LinkedHashMap 課保證遍歷順序是元素插入的順序。
ArrayList 與LinkedList 的主要區(qū)別
ArrrayList 底層的數(shù)據(jù)結(jié)構(gòu)是數(shù)組,支持隨機(jī)訪問(wèn),而 LinkedList的底層數(shù)據(jù)結(jié)構(gòu)是鏈表(雙向鏈表),不支持隨機(jī)訪問(wèn)(但是優(yōu)化了更新操作)。使用下標(biāo)訪問(wèn)一個(gè)元素,ArrayList 的時(shí)間復(fù)雜度是O(1),而LinkedList 是O(n)。
Hashtable 與HashMap 有什么不同
1)Hashtable 是JDK 1 遺留下來(lái)的類(lèi),而HashMap 是后來(lái)增加的。2)Hashtable 是同步的,比較慢,但HashMap 沒(méi)有同步策略,所以會(huì)更快。3)Hashtable不允許有個(gè)空的key,但是HashMap允許出現(xiàn)一個(gè)null key。
Java中的HashSet,內(nèi)部是如何工作
HashSet 的內(nèi)部采用HashMap 來(lái)實(shí)現(xiàn)。由于Map 需要key 和value,所以所有key 的都有一個(gè)默認(rèn)value。類(lèi)似于HashMap, HashSet 不允許重復(fù)的key,只允許有一個(gè)null key,意思就是HashSet 中只允許存儲(chǔ)一個(gè)null 對(duì)象。
ArrayList 和HashMap 的默認(rèn)大小
ArrayList 的默認(rèn)大小是10 個(gè)元素, HashMap的默認(rèn)大小是16 個(gè)元素(必須是2的冪次)
兩個(gè)相同的對(duì)象的hash code一定相同,兩個(gè)對(duì)象有相同的hash Code,但兩個(gè)對(duì)象不一定相同。
兩個(gè)不相等的對(duì)象可能會(huì)有相同的hashcode 值, 這就是為什么在hashmap 中會(huì)有沖突。相等hashcode 值的規(guī)定只是說(shuō)如果兩個(gè)對(duì)象相等, 必須有相同的hashcode 值, 但是沒(méi)有關(guān)于不相等對(duì)象的任何規(guī)定
Java中的TreeMap是使用紅黑樹(shù)實(shí)現(xiàn)的。
本文作者:strive_day本文鏈接:https://blog.csdn.net/qq_40542534/article/details/109241330
4.中間件等
更多信息請(qǐng)關(guān)注公眾號(hào):「軟件老王」,關(guān)注不迷路,軟件老王和他的IT朋友們,分享一些他們的技術(shù)見(jiàn)解和生活故事。



JVM的類(lèi)加載原理圖
JVM 中類(lèi)的裝載是由類(lèi)加載器(?