Java常见易错问题记录
1.for循環的流程問題:
例題:
package demo;public class testFor {static boolean foo(char c) {System.out.print(c);return true;}public static void main(String[] args) {int i = 0;for (foo('A'); foo('B') && (i < 2); foo('C')) {i++;foo('D');}} }問:輸出結果是多少?
for(初始化條件;判斷條件;每次循環完畢執行)
第一次循環:初始化條件foo(‘A’); 輸出A
判斷條件foo(‘B’) && (i < 2),滿足判斷條件:輸出B
進入循環體:輸出D
這次循環結束:輸出C
所以第一次循環輸出了:ABDC 此時i=1
第二次循環:
不用執行初始化條件,直接到判斷條件,滿足判斷條件,輸出B,
進入循環體:輸出D
這次循環結束:輸出C
第二次循環結束,輸出了BDC
此時i=2
這時不滿足foo(‘B’) && (i < 2)判斷條件,輸出B
循環結束,不進入循環體,不執行 foo(‘C’)
綜合起來就輸出了ABDCBDCB
2.移位運算
移位運算提高了運算效率
左移:<<
右移:>>
無符號右移:>>>
常有用左移代替乘法運算提高效率的問題。
3.String StringBuilder StringBuffer
1、String代表不可變字符序列。每次對String類型進行變更,都會生成一個新的對象,然后將指針指向新的 String 對象。這樣會影響性能,效率低下。
2、StringBuilder代表可變的字符序列。線程不安全,效率高。
3、StringBuffer代表可變字符序列,線程安全,效率比StringBuilder低。
4.短路運算(&& ||)
&&和&
&&和&都可以表示邏輯與,兩邊都為true ,運算結果為true
&&只要是第一個條件不成立為false,就不會再去判斷第二個條件,最終結果直接為false,而&會運行第二個條件
||和|表示邏輯或。
兩個判斷條件其中有一個成立最終的結果就是true,區別和邏輯與類似,|會執行兩邊的條件。
5.基本數據類型的長度
1byte (字節)= 8bit(位)
基本數據類型的字節數:
byte:1字節
short:2字節
int:4字節
long:8字節
float:4字節
double:8字節
char:2字節
boolean:1/8字節
范圍:
int short byte long 都為整數類型,范圍是 - 2^(n-1) 至2^(n-1)-1 (n為位數)
float和double為浮點數類型,因為計算機只認識0和1,要表示小數的話,需要有某種規范,
例如:
float占32bit 其中1bit表示符號,8bit表示指數,23bit表示尾數
double占64bit 其中1bit表示符號,11bit表示指數,52bit表示尾數
6.hashcode和equals方法
equal()相等的兩個對象他們的hashCode()肯定相等,用equal()是絕對可靠的。
hashCode()相等的兩個對象他們的equal()不一定相等,hashCode()不是絕對可靠的。
7.深拷貝和淺拷貝
淺拷貝:
(1) 對于基本數據類型的成員變量,直接將屬性值賦值給新的成員變量。對于基礎類型,其中一個對象修改該值,不會影響另外一個。
(2) 對于引用類型,淺拷貝只是把內存地址賦值給了成員變量,它們指向了同一內存空間。改變其中一個,會對另外一個也產生影響。
深拷貝:
(1) 對于基本數據類型的成員變量,和淺拷貝一樣,直接將屬性值賦值給新的成員變量,其中一個對象修改該值,不會影響另外一個。
(2) 對于引用類型,深拷貝會新建一個對象空間,然后拷貝里面的內容,所以它們指向了不同的內存空間。改變其中一個,不會對另外一個也產生影響。
(3) 深拷貝消耗資源比淺拷貝多
淺拷貝不同于直接進行引用傳遞,淺拷貝后生成了新的對象,而不是把原對象的地址直接給新對象。
8.Java異常機制
首先,Throwable是所有異常的父類,Error和Exception都實現了Throwable,其中Exception又分為RuntimeException和其他Exception。
RuntimeException可以不編寫異常處理的程序代碼,依然可以編譯成功,它是在程序運行時才有可能發生的,而其它Exception一定要編寫異常處理的程序代碼才能使程序通過編譯。
Java默認的異常處理方式:
1、拋出異常
2、終止程序
如果加上捕獲的代碼,可以針對不同的情況做處理,也就是try - catch - finally塊。
其中try是必須的,至少要有一個 catch 或者 finally 塊。
finally的程序代碼塊運行結束后,程序再回到try-catch-finally塊之后繼續執行。
當代碼中出現return時,一定是finally語句塊執行完成后才會去執行相應的return代碼,無論return語句在什么位置。
throws 和 throw
throws語句用在方法定義時聲明該方法要拋出的異常類型
如果一個方法可能會出現異常,但不想或者沒有能力處理這種異常,可以在方法聲明處用。
throw拋出的只能夠是可拋出類Throwable或者其子類的實例對象。
9.String s = "xxx"和String s = new String(“xxx”)的區別
JDK1.8
1、String s = “xxx”。JVM首先會去字符串常量池中查找是否存在"xxx"這個對象,如果不存在,則在字符串常量池中創建"xxx"這個對象,然后將池中"xxx"這個對象的地址賦給"xxx"對象的引用s;如果存在,直接將池中"xxx"這個對象的地址賦給引用s。
2、String s = new String(“xxx”) 首先會去字符串常量池中查找是否存在"xxx"這個對象,如果不存在,則在字符串常量池中創建"xxx"這個對象,并且再到堆中創建"xxx"這個對象,將堆中對象的地址賦給"xxx"對象的引用s,如果存在,也會在堆中創建對象,賦給引用變量s的依然是堆中的地址。
關于字符串常量池中存放的是對象還是對象的引用,在jdk1.8中,存放的是對象,證據數String類里intern()方法的注釋。
關于數組初始化時使用new或者不用new,和字符串是不同的,創建對象時,數組內容都是放在堆中,然后把地址賦給引用變量。
10.s5 == s2,s1==s5,返回值分別是什么?
String s1=“ab”;
String s2=“a”+“b”;
String s3=“a”;
String s4=“b”;
String s5=s3+s4;
返回值分別是false,false。
在執行String s = xxx的時候,JVM首先會去字符串常量池中尋找這個字符串對象,如果存在,將地址賦給引用s,如果不存在,就在字符串常量池中創建字符串對象,將地址賦給引用s。
而"a"+“b” 由于編譯器的優化(通過StringBuilder完成),該語句在class文件中就相當于String s = “ab”,(s1 == s2 為true)。為什么s5 == s2為false呢,因為s5=s3+s4,是兩個字符串變量相加。這兩個變量不是final類型的,無法在編譯期確認,然后兩個字符串相加是通過StringBuilder實現的,新建了一個對象。因此s5==s2為false。
11. ==和equals()的區別
== : 對于基本類型來說 == 比較值是否相等,對于引用類型來說,==比較地址是否相等
equals:Object類中的equals()方法是直接用==來比較的,因為所有的類都繼承了Object類,equals方法是可以被重寫的,所以默認情況下比較的是地址值,根據重寫,可以比較內容。例如String類中的equals()方法,先比較地址值,如果相同直接返回true,再比較內容,如果內容相同返回true,不同則返回false。
12. 靜態變量和實例變量的區別
靜態變量屬于類,實例變量屬于對象。類加載之后,就可以使用靜態變量,無論創建多少個對象,靜態變量在內存中僅有一份。實例變量必須創建對象后,才會被分配空間,才可以使用。
13. 父類的靜態方法能否被子類重寫
不可以,但是可以繼承,靜態方法屬于類。子類如果定義相同名稱的靜態方法,并沒有實現重寫。子類對象賦給父類的引用,如果子類父類有同名static方法,然后通過父類的引用調用方法的話,調用的是父類的靜態方法。
14. final,finalize()和finally{}的不同之處
fianl用來修飾類,方法,變量。被修飾的類不能被繼承,方法不能被重寫,變量不能被改變。
finalize()是Object類的一個方法,在垃圾收集器執行的時候會調用被回收對象的此方法,供垃圾收集時的其他資源回收,例如關閉文件。
finally{}用于異常處理try-catch塊中,表示一定會執行的代碼,常用于關閉某些資源。
15. switch中能否使用string做參數
在JDK7之前,不可以,無法通過編譯。在JDK7后可以,查看源碼可以知道,對字符串內容做了hashCode()。
16. int和Integer的區別
int和Integer的區別
1、Integer是int的包裝類,int則是java的一種基本數據類型
2、Integer變量必須實例化后才能使用,而int變量不需要
3、Integer實際是對象的引用,當new一個Integer時,實際上是生成一個指針指向此對象;而int則是直接存儲數據值 。
4、Integer的默認值是null,int的默認值是0
17. 線程、進程、協程的區別
1、進程是程序執行的時候資源分配的基本單元,線程是CPU調度的基本單元。
2、一個進程至少包含一個線程,進程間的資源是獨立的,同一進程的線程間的資源是共享的。
3、協程是一個特殊的函數,一個線程可以有多個協程。如果是多核CPU,多個進程或一個進程內的多個線程是可以并行運行的,多個協程是串行執行的,只能在一個線程內運行,沒法利用CPU多核能力。
18. 如何控制多線程執行順序
方法一:join()方法
join()作用:讓主線程等待子線程運行結束后才能繼續運行。
例如:new三個線程
thread1.start();
thread1.join();
thread2.start();
thread2.join();
thread3.start();
運行順序:
thread1
thread2
thread3
方法二:ExecutorService
利用并發包里的Excutors的newSingleThreadExecutor產生一個單線程的線程池,而這個線程池的底層原理就是一個先進先出(FIFO)的隊列。用submit依次添加了1線程,按照FIFO的特性,執行順序也就是添加的順序。
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(thread1);
19. 線程的生命周期和切換
線程的生命周期:
1、新建
當我們創建一個Thread對象的時候,線程就處于新建狀態。
2、就緒
執行start(),線程就變成就緒狀態,等待CPU調度、分配資源。
3、運行
當就緒狀態的線程獲得了執行權,就會執行run()里的內容,運行狀態下的線程,通過yield()方法可以切換到就緒狀態,
4、阻塞
當線程試圖獲取某個對象的同步鎖時,如果該鎖被其他線程所持有,則當前線程就會進入阻塞狀態,如果想從阻塞狀態進入就緒狀態必須獲取到其他線程所持有的鎖。
運行中的線程通過wait()、sleep()、join()使線程進入阻塞狀態,阻塞狀態的線程不能直接轉為運行狀態,必須先變為就緒狀態,等待CPU調度
5、死亡
run()執行完畢或者拋出了異常,死亡狀態的線程不能切換到其他狀態
wait()會釋放對象鎖資源而sleep()不會釋放對象鎖資源。但是 wait 和sleep 都會釋放cpu資源
當線程執行wait()方法時候,會釋放當前的鎖,然后讓出CPU,進入等待狀態。
只有當 notify/notifyAll() 被執行時候,才會喚醒一個或多個正處于等待狀態的線程,然后繼續往下執行,直到執行完synchronized 代碼塊的代碼或是中途遇到wait() ,再次釋放鎖。
20. 死鎖的產生條件
1、互斥條件:一個資源每次只能被一個進程所使用
2、不剝奪條件:一個進程未使用完一個資源不會釋放,只能由使用這個資源的進程主動釋放
3、請求與保持:一個進程請求另一個資源,并沒有釋放自己正在使用的資源
4、循環等待條件:幾個進程之間形成一種頭尾相接的循環等待資源關系,每一個進程已獲得的資源同時被下一個進程所請求
四個條件全部滿足,形成死鎖。
21. 同步方法和同步代碼塊
每一個對象有一把鎖。線程可以使用synchronized關鍵字來獲取對象上的鎖。synchronized關鍵字可應用在方法級別(粗粒度鎖)或者是代碼塊級別(細粒度鎖)。
synchronized(obj){ ... }- 任何線程進入同步代碼塊、同步方法之前,必須獲得鎖(java每個對象都有一個內置鎖),被synchronized修飾的方法,獲取的鎖是當前對象的鎖,被synchronized修飾的代碼塊可以指定獲取哪個對象的鎖(當前對象就是this)。
- synchronized關鍵字也可以修飾靜態方法,此時如果調用該靜態方法,將會鎖住整個類
22. HashMap
概述
HashMap實現了Map接口,存儲k-v型數據,在JDK1.8之前,HashMap由數組+鏈表實現。自JDK1.8之后,改為由數組+鏈表+紅黑樹實現。
當我們要向HashMap里存儲值的時候,會計算key的hash值,根據key的hash值來找到要存儲的位置,如果這個位置有元素存在了,即發生了hash沖突,就轉為鏈表形式存儲,默認情況下,當鏈表長度大于8,且數組長度大于64的時候,HashMap就把鏈表轉為紅黑樹存儲。使用數組+鏈表的時候,時間復雜度取決于鏈表的長度,為O(n),轉為紅黑樹之后,時間復雜度為O(logn)效率更高。
HashMap的key和value都可以為null,且key不可重復,value可重復。HashMap是線程不安全的,當需要線程安全的時候可以采用ConcurrentHashMap或者使用Collections類的synchronizedMap方法來滿足。
總結
以上是生活随笔為你收集整理的Java常见易错问题记录的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: clustalw序列比对_几个多序列比对
- 下一篇: python实现比较两手牌的大小--斗地