1、 Java技術基礎......................................................................................................... 1html
1.1編程語言.............................................................................................................. 1java
1.2 Java的特色.......................................................................................................... 1正則表達式
1.3 Java開發環境...................................................................................................... 1算法
1.4 Java開發環境配置............................................................................................... 2編程
1.5 Linux命令與相關知識.......................................................................................... 2小程序
1.6 Eclipse/Myeclipse程序結構................................................................................... 3windows
2、 Java語言基礎......................................................................................................... 4設計模式
2.1基礎語言要素....................................................................................................... 4數組
2.2八種基本數據類型................................................................................................ 4瀏覽器
2.3常量和變量.......................................................................................................... 5
2.4運算符與表達式................................................................................................... 5
2.5編程風格.............................................................................................................. 6
2.6流程控制語句....................................................................................................... 7
2.7數組..................................................................................................................... 8
2.8字符串................................................................................................................. 8
2.9方法三要素.......................................................................................................... 9
2.10插入排序............................................................................................................ 9
2.11冒泡排序............................................................................................................ 9
2.12冒泡排序:輕氣泡上浮的方式........................................................................... 10
2.13二分法查找....................................................................................................... 10
2.14 Java系統API方法調用..................................................................................... 11
2.15二進制基礎....................................................................................................... 11
2.16 Java基礎其餘注意事項..................................................................................... 11
3、 面向對象.............................................................................................................. 13
3.1類...................................................................................................................... 13
3.2對象................................................................................................................... 13
3.3包...................................................................................................................... 14
3.4方法及其調用..................................................................................................... 14
3.5引用................................................................................................................... 14
3.6訪問控制(封裝).............................................................................................. 14
3.7構造器............................................................................................................... 14
3.8 super()、super.和 this()、this......................................................................... 15
3.9重載和重寫........................................................................................................ 16
3.10繼承................................................................................................................. 17
3.11 static................................................................................................................. 20
3.12 final................................................................................................................. 21
3.13多態................................................................................................................. 22
3.14抽象類.............................................................................................................. 22
3.15接口................................................................................................................. 23
3.16內部類.............................................................................................................. 23
3.17匿名類.............................................................................................................. 24
3.18二維數組和對象數組......................................................................................... 25
3.19其餘注意事項................................................................................................... 26
4、 Java SE核心I....................................................................................................... 27
4.1 Object類............................................................................................................ 27
4.2 String類............................................................................................................. 28
4.3 StringUtils類...................................................................................................... 30
4.4 StringBuilder類.................................................................................................. 30
4.5正則表達式........................................................................................................ 31
4.6 Date類............................................................................................................... 32
4.7 Calendar類......................................................................................................... 32
4.8 SimpleDateFormat類........................................................................................... 33
4.9 DateFormat類..................................................................................................... 33
4.10包裝類.............................................................................................................. 34
4.11 BigDecimal類................................................................................................... 35
4.12 BigInteger類..................................................................................................... 35
4.13 Collection集合框架........................................................................................... 35
4.14 List集合的實現類ArrayList和LinkedList........................................................... 36
4.15 Iterator迭代器.................................................................................................. 39
4.16泛型................................................................................................................. 40
4.17加強型for循環................................................................................................. 40
4.18 List高級-數據結構:Queue隊列..................................................................... 41
4.19 List高級-數據結構:Deque棧......................................................................... 41
4.20 Set集合的實現類HashSet................................................................................. 42
4.21 Map集合的實現類HashMap.............................................................................. 43
4.22單例模式和模版方法模式.................................................................................. 45
5、 Java SE核心II..................................................................................................... 46
5.1 Java異常處理機制.............................................................................................. 46
5.2 File文件類......................................................................................................... 48
5.3 RandomAccessFile類........................................................................................... 50
5.4基本流:FIS和FOS........................................................................................... 52
5.5緩衝字節高級流:BIS和BOS............................................................................. 53
5.6基本數據類型高級流:DIS和DOS..................................................................... 53
5.7字符高級流:ISR和OSW................................................................................... 54
5.8緩衝字符高級流:BR和BW............................................................................... 55
5.9文件字符高級流:FR和FW............................................................................... 56
5.10 PrintWriter........................................................................................................ 57
5.11對象序列化....................................................................................................... 57
5.12 Thread線程類及多線程..................................................................................... 58
5.13 Socket網絡編程................................................................................................ 62
5.14線程池.............................................................................................................. 64
5.15雙緩衝隊列....................................................................................................... 64
機器語言:0 1 在硬件直接執行
彙編語言:助記符
高級語言: (Java運行比C/C++慢)
1)面向過程的高級語言:程序設計的基本單位爲函數,如:C/C++語言。
2)面向對象的高級語言:程序設計的基本單位爲類,如:Java、C#。
平臺無關性、簡單性、面向對象、健壯性、多線程、自動內存管理。
平臺無關性:指Java語言平臺無關,而Java的虛擬機卻不是,須要下載對應平臺JVM虛擬機的。
自動內存管理:對臨時存儲的數據自動進行回收,釋放內存。如:引用類型的變量沒有指向時,被回收;程序執行完後,局部變量被回收。
Java Developement Kit——Java開發工具包,簡稱JDK,是由Sun公司提供的一個免費的Java開發工具,編程人員和最終用戶能夠利用這個工具來編譯、運行Java程序。目前版本有JDK1.0、JDK1.一、JDK1.二、JDK1.三、JDK1.四、JDK1.5(J2SE5.0)、JDK1.6(J2SE6.0)、JDK1.7(J2SE7.0)。
JDK結構:JDK
|--開發工具(Tools)命令:java、javac、jar、rmic ...
|-- JRE(Java基本運行環境)
|--系統API庫,系統類庫
| 系統帶來的標準程序庫,標準API
|-- J VM java虛擬機
java 語言的執行環境
安裝完JDK以後,不能馬上使用,須要設置環境變量:
1)設置PATH:D:\Java\jdk1.6.0\bin(指向JDK中bin文件夾,有各類編譯命令)。
2)CLASSPATH:告訴Java程序去哪裏查找第三方和自定義類,若是 .class文件和類源文件在同一文件夾內,則不須要配置classpath,後續有包,則須要。
A.Windows:在命令行執行
set CLASSPATH=E:\workspace\1304\bin (臨時環境配置)
java day02.Demo1
u注意事項:
vE:\ set classpath = c:\ (不加分號就不找當前路徑)
= . ; c:\ ; d:\ ;(先找classpath,若無,再找當前路徑)
vC、D兩盤有同名 . class 文件,classpath設置爲D盤,而命令行窗口當前盤符爲C盤,則JVM現找classpath路徑,後找當前路徑。
B.Linux:在控制檯執行
①設置CLASSPATH環境變量,指向package所在的目錄,通常是項目文件夾中的bin目錄。
②執行java package.ClassName (包名必須寫)。
export CLASSPATH=/home/soft01/workspace/1304/bin (臨時環境配置)
java day01.HelloWorld
java -cp /home/soft01/workspace/1304/bin day01.HelloWorld(二合一)
u注意事項:
vWindows根目錄是反斜線:\
vLinux根目錄是斜線:/
1)Linux無盤符,只有一個根目錄(root)
2)終端 == 控制檯 == 命令行窗口
3)pwd:打印當前工做目錄,顯示當前工做目錄的位置
4)ls:列表顯示目錄內容,默認顯示當前目錄內容
5)cd:改變當前工做目錄;cd後不加參數=返回home文件夾;cd ~:返回home;
cd /:切換到根目錄;cd .. :返回上一層目錄(相對的);
6)mkdir:建立文件夾(目錄) 注意:目錄 == 文件夾
7)rm:刪除文件;rm xx xx:可刪多個文件;
rm –rf xx:-爲減號,r表遞歸,f表強制
8)cat xx:顯示文本文件內容
9)啓動Java開發工具:cd/opt/eclipse à ./eclipse . 表當前目錄下
10)絕對路徑: /home (以 / 開始爲絕對路徑,相對於根目錄)
相對路徑:home (相對於當前工做目錄)
11)home(用戶主目錄,用戶的家):/home/username 如:/home/soft01
12)主目錄(home):有最大訪問權限:什麼都能幹,增刪改查、建目錄等
其餘地方:通常只能查看,不能增刪改查、建立目錄等
Project項目文件
|-- src源文件
| |-- Package包
| |-- .java源文件
|-- bin
|-- Package包
|-- .class字節碼程序
u注意事項:
vMyeclipse5.5消耗少,Myeclipse6.5最穩定
1)標識符:給類、方法、變量起的名字
A.必須以字母或下劃線或 $ 符號開始,其他字符能夠是字母、數字、$ 符號和下劃線。
B.只能包含兩個特殊字符,即下劃線 _ 和美圓符號 $ 。不容許有任何其餘特殊字符。
C.標識符不能包含空格。
D.區分大小寫。
2)關鍵字:只有系統才能用的標識符
u注意事項:
vtrue、false、null不是關鍵字!是字面量。
vmain不是關鍵字!可是是一個特殊單詞,能夠被JVM識別,主函數是固定格式,做爲程序的入口。
3)註釋:單行註釋:// 多行註釋:/* ……*/ 文檔註釋:/**……*/
u注意事項:開發中類前、屬性前、方法前,必須有文檔注視。
1)四種整數類型(byte、short、int、long):
byte:8位,用於表示最小數據單位,如文件中數據,-128~127
short:16位,不多用,-32768 ~ 32767
int:32位、最經常使用,-2^31-1~2^31 (21億)
long:64位、次經常使用
u注意事項:
vint i=5; // 5叫直接量(或字面量),即直接寫出的常數。
v整數字面量默認都爲int類型,因此在定義的long型數據後面加L或l。
v小於32位數的變量,都按int結果計算。
v強轉符比數學運算符優先級高。見常量與變量中的例子。
2)兩種浮點數類型(float、double):
float:32位,後綴F或f,1位符號位,8位指數,23位有效尾數。
double:64位,最經常使用,後綴D或d,1位符號位,11位指數,52位有效尾數。
u注意事項:
v二進制浮點數:1010100010=101010001.0*2=10101000.10*2^10(2次方)=1010100.010*2^11(3次方)= . 1010100010*2^1010(10次方)
v尾數: . 1010100010 指數:1010 基數:2
v浮點數字面量默認都爲double類型,因此在定義的float型數據後面加F或f;double類型可不寫後綴,但在小數計算中必定要寫D或X.X。
vfloat 的精度沒有long高,有效位數(尾數)短。
vfloat 的範圍大於long 指數能夠很大。
v浮點數是不精確的,不能對浮點數進行精確比較。
3)一種字符類型(char):
char:16位,是整數類型,用單引號括起來的1個字符(能夠是一箇中文字符),使用Unicode碼錶明字符,0~2^16-1(65535)。
u注意事項:
v不能爲0個字符。
v轉義字符:\n 換行 \r 回車 \t Tab字符 \" 雙引號 \\ 表示一個\
v兩字符char中間用「+」鏈接,內部先把字符轉成int類型,再進行加法運算,char本質就是個數!二進制的,顯示的時候,通過「處理」顯示爲字符。
4)一種布爾類型(boolean):true真 和false假。
5)類型轉換: char-->
自動轉換:byte-->short-->int-->long-->float-->double
強制轉換:①會損失精度,產生偏差,小數點之後的數字所有捨棄。
②容易超過取值範圍。
變量:內存中一塊存儲空間,可保存當前數據。在程序運行過程當中,其值是能夠改變的量。
1)必須聲明而且初始化之後使用(在同一個做用域中不能重複聲明變量)!
2)變量必須有明確類型(Java是強類型語言)。
3)變量有做用域(變量在聲明的地方開始,到塊{}結束)。變量做用域越小越好。
4)局部變量在使用前必定要初始化!成員變量在對象被建立後有默認值,可直接用。
5)在方法中定義的局部變量在該方法被加載時建立。
常量:在程序運行過程當中,其值不能夠改變的量。
u注意事項:
v字面量、常量和變量的運算機制不一樣,字面量、常量由編譯器計算,變量由運算器處理,目的是爲了提升效率。
eg:小於32位數的字面量處理
byte b1 = 1; byte b2 = 3;
//byte b3 = b1+b2;//編譯錯誤,按照int結果,須要強制轉換
byte b3 = (byte)(b1+b2);
//byte b3 = (byte)b1+(byte)b2;//編譯錯誤!兩個byte、short、char相加仍是按int算
System.out.println(b3); //選擇結果:A編譯錯誤B運行異常 C 4 D b3
byte b4 = 1+3;//字面量運算,編譯期間替換爲4,字面量4
//byte b4 = 4; 不超過byte就能夠賦值
v無論是常量仍是變量,必須先定義,纔可以使用。即先在內存中開闢存儲空間,纔可以往裏面放入數據。
v無論是常量仍是變量,其存儲空間是有數據類型的差異的,即有些變量的存儲空間用於存儲整數,有些變量的存儲空間用於存儲小數。
1)數學運算:+ - * / % ++ --
u注意事項:
v+ - * / 兩端的變量必須是同種類型,並返回同種類型。
v% 取餘運算,負數的餘數符號與被模數符號相同, - 1 % 5 = - 1,1 % - 5 = 1;Num % n,n>0,結果範圍[0,n),是周期函數。
v注意整除問題:1 / 2 = 0(整數的除法是整除)1.0 / 2 = 0.5 1D / 2 = 0.5
v單獨的前、後自增或自減是沒區別的,有了賦值語句或返回值,則值不一樣!
eg1:自增自減
int a = 1; a = a++; System.out.println("a的值:"+a);
第1步:後++,先肯定表達式a++的值(當前a的值) a++ ---->1
第2步:++,給a加1 a ---->2
第3步:最後賦值運算,把a++整個表達式的值賦值給a a ---->1
a被賦值兩次,第1次a = 2,第2次把1賦值給1
eg2:自增自減
x,y,z分別爲5,6,7 計算z + = -- y * z++ ;// x = 5,y = 5,z = 42
z = z + -- y * z++ à 42 = 7 + 5 * 7 從左到右入棧,入的是值
eg3:取出數字的每一位
d = num%10;//獲取num的最後一位數 num/=10; //消除num的最後一位
2)位運算:& | ~(取反) ^(異或)>> << >>>
u注意事項:
v一個數異或同一個數兩次,結果仍是那個數。
v|:上下對齊,有1個1則爲1;&:上下對齊,有1個0則爲0;(都爲二進制)
v&至關於乘法,| 至關於加法;&:有0則爲0,| :有1則爲1,^:兩數相同爲0,不一樣爲1。
3)關係運算符:> < >= <= == !=
4)邏輯運算符:&& ||(短路) ! & |
eg:短路運算:&&:前爲flase,則後面不計算;|| :前爲true,則後面不計算
int x=1,y=1,z=1;
if(x--==1 && y++==1 || z++==1) // || 短路運算後面的不執行了!
System.out.println(「x=」+x+」,y=」+y+」,z=」+z);// 0 , 2, 1
5)賦值運算符:= += -= *= /= %=
eg:正負1交替
int flag= -1; System.out.println(flag *= -1); ……
6)條件(三目)運算符:表達式1 ? 表達式2 :表達式3
u注意事項:
v右結合性:a > b ? a : i > j ? i : j 至關於 a > b ? a : ( i > j ? i : j )
v三目運算符中:第二個表達式和第三個表達式中若是都爲基本數據類型,整個表達式的運算結果由容量高的決定。如:int x = 4; x > 4 ? 99.9 : 9;
99.9是double類型,而9是int類型,double容量高,因此最後結果爲9.9。
7) 運算符優先級:括號 > 自增自減 > ~ ! > 算數運算符 > 位移運算 > 關係運算 > 邏輯運算 > 條件運算 > 賦值運算
MyEclipse/Eclipse中出現的紅色叉叉:編譯錯誤
編譯錯誤:java編譯器在將Java源代碼編譯爲class文件的過程出現的錯誤,通常是語法使用錯誤!當有編譯錯誤時候,是沒有class文件產生,也就不能運行程序。
Java 程序結構:
1)選擇控制語句
if語句:if 、if-else、if-else-if:能夠處理一切分支判斷。
格式:if(判斷){…}、if(判斷){…}else{…}、if(判斷){…}else if(判斷){…}
switch語句:switch(必須爲int類型){case 常量1:…; case 常量2:… ; ….}
u注意事項:
vint類型指:byte、short、int,不能寫long類型,要寫也必須強轉成int類型;而byte、short爲自動轉換成int。
vswtich-case:若case中無符合的數,而且default寫在最前(無break時), 則爲順序執行,有break或 } 則退出。
vswtich-case:若case中無符合的數,而且default寫在最後,則執行default。
vswtich-case:若case中有符合的數,而且default寫在最後,而且default前面的case沒有break時,default也會執行。
2)循環控制語句
①for:最經常使用,用在與次數有關的循環處理,甚至只用for能夠解決任何循環問題。
u注意事項:for中定義的用於控制次數的循環變量,只在for中有效,for結束則循環變量被釋放(回收)。
②while:很經常使用,用在循環時候要先檢查循環條件再處理循環體,用在與次數無關的狀況。若是不能明確結束條件的時候,先使用while(true),在適當條件使用if語句加break結束循環。
③do-while:在循環最後判斷是否結束的循環。如:使用while(true) 實現循環的時候,結束條件break在while循環體的最後,就能夠使用 do-while 。do-while 的結束條件常常是「否認邏輯條件」,不便於思考業務邏輯,使用的時候須要注意。能夠利用while(true)+ break 替換。
④循環三要素:A.循環變量初值 B.循環條件 C.循環變量增量(是循環趨於結束的表達式)
⑤for和while循環體中僅一條語句,也要補全{ },當有多條語句,且不寫{ }時,它們只執行緊跟着的第一條語句。
⑥循環的替換:
while(布爾表達式){} 等價 for(;布爾表達式;){}
while(true){} 等價 for(;;)
while(true){} + break 替換 do{}while(布爾表達式);
for(;;) + break 替換 do{}while(布爾表達式);
3)跳轉控制語句
continue:退出本次循環,直接執行下一次循環
break:退出全部循環
類型一致的一組數據,至關於集合概念,在軟件中解決一組,一堆XX數據時候使用數組。
1)數組變量:是引用類型變量(不是基本變量)引用變量經過數組的內存地址位置引用了一個數組(數組對象),即栓到數組對象的繩子。
eg:數組變量的賦值
int[] ary = new int[3];// ary----->{0,0,0}<----ary1
int[] ary1 = ary;// ary 的地址賦值給ary1,ary 與 ary1 綁定了同一個數組
//ary[1] 與 ary1[1] 是同一個元素,數組變量不是數組(數組對象)
2)數組(數組對象)有3種建立(初始化)方式:①new int[10000] 給元素數量,適合不知道具體元素,或元素數量較多時 ②new int[]{3,4,5} 不須要給出數量,直接初始化具體元素適合知道數組的元素。③ {2,3,4} 靜態初始化,是②簡化版,只能用在聲明數組變量的時候直接初始化,不能用於賦值等狀況。
eg:數組初始化
int[] ary1 = new int[]{2,3,4};//建立數組時候直接初始化元素
int[] ary2 = {2,3,4};//數組靜態初始化,只能在聲明變量的同時直接賦值
//ary2 = {4,5,6};//編譯錯誤,不能用於賦值等狀況
ary2 = new int[]{4,5,6};
3)數組元素的訪問:①數組長度:長度使用屬性訪問,ary.length 獲取數組下標。②數組下標:範圍:0 ~ length-1就是[0,length),超範圍訪問會出現下標越界異常。③使用[index] 訪問數組元素:ary[2]。④迭代(遍歷):就是將數組元素逐一處理一遍的方法。
4)數組默認初始化值:根據數組類型的不一樣,默認初始化值爲:0(整數)、0.0(浮點數)、false(布爾類型)、\u0000(char字符類型,顯示無效果,至關於空格,編碼爲0的字符,是控制字符,強轉爲int時顯示0)、null(string類型,什麼都沒有,空值的意思)。
5)數組的複製:數組變量的賦值,是並不會複製數組對象,是兩個變量引用了同一個數組對象。數組複製的本質是建立了新數組,將原數組的內容複製過來。
6)數組的擴容:建立新數組,新數組容量大於原數組,將原數組內容複製到新數組,而且丟棄原數組,簡單說:就是更換更大的數組對象。System.arraycopy() 用於複製數組內容,簡化版的數組複製方法:Arrays.copyOf()方法,但需JKD1.5+。
字符串(string):永遠用「」雙引號(英文狀態下),用字符串鏈接任何數據(整數),都會默認的轉化爲字符串類型。
字符串與基本數據類型連接的問題:若是第一個是字符串那麼後續就都按字符串處理,如System.out.println("(Result)"+6 + 6 );那麼結果就是(Result)66,若是第一個和第二個…第n個都是基本數據,第n+1是字符串類型,那麼前n個都按加法計算出結果在與字符串鏈接。以下例中的System.out.println(1+2+」java」+3+4);結果爲3java34。
eg:字符串先後的「+」都是鏈接符!不是加法運算符!
System.out.println("A"+'B');//AB
System.out.println('A'+'B');//131
System.out.println(1+2+」java」+3+4);//3java34
u注意事項:比較字符串是否相等必須使用equals方法!不能使用==。"1".equals(cmd) 比cmd.equals("1") 要好。
方法:method(函數function = 功能) y=f(x)
1)方法的主要三要素:方法名、參數列表、返回值。
2)什麼是方法:一個算法邏輯功能的封裝,是通常完成一個業務功能,如:登陸系統,建立聯繫人,簡單說:方法是動做,是動詞。
3)方法名:通常按照方法實現的功能定名,通常使用動詞定義,通常使用小寫字母開頭,第二個單詞開始,單詞首字母大寫。如:createContact() 。
4)參數列表:是方法的前提條件,是方法執行依據,是數據。如:
login(String id, String pwd) ,參數的傳遞看定義的類型及順序,不看參數名。
5)方法返回值:功能執行的結果,方法必須定義返回值,而且方法中必須使用return語句返回數據;若是無返回值則定義爲void,此時return語句可寫可不寫;返回結果只能有一個,若返回多個結果,要用數組返回(返回多個值)。
u注意事項:遞歸調用:方法中調用了方法自己,用遞歸解決問題比較簡練,只需考慮一層邏輯便可!可是須要有經驗。必定要有結束條件!如:f(1)=1; 遞歸層次不能太深。總之:慎用遞歸!
將數組中每一個元素與第一個元素比較,若是這個元素小於第一個元素,則交換這兩個元素循環第1條規則,找出最小元素,放於第1個位置通過n-1輪比較完成排序。
for(int i = 1; i < arr.length; i++) {
int k = arr[i];// 取出待插入元素
int j;// 找到插入位置
for (j = i - 1; j >= 0 && k < arr[j]; j--) {
arr[j + 1] = arr[j];// 移動元素
}
arr[j + 1] = k;// 插入元素
System.out.println(Arrays.toString(arr));
}
比較相鄰的元素,將小的放到前面。
for(int i = 0; i < arr.length - 1; i++) {
boolean isSwap = false;
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int t = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = t;
isSwap = true;
}
}
if (!isSwap){ break; }
System.out.println(Arrays.toString(arr));
}
冒泡排序法能夠使用大氣泡沉底的方式,也能夠使用輕氣泡上浮的方式實現。以下爲使用輕氣泡上浮的方式實現冒泡排序算法。
for (int i = 0; i < arr.length - 1; i++) {
boolean isSwap = false;
for (int j = arr.length - 1; j > i; j--) {
if (arr[j] < arr[j - 1]) {
int t = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = t;
sSwap = true;
}
}
if (!isSwap){ break; }
System.out.println(Arrays.toString(arr));
}
intlow = 0; inthigh = arr.length - 1; intmid = -1;
while(low <= high) {
mid = (low + high) / 2;
if (arr[mid] < value){ low = mid + 1; }
else if (arr[mid] > value){ high = mid - 1; }
else{ break;}
}
if (low <= high) { System.out.println("能夠找到:index = " + mid + "。");
} else { System.out.println("沒法找到!"); }
二分法思想是取中,比較:
1)求有序序列arr的中間位置mid。 2)k爲要查找的數字。
若arr[mid] == k,查找成功;
若arr[mid] > k,在前半段中繼續進行二分查找;
若arr[mid] < k,則在後半段中繼續進行二分查找。
假若有一組數爲三、十二、2四、3六、5五、6八、7五、88要查給定的值k=24。可設三個變量low、mid、high分別指向數據的上界,中間和下界,mid=(low+high)/2.
1)開始令low=0(指向3),high=7(指向88),則mid=3(指向36)。由於k<mid,故應在前半段中查找。
2)令新的high=mid-1=2(指向24),而low=0(指向3)不變,則新的mid=1(指向12)。此時k>mid,故肯定應在後半段中查找。
3)令新的low=mid+1=2(指向24),而high=2(指向24)不變,則新的mid=2,此時k=arr[mid],查找成功。
若是要查找的數不是數列中的數,例如k=25,當第四次判斷時,k>mid[2],在後邊半段查找,令low=mid+1,即low=3(指向36),high=2(指向24)出現low>high的狀況,表示查找不成功。
Arrays類,是數組的工具類,包含不少數組有關的工具方法。如:
1)toString() 鏈接數組元素爲字符串,方便數組內容輸出。
2)equals 比較兩個數組序列是否相等。
3)sort() 對數組進行排序,小到大排序。
4)binarySearch(names, "Tom") 二分查找,必須在有序序列上使用。
1)計算機中一切數據都是2進制的! 基本類型,對象,音頻,視頻。
2)10進制是人類習慣,計算按照人類習慣利用算法輸入輸出。
"10" -算法轉化-> 1010(2) 1010 -算法轉化-> "10"
3)16進制是2進制的簡寫,16進制就是2進制!
4)計算機硬件不支持正負號,爲了解決符號問題,使用補碼算法,補碼規定高位爲1則爲負數,每位都爲1則爲-1,如1111 1111 = -1 = 0xff
5)二進制數右移>>:至關於數學 / 2(基數),且正數高位補0,負數高位補1;二進制字左移<<:至關於數學 * 2(基數),且低位補0;二進制數無符號右移>>>:至關於數學 / 2(基數),且不論正負,高位都補0。
6)注意掩碼運算:把擴展後前面爲1的狀況除去,與0xff作與運算。
eg1:二進制計算
int max = 0x7fffffff; long l = max + max + 2; System.out.println( l );// 0
eg2:二進制運算(拼接與拆分)
int b1 = 192; int b2 = 168; int b3 = 1; int b4 = 10; int color = 0xD87455;
int ip = (b1<<24) + (b2<<16) + (b3<<8) + b4; // 或者ip = (b1<<24) | (b2<<16) | (b3<<8) | b4;
int b = color&0xff; // 85 int g = (color >>> 8)&0xff; // 116 int r = (color >>> 16)&0xff;// 216
vJava程序嚴格區分大小寫。
v類名,每一個單詞首字母必須大寫(公司規範!)。
v一個Java應用程序,有且只有一個main方法,做爲程序的入口點。
v每一條Java語句必須以分號結束。
v類定義關鍵字class前面能夠有修飾符(如public),若是前面的修飾符是public,該類的類名必需要與這個類所在的源文件名稱相同。
v注意程序的縮進。
vdouble a[] = new double[2]; //語法能夠,但企業中必定不要這麼寫,Java中[]建議放前面。
vJava中全部範圍參數都是包含0,不包含結束,如int n = random.nextInt(26); //生成0到26範圍內的隨機數,不包括26。
v任何數據在內存中都是2進制的數據,內存中沒有10進制16進制。
vint n = Integer.parseInt(str);//將字符串--> int 整數。
vSystem.out.println(Long.toBinaryString(maxL)); Long類型用Long.XXXX 。
v程序:數據+算法 數據即爲變量,算法爲數據的操做步驟,如:順序、選擇、循環。
v字符串按編碼大小排序。
Object:對象,東西,一切皆對象 = = 啥都是東西
面向對象核心:封裝、繼承、多態。
1)是同類型東西的概念,是對現實生活中事物的描述,映射到Java中描述就是class定義的類。類是對象的模板、圖紙,是對象的數據結構定義。簡單說就是「名詞」。
2)其實定義類,就是在描述事物,就是在定義屬性(變量)和方法(函數)。
3)類中能夠聲明:屬性,方法,構造器;屬性(變量)分爲:實例變量,局部變量;實例變量:用於聲明對象的結構的,在建立對象時候分配內存,每一個對象有一份!實例變量(對象屬性)在堆中分配,並做用於整個類中,實例變量有默認值,不初始化也能參與運算;局部變量在棧中分配,做用於方法或語句中,必須初始化,有值才能運算。
4)類與類之間的關係:①關聯:一個類做爲另外一個類的成員變量:須要另外一個類來共同完成。class A { pulic B b } class B {} ②繼承:class B extends A {} class A {} ③依賴:個別方法和另外一個類相關。class A { public void f(B b) {}//參數裏有B public B g() {}//返回值裏有B } class B {}
5)null與空指針異常:引用類型變量用於存放對象的地址,能夠給引用類型賦值爲null,表示不指向任何對象。當某個引用類型變量爲null時沒法對對象實施訪問(由於它沒有指向任何對象)。此時,若是經過引用訪問成員變量或調用方法,會產生NullPointerException空指針異常。
u注意事項:除了8中基本類型,其餘都是引用類型變量(也叫句柄)。
是這類事物實實在在存在的個體!利用類作爲模板建立的個體實例,本質是數據。
匿名對象:使用方式一:當對對象的方法只調用一次時,可用匿名對象來完成,這樣比較簡化。若是對一個對象進行多個成員調用,則必須給這個對象起個名字。
使用方式二:能夠將匿名對象做爲實際參數進行傳遞。
1)包名必須是小寫,多個單詞用「.」隔開。在同一個包中,不能有同名的類!
2)只要在同一個包中,則可直接用extends(類型互知道在哪),若不在同一個包中,則用import導入。
是用於對當前對象數據進行算法計算,實現業務功能。方法是對象的功能,對象的動做,對象的行爲。總之是動詞!方法名沒有規定,建議首單詞爲小寫動詞,其餘單詞首字母大寫。必須定義返回值!可有無參數方法。方法調用只有兩種方式:①對象引用調用②類名調用(即靜態類時)。
是對個體的標識名稱。
1)是代詞,是對象的引用,就像拴着對象的繩子。
2)引用自己不是對象!引用指代了對象!
3)引用的值是對象的地址值(或叫句柄),經過地址值引用了對象。
4)引用的值不是對象!
u注意事項:「.」叫取成員運算,能夠理解爲「的」。
封裝:將數據封裝到類的內部,將算法封裝到方法中。
1)封裝原則:將不須要對外提供的內容都隱藏起來,把屬性都隱藏,提供公共方法對其訪問,一般有兩種訪問方式:set 設置,get 獲取。
2)封裝結果:存在可是不可見。
3)public:任何位置可見,能夠修飾:類、成員屬性、成員方法、內部類、跨包訪問類(須要使用import語句導入),成員屬性 = = 成員變量。
4)protected:當前包中可見,子類中可見。能夠修飾:成員屬性、成員方法、內部類(只能在類體中使用,不能修飾類)。
5)默認的:當前包內部可見,就是沒有任何修飾詞,能夠修飾:類、成員屬性、成員方法、內部類,但在實際項目中不多使用。默認類(包內類)的訪問範圍:當前包內部可見,不能在其餘包中訪問類,訪問受限!main方法若定在默認類中JVM將找不到,沒法執行,所以一定在public類中。
6)private:僅僅在類內部可見。能夠修飾:成員屬性、成員方法、內部類(只能在類體中使用,不能修飾類)。私有的方法不能繼承,也不能重寫。
u注意事項:在企業項目中建議:全部類都是公用類。封裝的類使用內部類!
用於建立對象並初始化對象屬性的方法,叫「構造方法」,也叫「構造器」;構造器在類中定義。
1)構造器的名稱必須與類名同名,包括大小寫。
2)構造器沒有返回值,但也不能寫void,也不能寫return。
3)構造器的參數:通常是初始化對象的前提條件。
4)用new調用!且對象一創建,構造器就運行且僅運行一次。通常方法可被調用屢次。
5)類必定有構造器!這是真的,不須要質疑!
6)若是類沒有聲明(定義)任何的構造器,Java編譯器會自動插入默認構造器!
7)默認構造是無參數,方法體是空的構造器,且默認構造器的訪問權限隨着所屬類的訪問權限變化而變化。如,若類被public修飾,則默認構造器也帶public修飾符。
8)默認構造器是看不到的,一旦本身寫上構造器則默認構造器就沒有了,本身寫的叫自定義構造器,即使本身寫的是空參數的構造器,也是自定義構造器,而不是默認構造器。
9)若是類聲明瞭構造器,Java編譯器將再也不提供默認構造器。若沒手動寫出無參構造器,但卻調用了無參構造器,將會報錯!
eg:默認構造器
public class Demo { public static void main(String[] args) {
Foo foo = new Foo();//調用了javac自動添加的默認構造器!
//Koo koo = new Koo();//編譯錯誤,沒有Koo()構造器
Koo koo = new Koo(8); } }
class Foo { } //Foo有構造器,有無參數的默認構造器!
class Koo { public Koo(int a) { //聲明瞭有參數構造器
System.out.println("Call Koo(int)"); }}
10)構造器是能夠重載的,重載的目的是爲了使用方便,重載規則與方法重載規則相同。
11)構造器是不能繼承的!雖然說是叫構造方法,但實際上它不是常說的通常方法。
12)子類繼承父類,那麼子類型構造器默認調用父類型的無參數構造器。
13)子類構造器必定要調用父類構造器,若是父類沒有無參數構造器,則必須使用super(有參數的),來調用父類有參的構造器。 那麼,爲何子類必定要訪問父類的構造器?
由於父類中的數據子類能夠直接獲取。因此子類對象在創建時,須要先查看父類是如何對這些數據進行初始化的,因此子類在對象初始化時,要先訪問一下父類中的構造器。
總之,子類中至少會有一個構造器會訪問父類中的構造器,且子類中每個構造函數內的第一行都有一句隱式super()。
1)this:在運行期間,哪一個對象在調用this所在的方法,this就表明哪一個對象,隱含綁定到當前「這個對象」。
2)super():調用父類無參構造器,必定在子類構造器第一行使用!若是沒有則是默認存在super()的!這是Java默認添加的super()。
3)super.是訪問父類對象,父類對象的引用,與this.用法一致
4)this():調用本類的其餘構造器,按照參數調用構造器,必須在構造器中使用,必須在第一行使用,this() 與 super() 互斥,不能同時存在
5)this.是訪問當前對象,本類對象的引用,在能區別實例變量和局部變量時,this可省略,不然必定不能省!
6)若是子父類中出現非私有的同名成員變量時,子類要訪問本類中的變量用this. ;子類要訪問父類中的同名變量用super. 。
eg1:方法參數傳遞原理 與 this關鍵字
eg2:this. 和 this()
Cell c = new Cell(); System.out.println(c.x + ","+c.y);
class Cell { int x; int y;
public Cell() { this(1,1);//調用本類的其餘構造器 }
public Cell( int x, int y) { this.x = x ; this.y = y; } }
eg3:super()
class Xoo{ public Xoo(int s) { System.out.println("Call Xoo(int)"); }} //super()用於在子類構造器中調用父類的構造器
class Yoo extends Xoo{
//public Yoo() {}//編譯錯誤,子類調用不到父類型無參數構造器 public Yoo(){//super();//編譯錯誤,子類調用不到父類型無參數構造器
super(100);//super(100) 調用了父類 Xoo(int) 構造器 } }
1)重寫:經過類的繼承關係,因爲父類中的方法不能知足新的要求,所以須要在子類中修改從父類中繼承的方法叫重寫(覆蓋)。
①方法名、參數列表、返回值類型與父類的如出一轍,但方法的實現不一樣。若方法名、參數列表相同,但返回值類型不一樣會有變異錯誤!若方法名、返回值類型相同,參數列表不一樣,則不叫重寫了。
②子類若繼承了抽象類或實現了接口,則必須重寫所有的抽象方法。若沒有所有實現抽象方法,則子類還是一個抽象類!
③子類重寫抽象類中的抽象方法或接口的方法時,訪問權限修飾符必定要大於或等於被重寫的抽象方法的訪問權限修飾符!
④靜態方法只能重寫靜態方法!
2)重載:方法名同樣,參數列表不一樣的方法構成重載的方法(多態的一種形式)。
①調用方法:根據參數列表和方法名調用不一樣方法。
②與返回值類型無關。
③重載遵循所謂「編譯期綁定」,即在編譯時根據參數變量的類型判斷應調用哪一個方法。 eg:重載
int[] ary1 = {'A','B','C'}; char[] ary2 = {'A', 'B', 'C'};
System.out.println(ary1);//println(Object)
//按對象調用,結果爲地址值,沒有println(int[])
System.out.println(ary2);//println(char[]) ABC
System.out.println('中');//println(char) 中
System.out.println((int)'中');//println(int) 20013
父子概念的繼承:圓繼承於圖形,圓是子概念(子類型 Sub class)圖形是父類型(Super Class也叫超類),繼承在語法方面的好處:子類共享了父類的屬性和方法的定義,子類複用了父類的屬性和方法,節省了代碼。
1)繼承是is a :「是」我中的一種,一種所屬關係。
2)子類型對象能夠賦值給父類型變量(多態的一種形式),變量是代詞,父類型代詞能夠引用子類型東西。
3)繼承只能是單繼承,即直接繼承,而非間接繼承。由於多繼承容易帶來安全隱患,當多個父類中定義了相同功能,當功能內容不一樣時,子類沒法肯定要運行哪個。
4)父類不能強轉成子類,會造型異常!子類向父類轉化是隱式的。
5)只有變量的類型定義的屬性和方法才能被訪問!見下例。
6)重寫遵循所謂「運行期綁定」,即在運行的時候根據引用變量指向的實際對象類型調用方法。
eg:Shape s,s只能訪問Shape上聲明的屬性和方法
Circle c = new Circle(3,4,5);
Shape s = c;//父類型變量s引用了子類型實例
//s 和 c引用了同一個對象new Circle(3,4,5)
s.up(); System.out.println(c.r);
System.out.println(c.area());
//System.out.println(s.area());//編譯錯誤
//System.out.println(s.r);//在Shape上沒有定義r屬性!
7)引用類型變量的類型轉換 instanceof
public static void main(String[] args) {
Circle c = new Circle(3,4,5); Rect r = new Rect(3,4,5,6);
Shape s = c; Shape s1 = r;
//Circle x = s;//編譯錯誤,父類型變量不能賦值給子類型
Circle x = (Circle)s;//正常執行
//Circle y = (Circle)s1;//運行異常,類型轉換異常
//instaceof instace:實例 of:的
//instaceof 運算 檢查變量引用的對象的類型是否兼容
//s引用的是圓對象,s instanceof Circle 檢查s引用的對象是不是Circle類型的實例!
System.out.println(s instanceof Circle);//true
System.out.println(s1 instanceof Circle);//false
test(c); test(r); }
public static void test(Shape s){//多態的參數
//if(s instanceof Circle)保護了(Circle)s不會出現異常
if(s instanceof Circle){//實現了安全的類型轉換
Circle c = (Circle) s; System.out.println("這是一個圓, 面積"+c.area()); }
if(s instanceof Rect){
Rect r = (Rect) s; System.out.println("這是一個矩形, 面積"+r.area()); } }
8)繼承時候對象的建立過程
①Java首先遞歸加載全部類搭配方法區。②分配父子類型的內存(實例變量)。③遞歸調用構造器。
9)重寫方法與重載方法的調用規則
10)屬性綁定到變量的類型,由變量類型決定訪問哪一個屬性;方法動態綁定到對象,由對象的類型決定訪問哪一個方法。(強轉對方法動態綁定到對象無影響,由於強轉的是父類的引用,而實例是沒變的,只是把實例看成另外一個狀態去看而已。可是強轉對屬性動態綁定到變量類型有影響。)其餘解釋請看多態部分!
eg1:方法動態綁定到運行期間對象的方法實例1
eg2:方法動態綁定到運行期間對象的方法 實例2
11)爲什麼查閱父類功能,建立子類對象使用功能?
Java中支持多層繼承,也就是一個繼承體系。想要使用體系,先查閱父類的描述,由於父類中定義的是該體系中共性的功能,經過了共性功能,就能夠知道該體系的基本功能,那麼這個體系已經能夠基本使用了,然而在具體調用時,要建立最(低)子類的對象,緣由以下:①由於父類有可能不能建立對象②建立子類對象,能夠使用更多的功能,包括基本的也包括特有的。
12)屬性無繼承概念,因此你有你的,我有個人,各自調用各自的,不影響,即便子父類中有同名屬性也無影響。
eg:子父類同名屬性無影響
class Base { public static final String FOO="foo"; public static void main(String[] args){
Base b=new Base(); Sub s=new Sub();
Base.FOO;//foo b.Foo; //foo Sub.Foo;//bar s.Foo;//bar
( (Base) s . Foo);// foo } }
class Sub extends Base{ public static final String FOO="bar"; }
靜態的,只能在類內部使用,能夠修飾:屬性,方法,內部類。在類加載期間初始化,存在方法區中。
1)靜態成員隨着類的加載而加載,加載於方法區中,且優先於對象存在。
2)靜態修飾的成員:屬於類級別的成員,是全體類實例(全部對象)所共享。
3)靜態屬性:只有一份(而實例變量是每一個對象有一份),全體實例共享,相似於全局變量。
4)使用類名訪問靜態變量,以及類名直接調用方法,不須要建立對象。
5)靜態方法只能訪問靜態成員(靜態屬性和靜態方法),非靜態方法既可訪問靜態,也可訪問非靜態。
6)靜態方法中沒有隱含參數this,所以不能訪問當前對象資源。也不能定義this和super關鍵字,由於靜態優於對象先存在。
7)非靜態方法省略的是this,靜態方法省略的是類名(在同一類中),即直接使用屬性和方法。
8)靜態方法通常用於與當前對象無關工具方法,工廠方法。如:Math.sqrt() Arrays.sort()
9)靜態代碼塊:隨着類的加載而執行(用到類的內容才叫加載,只有引用是不加載的),且只執行一次,且優先於主函數,用於給類初始化。
10)代碼塊(構造代碼塊):給全部對象進行統一初始化,且優先於構造器執行;而構造器是給對應的對象進行初始化。
eg:靜態代碼塊與代碼塊(構造代碼塊)
class Goo{
{//代碼塊(構造代碼塊),在建立對象時候執行!相似於構造器的做用
System.out.println("HI");
}
static{//靜態代碼塊,在類的加載期間執行,只執行一次
System.out.println("Loading Goo.class");
}
}
public static void main(String[] args) {
Point p1 = new Point(3,4); Point p2 = new Point(6,8);
//在對象上調用方法,當前對象隱含傳遞給隱含參數this
System.out.println(p1.distance(p2));//distance(p1,p2)
double d = Point.distance(p1, p2); System.out.println(d); //5
//靜態方法調用時候不傳遞隱含的當前對象參數 }
class Point{ int x; int y;
public Point(int x, int y) { this.x = x; this.y = y; }
//靜態方法中沒有隱含參數this!在靜態方法中不能訪問this的屬性和方法!
public static double distance(Point p1, Point p2){
int a = p1.x -p2.x; int b = p1.y -p2.y; return Math.sqrt(a*a + b*b); }
/** 計算當前點(this)到另一個點(other)的距離 */
public double distance(/*Point this*/ Point other){
int a = this.x - other.x; int b = this.y - other.y;
double c = Math.sqrt(a*a + b*b); return c; } }
11)對象的建立過程及順序:
Person P = new Person( 「chang」 , 23) ; 這句話都作了什麼事情?
①由於new用到了Person.class,因此會先找到Person.class文件加載到內存中。
②執行該類中的static代碼塊(若是有的話),給Person類.class類進行初始化。
③在堆內存中開闢空間,分配內存地址,棧內存中開闢空間也就有了。
④在堆內存中創建對象的特有屬性,並進行默認(隱式)初始化。
⑤對屬性進行顯式初始化。
⑥對對象進行構造代碼塊初始化。
⑦對對象進行對應的構造器初始化。
⑧將內存地址賦給棧內存中的P變量。
最終的,能夠修飾:類、方法、變量(成員變量和局部變量)。
1)final 修飾的類:不能再繼承。
2)final修飾的方法:不能再重寫。
3)final 的方法和類,阻止了動態代理模式!動態代理模式普遍的應用在: Spring Hibernate Struts2
4)企業編程規範:不容許使用final 的方法和類!
5)final的變量:final變量只能初始化一次(賦值一次,且方法中不能有給final變量賦值的語句!由於方法可被調用屢次!),不能再修改!也可在方法的參數列表中添加final。
eg1:final的局部變量
final int a; a = 5;//第一次叫初始化!不是賦值 //a = 8;//編譯錯誤
public static void test(final int a, int b){
//a++;//編譯錯誤,不能再修改
System.out.println(a); }
eg2:final的數組
final String[] ary={"A","B"}; //ary:數組變量,ary[0]數組元素
ary[0]="Tom";//數組元素能夠修改
//ary=null;//數組變量不能修改
eg3:final的實例變量
public static void main(String[] args) {
Dog d1 = new Dog(); Dog d2 = new Dog();
//d1.id = 8;//每一個實例的id 不能夠再修改
System.out.println(d1.id+","+d2.id+","+Dog.numOfDogs); }
class Dog{ final int id;//實例變量,每一個對象一份,不能再次修改
static int numOfDogs=0;//靜態,只有一份
public Dog() { id = numOfDogs++; } }
6)static final 共同修飾的叫常量,常量:public static final double PI = 3.14; PI 是直接數的代名詞,是名字。字面量(==直接量):直接寫出數值 3.1415926535897 宏觀說:字面量和常量都稱爲常量!
繼承體現了多態:父類型變量能夠引用各類各樣的子類型實例,也可接收子類對象。
個體的多態:父類型的子類型實例是多種多樣的。
行爲的多態:父類型定義方法被子類重寫爲多種多樣的,重載也是多態的方法。
1)千萬不能出現將父類對象轉成子類類型,會造型異常!
2)多態前提:必須是類與類之間有關係。要麼繼承,要麼實現。一般還有一個前提:存在覆蓋。
3)多態的好處:多態的出現大大的提升程序的擴展性。
4)多態的弊端:雖然提升了擴展性,可是隻能使用父類的引用訪問父類中的成員。
5)在多態中成員函數的特色:
①在編譯時期:參閱引用型變量所屬的類中是否有調用的方法。若是有,編譯經過,若是沒有編譯失敗。
②在運行時期:參閱對象所屬的類中是否有調用的方法。
③簡單總結就是:成員方法在多態調用時,編譯看左邊,運行看右邊。
6)在多態中,成員變量的特色:不管編譯和運行,都參考左邊(引用型變量所屬的類)。
7)在多態中,靜態成員方法和屬性的特色:不管編譯和運行,都參考作左邊。
8)父類引用指向子類對象,當父類想使用子類中特有屬性、方法時,要向下轉型。
抽象就是將擁有共同方法和屬性的對象提取出來,提取後,從新設計一個更加通用、更加大衆化的類,就叫抽象類。
1)abstract關鍵字能夠修飾類、方法,即抽象類和抽象方法。
2)抽象類能夠有具體的方法,或者所有都是具體方法,但一個類中只要有一個抽象方法,那麼這個類就是抽象類,而且必須用abstract修飾類。
3)抽象類能夠被繼承,則子類必須實現抽象類中的所有抽象方法,不然子類也將是抽象類。抽象類也可主動繼承實體類。
4)抽象類不能實例化,即不能用new生成實例。
5)能夠聲明一個抽象類型的變量並指向具體子類的對象。
6)抽象類能夠實現接口中的方法。
7)抽象類中能夠不定義抽象方法,這樣作僅僅是不讓該類創建對象。
interface 差很少 = = abstract class
1)接口是like a :「像」我中的一種,是繼承體系以外的,用於功能擴展!想擴展就實現,不想就不用實現。
2)接口中只能聲明抽象方法和常量且聲明格式都是固定的,只不過能夠省略。
eg:接口中聲明常量和抽象方法的格式是固定的
interface Runner {
/*public abstract final*/int SPEED=100;//聲明常量
/*public abstract 省略了,寫也對*/void run();//聲明抽象方法
}
3)接口中的成員不寫修飾符時,默認都是public。
4)接口不能有構造器,由於不能實例化何以初始化,接口只能被「實現」。
5)具體類實現了一個接口,則必須實現所有的抽象方法,若沒有所有實現,則該類爲抽象類。因此說,接口約定了具體類的方法,約定了類的外部行爲。
6)具體類能夠同時實現多個接口,就是多繼承現象。
7)多重繼承:class Cat implements Hunter , Runner Cat 便是Hunter 也是 Runner。
8)接口用 implements 表實現,實際是繼承關係,可有多個接口(實現),繼承用 extends 只能有一個繼承關係。
9)一個類既能夠繼承的同時,又「實現」接口:class A extends B implements C , D
10)類與類之間是繼承關係,類與接口之間是實現關係,接口與接口之間是繼承關係,且只有接口之間能夠多繼承,即:interface A{},interface B{},interface C extends A , B 但接口多繼承時要注意,要避免A、B接口中有方法名相同、參數列表相同,但返回值類型不相同的狀況,由於被具體類實現時,不肯定調用哪一個方法。
11)abstract class和interface有什麼區別。
①從語法角度:abstract class方法中能夠有本身的數據成員,也能夠有非abstract的成員方法,並賦予方法的默認行爲,而在interface方式中通常不定義成員數據變量,全部的方法都是abstract,方法不能擁有默認的行爲。
②從編程的角度:abstract class在java語言中表示的是一種繼承關係,一個類只能使用一次繼承關係。而一個類能夠實現多個interface。
③從問題域角度:abstract class在Java語言中體現了一種繼承關係,要想使得繼承關係合理,父類和派生類之間必須存在"is a"關係,即父類和派生類在概念本質上應該是相同的。對於interface 來講則否則,並不要求interface的實現者和interface定義在概念本質上是一致的,僅僅是實現了interface定義的契約而已。
當描述事物時,事物的內部還有事物,該事物用內部類來描述。由於內部事物在使用外部事物的內容。
在類內部定義的類爲成員內部類,在方法裏定義的類爲局部內部類,被static修飾的爲靜態內部類。一個類中可有多個內部類。
1)內部類主要用於,封裝一個類的聲明在類的內部,減小類的暴露。
2)內部類的實例化:實例化時不須要出寫對象,非要寫的話爲:new 外部類名.內部類名();而不是外部類名.new 內部類名()。
3)內部類的訪問規則:內部類能夠直接訪問外部類中的成員,包括私有。之因此能夠直接訪問外部類中的成員,是由於內部類中持有了一個外部類的引用,格式:外部類名.This即下面第4條。外部類要訪問內部類,必須創建內部類對象。
4)當內部類定義在外部類的成員位置上,並且非私有,則在外部其餘類中能夠直接創建內部類對象。格式:外部類名.內部類名 變量名 = 外部類對象.內部類對象;
Outer.Inner in = new Outer().new Inner();
5)當內部類在成員位置上,就能夠被成員修飾符所修飾。好比private:將內部類在外部類中進行封裝。
6)靜態內部類:被static修飾後就具有了靜態的特性。當內部類被static修飾後,只能直接訪問外部類中的static成員,出現了訪問侷限。
①在外部其餘類中,如何直接訪問static內部類的非靜態成員呢?
new Outer.Inner().function();
②在外部其餘類中,如何直接訪問static內部類的靜態成員呢?
Outer.Inner.function();
u注意事項:
v當內部類中定義了靜態成員,該內部類必須是static的。
v當外部類中的靜態方法訪問內部類時,內部類也必須是static的。
7)內部類想調用外部類的成員,須要使用:外部類名.this.成員,即OutterClassName.this表示外部類的對象。若是寫this.成員= =成員,調用的仍是內部類的成員(屬性或方法)。
8)Timer 和 TimerTask:繼承TimerTask 重寫run()方法,再用Timer類中的schedule方法定時調用,就能自動啓用run()(不像之前似的要用 .XXX 調用)。
eg:內部類
class Xoo{ Timer timer = new Timer();
public void start(){
timer.schedule(new MyTask(), 0, 1000);//0表示當即開始,無延遲
timer.schedule(new StopTask(), 1000*10);//在10秒之後執行一次 }
class StopTask extends TimerTask{
public void run() { timer.cancel(); }//取消timer上的任務 }
class MyTask extends TimerTask {
int i=10; public void run() { System.out.println(i--); } }
匿名內部類==匿名類
1)匿名內部類的格式: new 父類或者接口(){定義子類的內容};如new Uoo(){}就叫匿名內部類!是繼承於Uoo類的子類或實現Uoo接口的子類,而且同時建立了子類型實例,其中{}是子類的類體,能夠寫類體中的成員。
2)定義匿名內部類的前提:內部類必須是繼承一個類或者實現接口。
3)匿名內部類沒有類名,其實匿名內部類就是一個匿名子類對象。並且這個對象有點胖。能夠理解爲帶內容的對象。
4)在匿名內部類中只能訪問final局部變量。
5)匿名內部類中定義的方法最好不要超過3個。
eg1:匿名內部類的建立
public static void main(String[] args) {
Uoo u = new Uoo();//建立Uoo實例Uoo u1 = new Uoo(){};//建立匿名內部類實例
Uoo u2 = new Uoo(){
public void test() {//方法的重寫 System.out.println("u2.test()"); } };
u2.test();//調用在匿名內部類中重寫的方法。
// new Doo();編譯錯誤,不能建立接口實例
Doo doo = new Doo(){//實現接口,建立匿名內部類實例
public void test() {//實現接口中聲明的抽象方法
System.out.println("實現test"); } };
doo.test();//調用方法
}
interface Doo{ void test(); }
class Uoo{ public void test(){} }
eg2:匿名內部類中只能訪問final局部變量
final Timer timer=new Timer();
timer.schedule(new TimerTask(){
public void run(){ timer.cancel();//在匿名內部類中只能訪問final局部變量
}
}, 1000*10);
6)nonymous Inner Class (匿名內部類) 是否能夠extends(繼承)其它類?是否能夠implements(實現)interface(接口)?
匿名內部類是能夠繼承其它類,一樣也能夠去實現接口的,用法爲:
這樣的用法在swing編程中是常用的,就是由於它須要用到註冊監聽器機制,而該監聽類若是隻服務於一個組件,那麼將該類設置成內部類/匿名類是最方便的。
二維數組(假二維數組),Java中沒有真正的二維數組!Java二維數組是元素爲數組的數組。
對象數組:元素是對象(元素是對象的引用)的數組。
Point[] ary;// 聲明瞭數組變量ary
ary = new Point[3];// 建立了數組對象
// new Point[3]實際狀況:{null,null,null}
// 數組元素自動初始化爲null,並不建立元素對象!
System.out.println(ary[1]);// null
ary[0] = new Point(3, 4); ary[1] = new Point(5, 6); ary[2] = new Point(1, 2);
System.out.println(ary[1]);// 默認調用了對象的toString()
System.out.println(ary[1].toString());//結果上面的同樣
//toString是Object類定義,子類繼承的方法
//在輸出打印對象的時候,會默認調用,重寫這個方法能夠打印的更好看!
System.out.println(Arrays.toString(ary));//輸出3個對象
System.out.println(ary.toString());//地址值
int[] c={1,3,5,7};
System.out.println(c[1]);
//System.out.println(c[1].toString());//錯誤,不能在int類型調用toString()
1)Java 文件規則:
一個Java源文件中能夠有多個類,但只能有一個公有類!其餘類只能是默認類(包中類)並且Java的文件夾必定與公有類類名一致!若是沒有公有類,能夠和任何一個文件名一致。
u通常建議:一個文件一個公有類!通常不在一個文件中寫多個類
2)JVM內存結構堆、棧和方法區分別存儲的內容:
JVM會在其內存空間中開闢一個稱爲「堆」的存儲空間,這部分空間用於存儲使用new關鍵字建立的對象。
棧用於存放程序運行過程中全部的局部變量。一個運行的Java程序從開始到結束會有屢次方法的調用。JVM會爲每個方法的調用在棧中分配一個對應的空間,這個空間稱爲該方法的棧幀。一個棧幀對應一個正在調用中的方法,棧幀中存儲了該方法的參數、局部變量等數據。當某一個方法調用完成後,其對應的棧幀將被清除。
方法區該空間用於存放類的信息。Java程序運行時,首先會經過類裝載器載入類文件的字節碼信息,通過解析後將其裝入方法區。類的各類信息都在方法區保存。
在Java繼承體系中,java.lang.Object類位於頂端(是全部對象的直接或間接父類)。若是一個類沒有寫extends關鍵字聲明其父類,則該類默認繼承java.lang.Object類。Object類定義了「對象」的基本行爲,被子類默認繼承。
1)toString方法:返回一個能夠表示該對象屬性內容的字符串。
MyObject obj=new MyObject(); String info=obj.toString(); System.out.println(info);
A.上例爲何我有toString方法?
由於全部的類都繼承自Object,而toString方法是Ojbect定義的,咱們直接繼承了這個方法。Object的toString方法幫咱們返回一個字符串,這個字符串的格式是固定的:類型@hashcode,這個hashcode是一串數字,在java中叫句柄,或叫地址(但不是真實的物理地址,是java本身的一套虛擬地址,防止直接操做內存的)。
public String toString(){//只能用public,重寫的方法訪問權限要大於等於父類中方法的權限
return "這個是咱們本身定義的toString方法的返回值MyObject!"; }
B.上例爲何要重寫toString方法?
toString定義的原意是返回可以描述當前這個類的實例的一串文字,咱們看一串hashcode沒意義,因此幾乎是要重寫的。
public static void main(String[] args){ //System.out.println(toString());//不行!編譯錯誤!
Point p=new Point(1,2); System.out.println(p);//輸出p對象的toString方法返回值 }
C.上例爲什麼有編譯錯誤?
不能直接使用toString方法,由於該方法不是靜態的。ava語法規定:靜態方法中不能直接引用非靜態的屬性和方法,想引用必需建立對象。非靜態方法中能夠直接引用靜態屬性和方法。
2)equals方法:用於對象的「相等」邏輯。
A.在Object中的定義: public boolean equals(Object obj){ return (this==obj); }
因而可知,this==obj與直接的==(雙等於)效果同樣,僅僅是根據對象的地址(句柄,那個hashcode值)來判斷對象是否相等。所以想比較對象與給定對象內容是否一致,則必須重寫equals方法。
B.「==」與equals的區別:
用「==」比較對象時,描述的是兩個對象是否爲同一個對象!根據地址值判斷。而equals方法力圖去描述兩個對象的內容是否相等,內容相等取決於業務邏輯須要,能夠自行定義比較規則。
C.equals方法的重寫:如,判斷兩點是否相等。
public boolean equals(Object obj){//注意參數
/**若給定的對象obj的地址和當前對象地址一致,那麼他們是同一個對象,equals方法中有大量的內容比較邏輯時,加上這個判斷會節省性能的開銷!*/
if(this == obj){ return true; }
/** equals比較前要進行安全驗證,確保給定的對象不是null!若obj是null,說明該引用變量沒有指向任何對象,那麼就不能引用ojb所指象的對象(由於對象不存在)的屬性和方法!若這麼作就會引起NullPointException,空指針異常!*/
if(obj == null){ return false; }
/**直接將Object轉爲子類是存在風險的!咱們不能保證Object和咱們要比較的對象是同一個類型的這會引起ClassCastException!咱們稱爲:類造型異常。*/
/**重寫equals時第一件要作的事情就是判斷給定的對象是否和當前對象爲同一個類型,不是同類型直接返回false,由於不具有可比性!*/
if(!(obj instanceof Point)){ return false; }
Point p=(Point)obj;
/**不能隨便把父類轉成子類,由於Object是全部類的父類,任何類型均可以傳給它因此不能保證obj傳進來的就是相同類型(點類型)*/
return this.x==p.x && this.y==p.y;//內容比較邏輯定義 }
是字符串類型,是引用類型,是「不可變」字符串,無線程安全問題。在java.lang.String中。
u注意事項:String str =「abc」;和String str=new String(「abc」);的區別!
1)String在設計之初,虛擬機就對他作了特殊的優化,將字符串保存在虛擬機內部的字符串常量池中。一旦咱們要建立一個字符串,虛擬機先去常量池中檢查是否建立過這個字符串,若有則直接引用。String對象由於有了上述的優化,就要保證該對象的內容自建立開始就不能改變!因此對字符串的任何變化都會建立新的對象,而不是影響之前的對象!
2)String的equals方法:兩個字符串進行比較的時候,咱們一般使用equals方法進行比較,字符串重寫了Object的equals方法,用於比較字符串內容是否一致。雖然java虛擬機對字符串進行了優化,可是咱們不能保證任什麼時候候「==」都成立!
3)編程習慣:當一個字符串變量和一個字面量進行比較的時候,用字面量.equals方法去和變量進行比較,即:if("Hello".equals(str))由於這樣不會產生空指針異常。而反過來用,即:if(str.equals("Hello"))則咱們不能保證變量不是null,若變量是null,咱們在調用其equals方法時會引起空指針異常,致使程序退出。若都爲變量則if(str!=null&&str.equals(str1))也可。
4)String另外一個特有的equals方法:euqalsIgnoreCase,該方法的做用是忽略大小寫比較字符串內容,經常使用環境:驗證碼。if("hello".equalsIgnoreCase(str))。
5)String的基本方法:
①String toLowerCase():返回字符串的小寫形式。如:str.toLowerCase()
②String toUpperCase():返回字符串的大寫形式。如:str.toUpperCase()
③String trim():去掉字符串兩邊的空白(空格\t\n\r),中間的不去。如:str.trim()
④boolean startsWith():判斷字符串是否以參數字符串開頭。如:str.startsWith("s")
⑤boolean endsWith():判斷字符串是否以參數字符串結尾。如:str.endsWith("s")
⑥int length():返回字符串字符序列的長度。如:str.length()
eg:如何讓HelloWorld這個字符串以hello開頭成立
if(str.toLowerCase().startsWith("hello"))//有返回值的才能繼續 . 先轉成小寫再判斷
6)indexOf方法(檢索):位置都是從0開始的。
①int indexOf(String str):在給定的字符串中檢索str,返回其第一次出現的位置, 找不到則返回-1。
②int indexOf(String str,int from):在給定的字符串中從from位置開始檢索str,返 回其第一次出現的位置,找不到則返回-1(包含from位置,from以前的不看)。
eg:查找Think in Java中in後第一個i的位置
index=str.indexOf("in"); index=str.indexOf("i",index+"in".length());
//這裏對from參數加in的長度的目的是 從in以後的位置開始查找
③int lastIndexOf(String str):在給定的字符串中檢索str,返回其最後一次 出現的 位置,找不到則返回-1(也可認爲從右往左找,第一次出現的位置)。
④int lastIndexOf(String str,int from):在給定的字符串中從from位置開始檢索str, 返回其最後一次出現的位置,找不到則返回-1(包含from位置,from以後的不看)。
7)charAt方法:char charAt(int index):返回字符串指定位置(index)的字符。
eg:判斷是不是迴文:上海自來水來自海上
boolean tf=true; for(int i=0;i<str2.length()/2;i++){
//char first=str2.charAt(i); //char last=str2.charAt(str2.length()-i-1);
if(str2.charAt(i)!=str2.charAt(str2.length()-i-1)){//優化
tf = false; break;//已經不是迴文了,就沒有必要再繼續檢查了 } }
8)substring方法(子串):字符串的截取,下標從0開始的。
①String substring(int start,int end):返回下標從start開始(包含)到end結束的字 符串(不包含)。
②String substring(int start):返回下標從start開始(包含)到結尾的字符串。
9)getBytes方法(編碼):將字符串轉換爲相應的字節。
①byte[] getBytes():以當前系統默認的字符串編碼集,返回字符串所對應的二進制 序列。如:byte[] array=str.getBytes(); System.out.println(Arrays.toString(array));
②byte[] getBytes(String charsetName):以指定的字符串編碼集,返回字符串所對應 的二進制序列。這個重載方法須要捕獲異常,這裏可能引起沒有這個編碼集的異常, UnsupportedEncodingException,如:str="常"; byte[] bs=info.getBytes("UTF-8");
u注意事項:
vWindows的默認編碼集GBK:英文用1個字節描述,漢字用2個字節描述;ISO-8859-1歐洲經常使用編碼集:漢字用3個字節描述;GBK國標;GB2312國標;UTF-8編碼集是最經常使用的:漢字用3個字節描述。
v編碼:將數據以特定格式轉換爲字節;解碼:將字節以特定格式轉換爲數據。
vString(byte[] bytes, String charsetName)
:經過使用指定的charset解碼指定的byte數組,構造一個新的String。如:String str=new String(bs,"UTF-8");
10)split方法(拆分):字符串的拆分。
String[] split(String regex):參數regex爲正則表達式,以regex所表示的字符串爲 分隔符,將字符串拆分紅字符串數組。其中,regex所表示的字符串不被保留,即 不會存到字符串數組中,可理解爲被一刀切,消失!
eg:對圖片名從新定義,保留圖片原來後綴
String name="me.jpg"; String[] nameArray=name.split("\\.");
//以正則表達式拆分 .有特殊含義,因此用\\. 轉義
System.out.println("數組長度:"+nameArray.length);//若是不用\\.則長度爲0
System.out.println(Arrays.toString(nameArray));//任意字符都切一刀,都被切沒了
String newName="123497643."+nameArray[1];
System.out.println("新圖片名:"+newName);
u注意事項:分隔符放前、中都沒事,放最後將把無效內容都忽略。
String str="123,456,789,456,,,";
String[] array=str.split(",");//分隔符放前、中都沒事,放最後將把無效內容都忽略
System.out.println(Arrays.toString(array));//[123, 456, 789, 456]
11)replace方法:字符串的替換。
String replaceAll(String regex,String replacement):將字符串中匹配正則表達式regex 的字符串替換成replacement。如:String str1=str.replaceAll("[0-9]+", "chang");
12)String.valueOf()方法:重載的靜態方法,用於返回各種型的字符串形式。
String.valueOf(1);//整數,返回字符串1 String.valueOf(2.1);//浮點數,返回字符串1.2
針對字符串操做的工具類,提供了一系列靜態方法,在Apache阿帕奇Commons-lang包下中,需下載。
StringUtils經常使用方法:
1)String repeat(String str,int repeat):重複字符串repeat次後返回。
2)String join(Object[] array,String):將一個數組中的元素鏈接成字符串。
3)String leftPad(String str,int size,char padChar):向左邊填充指定字符padChar,以達到指定長度size。
4)String rightPad(String str,int size,char padChar):向右邊填充指定字符padChar,以達到指定長度size。
與String對象不一樣,StringBuilder封裝「可變」的字符串,有線程安全問題。對象建立後,可經過調用方法改變其封裝的字符序列。
StringBuilder經常使用方法:
1)追加字符串:StringBuilder append(String str):
2)插入字符串:StringBuilder insert(int index,String str):插入後,原內容依次後移
3)刪除字符串:StringBuilder delete(int start,int end):
4)替換字符串:StringBuilder replace(int start,int end,String str):含頭不含尾
5)字符串反轉:StringBuilder reverse():
eg:各種操做
StringBuilder builder=new StringBuilder();
builder.append("你們好!") .append("好好學習") .append("每天向上");
//返回的仍是本身:builder,因此能夠再 .
System.out.println(builder.toString());
builder.insert(4, "!"); System.out.println(builder.toString());
builder.replace(5,9,"Good Good Study!"); System.out.println(builder.toString());
builder.delete(9, builder.length()); System.out.println(builder.toString());
u注意事項:
v該類用於對某個字符串頻繁的編輯操做,使用StringBuilder能夠在大規模修改字符串時,不開闢新的字符串對象,從而節約內存資源,因此,對有着大量操做字符串的邏輯中,不該使用String而應該使用StringBuilder。
vappend是有返回值的,返回類型是StringBuilder,而返回的StringBuilder其實就是本身(this),append方法的最後一句是return this;
vStringBuilder與StringBuffer區別:效果是同樣的。
StringBuilder是線程不安全的,效率高,需JDK1.5+。
StringBuffer是線程安全的,效率低,「可變」字符串。
在多線程操做的狀況下應使用StringBuffer,由於StringBuffer是線程安全 的,他不免要顧及安全問題,而進行必要的安全驗證操做。因此效率上要 比StringBuilder低,根據實際狀況選擇。
實際開發中,常常須要對字符串數據進行一些複雜的匹配、查找、替換等操做,經過正則表達式,能夠方便的實現字符串的複雜操做。
正則表達式是一串特定字符,組成一個「規則字符串」,這個「規則字符串」是描述文本規則的工具,正則表達式就是記錄文本規則的代碼。
[] |
表示一個字符 |
[abc] |
表示a、b、c中任意一個字符 |
[^abc] |
除了a、b、c的任意一個字符 |
[a-z] |
表示a到z中的任意一個字符 |
[a-zA-Z0-9_] |
表示a到z、A到Z、0到9以及下滑線中的任意一個字符 |
[a-z&&[^bc]] |
表示a到z中除了b、c以外的任意一個字符,&&表示「與」的關係 |
. |
表示任意一個字符 |
\d |
任意一個數字字符,至關於[0-9] |
\D |
任意一個非數字字符,至關於[^0-9] |
\s |
空白字符,至關於[\t\n\f\r\x0B] |
\S |
非空白字符,至關於[^\s] |
\w |
任意一個單詞字符,至關於[a-zA-Z0-9_] |
\W |
任意一個非單詞字符,至關於[^\w] |
^ |
表示字符串必須以其後面約束的內容開始 |
$ |
表示字符串必須以其前面約束的內容結尾 |
? |
表示前面的內容出現0到1次 |
* |
表示前面的內容出現0到屢次 |
+ |
表示前面的內容出現1到屢次 |
{n} |
表示前面的字符重複n次 |
{n,} |
表示前面的字符至少重複n次 |
{n,m} |
表示前面的字符至少重複n次,而且小於m次 X>=n && X<m |
u注意事項:
v郵箱格式的正則表達式 @無特殊含義,可直接寫,也可[@]
v使用Java字符串去描述正則表達式的時候,會出現一個衝突,即如何正確描述正則表達式的「.」。
原由:在正則表達式中咱們想描述一個「.」,但「.」在正則表達式中有特殊含義,他表明任意字符,因此咱們在正則表達式中想描述「.」的願義就要寫成「\.」可是咱們用java字符串去描述正則表達式的時候,由於「.」在java字符串中沒有特殊意義,因此java認爲咱們書寫String s="\.";是有語法錯誤的,由於「.」不須要轉義,這就產生了衝突。
處理:咱們實際的目的很簡單,就是要讓java的字符串描述"\."又由於在java中"\"是有特殊含義的,表明轉義字符咱們只須要將"\"轉義爲單純的斜槓,便可描述"\."了因此咱們用java描述「\.」的正確寫法是String s="\\.";
v若正則表達式不書寫^或$,正則表達式表明匹配部份內容,都加上則表示權匹配
eg:測試郵箱正則表達式:Pattern的做用是描述正則表達式的格式支持,使用靜態方法compile註冊正則表達式,生成實例。
String regStr="^[a-zA-Z0-9_]+@[a-zA-Z0-9]+(\\.com|\\.cn|\\.net)+$";
Pattern pattern=Pattern.compile(regStr);//註冊正則表達式
String mailStr="chang_2013@chang.com.cn";
//匹配字符串,返回描述匹配結果的Matcher實例
Matcher matcher=pattern.matcher(mailStr);
//經過調用Matcher的find方法得知是否匹配成功
if(matcher.find()){ System.out.println("郵箱匹配成功!"); }
else{ System.out.println("郵箱格式錯誤!"); }
java.util.Date類用於封裝日期及時間信息,通常僅用它顯示某個日期,不對他做任何操做處理,做處理用Calendar類,計算方便。
Date date=new Date();//建立一個Date實例,默認的構造方法建立的日期表明當前系統時間
System.out.println(date);//只要輸出的不是類名@hashcode值,就說明它重寫過toString()
long time=date.getTime();//查看date內部的毫秒值
date.setTime(time+1000*60*60*24);//設置毫秒數讓一個時間Date表示一天後的當前時間
int year=date.getYear();//畫橫線的方法不建議再使用:一、有千年蟲問題。二、不方便計算
java.util.Calendar類用於封裝日曆信息,其主做用在於其方法能夠對時間份量進行運算。
1)經過Calendar的靜態方法獲取一個實例該方法會根據當前系統所在地區來自行決定時區,幫咱們建立Calendar實例,這裏要注意,實際上根據不一樣的地區,Calendar有若干個子類實現。而Calendar自己是抽象類,不能被實例化!咱們不須要關心建立的具體實例爲哪一個子類,咱們只須要根據Calendar規定的方法來使用就能夠了。
2)日曆類所解決的根本問題是簡化日期的計算,要想表示某個日期還應該使用Date類描述。Calendar是能夠將其描述的時間轉化爲Date的,咱們只須要調用其getTime()方法就能夠獲取描述的日期的Date對象了。
3)經過日曆類計算時間:爲日曆類設置時間,日曆類設置時間使用通用方法set。
set(int field,int value),field爲時間份量,Calendar提供了相應的常量值,value爲對應的值。
4)只有月份從0開始:0爲1月,以此類推,11爲12月,其餘時間是正常的從1開始。也能夠使用Calendar的常量 calendar.NOVEMBER……等.
5)Calendar.DAY_OF_MONTH 月裏邊的天---號;
Calendar.DAY_OF_WEEK 星期裏的天---星期幾
Calendar.DAY_OF_YEAR 年裏的天
Calendar calendar=Calendar.getInstance();//構造出來表示當前時間的日曆類
Date now=calendar.getTime();//獲取日曆所描述的日期
calendar.set(Calendar.YEAR, 2012);//設置日曆表示2012年
calendar.set(Calendar.DAY_OF_MONTH,15);//設置日曆表示15號
calendar.add(Calendar.DAY_OF_YEAR, 22);//想獲得22天之後是哪天
calendar.add(Calendar.DAY_OF_YEAR, -5);//5天之前是哪天
calendar.add(Calendar.MONTH, 1);獲得1個月後是哪天
System.out.println(calendar.getTime());
6)獲取當前日曆表示的日期中的某個時間單位能夠使用get方法.
int year=calendar.get(Calendar.YEAR);
int month=calendar.get(Calendar.MONTH);
int day=calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(year+"年"+(month+1)+"月"+day+"日");//month要處理
java.text.SimpleDateFormat類,日期轉換類,該類的做用是能夠很方便的在字符串和日期類之間相互轉換。
1)這裏咱們在字符串與日期類間相互轉換是須要一些約束的,"2012-02-02"這個字符串如何轉換爲Date對象?Date對象又如何轉爲字符串?
parse方法用於按照特定格式將表示時間的字符串轉化成Date對象。
format方法用於將日期數據(對象)按照指定格式轉爲字符串。
2)經常使用格式字符串
字符 |
含義 |
示例 |
y |
年 |
yyyy年-2013年;yy年-13年 |
M |
月 |
MM月-01月;M月-1月 |
d |
日 |
dd日-06日;d日-6日- |
E |
星期 |
E-星期日(Sun) |
a |
AM或PM |
a-下午(PM) |
H |
24小時制 |
a h時-小午12時 HH:mm:ss-12:46:33 hh(a):mm:ss-12(下午):47:48 |
h |
12小時制 |
|
m |
分鐘 |
|
s |
秒 |
eg:字符串轉成Date對象
//建立一個SimpleDateFormat而且告知它要讀取的字符串格式
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
String dateFormat="2013-05-14";//建立一個日期格式字符串
//將一個字符串轉換爲相應的Date對象
Date date=sdf.parse(dateFormat);//要先捕獲異常
System.out.println(date);//輸出這個Date對象
eg:Date對象轉成字符串
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
Date now=new Date(); String nowStr=sdf.format(now);//把日期對象傳進去
3)在日期格式中 - 和 空格 無特殊意義,無特殊含義的都將原樣輸出。
eg:將時間轉爲特定格式
//將當前系統時間轉換爲2012/05/14 17:05:22的效果
SimpleDateFormat format1=new SimpleDateFormat("yyyy/MM/dd hh:mm:ss");
nowStr=format1.format(now); System.out.println(nowStr);
java.text.DateFormat類(抽象類)是SimpleDateFormat類的父類,用的少,沒SimpleDateFormat靈活。
建立用於將Date對象轉換爲日期格式的字符串的DateFormat,建立DateFormat對象的實例,使用靜態方法getDateInstance(style,aLocale),style爲輸出日期格式的樣式:DateFormat有對應的常量;aLocale爲輸出的地區信息,影響字符串的語言和表現形式。
Date now=new Date(); DateFormat format=DateFormat.getDateInstance( DateFormat.MEDIUM,Locale.CHINA);
Java語言的8種基本類型分別對應了8種「包裝類」。每一種包裝類都封裝了一個對應的基本類型成員變量,同時還提供了針對該數據類型的實用方法。
1)包裝類的目的:用於將基本類型數據看成引用類型看待。
2)包裝類的名字:除了Integer(int),Character(char)外,其他包裝類名字都是基本類型名首字母大寫。
3)拆、裝箱:Integer i=new Integer(1);建立一個以對象形式存在的整數1,這種從基本類型轉爲引用類型的過程稱之爲「裝箱」,反之叫「拆箱」。
4)裝箱:方式一:Double d=new Double(2.2);//裝箱
方式二:Double d=Double.valueOf(2.2);//基本類型都有valueOf方法
5)拆箱:double num=d.doubleValue();//拆箱
6)包裝類使用前提:JDK1.5+
public static void say(Object obj){ System.out.println(obj); }
int a=1;//基本類型,不是Object子類!
say(a);//在java 1.4版本的時候,這裏仍是語法錯誤的!由於int是基本類型,不是Object對象,要本身寫8種基本類型對應的方法
7)包裝類的使用:實例化一個對象,該對象表明整數1;Integer的做用是讓基本類型int做爲一個引用類型去看待。這樣就能夠參與到面向對象的編程方式了。由此咱們能夠將一個int看成一個Object去看待了,也成爲了Object的子類。
Integer i=new Integer(a);//裝箱,或者寫Integer i=new Integer(1);
Integer ii=Integer.valueOf(a);//裝箱另外一種方式
int num=i.intValue();//拆箱 say(i);//Integer是Object的子類,能夠調用!
8)JDK1.5包裝類自動拆裝箱(原理):在編譯源程序的時候,編譯器會預處理,將未做拆箱和裝箱工做的語句自動拆箱和裝箱。可經過反編譯器發現。
say(Integer.valueOf(a));自動裝箱 num=i;//引用類型變量怎麼能複製給基本類型呢?
//num=i.intValuse();//自動拆箱
9)包裝類的一些經常使用功能:將字符串轉換爲其類型,方法是:parseXXX,XXX表明其類型。這裏要特別注意!必定要保證待轉換的字符串描述的確實是或者兼容要轉換的數據類型!不然會拋出異常!
String numStr="123"; System.out.println(numStr+1);//1231
int num=Integer.parseInt(numStr); System.out.println(num+1)//124
long longNum=Long.parseLong(numStr); System.out.println(longNum);//123
double doubleNum=Double.parseDouble(numStr); System.out.println(doubleNum);//123.0
10)Integer提供了幾個有趣的方法:將一個整數轉換爲16進制的形式,並以字符串返回;將一個整數轉換爲2進制的形式,並以字符串返回。
String bStr=Integer.toBinaryString(num); String hStr=Integer.toHexString(num);
11)全部包裝類都有幾個共同的常:獲取最大、最小值。
int max=Integer.MAX_VALUE;//int最大值 int min=Integer.MIN_VALUE;//int最小值
System.out.println(Integer.toBinaryString(max)); System.out.println(Integer.toBinaryString(min));
表示精度更高的浮點型,在java.math.BigDecimal包下,該類能夠進行更高精度的浮點運算。須要注意的是,BigDecimal能夠描述比Double還要高的精度,因此在轉換爲基本類型時,可能會丟失精度!
1)BigDecimal的使用:建立一個BigDecimal實例,能夠使用構造方法BigDecimal(String numberFormatString)用字符串描述一個浮點數做爲參數傳入。
BigDecimal num1=new BigDecimal("3.0");
BigDecimal num2=new BigDecimal("2.9"); //運算結果依然爲BigDecimal表示的結果
BigDecimal result=num1.subtract(num2);//num1-num2 System.out.println(result);
float f=result.floatValue();//將輸出結果轉換爲基本類型float
int i=result.intValue();//將輸出結果轉換爲基本類型int
2)BigDecimal能夠做加add、減subtract、乘multiply、除divide等運算:這裏須要注意除法,因爲除法存在結果爲無限不循環小數,因此對於除法而言,咱們要制定取捨模式,不然會一直計算下去,直到報錯(內存溢出)。
result=num1.divide(num2,8,BigDecimal.ROUND_HALF_UP);
//小數保留8位,捨去方式爲四捨五入
使用描述更長位數的整數「字符串」,來表示、保存更長位數的整數,在java.math.BigInteger包下。
1)BigInteger的使用:建立BigInteger
BigInteger num=new BigInteger("1");//num=new BigInteger(1);不能夠,沒有這樣的構造器
//這種方式咱們能夠將一個整數的基本類型轉換爲BigInteger的實例
num=BigInteger.valueOf(1);
2)理論上:BigInteger存放的整數位數只受內存容量影響。
3)BigInteger一樣支持加add、減subtract、乘multiply、除divide等運算。
eg:1-200的階乘
for(int i=1;i<=200;i++){ num=num.multiply(BigInteger.valueOf(i)); }
System.out.println("結果"+num.toString().length()+"位"); System.out.println(num);
在實際開發中,須要將使用的對象存儲於特定數據結構的容器中。而JDK提供了這樣的容器——集合框架,集合框架中包含了一系列不一樣數據結構(線性表、查找表)的實現類。
1)Collection經常使用方法:
①int size():返回包含對象個數。 ②boolean isEmpty():返回是否爲空。
③boolean contains(Object o):判斷是否包含指定對象。
④void clear():清空集合。 ⑤boolean add(E e):向集合中添加對象。
⑥boolean remove(Object o):從集合中刪除對象。
⑦boolean addAll(Collection<? extends E > c):另外一個集合中的全部元素添加到集合
⑧boolean removeAll(Collection<?> c):刪除集合中與另一個集合中相同的原素
⑨Iterator<E> iterator():返回該集合的對應的迭代器
2)Collection和Collentions的區別
Collection是java.util下的接口,它是各類集合的父接口,繼承於它的接口主要有Set 和List;Collections是個java.util下的類,是針對集合的幫助類,提供一系列靜態方法實現對各類集合的搜索、排序、線程安全化等操做。
List接口是Collection的子接口,用於定義線性表數據結構,元素可重複、有序的;能夠將List理解爲存放對象的數組,只不過其元素個數能夠動態的增長或減小。
1)List接口的兩個常見的實現類:ArrayList和LinkedList,分別用動態數組和鏈表的方式實現了List接口。List、ArrayList和LinkedList均處於java.util包下。
2)能夠認爲ArrayList和LinkedList的方法在邏輯上徹底同樣,只是在性能上有必定的差異,ArrayList更適合於隨機訪問,而LinkedList更適合於插入和刪除,在性能要求不是特別苛刻的情形下能夠忽略這個差異。
ArrayList LinkedList
3)使用List咱們不須要在建立的時候考慮容量集合的容量是根據其所保存的元素決定的換句話說,集合的容量是能夠自動擴充的。
4)List的實現類會重寫toString方法,依次調用所包含對象的toString方法,返回集合中所包含對象的字符串表現。
5)經常使用方法:
①add(Object obj):向想集合末尾追加一個新元素,從該方法的參數定義不難看出,集合能夠存聽任意類型的元素,但在實際編程中咱們發現,幾乎不會向集合中存放一種以上的不一樣類型的元素。
②size()方法:返回當前集合中存放對象的數量。
③clear()方法:用於清空集合。
④isEmpty()方法:用於返回集合是否爲空。
List list=new ArrayList(); list.add("One"); list.add("Two"); list.add("Three");
//list.add(1);//不建議這樣的操做!儘可能不在同一個集合中存放不用類型元素
System.out.println("集合中元素的數量:"+list.size());
System.out.println(list);//System.out.println(list.toString());
//ArrayList重寫了toString()方法返回的字符串是每一個元素的toString()返回值的序列
list.clear();//清空 System.out.println("清空後元素的數量:"+list.size());
System.out.println("集合是否爲空?:"+list.isEmpty());
⑤contains(Object obj)方法:檢查給定對象是否被包含在集合中,檢查規則是將obj對象與集合中每一個元素進行equals比較,若比對了全部元素均沒有equals爲true的則返回false。
u注意事項:根據狀況重寫equals:若比較是不是同一個對象,則不須要重寫,直接用contains裏的equals比較便可。若重寫equals爲內容是否相同,則按內容比較,無論是否同一個對象。是否重寫元素的equals方法對集合的操做結果有很大的效果不一樣!
⑥boolean remove(Object obj)方法:刪除一個元素,不重寫equals,不會有元素被刪除(由於比較的是對象的地址,都不相同),重寫equals爲按內容比較,則刪除第一個匹配的就退出,其餘即便內容相同也不會被刪除。
List list=new ArrayList();//多態的寫法 //ArrayList arrayList=new ArrayList();//正常的寫法
list.add(new Point(1,2)); //Point point =new Point(1,2); list.add(point);等量代換
list.add(new Point(3,4)); list.add(new Point(5,6));
System.out.println("集合中元素的數量:"+list.size()); System.out.println(list);
Point p=new Point(1,2);//建立一個Point對象
System.out.println("p在集合中存在麼?"+list.contains(p));//不重寫爲false 重寫爲true
System.out.println("刪前元素:"+list.size());
list.remove(p);//將p對象刪除,不重寫equals,不會有元素被刪除
System.out.println("刪後元素:"+list.size()); System.out.println(list);
⑦E remove(int index)方法:移除此列表中指定位置上的元素。向左移動全部後續元素(將其索引減1)。所以在作刪除操做時集合的大小爲動態變化的,爲了防止漏刪,必須從後往前刪!
ArrayList list=new ArrayList(); list.add("java"); list.add("aaa");
list.add("java"); list.add("java"); list.add("bbb");
//相鄰的元素刪不掉!
for(int i=0;i<list.size();i++){ if( "java".equals(list.get(i))); list.remove(i); }
//能夠刪乾淨!
for(int i=list.size()-1;i>=0;i--){ if("java".equals(list.get(i))){ list.remove(i); }
⑧addAll(Collection c)方法:容許將c對應的集合中全部元素存入該集合,即並集。注意,這裏的參數爲Collection,因此換句話說,任何集合類型均可以將其元素存入其餘集合中!
⑨removeAll(Collection c)方法:刪除與另外一個集合中相同的元素。它的「相同」邏輯經過equals方法來判斷。
⑩retainAll(Collection c)方法:保留與另外一個集合中相同的元素,即交集。它的「相同」邏輯經過equals方法來判斷。
list1.addAll(list2);//並集
list1.removeAll(list3);//從list1中刪除list3中相同(equals爲true)的元素
list1.retainAll(list2);//保留list1中刪除list2中相同(equals爲true)的元素
⑪Object get(int index)方法:根據元素下標獲取對應位置的元素並返回,這裏元素的下標和數組類似。
⑫Object set(int index,Object newElement)方法:將index位置的元素修改成newElement修改後會將被修改的元素返回。所以,可實現將List中第i個和第j個元素交換的功能:list.set ( i , list.set ( j , list.get ( i ) ) ) ;
⑬add(int index,Object newElement)方法:使用add的重載方法,咱們能夠向index指定位置插入newElement,原位置的元素自動向後移動,即所謂的「插隊」。
⑭Object remove(int index)方法:將集合中下標爲index的元素刪除,並將被刪除的元素返回(不根據equals,根據下標刪除元素)。
List list=new ArrayList(); list.add("One"); list.add("Two"); list.add("Three");
//由於get方法是以Object類型返回的元素,因此須要造型,默認泛型Object
String element=(String)list.get(2);//獲取第三個元素 System.out.println(element);
for(int i=0;i<list.size();i++){//遍歷集合 System.out.println(list.get(i)); }
Object old=list.set(2, "三");
System.out.println("被替換的元素:"+old); System.out.println(list);
list.add(2, "二");//在Two與「三」之間插入一個「二」 System.out.println(list);
Object obj=list.remove(1); System.out.println("被刪除的元素:"+obj);
⑮indexOf(Object obj)方法:用於在集合中檢索對象,返回對象第一次出現的下標。
⑯lastIndexOf(Object obj)方法:用於在集合中檢索對象,返回對象最後一次出現的下標。
⑰Object[] toArray()方法:該方法繼承自Collection的方法,該方法會將集合以對象數組的形式返回。
⑱toArray()的重載方法,T[] toArray(T[] a):能夠很方便的讓咱們轉換出實際的數組類型,以下例,參數new Point[0]的做用是做爲返回值數組的類型,因此參數傳入的數組不須要有任何長度,由於用不到,就沒有必要浪費空間。
Object[] array=list.toArray();//將集合以對象數組的形式返回
for(int i=0;i<array.length;i++){ Point p=(Point)array[i]; System.out.println(p.getX()); }
Point[] array1=(Point[])list.toArray(new Point[0]);//toArray()的重載方法
for(int i=0;i<array1.length;i++){ Point p=array1[i];//不須要每次都強轉了
System.out.println(p.getX()); }
⑲List<E> subList(int fromIndex, int toIndex)方法:獲取子集合,但在獲取子集後,若對子集合的元素進行修改,則會影響原來的集合。
List<Integer> list=new ArrayList<Integer>();
for(int i=0;i<10;i++){ list.add(i); }
List<Integer> subList=list.subList(3, 8);//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 取子集(3-7)
System.out.println(subList);//[3, 4, 5, 6, 7]
//咱們在獲取子集後,若對本身元素進行修改,會影響原來的集合
for(int i=0;i<subList.size();i++){ int element=subList.get(i);
element*=10; subList.set(i, element);
//subList.set(i, subList.get(i)*10);同上三步 }
System.out.println(list);//原集合內容也被修改了
⑳Comparable接口:針對對象數組或者集合中的元素進行排序時,首選須要肯定對象元素的「比較」邏輯(即哪一個大哪一個小)。Comparable接口用於表示對象間的大小關係,咱們須要實現Comparable接口,並重寫compareTo()方法定義比較規則。
public int compareTo(ComparablePoint o){ int r=x*x+y*y;//自身點到原點的距離
int other=o.x*o.x+o.y*o.y;//參數點到原點的距離
//返回結果大於0,自身比參數大;小於0,自身比參數小;等於0,自身和參數相等; //equals返回true的時候,comparTo的返回值應該爲0
return r-other; }
21Collections.sort()方法:須要集合中的對象實現Comparable接口,從而能夠調用其compareTo方法判斷對象的大小,不然sort將沒法判斷。該方法會依次調用集合中每一個元素的compareTo方法,並進行天然排序(從小到大)。
List<ComparablePoint> list=new ArrayList<ComparablePoint>();
list.add(new ComparablePoint(1,5)); list.add(new ComparablePoint(3,4));
list.add(new ComparablePoint(2,2)); System.out.println(list);//輸出順序與存方時一致
Collections.sort(list); System.out.println(list);
22Comparator接口:比較器。一旦Java類實現了Comparable,其比較邏輯就已經肯定了,若是但願在排序中的操做按照「臨時指定規則」,即自定義比較規則。能夠採用Comparator接口回調方式。
Comparator比較器建立步驟:A.定義一個類並實現Comparator接口。B.實現接口中的抽象方法compare(E o1,E o2)。C.實例化這個比較器D.調用Collections的重載方法:sort(Collection c,Comparator comparator)進行排序。一般使用匿名類方式建立一個實例來定義比較器。
Comparator<ComparablePoint> c=new Comparator<ComparablePoint>(){
public int compare(ComparablePoint o1,ComparablePoint o2){
return o1.getX()-o2.getX();//兩個點的X值大的大 } };
Collections.sort(list, c); System.out.println(list);
全部Collection的實現類都實現了iterator方法,該方法返回一個Iterator接口類型的對象,用於實現對集合元素迭代的便利。在java.util包下。
1)Iterator定義有三個方法:
①boolean hasNext()方法:判斷指針後面是否有元素。
②E next()方法:指針後移,並返回當前元素。E表明泛型,默認爲Object類型。
③void remove()方法:在原集合中刪除剛剛返回的元素。
2)對於List集合而言,能夠經過基於下標的get方法進行遍歷;而iterator方法是針對Collection接口設計的,因此,全部實現了Collection接口的類,均可以使用Iterator實現迭代遍歷。
3)迭代器的使用方式:先問後拿。問:boolean hasNext()該方法詢問迭代器當前集合是否還有元素;拿:E next()該方法會獲取當前元素。迭代器的迭代方法是while循環量身定製的。
List list=new ArrayList(); list.add("One"); list.add("#");
Iterator it=list.iterator();
while(it.hasNext()){//集合中是否還有下一個元素
Object element=it.next();//有就將其取出
System.out.println(element); }
4)迭代器中的刪除問題:在迭代器迭代的過程當中,咱們不能經過「集合」的增刪等操做,來改變該集合的元素數量!不然會引起迭代異常!若想刪除迭代出來的元素,只能經過Iterator。迭代器在使用本身的remove()方法時,能夠將剛剛獲取的元素從集合中刪除,可是不能重複調用兩次!即在不迭代的狀況下,不能在一個位置刪兩次。
while(it.hasNext()){//集合中是否還有下一個元素
String element=(String)it.next();//有就將其取出,next返回值爲E(泛型)默認爲Object因此須要強轉
if("#".equals(element)){ //list.remove(element);不能夠!
it.remove();//刪除當前位置元素 } }
1)泛型是 JDK1.5引入的新特性,泛型的本質是參數化類型。在類、接口、方法的定義過程當中,所操做的數據類型爲傳入的指定參數類型。全部的集合類型都帶有泛型參數,這樣在建立集合時能夠指定放入集合中的對象類型。同時,編譯器會以此類型進行檢查。
2)ArrayList支持泛型,泛型尖括號裏的符號可隨便些,但一般大寫E。
3)迭代器也支持泛型,可是迭代器使用的泛型應該和它所迭代的集合的泛型類型一致!
4)泛型只支持引用類型,不支持基本類型,但能夠使用對應的包裝類
5)若是泛型不指定類型的話,默認爲Object類型。
ArrayList<Point> list=new ArrayList<Point>();
list.add(new Point(1,2)); list.add(new Point(3,4));
//list.add("哈哈");//定義泛型後,只運行Point類型,不然造型異常
for(int i=0;i<list.size();i++){ Point p=/*(Point)也不須要強轉造型了*/list.get(i);
System.out.println(p.getX()); }
Iterator<Point> it=list.iterator(); while(it.hasNext()){ Point p=it.next();
//也不須要強轉了 System.out.println(p); }
6)自定義泛型
Point p=new Point(1,2);//只能保存整數
//把Point類的int都改爲泛型E,或者也可設置多個泛型Point<E,Z>
Point<Double> p1=new Point<Double>(1.0,2.3);//設置一個泛型
Point<Double,Long> p2=new Point<Double,Long>(2.3,3L);//設置多個泛型
JDK在1.5版本推出了加強型for循環,能夠用於數組和集合的遍歷。
u注意事項:集合中要有值,不然直接退出(不執行循環)。
1)老循環:本身維護循環次數, 循環體自行維護獲取元素的方法。
int[] array=new int[]{1,2,3,4,5,6,7};
for(int i=0;i<array.length;i++){//維護循環次數
int element=array[i];//獲取數組元素 System.out.print(element); }
2)新循環:自動維護循環次數(由遍歷的數組或集合的長度決定),自動獲取每次迭代的元素。
int[] array=new int[]{1,2,3,4,5,6,7};
for(int element:array){ System.out.print(element); }
3)新循環執行流程:遍歷數組array中的每一個元素,將元素一次賦值給element後進入循環體,直到全部元素均被迭代完畢後退出循環。
u注意事項:使用新循環,element的類型應與循環迭代的數組或集合中的元素類型一致!至少要是兼容類型!
4)新循環的內部實現是使用迭代器完成的Iterator。
5)使用新循環遍歷集合:集合若使用新循環,應該爲其定義泛型,不然咱們只能使用Object做爲被接收元素時的類型。一般狀況下,集合都要加泛型,要明確集合中的類型,集合默認是Object。
ArrayList<String> list=new ArrayList<String>();
list.add("張三"); list.add("李四"); list.add("王五");
for(String str:list){//默認是Object本身加泛型String System.out.println(str); }
隊列(Queue)是經常使用的數據結構,能夠將隊列當作特殊的線性表,隊列限制了對線性表的訪問方式:只能從線性表的一端添加(offer)元素,從另外一端取出(poll)元素。Queue接口:在包java.util.Queue。
1)隊列遵循先進先出原則:FIFO(First Input First Output)隊列不支持插隊,插隊是不道德的。
2)JDK中提供了Queue接口,同時使得LinkedList實現了該接口(選擇LinkedList實現Queue的緣由在於Queue常常要進行插入和刪除的操做,而LinkedList在這方面效率較高)。
poll offer
3)經常使用方法:
①boolean offer(E e):將一個對象添加至隊尾,若是添加成功則返回true。
②poll():從隊列中取出元素,取得的是最先的offer元素,從隊列中取出元素後,該元素會從隊列中刪除。若方法返回null說明 隊列中沒有元素了。
③peek():獲取隊首的元素(不刪除該元素!)
eg:隊列相關操做
Queue<String> queue=new LinkedList<String>();
queue.offer("A"); queue.offer("B"); queue.offer("C");
System.out.println(queue);//[A, B, C]
System.out.println("隊首:"+queue.peek());//獲取隊首元素,但不令其出隊
String element=null; while((element=queue.poll())!=null){
System.out.println(element); }
棧(Deque)是經常使用的數據結構,是Queue隊列的子接口,所以LinkedList也實現了Deque接口。棧將雙端隊列限制爲只能從一端入隊和出隊,對棧而言便是入棧和出棧。子彈夾就是一種棧結構。在包java.util.Deque下。
1)棧遵循先進後出的原則:FILO(First Input Last Output)。
2)經常使用方法:
①push:壓入,向棧中存入數據。
②pop:彈出,從棧中取出數據。
③peek:獲取棧頂位置的元素,但不取出。
u注意事項:咱們在使用pop獲取棧頂元素以前,應現使用peek方法獲取該元素,肯定該元素不爲null的狀況下才應該將該元素從棧中彈出」,不然若棧中沒有元素後,咱們調用pop會拋出異常「NoSuchElementException」。
eg:棧相關操做
Deque<Character> deque=new LinkedList<Character>();
for(int i=0;i<5;i++){ deque.push((char)('A'+i)); }
System.out.println(deque);
//注意使用peek判斷棧頂是否有元素
while(deque.peek()!=null){ System.out.print(deque.pop()+" "); }
Set是無序,用於存儲不重複的對象集合。在Set集合中存儲的對象中,不存在兩個對象equals比較爲true的狀況。
1)HashSet和TreeSet是Set集合的兩個常見的實現類,分別用hash表和排序二叉樹的方式實現了Set集合。HashSet是使用散列算法實現Set的。
2)Set集合沒有get(int index)方法,咱們不能像使用List那樣,根據下標獲取元素。想獲取元素須要使用Iterator。
3)向集合添加元素也使用add方法,可是add方法不是向集合末尾追加元素,由於無序。
4)宏觀上講:元素的順序和存放順序是不一樣的,可是在內容不變的前提下,存放順序是相同的,但在咱們使用的時候,要看成是無序的使用。
Set<String> set=new HashSet<String>();//多態
//也可HashSet<String> set=new HashSet<String>();
set.add("One"); set.add("Two"); set.add("Three"); Iterator<String> it=set.iterator();
while(it.hasNext()){ String element=it.next(); System.out.print(element+" "); }
for(String element:set){ System.out.print(element+" "); }//新循環遍歷Set集合
5)hashCode對HashSet的影響:若咱們不重寫hashCode,那麼使用的就是Object提供的,而該方法是返回地址(句柄)!換句話說,就是不一樣的對象,hashCode不一樣。
6)對於重寫了equals方法的對象,強烈要求重寫繼承自Object類的hashCode方法的,由於重寫hashCode方法與否會對集合操做有影響!
7)重寫hashCode方法須要注意兩點:
①與equals方法的一致性,即equals比較返回爲true的對象其hashCode方法返回值應該相同。
②hashCode返回的數值應該符合hash算法要求,若是有不少對象的hashCode方法返回值都相同,則會大大下降hash表的效率。通常狀況下,能夠使用IDE(如Eclipse)提供的工具自動生成hashCode方法。
8)boolean contains(Object o)方法:查看對象是否在set中被包含。下例雖然有新建立的對象,可是經過散列算法找到了位置後,和裏面存放的元素進行equals比較爲true,因此依然認爲是被包含的(重寫equals了時)。
Set<Point> set=new HashSet<Point>(); set.add(new Point(1,2));
set.add(new Point(3,4)); System.out.println(set.contains(new Point(1,2)));
9)HashCode方法和equals方法都重寫時對hashSet的影響:將兩個對象同時放入HashSet集合,發現存在,再也不放入(不重複集)。當咱們重寫了Point的equals方法和hashCode方法後,咱們發現雖然p1和p2是兩個對象,可是當咱們將他們同時放入集合時,p2對象並無被添加進集合。由於p1在放入後,p2放入時根據p2的hashCode計算的位置相同,且p2與該位置的p1的equals比較爲true, hashSet認爲該對象已經存在,因此拒絕將p2存入集合。
Set<Point> set=new HashSet<Point>();
Point p1=new Point(1,2); Point p2=new Point(1,2);
System.out.println("二者是否同一對象:"+(p1==p2));
System.out.println("二者內容是否同樣:"+p1.equals(p2));
System.out.println("二者HashCode是否同樣:"+ (p1.hashCode()==p2.hashCode()));
set.add(p1); set.add(p2); System.out.println("hashset集合的元素數"+set.size());
for(Point p:set){ System.out.println(p); }
10)不重寫hashCode方法,可是重寫了equals方法對hashSet的影響:兩個對象均可以放入HashStet集合中,由於兩個對象具備不用的hashCode值,那麼當他們在放入集合時,經過hashCode值進行的散列算法結果就不一樣。那麼他們會被放入集合的不一樣位置,位置不相同,HashSet則認爲它們不一樣,因此他們能夠所有被放入集合。
11)重寫了hashCode方法,可是不重寫equals方法對hashSet的影響:在hashCode相同的狀況下,在存放元素時,他們會在相同的位置,hashSet會在相同位置上將後放入的對象與該位置其餘對象一次進行equals比較,若不相同,則將其存入在同一個位置存入若干元素,這些元素會被放入一個鏈表中。由此能夠看出,咱們應該儘可能使得多種類的不一樣對象的hashcode值不一樣,這樣才能夠提升HashSet在檢索元素時的效率,不然可能檢索效率還不如List。
12)結論:不一樣對象存放時,不會保存hashCode相同而且equals相同的對象,缺一不可。不然HashSet不認爲他們是重複對象。
Map接口定義的集合又稱爲查找表,用於存儲所謂「Key-Value」鍵值對。Key能夠當作是Value的索引。而每每Key是Value的一部份內容。
1)Key不能夠重複,但所保存的Value能夠重複。
2)根據內部結構的不一樣,Map接口有多種實現類,其中經常使用的有內部爲hash表實現的HashMap和內部爲排序二叉樹實現的TreeMap。一樣這樣的數據結構在存放數據時,也不建議存放兩種以上的數據類型,因此,一般咱們在使用Map時也要使用泛型約束存儲內容的類型。
3)建立Map時使用泛型,這裏要約束兩個類型,一個是key的類型,一個是value的類型。
4)基本原理圖:
5)HashMap集合中經常使用的方法:
①V put(K Key,V value):將元素以Key-Value的形式放入map。若重複保存相同的key時,實際的操做是替換Key所對應的value值。
②V get(Object key):返回key所對應的value值。若是不存在則返回null。
③boolean containsKey(Object Key):判斷集合中是否包含指定的Key。
④boolean containsValue(Object value):判斷集合中是否包含指定的Value。
6)若給定的key在map中不存在則返回null,因此,原則上在從map中獲取元素時要先判斷是否有該元素,以後再使用,避免空指針異常的出現。Map在獲取元素時很是有針對性,集合想獲取元素須要遍歷集合內容,而Map不須要,你只要給他特定的key就能夠獲取該元素。
Map<String,Point> map=new HashMap<String,Point>();
map.put("1,2", new Point(1,2)); map.put("3,4", new Point(3,4));
Point p=map.get("1,2"); System.out.println("x="+p.getX()+",y="+p.getY());
map.put("1,2", new Point(5,6));//會替換以前的
p=map.get("1,2"); System.out.println("x="+p.getX()+",y="+p.getY());
p=map.get("haha"); System.out.println("x="+p.getX()+",y="+p.getY());//會報空指異常
eg:統計每一個數字出現的次數。步驟:①將字符串str根據「,」拆分。②建立map。③循環拆分後的字符串數組。④蔣梅一個數字做爲key在map中檢查是否包含。⑤包含則對value值累加1。⑥不包含則使用該數字做爲key,value爲1存入map。
String str="123,456,789,456,789,225,698,759,456";
String[] array=str.split(",");
Map<String,Integer> map=new HashMap<String,Integer>();
for(String number:array){ if(map.containsKey(number)){
int sum=map.get(number);//將原來統計的數字取出 sum++; //對統計數字加1
map.put(number, sum); //放回 map.put(number, map.get(number)+1);等同上三部
}else{ map.put(number, 1);//第一次出現value爲1 } }
System.out.println(map);//HashMap也重寫了toString()
7)計算機中有這麼一句話:越靈活的程序性能越差,顧及的多了。
8)遍歷HashMap方式一:獲取全部的key並根據key獲取value從而達到遍歷的效果(即迭代Key)。keySet()方法:是HashMap獲取全部key的方法,該方法能夠獲取保存在map下全部的key並以Set集合的形式返回。
Map<String,Point> map=new HashMap<String,Point>();
map.put("1,2", new Point(1,2)); map.put("2,3", new Point(2,3));
map.put("3,4", new Point(3,4)); map.put("4,5", new Point(4,5));
/** 由於key在HashMap的泛型中規定了類型爲String,因此返回的Set中的元素也是String,爲了更好的使用,咱們在定義Set類型變量時也應該加上泛型 */
Set<String> keyset=map.keySet();
for(String key:keyset){ Point p=map.get(key);//根據key獲取value
System.out.println(key+":"+p.getX()+","+p.getY()); }
for(Iterator<String> it=keyset.iterator() ; it.hasNext() ; ){//普通for循環
String key=it.next(); Point p=map.get(key);
System.out.println(key+":"+p.getX()+","+p.getY()); }
9)LinkedHashMap:用法和HashMap相同,內部維護着一個鏈表,能夠使其存放元素時的順序與迭代時一致。
10)Entry類,遍歷HashMap方式二:以「鍵值對」的形式迭代。Map支持另外一個方法entrySet():該方法返回一個Set集合,裏面的元素是map中的每一組鍵值對,Map以Entry類的實例來描述每個鍵值對。其有兩個方法:getKey()獲取key值;getValue()獲取value值。Entry也須要泛型的約束,其約束的泛型應該和Map相同!Entry所在位置:java.util.Map.Entry。
Map<String,Point> map=new LinkedHashMap<String,Point>();
map.put("1,2", new Point(1,2)); map.put("2,3", new Point(2,3));
map.put("3,4", new Point(3,4)); map.put("4,5", new Point(4,5)); //泛型套泛型
Set<Entry<String,Point>> entrySet=map.entrySet();//Set的泛型不會變,就是Entry
for(Entry<String,Point> entry:entrySet){
String key=entry.getKey();//獲取key Point p=entry.getValue();//獲取value
System.out.println(key+","+p.getX()+","+p.getY()); }
11)List、Map、Set三個接口存儲元素時各有什麼特色:
①List:是有序的Collection,使用此接口可以精確的控制每一個元素插入的位置。用戶可以使用索引(元素在List中的位置,相似於數組下標)來訪問List中的元素,這相似於Java的數組。
②Set:是一種不包含重複的元素的Collection,即任意的兩個元素e1和e2都有e1.equals(e2)=false,Set最多有一個null元素。
③Map:請注意,Map沒有繼承Collection接口,Map提供key到value的映射。
設計模式是一套被反覆使用、多數人知曉的、通過分類編目的、代碼設計經驗的總結。
1)使用設計模式是爲了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。簡單的說:設計模式是經典問題的模式化解決方法。
2)經典設計模式分爲三種類型,共23類。
建立模型式:單例模式、工廠模式等
結構型模式:裝飾模式、代理模式等
行爲型模式:模版方法模式、迭代器模式等
3)單例設計模式:意圖:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。適用性:當類只能有一個實例並且客戶能夠從一個衆所周知的訪問點訪問它。任何狀況下,該類只能建立一個實例!
4)單例設計模式建立步驟:①定義一個私有的靜態的當前類型的屬性。②私有化構造方法。③定義一個靜態的能夠獲取當前類實例的方法。這個方法中咱們能夠判斷是否建立過實例,建立過就直接返回,從而達到單例的效果。
private static DemoSingleton obj;
//或private static DemoSingleton obj=new DemoSingleton();
private DemoSingleton(){ }
public static DemoSingleton getInstance(){
if(obj==null){ obj= new DemoSingleton(); }
return obj; }
5)模版方法模式:意圖:定義一個操做中的算法過程的框架,而將一些步驟延遲到子類中實現。相似於定義接口或抽象類,子類去實現抽象方法。
異常結構中的父類Throwable類,其下子類Exceptionlei類和Error類。咱們在程序中能夠捕獲的是Exception的子類異常。
Error系統級別的錯誤:Java運行時環境出現的錯誤,咱們不可控。
Exception是程序級別的錯誤:咱們可控。
1)異常處理語句:try-catch,若是try塊捕獲到異常,則到catch塊中處理,不然跳過忽略catch塊(開發中,必定有解決的辦法才寫,沒法解決就向上拋throws)。
try{//關鍵字,只能有一個try語句
可能發生異常的代碼片斷
}catch(Exception e){//列舉代碼中可能出現的異常類型,可有多個catch語句
當出現了列舉的異常類型後,在這裏處理,並有針對性的處理
}
2)良好的編程習慣,在異常捕獲機制的最後書寫catch(Exception e)(父類,頂極異常)捕獲未知的錯誤(或不須要針對處理的錯誤)。
3)catch的捕獲是由上至下的,因此不要把父類異常寫在子類異常的上面,不然子類異常永遠沒有機會處理!在catch塊中能夠使用方法獲取異常信息:
①getMessage()方法:用來獲得有關異常事件的信息。
②printStackTrace()方法:用來跟蹤異常事件發生時執行堆棧的內容。
4)throw關鍵字:用於主動拋出一個異常
當咱們的方法出現錯誤時(不必定是真實異常),這個錯誤咱們不該該去解決,而是通知調用方法去解決時,會將這個錯誤告知外界,而告知外界的方式就是throw異常(拋出異常)catch語句中也可拋出異常。雖然不解決,但要捕獲,而後拋出去。
使用環境:
咱們常在方法中主動拋出異常,但不是什麼狀況下咱們都應該拋出異常。原則上,自身決定不了的應該拋出。那麼方法中何時該本身處理異常何時拋出?
方法一般有參數,調用者在調用咱們的方法幫助解決問題時,一般會傳入參數,若咱們方法的邏輯是由於參數的錯誤而引起的異常,應該拋出,如果咱們自身的緣由應該本身處理。
public static void main(String[] args) {
try{/**一般咱們調用方法時須要傳入參數的話,那麼這些方法,JVM都不會自動處理異常,而是將錯誤拋給咱們解決*/
String result=getGirlFirend("女神"); System.out.println("追到女神了麼?"+result);
}catch(Exception e){
System.out.println("沒追到");//咱們應該在這裏捕獲異常並處理。
}
}
public static String getGirlFirend(String name){
try{ if("春哥".equals(name)){ return "行";
}else if("曾哥".equals(name)){ return "行";
}else if("我女友".equals(name)){ return "不行";
}else{/**當出現了錯誤(不必定是真實異常)能夠主動向外界拋出一個異常!*/
throw new RuntimeException("人家不幹!");
}
}catch(NullPointerException e){
throw e;//出了錯不解決,拋給調用者解決
}
}
5)throws關鍵字:不但願直接在某個方法中處理異常,而是但願調用者統一處理該異常。聲明方法的時候,咱們能夠同時聲明可能拋出的異常種類,通知調用者強制捕獲。就是所謂的「醜話說前面」。原則上throws聲明的異常,必定要在該方法中拋出。不然沒有意義。相反的,若方法中咱們主動經過throw拋出一個異常,應該在throws中聲明該種類異常,通知外界捕獲。
u注意事項:
v注意throw和throws關鍵字的區別:拋出異常和聲明拋出異常。
v不能在main方法上throws,由於調用者JVM直接關閉程序。
public static void main(String[] args) {
try{ Date today=stringToDate("2013-05-20"); } catch (ParseException e){
//catch中必須含有有效的捕獲stringToDate方法throws的異常
// 輸出此次錯誤的棧信息能夠直觀的查看方法調用過程和出錯的根源
e.printStackTrace(); } }
eg:將一個字符串轉換爲一個Date對象,拋出的異常是字符格式錯誤java.text.ParseException
public static Date stringToDate(String str) throws ParseException{
SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-DD");
Date date=format.parse(str); return date; }
6)捕獲異常兩種方式:上例SimpleDataFormat的parse方法在聲明的時候就是用了throws,強制咱們調用parse方法時必須捕獲ParseException,咱們的作法有兩種:一是添加try-catch捕獲該異常,二是在咱們的方法中聲明出也追加這種異常的拋出(繼續往外拋)。
7)java中拋出異常過程:java虛擬機在運行程序時,一但在某行代碼運行時出現了錯誤,JVM會建立這個錯誤的實例,並拋出。這時JVM會檢查出錯代碼所在的方法是否有try捕獲,如有,則檢查catch塊是否有能夠處理該異常的能力(看可否把異常實例做爲參數傳進去,看有沒有匹配的異常類型)。若沒有,則將該異常拋給該方法的調用者(向上拋)。以此類推,直到拋至main方法外仍沒有解決(即拋給了JVM處理)。那麼JVM會終止該程序。
8)java中的異常Exception分爲:
①非檢測異常(RuntimeException子類):編譯時不檢查異常。若方法中拋出該類異常或其子類,那麼聲明方法時能夠不在throws中列舉該類拋出的異常。常見的運行時異常有: NullPointerException、 IllegalArgumentException、
ClassCastException、NumberFormatException、
ArrayIndexOutOfBoundsException、ArithmeticException
②可檢測異常(非RuntimeException子類):編譯時檢查,除了運行時異常以外的異常,都是可檢查異常,則必須在聲明方法時用throws聲明出可能拋出的異常種類!
9)finally塊:finally塊定義在catch塊的最後(全部catch最後),且只能出現一次(0-1次), 不管程序是否出錯都會執行的塊! 無條件執行!一般在finally語句中進行資源的消除工做,如關閉打開的文件,刪除臨時文件等。
public static void main(String[] args) {
System.out.println( test(null)+","+test("0")+","+test("") ); }
/**輸出結果?1,0,2 ? 4,4,4爲正確結果 */
public static int test(String str){
try{ return str.charAt(0)-'0';
}catch(NullPointerException e){ return 1;
}catch(RuntimeException e){ return 2;
}catch(Exception e){ return 3;
}finally{//無條件執行 return 4; } }
10)重寫方法時的異常處理
若是使用繼承時,在父類別的某個地方上宣告了throws某些異常,而在子類別中從新定義該方法時,能夠:①不處理異常(從新定義時不設定throws)。②可僅throws父類別中被從新定義的方法上的某些異常(拋出一個或幾個)。③可throws被從新定義的方法上的異常之子類別(拋出異常的子類)。
但不能夠:①throws出額外的異常。 ②throws被從新定義的方法上的異常之父類別(拋出了異常的父類)。
java使用File類(java.io.File)表示操做系統上文件系統中的文件或目錄。換句話說,咱們能夠使用File操做硬盤上的文件或目錄進行建立或刪除。
File能夠描述文件或目錄的名字,大小等信息,但不能對文件的內容操做!File類的構造器都是有參的。
1)關於路徑的描述:不一樣的文件系統差別較大,Linux和Windows就不一樣!最好使用相對路徑,不要用絕對路徑。
2)「.」表明的路徑:當前目錄(項目所處的目錄),在eclipse_workspace/project_name下,File.separator:常量,目錄分隔符,推薦使用!根據系統自動識別用哪一種分割符,windows中爲/,Linux中爲\。
3)建立該對象並不意味着硬盤上對應路徑上就有該文件了,只是在內存中建立了該對象去表明路徑指定的文件。固然這個路徑對應的文件可能根本不存在!
File file=new File("."+File.separator+"data.dat");// 效果爲./data.dat
//File file=new File("e:/XX/XXX.txt");不建議使用
4)createNewFile()中有throws聲明,要求強制捕獲異常!
5)新建文件或目錄:
①boolean mkdir():只能在已有的目錄基礎上建立目錄。
②boolean mkdirs():會建立全部必要的父目錄(不存在的自動建立)並建立該目錄。
③boolean createNewFile():建立一個空的新文件。
6)建立目錄中文件的兩種方式:
①直接指定data.dat須要建立的位置,並調用createNewFile(),前提是目錄都要存在!
②先建立一個File實例指定data.dat即將存放的目錄,若該目錄不存在,則建立全部不存在的目錄,再建立一個File實例,表明data.dat文件,建立是基於上一個表明目錄的File實例的。使用File(File dir,String fileName)構造方法建立File實例,而後再調用createNewFile():在dir所表明的目錄中表示fileName指定的文件
File dir=new File("."+File.separator+"demo"+File.separator+"A");
if(!dir.exists()){ dir.mkdirs();//不存在則建立全部必須的父目錄和當親目錄 }
File file=new File(dir,"data.dat");
if(!file.exists()){file.createNewFile();System.out.println("文件建立完畢!"); }
7)查看文件或目錄屬性經常使用方法
①long length():返回文件的長度。
②long lastModified():返回文件最後一次被修改的時間。
③String getName():返回文件或目錄名。 ⑧String getPath():返回路徑字符串。
④boolean exists():是否存在。⑨boolean isFile():是不是標準文件。
⑤boolean isDirectory():是不是目錄。 ⑩boolean canRead():是否能夠讀取。
⑥boolean canWrite():是否能夠寫入、修改。
⑦File[] listFiles():獲取當親目錄的子項(文件或目錄)
eg1:File類相關操做
File dir=new File("."); if(dir.exists()&&dir.isDirectory()){//是否爲一個目錄
File[] files=dir.listFiles();//獲取當前目錄的子項(文件或目錄)
for(File file:files){//循環子項
if(file.isFile()){//若這個子項是一個文件
System.out.println("文件:"+file.getName());
}else{ System.out.println("目錄:"+file.getName()); } } }
eg2:遞歸遍歷出全部子項
File dir=new File("."); File[] files=dir.listFiles(); if(files!=null&&files.length>0){//判斷子項數組有項
for(File file:files){//遍歷該目錄下的全部子項
if(file.isDirectory()){//若子項是目錄
listDirectory(file);//不到萬不得已,不要使用遞歸,很是消耗資源
}else{System.out.println("文件:"+file);//有路徑顯示,輸出File的toString()
//file.getName()無路徑顯示,只獲取文件名 } } }
8)刪除一個文件:boolean delete():①直接寫文件名做爲路徑和"./data.dat"表明相同文件,也可直接寫目錄名,但要注意第2條。②刪除目錄時:要確保該目錄下沒有任何子項後才能夠將該目錄刪除,不然刪除失敗!
File dir=new File("."); File[] files=dir.listFiles();
if(files!=null&&files.length>0){ for(File file:files){ if(file.isDirectory()){
deleteDirectory(file);//遞歸刪除子目錄下的全部子項 }else{
if(!file.delete()){ throw new IOException("沒法刪除文件:"+file); }
System.out.println("文件:"+file+"已被刪除!"); } }
9)FileFilter:文件過濾器。FileFilter是一個接口,不可實例化,能夠規定過濾條件,在獲取某個目錄時能夠經過給定的刪選條件來獲取知足要求的子項。accept()方法是用來定義過濾條件的參數pathname是將被過濾的目錄中的每一個子項一次傳入進行匹配,若咱們認爲該子項知足條件則返回true。以下重寫accept方法。
FileFilter filter=new FileFilter(){
public boolean accept(File pathname){
return pathname.getName().endsWith(".java");//保留文件名以.java結尾的
//return pathname.length()>1700;按大小過濾 } };
File dir=new File(".");//建立一個目錄
File[] sub=dir.listFiles(filter);//獲取過濾器中知足條件的子項,回調模式
for(File file:sub){ System.out.println(file); }
10)回調模式:咱們定義一段邏輯,在調用其餘方法時,將該邏輯經過參數傳入。這個方法在執行過程當中會調用咱們傳入的邏輯來達成目的。這種現象就是回調模式。最多見的應用環境:按鈕監聽器,過濾器的應用。
能夠方便的讀寫文件內容,但只能一個字節一個字節(byte)的讀寫8位。
1)計算機的硬盤在保存數據時都是byte by byte的,字節埃着字節。
2)RandomAccessFile打開文件模式:rw:打開文件後可進行讀寫操做;r:打開文件後只讀。
3)RandomAccessFile是基於指針進行讀寫操做的,指針在哪裏就從哪裏讀寫。
①void seek(long pos)方法:從文件開頭到設置位置的指針偏移量,在該位置發生下一次讀寫操做。
②getFilePointer()方法:獲取指針當前位置,而seek(0)則將指針移動到文件開始的位置。
③int skipBytes(int n)方法:嘗試跳過輸入的n個字節。
4)RandomAccessFile類的構造器都是有參的。
①RandomAccessFile構造方法1:
RandomAccessFile raf=new RandomAccessFile(file,"rw");
②RandomAccessFile構造方法2:
RandomAccessFile raf=new RandomAccessFile("data.dat","rw");
直接根據文件路徑指定,前提是確保其存在!
5)讀寫操做完了,再也不寫了就關閉:close();
6)讀寫操做:
File file=new File("data.dat");//建立一個File對象用於描述該文件
if(!file.exists()){//不存在則建立該文件
file.createNewFile();//建立該文件,應捕獲異常,僅爲演示因此拋給main了 }
RandomAccessFile raf=new RandomAccessFile(file,"rw");//建立RandomAccessFile,並將File傳入,RandomAccessFile對File表示的文件進行讀寫操做。
/**1位16進製表明4位2進制;2位16進製表明一個字節 8位2進制;
* 4字節表明32位2進制;write(int) 寫一個字節,且是從低8位寫*/
int i=0x7fffffff;//寫int值最高的8位 raf.write(i>>>24);//00 00 00 7f
raf.write(i>>>16);//00 00 7f ff raf.write(i>>>8);// 00 7f ff ff
raf.write(i);// 7f ff ff ff
byte[] data=new byte[]{0,1,2,3,4,5,6,7,8,9};//定義一個10字節的數組並所有寫入文件
raf.write(data);//寫到這裏,當前文件應該有14個字節了
/**寫字節數組的重載方法:write(byte[] data.int offset,int length),從data數組的offset位置開始寫,連續寫length個字節到文件中 */
raf.write(data, 2, 5);// {2,3,4,5,6}
System.out.println("當前指針的位置:"+raf.getFilePointer());
raf.seek(0);//將指針移動到文件開始的位置
int num=0;//準備讀取的int值
int b=raf.read();//讀取第一個字節 7f 也從低8位開始
num=num | (b<<24);//01111111 00000000 00000000 00000000
b=raf.read();//讀取第二個字節 ff
num=num| (b<<16);//01111111 11111111 00000000 00000000
b=raf.read();//讀取第三個字節 ff
num=num| (b<<8);//01111111 11111111 11111111 00000000
b=raf.read();//讀取第四個字節 ff
num=num| b;//01111111 11111111 11111111 11111111
System.out.println("int最大值:"+num); raf.close();//寫完了再也不寫了就關了
7)經常使用方法:
①write(int data):寫入第一個字節,且是從低8位寫。
②write(byte[] data):將一組字節寫入。
③write(byte[] data.int offset,int length):從data數組的offset位置開始寫,連續寫length個字節到文件中。
④writeInt(int):一次寫4個字節,寫int值。
⑤writeLong(long):一次寫8個字節,寫long值。
⑥writeUTF(String):以UTF-8編碼將字符串連續寫入文件。
write……
①int read():讀一個字節,若已經讀取到文件末尾,則返回-1。
②int read(byte[] buf):嘗試讀取buf.length個字節。並將讀取的字節存入buf數組。返回值爲實際讀取的字節數。
③int readInt():連續讀取4字節,返回該int值
④long readLong():連續讀取8字節,返回該long值
⑤String readUTF():以UTF-8編碼將字符串連續讀出文件,返回該字符串值
read……
byte[] buf=new byte[1024];//1k容量 int sum=raf.read(buf);//嘗試讀取1k的數據
System.out.println("總共讀取了:"+sum+"個字節");
System.out.println(Arrays.toString(buf)); raf.close();//寫完了再也不寫了就關了
8)複製操做:讀取一個文件,將這個文件中的每個字節寫到另外一個文件中就完成了複製功能。
try { File srcFile=new File("chang.txt");
RandomAccessFile src=new RandomAccessFile(srcFile,"r");//建立一個用於讀取文件的RandomAccessFile用於讀取被拷貝的文件
File desFile=new File("chang_copy.txt"); desFile.createNewFile();//建立複製文件
RandomAccessFile des=new RandomAccessFile(desFile,"rw");//建立一個用於寫入文件的RandomAccessFile用於寫入拷貝的文件
//使用字節數組做爲緩衝,批量讀寫進行復制操做比一個字節一個字節讀寫效率高的多!
byte[] buff=new byte[1024*100];//100k 建立一個字節數組,讀取被拷貝文件的全部字節並寫道拷貝文件中
int sum=0;//每次讀取的字節數
while((sum=src.read(buff))>0){ des.write(buff,0,sum);//注意!讀到多少寫多少!}
src.close(); des.close(); System.out.println("複製完畢!");
} catch (FileNotFoundException e) { e.printStackTrace();
} catch (IOException e) { e.printStackTrace(); }
//int data=0;//用於保存每個讀取的字節
//讀取一個字節,只要不是-1(文件末尾),就進行復制工做
//while((data=src.read())!=-1){ des.write(data);//將讀取的字符寫入 }
9)基本類型序列化:將基本類型數據轉換爲字節數組的過程。writeInt(111):將int值111轉換爲字節並寫入磁盤;持久化:將數據寫入磁盤的過程。
Java I/O 輸入/輸出
流:根據方向分爲:輸入流和輸出流。方向的定了是基於咱們的程序的。流向咱們程序的流叫作:輸入流;從程序向外流的叫作:輸出流
咱們能夠把流想象爲管道,管道里流動的水,而java中的流,流動的是字節。
1)輸入流是用於獲取(讀取)數據的,輸出流是用於向外輸出(寫出)數據的。
InputStream:該接口定義了輸入流的特徵
OutputStream:該接口定義了輸出流的特徵
2)流根據源頭分爲:
基本流(節點流):從特定的地方讀寫的流類,如磁盤或一塊內存區域。即有來源。
處理流(高級流、過濾流):沒有數據來源,不能獨立存在,它的存在是用於處理基本流的。是使用一個已經存在的輸入流或輸出流鏈接建立的。
3)流根據處理的數據單位不一樣劃分爲:
字節流:以一個「字節」爲單位,以Stream結尾
字符流:以一個「字符」爲單位,以Reader/Writer結尾
4)close()方法:流用完必定要關閉!流關閉後,不能再經過其讀、寫數據
5)用於讀寫文件的字節流FIS/FOS(基本流)
①FileInputStream:文件字節輸入流。②FileOutputStream:文件字節輸出流。
6)FileInputStream 經常使用構造方法:
①FileInputStream(File file):經過打開一個到實際文件的鏈接來建立一個FileInputStream,該文件經過文件系統中的File對象file指定。即向file文件中寫入數據。
②FileInputStream(String filePath):經過打開一個到實際文件的鏈接來建立一個FileInputStream,該文件經過文件系統中的文件路徑名指定。也可直接寫當前項目下文件名。
經常使用方法:
①int read(int d):讀取int值的低8位。
②int read(byte[] b):將b數組中全部字節讀出,返回讀取的字節個數。
③int read(byte[] b,int offset,int length):將b數組中offset位置開始讀出length個字節。
④available()方法:返回當前字節輸入流 可讀取的總字節數。
7)FileOutputStream經常使用構造方法:
①FileOutputStream(File File):建立一個向指定File對象表示的文件中寫入數據的文件輸出流。會重寫之前的內容,向file文件中寫入數據時,若該文件不存在,則會自動建立該文件。
②FileOubputStream(File file,boolean append):append爲true則對當前文件末尾進行寫操做(追加,但不重寫之前的)。
③FileOubputStream(String filePath):建立一個向具備指定名稱的文件中寫入數據的文件輸出流。前提路徑存在,寫當前目錄下的文件名或者全路徑。
④FileOubputStream(String filePath,boolean append):append爲true則對當前文件末尾進行寫操做(追加,但不重寫之前的)。
經常使用方法:
①void write(int d):寫入int值的低8位。
②void write(byte[] d):將d數組中全部字節寫入。
③void write(byte[] d,int offset,int length):將d數組中offset位置開始寫入length個字節。
對傳入的流進行處理加工,能夠嵌套使用。
1)BufferedInputStream:緩衝字節輸入流
A.構造方法:BufferedInputStream(InputStream in)
BufferedInputStream(InputStream in, int size)
B.經常使用方法:
①int read():從輸入流中讀取一個字節。
②int read(byte[] b,int offset,int length):今後字節輸入流中給定偏移量offset處開始將各字節讀取到指定的 byte 數組中。
2)BufferedOutputStream:緩衝字節輸出流
A.構造方法:BufferedOutputStream(OutputStream out)
BufferedOutputStream(OutputStream out, int size)
B.經常使用方法:
①void write(int d):將指定的字節寫入此緩衝的輸出流。
②void write(byte[] d,int offset,int length):將指定 byte數組中從偏移量 offset開始的 length個字節寫入此緩衝的輸出流。
③void flush():將緩衝區中的數據一次性寫出,「清空」緩衝區。
C.內部維護着一個緩衝區,每次都儘量的讀取更多的字節放入到緩衝區,再將緩衝區中的內容部分或所有返回給用戶,所以能夠提升讀寫效率。
3)辨別高級流的簡單方法:看構造方法,若構造方法要求傳入另外一個流,那麼這個流就是高級流。因此高級流是沒有空參數的構造器的,都須要傳入一個流。
4)有緩衝效果的流,通常爲寫入操做的流,在數據都寫完後必定要flush,flush的做用是將緩衝區中未寫出的數據一次性寫出:bos.flush();即不論緩存區有多少數據,先寫過去,緩衝區再下班~確保全部字符都寫出
5)使用JDK的話,一般狀況下,咱們只須要關閉最外層的流。第三方流可能須要一層一層關。
是對「流」功能的擴展,簡化了對基本類型數據的讀寫操做。
1)DataInputStream(InputStream in):能夠直接讀取基本數據類型的流
經常使用方法:
①int readInt():連續讀取4個字節(一個int值),返回該int值
②double readDouble():連續讀取8個字節(一個double值),返回double值
③String readUTF():連續讀取字符串
……
2)DataOutputStream(OutputStream out):能夠直接寫基本數據類型的流
經常使用方法:
①void writeInt(int i):連續寫入4個字節(一個int值)
②void writeLong(long l):連續寫入8個字節(一個long值)
③void writeUTF(String s):連續寫入字符串
④void flush():將緩衝區中的數據一次性寫出,「清空」緩衝區。
……
以「單個」「字符」爲單位讀寫數據,一次處理一個字符(unicode)。
字符流底層仍是基於字節形式讀寫的。
在字符輸入輸出流階段,進行編碼修改與設置。
全部字符流都是高級流。
1) OutputStreamWriter:字符輸出流。
A.經常使用構造方法:
OutputStreamWriter(OutputStream out):建立一個字符集的輸出流。
OutputStreamWriter(OutputStream out, String charsetName):建立一個使用指定字符集的輸出流。
B.經常使用方法:
①void write(int c):寫入單個字符。
②void write(char c[], int off, int len):寫入從字符數組off開頭到len長度的部分
③void write(String str, int off, int len):寫入從字符串off開頭到len長度的部分。
④void flush():將緩衝區中的數據一次性寫出,「清空」緩衝區。
⑤void close():關閉流。
eg:向文件中寫入字符:①建立文件輸出流(字節流)。②建立字符輸出流(高級流),處理文件輸出流,目的是咱們能夠以字節爲單位寫數據。③寫入字符。④寫完後關閉流。
OutputStreamWriter writer=null;//不寫try-catch外的話finally找不到流,就沒法關閉
try{ FileOutputStream fos=new FileOutputStream("writer.txt");
// writer=new OutputStreamWriter(fos);//默認構造方法使用系統默認的編碼集
writer=new OutputStreamWriter(fos,"UTF-8");//最好指定字符集輸出
writer.write("你好!"); writer.flush();//將緩衝區數據一次性寫出
}catch(IOException e){ throw e; }
finally{ if(writer!=null){ writer.close(); } }
2)InputStreamReader:字符輸入流。
A.經常使用構造方法:
InputStreamReader(InputStream in):建立一個字符集的輸入流。
InputStreamReader(InputStream in, String charsetName):建立一個使用指定字符集的輸入流。
B.經常使用方法:
①int read():讀取單個字符。
②int read(char cbuf[], int offset, int length):讀入字符數組中從offset開始的length長度的字符。
③void close():關閉流。
eg:讀取文件中的字符
InputStreamReader reader=null;
try{//建立用於讀取文件的字節出入流
FileInputStream fis=new FileInputStream("writer.txt");
//建立用於以字符爲單位讀取數據的高級流
reader=new InputStreamReader(fis,"UTF-8"); int c=-1;//讀取數據
while((c=reader.read())!=-1){ //InputStreamReader只能一個字符一個字符的讀
System.out.println((char)c); }
}catch(IOException e){ throw e; }
finally{ if(reader!=null){ reader.close(); } }
能夠以「行」爲單位讀寫「字符」,高級流。
在字符輸入輸出流修改編碼。
1)BufferedWriter:緩衝字符輸出流,以行爲單位寫字符
A.經常使用構造方法:
BufferedWriter(Writer out):建立一個使用默認大小的緩衝字符輸出流。
BufferedWriter(Writer out,int size):建立一個使用給定大小的緩衝字符輸出流。
B.經常使用方法:
①void write(int c):寫入單個字符。
②void write(char[] c,int off,int len):寫入字符數組從off開始的len長度的字符。
③void write(String s,int off,int len):寫入字符串中從off開始的len長度的字符。
④void newLine():寫入一個行分隔符。
⑤flush():將緩衝區中的數據一次性寫出,「清空」緩衝區。
⑥close():關閉流。
u注意事項:BufferedWriter的構造方法中不支持給定一個字節輸出流,只能給定一個字符輸出流Writer的子類,Writer是字符輸出流的父類。
//建立用於寫文件的輸出流
FileOutputStream fos=new FileOutputStream("buffered.txt");
//建立一個字符輸出流,在字符輸入輸出流修改編碼
OutputStreamWriter osw=new OutputStreamWriter(fos,"UTF-8");
BufferedWriter writer=new BufferedWriter(osw);
writer.write("你好啊!!"); writer.newLine();//輸出一個換行
writer.write("我是第二行!!"); writer.newLine();//輸出一個換行
writer.write("我是第三行!!"); writer.close();//輸出流關閉後,不能再經過其寫數據
2)BufferedReader:緩衝字符輸入流,以行爲單位讀字符
A.經常使用構造方法:
BufferedReader(Reader in):建立一個使用默認大小的緩衝字符輸入流。
BufferedReader(Reader in,int size):建立一個使用指定大小的緩衝字符輸入流。
B.經常使用方法:
①int read():讀取單個字符。若是已到達流末尾,則返回-1。
②int read(char cbuf[], int off, int len):從字符數組中讀取從off開始的len長度的字符。返回讀取的字符數,若是已到達流末尾,則返回-1。
③String readLine():讀取一個文本行。經過下列字符之一便可認爲某行已終止:換行 ('\n')、回車 ('\r') 或回車後直接跟着換行。若是已到達流末尾,則返回 null。EOF:end of file文件末尾。
④void close():關閉流。
eg:讀取指定文件中的數據,並顯示在控制檯
FileInputStream fis=new FileInputStream(
"src"+File.separator+"day08"+File.separator+"DemoBufferedReader.java");
InputStreamReader isr=new InputStreamReader(fis);
BufferedReader reader=new BufferedReader(isr); String str=null;
if((str=reader.readLine())!=null){//readLine()讀取一行字符並以字符串形式返回
System.out.println(str); } reader.close();
eg:讀取控制檯輸入的每以行信息,直到在控制檯輸入exit退出程序
//1 將鍵盤的字節輸入流轉換爲字符輸入流
InputStreamReader isr=new InputStreamReader(System.in);
//2 將字符輸入流轉換爲緩衝字符輸入流,按行讀取信息
BufferedReader reader=new BufferedReader(isr);
// 循環獲取用戶輸入的信息並輸出到控制檯
String info=null; while(true){ info=reader.readLine(); if("exit".equals(info.trim())){ break; }
System.out.println(info);//輸出到控制檯 } reader.close();
用於讀寫「文本文件」的「字符」輸入流和輸出流。
1)FileWriter寫入:繼承OutputStreamWriter
A.經常使用構造方法
FileWriter(File file) 、FileWriter(File file, boolean append)
FileWriter(String filePath)、FileWriter(String fileName, boolean append)
意思和FileOutputStream的四個同類型參數的構造方法一致。
u注意事項:FileWriter的效果等同於:FileOutputStream + OutputStreamWriter。
B.經常使用方法:
①void write(int c):寫入單個字符。
②void write(char c[], int off, int len):寫入字符數組從off到len長度的部分
③void write(String str, int off, int len):寫入字符串從off到len長度的部分。
④void flush():將緩衝區中的數據一次性寫出,「清空」緩衝區。
⑤void close():關閉流。
FileWriter writer=new FileWriter("filewriter.txt");
//File file=new File("filewriter.txt");
//FileWriter writer=new FileWriter(file);
writer.write("hello!FileWriter!"); writer.close();
2) FileReader讀取:繼承InputStreamReader
A.「只能」以「字符」爲單位讀取文件,因此效率低
B.經常使用構造方法
FileReader(File file)、FileReader(String filePath)
意思和FileInputStream的兩個同類型參數的構造方法一致。
C.經常使用方法:
①int read():讀取單個字符。
②int read(char cbuf[], int offset, int length):讀入字符數組中從offset開始的length長度的字符。
③void close():關閉流。
FileReader reader=new FileReader("filewriter.txt");
//int c=-1; //只能以字符爲單位讀取文件
//while((c=reader.read())!=-1){ System.out.println((char)c); }
//將文件字符輸入流轉換爲緩衝字符輸入流即可以行爲單位讀取
BufferedReader br=new BufferedReader(reader);
String info=null; while((info=br.readLine())!=null){ System.out.println(info); }
br.close();
另外一種緩衝「字符」輸出流,以「行」爲單位,經常使用它做輸出,BufferedWriter用的少。
1)Servlet:運行在服務器端的小程序,給客戶端發送相應使用的輸出流就是PrintWriter。
2)寫方法:println(String data):帶換行符輸出一個字符串,不用手動換行了。
println……
3)構造方式:
PrintWriter(File file):以行爲單位向文件寫數據
PrintWriter(OutputStream out):以行爲單位向字節輸出流寫數據
PrintWriter(Writer writer):以行爲單位向字符輸出流寫數據
PrintWriter(String fileName):以行爲單位向指定路徑的文件寫數據
PrintWriter writer=new PrintWriter("printwriter.txt"); //向文件寫入一個字符串
writer.println("你好!PrintWriter");//自動加換行符
/**咱們要在肯定作寫操做的時候調用flush()方法,不然數據可能還在輸出流的緩衝區中,沒有做真實的寫操做!*/
writer.flush(); writer.close();
eg:將輸出流寫入文件
System.out.println("你好!!"); PrintStream out=System.out;
PrintStream fileOut=new PrintStream( new FileOutputStream("SystemOut.txt") );
System.setOut(fileOut);//將咱們給定的輸出流賦值到System.out上
System.out.println("你好!我是輸出到控制檯的!"); System.setOut(out);
System.out.println("我是輸出到控制檯的!"); fileOut.close();
將一個對象轉換爲字節形式的過程就是對象序列化。序列化還有個名稱爲串行化,序列化後的對象再被反序列化後獲得的對象,與以前的對象再也不是同一個對象。
1)對象序列化必須實現Serializable接口,但該接口無任何抽象方法,不須要重寫方法,只爲了標註該類可序列化。
2)且同時建議最好添加版本號(編號隨便寫):serialVersionUID。版本號,用於匹配當前類與其被反序列化的對象是否處於一樣的特徵(屬性列表一致等)。反序列化時,ObjectInputStream會根據被反序列化對象的版本與當前版本進行匹配,來決定是否反序列化。不加版本號能夠,可是可能存在反序列化失敗的風險。
3)JDK提供的大多數java bean都實現了該接口
4)transient關鍵字:序列化時忽略被它修飾的屬性。
5)對象的序列化使用的類:ObjectOutputStream
writeObject(Object obj):①將給定對象序列化。②而後寫出。
6)對象的反序列化使用的類:ObjectInputStream
Object readObject():將讀取的字節序列還原爲對象
7)對於HTTP協議:通訊一次後,必須斷開鏈接,想再次通訊要再次鏈接。
8)想要實現斷點續傳,咱們必須告訴服務器咱們當前讀取文件的開始位置。至關於咱們本地調用的seek(),由於咱們不可能直接調用服務器的對象的方法,因此咱們只能經過某種方式告訴服務器咱們要幹什麼。讓它自行調用本身流對象的seek()到咱們想讀取的位置。bytes=0- 的意思是告訴服務器從第一個字節開始讀,即seek(0)從頭至尾;bytes=128- 的意思是告訴服務器從地129個字節開始讀,即seek(128)。String prop="bytes="+info.getPos()+"-";
eg:序列化和反序列化
try{ DownloadInfo info=new DownloadInfo("http://www.baidu.com/download/xxx.zip"
, "xxx.zip" );
info.setPos(12587); info.setFileSize(5566987);
File file=new File("obj.tmp");//將對象序列化之後寫到文件中
FileOutputStream fos=new FileOutputStream(file);
//經過oos能夠將對象序列化後寫入obj.tmp文件中
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(info);//將info序列化後寫出 oos.close();
///反序列化操做
FileInputStream fis=new FileInputStream(file);
ObjectInputStream ois=new ObjectInputStream(fis);
DownloadInfo obj=(DownloadInfo)ois.readObject();//反序列化
System.out.println(obj.getUrl()); System.out.println(obj.getFileName());
System.out.println(obj.getFileSize()); System.out.println(obj.getPos());
System.out.println(info==obj); ois.close();
}catch(Exception e){ e.printStackTrace(); System.out.println("很是sorry!"); }
進程:一個操做系統中能夠同時運行多個任務(程序),每一個運行的任務(程序)被稱爲一個進程。即系統級別上的多線程(多個任務)。
線程:一個程序同時可能運行多個任務(順序執行流),那麼每一個任務(順序執行流)就叫作一個線程。即在進程內部。
併發:線程是併發運行的。操做系統將時間化分爲若干個片斷(時間片),儘量的均勻分配給每個任務,被分配時間片後,任務就有機會被cpu所執行。微觀上看,每一個任務都是走走停停的。但隨着cpu高效的運行,宏觀上看全部任務都在運行。這種都運行的現象稱之爲併發,但不是絕對意義上的「同時發生」。
1)Thread類的實例表明一個併發任務。任何線程對象都是Thread類的(子類)實例。Thread類是線程的模版,它封裝了複雜的線程開啓等操做,封裝了操做系統的差別性。所以併發的任務邏輯實現只要重寫Thread的run方法便可。
2)線程調度:線程調度機制會將全部併發任務作統一的調度工做,劃分時間片(能夠被cup執行的時間)給每個任務,時間片儘量的均勻,但作不到絕對均勻。一樣,被分配時間片後,該任務被cpu執行,但調度的過程當中不能保證全部任務都是平均的獲取時間片的次數。只能作到儘量平均。這兩個都是程序不可控的。
3)線程的啓動和中止:void start():想併發操做不要直接調用run方法!而是調用線程的start()方法啓動線程!void stop():不要使用stop()方法來中止線程的運行,這是不安全的操做,想讓線程中止,應該經過run方法的執行完畢來進行天然的結束。
4)線程的建立方式一:1:繼承自Thread。2:重寫run方法:run方法中應該定義咱們須要併發執行的任務邏輯代碼。
5)線程的建立方式二:將線程與執行的邏輯分離開,即實現Runnalbe接口。由於有了這樣的設計,纔有了線程池。關注點在於要執行的邏輯。
6)Runnable接口:用於定義線程要執行的任務邏輯。咱們定一個類實現Runnable接口,這時咱們必須重寫run方法,在其中定義咱們要執行的邏輯。以後將Runnable交給線程去執行。從而實現了線程與其執行的任務分離開。將任務分別交給不一樣的線程併發處理,能夠使用線程的重載構造方法:Thread(Runnable runnable)。解藕:線程與線程體解藕,即打斷依賴關係。Spring的ioc就是幹這個的。
/**建立兩個須要併發的任務,MyFirstRunnable和MySecRunnable都繼承了Runnable接口並重寫了run()方法 */
Runnable r1=new MyFirstRunnable(); Runnable r2=new MySecRunnable();
Thread t1=new Thread(r1); Thread t2=new Thread(r2);
t1.start(); t2.start();
7)線程的建立方式三:使用匿名內部類方式建立線程
new Thread(){public void run(){…}}.start(); /** * 匿名類實現繼承Thread形式*/
Thread t1=new Thread(){
public void run(){
for(int i=0;i<1000;i++){
System.out.println(i);
}
}
};
new Thread(new Runnable(){public void run(){…}}).start();
/**匿名類實現Runnable接口的形式 */
Thread t2=new Thread(new Runnable(){
public void run(){
for(int i=0;i<1000;i++){
System.out.println("你好"+i+"次");
}
}
} );
8)線程生命週期:
9)線程睡眠阻塞:使當前線程放棄cpu時間,進入阻塞狀態。在阻塞狀態的線程不會分配時間片。直到該線程結束阻塞狀態回到Runnable狀態,方可再次得到時間片來讓cpu運行(進入Running狀態)。
①static void sleep(times)方法:讓當前線程主動進入Block阻塞狀態,並在time毫秒後回到Runnalbe狀態。
u注意事項:使用Thread.sleep()方法阻塞線程時,強制讓咱們必須捕獲「中斷異常」。 引起狀況:當前線程處於Sleep阻塞期間,被另外一個線程中斷阻塞狀態時,當前線程會拋出該異常。
int i=0; while(true){ System.out.println(i+"秒"); i++;
try { Thread.sleep(1000);
} catch (InterruptedException e) { e.printStackTrace(); } }
10)void interrupt()方法:打斷/喚醒線程。一個線程能夠提早喚醒另一個sleep Block的線程。
u注意事項:方法中定義的類叫局部內部類:局部內部類中,若想引用當前方法的其餘局部變量,那麼該變量必須是final的。
final Thread lin=new Thread(){
public void run(){ System.out.println("林:睡覺了……");
try { Thread.sleep(1000000); } catch (InterruptedException e) {
System.out.println("林:幹嗎呢!幹嗎呢!幹嗎呢!");
System.out.println("林:都破了相了!"); } } };
lin.start();//啓動第一個線程
Thread huang=new Thread(){
public void run(){ System.out.println("80一錘子,您說咂哪兒?");
for(int i=0;i<5;i++){ System.out.println("80!");
try { Thread.sleep(1000); } catch (InterruptedException e) {
e.printStackTrace(); } }
System.out.println("咣噹!");
System.out.println("黃:搞定!");
lin.interrupt();//中斷第一個線程的阻塞狀態 } };
huang.start();//啓動第二個線程
11)線程的其餘方法:
①static void yield():當前線程讓出處理器(離開Running狀態)即放棄當前時間片,主動進入Runnable狀態等待。
②final void setPriority(int):設置線程優先級;優先級越高的線程,理論上獲取cpu的次數就越多。但理想與現實是有差距的……設置線程優先級必定要在線程啓動前設置!
③final void join():等待該線程終止。
Thread t1=new Thread(){
public void run(){ for(int i=0;i<100;i++){ System.out.println("我是誰啊?");
Thread.yield(); } }
};
Thread t2=new Thread(){
public void run(){ for(int i=0;i<100;i++){ System.out.println("我是修水管的");
Thread.yield(); } }
};
Thread t3=new Thread(){
public void run(){ for(int i=0;i<100;i++){ System.out.println("我是打醬油的");
Thread.yield(); } }
};
t1.setPriority(Thread.MAX_PRIORITY); t2.setPriority(Thread.MIN_PRIORITY);
t1.start(); t2.start(); t3.start();
12)線程併發安全問題:synchronized關鍵字,線程安全鎖、同步監視器。
多線程在訪問同一個數據時(寫操做),可能會引起不安全操做。
①哪一個線程報錯不捕獲,則線程死,不影響主程序。
②同步:同一時刻只能有一個執行,A和B配合工做,步調一致的處理(B獲得A的執行結果才能繼續)。如一羣人上公交車。
異步:同一時刻能有多個執行,併發,各自幹各自的。如一羣人上卡車。
③synchronized能夠修飾方法也能夠單獨做爲語句塊存在(同步塊)。做用是限制多線程併發時同時訪問該做用域。
④synchronized修飾方法後,會爲方法上鎖。方法就不是異步的了,而是同步的。鎖的是當前對象。
⑤synchronized同步塊:分析出只有一段代碼須要上鎖,則使用。效率比直接修飾方法要高。
⑥線程安全的效率低,如Vector、Hashtable。線程不安全的效率高,如ArrayList、HashMap
synchronized void getMoney(int money){ if(count==0){
throw new RuntimeException("餘額爲0"); }
Thread.yield(); count-=money; }
void getMoney(int money){
synchronized(this){//synchronized(Object){須要同步的代碼片斷}
if(count==0){ throw new RuntimeException("餘額爲0"); }
Thread.yield(); count-=money; }
13)Daemon後臺線程也稱爲守護線程:噹噹前進程中「全部」「前臺」線程死亡後,後臺線程將被強制死亡(非天然死亡),不管是否還在運行。
①守護線程,必須在啓動線程前調用。
②main方法也是靠線程運行的,且是一個前臺線程。
③正在運行的線程都是守護線程時,JVM退出。
14)wait/notify方法
這兩個方法不是在線程Thread中定義的方法,這兩個方法定義在Object中。兩個方法的做用是用於協調線程工做的。
①等待機制與鎖機制密切關聯:wait/notify方法必須與synchronized同時使用,誰調用wait或otify方法,就鎖誰!
②wait()方法:當條將不知足時,則等待。當條件知足時,等待該條件的線程將被喚醒。如:瀏覽器顯示一個圖片,displayThread要想顯示圖片,則必須等代下載線程downloadThread將該圖片下載完畢。若是圖片沒有下雜完成,則dialpayThread能夠暫停。當downloadThread下載完成後,再通知displayThread能夠顯示了,此時displayThread繼續執行。
③notify()方法:隨機通知、喚醒一個在當前對象身上等待的線程。
④notifyAll方法:通知、喚醒全部在當前對象身上等待的線程。
Socket套接字。在java.net.Socket包下。
1)網絡通訊模型:C/S:client/server,客戶端/服務器端;B/S:browser/server,瀏覽器端/服務器端;C/S結構的優勢:應用的針對性強,畫面絢麗,應用功能複雜。缺點:不易維護。B/S結構的優勢:易於維護。缺點:效果差,交互性不強。
2)Socket:封裝着本地的地址,服務端口等信息。ServerSocket:服務端的套接字。
服務器:使用ServerSocket監聽指定的端口,端口能夠隨意指定(因爲1024如下的端口一般屬於保留端口,在一些操做系統中不能夠隨意使用,因此建議使用大於1024的端口),等待客戶鏈接請求,客戶鏈接後,會話產生;在完成會話後,關閉鏈接。
客戶端:使用Socket對網絡上某一個服務器的某一個端口發出鏈接請求,一旦鏈接成功,打開會話;會話完成後,關閉Socket。客戶端不須要指定打開的端口,一般臨時的、動態的分配一個1024以上的端口。
3)永遠都是Socket去主動鏈接ServerSocket。一個ServerSocket能夠接收若干個Socket的鏈接。網絡通訊的前提:必定要捕獲異常。
4)Socket鏈接基於TCP/IP協議,是一種長鏈接(長時間連着)。
5)讀取服務器信息會阻塞,寫操做不會。
6)創建鏈接並向服務器發送信息步驟:①經過服務器的地址及端口與服務器鏈接,而建立Socket時須要以上兩個數據。②鏈接成功後能夠經過Socket獲取輸入流和輸出流,使用輸入流接收服務端發送過來的信息。③關閉鏈接。
7)鏈接服務器:一旦Socket被實例化,那麼它就開始經過給定的地址和端口號去嘗試與服務器進行鏈接(自動的)。這裏的地址"localhost"是服務器的地址,8088端口是服務器對外的端口。咱們自身的端口是系統分配的,咱們無需知道。
8)和服務器通訊(讀寫數據):使用Socket中的getInputStream()獲取輸入流,使用getOutputStream()獲取輸出流。
9)ServerSocket構造方法要求咱們傳入打開的端口號,ServerSocket對象在建立的時候就向操做系統申請打開這個端口。
10)經過調用ServerSocket的accept方法,使服務器端開始等待接收客戶端的鏈接。該方法是一個阻塞方法,監聽指定的端口是否有客戶端鏈接。直到有客戶端與其鏈接並接收客戶端套接字,不然該方法不會結束。
eg1.1:客戶端ClientDemo類
private Socket socket;
public void send(){
try{ System.out.println("開始鏈接服務器"); socket=new Socket("localhost",8088);
InputStream in=socket.getInputStream();//獲取輸入流
OutputStream out=socket.getOutputStream();//獲取輸出流
/**將輸出流變成處理字符的緩衝字符輸出流*/
PrintWriter writer=new PrintWriter(out); writer.println("你好!服務器!");
/**注意,寫到輸出流的緩衝區裏了,並無真的發給服務器。想真的發送就要做真實的寫操做,清空緩衝區*/
writer.flush();
/**將輸入流轉換爲緩衝字符輸入流*/
BufferedReader reader=new BufferedReader(new InputStreamReader(in));
/**讀取服務器發送過來的信息*/
String info=reader.readLine();//讀取服務器信息會阻塞 System.out.println(info);
writer.println("再見!服務器!"); writer.flush();
info=reader.readLine(); System.out.println(info);
}catch(Exception e){ e.printStackTrace(); } }
public static void main(String[] args){
ClientDemo demo=new ClientDemo(); demo.send();//鏈接服務器並通訊 }
eg1.2:服務器端ServerDemo類(不使用線程)
private ServerSocket socket=null; private int port=8088;
/**構建ServerDemo對象時就打開服務端口*/
public ServerDemo(){
try{ socket=new ServerSocket(port); }catch(Exception e){ e.printStackTrace(); } }
/**開始服務,等待收受客戶端的請求並與其通訊*/
public void start(){
try{ System.out.println("等待客戶端鏈接……"); Socket s=socket.accept();
//獲取與客戶端通訊的輸入輸出流
InputStream in=s.getInputStream(); OutputStream out=s.getOutputStream();
//包裝爲緩衝字符流
PrintWriter writer=new PrintWriter(out);
BufferedReader reader=new BufferedReader(new InputStreamReader(in));
//先聽客戶端發送的信息
String info=reader.readLine();//這裏一樣會阻塞 System.out.println(info);
//發送信息給客戶端
writer.println("你好!客戶端"); writer.flush();
info=reader.readLine(); System.out.println(info);
writer.println("再見!客戶端"); writer.flush();
socket.close();//關閉與客戶端的鏈接
}catch(Exception e){ e.printStackTrace(); } }
public static void main(String[] args){ System.out.println("服務器啓動中……");
ServerDemo demo=new ServerDemo(); demo.start(); }
eg2:服務器端ServerDemo類(使用線程),start()方法的修改以及Handler類
public void start(){
try{while(true){ System.out.println("等待客戶端鏈接……"); Socket s=socket.accept();
/** 當一個客戶端鏈接了,就啓動一個線程去接待它 */
Thread clientThread=new Thread(new Handler(s)); clientThread.start(); }
}catch(Exception e){ e.printStackTrace(); } }
/** 定義線程體,該線程的做用是與鏈接到服務器端的客戶端進行交互操做 */
class Handler implements Runnable{
private Socket socket;//當前線程要進行通訊的客戶端Socket
public Handler(Socket socket){//經過構造方法將客戶端的Socket傳入
this.socket=socket; }
public void run(){
try{ //獲取與客戶端通訊的輸入輸出流
InputStream in=socket.getInputStream();OutputStream out=socket.getOutputStream();
PrintWriter writer=new PrintWriter(out);//包裝爲緩衝字符流
BufferedReader reader=new BufferedReader(new InputStreamReader(in));
String info=reader.readLine();//先聽客戶端發送的信息,這裏一樣會阻塞
System.out.println(info);
//發送信息給客戶端
writer.println("你好!客戶端"); writer.flush();
info=reader.readLine(); System.out.println(info);
writer.println("再見!客戶端"); writer.flush();
socket.close();//關閉與客戶端的鏈接
}catch(Exception e){ e.printStackTrace(); } } }
public static void main(String[] args){ System.out.println("服務器啓動中……");
ServerDemo demo=new ServerDemo(); demo.start(); }
線程若想啓動須要調用start()方法。這個方法要作不少操做。要和操做系統打交道。註冊線程等工做,等待線程調度。ExecutorService提供了管理終止線程池的方法。
1)線程池的概念:首先建立一些線程,它們的集合稱爲線程池,當服務器接受到一個客戶請求後,就從線程池中取出一個空閒的線程爲之服務,服務完後不關閉該線程,而是將該線程還回到線程池中。在線程池的編程模式下,任務是提交給整個線程池,而不是直接交給某個線程,線程池在拿到任務後,它就在內部找有無空閒的線程,再把任務交給內部某個空閒的線程,一個線程同時只能執行一個任務,但能夠同時向一個線程池提交多個任務。
2)線程池的建立都是工廠方法。咱們不要直接去new線程池,由於線程池的建立還要做不少的準備工做。
3)常見構造方法:
①Executors.newCachedThreadPool():可根據任務須要動態建立線程,來執行任務。若線程池中有空閒的線程將重用該線程來執行任務。沒有空閒的則建立新線程來完成任務。理論上池子裏能夠放int最大值個線程。緩存線程生命週期1分鐘,得不到任務直解kill
②Executors.newFixedThreadPool(int threads):建立固定大小的線程池。池中的線程數是固定的。若全部線程處於飽和狀態,新任務將排隊等待。
③Executors.newScheduledThreadPool():建立具備延遲效果的線程池。可將帶運行的任務延遲指定時長後再運行。
④Executors.newSingleThreadExecutor():建立單線程的線程池。池中僅有一個線程。全部未運行的任務排隊等待。
BlockingQueue:解決了讀寫數據阻塞問題,可是同時寫或讀仍是同步的。
1)雙緩衝隊列加快了讀寫數據操做,雙緩衝對列能夠規定隊列存儲元素的大小,一旦隊列中的元素達到最大值,待插入的元素將等。等待時間是給定的,當給定時間到了元素尚未機會被放入隊列那麼會拋出超時異常。
2)LinkedBlockingQueue是一個能夠不指定隊列大小的雙緩衝隊列。若指定大小,當達到峯值後,待入隊的將等待。理論上最大值爲int最大值。
eg1.1:log服務器寫日誌文件,客戶端ClientDemo類,try語句塊中修改以下
try{ System.out.println("開始鏈接服務器");
socket=new Socket("localhost",8088);
OutputStream out=socket.getOutputStream();
PrintWriter writer=new PrintWriter(out);
while(true){
writer.println("你好!服務器!");
writer.flush();
Thread.sleep(500); } }
eg1.2:log服務器寫日誌文件,服務器端ServerDemo類,增長線程池和雙緩衝隊列兩個屬性,刪掉與原客戶端的輸出流
private ExecutorService threadPool;//線程池
private BlockingQueue<String> msgQueue; //雙緩衝隊列
public ServerDemo(){
try{ socket=new ServerSocket(port);
//建立50個線程的固定大小的線程池
threadPool=Executors.newFixedThreadPool(50);
msgQueue=new LinkedBlockingQueue<String>(10000);
/**建立定時器,週期性的將隊列中的數據寫入文件*/
Timer timer=new Timer();
timer.schedule(new TimerTask(){
public void run(){
try{ //建立用於向文件寫信息的輸出流
PrintWriter writer=new PrintWriter(new FileWriter("log.txt",true));
//從隊列中獲取全部元素,做寫出操做
String msg=null;
for(int i=0;i<msgQueue.size();i++){
/**參數 0:時間量TimeUnit.MILLISECONDS:時間單位*/
msg=msgQueue.poll(0,TimeUnit.MILLISECONDS);
if(msg==null){ break; }
writer.println(msg);//經過輸出流寫出數據
}
writer.close();
}catch(Exception e){ e.printStackTrace(); }
}
}, 0,500);
}catch(Exception e){ e.printStackTrace(); } }
public void start(){
try{ while(true){ System.out.println("等待客戶端鏈接……");
Socket s=socket.accept();
/**將線程體(併發的任務)交給線程池,線程池會自動將該任務分配給一個空閒線程去執行。*/ threadPool.execute(new Handler(s));
System.out.println("一個客戶端鏈接了,分配線程"); }
}catch(Exception e){ e.printStackTrace(); } }
/**定義線程體,該線程的做用是與鏈接到服務器端的客戶端進行交互操做*/
class Handler implements Runnable{
private Socket socket;//當前線程要進行通訊的客戶端Socket
public Handler(Socket socket){//經過構造方法將客戶端的Socket傳入
this.socket=socket; }
public void run(){
try{ //獲取與客戶端通訊的輸入輸出流
InputStream in=socket.getInputStream();
//包裝爲緩衝字符流
BufferedReader reader=new BufferedReader(new InputStreamReader(in));
String info=null;
while(true){//循環讀取客戶端發送過來的信息
info=reader.readLine();
if(info!=null){ //插入對列成功返回true,失敗返回false
//該方法會阻塞線程,若中斷會報錯!
boolean b=msgQueue.offer(info, 5, TimeUnit.SECONDS); }
}
}catch(Exception e){ e.printStackTrace(); }
}
}