本人最近讀完《瘋狂java-突破程序員基本功的16課 》讀完後,感受對java基礎又有了新的認識,在這裏總結一下:
1、數組與內存控制
1.1 數組初始化
java語言的數組是靜態的,即數組初始化以後,長度不能夠變(
區別,JavaScript數組可變,是動態的)。
初始化分兩種:靜態初始化,初始化時由程序員指定每一個數組元素的初始值,系統決定長度。
動態初始化,初始化時由程序員指定數組的長度,由系統指定默認值(int爲0、boolean爲false)。
初始化=長度+初始值;注意不能同時指定長度又指定初始值。
數組是一種引用類型的變量(同C語言的指針):數組變量和數組對象組成,即數組變量指向數組對象。java引用類型變量包括數組和對象。
1.2 使用數組
多維數組就是一位數組。
1.3 小結
2、對象與內存控制
java內存管理分爲兩部分:內存分配和內存回收。
雖然JVM內置了垃圾回收機制回收失去引用的java對象所佔的內存;但仍存在內存泄露問題。
2.1 實例變量和類變量
java變量分爲:成員變量和局部變量;
局部變量分爲:形參、方法內的局部變量和代碼塊內的局部變量;(在方法的棧內存中,做用時間短)
成員變量分爲:實例變量(非靜態變量)和類變量(靜態變量)。
(若使用static修飾,成員成爲類自己,不然屬於類的實例;因此static只能修飾類裏的成員,不能修飾外部類、局部變量、局部內部類)
定義成員變量時,必須採用向前引用。
對實例變量初始化: 1定義實例時指定初始值;
2非靜態初始化塊中對實例變量指定初始值;
3構造器中對實例變量指定初始值。
其中,1,2優先3執行,1和2根據代碼中的先後順序優先執行。
類變量的初始化時機: 1 定義類變量時指定初始值;
2 靜態初始化塊中對類變量指定初始值。
其中,1,2執行順序與排列順序相同。(程序對類變量只初始化一次)
初始化實際過程是:1 系統爲成員變量分配內存空間(默認值一、null、false);
2 按順序對成員變量進行初始化。
(若分配內存的時候就在構造器裏調用成員變量的初始值,值是默認值0,形成結果錯誤,由於還將來得及初始化變量)
2.2 父類構造器
1)隱式和顯式調用(隱式調用無參數的構造器)
super調用用於顯式調用父類的構造器;
this用於顯式調用本類中另外一個重載的構造器;
super和this均只能在構造器中使用,而且都必須做爲第一行代碼,只能使用其中之一而且最多調用一次。
2)訪問子類對象的實例變量
問題:java對象由構造器建立的嗎?
答案:錯誤,構造器只負責對java對象實例變量執行初始化,在構造器以前就已經被分配內存,且初始值爲0、null、false。
3)調用被子類重寫的方法
致使子類的重寫方法在子類構造器以前執行,從而訪問不到子類的事例變量的正確初始值。
2.3 父子實例的內存控制
1)繼承成員變量和繼承方法的區別
子類的對象中保存了全部父類的實例變量(包括同名實例變量,父類的實例變量被隱藏了);
保存了全部父類的方法(不包括複寫的父類同名的方法)。
當變量編譯時類型和運行時類型不一樣時,經過該變量 訪問它引用的對象的實例變量時,該實例變量的值由聲明該變量的類型決定;
訪問它引用的對象的事例方法時,該方法由它實際所引用的對象決定。
(eg. animal a = new dog(); 假設animal和dog都含有同名的實例變量name和同名方法getname(),
編譯時類型aniaml,訪問a.name是animal的實例變量name;運行時類型是dog,調用a.getname();是dog的方法setname)
2.4 final修飾符
final修飾的變量賦初始值以後不能從新賦值;
final修飾的方法不能被重寫;
final修飾的類不能派生子類(所以一個類不能既被聲明爲
abstract的,又被聲明爲final的);
1)final修飾的變量
final修飾的實例變量的初始化位置:
1.定義實例變量時;
2.非靜態初始化塊中;
3.在構造器中(javap編譯後發現,本質上均是在構造器中賦值)
final修飾的類變量的初始化位置:
1.定義類變量時;
2.靜態初始化塊中。(編譯後發現,本質是均在靜態初始化塊中被賦值)
注:變量由final修飾而且定義時賦值,至關於宏替換,編譯時直接把變量替換成初始值。
2)執行宏替換的變量
eg.(final String str1="瘋狂";
final String str1="java";
str1 + str2 == "瘋狂java"爲true,執行了宏替換,str1指向了字符串池緩存中的串「瘋狂」)
final修飾變量只有在定義時初始化值,才被執行宏替換;在初始化塊和構造器中均不執行宏替換。
3)final方法不能被重寫
4)內部類中的局部變量
爲何內部類(匿名內部類)訪問的局部變量必須使用final修飾?
答:對於普通局部變量,它的做用域在方法內,方法執行結束,局部變量隨之消失;
但內部類則可能產生隱式的閉包,閉包將脫離它所在的方法繼續存在(new thread();),因此內部類擴大了局部變量的做用域,
若能夠隨意修改局部變量值,可能引發極大的混亂。
eg final
String str=」java講義「;
new thread(){
public void run(){
println(
str+1);
}
}
2.5小結
避免在父類構造器中訪問子類實例變量、調用子類實例方法。
3、常見java集合的實現細節
3.1 set和map
set的底層是由map實現的。
1)set和map的關係
map集合的key無序不重複就是set;
map集合其實是key和value的關聯數組,把map集合的key和value捆綁在一塊兒就是set集合。
2)hashmap和hashset
集合存儲java對象時,只是保存了java的引用變量。
eg對於hashmap,當執行map.put("語文",80);時,系統調用"語文"的hashcode()方法獲得hashcode值(每一個java對象都有hashcode方法),
系統根據hashcode值決定元素的存儲位置,系統會自動構建一個table數組保存hashmap的entry。(JDK安裝目錄下的src.zip中查看源 文件)
hashmap之因此能快速存取、取它所包含的entry,徹底相似於生活中:不一樣東西放在不一樣位置,須要時才能快速找到它。
hashset是基於hashmap實現的,二者本質是相同的。hashset的集合元素由hashmap的key保存,vlue保存一個靜態的object類PRESENT.
當對象存儲在hashmap的key或hashset中時,必定要重寫hashcode()和equals(),只有比較的兩個對象的hashcode同樣纔會比較equls()。
3)treemap和treeset
treeset的底層使用的存儲器就是treemap,treemap採用紅黑樹的排序二叉樹保存map中每一個entry(節點)。
hashmap和hashset集合,像「媽媽放東西」,每一個物品放在不一樣的地點。優勢:查找快
treemap和treeset集合, 像「體育課排隊」,按大小個依次排成一隊,每一個人按身高大小插入。優勢:有序
3.2 map和list
1)map的value()方法
2)map和list的關係
list至關於全部key都是int類型的map;map至關於索引是任意類型的list。
3.3 arraylist和linkedlist
list由vector、arraylist和linkedlist構成
1)vector和arraylist的區別
vector是一個古老的版本,惟一的優勢是線程安全的,arrarylist能夠取代vector了。
2)arraylist和linkedlist的實現差別
list是線性表的數據結構,arraylist是順序存儲的線性表(採用數組存儲)、linkedlist是鏈表存儲的線性表(採用Deque雙端隊列實現,由隊列和棧全部特徵)。
arraylist的整體性能比linkedlist好一些,尤爲是查找某個元素的操做;可是插入和刪除操做linkedlist比arratlist性能好,由於基於數組存儲插入刪除須要移動後面多有的元素。
3.4 iterator迭代器
iterator是一個迭代器接口,java要求各類集合提供一個iterator()方法,用於遍歷該集合中的元素。
迭代器模式:java的iterator和enumeration兩個接口都是迭代器模式的表明。目的是爲了遍歷多種數據列表、集合、容器,這些數據列表、集合、容器面向相同的接口編程。
4、java的內存回收
4.1 java引用的種類
1)對象在內存中的狀態
JVM是垃圾回收機制是否回收一個對象的標準在於:是否還有引用變量引用該對象,若沒有引用垃圾回收機制就回收它。
對象在堆內存中的狀態分爲:可達狀態、可恢復狀態(系統調用finalize方法進行清理資源,若恢復引用則可達狀態,不然不可達狀態)、和不可達狀態。
(區別final、finally(異常模塊中清理操做)和finalize(清理資源))
2)強引用
前面提到的引用均是強引用,內存永遠也不會被回收,即便異常內存泄漏。
3)軟引用(SoftReference)
若內存不足,則被內存回收機制回收,賦值爲null。
4)弱引用(WeakReference)
比軟引用生存期更短,當系統回收機制運行時,無論內存是否足夠,老是被回收釋放。
若大量的對象須要使用若引用,可使用WeakHashMap來保存它們。(因此不多使用WeakReference,由於老是大量對象纔會內存泄漏)
5)虛引用(PhantomReference)
虛引用不能夠單獨使用,由於它不能獲取引用對象,必須結合ReferenceQueue類(用來保存回收後的對象),執行內存回收機制後,虛引用就會保存到引用隊列中。
4.2 java的內存泄露
JVM的垃圾回收機制會自動回收不可達狀態對象的內存空間,由於垃圾回收機制實時的監控每一個對象的運行狀態
(而JVM不會釋放處於可達狀態的無用對象的內存致使內存泄漏,因此刪除對象後,須要把引用賦值空,讓JVM自動回收它們);
可是C++只能釋放指針引用的內存空間,沒法控制不可達狀態對象,從而更容易發生內存泄漏問題。
4.3 垃圾回收機制
綜述:回收不可達狀態的對象,清理內存分配、碎片。
設計:串行回收和並行回收、併發執行和應用程序中止、壓縮和不壓縮(標記清除)和複製(標記壓縮)。
對內存的分代回收:Young代、Old代、Permanent代。
Young代:採用複製算法,可達狀態的對象數量少複製成本不大。由一個Eden區和兩個Survivor區構成。
Old代:採用標記壓縮算法,避免複製大量對象,不會產生內存碎片,由於old代的對象不會很快死掉。Old代空間比Young代大。
Permanent代:用於裝載Class和方法等信息,回收機制不會回收該代中的內容。
4.4 內存管理的小技巧
1儘可能使用直接變量
eg. String str = "hello";(而不是String str = new String("hello");)
2使用StringBuilder和StringBuffer進行字符串鏈接
由於多個字符串鏈接運算,會產生臨時字符串。
3儘早釋放無用對象的引用
Object obj = new Object();
... ...
obj = null;//若後面存在耗時、耗內存操做,obj可能被回收
4儘可能少用靜態變量
類和類的靜態方法存在permanent永久代裏,一旦建立常駐內存。
5避免在常常調用的方法、循環中建立java對象
這種不斷建立、回收內存的操做,對系統性能影響很大。
6緩存常用對對對象
緩存器設計的關鍵是保留大部分已用過的對象。
7儘可能不要使用finalize方法
垃圾回收機制準備回收該對象以前,會調用該類的finalize方法執行資源整理。
8考慮使用SoftReference軟引用
當建立長度很大的數組時,使用SoftReference軟引用,內存不足時,內存回收機制會回收軟引用的對象內存;
可是使用該引用時要注意,軟引用的不肯定性,應顯示判斷對象是否爲null;若空則從新建立。
5、表達式中的陷阱
5.1 關於字符串的陷阱
String str="java";字符串池中的字符串對象不會被垃圾回收,再次使用時,無需建立新的字符串。
str1="hello java" == str2="hello"+"java"; true由於編譯時就能夠肯定該表達式的值;但表達式含方法調用或變量時,編譯時就不能夠肯定,false
問題:str=「hello"+"java"+"rock"建立了幾個字符串對象?答:一個,「hellojavarock」,由於編譯時就肯定了該表達式。
String 類是不可變字符串類(str="java";str="hello java";實際上引用變量str一直沒變,但引用對象有兩個,str指向了"hellojava",這樣會形成內存內存泄漏,由於"java"對象失去了引用而沒有回收);
StringBuilder和StringBuffer是可變字符串類(StringBuilder和StringBuffer惟一區別是StringBuffer是線程安全的,大多方法增長了synchronized修飾,但會下降執行效率,因此建議StringBuilder)。
String 類的比較方法:==比較地址; 重寫equals比較內容;compareTo比較大小(實現了Compareble接口),"ax">"abcd" 因此s1.compareTo(s2)>0。
5.2 表達式類的缺陷
強引用類型:變量先聲明後使用、只能賦該類型的值。
表達式類型自動提高:byte->short->int->long->float->double
複合賦值運算符(+=、-=、*=):short a=a+5 不等價 a+=5;由於a+=5 等價a=(int)a+5,包含隱含類型轉換, a+5將表達式轉爲int類型,在把int型賦值short型出現異常,而a+=5隱式將表達式轉爲short,再賦值就正確。但 要注意超出範圍的高位"截斷"。
5.3 輸入法致使的陷阱
編譯時提示"非法字符"是java代碼中出現了全角字符(尤爲注意全角空格)。
5.4 註釋字符必須合法
5.5 轉義字符的陷阱
5.6 泛型可能引發的錯誤
1)當原始類型的變量賦值給一個帶泛型信息的變量時,老是能夠經過編譯,但編譯會把集合元素當成泛型類型處理,當發生類型衝突時會報錯。
eg List list=new Arraylist();list.add("java");//原始類型就是List<String>
2)具備泛型信息的對象付給一個沒有泛型信息的變量時,多有<>內的類型信息都被拋棄,包括該對象方法中<>內的泛型信息。
3)泛型數組:java不支持泛型數組。即:
List<String>[] lsa=new List<String>[10];
5.7 正則表達式的陷阱
String類支持正則表達式的方法:spilt()、replaceAll()、replaceFirst()和matches;
使用支持正則表達式的方法,須要注意轉義字符:str.split(//.);而不能str.split(.);
5.8 多線程的陷阱
同步非靜態方法的同步監視器是this;靜態的同步方法監視器是類自己。
注意靜態初始化塊啓用新線程初始化時,和類自己初始化形成死鎖現象,由於主線程和子線程相互等待初始化結束。因此子線程不能初始化變量。
多線程環境存在危險,須要把訪問共同資源的方法使用synchronized同步修飾。(eg pbulic
synchronized double getbalance/graw(){})
6、流程控制的陷阱
6.1switch語句陷阱
switch的表達式類型不能是String、long、double、float等基本類型,能夠是int、char和enum枚舉
6.2標籤引發的陷阱
6.3if語句的陷阱
當心空語句: if(a>3);遇到第一個分號就結束執行體。
6.4循環體的花括號
單獨一條局部變量定義語句不能放在循環體裏,如 for(int i=0;i<9;i++) Cat =new Cat();
必須放在循環體裏定義局部變量:如 for(int i=0;i<9;i++) { Cat =new Cat(); }
6.5for語句的陷阱
5.6foreach循環的循環計數器
7、面向對象的陷阱
7.1 instanceof運算符的陷阱
Object obj = "hello";
String str = (String)obj;//編譯和運行時不會報錯
Int a = (Integer)obj;//編譯時不會報錯,但運行時報錯。之內Int 是Object的子類,但String 類型不能強轉Integer
Math m = (Math)str;//編譯會報錯,由於Math 既不是Object的父類也不是它的子類
7.2構造器的陷阱
構造器並不建立對象,只是進行初始化操做;
clone方法和反序列化機制複製對象不須要調用構造器;實現clone方法須要兩個要求:1)類實現了cloneable藉口;2)類提供clone方法;
eg Dog a=new Dog("xian"); Dog b=a.clone(); a.equals(b);
true a==b;
false
7.3持有當前類的事例
7.4到底調用哪一個重載方法
7.5方法重寫的陷阱
子類沒法重寫父類的private方法(也就是說子類容許存在和父類同名的方法,可是兩個方法)
若不使用控制符(public private protected)修飾類或方法,則該類或方法是包訪問控制權限(只能同一個包下的類能夠訪問)。
7.6非靜態內部類的陷阱
非靜態內部類限制多;
靜態內部類建議使用,外部類至關於一個包,易於使用
7.7static關鍵字
Animal a=new Animal(); Animal a2=new Dog(); a.info(); a2.info();注:
info方法都是靜態的
結果輸出的都是Animal 的info方法,由於靜態方法是屬於類的,聲明類都是Animal。
限制:靜態內部類不能訪問外部類的非靜態成員;
7.8naive方法的陷阱
8、異常捕捉的陷阱
1)粉紅色的是受檢查的異常(checked exceptions):必須在編譯時必須用try、catch捕捉,不然編譯器會報異常;
但若try塊中沒有拋出checked類型的異常,則編譯會報錯。
2)綠色的異常是運行時異常(runtime exceptions)
:運行時出現的異常,須要程序員本身決定是否捕捉,如空指針、除0等;
若try塊中沒有拋出runtime類型的異常,編譯卻不會報錯。
3)錯誤(error):嚴重的錯誤,不須要捕捉。
注:處理checked異常的兩種方法:1catch中修復該異常;2拋出該異常。
8.1正確關閉資源的方式
1)使用finally塊來關閉物理資源,保證關閉操做老是被執行的;
2)關閉每一個資源前先保證引用該資源的引用變量不爲null;
3)每一個資源使用單獨的try..catch塊關閉資源,保證關閉資源時引起的異常不會影響其它資源的關閉。
eg
finally{
if(oos != null){
try{oos.close}catch(Exception){ex.printStackTrace();}
}
}
8.2finally塊的陷阱
1)無論try塊是否正常結束(exit(0)),都會執行finally塊。
2)若try塊、catch塊遇到return語句,不會當即結束該方法,而是會去執行finally塊,而後返回該方法處結束;若finally塊中也使用了return,則系統會直接在finally塊中結束該方法。
3)若try塊、catch塊遇到throw語句(同使用return),先去執行finally塊,若finally塊使用return,則不返回try塊、catch塊拋出異常,不然返回try塊、catch塊處拋出異常
8.3catch塊的用法
catch執行順序:先處理小異常,再處理大異常,不然將出現編譯錯誤。
8.4實際的修復
不要在finally或catch塊中調用任何可能引發異常的方法,不然不能拋出異常或循環異常,形成StackOverflowError。
9、線性表
10、棧和隊列
11、樹和二叉樹
12、
經常使用的內部排序
十3、程序開發
十4、程序調試
十5、使用IDE工具
十6、軟件測試
16.1概述
1軟件測試分類:
1)按整體把握分類:
靜態測試:代碼審閱、代碼分析、文檔檢測;
動態測試:結構測試(白盒)、功能測試(黑盒);
2)按測試工程大小分類:
單元測試、集成測試、系統測試、用戶測試、平行測試;
2經常使用的bug管理工具:Bugzilla、BugFree、TestDirector、JIRA、ClearQuest;
16.2單元測試(屬於難度較大的白盒測試)
1)測試對象:函數;接口、類、對象;
測試任務:接口測試、局部數據結構測試、邊界條件測試、全部獨立執行路徑測試、各條錯誤處理路徑測試;
2)單元的邏輯覆蓋:
語句覆蓋:設計的測試用例,把每條語句均執行一遍;
斷定覆蓋:每一個判斷去真取假至少執行一次;eg
if(a>10 && b==0){取真}else{取假};
條件覆蓋:使斷定條件的每一個可能取值至少知足一次;eg:a>10,取真T1;取假F1; b==0取真T2;取假F2;
斷定-條件覆蓋:
路徑覆蓋:幾乎不能覆蓋全部路徑,通常測試知足斷定覆蓋便可。
3)Junit的使用
annotation註釋符:@Test、@Before(初始化方法)、@After(回收資源)
Assert類是系列斷言的集合,提供一些斷言方法用於比較是否與指望值匹配。
16.3系統測試和自動化測試(利用系統測試和自動化測試工具)
16.4性能測試(由性能測試工具完成)