一、基礎知識:
1、JVM、JRE和JDK的區別:
JVM(Java Virtual Machine):java虛擬機,用於保證java的跨平台的特性。java語言是跨平台,jvm不是跨平台的。
JRE(Java Runtime Environment):java的運行環境,包括jvm+java的核心類庫。
JDK(Java Development Kit):java的開發工具,包括JRE+開發工具
2、環境變量path和classpath的作用是什么?
(1)path是配置Windows可執行文件的搜索路徑,即擴展名為.exe的程序文件所在的目錄,用於指定DOS窗口命令的路徑。
(2)Classpath是配置class文件所在的目錄,用於指定類搜索路徑,JVM就是通過它來尋找該類的class類文件的。
3、變量有什么用?為什么要定義變量?什么時候用?
變量的作用:用來存儲數據。
為什么要定義變量:用來不斷的存放同一類型的常量,並可以重復使用
4、&和&&的區別?
(1)&&會出現短路,如果可以通過第一個表達式判斷出整個表達式的結果,則不繼續后面表達式的運算;
只能操作boolean類型數據;
(2)&不會出現短路,將整個表達式都運算。既可以操作boolean數據還可以操作數。
5、標示符命名規則:
由數字(0-9),大小寫英文字母,以及_和$組成。
不能以數字開頭。
不能使用關鍵字來自定義命名。
6、數據類型:
(1)基本數據類型(4類8種):
- 整數類型:byte、short、int、long
- 浮點數類型:float、double
- 字符類型:char
- 布爾類型:boolean(ture false)
(2)引用數據類型:
- 類
- 接口
- 數組
7、類型轉換
精度從高到低 double float long int short(char) byte
(1)自動類型轉換 將一個低精度---高精度
(2)強制類型轉換 將一個高精度---低精度(精度會下降)
8、java語言的三種技術架構
J2EE:企業版
是為開發企業環境下的應用程序提供的一套解決方案。
該技術體系中包含的技術如 Servlet、Jsp等,主要針對於Web應用程序開發。
J2SE:標准版
是為開發普通桌面和商務應用程序提供的解決方案。
該技術體系是其他兩者的基礎,可以完成一些桌面應用程序的開發。
比如Java版的掃雷。
J2ME:小型版
是為開發電子消費產品和嵌入式設備提供的解決方案。
該技術體系主要應用於小型電子消費類產品,如手機中的應用程序等。
9、java的跨平台性:
通過Java語言編寫的應用程序在不同的系統平台上都可以運行。
跨平台的原因:
只要在需要運行java應用程序的操作系統上,先安裝一個Java虛擬機(JVM Java Virtual Machine)即可。
由JVM來負責Java程序在該系統中的運行。
10、有符號數據的表示法(次重點)
原碼,反碼(原碼取反),補碼(反碼+1)。
11、函數:
定義:函數就是定義在類中的具有特定功能的一段獨立小程序。
特點:
定義函數可以將功能代碼進行封裝
便於對該功能進行復用
函數只有被調用才會被執行
函數的出現提高了代碼的復用性
對於函數沒有具體返回值的情況,返回值類型用關鍵字void表示,
那么該函數中的return語句如果在最后一行可以省略不寫。
函數的應用兩個明確:
明確要定義的功能最后的結果是什么?
明確在定義該功能的過程中,是否需要未知內容參與運算
12、重載:
- 概念:在同一個類中,允許存在一個以上的同名函數,只要它們的參數個數或者參數類型不同即可。
- 特點:與返回值類型無關,只看參數列表(參數類型以及參數個數)。
- 好處:方便於閱讀,優化了程序設計。
13、數組:
- 概念:同一種數據類型的集合。
- 好處:可以自動給數組中的元素從0開始編號,方便操作這些元素。
14、內存結構:
- 棧內存:用於存儲局部變量,當數據使用完,所占空間會自動釋放。
- 堆內存:數組和對象,通過new建立的實例都存放在堆內存中。
- 方法區:靜態成員、構造函數、常量池、線程池
- 本地方法區:window系統占用
- 寄存器:
二、面向對象
1、面向對象思想:
(1)概述:面向對象是相對於面向過程而言的,面向過程強調的是功能,面向對象強調的是將功能封裝進對象,強調具備功能的對象;
(2)思想特點:
A:是符合人們思考習慣的一種思想;
B:將復雜的事情簡單化了;
C:將程序員從執行者變成了指揮者;
比如我要達到某種結果,我就尋找能幫我達到該結果的功能的對象,如我要洗衣服我就買洗衣機,至於怎么洗我不管。
(3)特征:
封裝:隱藏對象的屬性和實現細節,僅對外提供公共訪問方式
繼承: 多個類中存在相同屬性和行為時,將這些內容抽取到單獨一個類中,那么多個類無需再定義這些屬性和行為,只要繼承那個類即可。
多態: 一個對象在程序不同運行時刻代表的多種狀態,父類或者接口的引用指向子類對象
2、類和對象:
類:對現實世界中某類事物的描述,是抽象的,概念上的定義。是對象的數據類型。
對象:事物具體存在的個體。就是Java的實例。對象有具體的屬性和行為。
3、成員變量和局部變量的區別(重點):
(1)作用域(生命周期)
- 成員變量:針對整個類有效。隨着對象創建而創建,對象消失則消失
- 局部變量:只在某個范圍內有效。(一般指的就是方法,語句體內)。隨着方法的調用而存在,隨着方法的調用完畢而消失
(2)存儲位置(在內存的位置)
- 成員變量:隨着對象的創建而存在,隨着對象的消失而消失,存儲在堆內存中。
- 局部變量:在方法被調用,或者語句被執行的時候存在,存儲在棧內存中。當方法調用完,或者語句結束后,就自動釋放。
(3)初始值
- 成員變量:有默認初始值。
- 局部變量:沒有默認初始值,使用前必須賦值。
4、匿名對象:
(1)匿名對象就是沒有名字的對象。是對象的一種簡寫形式。
(2)應用場景
A:只調用一次類中的方法。
B:可以作為實際參數在方法傳遞中使用
5、封裝:
客觀世界概念:指隱藏對象的屬性和實現細節,僅對外提供公共訪問方式;比如電腦機箱、筆記本等。
面向對象概念:是面向對象語言對客觀世界的模擬,客觀世界里成員變量都是隱藏在對象內部,外界是無法直接操作的。
原則:將類的某些信息隱藏在內部,外界無法直接訪問和操作。成員變量private,提供get和set方法。
好處:
將變化隔離;
方便使用;
提高復用性;
提高安全性
6、關鍵字private:封裝在代碼中的體現
(1)私有的意思,權限修飾符
(2)用來修飾成員變量和成員函數
(3)用private修飾的成員只在本類中有效
(4)私有是封裝的一種體現
7、構造方法:
(1)特點:
- 方法名與類名相同
- 沒有返回類型
- 沒有返回值
(2)作用:構造函數是用於創建對象,並對其進行初始化賦值,對象一建立就自動調用相對應的構造函數,
(3)構造方法的注意事項:
A:如果一個自定義類沒有構造方法,系統會默認給出一個無參構造方法。
B:如果一個自定義類提供了構造方法,那么,系統將不再給出無參構造方法。
這個時候,你可以不使用無參構造方法。
如果你想使用,那么,就必須手動給出無參構造方法。
建議:一般情況下,我們的自定義類都要手動給出無參構造方法。
(4)構造方法和成員方法的區別
A:格式區別
構造方法和類名相同,並且沒有返回類型,也沒有返回值。
普通成員方法可以任意起名,必須有返回類型,可以沒有返回值。
B:作用區別
構造方法用於創建對象,並進行初始化值。
普通成員方法是用於完成特定功能的。
C:調用區別
構造方法是在創建對象時被調用的,一個對象建立,只調用一次相應構造函數
普通成員方法是由創建好的對象調用,可以調用多次
8、構造代碼塊:
(1)作用:給對象進行初始化,對象一建立就執行,而且優先於構造函數執行
(2)構造代碼塊和構造函數的區別:
- 構造代碼塊是給所有不同對象的共性進行統一初始化
- 構造函數是給對應的對象進行初始化
9、this關鍵字:
(1) this關鍵字代表本類對象的一個引用,誰調用this所在的方法,this就代表誰
(2) this的使用場景
A:用於區分同名成員變量和局部變量;
B:在定義函數時,該函數內部要用到調用該函數的對象時,因為此時對象還沒建立,故this代表此對象
D:構造函數間調用
這個時候,this(參數)必須作為第一條語句存在。
(3) this的內存解析:this在方法內,當方法被哪個對象調用,this就代表哪個對象的內存地址
10、Person p = new Person();在內存中做了哪些事情。
(1)將Person.class文件加載進內存中。
(2)如果p定義在主方法中,那么,就會在棧空間開辟一個變量空間p。
(3)在堆內存給對象分配空間。
(4)對對象中的成員進行默認初始化。
(5)對對象中的成員進行顯示初始化。
(6)調用構造代碼塊對對象進行初始化。(如果沒有就不執行)
(7)調用構造方法對對象進行初始化。對象初始化完畢。
(8)將對象的內存地址賦值給p變量,讓p變量指向該對象。
11、static關鍵字:
(1)靜態的意思,用來修飾成員變量和成員函數。
(2)靜態的特點:
隨着類的加載而加載
優先於對象存在
對所有對象共享
可以被類名直接調用
(3)靜態的注意事項
A:靜態方法只能訪問靜態成員
為什么:因為靜態的內容是隨着類的加載而加載,它是先進內存的。
B:靜態方法中不能使用this,super關鍵字
C:主方法是靜態的
public static void main(String[] args)
public:公共的意思,是最大權限修飾符。
static:由於jvm調用main方法的時候,沒有創建對象。
只能通過類名調用。所以,main必須用static修飾。
void:由於main方法是被jvm調用,不需要返回值。用void修飾。
main:main是主要的意思,所以jvm采用了這個名字。是程序的入口。
String[]:字符串數組
args:數組名
在運行的時候,通過java命令給args數組賦值。
格式:java MainTest hello world itcast
(4)靜態變量和成員變量的區別
A:調用方式
靜態變量也稱為類變量,可以直接通過類名調用。也可以通過對象名調用。這個變量屬於類。
成員變量也稱為實例變量,只能通過對象名調用。這個變量屬於對象。
B:存儲位置
靜態變量存儲在方法區長中的靜態區。
成員變量存儲在堆內存。
C:生命周期
靜態變量隨着類的加載而存在,隨着類的消失而消失。生命周期長。
成員變量隨着對象的創建而存在,隨着對象的消失而消失。
D:與對象的相關性
靜態變量是所有對象共享的數據。
成員變量是每個對象所特有的數據。
(5)靜態的優點和弊端
優點:
- 對對象的共享數據進行單獨空間的存儲,節省內存,沒有必要每個對象都存儲一份
- 可直接被類名調用
弊端:
- 生命周期過長,隨着類的消失而消失
- 訪問出現權限,即靜態雖好但只能訪問靜態
(6)什么時候使用使用靜態呢?
A:當所有對象共享某個數據的時候,就把這個成員變量定義為靜態修飾的。
B:當某個方法沒有訪問該類中的非靜態成員,就可以把這個方法定義為靜態修飾。
靜態的生命周期比較長,所以一般不推薦使用。
(7)靜態代碼塊
A:它只執行一次,它比main還先執行。
B:執行順序
靜態代碼塊--構造代碼塊--構造方法
C:位置:類中,方法外
12、制作API(次重點):
API(全拼):Application Program Interface 應用程序編程接口。
(1)類中的內容需要用文檔注釋。
(2)使用JDK\bin目錄下的javadoc工具。
格式:javadoc -d 目錄 -author -version ArrayTool.java
13、單例設計模式:
(1)設計模式:
解決某類問題行之有效的方法,是一種思想,是規律的總結
(2)用來保證某個類在內存中只有一個對象
(3)保證唯一性的思想及步驟
- 為了避免其他程序建立該類對象,先禁止其他程序建立該類對象,即將構造函數私有化
- 為了其他程序訪問到該類對象,須在本類中創建一個該類私有對象
- 為了方便其他程序訪問到該類對象,可對外提供一個公共訪問方式
比如API中的Runtime類就是單例設計模式。
(4)單例設計模式的兩種方式
A:餓漢式 當類加載的時候,就創建對象。
1 class Student 2 { 3 private Student(){} 4 5 private static final Student s = new Student(); 6 7 public static Student getInstance() 8 { 9 return s; 10 } 11 }
B:懶漢式 當使用的使用,才去創建對象。
1 class Student 2 { 3 private Student(){} 4 5 private static final Student s = null; 6 7 public static Student getInstance() 8 { 9 if(s==null) 10 { 11 //線程1就進來了,線程2就進來了。 12 s = new Student(); 13 } 14 return s; 15 } 16 }
餓漢式和懶漢式的區別:
**
餓漢式是類一加載進內存就創建好了對象;
懶漢式則是類才加載進內存的時候,對象還沒有存在,只有調用了getInstance()方法時,對象才開始創建。
**
懶漢式是延遲加載,如果多個線程同時操作懶漢式時就有可能出現線程安全問題,解決線程安全問題
可以加同步來解決。但是加了同步之后,每一次都要比較鎖,效率就變慢了,所以可以加雙重判斷來提高程序效率。
注:開發常用餓漢式,因為餓漢式簡單安全。懶漢式多線程的時候容易發生問題
14、Math類的使用(重點)
(1)數學操作類:該類沒有構造函數,方法均為靜態的
(2)掌握內容
A:成員變量
**E: 比任何其他值都更接近e(即自然對數的底數)的double值。
**PI:比任何其他值都更接近pi(即圓的周長與直徑之比)的double值。
B:成員方法
**static double abs(double a)
返回 double 值的絕對值。返回絕對值
**static double ceil(double a)
返回最小的(最接近負無窮大)double 值,該值大於等於參數,並等於某個整數。
**static double floor(double a)
返回最大的(最接近正無窮大)double 值,該值小於等於參數,並等於某個整數。
**max:返回兩個值中較大的那個
**min:返回兩個值中較小的那個
**static long round(double a) 返回最接近參數的 long。
**static int round(float a) 返回最接近參數的 int。
**static double random()
返回帶正號的 double 值,該值大於等於 0.0 且小於 1.0。
**static double pow(double a, double b)
返回第一個參數的第二個參數次冪的值。
**static double sqrt(double a)
返回正確舍入的 double 值的正平方根。
15、Random類的使用(重點)
(1)產生隨機數的類
(2)掌握內容
A:構造方法
**Random() 創建一個新的隨機數生成器。
**Random(long seed) 使用單個 long 種子創建一個新的隨機數生成器。
B:成員方法
**int nextInt() 返回下一個偽隨機數,它是此隨機數生成器的序列中均勻分布的 int 值。
**int nextInt(int n) 返回一個偽隨機數,它是取自此隨機數生成器序列的、在 0(包括)和指定值(不包括)之間均勻分布的 int 值。
16、Scanner類的使用
(1)可以獲取從鍵盤的輸入數據
(2)掌握內容
構造方法:
Scanner(InputStream source) 構造一個新的 Scanner,它生成的值是從指定的輸入流掃描的。
如:Scanner sc = new Scanner(System.in);
方法摘要
sc.nextInt();獲取整型數據
sc.nextLine();獲取字符串數據
17、繼承(重點)
(1)把很多類的相同特征和行為進行抽取,用一個類來描述。讓多個類和這個類產生一個關系。這樣的話,多個類就可以省略很多代碼。這個關系就是繼承。java中用extends關鍵字表示。
(2)繼承的體系結構
A:多個具體的對象,不斷的向上抽取共享的內容,最終形成了一個體系。這個體系叫做繼承體系。
B:繼承體系的學習和使用原則
**學習頂層的內容。因為他是整個體系的共性內容。
**創建子類使用。也就是使用底層的具體對象。
(3)繼承的特點:
A:java中只能單繼承,沒有多繼承。
B:java可以有多重(層)繼承。
(4)繼承的好處:
繼承的出現提高了代碼的復用性。
繼承的出現讓類與類之間產生了關系,提供了多態的前提。
(5)子父類中的成員關系
A:成員變量
在子類方法中使用一個變量時:
首先,在方法的局部變量中找這個變量,有則使用。
否則,在本類中找成員變量,有則使用。
否則,在父類中找成員變量,有則使用。
否則,報錯。
B:成員方法
用子類對象使用一個方法時。
首先,在子類中找這個方法,有則使用。
否則,在父類中找這個方法,有則使用。
否則,報錯。
重寫和重載的區別?
重載:在同一類中。方法名相同,參數列表不同。重載可以改變返回類型。
重寫:在不同類中(子父類中)。
方法聲明相同(返回類型,方法名,參數列表均相同)。
重寫需要注意:
**子類方法的訪問權限要大於等於父類方法的訪問權限。
**靜態只能重寫靜態。但是這種情況一般不會出現。
構造方法
**子類的實例化過程:
***子類創建對象時,會先去創建父類的對象。默認是去調用父類的無參構造方法。
***子類構造方法中,第一行默認是super()
***為什么子類中第一行會默認有super()
因為他繼承父類的成員使用,使用前這些成員必須初始化,而他們是父類的成員,所以,必須通過父類進行初始化。所以,會先創建一個父類的對象。
**當父類沒有無參構造方法時
必須使用this或者super調用其他的構造方法。
(6)this和super的區別
this:代表本類對象的引用。
super:代表父類的存儲空間。
18、final關鍵字(重點)
(1)最終的意思,可以用於修飾類,方法,變量。
- final修飾的類不能被繼承。
- final修飾的方法不能被重寫。
- final修飾的變量是一個常量。只能被賦值一次。
- 內部類只能訪問被final修飾的局部變量。
19、抽象類(重點)
抽象類和普通類兩點區別:抽象類不能被實例化,可以擁有抽象方法
(1)多個類有相同的方法聲明,但是方法體不一樣。這個時候,我們考慮把方法聲明進行抽取。
讓子類繼承后,自己去實現方法體。子類必須去實現父類的抽象方法,否則該子類也是抽象類。沒有方法體的方法,我們需要用抽象標志下。
抽象的關鍵字是:abstract。
(2)抽象類:
該方法稱為抽象方法,包含抽象方法的類就是抽象類。
(3)抽象類的特點:
A:抽象類和抽象方法都要用abstract進行修飾
B:抽象類不能被實例化
C:抽象類中不一定有抽象方法,但是,有抽象方法的類一定是抽象類。
(4)抽象類中數據的特點
A:成員變量
抽象類中可以有變量,也可以有常量。
B:成員方法
抽象類中可以有抽象方法,也可以有非抽象方法。無需實現,沒有主體。
C:構造方法
抽象類是一個類,所以,它有構造方法。雖然本身不能實例化。但是可以給子類實例化使用。
(5)抽象類中的問題
A:抽象類中是否有構造方法?能不能被實例化?如果不能,為什么有構造方法?
抽象類有構造方法。
抽象類不能被實例化。
抽象類中的構造方法供子類實例化調用。
B:抽象關鍵字abstract不可以和哪些關鍵字共存?
**private:
私有內容子類繼承不到,所以,不能重寫。
但是abstract修飾的方法,要求被重寫。兩者沖突。
**final:
final修飾的方法不能被重寫。
而abstract修飾的方法,要求被重寫。兩者沖突。
**static:
假如一個抽象方法能通過static修飾,那么這個方法,就可以直接通過類名調用。
而抽象方法是沒有方法體的,這樣的調用無意義。所以,不能用static修飾。
C:抽象類中可不可以沒有抽象方法?如果可以,這樣的類有什么用嗎?
抽象類可以沒有抽象方法。
抽象類中沒有抽象方法的作用,只是為了不讓別的類建立該抽象類對象。這個在awt中有體現。
20、接口interface
(1)當一個類中的方法都是抽象的時候,java提供了另一種表示方式,叫接口。
用interface關鍵字表示。類與接口關系用implements表示。
(2)接口的成員特點
A:成員變量
是常量,默認修飾 public static final
B:成員方法
都是抽象的,默認修飾 public abstract
(3)關系
A:類與類的關系
是繼承關系。類與類只能單繼承,可以多重繼承。
B:類和接口的關系
是實現關系。類可以多實現接口。
類在繼承一個類的同時,可以實現多個接口。
C:接口和接口的關系
是繼承關系。接口可以多繼承接口。
(4)接口的特點
A:是對外暴露的規則
B:是功能的擴展
C:接口的出現降低耦合性。
耦合(類與類之間的關系)
內聚(類完成功能的能力)
編程規范:低耦合,高內聚。
D:接口可以多實現。如:CPU和主板、筆記本的USB插口、插座
(5)接口和抽象類的區別
A:抽象類只能被單繼承
接口可以多實現,接口的出現避免了多繼承的局限性。
B:抽象類中的數據特點:
成員變量:可以是變量,也可以是常量
成員方法:可以是抽象方法,也可以是非抽象方法
構造方法:有構造方法
C:接口中的數據特點:
成員變量:是常量。默認修飾 public static final
成員方法:都是抽象方法。都有默認修飾 public abstract
構造方法:沒有構造方法
D:抽象類中定義的是繼承體系中的共性功能。
接口中定義的是繼承體系中的擴展功能。
E:抽象類被繼承是"is a"關系:xx是yy的一種
F:接口被實現是"like a"關系:xx像yy的一種
21、多態:
(1)定義:多態是同一個行為具有多個不同表現形式或形態的能力。
多態就是同一個接口,使用不同的實例而執行不同操作。
同一個對象,在程序不同時刻的多種運行狀態。舉例:動物,狗是狗,狗是動物。水(氣態,液態,固態)
(2)多態前提
A:存在繼承或者實現關系
B:有方法的重寫
C:父類(接口)引用指向子類(實現)對象
(3)多態的好處和弊端:
好處:多態的存在提高了程序的擴展性和后期可維護性
弊端:雖然可以預先使用,但是只能訪問父類中已有的功能,運行的是后期子類的功能內容。不能預先使用子類中定義的特有功能。
(4)多態中對象調用成員的特點
Fu f = new Zi();
A:成員變量(不具有多態性)
編譯看左邊,運行看左邊
B:成員方法(向上轉型呈現的多態性僅僅針對成員方法,且不包含static和private方法)
編譯看左邊,運行看右邊(編譯看父類,運行看子類)
原理:當使用多態方式調用方法時,首先檢查基類中是否有該方法,如果沒有,則編譯錯誤;如果有,再去調用派生類的同名方法。
C:靜態方法
編譯看左邊,運行看左邊
(5)多態中重寫的基本規則:
-
參數必須要一樣,且返回類型必須兼容
重寫是派生類對基類的允許訪問的方法的實現過程進行重新編寫, 返回值和形參都不能改變。即外殼不變,核心重寫!
基類定義出其他的程序代碼要如何使用方法。不管基類使用了哪種參數,覆蓋此方法的派生類也一定要使用相同的參數。同樣,不論基類聲明的返回類型是什么,派生類必須要聲明返回一樣的類型或該類型的派生類。要記得,派生類對象必須保證可以執行基類的一切。
-
不能降低方法存取的極限
簡而言之,方法和變量的存取權必須相同或者更為開放。
例如不能把public的方法降低為private。
-
基類的成員方法只能被它的派生類重寫。
-
聲明為 final 的方法不能被重寫。
-
聲明為 static 的方法不能被重寫,但是能夠被再次聲明。
-
構造方法不能被重寫。
-
如果不能繼承一個方法,那么它一定不能被重寫
-
當需要在派生類(子類)中調用基類(父類)的被重寫方法時,要使用 super 關鍵字。如果父類該方法沒有被重寫,就不用super
(6)多態的思想
指揮同一批對象做事情。舉例:帶兵打仗,下課等。
22、instanceof關鍵字
A:用於判斷某個對象是否是某種類型。
B:格式
對象名 instanceof 子類(實現)名
23、Object類:
(1)是所有類的根類,超類。
java中提供的類以及我們自定義的類都直接或者間接的繼承自Object類。
(2)Object類中的方法
A:void finalize()
當垃圾回收器確定不存在對該對象的更多引用時,由對象的垃圾回收器調用此方法。
B:Class getClass()
獲取對象的字節碼文件的描述類,后面再講反射的時候還會在說這個類。
String name = s.getClass().getName();
C:int hashCode()
獲取對象的哈希值。其實就是對象的內存地址值十進制表示
D:String toString()
返回對象的字符串表示。
表示格式:
getClass().getName()+"@"+Integer.toHexString(hashCode());
一般我們輸出對象名的時候,其實底層調用的就是該對象的toString()方法。
這種返回沒有意義,所以,我們會重寫這個方法,顯示類的成員變量信息。
E:boolean equals(Object obj)
用於比較兩個對象的地址值是否相同。
我們獲取對象后,比較它的地址值意義不大。所以也會對這個方法進行重寫。
重寫要完成什么功能,是根據需求定的。
(3)==和equals的用法:
A:==怎么用?
**可以用於比較基本數據類型,比較的就是基本數據類型的值是否相等。
**可以用於比較引用數據類型,比較的是對象的地址值是否相等。
B:equals怎么用?
equals只能用於比較引用數據類型的。
**Object提供的equals是用於比較對象地址值是否相同。
**自定義類中,如果重寫了equals方法,那么就是按照你自己的需求來比較的。
24、package關鍵字
(1)包:其實就是文件夾。用於區分不同包下相同的類名。
(2)好處:
A:對類文件進行分類管理。
B:給類提供了多層命名空間
aaa.Demo
bbb.Demo
C:寫在程序文件的第一行。
D:包也是一種封裝形式。
25、import關鍵字
(1)導入包的關鍵字
(2)格式:
import 包名;
(3)注意:
A:一個程序文件中只有一個package,可以有多個import。
B:用來導包中的類,不導入包中的包。
C:通常寫import mypack.Demo,明確自己使用的類。
(4)關鍵字的順序
類,包,導包這些關鍵的順序。
包 -- > 到包 -- > 類
26、不同修飾符可以修飾哪些內容
一般格式:
成員變量:
權限修飾符+static/final+數據類型+成員變量名
public static final int NUM = 10;
成員方法:
權限修飾符+static/final/abstract+返回類型+方法名
27、內部類(次重點)
(1)把一個類定義在某個類中的,這個類就被稱為內部類,內置類,嵌套類。
(2)訪問特點:
A:內部類可以直接訪問外部類中的成員,因為內部類持有外部類的引用,
格式為:外部類名.this
B:外部類要想訪問內部類的成員,必須創建對象訪問。
(3)內部類的訪問格式:
A:當內部類定義在外部類的成員位置,而且非私有,則可以在其他外部類中直接建立內部類對象
格式:外部類名.內部類名 變量名 = new 外部類對象.內部類對象
如:Outer.Inner in = new Outer().new Inner()
B:當內部類在外部類成員位置,且被static修飾時
**外部其他類可直接訪問靜態內部類的非靜態成員
格式:new 外部類名.內部類名().內部類成員
如:new Outer.Inner().function();
**外部其他類可直接訪問靜態內部類的靜態成員
格式:new 外部類名.內部類名.內部類成員
如:new Outer.Inner.function();
(4)什么使用時候內部類呢?
假如有A類和B類,A類想直接訪問B類的成員,B類訪問A類成員的時候,需要創建A類對象進行訪問,這個時候,就可以把A類定義為B類的內部類。
(5)內部類的位置
A:成員位置
**可以被private修飾(Body,Heart)
**可以被static修飾。(它訪問的外部類的成員必須是靜態的)
B:局部位置
**可以直接訪問外部類中的成員,因為還持有外部類的持用
也可以直接訪問局部成員,但是局部成員要用final修飾。
注意:局部內部類不能用private和static修飾
(6)通過class文件我們就可以區分是否帶有內部類,以及內部類的位置
Outer$Inner:成員內部類
Outer$1Inner:局部內部類
28、匿名內部類(局部內部類的簡寫) (重點)
(1)前提:繼承一個類或者實現一個接口
(注意不要弄混匿名內部類的前提和多態的前提)
(2)格式:
new 父類名或者接口名()
{
重寫父類方法或者實現接口中的方法。
也可以自定義其他方法。
};
(3)什么時候定義匿名內部類?
匿名內部類只是為了簡化書寫,匿名內部類有局限,通常定義匿名內部類時,該類方法不超過3個
(4)匿名內部類的好處和弊端:
好處:簡化代碼書寫
弊端:
不能直接調用自己的特有方法
不能執行強轉換動作
如果該類里面方法較多,不允許使用匿名內部類
29、模板設計模式:
在定義功能時,功能的一部分是確定的,有一部分是不確定的,而且確定的部分在使用不確定的部分,可將不確定的部分暴露出去,由該類的子類去完成。
如:求一段程序的運行時間例子。
30、異常
(1)程序運行過程中的不正常現象就叫異常。
(2)導致程序運行不正常的現象有很多,所以,就有很多的異常對象。而這些異常對象存在着共性的內容,所以,可以不斷的進行抽取。最終形成了異常的體系結構。
異常體系的根類是:Throwable
Throwable(父類):
|--Error:重大的問題,我們處理不了。也不需要編寫代碼處理。比如說內存溢出。
|--Exception:一般性的錯誤,是需要我們編寫代碼進行處理的。
|--RuntimeException:運行時異常,這個我們也不需要處理。
其實就是為了讓他在運行時出問題,然后我們回來修改代碼。
(3)異常的分類
異常有兩種:
編譯時被檢測異常-CheckedException:例如要打開一個不存在文件時,一個異常就發生了,這些異常在編譯時不能被簡單地忽略。
該異常在編譯時,如果沒有處理(沒有拋也沒有try),編譯失敗。
該異常被標識,代表這可以被處理。
運行時異常(編譯時不檢測)-RuntimeException
在編譯時,不需要處理,編譯器不檢查。
該異常的發生,建議不處理,讓程序停止。需要對代碼進行修正。
(4)異常體系的特點:
異常體系中的所有類及其子類對象都具備可拋性。也就是說可以被throw和throws關鍵字所操作。
(5)main方法是如何處理異常的。
A:在main里面編寫代碼進行處理
B:交給jvm自己進行處理。采用的是jvm的默認處理方式。
其實就是相當於調用了異常對象的printStackTrace()方法。
(6)Throwable類的學習
getMessage():獲取異常信息,返回字符串。
toString():獲取異常類名和異常信息,返回字符串。
printStackTrace():獲取異常類名和異常信息,以及異常出現在程序中的位置。返回值void。
(7)異常的處理·
A:try...catch...finally
基本格式:
try
{
可能出現異常的代碼
}
catch(異常對象)
{
異常處理代碼
}
finally
{
釋放資源
}
變形格式:
try...catch
try...catch...catch...
try...catch...catch...finally
**多個異常同時被捕獲的時候,記住一個原則:
先逮小的,再逮大的。
**finally:永遠被執行,除非退出jvm。System.exit(0);
面試題2個。
***:final,finally,finalize區別。
final:是最終的意思。它可以用於修飾類,成員變量,成員方法。它修飾的類不能被繼承,它修飾的變量時常量,它修飾的方法不能被重寫。
finally:是異常處理里面的關鍵字。它其中的代碼永遠被執行。特殊情況:在執行它之前jvm退出。System.exit(0);
finalize:是Object類中的一個方法。它是於垃圾回收器調用的方式。
***:假如catch中有return語句, finally里中的代碼會執行嗎?是在return前,還是在return后呢?
會,在return前執行finally里面的代碼。
(8)Exception和RuntimeException的區別
A:Exception:一般性的錯誤,是需要我們編寫代碼進行處理的。
B:RuntimeException:運行時異常,這個我們也不需要處理。其實就是為了讓他在運行時出問題,然后我們回來修改代碼。
在用throws拋出一個的時候,如果這個異常是屬於RuntimeException的體系的時候,我們在調用的地方可以不用處理。(RuntimeException和RuntimeException的子類)
在用throws拋出一個的時候,如果這個異常是屬於Exception的體系的時候,我們在調用的地方必須進行處理或者繼續拋出。
(9)自定義異常
定義類繼承Exception或者RuntimeException
1,為了讓該自定義類具備可拋性。
2,讓該類具備操作異常的共性方法。
1 class MyExcepiton extends Exception 2 { 3 MyExcepiton(){} 4 5 MyExcepiton(String message) 6 { 7 super(message); 8 } 9 } 10 11 class MyException extends RuntimeException 12 { 13 MyExcepiton(){} 14 15 MyExcepiton(String message) 16 { 17 super(message); 18 } 19 }
(10)throws和throw的區別
A:有throws的時候可以沒有throw。
有throw的時候,如果throw拋的異常是Exception體系,那么必須有throws在方法上聲明。
B:throws用於方法的聲明上,其后跟的是異常類名,后面可以跟多個異常類,之間用逗號隔開
throw用於方法體中,其后跟的是一個異常對象名
三、多線程:
1、進程和線程:
進程:正在進行的程序。每一個進程執行都有一個執行順序,該順序是一個執行路徑,或者叫一個控制單元。
線程:進程內部的一條執行路徑或者一個控制單元。
兩者的區別:
一個進程至少有一個線程
進程在執行過程中擁有獨立的內存單元,而多個線程共享內存;
2、多線程的優勢與弊端:
優勢:解決了多部分同時運行的問題,提高效率。
弊端:線程太多會導致效率的降低,因為線程的執行依靠的是CPU的來回切換。
3、jvm多線程的啟動是多線程嗎?
java的虛擬機jvm啟動的是單線程,就有發生內存泄露的可能,而我們使用java程序沒出現這樣的問題,也就是jvm啟動至少有兩個線程,一個執行java程序,一個執行垃圾回收。所以是多線程。
4、什么叫多線程:
一個進程中有多個線程,稱為多線程。
5、實現多線程的方法:
(1)繼承Thread
1. 定義一個類繼承Thread類
2. 復寫Thread類中的public void run()方法,將線程的任務代碼封裝到run方法中
3. 直接創建Thread的子類對象,創建線程
4. 調用start()方法,開啟線程(調用線程的任務run方法)
//另外可以通過Thread的getName()獲取線程的名稱。
(2)實現Runnable接口
1. 定義一個類,實現Runnable接口;
2. 覆蓋接口的public void run()的方法,將線程的任務代碼封裝到run方法中;
3. 創建Runnable接口的子類對象
4. 將Runnabl接口的子類對象作為參數傳遞給Thread類的構造函數,創建Thread類對象
(原因:線程的任務都封裝在Runnable接口子類對象的run方法中。所以要在線程對象創建時就必須明確要運行的任務)。
調用start()方法,啟動線程。
兩種方法區別:
(1)實現Runnable接口避免了單繼承的局限性
(2)繼承Thread類線程代碼存放在Thread子類的run方法中
實現Runnable接口線程代碼存放在接口的子類的run方法中;
在定義線程時,建議使用實現Runnable接口,因為幾乎所有多線程都可以使用這種方式實現
6、創建線程是為什么要復寫run方法?
Thread類用於描述線程。Thread類定義了一個功能,用於存儲線程要運行的代碼,該存儲功能就是run方法。
7、start()和run方法有什么區別?
調用start方法方可啟動線程,而run方法只是thread的一個普通方法,調用run方法不能實現多線程;
Start()方法:
start方法用來啟動線程,實現了多線程運行,這時無需等待run方法體代碼執行完畢而直接繼續執行下面的代碼。通過調用Thread類的start()方法來啟動一個線程,這時此線程處於就緒(可運行)狀態,並沒有運行,一旦得到cpu時間片(執行權),就開始執行run()方法,這里方法run()稱為線程體,它包含了要執行的這個線程的內容,Run方法運行結束,此線程隨即終止。
Run()方法:
run()方法只是Thread類的一個普通方法,如果直接調用Run方法,程序中依然只有主線程這一個線程,其程序執行路徑還是只有一條,還是要等待run方法體執行完畢后才可繼續執行下面的代碼,這樣就沒有達到多線程的目的。
8、線程的幾種狀態:
- 新建:new一個Thread對象或者其子類對象就是創建一個線程,當一個線程對象被創建,但是沒有開啟,這個時候,只是對象線程對象開辟了內存空間和初始化數據。
- 就緒:新建的對象調用start方法,就開啟了線程,線程就到了就緒狀態。在這個狀態的線程對象,具有執行資格,沒有執行權。
- 運行:當線程對象獲取到了CPU的資源。在這個狀態的線程對象,既有執行資格,也有執行權。
- 凍結:運行過程中的線程由於某些原因(比如wait,sleep),釋放了執行資格和執行權。當然,他們可以回到運行狀態。只不過,不是直接回到。而是先回到就緒狀態。
- 死亡:當線程對象調用的run方法結束,或者直接調用stop方法,就讓線程對象死亡,在內存中變成了垃圾。
9、sleep()和wait()的區別:
(1)這兩個方法來自不同的類,sleep()來自Thread類,和wait()來自Object類。
(2)sleep是Thread的靜態類方法,誰調用的誰去睡覺,即使在a線程里調用了b的sleep方法,實際上還是a去睡覺,要讓b線程睡覺要在b的代碼中調用sleep。而wait()是Object類的非靜態方法
(3)sleep()釋放資源不釋放鎖,而wait()釋放資源釋放鎖;
(4)使用范圍:wait,notify和notifyAll只能在同步控制方法或者同步控制塊里面使用,而sleep可以在任何地方使用
10、多線程安全問題:
(1)原因:當程序的多條語句在操作線程共享數據時(如買票例子中的票就是共享資源),由於線程的隨機性導致一個線程對多條語句,執行了一部分還沒執行完,另一個線程搶奪到cpu執行權參與進來執行,此時就導致共享數據發生錯誤。比如買票例子中打印重票和錯票的情況。
(2)解決方法:對多條操作共享數據的語句進行同步,一個線程在執行過程中其他線程不可以參與進來
11、Java中多線程同步是什么?
同步是用來解決多線程的安全問題的,在多線程中,同步能控制對共享數據的訪問。如果沒有同步,當一個線程在修改一個共享數據時,而另外一個線程正在使用或者更新同一個共享數據,這樣容易導致程序出現錯誤的結果。
12、什么是鎖?鎖的作用是什么?
鎖就是對象
鎖的作用是保證線程同步,解決線程安全問題。
持有鎖的線程可以在同步中執行,沒有鎖的線程即使獲得cpu執行權,也進不去。
13、同步的前提:
(1)必須保證有兩個以上線程
(2)必須是多個線程使用同一個鎖,即多條語句在操作線程共享數據
(3)必須保證同步中只有一個線程在運行
14、同步的好處和弊端
- 好處:同步解決了多線程的安全問題
- 弊端:多線程都需要判斷鎖,比較消耗資源
15、同步的兩種表現形式:
(1)同步代碼塊:
可以指定需要獲取哪個對象的同步鎖,使用synchronized的代碼塊同樣需要鎖,但他的鎖可以是任意對象
考慮到安全問題,一般還是使用同一個對象,相對來說效率較高。
注意:
**雖然同步代碼快的鎖可以使任何對象,但是在進行多線程通信使用同步代碼快時,必須保證同步代碼快的鎖的對象和,否則會報錯。
**同步函數的鎖是this,也要保證同步函數的鎖的對象和調用wait、notify和notifyAll的對象是
同一個對象,也就是都是this鎖代表的對象。
格式:
synchronized(對象)
{
需同步的代碼;
}
(2)同步函數
同步方法是指進入該方法時需要獲取this對象的同步鎖,在方法上使用synchronized關鍵字,使用this對象作為鎖,也就是使用了當前對象,因為鎖住了方法,所以相對於代碼塊來說效率相對較低。
注:靜態同步函數的鎖是該方法所在的類的字節碼文件對象,即類名.class文件
格式:
修飾詞 synchronized 返回值類型 函數名(參數列表)
{
需同步的代碼;
}
在jdk1.5后,用lock鎖取代了synchronized,個人理解也就是對同步代碼塊做了修改,並沒有提供對同步方法的修改,主要還是效率問題吧。
16、多線程的單例設計模式:保證某個類中內存中只有一個對象
(1)餓漢式:
1 class Single 2 { 3 private Single(){}//將構造函數私有化,不讓別的類建立該類對象 4 private static final Single s=new Single();//自己建立一個對象 5 public static Single getInstance()//提供一個公共訪問方式 6 { 7 return s; 8 } 9 }
(2)懶漢式:
1 class Single 2 { 3 private Single(){} 4 private static Single s; 5 public static Single getInstance() 6 { 7 if(s==null) 8 s=new Single(); 9 return s; 10 } 11 }
餓漢式和懶漢式的區別:
**
餓漢式是類一加載進內存就創建好了對象;
懶漢式則是類加載進內存的時候,對象還沒有存在,只有調用了getInstance()方法時,對象才開始創建。
**
懶漢式是延遲加載,如果多個線程同時操作懶漢式時就有可能出現線程安全問題,解決線程安全問題
可以加同步來解決。但是加了同步之后,每一次都要比較鎖,效率就變慢了,
所以可以加雙重判斷來提高程序效率。
如將上述懶漢式的Instance函數改成同步:
1 public static Single getInstance() 2 { 3 if(s==null) 4 { 5 synchronized(Single.class) 6 { 7 if(s==null) 8 s=new Single(); 9 } 10 } 11 return s; 12 }
17、死鎖
兩個線程對兩個同步對象具有循環依賴時,就會發生死鎖。即同步嵌套同步,而鎖卻不同。
18、wait()、sleep()、notify()、notifyAll()
- wait():使一個線程處於等待狀態,並且釋放所持有的對象的lock。
- sleep():使一個正在運行的線程處於睡眠狀態,是一個靜態方法,調用此方法要捕捉InterruptedException異常。
- notify():喚醒一個處於等待狀態的線程,注意的是在調用此方法的時候,並不能確切的喚醒某一個等待狀態的線程,而是由JVM確定喚醒哪個線程(一般是最先開始等待的線程),而且不是按優先級。
- Allnotity():喚醒所有處入等待狀態的線程,注意並不是給所有喚醒線程一個對象的鎖,而是讓它們競爭。
19、為什么wait()、notify()、notifyAll()這些用來操作線程的方法定義在Object類中?
(1)這些方法只存在於同步中;
(2)使用這些方法時必須要指定所屬的鎖,即被哪個鎖調用這些方法;
(3)而鎖可以是任意對象,所以任意對象調用的方法就定義在Object中。
20、多線程間通訊:
多線程間通訊就是多個線程在操作同一資源,但是操作的動作不同。
(1)為什么要通信
多線程並發執行的時候, 如果需要指定線程等待或者喚醒指定線程, 那么就需要通信.
比如生產者消費者的問題:
生產一個消費一個,生產的時候需要負責消費的進程等待,生產一個后完成后需要喚醒負責消費的線程,
同時讓自己處於等待,消費的時候負責消費的線程被喚醒,消費完生產的產品后又將等待的生產線程喚醒,
然后使自己線程處於等待。這樣來回通信,以達到生產一個消費一個的目的。
(2)怎么通信
在同步代碼塊中, 使用鎖對象的wait()方法可以讓當前線程等待, 直到有其他線程喚醒為止.
使用鎖對象的notify()方法可以喚醒一個等待的線程,或者notifyAll喚醒所有等待的線程.
多線程間通信用sleep很難實現,睡眠時間很難把握。
21、Lock和Condition
實現提供比synchronized方法和語句可獲得的更廣泛的鎖的操作,可支持多個相關的Condition對象
Lock是個接口
鎖是控制多個線程對共享數據進行訪問的工具。
JDK1.5中提供了多線程升級的解決方案:
將同步synchonized替換成了顯示的Lock操作,將Object中的wait、notify、notifyAll替換成了Condition對象。
該對象可以Lock鎖進行獲取。
Lock的方法摘要:
1 void lock() //獲取鎖。 2 Condition newCondition() //返回綁定到此 Lock 實例的新 Condition 實例。 3 void unlock() //釋放鎖。
Condition方法摘要:
1 void await() //造成當前線程在接到信號或被中斷之前一直處於等待狀態。 2 void signal() //喚醒一個等待線程。 3 void signalAll() //喚醒所有等待線程。
22、停止線程:
stop方法已經過時,如何停止線程?
停止線程的方法只有一種,就是run方法結束。如何讓run方法結束呢?
開啟多線程運行,運行代碼通常是循環體,只要控制住循環,就可以讓run方法結束,也就是結束線程。
特殊情況:當線程屬於凍結狀態,就不會讀取循環控制標記,則線程就不會結束。
為解決該特殊情況,可引入Thread類中的Interrupt方法結束線程的凍結狀態;
當沒有指定的方式讓凍結線程恢復到運行狀態時,需要對凍結進行清除,強制讓線程恢復到運行狀態
23、interrupt:
void interrupt() 中斷線程:
中斷狀態將被清除,它還將收到一個 InterruptedException
24、守護線程(后台線程)
setDaemon(boolean on):將該線程標記為守護線程或者用戶線程。
當主線程結束,守護線程自動結束,比如聖斗士星矢里面的守護雅典娜,
在多線程里面主線程就是雅典娜,守護線程就是聖斗士,主線程結束了,
守護線程則自動結束。
當正在運行的線程都是守護線程時,java虛擬機jvm退出;所以該方法必須在啟動線程前調用;
守護線程的特點:
守護線程開啟后和前台線程共同搶奪cpu的執行權,開啟、運行兩者都沒區別,
但結束時有區別,當所有前台線程都結束后,守護線程會自動結束。
25、多線程join方法:
void join() 等待該線程終止。
void join(long millis) 等待該線程終止的時間最長為 millis 毫秒。
throws InterruptedException
特點:當A線程執行到B線程的join方法時,A就會等待B線程都執行完,A才會執行
作用: join可以用來臨時加入線程執行;
26、多線程優先級:yield()方法
1 yield() //暫停當前正在執行的線程對象,並執行其他線程 2 setPriority(int newPriority) //更改線程優先級 3 int getPriority() //返回線程的優先級。 4 String toString() //返回該線程的字符串表示形式,包括線程名稱、優先級和線程組
(1)MAX_PRIORITY:最高優先級(10級)
(1)Min_PRIORITY:最低優先級(1級)
(1)Morm_PRIORITY:默認優先級(5級)
27、什么是ThreadLocal類,怎么使用它?
ThreadLocal 類提供了線程局部 (thread-local) 變量。是一個線程級別的局部變量,並非“本地線程”。
ThreadLocal 為每個使用該變量的線程,提供了一個獨立的變量副本,每個線程修改副本時不影響其它線程對象的副本。
下面是線程局部變量(ThreadLocal variables)的關鍵點:
一個線程局部變量(ThreadLocal variables)為每個線程方便地提供了一個單獨的變量。
ThreadLocal 實例通常作為靜態的私有的(private static)字段出現在一個類中,這個類用來關聯一個線程。
當多個線程訪問 ThreadLocal 實例時,每個線程維護 ThreadLocal 提供的獨立的變量副本。
常用的使用可在 DAO 模式中見到,當 DAO 類作為一個單例類時,
數據庫鏈接(connection)被每一個線程獨立的維護,互不影響。(基於線程的單例)
28、什么時候拋出InvalidMonitorStateException異常?為什么?
調用 wait ()/notify ()/notifyAll ()中的任何一個方法時
如果當前線程沒有獲得該對象的鎖,那么就會拋出 IllegalMonitorStateException 的異常
也就是說程序在沒有執行對象的任何同步塊或者同步方法時,仍然嘗試調用 wait ()/notify ()/notifyAll ()時。
由於該異常是 RuntimeExcpetion 的子類,所以該異常不一定要捕獲(盡管你可以捕獲只要你願意。
作為 RuntimeException,此類異常不會在 wait (),notify (),notifyAll ()的方法簽名提及。
29、在靜態方法上使用同步時會發生什么事?
同步靜態方法時會獲取該類的“Class”對象,所以當一個線程進入同步的靜態方法中時,
線程監視器獲取類本身的對象鎖,其它線程不能進入這個類的任何靜態同步方法。
它不像實例方法,因為多個線程可以同時訪問不同實例同步實例方法。
30、當一個同步方法已經執行,線程能夠調用對象上的非同步實例方法嗎?
可以,一個非同步方法總是可以被調用而不會有任何問題。
實際上,Java 沒有為非同步方法做任何檢查,鎖對象僅僅在同步方法或者同步代碼塊中檢查。
如果一個方法沒有聲明為同步,即使你在使用共享數據Java照樣會調用,而不會做檢查是否安全,
所以在這種情況下要特別小心。一個方法是否聲明為同步取決於臨界區訪問(critial section access),
如果方法不訪問臨界區(共享資源或者數據結構)就沒必要聲明為同步的。
31、在一個對象上兩個線程可以調用兩個不同的同步實例方法嗎?
不能,因為一個對象已經同步了實例方法,線程獲取了對象的對象鎖。
所以只有執行完該方法釋放對象鎖后才能執行其它同步方法。
32、什么是線程餓死,什么是活鎖?
線程餓死和活鎖雖然不像死鎖一樣是常見的問題,但是對於並發編程的設計者來說就像一次邂逅一樣。
當所有線程阻塞,或者由於需要的資源無效而不能處理,不存在非阻塞線程使資源可用。
JavaAPI 中線程活鎖可能發生在以下情形:
當所有線程在程序中執行 Object.wait (0),參數為 0 的 wait 方法。
程序將發生活鎖直到在相應的對象上有線程調用 Object.notify ()或者 Object.notifyAll ()。
當所有線程卡在無限循環中。
四、集合框架
1、String類:字符串(重點)
(1)多個字符組成的一個序列,叫字符串。
生活中很多數據的描述都采用的是字符串的。而且我們還會對其進行操作。
所以,java就提供了這樣的一個類供我們使用。
(2)創建字符串對象
A:String():無參構造
**舉例:
String s = new String();
s = "hello";
sop(s);
B:String(byte[] bys):傳一個字節數組作為參數 *****
**舉例
byte[] bys = {97,98,99,100,101};
String s = new String(bys);
sop(s);
C:String(byte[] bys,int index,int length):把字節數組的一部分轉換成一個字符串 *****
**舉例
byte[] bys = {97,98,99,100,101};
String s = new String(bys,1,2);
sop(s);
D:String(char[] chs):傳一個字符數組作為參數 *****
**舉例
char[] chs = {'a','b','c','d','e'};
String s = new String(chs);
sop(s);
E:String(char[] chs,int index,int length):把字符數組的一部分轉換成一個字符串 *****
**舉例
char[] chs = {'a','b','c','d','e'};
String s = new String(chs,1,2);
sop(s);
F:String(String str):把一個字符串傳遞過來作為參數
char[] chs = {'a','b','c','d','e'};
String ss = new String(s);
sop(ss);
G:直接把字符串常量賦值給字符串引用對象(最常用) *****
**舉例
String s = "hello";
sop(s);
(3)面試題
A:請問String s = new String("hello");創建了幾個對象。
兩個。一個"hello"字符串對象,在方法區的常量池;一個s對象,在棧內存。
B:請寫出下面的結果
1 String s1 = new String("abc"); 2 Strign s2 = new String("abc"); 3 String s3 = "abc"; 4 String s4 = "abc"; 5 6 sop(s1==s2); //false 7 sop(s1==s3); //false 8 sop(s3==s4); //true
C:字符串對象一旦被創建就不能被改變。
指的是字符串常量值不改變。
(4)字符串中各種功能的方法
A:判斷
1 boolean equals(Object anObject)://判斷兩個字符串的內容是否相同,復寫了Object的方法 2 boolean equalsIgnoreCase(String anotherString)://判斷兩個字符串的內容是否相同,不區分大小寫 3 boolean contains(String s)://判斷一個字符串中是否包含另一個字符串 4 //注意:判斷字符串是否包含特殊字符.直接表示為str.contains(".") 5 boolean endsWith(String suffix)://測試此字符串是否以指定的后綴結束 6 boolean startsWith(String suffix)://測試此字符串是否以指定的前綴開始 7 boolean isEmpty()://測試字符串是否為空
B:獲取
1 int length()://返回此字符串的長度 2 char charAt(int index)://返回指定索引處的 char值 3 int indexOf(int ch)://返回指定字符在此字符串中第一次出現處的索引。 4 int indexOf(int ch, int fromIndex)://返回在此字符串中第一次出現指定字符處的索引,從指定的索引開始搜索。 5 int indexOf(String str)://返回指定子字符串在此字符串中第一次出現處的索引。 6 int indexOf(String str, int fromIndex)://返回指定子字符串在此字符串中第一次出現處的索引,從指定的索引開始。 7 int lastIndexOf(int ch)://返回指定字符在此字符串中最后一次出現處的索引。 8 int lastIndexOf(int ch, int fromIndex)://返回指定字符在此字符串中最后一次出現處的索引,從指定的索引處開始進行反向搜索。 9 int lastIndexOf(String str)://返回指定子字符串在此字符串中最右邊出現處的索引。 10 int lastIndexOf(String str, int fromIndex)://返回指定子字符串在此字符串中最后一次出現處的索引,從指定的索引開始反向搜索。 11 String substring(int beginIndex)://(注意:該方法substring的String是小寫!!!)返回一個新的字符串,它是此字符串的一個子字符串。 12 String substring(int beginIndex, int endIndex)://(注意該方法的String是小寫!!!)返回一個新字符串,它是此字符串的一個子字符串,包含頭不包含尾
C:轉換
1 byte[] getBytes():(很常用!)從字符串到字節數組的方法 2 void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)://將字符從此字符串復制到目標字符數組。 3 char[] toCharArray()://(很常用!)從字符串到字符數組的方法 4 static String copyValueOf(char[] data)://返回指定數組中表示該字符序列的 String。 5 static String copyValueOf(char[] data, int offset, int count)://返回指定數組中表示該字符序列的 String。 6 static String valueOf(數據類型)://把該數據類型的數據轉換成字符串。 7 String toLowerCase():把字符串轉換成小寫 8 String toUpperCase():把字符串轉換成大寫 9 //字符串的連接 10 String concat(String str)://將指定字符串連接到此字符串的結尾。
D:替換
1 String replace(char oldChar, char newChar)://用新字符替換舊字符(替換所有) 2 String replace(String target, String replacement)://用新的子串換舊串
E:分割
1 String[] split(String regex)://根據指定的字符串把一個字符串分割成一個字符串數組
F:其他
1 String trim()://去除字符串的前后空格 2 int compareTo(String anotherString)://按字典順序比較兩個字符串。 3 int compareToIgnoreCase(String str)://按字典順序比較兩個字符串,不考慮大小寫。
(5)練習
1:模擬登錄,給三次機會,並提示還有幾次.
默認的用戶名和密碼為admin。 區分大小寫。
自己從鍵盤輸入用戶名和密碼。
2:給定一個字符串統計,統計大寫字母,小寫字母,數字出現的個數.
***注意:不包括特殊字符
從鍵盤輸入一個不包含特殊字符的字符串(只有26個字母和0-9組成)。
3:給定一個字符串,把它變成首字母大寫,其他字母小寫的字符串.
從鍵盤輸入一個字符串,全部26個字母組成的。
4:子串在整串中出現的次數。
也就是說:獲取一個字符串中,指定的字串在該字符串中出現的次數.
例如:
"nbasdnbafllgnbahjnbakqqqqlnba" 在這個字符串中,多有個nba.
5:對字符串中字符進行自然順序排序。
"basckd"-->"abcdks"
先留做思考內容:
6:兩個字符串的最大相同子串。
兩個字符串的最大相同子串。
比如:
"sadabcdfghjkl"
werabcdtyu"
2、StringBuffer
(1)字符串的緩沖區,是一個容器。
(2)它和String的區別
它是緩沖區可變長度的。
(3)構造方法
StringBuffer() 構造一個其中不帶字符的字符串緩沖區,初始容量為 16 個字符。
StringBuffer(int num) 構造一個不帶字符,但具有指定初始容量的字符串緩沖區。
StringBuffer(String str) 構造一個字符串緩沖區,並將其內容初始化為指定的字符串內容。
(4)常用方法
A:增加數據
**append :添加各種類型的數據
**insert : 在容器指定位置插入各種類型的數據。
B:刪除數據
**deleteCharAt : 刪除指定位置的字符
**delete 還可以用於清空StringBuffer的緩沖區
C:替換
**replace
D:獲取
**charAt
E:長度和容量
**length() 元素的個數
**capacity 元素的理論值
F:獲取元素的位置
**indexOf
**lastIndexOf
G:截取
**substring(int start)
**substring(int start,int end)
H:反轉
**reverse
(5)字符串和StringBuffer的轉換
String-->StringBuffer通過構造:
如:StringBuffer sb = new StringBuffer(String str)
StringBuffer--String通過toString方法
如:StringBuffer sb = new StringBuffer();
sb.toString();
3、StringBuilder
和StringBuffer的功能是一樣的,但是有區別:
StringBuffer(JDK1.0)是線程安全的。
StringBuilder(JDK1.5)不保證線程安全。
一般來說,我們寫的程序都是單線程的,所以,用StringBuilder,效率高。
JDK版本的升級原則:
A:提高效率
B:提高安全性
C:簡化書寫
4、基本數據類型的對象包裝類
(1)為了更方便的操作每個基本數據類型,java對其提供了很多的屬性和方法供我們使用。
(2)用途:
**將基本數據類型封裝成對象的好處在於可以在對象中定義更多的功能操作該數據。
**常用的操作之一:用於基本數據類型與字符串之間的轉換。
A:方便操作
B:用於和字符串進行相互轉換
(3)基本數據類型和對象類型的對應
byte Byte
short Short
int Integer
long Long
float Float
double Double
boolean Boolean
char Character
(4)構造方法
字段摘要:
static int MAX_VALUE 值為 2^31-1 的常量,它表示 int 類型能夠表示的最大值
static int MIN_VALUE 值為 -2^31 的常量,它表示 int 類型能夠表示的最小值
static Class<Integer> TYPE 表示基本類型int的Class 實例
Integer(int value) 構造一個新分配的Integer對象,它表示指定的int值。
Inreger(String s) 注意:s必須是純數字的字符串。否則會有異常NumberFormatException
(5)幾個常用的方法
1 Integer.toBinaryString(); 2 //以二進制(基數 2)無符號整數形式返回一個整數參數的字符串表示形式。 3 Integer.toOctalString(); 4 //以八進制(基數 8)無符號整數形式返回一個整數參數的字符串表示形式。 5 Integer.toHexString(); 6 //以十六進制(基數 16)無符號整數形式返回一個整數參數的字符串表示形式。 7 static int Integer.parseInt(String s) 將字符串參數作為有符號的十進制整數進行解析, 8 //字符串必須是int型范圍內的數字字符串 9 static int Integer.parseInt(String s,int basic) 10 //使用第二個參數指定的基數,將字符串參數解析為有符號的整數. 11 //字符串必須是int型范圍內的數字字符串 12 short shortValue() //以short類型返回該Integer的值。 13 int intValue() //以int類型返回該Integer的值。 14 static Integer valueOf(int num) //返回一個表示指定的 int 值的 Integer 實例。 15 static Integer valueOf(String s) //返回保存指定的String的值的Integer對象。 16 static Integer valueOf(String s, int radix) 17 //返回一個Integer對象,該對象中保存了用第二個參數提供的基數進行 18 //解析時從指定的String中提取的值。
(6)類型轉換
1 //int -- Integer 2 int num = 20; 3 A:Integer i = new Integer(num); 4 B:Integer i = Integer.valueOf(num); 5 //Integer -- int 6 Integer i = new Integer(20); 7 A:int num = i.intValue(); 8 9 //int -- String 10 int num = 20; 11 A:String s = String.valueOf(num); 12 B:String s = ""+num; 13 C:String s = Integer.toString(num); 14 //String -- int 15 String s = "20"; 16 A:int num = Integer.parseInt(s); 17 B:Integer i = new Integer(s);或者Integer i = Integer.valueOf(s); 18 int num = i.intValue();
6、集合框架:
(1)為什么出現集合類?
面向對象對事物的體現都是以對象的形式,為了方便對多個對象的操作,就對對象進行存儲。
集合就是存儲對象最常用的一種方式.
(2)數組和集合都是容器,兩者有何不同?
**數組長度固定,而集合長度是可變的
**數組值可以存儲對象,還可以存儲基本數據類型;而集合只能存儲對象
**數組存儲數據類型是固定的,而集合存儲的數據類型不固定
(3)集合類的特點:
集合只能存儲對象
集合的長度是可變的
集合可以存儲不同類型的對象
(4)集合類框架(重要!!!要分清幾種容器間的區別):
**Collection:頂層接口
Collection是集合類的上級接口,繼承於他的接口主要有Set 和List.
|--->List:列表,元素是有序的(元素帶角標索引),可以有重復元素,可以有null元素。
|--->ArrayList(JDK1.2):底層的數據結構是數組數據結構,特點是查詢速度快(因為帶角標),
但是增刪速度稍慢,因為當元素多時,增刪一個元素則所有元素的角標都得改變
線程不同步。默認長度是10,當超過長度時,按50%延長集合長度。
|--->LinkedList(JDK1.2):底層數據結構式鏈表數據結構(即后面一個元素記錄前一個),
特點:查詢速度慢,因為每個元素只知道前面一個元素,但增刪速度快
因為元素再多,增刪一個,只要讓其前后的元素重新相連即可
線程是不同步的。
|--->Vector(JDK1.0):底層數據結構是數組數據結構.特點是查詢和增刪速度都很慢。
默認長度是10,當超過長度時,按100%延長集合長度。
線程同步。
(Vector功能跟ArrayList功能一模一樣,已被ArrayList替代)
**List使用注意!
|--->ArrayList:
(1)當往ArrayList里面存入元素沒什么要求時,即只要求有序就行時;
(2)當往ArrayList里面存入元素要求不重復時,比如存入學生對象,當同名同姓時
視為同一個人,則不往里面存儲。則定義學生對象時,需復寫equals方法
1 public boolean equals(Object obj) 2 { 3 if(!(obj instanceof Student)) 4 return false; 5 Student stu = (Student)obj; 6 return this.name.equals(stu.name)&&this.age==stu.age; 7 }
則往ArrayList集合通過add存入學生對象時,集合底層自己會調用學生類的equals方法,
判斷重復學生則不存入。
注:對於List集合,無論是add、contains、還是remove方法,判斷元素是否相同,
都是通過復寫equals方法來判斷!
|--->LinkedList
(1)LinkLedist的特有方法:
1 boolean offerFirst(E e) //在此列表的開頭插入指定的元素。 2 boolean offerLast(E e) //在此列表末尾插入指定的元素。 3 E peekFirst() //獲取但不移除此列表的第一個元素;如果此列表為空,則返回 null。 4 E peekLast() //獲取但不移除此列表的最后一個元素;如果此列表為空,則返回 null。 5 E pollFirst() //獲取並移除此列表的第一個元素;如果此列表為空,則返回 null。 6 E pollLast() //獲取並移除此列表的最后一個元素;如果此列表為空,則返回 null。
(2)通過LinkLedist的特有方法,可以實現某些數據特殊方式的存取,比如堆棧和隊列。
一般情況下,使用哪種List接口下的實現類呢?
如果要求增刪快,考慮使用LinkedList
如果要求查詢快,考慮使用ArrayList
如果要求線程安全,考慮使用Vector。
|--->Set:集合,元素是無序的(因為沒有索引),元素不可以重復。可以有null元素。
|--->HashSet(JDK1.2):底層數據結構是哈希表、存取速度快、元素唯一、線程不同步。
保證性元素唯一的原理:
先判斷元素的hashCode值是否相同,再判斷兩元素的equals方法是否為true
(往HashSet里面存的自定義元素要復寫hashCode和equals方法,以保證元素的唯一性!)
|--->TreeSet:底層數據結構式二叉樹。可以對Set集合中的元素進行排序。元素有序、線程不同步。
保證元素唯一性的依據:compareTo方法return 0
TreeSet排序的第一種方式:讓元素自身具備比較性,比如八種基本數據類型或則字符串,
實現Compareble接口,覆蓋compareTo方法,
此方式是元素的自然順序
TreeSet排序的第二種方式:當元素自身不具備比較性(比如存儲學生對象時)或者具備的
比較性不是我們所需要的比較性時(比如想字符串的長度排序),此時就需要讓集合自身具備自定義的比較性。
那如何讓集合自身具備比較性呢?可在集合初始化時,
就讓集合具備比較方式。即定義一個類,
實現Comparator接口,覆蓋compare方法。
**Set集合使用注意事項:
(1)HashSet:
通過new的方式往HashSet里面存的元素的hashCode都不同,但通常我們定義對象,
比如學生對象時,雖然是new的兩個學生對象,但是當他們name和age一樣時,我們認為是
同一個對象,所以為了保證元素的唯一性,我們通常在往HashSet集合里面存儲元素時,
在定義對象的類中通常復寫hashCode和equals方法。
public int hashCode() { return name.hashCode()+age*39; } public boolean equals(Object obj) { if(!(obj instanceof Student)) return false; Student stu = (Student)obj; return this.name.equals(stu.name)&&this.age==stu.age; }
HashSet是如何保證元素唯一性的呢?
**如果兩元素的hashCode值不同,則不會調用equals方法
**如果兩元素的hashCode值相同,則繼續判斷equals是否返回true;
**hashCode和equals方法雖然定義在自定義對象類里面,但不是我們手動調用
而是往HashSet集合里面存儲元素的時候,集合底層自己調用hashCode和equals
它自己拿對象去判斷,自己判斷兩元素是否是同一個元素。
(2)TreeSet:
TreeSet要求往里面存的元素具備比較性,否則會報錯。
TreeSet排序的第一種方式:讓元素自身具備比較性
定義對象類,實現Compareble接口,復寫compareTo方法,此方式是元素的自然順序
1 class Student implements Comparable 2 { 3 private String name; 4 private int age; 5 public Student(String name,int age) 6 { 7 this.name=name; 8 this.age=age; 9 } 10 public String getName() 11 { 12 return name; 13 } 14 public int getAge() 15 { 16 return age; 17 } 18 public int compareTo(Object obj) 19 { 20 if(!(obj instanceof Student)) 21 throw new RuntimeException("不是學生對象!"); 22 Student stu = (Student)obj; 23 int num = this.age-stu.age; 24 if(num==0) 25 return this.name.compareTo(stu.name); 26 return num; 27 } 28 }
TreeSet排序的第二種方式:讓集合具備比較性
當元素自身不具備比較性(比如存儲學生對象時)或者具備的
比較性不是我們所需要的比較性時(比如想字符串的長度排序),
此時就需要讓集合自身具備自定義的比較性。
那如何讓集合自身具備比較性呢?可在集合初始化時,
就讓集合具備比較方式。即定義一個類,
實現Comparator接口,覆蓋compare方法。
1 class StringLengthComparator implements Comparator 2 { 3 public int compare(Object obj1,Object obj2) 4 { 5 String s1 = (String)obj1; 6 String s2 = (String)obj2; 7 int num = new Integer(s1.length()).compareTo(new Integer(s2.length())); 8 if(num==0) 9 return s1.compareTo(s2); 10 return num; 11 } 12 } 13 class TreeSetTest 14 { 15 public static void main(String[] args) 16 { 17 TreeSet ts = new TreeSet(new StringLengthComparator()); 18 ts.add("addfg"); 19 ts.add("dfg"); 20 ts.add("agtuug"); 21 ts.add("vgjkg"); 22 sop(ts); 23 } 24 }
基本數據類型或字符串對象均實現了Comparable接口,故同種類型基本數據間具備比較性,即自然順序。
**Map:頂層接口
該集合存儲的是鍵值對,而且鍵是唯一的,Map和Set很像,Set集合底層就是使用了Map集合。
Map集合沒有迭代器,要取出元素必須先將Map集合轉換成Set集合才能遍歷元素。
|--->HashTable(JDK1.0):
底層是哈希表數據結構;
不可以使用null鍵和null值;
用作鍵的對象必須實現hashCode和equals方法來保證鍵的唯一性
線程同步,效率低
|--->HashMap(JDK1.2):
底層是哈希表數據結構;
允許使用null鍵和null值;
線程不同步,效率高;
保證元素唯一性的:
原理:先判斷元素的hashCode值是否相同,再判斷兩元素的equals方法是否為true
(往HashSet里面存的自定義元素要復寫hashCode和equals方法,以保證元素的唯一性!)
1 class Student { 2 private String name; 3 private int age; 4 public Student(String name, int age) { 5 super(); 6 this.name = name; 7 this.age = age; 8 } 9 public int getAge() { 10 return age; 11 } 12 public void setAge(int age) { 13 this.age = age; 14 } 15 public String getName() { 16 return name; 17 } 18 public void setName(String name) { 19 this.name = name; 20 } 21 22 @Override 23 public int hashCode(){ 24 return name.hashCode()+age*34; 25 } 26 @Override 27 public boolean equals(Object obj){ 28 29 if(!(obj instanceof Student)) 30 return false; 31 Student stu = (Student)obj; 32 return this.name.equals(stu.name)&&this.age==stu.age; 33 } 34 public class HashMapDemo1 { 35 public static void main(String[] args) { 36 Map<Student , String> hmap = new HashMap<Student , String>(); 37 hmap.put(new Student("001",20), "beijing"); 38 hmap.put(new Student("002",25), "hebei"); 39 hmap.put(new Student("003",50), "hainan"); 40 hmap.put(new Student("001",20), "beijing"); 41 42 System.out.println(hmap.size()); 43 Set<Student> keySet = hmap.keySet(); 44 Iterator<Student> it = keySet.iterator(); 45 while(it.hasNext()){ 46 Student stu = it.next(); 47 String addr = hmap.get(stu); 48 System.out.println(stu.getName()+".."+stu.getAge()+"::"+addr); 49 } 50 } 51 }
|--->TreeMap(JDK1.0):
底層是二叉樹結構;
允許使用null鍵和null值;
線程不同步;
可以給Map集合中的鍵進行排序.
TreeMap排序的第一種方式:讓元素自身具備比較性,比如八種基本數據類型或則字符串,
實現Compareble接口,覆蓋compareTo方法,此方式是元素的自然順序
TreeMap排序的第二種方式:當元素自身不具備比較性(比如存儲學生對象時)或者具備的
比較性不是我們所需要的比較性時(比如想字符串的長度排序),
此時就需要讓集合自身具備自定義的比較性。
那如何讓集合自身具備比較性呢?可在集合初始化時,就讓集合具備比較方式。即定義一個類,
實現Comparator接口,覆蓋compare方法。
1 class Student implements Comparable<Student>{ 2 private String name; 3 private int age; 4 public Student(String name, int age) { 5 super(); 6 this.name = name; 7 this.age = age; 8 } 9 public int getAge() { 10 return age; 11 } 12 public void setAge(int age) { 13 this.age = age; 14 } 15 public String getName() { 16 return name; 17 } 18 public void setName(String name) { 19 this.name = name; 20 } 21 @Override 22 public int compareTo(Student stu) { 23 int num = new Integer(this.age).compareTo(new Integer(stu.age)); 24 if(num==0) 25 return this.name.compareTo(stu.name); 26 return num; 27 } 28 } 29 30 public class HashMapDemo1 { 31 public static void main(String[] args) { 32 33 Map<Student , String> tmap = new TreeMap<Student , String>(); 34 tmap.put(new Student("001",20), "beijing"); 35 tmap.put(new Student("002",25), "hebei"); 36 tmap.put(new Student("003",50), "hainan"); 37 tmap.put(new Student("001",20), "beijing"); 38 39 System.out.println(tmap.size()); 40 Set<Student> keySet1 = tmap.keySet(); 41 Iterator<Student> it1 = keySet1.iterator(); 42 while(it1.hasNext()){ 43 Student stu = it1.next(); 44 String addr = tmap.get(stu); 45 System.out.println(stu.getName()+".."+stu.getAge()+"::"+addr); 46 } 47 } 48 }
**Iterator:對collection進行迭代的迭代器.迭代器取代了Enumeration。
迭代器和枚舉的區別:
迭代器允許調用者利用定義良好的語義在迭代期間從迭代器所指向的collection移除元素
方法名稱得到了改進,簡化書寫
**LisIterator:系列表迭代器,允許程序員按任一方向遍歷列表、迭代期間修改列表
**Comparable:此接口強行對實現它的每個類的對象進行整體自然排序。使元素具備比較性
**Comparator:強行對某個對象collection進行整體排序的比較函數,使集合具備比較性
**Collections:此類完全由在 collection 上進行操作或返回 collection 的靜態方法組成。
**Arrays:此類包含用來操作數組(比如排序和搜索)的各種靜態方法
7、集合類各容器方法
**接口Collection方法摘要(沒有構造方法)
1 a)添加: 2 i. boolean add(E e) 3 j. boolean addAll(Collection c) 4 b)刪除: 5 i. void clear()://清空容器 6 j. boolean remove(Objec object):// 7 k. boolean removeAll(Collection c):// 8 c)判斷: 9 i. boolean contains(Object object)://判斷是否包含此元素 10 j. boolean containsAll(Collection c)://判斷是否包含一堆元素 11 k. boolean equals(Object object)://比較此collection與指定對象是否相等 12 m. boolean isEmpty()://判斷是否集合為空 13 d)獲取: 14 h. Iterator iterator()://取出 15 i. int hashCode()://返回此collection的哈希值 16 j. int size()://返回此collection中元素的個數 17 k. boolean retainAll(Collection c)://取交集 18 m. Object toArray()://返回此collection中所有元素的數組 19 n. T[] toArray(T[] a)://返回包含此collection中所有元素的數值。
*****List集合子類及其方法
(1)List接口是Collection接口的一個子接口。
(2)List接口中的元素有如下特點(對角標的操作都是特有方法,因為有序):
A:元素有序(存儲順序和取出順序一致)
B:元素可以重復
(3)List接口中的特有方法
1 A:add(int index,Object obj)://在指定位置加入元素 2 B:remove(int index)://移除指定位置的元素 3 C:set(int index,Object obj)://修改指定位置的元素 4 D:get(int index)://獲取指定位置的元素 5 E:indexOf(Object obj)://獲取指定元素的位置 6 F:subList(int start,int end)://從一個大的List中截取一個小的List 7 G:listIterator()://返回一個List接口特有的迭代器
(1)、ArrayList:
|--->構造方法摘要:(少用,不是重點)
|--->方法摘要:
|--->添加:
1 boolean add(E e):// 將指定的元素添加到此列表的尾部。 2 void add(int index, E element)://將指定的元素插入此列表中的指定位置。 3 boolean addAll(Collection<? extends E> c)://按照指定 collection 的迭代器所返回的元素順序,將該 collection 中的所有元素添加到此列表的尾部 4 boolean addAll(int index, Collection<? extends E> c)://從指定的位置開始,將指定 collection中的所有元素插入到此列表中。
|--->刪除:
1 void clear():// 移除此列表中的所有元素。 2 E remove(int index):// 移除此列表中指定位置上的元素。 3 boolean remove(Object o):// 移除此列表中首次出現的指定元素(如果存在)。 4 protected void removeRange(int fromIndex, int toIndex)://移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之間的所有元素。 5 boolean removeAll(Collection<?> c):// 從列表中移除指定 collection 中包含的其所有元素
|--->獲取:
1 E get(int index):// 返回此列表中指定位置上的元素。 2 int indexOf(Object o):// 返回此列表中首次出現的指定元素的索引,或如果此列表不包含元素,則返回 -1。 3 int lastIndexOf(Object o)://返回此列表中最后一次出現的指定元素的索引,或如果此列表不包含索引,則返回 -1。 4 public List<E> subList(int fromIndex,int toIndex):// 返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之間的部分視圖。 5 Iterator<E> iterator():// 返回按適當順序在列表的元素上進行迭代的迭代器。 6 ListIterator<E> listIterator(int index)://返回列表中元素的列表迭代器(按適當順序),從列表的指定位置開始。
|--->修改:(特有方法!!)
E set(int index, E element): 用指定的元素替代此列表中指定位置上的元素。
(2)LinkedList:
|--->構造方法摘要:
LinkedList(): 構造一個空列表。
LinkedList(Collection<? extends E> c): 構造一個包含指定 collection 中的元素的列表,這些元素按其 collection 的迭代器返回的順序排列。
|--->方法摘要:(特有的)
1 |--->添加 2 void addFirst(E e): //將指定元素插入此列表的開頭。 3 void addLast(E e): //將指定元素添加到此列表的結尾。 4 |--->獲取元素,但不刪除元素 5 E get(int index): //返回此列表中指定位置處的元素。 6 E getFirst(): //返回此列表的第一個元素。 7 E getLast(): //返回此列表的最后一個元素。 8 |--->獲取元素且刪除元素 9 E remove(): //獲取並移除此列表的頭(第一個元素)。 10 E remove(int index): //移除此列表中指定位置處的元素。 11 boolean remove(Object o): 從此列表中移除首次出現的指定元素(如果存在)。 12 E removeFirst(): //移除並返回此列表的第一個元素。 13 E removeLast(): //移除並返回此列表的最后一個元素。 14 |--->修改 15 E set(int index, E element) //將此列表中指定位置的元素替換為指定的元素。
(3)Vector:
|--->構造方法摘要:
Vector(): 構造一個空向量,使其內部數據數組的大小為 10,其標准容量增量為零。
Vector(Collection<? extends E> c): 構造一個包含指定 collection 中的元素的向量,這些元素按其 collection 的迭代器返回元素的順序排列。
|--->方法摘要:
1 |--->添加: 2 boolean add(E e): //將指定元素添加到此向量的末尾。 3 void add(int index, E element): //在此向量的指定位置插入指定的元素。 4 boolean addAll(Collection<? extends E> c): //將指定 Collection 中的所有元素添加到此向量的末尾,按照指定 collection 的迭代器所返回的順序添加這些元素。 5 boolean addAll(int index, Collection<? extends E> c): //在指定位置將指定 Collection 中的所有元素插入到此向量中。 6 |--->獲取: 7 Enumeration<E> elements(): //返回此向量的組件的枚舉。 8 Vector特有的取出方式: 9 枚舉和迭代器很像,其實枚舉和迭代器是一樣的,只是因為枚舉的名稱和方法的名稱名字都過長,所以枚舉被迭代器取代了。 10 11 |--->枚舉Enumeration的方法摘要: 12 boolean hasMoreElements(): //測試此枚舉是否包含更多的元素。 13 E nextElement(): //如果此枚舉對象至少還有一個可提供的元素,則返回此枚舉的下一個元素。
*****Set集合子類及其方法
(1)HashSet:它不保證set的迭代順序;特別是它不保證該順序恆久不變.此類允許使用null元素。
|--->構造方法:
HashSet() 構造一個新的空 set,其底層 HashMap 實例的默認初始容量是 16,加載因子是 0.75。
HashSet(Collection<? extends E> c) 構造一個包含指定 collection 中的元素的新 set。
|--->方法摘要:
1 boolean add(E e) //如果此 set 中尚未包含指定元素,則添加指定元素。 2 void clear() //從此 set 中移除所有元素。 3 Object clone() //返回此 HashSet 實例的淺表副本:並沒有復制這些元素本身。 4 boolean contains(Object o) //如果此 set 包含指定元素,則返回 true。 5 boolean isEmpty() //如果此 set 不包含任何元素,則返回 true。 6 Iterator<E> iterator() //返回對此 set 中元素進行迭代的迭代器。 7 boolean remove(Object o) //如果指定元素存在於此 set 中,則將其移除。 8 int size() //返回此 set 中的元素的數量(set 的容量)。
(2)TreeSet:使用元素的自然順序對元素進行排序,或者根據創建 set 時提供的 Comparator 進行排序.
|--->構造方法:
TreeSet() 構造一個新的空 set,該set根據其元素的自然順序進行排序。
TreeSet(Collection<? extends E> c) 構造一個包含指定 collection 元素的新 TreeSet,它按照其元素的自然順序進行排序。
TreeSet(Comparator<? super E> comparator) 構造一個新的空 TreeSet,它根據指定比較器進行排序。
|--->方法摘要:
1 添加: 2 boolean add(E e) // 將指定的元素添加到此 set(如果該元素尚未存在於 set 中)。 3 boolean addAll(Collection<? extends E> c) // 將指定 collection 中的所有元素添加到此 set 中。 4 刪除: 5 void clear() // 移除此 set 中的所有元素。 6 boolean remove(Object o) // 將指定的元素從 set 中移除(如果該元素存在於此 set 中)。 7 E pollFirst() // 獲取並移除第一個(最低)元素;如果此 set 為空,則返回 null。 8 E pollLast() // 獲取並移除最后一個(最高)元素;如果此 set 為空,則返回 null。 9 獲取: 10 Iterator<E> iterator() // 返回在此 set 中的元素上按升序進行迭代的迭代器。 11 E first() // 返回此 set 中當前第一個(最低)元素。 12 E last() // 返回此 set 中當前最后一個(最高)元素。 13 int size() // 返回 set 中的元素數(set 的容量)。 14 判斷: 15 boolean isEmpty() // 如果此 set 不包含任何元素,則返回 true。 16 boolean contains(Object o) // 如果此 set 包含指定的元素,則返回 true。
**Map:將鍵映射到值的對象。Map集合沒有迭代器!
Map集合特點:該集合存儲鍵值對。而且鍵是唯一的。
|--->方法摘要:
1 |--->添加: 2 V put(K key, V value) //將指定的值與此映射中的指定鍵關聯(可選操作)。 3 void putAll(Map<? extends K,? extends V> m) //從指定映射中將所有映射關系復制到此映射中 4 |--->刪除: 5 void clear() //從此映射中移除所有映射關系(可選操作)。 6 V remove(Object key) //如果存在一個鍵的映射關系,則將其從此映射中移除(可選操作)。 7 |--->判斷 8 boolean containsKey(Object key) //如果此映射包含指定鍵的映射關系,則返回 true。 9 boolean containsValue(Object value) //如果此映射將一個或多個鍵映射到指定值,則返回 true。 10 boolean isEmpty() //如果此映射未包含鍵-值映射關系,則返回 true。 11 |--->獲取 12 int size() //返回此映射中的鍵-值映射關系數。 13 Collection<V> values() //返回此映射中包含的值的 Collection 視圖。
重點:Map集合沒有迭代器,以下是Map的兩種取出方式:
第一種:Set<K> keySet()
返回此映射中包含的鍵的Set視圖,將Map集合中所有的鍵存入Set集合,然后再通過Set集合的
迭代器取出所有的鍵,再根據get方法獲取每個鍵的值;
第二種:Set<Map.Entry<K,V>> entrySet()
返回此映射中包含的映射關系的Set視圖,將Map集合中的映射關系存入到Set集合中,
這個映射關系的數據類型是Map.entry,再通過Map.Entry類的方法再要取出關系里面的鍵和值
Map.Entry的方法摘要:
1 boolean equals(Object o) //比較指定對象與此項的相等性。 2 K getKey() //返回與此項對應的鍵。 3 V getValue() //返回與此項對應的值。 4 int hashCode() //返回此映射項的哈希碼值。 5 V setValue(V value) //用指定的值替換與此項對應的值(特有!!!)。
8、Map集合和Collection集合的區別?
1,
Map中一次存儲是鍵值對。
Collection中一次存儲是單個元素。
2,
Map的存儲使用的put方法。
Collection存儲使用的是add方法。
3,
Map集合沒有迭代器,Map的取出,是將Map轉成Set,在使用迭代器取出。
Collection取出,使用就是迭代器。
4,
如果對象很多,必須使用集合存儲。
如果元素存在着映射關系,可以優先考慮使用Map存儲或者用數組,
如果沒有映射關系,可以使用Collection存儲。
9、迭代器:Iterator(Map集合沒有迭代器)
(1)迭代器就是取出集合元素的方式
(2)迭代器的作用
因為每個集合中元素的取出方式都不一樣,於是就把元素的取出方式進行抽取,並定義在集合內部,
這樣取出方式就可以直接訪問集合內部的元素;
而每個容器的數據結構不同,所以取出動作的細節也不一樣,但是有共性內容:判斷和取出。
那么就將共性內容進行抽取,從而形成了接口Iterater
(3)獲取迭代器的方法:
Iterator<E> iterator() 返回在此 collection 的元素上進行迭代的迭代器。
Iterator<E> iterator() 返回在此 set 中的元素上進行迭代的迭代器。
(3)迭代器方法:
boolean hasNext() 如果仍有元素可以迭代,則返回 true。
E next() 返回迭代的下一個元素。
void remove() 從迭代器指向的collection中移除迭代器返回的最后一個元素(可選操作)。
10、列表迭代器:ListIterator
(1)List集合特有的迭代器ListIterator是Iterator的子接口,在迭代時,不可以通過集合對象的
方法操作集合中的元素,因為會發生ConcurrentModificationException(當方法檢測到對象的並發修改,
但不允許這種修改時,拋出此異常)
(2)Iterator方法有限,只能對元素進行判斷、取出和刪除的操作
ListIterator可以對元素進行添加和修改動作等。
(3)獲取列表迭代器方法:
ListIterator<E> listIterator() 返回此列表元素的列表迭代器(按適當順序)。
ListIterator<E> listIterator(int index) 返回此列表中的元素的列表迭代器(按適當順序),從列表中指定位置開始。
(4)列表迭代器方法:
1 void add(E e) //將指定的元素插入列表(可選操作)。 2 boolean hasPrevious() //如果以逆向遍歷列表,列表迭代器有多個元素,則返回 true。 3 int nextIndex() //返回對 next 的后續調用所返回元素的索引。 4 E previous() //返回列表中的前一個元素。 5 int previousIndex() 返回對 //previous 的后續調用所返回元素的索引。 6 void set(E e) //用指定元素替換 next 或 previous 返回的最后一個元素(可選操作)。
11、堆棧和隊列
堆棧:先進后出,比如杯子里的水
隊列:先進先出,比如水管的水
12、集合類各種容器的使用注意細節
(1)迭代器:
**迭代器的next方法是自動向下取元素,要避免出現NoSuchElementException。
也就是在迭代循環中調用一次next方法一次就要hasNext判斷一次,比如語句
sop(it.next()+"..."+it.next())會發生上述異常。
**迭代器的next方法返回值類型是Object,所以要記得類型轉換,應用泛型后就不用強轉
(2)List:
**List集合里面的元素因為是帶角標,所以List集合里面的元素都是有序的,
另外List集合可以包含重復元素,也可以包含null。
**List集合有迭代器Iterator,還有一個特有迭代器列表ListIterator
**List集合中判斷元素是否相同都是用equals方法,無論contains、remove都依賴equals方法
比如往ArrayList集合里面存放學生,同名同年齡視為同一個人,此時就需要在學生類復寫Object類里面的equals方法(非常重要!!!要注意!!)
(3)Set:
**Set接口里面存放的是元素是無序的,不可以有重復元素,可以包含null
**Set集合只有一種取出方式,就是迭代器Iterator
**Set集合功能和Collection是一致的,沒有特殊方法
|--->HashSet:
**集合里面存放的元素是無序的,唯一的
**底層數據結構是哈希表,哈希表結構的數據都是無序的,哈希表結構的操作效率都高效
**線程不同步
**保證元素唯一性的原理是:通過復寫hashCode和equals方法
****如果兩元素的hashCode值相同,則繼續判斷兩元素equals是否為真
****如果兩元素的hashCode值不同,則不會調用equals方法。
**當我們往HashSet集合存放自定義的元素時(比如學生對象),通常都要復寫hashCode和equals方法,
而且hashCode和equals方法不通過我們調用,HashSet集合底層內部自己調用,自己拿元素去比較
|--->TreeSet
**TreeSet集合可以對存放的元素進行排序,彌補了Set集合元素無序的缺點,且元素是唯一的
**底層數據結構是二叉樹,二叉樹結構都是有序的
**線程不同步
**TreeSet集合要求往集合里存放的元素自身具備比較性,否則會報錯
**TreeSet集合保證元素唯一性的依據是:通過compareTo或者compare方法中的來保證元素的唯一性。
TreeSet排序的第一種方式:讓元素自身具備比較性,
定義元素類實現Compareble接口,覆蓋compare方法,
此方式是元素的自然順序。
TreeSet排序的第二種方式:讓集合具備比較性
當元素自身不具備比較性或者具備的比較性不是
我們所需要的比較性時,此時就需要讓集合具備自定義的比較性。
那如何讓集合自身具備比較性呢?
可在集合初始化時,就讓集合具備比較方式。
即定義一個類,實現Comparator接口,覆蓋compare方法。
注:
**判斷元素唯一時,當主要條件一樣時,判斷次要條件
**兩種排序方式都在時,以比較器為主!!!
(4)Map:
|--Hashtable
底層是哈希表結構
線程安全的,並且鍵和值不能為null。
|--HashMap
底層是哈希表結構
線程不安全的,鍵和值可以為null。
|--LinkedHashMap
底層是鏈表和哈希表
線程不安全
|--TreeMap
底層是二叉樹
線程不安全的
13、Vector、HashSet、ArrayList、HashMap、Hashtable
- 如果想將一組對象按一定順序存取,在不考慮並發訪問的情況下會使用ArrayList
- 如果想將一組對象按一定順序存取,考慮並發訪問的情況下會使用Vector
- 如果想存儲一組無序但唯一的對象,會使用HashSet
- 如果想按關鍵字對對象進行存取,在不考慮並發訪問的情況下會使用HashMap
- 如果想按關鍵字對對象進行存取,考慮並發訪問的情況下會使用Hashtable
14、泛型
(1)為什么會出現泛型?
因為集合存放的數據類型不固定,故往集合里面存放元素時,存在安全隱患,
如果在定義集合時,可以想定義數組一樣指定數據類型,那么就可以解決該類安全問題。
JDK1.5后出現了泛型,用於解決集合框架的安全問題。
泛型是一個類型安全機制。
(2)泛型定義格式:通過<>來定義要操作的引用數據類型
ArrayList<String> al = new ArrayList<String>;
(3)泛型的好處:
**將運行時期出現的ClassCastException(類型轉換異常)問題轉移到編譯時期;
**避免了強制轉換的麻煩
(4)什么時候定義泛型?
泛型在集合框架中很常見,只要見到<>就要定義泛型。其實<>就是用來接收類型的。
當使用集合時,將集合中要存儲的數據類型作為參數傳遞到<>中即可
(5)泛型的形式
**泛型類:即自定義泛型類
A:當類中要操作的引用數據類型不確定時,早起定義Object來完成擴展,現在定義泛型來完成
B:局限性:泛型類定義的泛型,在整個類中有效,如果該泛型類的方法被調用,當泛型類的對象明確要操作的類型后,所有要操作的類型就被固定。
**泛型方法:泛型放在返回值前面,修飾符的后面
A:為了避免泛型類的局限性,讓不同方法可以操作不同的類型,而且類型還不確定,則可以將泛型定義在方法上
B:特殊之處:靜態方法不可以反問類上定義的泛型
如果靜態方法操作的應用數據類型不確定,可以講泛型定義在靜態方法上
**泛型接口:
當泛型定義在接口上時,則子類中要指定實現接口類型,同時還可以子類也可以定義為泛型類
(6)泛型的高級應用:?通配符
**當指定兩種泛型的集合,則迭代時也要定義兩種泛型的迭代器,麻煩,此時可通過將迭代器的泛型
改為?,如Iterator<?> it=al.iterator();
**兩種泛型限定
向上限定: ? extends E ;E可以接收E類型或者E的子類
向下限定: ? super E ;E可以接收E類型或者E的父類
15、高級for循環
(1)JDK1.5新特性,代替迭代器使用時的不爽,簡化書寫,底層原理是迭代器凡是支持迭代器的都支持高級for循環
高級for循環,只用於集合和數組的遍歷,集合只能用Collection不能用Map集合
只能把Map集合轉化成Set集合,才能用for循環。
(2)格式
for(數據類型 變量名:被遍歷的集合(Collection)或者數組)
{
}
(3)局限性:
必須要有遍歷的目標
對集合或者數組進行遍歷時,只能獲取集合元素,不能對集合元素進行操作
迭代器除了遍歷,還可以進行remove操作集合中的元素
列表迭代器還可以在遍歷過程中進行增刪改查的操作
(4)傳統for循環和高級for循環的區別
高級for循環有一個局限性,就是必須要有遍歷的目標(集合或者數組)
遍歷數組時建議使用傳統for循環,因為可以定義角標,比如打印100次helloworld時用傳統for循環方便
16、可變參數
(1)數組的可變參數
格式:
int... arr
(2)方法的可變參數
格式:
public static void show(String str,int... arr)
{
}
注意:可變參數一定要放在參數列表的最后面
17、靜態導入
**import static java.util.Arrays.* 導入的是Arrays這個類中所有的靜態方法
**當類名重名時,需要制定具體的報名
**當方法重名時,需要制定具體所屬的對象或者類
18、Collections類
Collections是針對集合類的一個幫助類,他提供一系列靜態方法實現對各種集合的搜索、排序、線程安全化等操作。
(1)此類完全由在 collection 上進行操作或返回 collection 的靜態方法組成。
(2)靜態方法摘要:
1 static <T> boolean addAll(Collection<? super T> c, T... elements) 2 //將所有指定元素添加到指定 collection 中。 3 static <T> void fill(List<? super T> list, T obj) 4 //使用指定元素替換指定列表中的所有元素。 5 static <T> boolean replaceAll(List<T> list, T oldVal, T newVal) 6 //使用另一個值替換列表中出現的所有某一指定值。 7 static void reverse(List<?> list) 8 //反轉指定列表中元素的順序。 9 static <T> Comparator<T> reverseOrder() 10 //返回一個比較器,它強行逆轉實現了 Comparable 接口的對象 collection 的自然順序 11 static <T> Comparator<T> reverseOrder(Comparator<T> cmp) 12 //返回一個比較器,它強行逆轉指定比較器的順序。
(3)Collections類特牛的方法:
集合有一個共同的缺點,那就是線程不安全,被多線程操作時,容易出現問題,雖然可以自己加鎖
但是麻煩。Collections提供特牛的方法,就是給它一個不同步的集合,它返回一個同步的安全的集合
1 static <T> Collection<T> synchronizedCollection(Collection<T> c) 2 //返回指定 collection 支持的同步(線程安全的)collection。 3 static <T> List<T> synchronizedList(List<T> list) 4 //返回指定列表支持的同步(線程安全的)列表。 5 static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) 6 //返回由指定映射支持的同步(線程安全的)映射。 7 static <T> Set<T> synchronizedSet(Set<T> s) 8 //返回指定 set 支持的同步(線程安全的)set。 9 static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m) 10 //返回指定有序映射支持的同步(線程安全的)有序映射。 11 static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s) 12 //返回指定有序 set 支持的同步(線程安全的)有序 set。
19、Arrays類
此類包含用來操作數組(比如排序和搜索)的各種方法。里面都是靜態方法。
如果指定數組引用為 null,則此類中的方法都會拋出 NullPointerException。
(1)靜態方法摘要:
static <T> List<T> asList(T... a)
返回一個受指定數組支持的固定大小的列表。
注意:
A:該方法將一個數組變成集合后,不可以使用集合的增刪方法,因為數組的長度是固定的!
如果增刪,則發生UnsupportedOprationException(不支持操作異常)
B:如果數組中的元素都是基本數據類型,則該數組變成集合時,會將該數組作為集合的一個
元素出入集合
C:如果數組中的元素都是對象,如String,那么數組變成集合后,數組中的元素就直接轉成
集合中的元素
20、數組變集合以及集合變數組的對比
(1)數組變集合:
方法:static <T> List<T> asList(T... a) 返回一個受指定數組支持的固定大小的列表。
好處:可以使用集合的思想和方法操作數組中的元素,數組是一個對象,但是數組中的功能很少
(2)集合變數組:
方法:Collction中的toArray方法
好處:可以限定對集合元素的操作,防止對集合的元素進行增刪,因為數組長度是固定的。
21、Collections類和Arrays類的使用。(重點)
A:Collections
排序
二分查找
反轉
B:Arrays
把數組變成字符串輸出
排序
二分查找
22、System
(1)描述系統信息的類
(2)該類沒有構造方法,該類的方法和屬性都是靜態的
(3)字段摘要:
static InputStream in “標准”輸入流。
static PrintStream out “標准”輸出流。
(4)方法摘要:
1 static void exit(int status) //終止當前正在運行的 Java 虛擬機。 2 static void gc() //運行垃圾回收器。 3 static Properties getProperties() //確定當前的系統屬性 4 static String getProperty(String key) //獲取指定鍵指示的系統屬性。 5 static String getProperty(String key, String def) //獲取用指定鍵描述的系統屬性。 6 static void setIn(InputStream in) //重新分配“標准”輸入流。 7 static void setOut(PrintStream out) //重新分配“標准”輸出流。 8 static void setProperties(Properties props) //將系統屬性設置為 Properties 參數。 9 static String setProperty(String key, String value) //設置指定鍵指示的系統屬性。
23、Runtime
(1)每個 Java 應用程序都有一個 Runtime 類實例,使應用程序能夠與其運行的環境相連接。
可以通過 getRuntime 方法獲取當前運行時。 應用程序不能創建自己的 Runtime 類實例。
(2)該類沒有構造函數,也就是它不能直接創建對象,但是它里里面的方法又不是靜態的
,故它一定有一個方法返回本類對象
(3)故該類是單例設計模式,保證在內存中只有一個對象
(4)方法摘要:
1 Process exec(String command) //在單獨的進程中執行指定的字符串命令 2 void gc() //運行垃圾回收器。 3 static Runtime getRuntime() //返回與當前 Java 應用程序相關的運行時對象 4 void exit(int status) //通過啟動虛擬機的關閉序列,終止當前正在運行的 Java 虛擬機
24、Date
(1)Date接口表示特定的瞬間,精確到毫秒
(2)構造方法
Date() 分配 Date 對象並初始化此對象,以表示分配它的時間(精確到毫秒)。
Date(long date) 分配Date對象並初始化此對象,以表示自從標准基准時間(稱為“歷元(epoch)”,
即1970年1月1日00:00:00GMT)以來的指定毫秒數。
(3)方法摘要:
int compareTo(Date anotherDate) 比較兩個日期的順序。
boolean equals(Object obj) 比較兩個日期的相等性。
25、Calendar
(1)直接已知子類: GregorianCalendar
(2)構造方法:
protected Calendar() 構造一個帶有默認時區和語言環境的 Calendar。
protected Calendar(TimeZone zone, Locale aLocale) 構造一個帶有指定時區和語言環境的 Calendar。
(3)方法摘要:
static Calendar getInstance() 使用默認時區和語言環境獲得一個日歷。
五、jdk1.5的新特性
(1)靜態導入:
**import語句可以導入一個類或某個包中的所有類
**import static語句導入一個類中的某個靜態方法或所有靜態方法
靜態導入后,靜態方法前面就不用寫類名.方法的方式類調用
**語法舉例:
import static java.lang.Math.sin;//導入一個靜態方法
import static java.lang.Math.*; //導入一個類中的所有靜態方法
**靜態導入使用注意:
當類名重復時,需要制定具體的包名;
當方法重名時,需要制定具體所屬的對象或者類
(2)可變參數:
**可變參數的特點:
*可變參數只能出現在參數列表的最后;
*...位於變量類型和變量名之間,前后有無空格都可以;
*調用可變參數的方法時,編譯器為該可變參數隱含創建一個數組,在方法體中以數組的形式訪問可變參數。
**可變參數舉例:
*變量類型... 變量名 如 int... arr 表示可變參數數組
*public static void show(String str , int... arr){}
(3)增強for循環:
**語法:
for ( type 變量名:集合變量名 ) { … }
**注意事項:
迭代變量必須在( )中定義!
集合變量可以是數組或實現了Iterable接口的集合類
**舉例:
public static int add(int x,int ...args) {
int sum = x;
for(int arg:args) {
sum += arg;
}
return sum;
}
**增強for循環代替了迭代器使用的不爽,簡化書寫
**增強for循環局限性:
對集合或者數組進行遍歷時,只能取元素,不能對集合進行操作
(4)基本數據類型的自動裝箱和拆箱
**基本數據類型
byte--->Byte
short--->Short
int--->Integer
long--->Long
float--->Float
double--->Double
char--->Character
boolean--->Boolean
**例子:
**裝箱:自動把一個基本數據類型的數據裝箱成一個該類型數據的對象引用
Integer i = 3;(jdk1.5之前這樣寫是不行的,編譯報錯)
**拆箱:自動把一個基本數據類型的對象引用拆箱成一個基本數據類型的數據,再參與運算
Integer i = 12;
sop(i+4);
**享元模式:
1 Integer num1 = 12; 2 3 Integer num2 = 12; 4 5 System.out.println(num1 == num2);//打印true 6 7 8 Integer num5 = Integer.valueOf(12); 9 10 Integer num6 = Integer.valueOf(12); 11 12 System.out.println(num5 == num6);//打印true 13 14 15 Integer num3 = 129; 16 17 Integer num4 = 129; 18 19 System.out.println(num3 == num4);//打印false
為什么前面的返回true而后面的運算返回false呢?
對於基本數據類型的整數,裝箱成Integer對象時,如果該數值在一個字節內,(-128~127),
一旦裝箱成Integer對象后,就把它緩存到磁里面,當下次,又把該數值封裝成Integer對象時
會先看磁里面有沒有該對象,有就直接拿出來用,這樣就節省了內存空間。因為比較小的整數,
用的頻率比較高,就沒必要每個對象都分配一個內存空間。
這就是享元模式!比如26個英文字母,10個阿拉伯數字
(5)枚舉
**為什么要有枚舉?
問題:要定義星期幾或性別的變量,該怎么定義?假設用1-7分別表示星期一到星期日,
但有人可能會寫成int weekday = 0;或即使使用常量方式也無法阻止意外。
枚舉就是要讓某個類型的變量的取值只能為若干個固定值中的一個,否則,編譯器就會報錯。
枚舉可以讓編譯器在編譯時就可以控制源程序中填寫的非法值,
普通變量的方式在開發階段無法實現這一目標。
**用普通類如何實現枚舉的功能?定義一個Weekday類來模擬實現:
步驟:
*私有化構造方法
*每個元素分別用一個公有的靜態成員變量表示(public static final)
*可以有若干公有方法或抽象方法。采用抽象方法定義nextDay就將大量的if.else語句轉移成了一個個獨立的類。
**枚舉的應用:
舉例:定義一個Weekday的枚舉。
擴展:枚舉類的values,valueOf,name,toString,ordinal等方法
(記住,講課時要先於自定義方法前介紹,講課更流暢)
總結:枚舉是一種特殊的類,其中的每個元素都是該類的一個實例對象。
例如可以調用WeekDay.SUN.getClass().getName和WeekDay.class.getName()。
**枚舉的高級應用:
**枚舉就相當於一個類,其中也可以定義構造方法、成員變量、普通方法和抽象方法。
**枚舉元素必須位於枚舉體中的最開始部分,枚舉元素列表的后要有分號與其他成員分隔。
把枚舉中的成員方法或變量等放在枚舉元素的前面,編譯器報告錯誤。
**帶構造方法的枚舉
構造方法必須定義成私有的
如果有多個構造方法,該如何選擇哪個構造方法?
枚舉元素MON和MON()的效果一樣,都是調用默認的構造方法。
**帶方法的枚舉
定義枚舉TrafficLamp
實現普通的next方法
實現抽象的next方法:每個元素分別是由枚舉類的子類來生成的實例對象,
這些子類采用類似內部類的方式進行定義。增加上表示時間的構造方法
**枚舉只有一個成員時,就可以作為一種單例的實現方式。
(6)泛型:
**泛型是提供給javac編譯器使用的,可以限定集合中的輸入類型,讓編譯器擋住源程序中的非法輸入,
編譯器編譯帶類型說明的集合時會去除掉“類型”信息,使程序運行效率不受影響,
對於參數化的泛型類型,getClass()方法的返回值和原始類型完全一樣。
由於編譯生成的字節碼會去掉泛型的類型信息,只要能跳過編譯器,
就可以往某個泛型集合中加入其它類型的數據,例如,用反射得到集合,再調用其add方法即可。
**ArrayList<E>類定義和ArrayList<Integer>類引用中涉及如下術語:
整個稱為ArrayList<E>泛型類型
ArrayList<E>中的E稱為類型變量或類型參數
整個ArrayList<Integer>稱為參數化的類型
ArrayList<Integer>中的Integer稱為類型參數的實例或實際類型參數
ArrayList<Integer>中的<>念着typeof
ArrayList稱為原始類型
**參數化類型與原始類型的兼容性:
參數化類型可以引用一個原始類型的對象,編譯報告警告,
例如,Collection<String> c = new Vector();//可不可以,不就是編譯器一句話的事嗎?
原始類型可以引用一個參數化類型的對象,編譯報告警告,
例如,Collection c = new Vector<String>();//原來的方法接受一個集合參數,新的類型也要能傳進去
**參數化類型不考慮類型參數的繼承關系:
Vector<String> v = new Vector<Object>(); //錯誤!///不寫<Object>沒錯,寫了就是明知故犯
Vector<Object> v = new Vector<String>(); //也錯誤!
編譯器不允許創建泛型變量的數組。即在創建數組實例時,
數組的元素不能使用參數化的類型,
例如,下面語句有錯誤:
Vector<Integer> vectorList[] = new Vector<Integer>[10];
**泛型限定:
**限定通配符的上邊界:
正確:Vector<? extends Number> x = new Vector<Integer>();
錯誤:Vector<? extends Number> x = new Vector<String>();
**限定通配符的下邊界:
正確:Vector<? super Integer> x = new Vector<Number>();
錯誤:Vector<? super Integer> x = new Vector<Byte>();
**提示:
限定通配符總是包括自己。
?只能用作引用,不能用它去給其他變量賦值
Vector<? extends Number> y = new Vector<Integer>();
Vector<Number> x = y;
上面的代碼錯誤,原理與Vector<Object > x11 = new Vector<String>();相似,
只能通過強制類型轉換方式來賦值。
六、IO流
1、IO流概述
(1)用來處理設備(硬盤,控制台,內存)間的數據。
(2)java中對數據的操作都是通過流的方式。
(3)java用於操作流的類都在io包中。
(4)按照流操作的數據的類型不同:分為字節流和字符流。字符流是為了方便中文的操作而來的。
(5)按照流的流向不同分為:輸入流,輸出流
2、IO流常用基類:
(1)字節流
輸出字節流:OutputStream:字節寫入流抽象類
|--->FileOutputStream:
字節寫入流
|--->BufferedOutputStream:
字節寫入流緩沖區
|--->PrintStream:
打印流
輸入字節流:InputStream:字節讀取流抽象類
|--->FileInputStream:
字節讀取流
|--->BufferedInputStream:
字節讀取流緩沖區
(2)字符流
輸出字符流:Writer:字符寫入流的抽象
|--->FileWriter:
字符寫入流
|--->BufferedWriter:
字符寫入流緩沖區
|--->OutputStreamWriter:
字符通向字節的轉換流(涉及鍵盤錄入時用)
|--->OutputStreamWriter:
打印流,可處理各種類型的數據
輸入字符流:Reader: 字符讀取流的抽象類
|--->FileReader:
字符讀取流
|--->LineNumberReader:
跟蹤行號的緩沖字符讀取流
|--->BufferedReader:
字符讀取流緩沖區
|--->InputStreamReader:
字節通向字符的轉換流(涉及鍵盤錄入時用)
(3)IO流常用基類方法摘要:
**字節寫入流:OutputStream:
1 void close() //關閉此輸出流並釋放與此流有關的所有系統資源。 2 void flush() //刷新此輸出流並強制寫出所有緩沖的輸出字節。 3 abstract void write(int b) //將指定的字節寫入此輸出流。 4 void write(byte[] b) //將 b.length 個字節從指定的 byte 數組寫入此輸出流。 5 void write(byte[] b, int off, int len) //將指定 byte 數組中從偏移量 off 開始的 len 個字節寫入此輸出流。
**字節讀取流:InputStream:
1 void close() //關閉此輸入流並釋放與該流關聯的所有系統資源。 2 int available() //(特有方法!!)返回此輸入流下一個方法調用可以不受阻塞地從此輸入流讀取(或跳過)的估計字節數。 3 abstract int read() //從輸入流中讀取數據的下一個字節。 4 int read(byte[] b) //從輸入流中讀取一定數量的字節,並將其存儲在緩沖區數組 b 中。 5 int read(byte[] b, int off, int len) //將輸入流中最多 len 個數據字節讀入 byte 數組。 6 long skip(long n) //跳過和丟棄此輸入流中數據的 n 個字節。
**字符寫入流:Writer:
1 abstract void close() // 關閉此流,但要先刷新它。 2 abstract void flush() // 刷新該流的緩沖。 3 void write(int c) // 寫入單個字符。 4 void write(char[] cbuf) // 寫入字符數組。 5 abstract void write(char[] cbuf, int off, int len) // 寫入字符數組的某一部分。 6 void write(String str) // 寫入字符串。 7 void write(String str, int off, int len) // 寫入字符串的某一部分。
**字符讀取流:Reader:
1 abstract void close() // 關閉該流並釋放與之關聯的所有資源。 2 int read() // 讀取單個字符。 3 int read(char[] cbuf) // 將字符讀入數組 4 abstract int read(char[] cbuf, int off, int len) // 將字符讀入數組的某一部分。 5 long skip(long n) // 跳過字符。
3、IO流常用字節流基類的子類:
**寫入流:
(1)FileOutputStream:
**構造方法:
1 FileOutputStream(String name) 2 //創建一個向具有指定名稱的文件中寫入數據的輸出文件流。 3 FileOutputStream(String name, boolean append) 4 //創建一個向具有指定 name 的文件中寫入數據的輸出文件流。 5 FileOutputStream(File file) 6 //創建一個向指定 File 對象表示的文件中寫入數據的文件輸出流。 7 FileOutputStream(File file, boolean append) 8 //創建一個向指定 File 對象表示的文件中寫入數據的文件輸出流。
**方法摘要:
1 public void flush() 2 void close() //關閉此文件輸出流並釋放與此流有關的所有系統資源。 3 void write(int b) //將指定字節寫入此文件輸出流。 4 void write(byte[] b, int off, int len) //將指定 byte 數組中從偏移量 off 開始的 len 個字節寫入此文件輸出流。 5 void write(int b) //將指定字節寫入此文件輸出流。
(2)BufferedOutputStream:
**構造方法:
1 BufferedOutputStream(OutputStream out) 3 //創建一個新的緩沖輸出流,以將數據寫入指定的底層輸出流。 5 BufferedOutputStream(OutputStream out, int size) 7 //創建一個新的緩沖輸出流,以將具有指定緩沖區大小的數據寫入指定的底層輸出流。
**方法摘要:
1 void flush() //刷新此緩沖的輸出流。 2 void write(byte[] b, int off, int len) //將指定 byte 數組中從偏移量 off 開始的 len 個字節寫入此緩沖的輸出流。 3 void write(int b) //將指定的字節寫入此緩沖的輸出流。
(3)PrintStream:打印流,可將各種類型的數據原樣打印,有自動刷新功能
**構造方法:
1 PrintStream(String fileName) 2 //創建具有指定文件名稱且不帶自動行刷新的新打印流。 3 PrintStream(File file) 4 //創建具有指定文件且不帶自動行刷新的新打印流。 5 PrintStream(OutputStream out) 6 //創建新的打印流。 7 PrintStream(OutputStream out, boolean autoFlush) (當autoFlush為true時具有自動刷新功能) 8 //創建新的打印流。
**方法摘要:
1 PrintStream append(char c) //將指定字符添加到此輸出流。 2 void close() //關閉流。 3 void flush() //刷新該流的緩沖。 4 void print(各種類型的數據:) //打印各種類型的數據 5 void println(各種類型的數據:)://自動換行,打印各種類型的數據 6 void write(byte[] buf, int off, int len) //將 len 字節從指定的初始偏移量為 off 的 byte 數組寫入此流。 7 void write(int b) //將指定的字節寫入此流。
**讀取流:
(1)FileInputStream:
**構造方法:
1 FileInputStream(String name) 2 //通過打開一個到實際文件的連接來創建一個 FileInputStream, 3 //該文件通過文件系統中的路徑名 name 指定。 4 FileInputStream(File file) 5 //通過打開一個到實際文件的連接來創建一個 FileInputStream, 6 //該文件通過文件系統中的 File 對象 file 指定。
**方法摘要:
1 int available() //(字節讀取流特有方法!!!) 2 //返回下一次對此輸入流調用的方法可以不受阻塞地從此輸入流讀取(或跳過)的估計剩余字節數。 3 int read() 4 //從此輸入流中讀取一個數據字節。 5 int read(byte[] b) 6 //從此輸入流中將最多 b.length 個字節的數據讀入一個 byte 數組中。 7 int read(byte[] b, int off, int len) 8 //從此輸入流中將最多 len 個字節的數據讀入一個 byte 數組中。 9 long skip(long n) 10 //從輸入流中跳過並丟棄 n 個字節的數據。
(2)BufferedInputStream:
**構造方法:
1 BufferedInputStream(InputStream in) 2 //創建一個 BufferedInputStream 並保存其參數,即輸入流 in,以便將來使用。 3 BufferedInputStream(InputStream in, int size) 4 //創建具有指定緩沖區大小的 BufferedInputStream 並保存其參數,即輸入流 in,以便將來使用。
**方法摘要:
1 int available() //(字節讀取流特有方法!!!) 2 //返回可以從此輸入流讀取(或跳過)、且不受此輸入流接下來的方法調用阻塞的估計字節數。 3 int read() 4 //參見 InputStream 的 read 方法的常規協定。 5 int read(byte[] b, int off, int len) 6 //從此字節輸入流中給定偏移量處開始將各字節讀取到指定的 byte 數組中。 7 long skip(long n) 8 //參見 InputStream 的 skip 方法的常規協定。
4、字符流常用基類的子類
**寫入流:
(1)FileWriter:
**構造方法:
1 FileWriter(String fileName) 2 //根據給定的文件名構造一個 FileWriter 對象。 3 FileWriter(String fileName, boolean append) 4 //根據給定的文件名以及指示是否附加寫入數據的 boolean 值來構造 FileWriter 對象。 5 FileWriter(File file) 6 //根據給定的 File 對象構造一個 FileWriter 對象。 7 FileWriter(File file, boolean append) 8 //根據給定的 File 對象構造一個 FileWriter 對象。 9 FileWriter(FileDescriptor fd) 10 //構造與某個文件描述符相關聯的 FileWriter 對象。
**方法摘要:跟Writer一樣
1 abstract void close() //關閉此流,但要先刷新它。 2 abstract void flush() //刷新該流的緩沖。 3 void write(int c) //寫入單個字符。 4 void write(char[] cbuf) 寫入字符數組。 5 abstract void write(char[] cbuf, int off, int len) //寫入字符數組的某一部分。 6 void write(String str) //寫入字符串。 7 void write(String str, int off, int len) //寫入字符串的某一部分。
(2)BufferedWriter:
**構造方法:
1 BufferedWriter(Writer out) 2 //創建一個使用默認大小輸出緩沖區的緩沖字符輸出流。 3 BufferedWriter(Writer out, int sz) 4 //創建一個使用給定大小輸出緩沖區的新緩沖字符輸出流。
**方法摘要:
1 void close() 2 //關閉此流,但要先刷新它。 3 void flush() 4 //刷新該流的緩沖。 5 void newLine() 6 //寫入一個行分隔符。 7 void write(char[] cbuf, int off, int len) 8 // 寫入字符數組的某一部分。 9 void write(int c) 10 //寫入單個字符。 11 void write(String s, int off, int len) 12 //寫入字符串的某一部分。
(3)OutputStreamWriter:字節通向字符的轉換流
**構造方法:
OutputStreamWriter(OutputStream out)
創建使用默認字符編碼的 OutputStreamWriter。
**方法摘要:
1 void write(char[] cbuf, int off, int len) 2 //寫入字符數組的某一部分。 3 void write(int c) 4 //寫入單個字符。 5 void write(String str, int off, int len) 6 //寫入字符串的某一部分。
(4)PrintWriter:
**構造方法:
1 PrintWriter(String fileName) 2 //創建具有指定文件名稱且不帶自動行刷新的新 PrintWriter。 3 PrintWriter(File file) 4 //使用指定文件創建不具有自動行刷新的新 PrintWriter。 5 PrintWriter(Writer out) 6 //創建不帶自動行刷新的新 PrintWriter。 7 PrintWriter(Writer out, boolean autoFlush) 8 //創建新 PrintWriter。 9 PrintWriter(OutputStream out) 10 //根據現有的 OutputStream 創建不帶自動行刷新的新 PrintWriter。 11 PrintWriter(OutputStream out, boolean autoFlush) 12 //通過現有的 OutputStream 創建新的 PrintWriter。
**方法摘要:
1 PrintWriter append(char c) 2 //將指定字符添加到此 writer。 3 void print(各種類型的數據:) 4 //打印各種類型的數據 5 void println(各種類型的數據:):自動換行 6 //打印各種類型的數據 7 void write(char[] buf) 8 //寫入字符數組。 9 void write(char[] buf, int off, int len) 10 //寫入字符數組的某一部分。 11 void write(int c) 12 //寫入單個字符。 13 void write(String s) 14 //寫入字符串。 15 void write(String s, int off, int len) 16 //寫入字符串的某一部分。
**讀取流:
(1)FileReader:
**構造方法:
1 FileReader(String fileName) 2 //在給定從中讀取數據的文件名的情況下創建一個新 FileReader。 3 FileReader(File file) 4 //在給定從中讀取數據的 File 的情況下創建一個新 FileReader。 5 FileReader(FileDescriptor fd) 6 //在給定從中讀取數據的 FileDescriptor 的情況下創建一個新 FileReader。
**方法摘要:和Reader基類方法一致:
1 abstract void close() //關閉該流並釋放與之關聯的所有資源。 2 int read() //讀取單個字符。 3 int read(char[] cbuf) //將字符讀入數組 4 abstract int read(char[] cbuf, int off, int len) //將字符讀入數組的某一部分。 5 long skip(long n) //跳過字符。
(2)BufferedReader:
**構造方法:
BufferedReader(Reader in)
創建一個使用默認大小輸入緩沖區的緩沖字符輸入流。
**方法摘要:
1 int read() 2 //讀取單個字符。 3 int read(char[] cbuf, int off, int len) 4 //將字符讀入數組的某一部分。 5 String readLine() 6 //讀取一個文本行。
(3)InputStreamReader:字符通向字節的橋梁:
**構造方法:
InputStreamReader(InputStream in)
創建一個使用默認字符集的 InputStreamReader。
**方法摘要:
1 int read() //讀取單個字符。 2 int read(char[] cbuf) //將字符讀入數組 3 abstract int read(char[] cbuf, int off, int len) //將字符讀入數組的某一部分。 4 long skip(long n) //跳過字符。
(4)LineNumberReader:
**構造方法:
LineNumberReader(Reader in)
使用默認輸入緩沖區的大小創建新的行編號 reader。
**方法摘要:
1 int read() 2 //讀取單個字符。 3 int read(char[] cbuf, int off, int len) 4 //將字符讀入數組中的某一部分。 5 String readLine() 6 //讀取文本行。 7 long skip(long n) 8 //跳過字符。 9 int getLineNumber() 10 //獲得當前行號。 11 void setLineNumber(int lineNumber) 12 //設置當前行號。
6、IO流常見需求:
****字符流:
(1)需求1:在硬盤上創建一個文件並寫入信息
用字符寫入流:FileWriter
1 FileWriter fw = new FileWriter("g:\\filewriter.txt"); 2 fw.write("輸入信息"); 3 fw.write("也可以寫入字符數組".toCharArray()); 4 fw.flush(); 5 fw.close();
(2)需求2:在原有文件上續寫數據
1 FileWriter fw = new FileWriter("g:\\filewriter.txt",true); 2 fw.write("還可以續寫信息"); 3 fw.write("也可以寫入字符數組".toCharArray()); 4 fw.flush(); 5 fw.close();
(3)需求3:讀取硬盤上的文本文件,並將數據打印在控制台
1 FileReader fr = new FileReader("g:\\filewriter.txt"); 2 //第一種讀取方法:一個一個字節的讀 3 int ch = 0; 4 ch = fr.read(); 5 sop((char)ch); 6 fr.close(); 7 //第二種讀取方法:利用數組來提高效率 8 char[] buf = new char[1024]; 9 int len = 0; 10 while((len = fr.read(buf))!=-1) 11 { 12 sop(new String(buf,0,len)); 13 } 14 fr.close();
(4)需求4:拷貝文本文件
1 //利用緩沖區提高數據讀寫效率 2 //(無緩沖區就相當於一滴一滴的喝水,有緩沖區就相當於一杯一杯的喝水) 3 BufferedReader bufr = new BufferedReader(new FileReader("g:\\filewriter.txt")); 4 BufferedWriter bufw = new BufferedWriter(new FileWriter("d:\\copyfilewriter.txt")); 5 String line = null; 6 while((line = bufr.readLine())!=null) 7 { 8 burw.write(line); 9 bufw.newLine(); 10 bufw.flush(); 11 } 12 bufr.close(); 13 bufw.close();
****字節流:字節流寫入時沒有刷新
(1)需求1:在硬盤上創建一個文件並寫入信息(字節流寫入時沒有刷新)
1 FileOutputStream fos = new FileOutputStream("g:\\filestream.txt"); 2 fos.write(97);//寫入一個字節,int:97代表寫入char:a 3 fos.write("也可以寫入字節數組".getBytes());//通常使用此種方式寫入,直觀! 4 fos.close();
(2)需求2:在硬盤已有文件上續寫數據(字節流寫入時沒有刷新)
1 FileOutputStream fos = new FileOutputStream("g:\\filestream.txt",true); 2 fos.write("創建字節寫入流時,傳進去一個true參數就可以繼續寫入信息".getBytes()); 3 fos.close();
(3)需求3:讀取硬盤上的文件
1 FileInputStream fis = new FileInputStream("g:\\filestream.txt"); 2 //第一種讀法:一個字節一個字節的讀(此種讀法慢) 3 int ch = 0; 4 while((ch = fis.read())!=-1) 5 { 6 sop((char)ch); 7 } 8 //第一種讀法:利用字節數組讀(此種讀法效率有一定提高) 9 byte[] buf = new byte[1024]; 10 int len = 0; 11 while((len = fis.read())!=-1) 12 { 13 sop(new String(buf,0,len)); 14 }
(4)需求4:拷貝字節文件,如圖片或者MP3或者電影
1 //第一種拷貝:不帶緩沖區(慢,還是效率問題) 2 FileInputStream fis = new FileInputStream("g:\\1.mp3"); 3 FileOutputStream fos = new FileOutputStream("g:\\copy1.mp3"); 4 byte[] buf = new byte[1024]; 5 int len = 0; 6 while((len = fis.read(buf))!=-1) 7 { 8 fos.(buf,0,len);//字節流寫入無需刷新 9 } 10 fis.close(); 11 fos.close(); 12 //第二種拷貝:帶緩沖區,高效 13 BufferedInputStream bufi = new BufferedInputStream(new FileInputStream("g:\\1.mp3")); 14 BufferedOutputStream bufo = new BufferedOutputStream(new FileOutputStream("g:\\copy1.mp3")); 15 int ch = 0; 16 while((ch = bufi.read())!=-1) 17 { 18 bufo.write(ch); 19 } 20 bufi.close(); 21 bufo.close();
****轉換流:
(1)需求1:讀取一個鍵盤錄入
1 InputStream in = System.in;//創建一個鍵盤錄入流,流不關則可以一直錄入 2 int by1 = in.read();//一次讀一個字節 3 int by2 = in.read();//一次讀一個字節 4 sop(by1);//假設鍵盤錄入的是abcd,則打印a 5 sop(by2);//假設鍵盤錄入的是abcd,則打印b 6 in.close();
(2)需求2:鍵盤錄入一行數據打印一行數據,如果錄入的是over則結束錄入
1 InputStream in = System.in; 2 StringBuilder sb = new StringBuilder(); 3 while(true) 4 { 5 int ch = in.read(); 6 if(ch=='\r') 7 continue; 8 if(ch=='\n') 9 { 10 String line = sb.toString(); 11 if("over".equals(line)) 12 break; 13 sop(line.toUpperCase());//輸出大寫 14 sb.delete(0.sb.length());//清除上一行錄入的數據 15 16 } 17 else 18 sb.append((char)ch); 19 } 20 in.close();
(3)需求3:發現需求2中其實就是讀一行的原理,故引入字節通向字符的橋梁:InputStreamReader
為提高效率加入緩沖區:
1 BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); 2 String line = null; 3 while((line = bufr.readLine())!=null) 4 { 5 if("over".equals(line)) 6 break; 7 sop(line.toUpperCase());//輸出大寫 8 } 9 bufr.close();
(4)需求4:鍵盤錄入數據並打印到控制台
1 BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); 2 BufferedWriter bufw = new BufferedWriter(new OntputStreamWriter(System.out)); 3 String line = null; 4 while((line = bufr.readLine())!=null) 5 { 6 if("over".equals(line)) 7 break; 8 bufw.write(line.toUpperCase()); 9 bufw.newLine(); 10 bufw.flush(); 11 } 12 bufr.close(); 13 bufw.close();
(5)需求5:將鍵盤錄入的數據存儲到硬盤文件
1 //則只需將(4)中的 2 //BufferedWriter bufw = new BufferedWriter(new OntputStreamWriter(System.out)); 3 //改為: 4 //BufferedWriter bufw = new BufferedWriter(new OntputStreamWriter(new FileWriter("g:\\demo.txt"))); 5 //即: 6 BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); 7 BufferedWriter bufw = new BufferedWriter(new OntputStreamWriter(new FileWriter("g:\\demo.txt"))); 8 String line = null; 9 while((line = bufr.readLine())!=null) 10 { 11 if("over".equals(line)) 12 break; 13 bufw.write(line.toUpperCase()); 14 bufw.newLine(); 15 bufw.flush(); 16 } 17 bufr.close(); 18 bufw.close();
(6)需求6:將硬盤文件的數據打印到控制台
1 //則只需將(4)中的 2 //BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); 3 //改為: 4 //BufferedReader bufr = new BufferedReader(new InputStreamReader(new FileReader("g:\\demo.txt"))); 5 //即: 6 BufferedReader bufr = new BufferedReader(new InputStreamReader(new FileReader("g:\\demo.txt"))); 7 BufferedWriter bufw = new BufferedWriter(new OntputStreamWriter(System.out)); 8 String line = null; 9 while((line = bufr.readLine())!=null) 10 { 11 if("over".equals(line)) 12 break; 13 bufw.write(line.toUpperCase()); 14 bufw.newLine(); 15 bufw.flush(); 16 } 17 bufr.close(); 18 bufw.close();
7、流操作的規律:
****流操作的難點:流對象很多,不知道具體用哪個
****規律:
(1)第一步:先明確源和目的
源:
文本:用Reader
字節:用InputStream
目的:
文本:用Writer
字節:用OutputStream
(2)第二步:明確是不是純文本
是:用字符流;
不是:用字節流
(3)第三步:明確流體系后,通過設備來明確具體使用哪個流對象
源設備:
鍵盤:System.in
硬盤:文件流File
內存:數組流ArrayStream
目的設備:
鍵盤:System.out
硬盤:文件流File
內存:數組流ArrayStream
8、File類
構造方法:
1 File(String pathname) 2 //通過將給定路徑名字符串轉換為抽象路徑名來創建一個新 File 實例。 3 File(String parent, String child) 4 //根據 parent 路徑名字符串和 child 路徑名字符串創建一個新 File 實例。 5 File(File parent, String child) 6 //根據 parent 抽象路徑名和 child 路徑名字符串創建一個新 File 實例。
方法摘要:
1 //(1)創建: 2 boolean createNewFile() 3 //當且僅當不存在具有此抽象路徑名指定名稱的文件時,不可分地創建一個新的空文件。 4 boolean mkdir() 5 //創建一級文件夾 6 boolean mkdirs() 7 //創建多級文件夾 8 //(判斷): 9 boolean canExecute() 10 //測試應用程序是否可以執行此抽象路徑名表示的文件。 11 boolean canRead() 12 //測試應用程序是否可以讀取此抽象路徑名表示的文件。 13 boolean canWrite() 14 //測試應用程序是否可以修改此抽象路徑名表示的文件。 15 int compareTo(File pathname) 16 //按字母順序比較兩個抽象路徑名。 17 boolean isAbsolute() 18 //測試此抽象路徑名是否為絕對路徑名。 19 boolean isDirectory() 20 //測試此抽象路徑名表示的文件是否是一個目錄。 21 boolean isFile() 22 //測試此抽象路徑名表示的文件是否是一個標准文件。 23 boolean isHidden() 24 測試此抽象路徑名指定的文件是否是一個隱藏文件。 25 boolean exists() 26 //測試此抽象路徑名表示的文件或目錄是否存在。 27 //(3)獲取: 28 String getParent() 29 //返回此抽象路徑名父目錄的路徑名字符串;如果此路徑名沒有指定父目錄,則返回 null。 30 File getParentFile() 31 //返回此抽象路徑名父目錄的抽象路徑名;如果此路徑名沒有指定父目錄,則返回 null。 32 String getName() 33 //返回由此抽象路徑名表示的文件或目錄的名稱。 34 String getPath() 35 //將此抽象路徑名轉換為一個路徑名字符串。 36 String getAbsolutePath() 37 //返回此抽象路徑名的絕對路徑名字符串。 38 File getAbsoluteFile() 39 //返回此抽象路徑名的絕對路徑名形式。 40 //(4)刪除: 41 boolean delete() 42 //刪除此抽象路徑名表示的文件或目錄。 43 oid deleteOnExit() 44 //在虛擬機終止時,請求刪除此抽象路徑名表示的文件或目錄。 45 //(5)獲取全部:(非常重要!!!) 46 String[] list() 47 //返回一個字符串數組,這些字符串指定此抽象路徑名表示的目錄中的文件和目錄。 48 String[] list(FilenameFilter filter) 49 //返回一個字符串數組,這些字符串指定此抽象路徑名表示的目錄中滿足指定過濾器的文件和目錄。 50 File[] listFiles() 51 //返回一個抽象路徑名數組,這些路徑名表示此抽象路徑名表示的目錄中的文件。 52 File[] listFiles(FileFilter filter) 53 //返回抽象路徑名數組,這些路徑名表示此抽象路徑名表示的目錄中滿足指定過濾器的文件和目錄。 54 55 //****FilenameFilter接口只有一個方法: 56 boolean accept(File dir, String name) 57 //測試指定文件是否應該包含在某一文件列表中。 58 //****FileFilter接口只有一個方法: 59 boolean accept(File dir, String name) 60 //測試指定文件是否應該包含在某一文件列表中。
8、File類常見需求:
(1)文件名過濾:列出給定目錄的所有.java文件
1 public void showFileName(File file) 2 { 3 String[] filenames = file.list(new FilenameFilter()//匿名內部類 4 { 5 public boolean accept(File dir,String name)//復寫唯一方法 6 { 7 return name.endsWith(".java");//列出所有.java文件 8 } 9 }); 10 }
(2)列出指定目錄下的所有文件和文件夾(遞歸)
**示例1:不帶層次遞歸:
1 public static void showDir(File dir) 2 { 3 File[] files = dir.listFile(); 4 for(int i = 0;i<files.length;i++) 5 { 6 if(files[i].isDirectory&&!files[i].isHidden()) 7 showDir(files[i]); 8 else 9 sop(files[i]); 10 } 11 }
**示例2:帶層次遞歸:
1 public static void showDir(File dir,int level) 2 { 3 sop(getLevel(level)+C);//進來先打印層次和目錄 4 level++; 5 File[] files = dir.listFile(); 6 for(int i = 0;i<files.length;i++) 7 { 8 if(files[i].isDirectory&&!files[i].isHidden()) 9 showDir(files[i]); 10 else 11 sop(getLevel(level)+files[i]);//是文件就打印層次和目錄 12 } 13 } 14 public static String getLevel(int level) 15 { 16 sop("|--"); 17 StringBuilder sb = new StringBuilder(); 18 for(int i=0;i<level;i++) 19 { 20 sb.inset(0."| ") 21 } 22 return sb.toString(); 23 }
(3)需求:刪除帶內容的目錄:
1 public static void removeDir(File dir) 2 { 3 File[] files = file.listFile(); 4 for(int i = 0;i<files.length;i++) 5 { 6 if(files[i].isDirectory&&!files[i].isHidden()) 7 removeDir(files[i]);//如果是文件夾則繼續調用函數 8 else//如果是文件則刪除。注意刪除的時候打印刪除的結果,防止誤刪或者重刪的情況 9 sop(files[i].toString()+"::"+files[i].delete()); 10 } 11 sop(dir+"::"+dir.delete()); 12 }
(4)需求:將制定目錄下的java文件的絕對路徑存儲到文本文件中。
思路:
**對指定目錄進行遞歸
**獲取遞歸過程中所有java文件的路徑
**將這些路徑存儲到集合中
**將集合中的數據寫入文件中
1 //對指定目錄進行遞歸並將所以Java文件存儲到集合中 2 public static void getFileName(File file,ArrayList<File> arraylist){ 3 File[] files = file.listFiles(); 4 for (int i = 0; i < files.length; i++) { 5 if(files[i].isDirectory()&&!files[i].isHidden()){ 6 getFileName(files[i],arraylist); 7 }else{ 8 if(files[i].getName().endsWith(".java")){ 9 arraylist.add(files[i]); 10 } 11 } 12 } 13 } 14 //將集合中所有數據存儲到新文件中 15 public static void saveFileToNewDir(ArrayList<File> arraylist,File newDir){ 16 BufferedWriter bufw = null; 17 try { 18 bufw = new BufferedWriter(new FileWriter(newDir)); 19 for (File file : arraylist) { 20 String fileAbsolutePath = file.getAbsolutePath(); 21 bufw.write(fileAbsolutePath); 22 bufw.newLine(); 23 bufw.flush(); 24 } 25 } catch (Exception e) { 26 System.out.println("文件寫入失敗"); 27 }finally{ 28 try { 29 if(bufw!=null) 30 bufw.close(); 31 } catch (Exception e2) { 32 System.out.println("文件寫入流關閉失敗"); 33 } 34 } 35 }
9、Properties
(1)Properties是HashTable的子類,具備Map集合的特點,里面存儲的是鍵值對
(2)Properties是IO流合集合相結合的集合容器
(3)Properties的特點是可以用於存儲鍵值對形式的配置文件
(4)構造方法:
Properties()
創建一個無默認值的空屬性列表。
Properties(Properties defaults)
創建一個帶有指定默認值的空屬性列表。
(5)方法摘要:
1 Object setProperty(String key, String value) 2 //調用 Hashtable 的方法 put。 3 String getProperty(String key) 4 //用指定的鍵在此屬性列表中搜索屬性。 5 void load(InputStream inStream) 6 //從輸入流中讀取屬性列表(鍵和元素對)。 7 void load(Reader reader) 8 //按簡單的面向行的格式從輸入字符流中讀取屬性列表(鍵和元素對)。 9 void list(PrintStream out) 10 //將屬性列表輸出到指定的輸出流。 11 void list(PrintWriter out) 12 //將屬性列表輸出到指定的輸出流。 13 void store(OutputStream out, String comments) 14 //以適合使用 load(InputStream) 方法加載到 Properties 表中的格式, 15 //將此 Properties 表中的屬性列表(鍵和元素對)寫入輸出流。 16 void store(Writer writer, String comments) 17 //以適合使用 load(Reader) 方法的格式,將此 Properties 表中的 18 //屬性列表(鍵和元素對)寫入輸出字符。 19 Set<String> stringPropertyNames() 20 //返回此屬性列表中的鍵集,其中該鍵及其對應值是字符串,如果在主屬性列表中 21 //未找到同名的鍵,則還包括默認屬性列表中不同的鍵
(6)Properties代碼示例:
1 public static void show() 2 { 3 Properties prop = new Properties(); 4 prop.setProperty("張三","26"); 5 prop.setProperty("李四","30"); 6 prop.setProperty("王五","35"); 7 sop(prop); 8 String value = prop.getProperty("張三"); 9 10 Set<String> keys = prop.stringPropertyName(); 11 for(String key : values) 12 { 13 sop(key+":"+prop.getPropety(key)); 14 } 15 }
(7)需求:記錄應用程序的使用次數,如果使用次數已到,則提示用戶注冊。
思路:
**第一次使用時建立一個配置文件用於記錄使用次數
**每次使用都加載該配置文件,並先判斷已使用次數
**每次使用完使用次數加1,寫入配置文件
1 public static void main(String[] args) throws IOException{ 2 Properties prop = new Properties();//定義Properties,用來和IO流結合 3 File file = new File("library\\time.ini");//配置文件 4 if(!file.exists()) 5 file.createNewFile();//如果文件不存在則創建文件(用於第一次使用時創建文件) 6 FileInputStream fis = new FileInputStream(file);//定義字節讀取流,讀取配置文件中記錄的使用次數 7 prop.load(fis);//載入流,以獲取文件中配置的鍵值對 8 int count = 0;//定義使用次數 9 String countValue = prop.getProperty("time");//通過鍵獲取值 10 if(countValue!=null){//第一次時countValue為null 11 count = Integer.parseInt(countValue);//將字符串次數變成數字次數 12 if(count>3){ 13 System.out.println("您使用次數已到,繼續使用請注冊!"); 14 return; 15 } 16 } 17 count++;//如果使用次數未到則次數加1 18 prop.setProperty("time", count+"");//配置新的鍵值對 19 FileWriter fos = new FileWriter(file); 20 prop.store(fos, "這是應用程序使用次數的配置文件");//將新的鍵值對寫入文件 21 fis.close(); 22 fos.close(); 23 }
10、IO中的其他流:
(1)打印流:
**PrintWriter:字符打印流
****構造方法:
1 PrintWriter(String fileName) 2 //創建具有指定文件名稱且不帶自動行刷新的新 PrintWriter。 3 PrintWriter(File file) 4 //使用指定文件創建不具有自動行刷新的新 PrintWriter。 5 PrintWriter(Writer out) 6 //創建不帶自動行刷新的新 PrintWriter。 7 PrintWriter(Writer out, boolean autoFlush) 8 //自動刷新 9 PrintWriter(OutputStream out) 10 //根據現有的 OutputStream 創建不帶自動行刷新的新 PrintWriter。 11 PrintWriter(OutputStream out, boolean autoFlush) 12 //自動刷新
****方法摘要:
1 PrintWriter append(char c) 2 //將指定字符添加到此 writer。 3 void close() 4 //關閉該流並釋放與之關聯的所有系統資源。 5 void flush() 6 //刷新該流的緩沖。 7 void print(Object obj) 8 //打印對象。 9 void print(String s) 10 //打印字符串。 11 void println() 12 //通過寫入行分隔符字符串終止當前行。
**PrintStream:字節打印流
****構造方法:
1 PrintStream(String fileName) 2 //創建具有指定文件名稱且不帶自動行刷新的新打印流。 3 PrintStream(File file) 4 //創建具有指定文件且不帶自動行刷新的新打印流。 5 PrintStream(OutputStream out) 6 //創建新的打印流。 7 PrintStream(OutputStream out, boolean autoFlush) 8 //創建新的打印流。
****方法摘要:
1 PrintWriter append(char c) 2 //將指定字符添加到此 writer。 3 void close() 4 //關閉該流並釋放與之關聯的所有系統資源。 5 void flush() 6 //刷新該流的緩沖。 7 void print(Object obj) 8 //打印對象。 9 void print(String s) 10 //打印字符串。 11 void println() 12 //通過寫入行分隔符字符串終止當前行。
(2)對象序列化:
**對象實體化:找一個介質,能長期的存儲對象。
**對象的屬性在Java程序中,都是存在於對內存中,隨着對象的消失而消失,
而ObjectOutputStream可以將對象實體化
**Serializable接口沒有一個方法,也就是說其是一個標記接口。比如蓋章的豬肉才是安全的。
**只有實現Serializable接口的子類才能被ObjectOutputStream系列化寫入流,當某個類實現該接口后,會被Java自動分配UID號,以便編譯器識別,區分不同對象。
**用ObjectOutputStream系列化的對象存儲到文件后,該文件是亂碼,也就是不可讀的得用ObjectInputStream讀取該類對象的屬性。
**由於對象是有Java給對象分配相應的UID號,而UID號是根據對象的屬性不同而分配的。
當一個類對象被系列化到文件后,如果該類改動了對象的屬性,比如將某個成員變量變成私有
則該對象再用ObjectInputStream讀取時會報異常,也就是說該系列化到文件的對象不能再被使用了
那么,要想繼續使用屬性被改動后的對象,我們可以自定義給對象分配UID號,讓UID號不隨對象的屬性變化而變化。
自定義對象分配UID方法如下:
public static final long serialVersion UID = 43L;
**注意:
靜態不能被系列化,因為靜態成員變量實在內存的方法區,而ObjectOutputStream只能對對內存里面的數據進行系列化
被transient修飾的非靜態成員變量也不能被系列化。被系列化的對象存儲到文件中,該文件是不可讀的,所以該文件的擴展名一般不寫成.txt,通常后綴名寫.object
**ObjectOutputStream
**ObjectInputStream
(3)管道流:
PipedInputStream
PipedOutputStream
(4)隨機訪問文件:RandomAccess(重要!!!)
**自身具備讀寫方法(很牛逼!又可以讀又可以寫)
**通過skipByte(int x)和seek(int x)來達到隨機訪問文件
**該類不是IO體系子類,而是直接繼承Object,但它是IO包中的成員,因為它具備讀寫方法
**該類內部封裝了數組,而且通過指針對數組的元素進行操作,可以通過getFilePoint獲取指針位置, 同時可以通過seek改變指針位置
**該類完成讀寫的原理是內部封裝了字節輸入輸出流
**通過該類的構造看出,該類只能操作文件,而且操作的文件只能有固定模式:
"r":只讀
"rw":讀寫
"rws":
"red":
**構造方法:
RandomAccessFile(File file, String mode)
創建從中讀取和向其中寫入(可選)的隨機訪問文件流,該文件由 File 參數指定。
RandomAccessFile(String name, String mode)
創建從中讀取和向其中寫入(可選)的隨機訪問文件流,該文件具有指定名稱。
**方法摘要:
1 void write(byte[] b) 2 //將 b.length 個字節從指定 byte 數組寫入到此文件,並從當前文件指針開始。 3 void write(byte[] b, int off, int len) 4 //將 len 個字節從指定 byte 數組寫入到此文件,並從偏移量 off 處開始。 5 void write(int b) 6 //向此文件寫入指定的字節。 7 int read() 8 //從此文件中讀取一個數據字節。 9 int read(byte[] b) 10 //將最多 b.length 個數據字節從此文件讀入 byte 數組。 11 int read(byte[] b, int off, int len) 12 //將最多 len 個數據字節從此文件讀入 byte 數組。 13 String readLine() 14 //從此文件讀取文本的下一行。 15 long getFilePointer() 16 //返回此文件中的當前偏移量。 17 long length() 18 //返回此文件的長度。 19 void seek(long pos) 20 //設置到此文件開頭測量到的文件指針偏移量,在該位置發生下一個讀取或寫入操作。
(4)操作基本數據類型的流對象:DateStream
(5)操作字節數組流:
ByteArrayInputStream
ByteArrayOutputStream
11、IO流轉換流的字符編碼
(1)字符流的出現為了方便操作字符,更重要的是加入了編碼轉換
(2)通過子類轉換流來完成
InputStreamReander
OutputStreamWriter
(3)在兩個子類對象進行構造的時候可以加入編碼表
(4)編碼表:
將各個國家的文字用二進制數字表示並一一對應,形成一張表,這就是編碼表
(5)常見的編碼表:
**ASCII:美國標准信息交換碼,用一個字節的七位表示
**ISO8859-1:拉丁碼表,歐洲碼表,用一個字節的八位表示
**GB2312:中文編碼表,用兩個字節表示
**GBK:中文編碼表升級,融合錄入更多的中文字符,用兩個字節表示,為避免和老美重復
兩字節的最高位都是1,即漢字都是用負數表示
**Unicode:國際標准碼,融合了多種文字,所有文字都用兩個字節表示
**UTF-8:用一個字節到三個字節表示。
注:Unicode能識別中文,UTF-8也能識別中文,但兩種編碼表示一個漢字所用的字節數不同
Unicode用兩個字節,UTF-8用三個字節,故涉及到編碼轉換。
(6)在流中涉及編碼表的轉換只有轉換流:
InputStreamReander
OutputStreamWriter
(7)代碼示例:
1 public static void write() throws IOException 2 { 3 OutputStreamWriter osw1 = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"GBK"); 4 osw1.write("你好"); 5 osw1.close(); 6 7 OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream("utf-8.txt"),"UTF-8"); 8 osw2.write("你好"); 9 osw2.close(); 10 } 11 public static void read() throws IOException 12 { 13 InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk.txt"),"GBK"); 14 byte[] buf = new byte[1024]; 15 int len = isr.read(buf); 16 sop(new String(buf,0,len)); 17 }
(8)編碼解碼
編碼:字符串變成字節數組:String-->getBytes()-->byte[]()
解碼:字節數組變成字符串:byte[]-->new String(byte[],0,len)-->String
(9)代碼示例:
1 public static void main(String[] args) 2 { 3 //編碼解碼1:默認編碼 4 String str1 = "你好"; 5 byte[] buf1 = str1.getBytes();//默認解碼:Unicode,四個字節 6 7 //編碼解碼2:指定編碼 8 String str2 = "你好"; 9 byte[] buf2 = str2.getBytes("UTF-8");//指定解碼:UTF-8,六個字節 10 11 12 //編碼解碼3:編碼正確解碼錯誤 13 String str3 = "你好"; 14 byte[] buf3 = str3.getBytes("GBK");//指定編碼:GBK,四個字節 15 String str3 = new String(buf3,"ISO8859-1");//錯誤解碼 16 17 //編碼解碼4:錯誤編碼正確解碼 18 String str4 = "你好"; 19 byte[] buf4 = str4.getBytes("ISO8859-1");//錯誤編碼 20 String str4 = new String(buf4,"GBK");//正確解碼,讀不出來 21 22 //編碼解碼5:編碼對了,但是解碼錯誤了,怎么辦呢? 23 //此時可以將錯誤的解碼再錯編回去,載用正確編碼解碼 24 String str5 = "你好"; 25 byte[] buf5 = str5.getBytes("GBK");//正確編碼 26 String str6 = new String(buf5,"ISO8859-1");//錯誤解碼,讀不出來 27 byte[] buf6 = str6.getBytes("ISO8859-1");//再錯誤編碼 28 String str7 = new String(buf6,"GBK");//再正確解碼,這樣就可以讀出來了 29 }
七、網絡編程:
1、網絡編程概述
(1)網絡模型
OSI參考模型
TCP/IP參考模型
(2)網絡通訊要素
IP地址
端口號
傳輸協議
(3)網絡通訊前提:
**找到對方IP
**數據要發送到指定端口。為了標示不同的應用程序,所以給這些網絡應用程序都用數字進行標示
。這個表示就叫端口。
**定義通信規則。這個規則稱為通信協議,國際組織定義了通用協議TCP/IP
(4)計算機網絡:
是指將地理位置不同的具有獨立功能的多台計算機及其外部設備,
通過通信線路連接起來,在網絡操作系統,網絡管理軟件及網絡通信協議的管理和協調下,
實現資源共享和信息傳遞的計算機系統。
(5)IP地址:
IP地址 = 網絡號碼+主機地址
A類IP地址:第一段號碼為網絡號碼,剩下的三段號碼為本地計算機的號碼
B類IP地址:前二段號碼為網絡號碼,剩下的二段號碼為本地計算機的號碼
C類IP地址:前三段號碼為網絡號碼,剩下的一段號碼為本地計算機的號碼
特殊地址:
127.0.0.1 回環地址,可用於測試本機的網絡是否有問題. ping 127.0.0.1
ipconfig:查看本機IP地址
xxx.xxx.xxx.0 網絡地址
xxx.xxx.xxx.255 廣播地址
A類1.0.0.1---127.255.255.25410.X.X.X是私有地址(私有地址就是在互聯網上不使用,而被用在局域網絡中的地址)(2)127.X.X.X是保留地址,用做循環測試用的。
B類128.0.0.1---191.255.255.254172.16.0.0---172.31.255.255是私有地址。169.254.X.X是保留地址。
C類192.0.0.1---223.255.255.254192.168.X.X是私有地址
D類224.0.0.1---239.255.255.254
E類240.0.0.1---247.255.255.254
(6)各種網絡分類方式
A:按網絡覆蓋范圍划分
局域網(幾米至10公里以內) 城域網(10~100公里) 廣域網(幾百公里到幾千公里) 國際互聯網
B:按網絡拓撲結構划分
總線型網絡 星形網絡 環型網絡 樹狀網絡 混合型網絡
C:按傳輸介質划分
有線網 無線網
D:按網絡使用性質划分
公用網 專用網
(7)虛擬專用網絡(Virtual Private Network ,簡稱VPN)指的是在公用網絡上建立專用網絡的技術。
其之所以稱為虛擬網,主要是因為整個VPN網絡的任意兩個節點之間的連接並沒有傳統專網
所需的端到端的物理鏈路,而是架構在公用網絡服務商所提供的網絡平台,如Internet、
ATM(異步傳輸模式〉、Frame Relay (幀中繼)等之上的邏輯網絡,
用戶數據在邏輯鏈路中傳輸。它涵蓋了跨共享網絡或公共網絡的封裝、
加密和身份驗證鏈接的專用網絡的擴展。VPN主要采用了隧道技術、加解密技術、
密鑰管理技術和使用者與設備身份認證技術。
(8)網絡模型:
****OSI模型
應用層
表示層
會話層
傳輸層
網絡層
數據連接層
物理層
****TCP/IP模型
應用層
傳輸層
網際層
主機至網絡層
2、TCP和UDP
(1)UDP和TCP的區別:
UDP
將數據及源和目的封裝成數據包中,不需要建立連接
每個數據報的大小在限制在64k內
因無連接,是不可靠協議
不需要建立連接,速度快
TCP
建立連接,形成傳輸數據的通道。
在連接中進行大數據量傳輸
通過三次握手完成連接,是可靠協議
必須建立連接,效率會稍低
注:三次握手:
第一次:我問你在么?
第二次:你回答在。
第三次:我反饋哦我知道你在。
3、Socket(UDP傳輸)
**Socket就是為網絡服務提供的一種機制。
**通信的兩端都有Socket。
**網絡通信其實就是Socket間的通信。
**數據在兩個Socket間通過IO傳輸。
**玩Socket主要就是記住流程,代碼查文檔就行
(1)UDP傳輸:DatagramSocket與DatagramPacket
**發送端:
建立DatagramSocket服務;
提供數據,並將數據封裝到字節數組中;
創建DatagramPacket數據包,並把數據封裝到包中,同時指定IP和接收端口
通過Socket服務,利用send方法將數據包發送出去;
關閉DatagramSocket和DatagramPacket服務。
**接收端:
建立DatagramSocket服務,並監聽一個端口;
定義一個字節數組和一個數據包,同時將數組封裝進數據包;
通過DatagramPacket的receive方法,將接收的數據存入定義好的數據包;
通過DatagramPacke關閉t的方法,獲取發送數據包中的信息;
關閉DatagramSocket和DatagramPacket服務。
DatagramSocket與DatagramPacket方法摘要:
*****DatagramSocket
構造方法:
DatagramSocket()
構造數據報套接字並將其綁定到本地主機上任何可用的端口。
DatagramSocket(int port)
創建數據報套接字並將其綁定到本地主機上的指定端口。
DatagramSocket(int port, InetAddress laddr)
創建數據報套接字,將其綁定到指定的本地地址。
方法摘要:
1 void close() 2 //關閉此數據報套接字。 3 InetAddress getInetAddress() 4 //返回此套接字連接的地址。 5 InetAddress getLocalAddress() 6 //獲取套接字綁定的本地地址。 7 int getPort() 8 //返回此套接字的端口。 9 void receive(DatagramPacket p) 10 //從此套接字接收數據報包。 11 void send(DatagramPacket p) 12 //從此套接字發送數據報包。
****DatagramPacket
構造方法:
DatagramPacket(byte[] buf, int length)
構造 DatagramPacket,用來接收長度為 length 的數據包。
DatagramPacket(byte[] buf, int length, InetAddress address, int port)
構造數據報包,用來將長度為 length 的包發送到指定主機上的指定端口號。
方法摘要:
1 InetAddress getAddress() 2 //返回某台機器的 IP 地址,此數據報將要發往該機器或者是從該機器接收到的。 3 byte[] getData() 4 //返回數據緩沖區。 5 int getLength() 6 //返回將要發送或接收到的數據的長度。 7 int getPort() 8 //返回某台遠程主機的端口號,此數據報將要發往該主機或者是從該主機接收到的。
代碼示例:
****發送端:
1 class UDPSend 2 { 3 public static void main(String[] args) throws Exception 4 { 5 DatagramSocket ds = new DatagramSocket(); 6 byte[] buf = "這是UDP發送端".getBytes(); 7 DatagramPacket dp = new DatagramPacket( 8 buf,buf.length,InetAddress.getByName("192.168.1.253"),10000); 9 ds.send(dp); 10 ds.close(); 11 } 12 }
****接收端
1 class UDPRece 2 { 3 public static void main(String[] args) throws Exception 4 { 5 DatagramSocket ds = new DatagramSocket(10000); 6 byte[] buf = new byte[1024]; 7 DatagramPacket dp = new DatagramPacket(buf,buf.length); 8 ds.receive(dp);//將發送端發送的數據包接收到接收端的數據包中 9 String ip = dp.getAddress().getHosyAddress();//獲取發送端的ip 10 String data = new String(dp.getData(),0,dp.getLength());//獲取數據 11 int port = dp.getPort();//獲取發送端的端口號 12 sop(ip+":"+data+":"+port); 13 ds.close(); 14 } 15 }
需求1:UDP鍵盤錄入數據,並發送給接收端
發送端:
1 class UDPSend 2 { 3 public static void main(String[] args) throws Exception 4 { 5 6 DatagramSocket ds = new DatagramSocket(); 7 BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); 8 String line = null; 9 while((line = bufr.readLine())!=null) 10 { 11 if("886".equals(line)) 12 break; 13 byte[] buf = line.getBytes(); 14 DatagramPacket dp = new DatagramPacket( 15 buf,buf.length,InetAddress.getByName("192.168.1.253"),10000); 16 ds.send(dp); 17 } 18 ds.close(); 19 } 20 21 }
接收端:
1 class UDPRece 2 { 3 public static void main(String[] args) throws Exception 4 { 5 DatagramSocket ds = new DatagramSocket(10000); 6 while(true) 7 { 8 byte[] buf = new byte[1024]; 9 DatagramPacket dp = new DatagramPacket(buf,buf.length); 10 ds.receive(dp);//將發送端發送的數據包接收到接收端的數據包中 11 String ip = dp.getAddress().getHosyAddress();//獲取發送端的ip 12 String data = new String(dp.getData(),0,dp.getLength());//獲取數據 13 int port = dp.getPort();//獲取發送端的端口號 14 sop(ip+":"+data+":"+port); 15 ds.close(); 16 } 17 } 18 19 }
需求2:編寫簡單的聊天工具
思路:
使用多線程技術
發送端:
1 class UDPSend implements Runnable 2 { 3 private DatagramSocket ds; 4 public UDPSend(){} 5 public UDPSend(DatagramSocket ds) 6 { 7 this.ds=ds; 8 } 9 public void run() 10 { 11 try 12 { 13 BufferedReader bufr = new BufferedReader( 14 new InputStreamReader(System.in)); 15 String line = null; 16 while((line = bufr.readLine())!=null) 17 { 18 if("886".equals(line)) 19 break; 20 byte[] buff = line.getBytes(); 21 DatagramPacket dp = new DatagramPacket( 22 buf,buf.length,InetAddress.getByName("192.168.1.253"),10000); 23 ds.send(dp); 24 } 25 } 26 catch(Exception e) 27 { 28 throw new RuntimeException("發送失敗"); 29 } 30 } 31 }
接收端:
1 class UDPRece implements Runnable 2 { 3 private DatagramSocket ds; 4 public UDPSend(){} 5 public UDPSend(DatagramSocket ds) 6 { 7 this.ds=ds; 8 } 9 public void run() 10 { 11 try 12 { 13 while(true) 14 { 15 byte[] buf = new byte[1024]; 16 DatagramPacket dp = new DatagramPacket(buf,buf.length); 17 ds.receive(dp);//將發送端發送的數據包接收到接收端的數據包中 18 String ip = dp.getAddress().getHosyAddress();//獲取發送端的ip 19 String data = new String(dp.getData(),0,dp.getLength());//獲取數據 20 int port = dp.getPort();//獲取發送端的端口號 21 sop(ip+":"+data+":"+port); 22 } 23 } 24 catch(Exception e) 25 { 26 throw new RuntimeException("接收失敗"); 27 } 28 } 29 }
測試類:
1 class UDPTest 2 { 3 public static void main(String[] args) 4 { 5 DatagramSocket sendSocket = new DatagramSocket(); 6 DatagramSocket receSocket = new DatagramSocket(10000); 7 8 new Thread(new UDPSend(sendSocket)).start(); 9 new Thread(new UDPRece(receSocket)).start(); 10 } 11 }
(2)TCP傳輸
Socket和ServerSocket
建立客戶端和服務器端
建立連接后,通過Socket中的IO流進行數據的傳輸
關閉socket
同樣,客戶端與服務器端是兩個獨立的應用程序。
****Socket
**構造方法:
Socket()
通過系統默認類型的 SocketImpl 創建未連接套接字
Socket(InetAddress address, int port)
創建一個流套接字並將其連接到指定 IP 地址的指定端口號。
Socket(String host, int port)
創建一個流套接字並將其連接到指定主機上的指定端口號。
**方法摘要:
1 void close() 2 //關閉此套接字。 3 InetAddress getInetAddress() 4 //返回套接字連接的地址。 5 InputStream getInputStream() 6 //返回此套接字的輸入流。 7 OutputStream getOutputStream() 8 //返回此套接字的輸出流。 9 int getPort() 10 //返回此套接字連接到的遠程端口。 11 void shutdownInput() 12 //此套接字的輸入流置於“流的末尾”。 13 void shutdownOutput() 14 //禁用此套接字的輸出流。 15 String toString() 16 //將此套接字轉換為 String。
****ServerSocket
**構造方法:
ServerSocket()
創建非綁定服務器套接字。
ServerSocket(int port)
創建綁定到特定端口的服務器套接字。
方法摘要:
1 Socket accept() 2 //偵聽並接受到此套接字的連接。 3 void close() 4 //關閉此套接字。 5 InetAddress getInetAddress() 6 //返回此服務器套接字的本地地址。
****TCP傳輸流程:
**客戶端:
建立Socket服務,並制定要連接的主機和端口;
獲取Socket流中的輸出流OutputStream,將數據寫入流中,通過網絡發送給服務端;
獲取Socket流中的輸出流InputStream,獲取服務端的反饋信息;
關閉資源。
**服務端:
建立ServerSocket服務,並監聽一個端口;
通過ServerSocket服務的accept方法,獲取Socket服務對象;
使用客戶端對象的讀取流獲取客戶端發送過來的數據;
通過客戶端對象的寫入流反饋信息給客戶端;
關閉資源;
****代碼示例:
客戶端:
1 class TCPClient 2 { 3 public static void main(String[] args) 4 { 5 Socket s = new Socket("192.168.1.253",10000); 6 OutputStream os = s.getOutputStream(); 7 out.write("這是TCP發送的數據".getBytes()); 8 s.close(); 9 } 10 }
服務端:
1 class TCPServer 2 { 3 public static void main(String[] args) 4 { 5 ServerSocket ss = new ServerSocket(10000); 6 Socket s = ss.accept(); 7 8 String ip = s.getInetAddress().getHostAddress(); 9 sop(ip); 10 11 InputStream is = s.getInputStream(); 12 byte[] buf = new byte[1024]; 13 int len = is.read(buf); 14 sop(new String(buf,0,len)); 15 s.close(); 16 ss.close(); 17 } 18 }
TCP需求1:客戶端給服務端發送數據,服務端接收到后反饋信息給客戶端
客戶端:
1 class TCPClient 2 { 3 public static void main(String[] args) 4 { 5 Socket s = new Socket("192.168.1.253",10000); 6 OutputStream os = s.getOutputStream(); 7 out.write("這是TCP發送的數據".getBytes()); 8 9 InputStream is = s.getInputStream(); 10 byte[] buf = new byte[1024]; 11 int len = is.read(buf); 12 sop(new String(buf,0,len)); 13 s.close(); 14 } 15 }
服務端:
1 class TCPServer 2 { 3 public static void main(String[] args) 4 { 5 ServerSocket ss = new ServerSocket(10000); 6 Socket s = ss.accept(); 7 8 String ip = s.getInetAddress().getHostAddress(); 9 sop(ip); 10 11 InputStream is = s.getInputStream(); 12 byte[] buf = new byte[1024]; 13 int len = is.read(buf); 14 sop(new String(buf,0,len)); 15 16 OutputStream os = s.getOutputStream(); 17 out.write("這是TCP發送的數據".getBytes()); 18 19 s.close(); 20 ss.close(); 21 } 22 }
TCP需求2:建立一個文本轉換服務端,客戶給服務端發送文本,服務端將數據轉換成大寫后返回給客戶端
當客戶端輸入over時,轉換結束
客戶端:
1 class TCPClient 2 { 3 public static void main(String[] args) 4 { 5 Socket s = new Socket("192.168.1.253",10000); 6 BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); 7 BufferedWriter bufOut = new BufferedWriter(new OutputStreamWriter( 8 s.getOutputStream())); 9 BufferedReader bufIn = new BufferedReader(new InputStreamReader( 10 s.getInputStream())); 11 String line = null; 12 while((line = bufr.readLine())!=null) 13 { 14 if("over".equals(line)) 15 break; 16 bufOut.write(line); 17 bufOut.newLine(); 18 bufOut.flush(); 19 String retVal = bufIn.readLine(); 20 sop("server:"+retVal); 21 } 22 bufr.close(); 23 s.close(); 24 } 25 }
服務端:
1 class TCPServer 2 { 3 public static void main(String[] args) 4 { 5 ServerSocket ss = new ServerSocket(10000); 6 Socket s = ss.accept(); 7 8 String ip = s.getInetAddress().getHostAddress(); 9 sop(ip); 10 11 BufferedReader bufIn = new BufferedReader(new InputStreamReader( 12 s.getInputStream())); 13 BufferedWriter bufOut = new BufferedWriter(new OutputStreamWriter( 14 s.getOutputStream())); 15 16 while((line = bufIn.readLine())!=null) 17 { 18 bufOut.write(line.toUpperCase()); 19 bufOut.newLine(); 20 bufOut.flush(); 21 } 22 s.close(); 23 ss.close(); 24 } 25 }
**需求3:拷貝文件
客戶端:
1 class TCPClient 2 { 3 public static void main(String[] args) 4 { 5 Socket s = new Socket("192.168.1.253",10000); 6 BufferedReader bufr = new BufferedReader(new FileReader("g:\\demo.txt")); 7 PrintWriter pw = new PrintWriter(s.getOutputStream(),true); 8 String line = null; 9 while((line = bufr.readLine())!=null) 10 { 11 pw.println(); 12 } 13 s.shutDownOutput(); 14 BufferedReader bufIn = new BufferedReader(new InputStreamReader( 15 s.getInputStream())); 16 String retVal = bufIn.readLine(); 17 sop(retVal); 18 bufr.close(); 19 s.close(); 20 } 21 }
服務端:
1 class TCPServer 2 { 3 public static void main(String[] args) 4 { 5 ServerSocket ss = new ServerSocket(10000); 6 Socket s = ss.accept(); 7 8 String ip = s.getInetAddress().getHostAddress(); 9 sop(ip); 10 11 BufferedReader bufIn = new BufferedReader(new InputStreamReader( 12 s.getInputStream())); 13 PrintWriter out = new PrintWriter(new FileWriter"copy.txt",true); 14 String line =null; 15 while((line = bufIn.readLine())!=null) 16 { 17 out.write(line); 18 } 19 PrintWriter pw = new PrintWriter(s.getOutputStream(),true); 20 pw.println("上傳成功"); 21 out.close(); 22 s.close(); 23 ss.close(); 24 } 25 }
需求4:上傳圖片
客戶端:
1 class TCPClient 2 { 3 public static void main(String[] args) 4 { 5 Socket s = new Socket("192.168.1.253",10000); 6 FileInputStream fis = new FileInputStream("g:\\1.bmp"); 7 OutputStream out = s.getOutputStream(); 8 byte[] buf = new byte[1024]; 9 int len = 0; 10 while((len = bufr.read())!=-1) 11 { 12 out.write(buf,0,len); 13 } 14 s.shutDownOutput(); 15 16 InputStream in = s.getInputStream(); 17 byte[] bufIn = new byte[1024]; 18 int lenIn = in.read(bufIn); 19 sop(new String(bufIn,0,lenIn); 20 fis.close(); 21 s.close(); 22 } 23 }
服務端:
1 class TCPServer 2 { 3 public static void main(String[] args) 4 { 5 ServerSocket ss = new ServerSocket(10000); 6 Socket s = ss.accept(); 7 8 String ip = s.getInetAddress().getHostAddress(); 9 sop(ip); 10 FileOutputStream fos = new FileOutputStream("g:\\copy.bmp"); 11 InputStream in = s.getInputStream(); 12 byte[] bufIn = new byte[1024]; 13 int lenIn = 0; 14 while((lenIn=bufIn.read())!=-1) 15 { 16 fos.write(bufIn,0,lenIn) 17 } 18 19 OutputStream outIn = s.getOutputStream(); 20 outIn.write("上傳成功".getBytes()); 21 fos.close(); 22 s.close(); 23 ss.close(); 24 } 25 }
需求5:客戶端並發登陸
客戶端通過鍵盤錄入用戶名,服務端對這個用戶名進行校驗
如果用戶存在,在服務端現實xxx已登錄,並在客戶端現實歡迎xxx
如果用戶不存在,在服務端現實xxx正在嘗試登陸,並在客戶端現實xxx用戶不存在
最多登陸三次。
校驗端:
1 class User implements Runnable 2 ( 3 private Socket s; 4 public User(){} 5 public User(Socket s) 6 { 7 this.s=s; 8 } 9 public void run() 10 { 11 try 12 { 13 BufferedReader bufrIn = new BufferedReader( 14 new InputStream(s.getInputStream())) 15 String name = bufrIn.readLine(); 16 if(name==null) 17 { 18 sop("用戶名為空"); 19 break; 20 } 21 BufferedReader bufr = new BufferedReader( 22 new FileReader("user.txt")); 23 PrintWriter pw = new PrintWriter(s.getOutputStream(),true); 24 String line = null; 25 boolean flag = false; 26 while((line = bufr.reanLine())!=null) 27 { 28 if(line.equals(name)) 29 { 30 flag = true; 31 break; 32 } 33 if(flag) 34 { 35 sop(name+"已登陸"); 36 pw.println("歡迎"+name); 37 break; 38 } 39 else 40 { 41 sop(name+"正嘗試登陸"); 42 pw.println(name+"用戶不存在"); 43 } 44 45 } 46 s.close(); 47 } 48 catch(Exception e) 49 { 50 throw new RuntimeException("用戶校驗失敗"); 51 } 52 } 53 )
客戶端:
1 class LoginClient 2 { 3 public static void main(String[] args) 4 { 5 Socket s = new Socket("192.168.1.253",10000); 6 BufferedReader bufr = new BufferedReader( 7 new InputStreamReader(System.in))); 8 PrintWriter out = new PrintWriter(s.getOutputStream(),true); 9 BufferedReader bufIn = new BufferedReader( 10 new InputStreamReader(s.getInputStream())); 11 for(int i=0;i<3;i++) 12 { 13 String line = bufr.readLine(); 14 if(line == null) 15 { 16 sop("用戶名不能為空!"); 17 break; 18 } 19 out.write(line); 20 String retVal = bufIn.readLine(); 21 sop(retVal); 22 } 23 bufr.close(); 24 s.close(); 25 } 26 }
服務端:
1 class LoginServer 2 { 3 public static void main(String[] args) 4 { 5 ServerSocket ss = new ServerSocket(10000); 6 while(true) 7 { 8 Socket s = ss.accept(); 9 new Thread(new User()).start(); 10 } 11 } 12 }
里面的equals方法(非常重要!!!要注意!!)