目錄html
學習連接:Java 視頻教程全集java
課件連接:Java課件git
計算機語言發展史以及將來方向程序員
第二代語言:彙編語言github
第三代語言:高級語言web
Java的核心優點redis
Java的各個版本算法
JavaSE(Java Standard Edition):標準版,定位在我的計算機上的應用。shell
JavaEE(Java Enterprise Edition):企業版,定位在服務器端的應用。數據庫
JavaME(Java Micro Edition):微型版,定位在消費性電子產品的應用上。
雷區:不少人開始會誤解爲安卓開發就是JavaME,這兩個是徹底不一樣的內容。
Java的特徵和優點
Java應用程序的運行機制
JVM、JRE和JDK
JVM(Java Virtual Machine)就是一個虛擬的用於執行bytecode字節碼的」虛擬計算機」。他也定義了指令集、寄存器集、結構棧、垃圾收集堆、內存區域。JVM負責將Java字節碼解釋運行,邊解釋邊運行,這樣,速度就會受到必定的影響。
Java Runtime Environment (JRE) 包含:Java虛擬機、庫函數、運行Java應用程序所必須的文件。
Java Development Kit (JDK)包含:包含JRE,以及增長編譯器和調試器等用於程序開發的文件
第一個Java程序的總結和提高
最經常使用DOS命令
註釋
/*
」開頭以「*/
」結尾,在「/*
」和「*/
」之間的內容爲註釋,咱們也能夠使用多行註釋做爲行內註釋。可是在使用時要注意,多行註釋不能嵌套使用。/**
」開頭以「*/」結尾,註釋中包含一些說明性的文字及一些JavaDoc標籤(後期寫項目時,能夠生成項目的API)標識符
Java中的關鍵字/保留字
變量的分類
變量和常量命名規範(規範是程序員的基本準則,不規範會直接損害你的我的形象):
基本數據類型(primitive data type)
整型變量/常量
浮點型變量/常量
字符型變量/常量
char 類型用來表示在Unicode編碼表中的字符。Unicode編碼被設計用來處理各類語言的文字,它佔2個字節,可容許有65536個字符。
轉義字符
String類,實際上是字符序列(char sequence)。
運算符(operator)
算術運算符
賦值及其擴展賦值運算符
關係運算符
邏輯運算符
位運算符
運算符的優先級
自動類型轉換
自動類型轉換指的是容量小的數據類型能夠自動轉換爲容量大的數據類型。實線表示無數據丟失的自動類型轉換,而虛線表示在轉換時可能會有精度的損失。
能夠將整型常量直接賦值給byte、 short、 char等類型變量,而不須要進行強制類型轉換,只要不超出其表數範圍便可。
帶標籤的break和continue
「標籤」是指後面跟一個冒號的標識符,例如:「label:」。對Java來講惟一用到標籤的地方是在循環語句以前。而在循環以前設置標籤的惟一理由是:咱們但願在其中嵌套另外一個循環,因爲break和continue關鍵字一般只中斷當前循環,但若隨同標籤使用,它們就會中斷到存在標籤的地方。
在 「goto有害」論中,最有問題的就是標籤,而非goto, 隨着標籤在一個程序裏數量的增多,產生錯誤的機會也愈來愈多。 但Java標籤不會形成這方面的問題,由於它們的活動場所已被限死,不可經過特別的方式處處傳遞程序的控制權。由此也引出了一個有趣的問題:經過限制語句的能力,反而能使一項語言特性更加有用。
帶標籤break和continue:控制嵌套循環跳轉(打印101-150之間全部的質數)
public class Test18 { public static void main(String args[]) { outer: for (int i = 101; i < 150; i++) { for (int j = 2; j < i / 2; j++) { if (i % j == 0){ continue outer; } } System.out.print(i + " "); } } }
方法
方法的重載(overload)
方法的重載是指一個類中能夠定義多個方法名相同,但參數不一樣的方法。 調用時,會根據不一樣的參數自動匹配對應的方法。
重載的方法,實際是徹底不一樣的方法,只是名稱相同而已!
構成方法重載的條件:
不一樣的含義:形參類型、形參個數、形參順序不一樣
只有形參的名稱不一樣,不構成方法的重載
遞歸結構
遞歸是一種常見的解決問題的方法,即把問題逐漸簡單化。遞歸的基本思想就是「本身調用本身」,一個使用遞歸技術的方法將會直接或者間接的調用本身。
遞歸結構包括兩個部分:
定義遞歸頭。解答:何時不調用自身方法。若是沒有頭,將陷入死循環,也就是遞歸的結束條件。
遞歸體。解答:何時須要調用自身方法。
遞歸的缺陷
Java虛擬機的內存能夠分爲三個區域:棧stack、堆heap、方法區method area。
棧的特色以下:
棧描述的是方法執行的內存模型。每一個方法被調用都會建立一個棧幀(==存儲局部變量、操做數、方法出口==等)
JVM爲每一個線程建立一個棧,用於存放該線程執行方法的信息(實際參數、局部變量等)
棧屬於線程私有,不能實現線程間的共享!
棧的存儲特性是「先進後出,後進先出」
5. 棧是由系統自動分配,速度快!棧是一個連續的內存空間!
堆的特色以下:
堆用於存儲建立好的==對象和數組==(數組也是對象)
JVM只有一個堆,被全部線程共享
堆是一個不連續的內存空間,分配靈活,速度慢!
方法區(又叫靜態區)特色以下:
JVM只有一個方法區,被全部線程共享!
方法區實際也是堆,只是用於存儲類、常量相關的信息!
用來存放程序中永遠是不變或惟一的內容。(==類信息==【Class對象】、==靜態變量、字符串常量==等)
構造器也叫構造方法(constructor),用於對象的初始化。構造器是一個建立對象時被自動調用的特殊方法,目的是對象的初始化。構造器的名稱應與類的名稱一致。Java經過new關鍵字來調用構造器,從而返回該類的實例,是一種特殊的方法。
要點:
經過new關鍵字調用!!
構造器雖然有返回值,可是不能定義返回值類型(返回值的類型確定是本類),不能在構造器裏使用return返回某個值。
若是咱們沒有定義構造器,則編譯器會自動定義一個無參的構造函數。若是已定義則編譯器不會自動添加!
構造方法的重載:
Java引入了垃圾回收機制,令C++程序員最頭疼的內存管理問題迎刃而解。Java程序員能夠將更多的精力放到業務邏輯上而不是內存管理工做上,大大的提升了開發效率。
內存管理
Java的內存管理很大程度指的就是對象的管理,其中包括對象空間的分配和釋放。
對象空間的釋放:將對象賦值null便可。垃圾回收器將負責回收全部」不可達」對象的內存空間。
垃圾回收過程
任何一種垃圾回收算法通常要作兩件基本事情:
發現無用的對象
回收無用對象佔用的內存空間。
垃圾回收機制保證能夠將「無用的對象」進行回收。無用的對象指的就是沒有任何變量引用該對象。Java的垃圾回收器經過相關算法發現無用對象,並進行清除和整理。
垃圾回收相關算法
引用計數法
堆中每一個對象都有一個引用計數。被引用一次,計數加1. 被引用變量值變爲null,則計數減1,直到計數爲0,則表示變成無用對象。優勢是算法簡單,缺點是「循環引用的無用對象」沒法別識別。
引用可達法(根搜索算法)
程序把全部的引用關係看做一張圖,從一個節點GC ROOT開始,尋找對應的引用節點,找到這個節點之後,繼續尋找這個節點的引用節點,當全部的引用節點尋找完畢以後,剩餘的節點則被認爲是沒有被引用到的節點,即無用的節點。
通用的分代垃圾回收機制
分代垃圾回收機制,是基於這樣一個事實:不一樣的對象的生命週期是不同的。所以,不一樣生命週期的對象能夠採起不一樣的回收算法,以便提升回收效率。咱們將對象分爲三種狀態:年輕代、年老代、持久代。JVM將堆內存劃分爲 Eden、Survivor 和 Tenured/Old 空間。
年輕代
全部新生成的對象首先都是放在Eden區。 年輕代的目標就是儘量快速的收集掉那些生命週期短的對象,對應的是Minor GC,每次 Minor GC 會清理年輕代的內存,算法採用效率較高的複製算法,頻繁的操做,可是會浪費內存空間。當「年輕代」區域存放滿對象後,就將對象存放到年老代區域。
年老代
在年輕代中經歷了N(默認15)次垃圾回收後仍然存活的對象,就會被放到年老代中。所以,能夠認爲年老代中存放的都是一些生命週期較長的對象。年老代對象愈來愈多,咱們就須要啓動Major GC和Full GC(全量回收),來一次大掃除,全面清理年輕代區域和年老代區域。
持久代
用於存放靜態文件,如Java類、方法等。持久代對垃圾回收沒有顯著影響。
堆內存的劃分細節
Minor GC:
用於清理年輕代區域。Eden區滿了就會觸發一次Minor GC。清理無用對象,將有用對象複製到「Survivor1」、「Survivor2」區中(這兩個區,大小空間也相同,同一時刻Survivor1和Survivor2只有一個在用,一個爲空)
Major GC:
用於清理老年代區域。
Full GC:
用於清理年輕代、年老代區域。 成本較高,會對系統性能產生影響。
垃圾回收過程:
一、新建立的對象,絕大多數都會存儲在Eden中,
二、當Eden滿了(達到必定比例)不能建立新對象,則觸發垃圾回收(GC),將無用對象清理掉,而後剩餘對象複製到某個Survivor中,如S1,同時清空Eden區
三、當Eden區再次滿了,會將S1中的不能清空的對象存到另一個Survivor中,如S2,同時將Eden區中的不能清空的對象,也複製到S1中,保證Eden和S1,均被清空。
四、重複屢次(默認15次)Survivor中沒有被清理的對象,則會複製到老年代Old(Tenured)區中,
五、當Old區滿了,則會觸發一個一次完整地垃圾回收(FullGC),以前新生代的垃圾回收稱爲(minorGC)
JVM調優和Full GC
在對JVM調優的過程當中,很大一部分工做就是對於Full GC的調節。有以下緣由可能致使Full GC:
1.年老代(Tenured)被寫滿
2.持久代(Perm)被寫滿
3.System.gc()被顯式調用(程序建議GC啓動,不是調用GC)
4.上一次GC以後Heap的各域分配策略動態變化
開發中容易形成內存泄露的操做
建立大量無用對象:
好比,咱們在須要大量拼接字符串時,使用了String而不是StringBuilder。
靜態集合類的使用:
像HashMap、Vector、List等的使用最容易出現內存泄露,這些靜態變量的生命週期和應用程序一致,全部的對象Object也不能被釋放。
各類鏈接對象(IO流對象、數據庫鏈接對象、網絡鏈接對象)未關閉:
IO流對象、數據庫鏈接對象、網絡鏈接對象等鏈接對象屬於物理鏈接,和硬盤或者網絡鏈接,不使用的時候必定要關閉。
監聽器的使用:
釋放對象時,沒有刪除相應的監聽器。
要點:
1. 程序員無權調用垃圾回收器。
2. 程序員能夠調用System.gc(),該方法只是通知JVM,並非運行垃圾回收器。儘可能少用,會申請啓動Full GC,成本高,影響系統性能。 3. finalize方法,是Java提供給程序員用來釋放對象或資源的方法,可是儘可能少用。
構造方法是建立Java對象的重要途徑,經過new關鍵字調用構造器時,構造器也確實返回該類的對象,但這個對象並非徹底由構造器負責建立。建立一個對象分爲以下四步:
分配對象空間,並將對象成員變量初始化爲0或空
執行屬性值的顯示初始化
執行構造方法
返回對象的地址給相關的變量
this的本質就是「建立好的對象的地址」! 因爲在構造方法調用前,對象已經建立。所以,在構造方法中也能夠使用this表明「當前對象」 。
this最常的用法:
在程序中產生二義性之處,應使用this來指明當前對象;普通方法中,this老是指向調用該方法的對象。構造方法中,this老是指向正要初始化的對象。
使用this關鍵字調用重載的構造方法,避免相同的初始化代碼。但只能在構造方法中用,而且必須位於構造方法的第一句。
this不能用於static方法中。
在類中,用static聲明的成員變量爲靜態成員變量,也稱爲類變量。 類變量的生命週期和類相同,在整個應用程序執行期間都有效。它有以下特色:
爲該類的公用變量,屬於類,被該類的全部實例共享,在類被載入時被顯式初始化。
對於該類的全部對象來講,static成員變量只有一份。被該類的全部對象共享!
通常用「類名.類屬性/方法」來調用。(也能夠經過對象引用或類名(不須要實例化)訪問靜態成員。)
在static方法中不可直接訪問非static的成員。
static修飾的成員變量和方法,從屬於類。
普通變量和方法從屬於對象的。
構造方法用於對象的初始化!靜態初始化塊,用於類的初始化操做!在靜態初始化塊中不能直接訪問非static成員。
靜態初始化塊執行順序:
上溯到Object類,先執行Object的靜態初始化塊,再向下執行子類的靜態初始化塊,直到咱們的類的靜態初始化塊爲止。
構造方法執行順序和上面順序同樣!!
包機制是Java中管理類的重要手段。 開發中,咱們會遇到大量同名的類,經過包咱們很容易對解決類重名的問題,也能夠實現對類的有效管理。 包對於類,至關於文件夾對於文件的做用。
咱們經過package實現對類的管理,package的使用有兩個要點:
一般是類的第一句非註釋性語句。
包名:域名倒着寫便可,再加上模塊名,便於內部管理類。
注意事項:
寫項目時都要加包,不要使用默認包。
com.gao和com.gao.car,這兩個包沒有包含關係,是兩個徹底獨立的包。只是邏輯上看起來後者是前者的一部分。
JDK中的主要包
導入類import
若是咱們要使用其餘包的類,須要使用import導入,從而能夠在本類中直接經過類名來調用,不然就須要書寫類的完整包名和類名。import後,便於編寫代碼,提升可維護性。
注意要點:
Java會默認導入java.lang包下全部的類,所以這些類咱們能夠直接使用。
若是導入兩個同名的類,只能用包名+類名來顯示調用相關類。
靜態導入
靜態導入(static import)是在JDK1.5新增長的功能,其做用是用於導入指定類的靜態屬性,這樣咱們能夠直接使用靜態屬性。
package cn.sxt; //如下兩種靜態導入的方式二選一便可 import static java.lang.Math.*;//導入Math類的全部靜態屬性 import static java.lang.Math.PI;//導入Math類的PI屬性 public class Test2{ public static void main(String [] args){ System.out.println(PI); System.out.println(random()); } }
繼承的實現
繼承讓咱們更加容易實現類的擴展。 好比,咱們定義了人類,再定義Boy類就只須要擴展人類便可。實現了代碼的重用,不用再從新發明輪子(don’t reinvent wheels)。
從英文字面意思理解,extends的意思是「擴展」。子類是父類的擴展。現實世界中的繼承無處不在。好比:
繼承使用要點
父類也稱做超類、基類、派生類等。
Java中只有單繼承,沒有像C++那樣的多繼承。多繼承會引發混亂,使得繼承鏈過於複雜,系統難於維護。
Java中類沒有多繼承,接口有多繼承(??實現)。
子類繼承父類,能夠獲得父類的所有屬性和方法 (除了父類的構造方法),但不見得能夠直接訪問(好比,父類私有的屬性和方法)。
能夠使用ctrl+T方便的查看繼承結構。
instanceof 運算符
方法的重寫override
子類經過重寫父類的方法,能夠用自身的行爲替換父類的行爲。方法的重寫是實現多態的必要條件。
方法的重寫須要符合下面的三個要點:
1.「==」: 方法名、形參列表相同。
2.「≤」:返回值類型和聲明異常類型,子類小於等於父類。
3.「≥」: 訪問權限,子類大於等於父類。
Object類是全部Java類的根基類,也就意味着全部的Java對象都擁有Object類的屬性和方法。若是在類的聲明中未使用extends關鍵字指明其父類,則默認繼承Object類。
toString方法
Object類中定義有public String toString()方法,其返回值是 String 類型。
Object類中toString方法的源碼爲:
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
根據如上源碼得知,默認會返回「類名+@+16進制的hashcode」。在打印輸出或者用字符串鏈接對象時,會自動調用該對象的toString()方法。
==和equals方法
super是直接父類對象的引用。能夠經過super來訪問父類中被子類覆蓋的方法或屬性。
使用super調用普通方法,語句沒有位置限制,能夠在子類中隨便調用。
如果構造方法的第一行代碼沒有顯式的調用super(...)或者this(...);那麼Java默認都會調用super(),含義是調用父類的無參數構造方法。這裏的super()能夠省略。
屬性/方法查找順序:(好比:查找變量h)
查找當前類中有沒有屬性h
依次上溯每一個父類,查看每一個父類中是否有h,直到Object
若是沒找到,則出現編譯錯誤。
上面步驟,只要找到h變量,則這個過程終止。
構造方法調用順序:
須要讓用戶知道的才暴露出來,不須要讓用戶知道的所有隱藏起來,這就是封裝。說的專業一點,封裝就是把對象的屬性和操做結合爲一個獨立的總體,並儘量隱藏對象的內部實現細節。
咱們程序設計要追求「高內聚,低耦合」。 高內聚就是類的內部數據操做細節本身完成,不容許外部干涉;低耦合是僅暴露少許的方法給外部使用,儘可能方便外部調用。
編程中封裝的具體優勢:
提升代碼的安全性。
提升代碼的複用性。
「高內聚」:封裝細節,便於修改內部代碼,提升可維護性。
「低耦合」:簡化外部調用,便於調用者使用,便於擴展和協做。
Java是使用「訪問控制符」來控制哪些細節須要封裝,哪些細節須要暴露的。 Java中4種「訪問控制符」分別爲private、default、protected、public,它們說明了面向對象的封裝性,因此咱們要利用它們儘量的讓訪問權限降到最低,從而提升安全性。
private 表示私有,只有本身類能訪問
default表示沒有修飾符修飾,只有同一個包的類能訪問
protected表示能夠被同一個包的類以及其餘包中的子類訪問
public表示能夠被該項目的全部包中的全部類訪問
類的屬性的處理:
多態指的是同一個方法調用,因爲對象不一樣可能會有不一樣的行爲。
多態的要點:
多態是方法的多態,不是屬性的多態(多態與屬性無關)。
多態的存在要有3個必要條件:繼承,方法重寫,父類引用指向子類對象。
由此,咱們能夠看出多態的主要優點是提升了代碼的可擴展性,符合開閉原則。可是多態也有弊端,就是沒法調用子類特有的功能。
對象的轉型(casting)
抽象方法:使用abstract修飾的方法,沒有方法體,只有聲明。定義的是一種「規範」,就是告訴子類必需要給抽象方法提供具體的實現。
抽象類:包含抽象方法的類就是抽象類。經過abstract方法定義規範,而後要求子類必須定義具體實現。經過抽象類,咱們就能夠作到嚴格限制子類的設計,使子類之間更加通用。
抽象類的使用要點:
有抽象方法的類只能定義成抽象類
抽象類不能實例化,即不能用new來實例化抽象類。
抽象類只能用來被繼承。
抽象方法必須被子類實現。
爲何須要接口?接口和抽象類的區別?
接口就是比「抽象類」還「抽象」的「抽象類」,能夠更加規範的對子類進行約束。全面地專業地實現了:規範和具體實現的分離。
抽象類還提供某些具體實現,接口不提供任何實現,接口中全部方法都是抽象方法。接口是徹底面向規範的,規定了一批類具備的公共方法規範。
從接口的實現者角度看,接口定義了能夠向外部提供的服務。
從接口的調用者角度看,接口定義了實現者能提供哪些服務。
接口是兩個模塊之間通訊的標準,通訊的規範。若是能把你要設計的模塊之間的接口定義好,就至關於完成了系統的設計大綱,剩下的就是添磚加瓦的具體實現了。你們在工做之後,作系統時每每就是使用「面向接口」的思想來設計系統。
接口和實現類不是父子關係,是實現規則的關係。好比:我定義一個接口Runnable,Car實現它就能在地上跑,Train實現它也能在地上跑,飛機實現它也能在地上跑。就是說,若是它是交通工具,就必定能跑,可是必定要實現Runnable接口。
接口的本質探討
接口就是規範,定義的是一組規則,體現了現實世界中「若是你是…則必須能…」的思想。若是你是天使,則必須能飛。若是你是汽車,則必須能跑。若是你是好人,則必須能幹掉壞人;若是你是壞人,則必須欺負好人。
接口的本質是契約,就像咱們人間的法律同樣。制定好後你們都遵照。
面向對象的精髓,是對對象的抽象,最能體現這一點的就是接口。爲何咱們討論設計模式都只針對具有了抽象能力的語言(好比C++、Java、C#等),就是由於設計模式所研究的,實際上就是如何合理的去抽象。
區別
普通類:具體實現
抽象類:具體實現,規範(抽象方法)
接口:規範!
聲明格式:
[訪問修飾符] interface 接口名 [extends 父接口1,父接口2…] { 常量定義; 方法定義; }
定義接口的詳細說明:
訪問修飾符:只能是public或默認。
接口名:和類名採用相同命名機制。
extends:接口能夠多繼承。
常量:接口中的屬性只能是常量,老是:public static final 修飾。不寫也是。
方法:接口中的方法只能是:public abstract。 省略的話,也是public abstract。
要點
子類經過implements來實現接口中的規範。
接口不能建立實例,可是可用於聲明引用變量類型。
一個類實現了接口,必須實現接口中全部的方法,而且這些方法只能是public的。
JDK1.7以前,接口中只能包含靜態常量、抽象方法,不能有普通屬性、構造方法、普通方法。
JDK1.8後,接口中包含普通的靜態方法。
接口的多繼承
經過面向接口編程,而不是面向實現類編程,能夠大大下降程序模塊間的耦合性,提升整個系統的可擴展性和和可維護性。
面向接口編程的概念比接口自己的概念要大得多。設計階段相對比較困難,在你沒有寫實現時就要想好接口,接口一變就亂套了,因此設計要比實現難!
通常狀況,咱們把類定義成獨立的單元。有些狀況下,咱們把一個類放在另外一個類的內部定義,稱爲內部類(innerclasses)。
內部類能夠使用public、default、protected 、private以及static修飾。而外部頂級類(咱們之前接觸的類)只能使用public和default修飾。
注意:內部類只是一個編譯時概念,一旦咱們編譯成功,就會成爲徹底不一樣的兩個類。對於一個名爲Outer的外部類和其內部定義的名爲Inner的內部類。編譯完成後會出現Outer.class和Outer$Inner.class兩個類的字節碼文件。因此內部類是相對獨立的一種存在,其成員變量/方法名能夠和外部類的相同。
內部類的做用:
內部類提供了更好的封裝。只能讓外部類直接訪問,不容許同一個包中的其餘類直接訪問。
內部類能夠直接訪問外部類的私有屬性,內部類被當成其外部類的成員。 但外部類不能訪問內部類的內部屬性。
接口只是解決了多重繼承的部分問題,而內部類使得多重繼承的解決方案變得更加完整。
內部類的使用場合:
因爲內部類提供了更好的封裝特性,而且能夠很方便的訪問外部類的屬性。因此,在只爲外部類提供服務的狀況下能夠優先考慮使用內部類。
使用內部類間接實現多繼承:每一個內部類都能獨立地繼承一個類或者實現某些接口,因此不管外部類是否已經繼承了某個類或者實現了某些接口,對於內部類沒有任何影響。
內部類的分類
在Java中內部類主要分爲成員內部類(非靜態內部類、靜態內部類)、匿名內部類、局部內部類。
成員內部類(能夠使用private、default、protected、public任意進行修飾。 類文件:外部類$內部類.class)
非靜態內部類(外部類裏使用非靜態內部類和平時使用其餘類沒什麼不一樣)
非靜態內部類必須寄存在一個外部類對象裏。所以,若是有一個非靜態內部類對象那麼必定存在對應的外部類對象。非靜態內部類對象單獨屬於外部類的某個對象。
非靜態內部類能夠直接訪問外部類的成員,可是外部類不能直接訪問非靜態內部類成員。
非靜態內部類不能有靜態方法、靜態屬性和靜態初始化塊。
外部類的靜態方法、靜態代碼塊不能訪問非靜態內部類,包括不能使用非靜態內部類定義變量、建立實例。
成員變量訪問要點:
內部類裏方法的局部變量:變量名。
內部類屬性:this.變量名。
外部類屬性:外部類名.this.變量名。
class Outer { private int age = 10; class Inner { int age = 20; public void show() { int age = 30; System.out.println("內部類方法裏的局部變量age:" + age);// 30 System.out.println("內部類的成員變量age:" + this.age);// 20 System.out.println("外部類的成員變量age:" + Outer.this.age);// 10 } } }
內部類的訪問:
外部類中定義內部類
new Inner()
外部類之外的地方使用非靜態內部類
Outer.Inner varname = new Outer().new Inner()
靜態內部類
定義方式
static class ClassName { //類體 }
使用要點
當一個靜態內部類對象存在,並不必定存在對應的外部類對象。 所以,靜態內部類的實例方法不能直接訪問外部類的實例方法。
靜態內部類看作外部類的一個靜態成員。 所以,外部類的方法中能夠經過:「靜態內部類.名字」的方式訪問靜態內部類的靜態成員,經過 new 靜態內部類()訪問靜態內部類的實例。
class Outer{ //至關於外部類的一個靜態成員 static class Inner{ } } public class TestStaticInnerClass { public static void main(String[] args) { //經過 new 外部類名.內部類名() 來建立內部類對象 Outer.Inner inner =new Outer.Inner(); } }
匿名內部類
適合那種只須要使用一次的類。好比:鍵盤監聽操做等等。
new 父類構造器(實參類表) \實現接口 () { //匿名內部類類體! }
this.addWindowListener(new WindowAdapter(){ @Override public void windowClosing(WindowEvent e) { System.exit(0); } } ); this.addKeyListener(new KeyAdapter(){ @Override public void keyPressed(KeyEvent e) { myTank.keyPressed(e); } @Override public void keyReleased(KeyEvent e) { myTank.keyReleased(e); } } );
注意
匿名內部類沒有訪問修飾符。
匿名內部類沒有構造方法。由於它連名字都沒有那又何來構造方法呢。
局部內部類
還有一種內部類,它是定義在方法內部的,做用域只限於本方法,稱爲局部內部類。
局部內部類的的使用主要是用來解決比較複雜的問題,想建立一個類來輔助咱們的解決方案,到那時又不但願這個類是公共可用的,因此就產生了局部內部類。局部內部類和成員內部類同樣被編譯,只是它的做用域發生了改變,它只能在該方法中被使用,出了該方法就會失效。
局部內部類在實際開發中應用不多。
public class Test2 { public void show() { //做用域僅限於該方法 class Inner { public void fun() { System.out.println("helloworld"); } } new Inner().fun(); } public static void main(String[] args) { new Test2().show(); } }
String基礎
String類又稱做不可變字符序列。
String位於java.lang包中,Java程序默認導入java.lang包下的全部類。
Java字符串就是Unicode字符序列,例如字符串「Java」就是4個Unicode字符’J’、’a’、’v’、’a’組成的。
Java容許使用符號"+"把兩個字符串鏈接起來。
String類和常量池
在Java的內存分析中,咱們會常常聽到關於「常量池」的描述,實際上常量池也分了如下三種:
1. 全局字符串常量池(String Pool):全局字符串常量池中存放的內容是在類加載完成後存到String Pool中的,在每一個VM中只有一份,存放的是字符串常量的引用值(在堆中生成字符串對象實例)。
2. class文件常量池(Class Constant Pool):class常量池是在編譯的時候每一個class都有的,在編譯階段,存放的是常量(文本字符串、final常量等)和符號引用。
3. 運行時常量池(Runtime Constant Pool):運行時常量池是在類加載完成以後,將每一個class常量池中的符號引用值轉存到運行時常量池中,也就是說,每一個class都有一個運行時常量池,類在解析以後,將符號引用替換成直接引用,與全局常量池中的引用值保持一致。
String str1 = "abc"; String str2 = new String("def"); String str3 = "abc"; String str4 = str2.intern(); String str5 = "def"; System.out.println(str1 == str3);// true System.out.println(str2 == str4);// false System.out.println(str4 == str5);// true
示例首先通過編譯以後,在該類的class常量池中存放一些符號引用,而後類加載以後,將class常量池中存放的符號引用轉存到運行時常量池中,而後通過驗證,準備階段以後,在堆中生成駐留字符串的實例對象(也就是上例中str1所指向的「abc」實例對象),而後將這個對象的引用存到全局String Pool中,也就是String Pool中,最後在解析階段,要把運行時常量池中的符號引用替換成直接引用,那麼就直接查詢String Pool,保證String Pool裏的引用值與運行時常量池中的引用值一致,大概整個過程就是這樣了。
回到示例程序,如今就很容易解釋整個程序的內存分配過程了,首先,在堆中會有一個「abc」實例,全局String Pool中存放着「abc」的一個引用值,而後在運行第二句的時候會生成兩個實例,一個是「def」的實例對象,而且String Pool中存儲一個「def」的引用值,還有一個是new出來的一個「def」的實例對象,與上面那個是不一樣的實例,當在解析str3的時候查找String Pool,裏面有「abc」的全局駐留字符串引用,因此str3的引用地址與以前的那個已存在的相同,str4是在運行的時候調用intern()函數,返回String Pool中「def」的引用值,若是沒有就將str2的引用值添加進去,在這裏,String Pool中已經有了「def」的引用值了,因此返回上面在new str2的時候添加到String Pool中的 「def」引用值,最後str5在解析的時候就也是指向存在於String Pool中的「def」的引用值,那麼這樣一分析以後,結果就容易理解了。
String類的經常使用方法
異常機制本質:就是當程序出現錯誤,程序安全退出的機制。
try { copyFile("d:/a.txt","e:/a.txt"); } catch (Exception e) { e.printStackTrace(); }
異常(Exception)的概念
異常指程序運行過程當中出現的非正常現象,例如用戶輸入錯誤、除數爲零、須要處理的文件不存在、數組下標越界等。
在Java的異常處理機制中,引進了不少用來描述和處理異常的類,稱爲異常類。異常類定義中包含了該類異常的信息和對異常進行處理的方法。
所謂異常處理,就是指程序在出現問題時依然能夠正確的執行完。
Java是採用面向對象的方式來處理異常的。處理過程:
拋出異常:在執行一個方法時,若是發生異常,則這個方法生成表明該異常的一個對象,中止當前執行路徑,並把異常對象提交給JRE。
捕獲異常:JRE獲得該異常後,尋找相應的代碼來處理該異常。JRE在方法的調用棧中查找,從生成異常的方法開始回溯,直到找到相應的異常處理代碼爲止。
異常分類
Java對異常進行了分類,不一樣類型的異常分別用不一樣的Java類表示,全部異常的根類爲java.lang.Throwable,Throwable下面又派生了兩個子類:Error和Exception。Java異常類的層次結構如圖所示。
Exception
Exception是程序自己可以處理的異常,如:空指針異常(NullPointerException)、數組下標越界異常(ArrayIndexOutOfBoundsException)、類型轉換異常(ClassCastException)、算術異常(ArithmeticException)等。
Exception類是全部異常類的父類,其子類對應了各類各樣可能出現的異常事件。 一般Java的異常可分爲:
RuntimeException 運行時異常
派生於RuntimeException的異常,如被 0 除、數組下標越界、空指針等,其產生比較頻繁,處理麻煩,若是顯式的聲明或捕獲將會對程序可讀性和運行效率影響很大。 所以由系統自動檢測並將它們交給缺省的異常處理程序(用戶可沒必要對其處理)。
這類異常一般是由編程錯誤致使的,因此在編寫程序時,並不要求必須使用異常處理機制來處理這類異常,常常須要經過增長「邏輯處理來避免這些異常」。
在引用數據類型轉換時,有可能發生類型轉換異常(ClassCastException)。
當程序訪問一個數組的某個元素時,若是這個元素的索引超出了0~數組長度-1這個範圍,則會出現數組下標越界異常(ArrayIndexOutOfBoundsException)。
在使用包裝類將字符串轉換成基本數據類型時,若是字符串的格式不正確,則會出現數字格式異常(NumberFormatException)。
注意事項
在方法拋出異常以後,運行時系統將轉爲尋找合適的異常處理器(exception handler)。潛在的異常處理器是異常發生時依次存留在調用棧中的方法的集合。當異常處理器所能處理的異常類型與方法拋出的異常類型相符時,即爲合適的異常處理器。
運行時系統從發生異常的方法開始,依次回查調用棧中的方法,直至找到含有合適異常處理器的方法並執行。當運行時系統遍歷調用棧而未找到合適的異常處理器,則運行時系統終止。同時,意味着Java程序的終止。
異常的處理方式之一:捕獲異常
捕獲異常是經過3個關鍵詞來實現的:try-catch-finally。用try來執行一段程序,若是出現異常,系統拋出一個異常,能夠經過它的類型來捕捉(catch)並處理它,最後一步是經過finally語句爲異常處理提供一個統一的出口,finally所指定的代碼都要被執行(catch語句可有多條;finally語句最多隻能有一條,根據本身的須要無關緊要)。
1. try:
try語句指定了一段代碼,該段代碼就是異常捕獲並處理的範圍。在執行過程當中,當任意一條語句產生異常時,就會跳過該條語句中後面的代碼。代碼中可能會產生並拋出一種或幾種類型的異常對象,它後面的catch語句要分別對這些異常作相應的處理。
一個try語句必須帶有至少一個catch語句塊或一個finally語句塊 。
注意事項
2. catch:
每一個try語句塊能夠伴隨一個或多個catch語句,用於處理可能產生的不一樣類型的異常對象。
經常使用方法,這些方法均繼承自Throwable類 。
toString()方法,顯示異常的類名和產生異常的緣由
getMessage()方法,只顯示產生異常的緣由,但不顯示類名。
printStackTrace()方法,用來跟蹤異常事件發生時堆棧的內容。
catch捕獲異常時的捕獲順序:若是異常類之間有繼承關係,在順序安排上需注意。越是頂層的類,越放在下面,再否則就直接把多餘的catch省略掉。也就是先捕獲子類異常再捕獲父類異常。
3. finally:
有些語句,無論是否發生了異常,都必需要執行,那麼就能夠把這樣的語句放到finally語句塊中。
一般在finally中關閉程序塊已打開的資源,好比:關閉文件流、釋放數據庫鏈接等。
try-catch-finally語句塊的執行過程:程序首先執行可能發生異常的try語句塊。若是try語句沒有出現異常則執行完後跳至finally語句塊執行;若是try語句出現異常,則中斷執行並根據發生的異常類型跳至相應的catch語句塊執行處理。catch語句塊能夠有多個,分別捕獲不一樣類型的異常。catch語句塊執行完後程序會繼續執行finally語句塊。finally語句是可選的,若是有的話,則無論是否發生異常,finally語句都會被執行。
注意事項
即便try和catch塊中存在return語句,finally語句也會執行。是在執行完finally語句後再經過return退出。
finally語句塊只有一種狀況是不會執行的,那就是在執行finally以前遇到了System.exit(0)結束程序運行。
異常的處理方式之二:聲明異常(throws子句)
數組的定義
數組是相同類型數據的有序集合。數組描述的是相同類型的若干個數據,按照必定的前後次序排列組合而成。其中,每個數據稱做一個元素,每一個元素能夠經過一個索引(下標)來訪問它們。數組的三個基本特色:
長度是肯定的。數組一旦被建立,它的大小就是不能夠改變的。
其元素必須是相同類型,不容許出現混合類型。
數組類型能夠是任何數據類型,包括基本類型和引用類型。
數組變量屬引用類型,數組也能夠當作是對象,數組中的每一個元素至關於該對象的成員變量。數組自己就是對象,Java中對象是在堆中的,所以數組不管保存原始類型仍是其餘對象類型,數組對象自己是在堆中存儲的。
數組聲明
數組的聲明方式有兩種(以一維數組爲例)
type[] arr_name; //(推薦使用這種方式) type arr_name[];
注意事項
聲明的時候並無實例化任何對象,只有在實例化數組對象時,JVM才分配空間,這時才與長度有關。
聲明一個數組的時候並無數組真正被建立。
構造一個數組,必須指定長度。
初始化
數組的初始化方式總共有三種:靜態初始化、動態初始化、默認初始化。下面針對這三種方式分別講解。
1. 靜態初始化
除了用new關鍵字來產生數組之外,還能夠直接在定義數組的同時就爲數組元素分配空間並賦值。
int[] a = { 1, 2, 3 };// 靜態初始化基本類型數組; Man[] mans = { new Man(1, 1), new Man(2, 2) };// 靜態初始化引用類型數組;
2.動態初始化
數組定義與爲數組元素分配空間並賦值的操做分開進行。
int[] a1 = new int[2];//動態初始化數組,先分配空間; a1[0]=1;//給數組元素賦值; a1[1]=2;//給數組元素賦值;
3.數組的默認初始化
數組是引用類型,它的元素至關於類的實例變量,所以數組一經分配空間,其中的每一個元素也被按照實例變量一樣的方式被隱式初始化。
int a2[] = new int[2]; // 默認值:0,0 boolean[] b = new boolean[2]; // 默認值:false,false String[] s = new String[2]; // 默認值:null, null
數組元素下標的合法區間:[0, length-1]。咱們能夠經過下標來遍歷數組中的元素,遍歷時能夠讀取元素的值或者修改元素的值。使用循環遍歷初始化和讀取數組。
public class Test { public static void main(String[] args) { int[] a = new int[4]; //初始化數組元素的值 for(int i=0;i<a.length;i++){ a[i] = 100*i; } //讀取元素的值 for(int i=0;i<a.length;i++){ System.out.println(a[i]); } } }
for-each循環:加強for循環for-each是JDK1.5新增長的功能,專門用於讀取數組或集合中全部的元素,即對數組進行遍歷。
public class Test { public static void main(String[] args) { String[] ss = { "aa", "bbb", "ccc", "ddd" }; for (String temp : ss) { System.out.println(temp); } } }
注意事項
for-each加強for循環在遍歷數組過程當中不能修改數組中某元素的值。
for-each僅適用於遍歷,不涉及有關索引(下標)的操做。
System類裏也包含了一個static void arraycopy(object src,int srcpos,object dest, int destpos,int length)方法,該方法能夠將src數組裏的元素值賦給dest數組的元素,其中srcpos指定從src數組的第幾個元素開始賦值,length參數指定將src數組的多少個元素賦給dest數組的元素。
public class testCopy { public static void main(String[] args) { String[] s1 = {"aa", "bb", "cc", "dd", "ee"}; String[] s2 = new String[10]; System.arraycopy(s1, 2, s2, 6, 3); for(int i=0; i<s2.length; i++){ System.out.println(i+"--"+s2[i]); } System.out.println("########"); removeElement(s1, 2); System.out.println("########"); extendRange(s1); } // 刪除數組中指定索引位置的元素,並將原數組返回 private static String[] removeElement(String[] s, int index){ System.arraycopy(s, index+1, s, index, s.length-index-1); s[s.length-1] = null; for (String m: s) { System.out.println(m); } return s; } // 數組的擴容(本質上時:先定義一個更大的數組,而後將原數組內容原封不動拷貝到新數組中) private static String[] extendRange(String[] s){ String[] s2 = new String[s.length + 10]; System.arraycopy(s, 0, s2, 0, s.length); for(String x: s2){ System.out.println(x); } return s2; } }
JDK提供的java.util.Arrays類,包含了經常使用的數組操做,方便咱們平常開發。Arrays類包含了:排序、查找、填充、打印內容等常見的操做。
import java.util.Arrays; public class TestArrays { public static void main(String[] args) { int[] a = {100, 20, 30, 5, 150, 80, 200}; System.out.println(a); System.out.println(Arrays.toString(a)); Arrays.sort(a); System.out.println(Arrays.toString(a)); // 二分法查找 System.out.println(Arrays.binarySearch(a, 30)); // 數組填充,前閉後開 Arrays.fill(a, 2, 4, 800); System.out.println(Arrays.toString(a)); } }
多維數組能夠當作以數組爲元素的數組。能夠有二維、三維、甚至更多維數組,可是實際開發中用的很是少。最多到二維數組(學習容器後,咱們通常使用容器,二維數組用的都不多)。
二維數組的聲明
public class Test { public static void main(String[] args) { // Java中多維數組的聲明和初始化應按從低維到高維的順序進行 int[][] a = new int[3][]; a[0] = new int[2]; a[1] = new int[4]; a[2] = new int[3]; // int a1[][]=new int[][4];//非法 } }
二維數組的靜態初始化
public class Test { public static void main(String[] args) { int[][] a = { { 1, 2, 3 }, { 3, 4 }, { 3, 5, 6, 7 } }; System.out.println(a[2][3]); } }
二維數組的動態初始化
import java.util.Arrays; public class Test { public static void main(String[] args) { int[][] a = new int[3][]; // a[0] = {1,2,5}; //錯誤,沒有聲明類型就初始化 a[0] = new int[] { 1, 2 }; a[1] = new int[] { 2, 2 }; a[2] = new int[] { 2, 2, 3, 4 }; System.out.println(a[2][3]); System.out.println(Arrays.toString(a[0])); System.out.println(Arrays.toString(a[1])); System.out.println(Arrays.toString(a[2])); } }
獲取數組長度
//獲取的二維數組第一維數組的長度。 System.out.println(a.length); //獲取第二維第一個數組長度。 System.out.println(a[0].length);
數組存儲表格數據
import java.util.Arrays; public class Test { public static void main(String[] args) { Object[] a1 = {1001,"高淇",18,"講師","2006-2-14"}; Object[] a2 = {1002,"高小七",19,"助教","2007-10-10"}; Object[] a3 = {1003,"高小琴",20,"班主任","2008-5-5"}; Object[][] emps = new Object[3][]; emps[0] = a1; emps[1] = a2; emps[2] = a3; System.out.println(Arrays.toString(emps[0])); System.out.println(Arrays.toString(emps[1])); System.out.println(Arrays.toString(emps[2])); } }
冒泡排序
import java.util.Arrays; public class TestBubbleSort { public static void main(String[] args) { int[] values = {3, 1, 6, 2, 9, 0, 7, 4, 5, 8}; bubble(values); bubble_improve(values); } public static void bubble(int[] values){ /*冒泡排序*/ int temp = 0; // 遍歷前n-1個數 for (int i = 0; i < values.length-1; i++) { // 每次循環都把最大值放到後面,因此後面的就沒必要在比較了 for (int j = 0; j < values.length-1-i; j++) { // 若是前一個值大於後一個值,則交換位置 if(values[j]>values[j+1]){ temp = values[j]; values[j] = values[j+1]; values[j+1] = temp; } } } System.out.println(Arrays.toString(values)); } public static void bubble_improve(int[] values){ /* 改良冒泡排序 */ /* 也就是當一次外循環沒有發生交換的時候,那麼中止 */ int temp = 0; // 遍歷前n-1個數 for (int i = 0; i < values.length-1; i++) { // 每次循環都把最大值放到後面,因此後面的就沒必要在比較了 boolean flag = true; for (int j = 0; j < values.length-1-i; j++) { // 若是前一個值大於後一個值,則交換位置 if(values[j]>values[j+1]){ temp = values[j]; values[j] = values[j+1]; values[j+1] = temp; flag = false; } } if(flag){ break; } } System.out.println(Arrays.toString(values)); } }
冒泡排序算法的運做以下:
比較相鄰的元素。若是第一個比第二個大,就交換他們兩個。
對每一對相鄰元素做一樣的工做,從開始第一對到結尾的最後一對。在這一點,最後的元素應該會是最大的數。
針對全部的元素重複以上的步驟,除了最後一個。
持續每次對愈來愈少的元素重複上面的步驟,直到沒有任何一對數字須要比較。
優化冒泡排序:
整個數列分紅兩部分:前面是無序數列,後面是有序數列。
初始狀態下,整個數列都是無序的,有序數列是空。
每一趟循環可讓無序數列中最大數排到最後,(也就是說有序數列的元素個數增長1),也就是不用再去顧及有序序列。
每一趟循環都從數列的第一個元素開始進行比較,依次比較相鄰的兩個元素,比較到無序數列的末尾便可(而不是數列的末尾);若是前一個大於後一個,交換。
判斷每一趟是否發生了數組元素的交換,若是沒有發生,則說明此時數組已經有序,無需再進行後續趟數的比較了。此時能夠停止比較。
二分法查找
import java.util.Arrays; public class TestBinarySearch { public static void main(String[] args) { int[] arr = {30, 20, 50 ,10, 80, 9, 7, 12, 100, 40, 8}; Arrays.sort(arr); System.out.println(Arrays.toString(arr)); System.out.println(myBinarySearch(arr, 400)); } public static int myBinarySearch(int[] arr, int value){ /* 二分法查找 */ int low = 0; int high = arr.length - 1; while (low <= high){ int mid = (low + high)/2; if (value == arr[mid]){ return mid; } if (value > arr[mid]){ low = mid + 1; } if (value < arr[mid]){ high = mid - 1; } System.out.println(low + "+" + high); } return -1; } }
Java是面向對象的語言,但並非「純面向對象」的,由於咱們常常用到的基本數據類型就不是對象。可是咱們在實際應用中常常須要將基本數據轉化成對象,以便於操做。好比:將基本數據類型存儲到Object[]數組或集合中的操做等等。
爲了解決這個不足,Java在設計類時爲每一個基本數據類型設計了一個對應的類進行表明,這樣八個和基本數據類型對應的類統稱爲包裝類(Wrapper Class)。
在這八個類名中,除了Integer和Character類之外,其它六個類的類名和基本數據類型一致,只是類名的第一個字母大寫而已。
包裝類的用途
對於包裝類來講,這些類的用途主要包含兩種:
做爲和基本數據類型對應的類型存在,方便涉及到對象的操做,如Object[]、集合等的操做。
包含每種基本數據類型的相關屬性如最大值、最小值等,以及相關的操做方法(這些操做方法的做用是在基本數據類型、包裝類對象、字符串之間提供相互之間的轉化!)。
自動裝箱和拆箱
自動裝箱和拆箱就是將基本數據類型和包裝類之間進行自動的互相轉換。JDK1.5後,Java引入了自動裝箱(autoboxing)/拆箱(unboxing)。
自動裝箱:基本類型的數據處於須要對象的環境中時,會自動轉爲「對象」。
Integer i = 100;//自動裝箱 //至關於編譯器自動爲您做如下的語法編譯: Integer i = Integer.valueOf(100);//調用的是valueOf(100),而不是new Integer(100)
自動拆箱:每當須要一個值時,對象會自動轉成基本數據類型,不必再去顯式調用intValue()、doubleValue()等轉型方法。
Integer i = 100; int j = i;//自動拆箱 //至關於編譯器自動爲您做如下的語法編譯: int j = i.intValue();
咱們能夠用一句話總結自動裝箱/拆箱:自動裝箱過程是經過調用包裝類的valueOf()方法實現的,而自動拆箱過程是經過調用包裝類的xxxValue()方法實現的(xxx表明對應的基本數據類型,如intValue()、doubleValue()等)。自動裝箱與拆箱的功能事實上是編譯器來幫的忙,編譯器在編譯時依據您所編寫的語法,決定是否進行裝箱或拆箱動做。
包裝類空指針異常問題
public class Test1 { public static void main(String[] args) { Integer i = null; int j = i; } }
包裝類的緩存問題
整型、char類型所對應的包裝類,在自動裝箱時,對於-128~127之間的值會進行緩存處理,其目的是提升效率。
緩存處理的原理爲:若是數據在-128~127這個區間,那麼在類加載時就已經爲該區間的每一個數值建立了對象,並將這256個對象存放到一個名爲cache的數組中。每當自動裝箱過程發生時(或者手動調用valueOf()時),就會先判斷數據是否在該區間,若是在則直接獲取數組中對應的包裝類對象的引用,若是不在該區間,則會經過new調用包裝類的構造方法來建立對象。
public class Test3 { public static void main(String[] args) { Integer in1 = -128; Integer in2 = -128; System.out.println(in1 == in2);//true 由於123在緩存範圍內 System.out.println(in1.equals(in2));//true Integer in3 = 1234; Integer in4 = 1234; System.out.println(in3 == in4);//false 由於1234不在緩存範圍內 System.out.println(in3.equals(in4));//true } }
內存分析
注意
JDK1.5之後,增長了自動裝箱與拆箱功能,如:
Integer i = 100; int j = new Integer(100);
自動裝箱調用的是valueOf()方法,而不是new Integer()方法。
自動拆箱調用的xxxValue()方法。
包裝類在自動裝箱時爲了提升效率,對於-128~127之間的值會進行緩存處理。超過範圍後,對象之間不能再使用==進行數值的比較,而是使用equals方法。
String 類對象表明不可變的Unicode字符序列,所以咱們能夠將String對象稱爲「不可變對象」。 那什麼叫作「不可變對象」呢?指的是對象內部的成員變量的值沒法再改變。
咱們發如今前面學習String的某些方法,好比:substring()是對字符串的截取操做,但本質是讀取原字符串內容生成了新的字符串。
在遇到字符串常量之間的拼接時,編譯器會作出優化,即在編譯期間就會完成字符串的拼接。所以,在使用==進行String對象之間的比較時,咱們須要特別注意。
public class TestString2 { public static void main(String[] args) { //編譯器作了優化,直接在編譯的時候將字符串進行拼接 String str1 = "hello" + " java";//至關於str1 = "hello java"; String str2 = "hello java"; System.out.println(str1 == str2);//true String str3 = "hello"; String str4 = " java"; //編譯的時候不知道變量中存儲的是什麼,因此沒辦法在編譯的時候優化 String str5 = str3 + str4; System.out.println(str2 == str5);//false } }
String類經常使用的方法有:
String類的下述方法能建立並返回一個新的String對象: concat()、 replace()、substring()、 toLowerCase()、 toUpperCase()、trim()。
提供查找功能的有關方法: endsWith()、 startsWith()、 indexOf()、lastIndexOf()。
提供比較功能的方法: equals()、equalsIgnoreCase()、compareTo()。
其它方法: charAt() 、length()。
StringBuffer和StringBuilder
StringBuffer和StringBuilder很是相似,均表明可變的字符序列。 這兩個類都是抽象類AbstractStringBuilder的子類,方法幾乎如出一轍。
StringBuffer JDK1.0版本提供的類,線程安全,作線程同步檢查, 效率較低。
StringBuilder JDK1.5版本提供的類,線程不安全,不作線程同步檢查,所以效率較高。 建議採用該類。
經常使用方法列表:
重載的public StringBuilder append(…)方法:能夠爲該StringBuilder 對象添加字符序列,仍然返回自身對象。
方法 public StringBuilder delete(int start,int end):能夠刪除從start開始到end-1爲止的一段字符序列,仍然返回自身對象。
方法 public StringBuilder deleteCharAt(int index):移除此序列指定位置上的 char,仍然返回自身對象。
重載的public StringBuilder insert(…)方法:能夠爲該StringBuilder 對象在指定位置插入字符序列,仍然返回自身對象。
方法 public String toString() 返回此序列中數據的字符串表示形式。
和 String 類含義相似的方法:
public int indexOf(String str) public int indexOf(String str,int fromIndex) public String substring(int start) public String substring(int start,int end) public int length() char charAt(int index)
StringBuffer/StringBuilder基本用法
public class TestStringBufferAndBuilder 1{ public static void main(String[] args) { /**StringBuilder*/ StringBuilder sb = new StringBuilder(); for (int i = 0; i < 7; i++) { sb.append((char) ('a' + i));//追加單個字符 } System.out.println(sb.toString());//轉換成String輸出 sb.append(", I can sing my abc!");//追加字符串 System.out.println(sb.toString()); /**StringBuffer*/ StringBuffer sb2 = new StringBuffer("中華人民共和國"); sb2.insert(0, "愛").insert(0, "我");//插入字符串 System.out.println(sb2); sb2.delete(0, 2);//刪除子字符串 System.out.println(sb2); sb2.deleteCharAt(0).deleteCharAt(0);//刪除某個字符 System.out.println(sb2.charAt(0));//獲取某個字符 System.out.println(sb2.reverse());//字符串逆序 } }
不可變和可變字符序列使用陷阱
String使用的陷阱:String一經初始化後,就不會再改變其內容了。對String字符串的操做其實是對其副本(原始拷貝)的操做,原來的字符串一點都沒有改變。好比:
String s ="a"; 建立了一個字符串
s = s+"b"; 實際上原來的"a"字符串對象已經丟棄了,如今又產生了另外一個字符串s+"b"(也就是"ab")。 若是屢次執行這些改變串內容的操做,會致使大量副本字符串對象存留在內存中,下降效率。若是這樣的操做放到循環中,會極大影響程序的時間和空間性能,甚至會形成服務器的崩潰。
相反,StringBuilder和StringBuffer類是對原字符串自己操做的,能夠對字符串進行修改而不產生副本拷貝或者產生少許的副本。所以能夠在循環中使用。
Date時間類(java.util.Date)
在標準Java類庫中包含一個Date類。它的對象表示一個特定的瞬間,精確到毫秒。
Date() 分配一個Date對象,並初始化此對象爲系統當前的日期和時間,能夠精確到毫秒)。
Date(long date) 分配 Date 對象並初始化此對象,以表示自從標準基準時間(稱爲「曆元(epoch)」,即 1970 年 1 月 1 日 00:00:00 GMT)以來的指定毫秒數。
boolean after(Date when) 測試此日期是否在指定日期以後。
booleanbefore(Date when) 測試此日期是否在指定日期以前。
boolean equals(Object obj) 比較兩個日期的相等性。
long getTime() 返回自 1970 年 1 月 1 日 00:00:00 GMT 以來此 Date 對象表示的毫秒數。
String toString() 把此 Date 對象轉換爲如下形式的 String:
dow mon dd hh:mm:ss zzz yyyy 其中: dow 是一週中的某一天 (Sun、 Mon、Tue、Wed、 Thu、 Fri、 Sat)。
DateFormat類的做用
把時間對象轉化成指定格式的字符串。反之,把指定格式的字符串轉化成時間對象。
DateFormat是一個抽象類,通常使用它的的子類SimpleDateFormat類來實現。
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class TestDateFormat { public static void main(String[] args) throws ParseException { // new出SimpleDateFormat對象 SimpleDateFormat s1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); SimpleDateFormat s2 = new SimpleDateFormat("yyyy-MM-dd"); // 將時間對象轉換成字符串 String daytime = s1.format(new Date()); System.out.println(daytime); System.out.println(s2.format(new Date())); System.out.println(new SimpleDateFormat("hh:mm:ss").format(new Date())); // 將符合指定格式的字符串轉成成時間對象.字符串格式須要和指定格式一致。 String time = "2007-10-7"; Date date = s2.parse(time); System.out.println("date1: " + date); time = "2007-10-7 20:15:30"; date = s1.parse(time); System.out.println("date2: " + date); } }
代碼中的格式化字符的具體含義:
時間格式字符也能夠爲咱們提供其餘的便利。好比:得到當前時間是今年的第幾天。
import java.text.SimpleDateFormat; import java.util.Date; public class TestDateFormat2 { public static void main(String[] args) { SimpleDateFormat s1 = new SimpleDateFormat("D"); String daytime = s1.format(new Date()); System.out.println(daytime); } }
Calendar日曆類
Calendar 類是一個抽象類,爲咱們提供了關於日期計算的相關功能,好比:年、月、日、時、分、秒的展現和計算。
GregorianCalendar 是 Calendar 的一個具體子類,提供了世界上大多數國家/地區使用的標準日曆系統。
注意月份的表示,一月是0,二月是1,以此類推,12月是11。
GregorianCalendar類和Calendar類的使用
import java.util.*; public class TestCalendar { public static void main(String[] args) { // 獲得相關日期元素 GregorianCalendar calendar = new GregorianCalendar(2999, 10, 9, 22, 10, 50); int year = calendar.get(Calendar.YEAR); // 打印:1999 int month = calendar.get(Calendar.MONTH); // 打印:10 int day = calendar.get(Calendar.DAY_OF_MONTH); // 打印:9 int day2 = calendar.get(Calendar.DATE); // 打印:9 // 日:Calendar.DATE和Calendar.DAY_OF_MONTH同義 int date = calendar.get(Calendar.DAY_OF_WEEK); // 打印:3 // 星期幾 這裏是:1-7.週日是1,週一是2,。。。週六是7 System.out.println(year); System.out.println(month); System.out.println(day); System.out.println(day2); System.out.println(date); // 設置日期 GregorianCalendar calendar2 = new GregorianCalendar(); calendar2.set(Calendar.YEAR, 2999); calendar2.set(Calendar.MONTH, Calendar.FEBRUARY); // 月份數:0-11 calendar2.set(Calendar.DATE, 3); calendar2.set(Calendar.HOUR_OF_DAY, 10); calendar2.set(Calendar.MINUTE, 20); calendar2.set(Calendar.SECOND, 23); printCalendar(calendar2); // 日期計算 GregorianCalendar calendar3 = new GregorianCalendar(2999, 10, 9, 22, 10, 50); calendar3.add(Calendar.MONTH, -7); // 月份減7 calendar3.add(Calendar.DATE, 7); // 增長7天 printCalendar(calendar3); // 日曆對象和時間對象轉化 Date d = calendar3.getTime(); GregorianCalendar calendar4 = new GregorianCalendar(); calendar4.setTime(new Date()); long g = System.currentTimeMillis(); } static void printCalendar(Calendar calendar) { int year = calendar.get(Calendar.YEAR); int month = calendar.get(Calendar.MONTH) + 1; int day = calendar.get(Calendar.DAY_OF_MONTH); int date = calendar.get(Calendar.DAY_OF_WEEK) - 1; // 星期幾 String week = "" + ((date == 0) ? "日" : date); int hour = calendar.get(Calendar.HOUR); int minute = calendar.get(Calendar.MINUTE); int second = calendar.get(Calendar.SECOND); System.out.printf("%d年%d月%d日,星期%s %d:%d:%d\n", year, month, day, week, hour, minute, second); } }
可視化日曆的編寫
import java.text.ParseException; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Scanner; public class TestCalendar2 { public static void main(String[] args) throws ParseException { System.out.println("請輸入日期(格式爲:2010-3-3):"); Scanner scanner = new Scanner(System.in); String dateString = scanner.nextLine(); // 2010-3-1 // 將輸入的字符串轉化成日期類 System.out.println("您剛剛輸入的日期是:" + dateString); String[] str = dateString.split("-"); int year = Integer.parseInt(str[0]); int month = new Integer(str[1]); int day = new Integer(str[2]); Calendar c = new GregorianCalendar(year, month - 1, day); // Month:0-11 // 你們本身補充另外一種方式:將字符串經過SImpleDateFormat轉化成Date對象, //再將Date對象轉化成日期類 // SimpleDateFormat sdfDateFormat = new SimpleDateFormat("yyyy-MM-dd"); // Date date = sdfDateFormat.parse(dateString); // Calendar c = new GregorianCalendar(); // c.setTime(date); // int day = c.get(Calendar.DATE); c.set(Calendar.DATE, 1); int dow = c.get(Calendar.DAY_OF_WEEK); // week:1-7 日一二三四五六 System.out.println("日\t一\t二\t三\t四\t五\t六"); for (int i = 0; i < dow - 1; i++) { System.out.print("\t"); } int maxDate = c.getActualMaximum(Calendar.DATE); // System.out.println("maxDate:"+maxDate); for (int i = 1; i <= maxDate; i++) { StringBuilder sBuilder = new StringBuilder(); if (c.get(Calendar.DATE) == day) { sBuilder.append(c.get(Calendar.DATE) + "*\t"); } else { sBuilder.append(c.get(Calendar.DATE) + "\t"); } System.out.print(sBuilder); // System.out.print(c.get(Calendar.DATE)+ // ((c.get(Calendar.DATE)==day)?"*":"")+"\t"); if (c.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) { System.out.print("\n"); } c.add(Calendar.DATE, 1); } } }
java.lang.Math提供了一系列靜態方法用於科學計算;其方法的參數和返回值類型通常爲double型。若是須要更增強大的數學運算能力,計算高等數學中的相關內容,能夠使用apache commons下面的Math類庫。
Math類的經常使用方法:
abs 絕對值
acos,asin,atan,cos,sin,tan 三角函數
sqrt 平方根
pow(double a, double b) a的b次冪
max(double a, double b) 取大值
min(double a, double b) 取小值
ceil(double a) 大於a的最小整數
floor(double a) 小於a的最大整數
random() 返回 0.0 到 1.0 的隨機數
long round(double a) double型的數據a轉換爲long型(四捨五入)
toDegrees(double angrad) 弧度->角度
toRadians(double angdeg) 角度->弧度
Random類的經常使用方法
import java.util.Random; public class TestRandom { public static void main(String[] args) { Random rand = new Random(); //隨機生成[0,1)之間的double類型的數據 System.out.println(rand.nextDouble()); //隨機生成int類型容許範圍以內的整型數據 System.out.println(rand.nextInt()); //隨機生成[0,1)之間的float類型的數據 System.out.println(rand.nextFloat()); //隨機生成false或者true System.out.println(rand.nextBoolean()); //隨機生成[0,10)之間的int類型的數據 System.out.print(rand.nextInt(10)); //隨機生成[20,30)之間的int類型的數據 System.out.print(20 + rand.nextInt(10)); //隨機生成[20,30)之間的int類型的數據(此種方法計算較爲複雜) System.out.print(20 + (int) (rand.nextDouble() * 10)); } }
java.io.File類:表明文件和目錄。 在開發中,讀取文件、生成文件、刪除文件、修改文件的屬性時常常會用到本類。
File類的常見構造方法:public File(String pathname)
以pathname爲路徑建立File對象,若是pathname是相對路徑,則默認的當前路徑在系統屬性user.dir中存儲
文件的建立
import java.io.File; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: pycharm * @file: FileTest1.java * @time: 2019/9/22 15:09 * @desc: File類的常見構造方法 */ public class FileTest1{ public static void main(String[] args) throws Exception{ test1(); } public static void test1() throws Exception{ System.out.println(System.getProperty("user.dir")); // 相對路徑,默認放到user.dir目錄下 File f = new File("a.txt"); // 建立文件 f.createNewFile(); // 絕對路徑 File f2 = new File("D:\\李添的數據哦!!!\\BookStudy\\else\\JavaWorkSpace\\b.txt"); f2.createNewFile(); } }
經過File對象能夠訪問文件的屬性:
測試File類訪問屬性的基本用法
import java.io.File; import java.util.Date; public class TestFile2 { public static void main(String[] args) throws Exception { File f = new File("d:/b.txt"); System.out.println("File是否存在:"+f.exists()); System.out.println("File是不是目錄:"+f.isDirectory()); System.out.println("File是不是文件:"+f.isFile()); System.out.println("File最後修改時間:"+new Date(f.lastModified())); System.out.println("File的大小:"+f.length()); System.out.println("File的文件名:"+f.getName()); System.out.println("File的目錄路徑:"+f.getPath()); } }
經過File對象建立空文件或目錄(在該對象所指的文件或目錄不存在的狀況下)
使用mkdir建立目錄
import java.io.File; public class TestFile3 { public static void main(String[] args) throws Exception { File f = new File("d:/c.txt"); f.createNewFile(); // 會在d盤下面生成c.txt文件 f.delete(); // 將該文件或目錄從硬盤上刪除 File f2 = new File("d:/電影/華語/大陸"); boolean flag = f2.mkdir(); //目錄結構中有一個不存在,則不會建立整個目錄樹 System.out.println(flag);//建立失敗 } }
使用mkdirs建立目錄
import java.io.File; public class TestFile4 { public static void main(String[] args) throws Exception { File f = new File("d:/c.txt"); f.createNewFile(); // 會在d盤下面生成c.txt文件 f.delete(); // 將該文件或目錄從硬盤上刪除 File f2 = new File("d:/電影/華語/大陸"); boolean flag = f2.mkdirs();//目錄結構中有一個不存在也不要緊;建立整個目錄樹 System.out.println(flag);//建立成功 } }
File類的綜合應用
import java.io.File; import java.io.IOException; public class TestFile5 { public static void main(String[] args) { //指定一個文件 File file = new File("d:/sxt/b.txt"); //判斷該文件是否存在 boolean flag= file.exists(); //若是存在就刪除,若是不存在就建立 if(flag){ //刪除 boolean flagd = file.delete(); if(flagd){ System.out.println("刪除成功"); }else{ System.out.println("刪除失敗"); } }else{ //建立 boolean flagn = true; try { //若是目錄不存在,先建立目錄 File dir = file.getParentFile(); dir.mkdirs(); //建立文件 flagn = file.createNewFile(); System.out.println("建立成功"); } catch (IOException e) { System.out.println("建立失敗"); e.printStackTrace(); } } //文件重命名(同窗能夠本身測試一下) //file.renameTo(new File("d:/readme.txt")); } }
遞歸遍歷目錄結構和樹狀展示
import java.io.File; public class TestFile6 { public static void main(String[] args) { File f = new File("d:/電影"); printFile(f, 0); } /** * 打印文件信息 * @param file 文件名稱 * @param level 層次數(實際就是:第幾回遞歸調用) */ static void printFile(File file, int level) { //輸出層次數 for (int i = 0; i < level; i++) { System.out.print("-"); } //輸出文件名 System.out.println(file.getName()); //若是file是目錄,則獲取子文件列表,並對每一個子文件進行相同的操做 if (file.isDirectory()) { File[] files = file.listFiles(); for (File temp : files) { //遞歸調用該方法:注意等+1 printFile(temp, level + 1); } } } }
JDK1.5引入了枚舉類型。枚舉類型的定義包括枚舉聲明和枚舉體。格式以下:
enum 枚舉名 { 枚舉體(常量列表) }
枚舉體就是放置一些常量。咱們能夠寫出咱們的第一個枚舉類型。
enum Season { SPRING, SUMMER, AUTUMN, WINDER }
全部的枚舉類型隱性地繼承自 java.lang.Enum。枚舉實質上仍是類!而每一個被枚舉的成員實質就是一個枚舉類型的實例,他們默認都是public static final修飾的。能夠直接經過枚舉類型名使用它們。
注意:
當你須要定義一組常量時,能夠使用枚舉類型。
儘可能不要使用枚舉的高級特性,事實上高級特性均可以使用普通類來實現,沒有必要引入枚舉,增長程序的複雜性!
枚舉的使用
import java.util.Random; public class TestEnum { public static void main(String[] args) { // 枚舉遍歷 for (Week k : Week.values()) { System.out.println(k); } // switch語句中使用枚舉 int a = new Random().nextInt(4); // 生成0,1,2,3的隨機數 switch (Season.values()[a]) { case SPRING: System.out.println("春天"); break; case SUMMER: System.out.println("夏天"); break; case AUTUMN: System.out.println("秋天"); break; case WINDTER: System.out.println("冬天"); break; } } } /**季節*/ enum Season { SPRING, SUMMER, AUTUMN, WINDTER } /**星期*/ enum Week { 星期一, 星期二, 星期三, 星期四, 星期五, 星期六, 星期日 }
什麼是「容器」呢?生活中的容器不難理解,是用來容納物體的,如鍋碗瓢盆、箱子和包等。程序中的「容器」也有相似的功能,就是用來容納和管理數據。
數組就是一種容器,能夠在其中放置對象或基本類型數據。
數組的優點:是一種簡單的線性序列,能夠快速地訪問數組元素,效率高。若是從效率和類型檢查的角度講,數組是最好的。
數組的劣勢:不靈活。容量須要事先定義好,不能隨着需求的變化而擴容。
容器,也叫集合(Collection)。
泛型是JDK1.5之後增長的,它能夠幫助咱們創建類型安全的集合。在使用了泛型的集合中,遍歷時沒必要進行強制類型轉換。JDK提供了支持泛型的編譯器,將運行時的類型檢查提早到了編譯時執行,提升了代碼可讀性和安全性。
泛型的本質就是「數據類型的參數化」。 咱們能夠把「泛型」理解爲數據類型的一個佔位符(形式參數),即告訴編譯器,在調用泛型時必須傳入實際類型。
咱們能夠在類的聲明處增長泛型列表,如:<T,E,V>。
此處,字符能夠是任何標識符,通常採用這3個字母。
自定義泛型
泛型類的聲明
class MyCollection<E> {// E:表示泛型; Object[] objs = new Object[5]; public E get(int index) {// E:表示泛型; return (E) objs[index]; } public void set(E e, int index) {// E:表示泛型; objs[index] = e; } }
泛型E像一個佔位符同樣表示「未知的某個數據類型」,咱們在真正調用的時候傳入這個「數據類型」。
泛型類的應用
public class TestGenerics { public static void main(String[] args) { // 這裏的」String」就是實際傳入的數據類型; MyCollection<String> mc = new MyCollection<String>(); mc.set("aaa", 0); mc.set("bbb", 1); String str = mc.get(1); //加了泛型,直接返回String類型,不用強制轉換; System.out.println(str); } }
容器中使用泛型
Collection 表示一組對象,它是集中、收集的意思。Collection接口的兩個子接口是List、Set接口。
Collection接口中定義的方法
因爲List、Set是Collection的子接口,意味着全部List、Set的實現類都有上面的方法。
List是有序、可重複的容器。
有序:List中每一個元素都有索引標記。能夠根據元素的索引標記(在List中的位置)訪問元素,從而精確控制這些元素。
可重複:List容許加入重複的元素。更確切地講,List一般容許知足 e1.equals(e2) 的元素重複加入容器。
除了Collection接口中的方法,List多了一些跟順序(索引)有關的方法
List接口經常使用的實現類有3個:ArrayList、LinkedList和Vector。
List的經常使用方法:
public class TestList { /** * 測試add/remove/size/isEmpty/contains/clear/toArrays等方法 */ public static void test01() { List<String> list = new ArrayList<String>(); System.out.println(list.isEmpty()); // true,容器裏面沒有元素 list.add("高淇"); System.out.println(list.isEmpty()); // false,容器裏面有元素 list.add("高小七"); list.add("高小八"); System.out.println(list); System.out.println("list的大小:" + list.size()); System.out.println("是否包含指定元素:" + list.contains("高小七")); list.remove("高淇"); System.out.println(list); Object[] objs = list.toArray(); System.out.println("轉化成Object數組:" + Arrays.toString(objs)); list.clear(); System.out.println("清空全部元素:" + list); } public static void main(String[] args) { test01(); } }
兩個List之間的元素處理
public class TestList { public static void main(String[] args) { test02(); } /** * 測試兩個容器之間元素處理 */ public static void test02() { List<String> list = new ArrayList<String>(); list.add("高淇"); list.add("高小七"); list.add("高小八"); List<String> list2 = new ArrayList<String>(); list2.add("高淇"); list2.add("張三"); list2.add("李四"); System.out.println(list.containsAll(list2)); //false list是否包含list2中全部元素 System.out.println(list); list.addAll(list2); //將list2中全部元素都添加到list中 System.out.println(list); list.removeAll(list2); //從list中刪除同時在list和list2中存在的元素 System.out.println(list); list.retainAll(list2); //取list和list2的交集 System.out.println(list); } }
List中操做索引的經常使用方法
public class TestList { public static void main(String[] args) { test03(); } /** * 測試List中關於索引操做的方法 */ public static void test03() { List<String> list = new ArrayList<String>(); list.add("A"); list.add("B"); list.add("C"); list.add("D"); System.out.println(list); // [A, B, C, D] list.add(2, "高"); System.out.println(list); // [A, B, 高, C, D] list.remove(2); System.out.println(list); // [A, B, C, D] list.set(2, "c"); System.out.println(list); // [A, B, c, D] System.out.println(list.get(1)); // 返回:B list.add("B"); System.out.println(list); // [A, B, c, D, B] System.out.println(list.indexOf("B")); // 1 從頭至尾找到第一個"B" System.out.println(list.lastIndexOf("B")); // 4 從尾到頭找到第一個"B" } }
ArrayList特色和底層實現:
LinkedList特色和底層實現
LinkedList底層用雙向鏈表實現的存儲。特色:查詢效率低,增刪效率高,線程不安全。
雙向鏈表也叫雙鏈表,是鏈表的一種,它的每一個數據節點中都有兩個指針,分別指向前一個節點和後一個節點。 因此,從雙向鏈表中的任意一個節點開始,均可以很方便地找到全部節點。
每一個節點都應該有3部份內容:
class Node { Node previous; //前一個節點 Object element; //本節點保存的數據 Node next; //後一個節點 }
Vector向量
如何選用ArrayList、LinkedList、Vector?
須要線程安全時,用Vector。
不存在線程安全問題時,而且查找較多用ArrayList(通常使用它)。
不存在線程安全問題時,增長或刪除元素較多用LinkedList。
Map接口
Map就是用來存儲「鍵(key)-值(value) 對」的。 Map類中存儲的「鍵值對」經過鍵來標識,因此「鍵對象」不能重複。
Map 接口的實現類有HashMap、TreeMap、HashTable、Properties等。
Map接口中經常使用的方法
HashMap和HashTable
HashMap採用哈希算法實現,是Map接口最經常使用的實現類。 因爲底層採用了哈希表存儲數據,咱們要求鍵不能重複,若是發生重複,新的鍵值對會替換舊的鍵值對。 HashMap在查找、刪除、修改方面都有很是高的效率。
import java.util.HashMap; import java.util.Map; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: pycharm * @file: TestMap.java * @time: 2019/10/5 14:05 * @desc: */ public class TestMap { public static void main(String[] args){ Map<Integer, String> m1 = new HashMap<>(); m1.put(1, "one"); m1.put(2, "two"); m1.put(3, "three"); System.out.println(m1.get(1)); System.out.println(m1.size()); System.out.println(m1.isEmpty()); System.out.println(m1.containsKey(2)); System.out.println(m1.containsValue("four")); Map<Integer, String> m2 = new HashMap<>(); m2.put(4, "四"); m2.put(5, "五"); m1.putAll(m2); System.out.println(m1); // map中鍵不能重複!若是重複(是否重複是根據equals方法),則新的覆蓋舊的! m1.put(3, "三"); System.out.println(m1); } }
HashTable類和HashMap用法幾乎同樣,底層實現幾乎同樣,只不過HashTable的方法添加了synchronized關鍵字確保線程同步檢查,效率較低。
HashMap與HashTable的區別
HashMap: 線程不安全,效率高。容許key或value爲null。
HashTable: 線程安全,效率低。不容許key或value爲null。
HashMap底層實現詳解
HashMap底層實現採用了哈希表,這是一種很是重要的數據結構。對於咱們之後理解不少技術都很是有幫助(好比:redis數據庫的核心技術和HashMap同樣)。
數據結構中由數組和鏈表來實現對數據的存儲,他們各有特色。
(1) 數組:佔用空間連續。 尋址容易,查詢速度快。可是,增長和刪除效率很是低。
(2) 鏈表:佔用空間不連續。 尋址困難,查詢速度慢。可是,增長和刪除效率很是高。
那麼,咱們能不能結合數組和鏈表的優勢(即查詢快,增刪效率也高)呢? 答案就是「哈希表」。 哈希表的本質就是「數組+鏈表」。
Entry數組存儲結構圖
存儲數據過程put(key,value)
咱們的目的是將「key-value兩個對象」成對存放到HashMap的Entry[]數組中。參見如下步驟:
得到key對象的hashcode:首先調用key對象的hashcode()方法,得到hashcode。
根據hashcode計算出hash值(要求在[0, 數組長度-1]區間):hashcode是一個整數,咱們須要將它轉化成[0, 數組長度-1]的範圍。咱們要求轉化後的hash值儘可能均勻地分佈在[0,數組長度-1]這個區間,減小「hash衝突」。
一種極端簡單和低下的算法是:hash值 = hashcode/hashcode
也就是說,hash值老是1。意味着,鍵值對對象都會存儲到數組索引1位置,這樣就造成一個很是長的鏈表。至關於每存儲一個對象都會發生「hash衝突」,HashMap也退化成了一個「鏈表」。
一種簡單和經常使用的算法是(相除取餘算法):hash值 = hashcode%數組長度
這種算法可讓hash值均勻的分佈在[0,數組長度-1]的區間。 早期的HashTable就是採用這種算法。可是,這種算法因爲使用了「除法」,效率低下。JDK後來改進了算法。首先約定數組長度必須爲2的整數冪,這樣採用位運算便可實現取餘的效果:hash值 = hashcode&(數組長度-1)。
事實上,爲了得到更好的散列效果,JDK對hashcode進行了兩次散列處理(核心目標就是爲了分佈更散更均勻)
生成Entry對象:如上所述,一個Entry對象包含4部分:key對象、value對象、hash值、指向下一個Entry對象的引用。咱們如今算出了hash值。下一個Entry對象的引用爲null。
將Entry對象放到table數組中:若是本Entry對象對應的數組索引位置尚未放Entry對象,則直接將Entry對象存儲進數組;若是對應索引位置已經有Entry對象,則將已有Entry對象的next指向本Entry對象,造成鏈表。
總結如上過程:當添加一個元素(key-value)時,首先計算key的hash值,以此肯定插入數組中的位置,可是可能存在同一hash值的元素已經被放在數組同一位置了,這時就添加到同一hash值的元素的後面,他們在數組的同一位置,就造成了鏈表,同一個鏈表上的Hash值是相同的,因此說數組存放的是鏈表。 JDK8中,當鏈表長度大於8時,鏈表就轉換爲紅黑樹,這樣又大大提升了查找的效率。
取數據過程get(key):咱們須要經過key對象得到「鍵值對」對象,進而返回value對象。明白了存儲數據過程,取數據就比較簡單了,參見如下步驟:
hashcode()和equals方法的關係:Java中規定,兩個內容相同(equals()爲true)的對象必須具備相等的hashCode。由於若是equals()爲true而兩個對象的hashcode不一樣;那在整個存儲過程當中就發生了悖論。
擴容問題:HashMap的位桶數組,初始大小爲16。實際使用時,顯然大小是可變的。若是位桶數組中的元素達到(0.75*數組 length), 就從新調整數組大小變爲原來2倍大小。擴容很耗時。擴容的本質是定義新的更大的數組,並將舊數組內容挨個拷貝到新數組中。
JDK8將鏈表在大於8狀況下變爲紅黑二叉樹:JDK8中,HashMap在存儲一個元素時,當對應鏈表長度大於8時,鏈表就轉換爲紅黑樹,這樣又大大提升了查找的效率。
二叉樹和紅黑二叉樹
二叉樹(BinaryTree)由一個節點及兩棵互不相交的、分別稱做這個根的左子樹和右子樹的二叉樹組成。
二叉樹的左子樹和右子樹是嚴格區分而且不能隨意顛倒的。
排序二叉樹特性以下:
(1) 左子樹上全部節點的值均小於它的根節點的值。
(2) 右子樹上全部節點的值均大於它的根節點的值。
平衡二叉樹(AVL)
紅黑二叉樹
紅黑二叉樹(簡稱:紅黑樹),它首先是一棵二叉樹,同時也是一棵自平衡的排序二叉樹。
紅黑樹在原有的排序二叉樹增長了以下幾個要求:
每一個節點要麼是紅色,要麼是黑色。
根節點永遠是黑色的。
全部的葉節點都是空節點(即 null),而且是黑色的。
每一個紅色節點的兩個子節點都是黑色。(從每一個葉子到根的路徑上不會有兩個連續的紅色節點)
從任一節點到其子樹中每一個葉子節點的路徑都包含相同數量的黑色節點。
這些約束強化了紅黑樹的關鍵性質:從根到葉子的最長的可能路徑很少於最短的可能路徑的兩倍長。這樣就讓樹大體上是平衡的。
紅黑樹是一個更高效的檢索二叉樹,JDK 提供的集合類 TreeMap、TreeSet 自己就是一個紅黑樹的實現。
紅黑樹的基本操做:插入、刪除、左旋、右旋、着色。 每插入或者刪除一個節點,可能會致使樹不在符合紅黑樹的特徵,須要進行修復,進行 「左旋、右旋、着色」操做,使樹繼續保持紅黑樹的特性。
TreeMap的使用和底層實現
Set接口
HashSet基本使用
重點體會「Set是無序、不可重複」的核心要點。
public class Test { public static void main(String[] args) { Set<String> s = new HashSet<String>(); s.add("hello"); s.add("world"); System.out.println(s); s.add("hello"); //相同的元素不會被加入 System.out.println(s); s.add(null); System.out.println(s); s.add(null); System.out.println(s); } }
HashSet底層實現
TreeSet的使用和底層實現
TreeSet底層實際是用TreeMap實現的,內部維持了一個簡化版的TreeMap,經過key來存儲Set的元素。 TreeSet內部須要對存儲的元素進行排序,所以,咱們對應的類須要實現Comparable接口。這樣,才能根據compareTo()方法比較對象之間的大小,才能進行內部排序。
public class Test { public static void main(String[] args) { User u1 = new User(1001, "高淇", 18); User u2 = new User(2001, "高希希", 5); Set<User> set = new TreeSet<User>(); set.add(u1); set.add(u2); } } class User implements Comparable<User> { int id; String uname; int age; public User(int id, String uname, int age) { this.id = id; this.uname = uname; this.age = age; } /** * 返回0 表示 this == obj 返回正數表示 this > obj 返回負數表示 this < obj */ @Override public int compareTo(User o) { if (this.id > o.id) { return 1; } else if (this.id < o.id) { return -1; } else { return 0; } } }
使用TreeSet要點:
(1) 因爲是二叉樹,須要對元素作內部排序。 若是要放入TreeSet中的類沒有實現Comparable接口,則會拋出異常:java.lang.ClassCastException。
(2) TreeSet中不能放入null元素。
迭代器爲咱們提供了統一的遍歷容器的方式。
若是遇到遍歷容器時,判斷刪除元素的狀況,使用迭代器遍歷!
public class Test { public static void main(String[] args) { List<String> aList = new ArrayList<String>(); for (int i = 0; i < 5; i++) { aList.add("a" + i); } System.out.println(aList); for (Iterator<String> iter = aList.iterator(); iter.hasNext();) { String temp = iter.next(); System.out.print(temp + "\t"); if (temp.endsWith("3")) {// 刪除3結尾的字符串 iter.remove(); } } System.out.println(); System.out.println(aList); } }
遍歷集合的方法總結
import java.util.*; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TestIterator.java * @time: 2019/10/8 14:57 * @desc: 迭代器學習 */ public class TestIterator { public static void main(String[] args){ testIteratorList(); testIteratorSet(); testIteratorMap1(); testIteratorMap2(); } // 遍歷List public static void testIteratorList(){ List<String> list = new ArrayList<>(); list.add("aa"); list.add("bb"); list.add("cc"); for(Iterator<String> iter=list.iterator(); iter.hasNext();){ String temp = iter.next(); System.out.println(temp); } } // 遍歷Set public static void testIteratorSet(){ Set<String> set = new HashSet<>(); set.add("aa"); set.add("bb"); set.add("cc"); for(Iterator<String> iter=set.iterator(); iter.hasNext();){ String temp = iter.next(); System.out.println(temp); } } // 遍歷Map:方法1 public static void testIteratorMap1(){ Map<Integer, String> map = new HashMap<>(); map.put(100, "aa"); map.put(200, "bb"); map.put(300, "cc"); Set<Map.Entry<Integer, String>> ss = map.entrySet(); for(Iterator<Map.Entry<Integer, String>> iter = ss.iterator(); iter.hasNext();){ Map.Entry<Integer, String> temp = iter.next(); System.out.println(temp.getKey() + "--" + temp.getValue()); } } // 遍歷Map:方法2 public static void testIteratorMap2(){ Map<Integer, String> map = new HashMap<>(); map.put(100, "aa"); map.put(200, "bb"); map.put(300, "cc"); Set<Integer> keySet = map.keySet(); for(Iterator<Integer> iter = keySet.iterator(); iter.hasNext();){ Integer key = iter.next(); System.out.println(key + "--" + map.get(key)); } } }
類 java.util.Collections 提供了對Set、List、Map進行排序、填充、查找元素的輔助方法。
void sort(List) //對List容器內的元素排序,排序的規則是按照升序進行排序。
void shuffle(List) //對List容器內的元素進行隨機排列。
void reverse(List) //對List容器內的元素進行逆續排列 。
void fill(List, Object) //用一個特定的對象重寫整個List容器。
int binarySearch(List, Object)//對於順序的List容器,採用折半查找的方法查找特定對象。
import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TestCollections.java * @time: 2019/10/8 15:24 * @desc: 學習Collections輔助類 */ public class TestCollections { public static void main(String[] args){ List<String> list = new ArrayList<>(); for(int i =0; i<10; i++){ list.add("li" + i); } System.out.println(list); // 隨機排列list中的元素 Collections.shuffle(list); System.out.println(list); // 逆序排列 Collections.reverse(list); System.out.println(list); // 遞增排序 Collections.sort(list); System.out.println(list); // 二分查找 System.out.println(Collections.binarySearch(list, "li")); System.out.println(Collections.binarySearch(list, "li2")); } }
對於任何程序設計語言而言,輸入輸出(Input/Output)系統都是很是核心的功能。程序運行須要數據,數據的獲取每每須要跟外部系統進行通訊,外部系統多是文件、數據庫、其餘程序、網絡、IO設備等等。外部系統比較複雜多變,那麼咱們有必要經過某種手段進行抽象、屏蔽外部的差別,從而實現更加便捷的編程。
數據源:數據源data source,提供數據的原始媒介。常見的數據源有:數據庫、文件、其餘程序、內存、網絡鏈接、IO設備。
數據源分爲:源設備、目標設備。
源設備:爲程序提供數據,通常對應輸入流。
目標設備:程序數據的目的地,通常對應輸出流。
流的概念
流是一個抽象、動態的概念,是一連串連續動態的數據集合。
對於輸入流而言,數據源就像水箱,流(stream)就像水管中流動着的水流,程序就是咱們最終的用戶。咱們經過流(A Stream)將數據源(Source)中的數據(information)輸送到程序(Program)中。
對於輸出流而言,目標數據源就是目的地(dest),咱們經過流(A Stream)將程序(Program)中的數據(information)輸送到目的數據源(dest)中。
第一個簡單的IO流程序及深刻理解
當程序須要讀取數據源的數據時,就會經過IO流對象開啓一個通向數據源的流,經過這個IO流對象的相關方法能夠順序讀取數據源中的數據。
import java.io.*; public class TestIO1 { public static void main(String[] args) { try { //建立輸入流 FileInputStream fis = new FileInputStream("d:/a.txt"); // 文件內容是:abc //一個字節一個字節的讀取數據 int s1 = fis.read(); // 打印輸入字符a對應的ascii碼值97 int s2 = fis.read(); // 打印輸入字符b對應的ascii碼值98 int s3 = fis.read(); // 打印輸入字符c 對應的ascii碼值99 int s4 = fis.read(); // 因爲文件內容已經讀取完畢,返回-1 System.out.println(s1); System.out.println(s2); System.out.println(s3); System.out.println(s4); // 流對象使用完,必須關閉!否則,總佔用系統資源,最終會形成系統崩潰! fis.close(); } catch (Exception e) { e.printStackTrace(); } } }
注意:
在示例10-1中咱們讀取的文件內容是已知的,所以能夠使用固定次數的「int s= fis.read();」語句讀取內容,可是在實際開發中一般咱們根本不知道文件的內容,所以咱們在讀取的時候須要配合while循環使用。
爲了保證出現異常後流的正常關閉,一般要將流的關閉語句要放到finally語句塊中,而且要判斷流是否是null。
使用流讀取文件內容(經典代碼,必定要掌握)
import java.io.*; public class TestIO2 { public static void main(String[] args) { FileInputStream fis = null; try { fis = new FileInputStream("d:/a.txt"); // 內容是:abc StringBuilder sb = new StringBuilder(); int temp = 0; //當temp等於-1時,表示已經到了文件結尾,中止讀取 while ((temp = fis.read()) != -1) { sb.append((char) temp); } System.out.println(sb); } catch (Exception e) { e.printStackTrace(); } finally { try { //這種寫法,保證了即便遇到異常狀況,也會關閉流對象。 if (fis != null) { fis.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
Java中流的概念細分
按流的方向分類:
輸入流:數據流向是數據源到程序(以InputStream、Reader結尾的流)。
輸出流:數據流向是程序到目的地(以OutPutStream、Writer結尾的流)。
按處理的數據單元分類:
字節流:以字節爲單位獲取數據,命名上以Stream結尾的流通常是字節流,如FileInputStream、FileOutputStream。
字符流:以字符爲單位獲取數據,命名上以Reader/Writer結尾的流通常是字符流,如FileReader、FileWriter。
按處理對象不一樣分類:
節點流:能夠直接從數據源或目的地讀寫數據,如FileInputStream、FileReader、DataInputStream等。
處理流:不直接鏈接到數據源或目的地,是」處理流的流」。經過對其餘流的處理提升程序的性能,如BufferedInputStream、BufferedReader等。處理流也叫包裝流。
節點流處於IO操做的第一線,全部操做必須經過它們進行;處理流能夠對節點流進行包裝,提升性能或提升程序的靈活性。
Java中IO流類的體系
InputStream/OutputStream:字節流的抽象類。
Reader/Writer:字符流的抽象類。
FileInputStream/FileOutputStream:節點流:以字節爲單位直接操做「文件」。
ByteArrayInputStream/ByteArrayOutputStream:節點流:以字節爲單位直接操做「字節數組對象」。
ObjectInputStream/ObjectOutputStream:處理流:以字節爲單位直接操做「對象」。
DataInputStream/DataOutputStream:處理流:以字節爲單位直接操做「基本數據類型與字符串類型」。
FileReader/FileWriter:節點流:以字符爲單位直接操做「文本文件」(注意:只能讀寫文本文件)。
BufferedReader/BufferedWriter:處理流:將Reader/Writer對象進行包裝,增長緩存功能,提升讀寫效率。
BufferedInputStream/BufferedOutputStream:處理流:將InputStream/OutputStream對象進行包裝,增長緩存功能,提升 讀寫效率。
InputStreamReader/OutputStreamWriter:處理流:將字節流對象轉化成字符流對象。
PrintStream:處理流:將OutputStream進行包裝,能夠方便地輸出字符,更加靈活。
四大IO抽象類
InputStream/OutputStream和Reader/writer類是全部IO流類的抽象父類,咱們有必要簡單瞭解一下這個四個抽象類的做用。而後,經過它們具體的子類熟悉相關的用法。
IO文件字節輸入流操做標準步驟
字節輸入流測試:
import java.io.*; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TestIO05.java * @time: 2019/10/17 16:39 * @desc: 理解操做步驟 */ public class TestIO05 { public static void main(String[] args){ // 1. 建立源 File src = new File("abc.txt"); // 2. 選擇流 try{ InputStream is = new FileInputStream(src); System.out.println(src.getAbsolutePath()); // 3. 操做(讀取) int data1 = is.read(); // 第1個數據 int data2 = is.read(); // 第2個數據 int data3 = is.read(); // 第3個數據 System.out.println((char)data1); System.out.println((char)data2); System.out.println((char)data3); // 4. 釋放資源 is.close(); }catch(FileNotFoundException e){ e.printStackTrace(); }catch (IOException e){ e.printStackTrace(); } } }
字節輸入流標準操做
import java.io.*; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TestIO06.java * @time: 2019/10/17 16:39 * @desc: 理解操做步驟 標準 */ public class TestIO06 { public static void main(String[] args){ // 1. 建立源 File src = new File("abc.txt"); // 2. 選擇流 InputStream is = null; try{ is = new FileInputStream(src); // 3. 操做(讀取) int temp; while((temp=is.read()) != -1){ System.out.println((char)temp); } }catch(FileNotFoundException e){ e.printStackTrace(); }catch (IOException e){ e.printStackTrace(); }finally{ // 4. 釋放資源 if (null != is) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
字節輸出流標準操做
import java.io.*; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TestIO07.java * @time: 2019/10/17 16:39 * @desc: 讀入字符數組 */ public class TestIO07 { public static void main(String[] args){ // 1. 建立源 File src = new File("abc.txt"); // 2. 選擇流 InputStream is = null; try{ is = new FileInputStream(src); // 3. 操做(讀取) // 緩衝容器,這裏設爲3個字節 byte[] car = new byte[3]; // 接受長度 int len = -1; while((len=is.read(car)) != -1){ // 字節數組 --> 字符串(解碼) String str = new String(car, 0, len); System.out.println(str); } }catch(FileNotFoundException e){ e.printStackTrace(); }catch (IOException e){ e.printStackTrace(); }finally{ // 4. 釋放資源 if (null != is) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
IO文件字節輸出流操做標準步驟
輸出流實戰
import java.io.*; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TestIO08.java * @time: 2019/10/17 18:10 * @desc: 文件字節輸出流 */ public class TestIO08 { public static void main(String[] args){ // 1. 建立源 File dest = new File("dest.txt"); // 2. 選擇流 OutputStream os = null; try{ // true則是增長,false則是不增長 os = new FileOutputStream(dest, true); // 3. 操做(寫出) String temp = "IO is so easy!"; byte[] datas = temp.getBytes(); os.write(datas, 0, datas.length); os.flush(); }catch(FileNotFoundException e){ e.printStackTrace(); }catch (IOException e){ e.printStackTrace(); }finally{ // 釋放資源 if(null != os){ try { os.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
文件的拷貝
import java.io.*; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TestFileCopy.java * @time: 2019/10/17 18:22 * @desc: 文件的拷貝 */ public class TestFileCopy { public static void main(String[] args) { copy("test.png", "copy_test.png"); } public static void copy(String srcPath, String destPath){ // 1. 建立源 // 源頭 File src = new File(srcPath); File dest = new File(destPath); // 2. 選擇流 InputStream is = null; OutputStream os = null; try{ is = new FileInputStream(src); os = new FileOutputStream(dest, true); // 3. 操做(分段讀取) // 緩衝容器 byte[] flush = new byte[1024]; // 接受長度 int len = -1; while((len=is.read(flush)) != -1){ // 字節數組 --> 字符串(解碼) String str = new String(flush, 0, len); os.write(flush, 0, len); } os.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { // 釋放資源 先打開的後關閉 try{ if(null != os){ os.close(); } } catch (IOException e) { e.printStackTrace(); } try{ if(null != is){ is.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
字符輸入流
import java.io.*; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TestIO07.java * @time: 2019/10/17 16:39 * @desc: 文件字符輸入流 */ public class TestIO09 { public static void main(String[] args){ // 1. 建立源 File src = new File("abc.txt"); // 2. 選擇流 Reader reader = null; try{ reader = new FileReader(src); // 3. 操做(讀取) char[] flush = new char[1024]; // 接受長度 int len = -1; while((len=reader.read(flush)) != -1){ String str = new String(flush, 0, len); System.out.println(str); } }catch(FileNotFoundException e){ e.printStackTrace(); }catch (IOException e){ e.printStackTrace(); }finally{ // 4. 釋放資源 if (null != reader) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
字符輸出流
import java.io.*; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TestIO08.java * @time: 2019/10/17 18:10 * @desc: 文件字符輸出流 */ public class TestIO10 { public static void main(String[] args){ // 1. 建立源 File dest = new File("dest.txt"); // 2. 選擇流 Writer writer = null; try{ // true則是增長,false則是不增長 writer = new FileWriter(dest, false); // 3. 操做(寫出) // 寫法1 String temp = "IO is so easy!我是你大爺"; char[] datas = temp.toCharArray(); writer.write(datas, 0, datas.length); writer.flush(); // 寫法2 String temp = "IO is so easy!我是你大爺"; writer.write(temp); writer.flush(); // 寫法3 writer.append("IO is so easy!").append("我是你大爺"); writer.flush(); }catch(FileNotFoundException e){ e.printStackTrace(); }catch (IOException e){ e.printStackTrace(); }finally{ // 釋放資源 if(null != writer){ try { writer.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
字節數組輸入流
import java.io.*; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TestIO07.java * @time: 2019/10/17 16:39 * @desc: 字節數組輸入流 */ public class TestIO11 { public static void main(String[] args){ // 1. 建立源 byte[] src = "talk is cheap show me the code. ".getBytes(); // 2. 選擇流 InputStream is = null; try{ is = new ByteArrayInputStream(src); // 3. 操做(讀取) // 緩衝容器,這裏設爲5個字節 byte[] car = new byte[5]; // 接受長度 int len = -1; while((len=is.read(car)) != -1){ // 字節數組 --> 字符串(解碼) String str = new String(car, 0, len); System.out.println(str); } }catch (IOException e){ e.printStackTrace(); }finally{ // 4. 釋放資源 if (null != is) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
字節數組輸出流:
import java.io.*; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TestIO08.java * @time: 2019/10/17 18:10 * @desc: 字節數組輸出流 */ public class TestIO12 { public static void main(String[] args){ // 1. 建立源:不用建立源 byte[] dest = null; // 2. 選擇流:新增方法 ByteArrayOutputStream baos = null; try{ // true則是增長,false則是不增長 baos = new ByteArrayOutputStream(); // 3. 操做(寫出) String temp = "show me the code bie bibi"; byte[] datas = temp.getBytes(); baos.write(datas, 0, datas.length); baos.flush(); // 獲取數據 dest = baos.toByteArray(); System.out.println(dest.length + "-->" + new String(dest, 0, baos.size())); } catch(IOException e){ e.printStackTrace(); } finally{ // 釋放資源 if(null != baos){ try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
從文件拷貝到字節數組,再從字節數組輸出到文件。
import java.io.*; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TestIO08.java * @time: 2019/10/17 18:10 * @desc: 圖片讀取到字節數組,字節數組寫出到文件 */ public class TestIO13 { public static void main(String[] args){ byte[] datas = fileToByteArray("test.png"); System.out.println(datas.length); byteArrayToFile(datas, "p-byte.png"); } public static byte[] fileToByteArray(String filePath){ /* 1. 圖片讀取到字節數組中 1). 圖片到程序:FileInputStream 2). 程序到字節數組:ByteArrayOutputStream */ // 1. 建立源與目的地 File src = new File(filePath); byte[] dest = null; // 2. 選擇流 InputStream is = null; ByteArrayOutputStream baos = null; try{ is = new FileInputStream(src); baos = new ByteArrayOutputStream(); // 3. 操做:分段讀取 byte[] flush = new byte[1024*10]; int len = -1; while((len = is.read(flush)) != -1){ baos.write(flush, 0, len); // 寫出到字節數組中 } baos.flush(); return baos.toByteArray(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { // 4. 釋放資源 try{ if(null != is){ is.close(); } } catch (IOException e) { e.printStackTrace(); } } return null; } public static void byteArrayToFile(byte[] src, String filePath){ /* 2. 字節數組寫出到文件 1). 字節數組到程序:ByteArrayInputStream 2). 程序寫出到文件:FileOutputStream */ // 1. 建立源 File dest = new File(filePath); // 2. 選擇流 InputStream is = null; OutputStream os = null; try{ is = new ByteArrayInputStream(src); os = new FileOutputStream(dest, false); // 3. 操做:分段讀取 byte[] flush = new byte[5]; // 緩衝容器 int len = -1; while((len = is.read(flush)) != 1){ os.write(flush, 0, len); } os.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { // 4. 釋放資源 try { if (null != os) { os.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
java1.7以後能夠用try…with…resource自動釋放
try(is;os){} try(InputStream is = new FileInputStream(src); OutputStream os = new FileOutputStream(dest);){}
IO基礎操做
import java.io.File; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TestIO1.java * @time: 2019/10/9 17:19 * @desc: IO學習1 */ public class TestIO1 { public static void main(String[] args){ // 輸出文件分隔符 System.out.println(File.separator); // 1. 構建File對象 String path = "F:/BookStudy/else/Java知識點思惟導圖.png"; File src = new File(path); // 輸出文件大小 System.out.println(src.length()); // 2. 第二種構建File對象的方法 File src2 = new File("F:/BookStudy/else", "Java知識點思惟導圖.png"); System.out.println(src2.length()); // 3. 第三種構建File對象的方法 File src3 = new File(new File("F:/BookStudy/else"), "Java知識點思惟導圖.png"); System.out.println(src3.length()); // 相對路徑的源路徑 System.out.println(System.getProperty("user.dir")); // 絕對路徑 System.out.println(src3.getAbsolutePath()); // 構建一個不存在的對象 File src4 = new File("aaa/asdf.jpg"); System.out.println(src4.getAbsolutePath()); } }
文件操做
import java.io.File; import java.io.IOException; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TestIO2.java * @time: 2019/10/11 17:31 * @desc: IO操做api */ public class TestIO2 { public static void main(String[] args) throws IOException { File src = new File("F:/BookStudy/else/Java知識點思惟導圖.png"); // 基本信息 System.out.println("名稱:" + src.getName()); System.out.println("路徑:" + src.getPath()); System.out.println("絕對路徑:" + src.getAbsolutePath()); System.out.println("父路徑:" + src.getParent()); System.out.println("父對象:" + src.getParentFile().getName()); // 文件狀態 System.out.println("是否存在:" + src.exists()); System.out.println("是否文件:" + src.isFile()); System.out.println("是否文件夾:" + src.isDirectory()); // 獲取文件的字節數,若是是文件夾,則爲0。 System.out.println("長度:" + src.length()); // 建立文件:不存在才建立,返回true,否則返回false;不帶後綴只是文件名,不是文件夾 boolean flag = src.createNewFile(); System.out.println(flag); // 文件的刪除:刪除已經存在的文件 flag = src.delete(); System.out.println(flag); } }
文件夾的建立和遍歷
import java.io.File; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TestIO3.java * @time: 2019/10/11 17:50 * @desc: 文件夾建立和遍歷 */ public class TestIO3 { public static void main(String[] args){ // mkdir():確保上級目錄存在,不存在則建立失敗 // mkdirs():上級目錄能夠不存在,不存在則一同建立 File dir = new File("D:/"); boolean flag1 = dir.mkdir(); boolean flag2 = dir.mkdirs(); System.out.println(flag1); System.out.println(flag2); // list():列出下級名稱 // listFiles():列出下級File對象 String[] subNames = dir.list(); for(String s: subNames){ System.out.println(s); } File[] subFiles = dir.listFiles(); for(File s: subFiles){ System.out.println(s.getAbsolutePath()); } // listRoots():列出全部盤符 File[] roots = dir.listRoots(); for(File r: roots){ System.out.println(r.getAbsolutePath()); } // 遞歸:方法本身調用本身 // 遞歸頭:什麼時候結束遞歸 // 遞歸體:重複調用 printName(dir, 0); } public static void printName(File src, int deep){ /* 打印子孫級目錄和文件的名稱 */ for(int i=0; i<deep; i++){ System.out.print("-"); } System.out.println(src.getName()); if(null == src || !src.exists()){ return; } else if(src.isDirectory()){ for(File s: src.listFiles()){ printName(s, deep + 1); } } } }
統計文件夾的大小
import java.io.File; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TestIO4.java * @time: 2019/10/15 15:20 * @desc: 統計文件夾的大小 */ public class TestIO4 { public static void main(String[] args){ File src = new File("F:\\BookStudy"); count(src); System.out.println(LEN); } private static long LEN = 0; public static void count(File src){ // 獲取大小 if(null != src && src.exists()){ if(src.isFile()){ LEN += src.length(); }else{ for(File s: src.listFiles()){ count(s); } } } } }
使用面向對象:統計文件夾大小
import java.io.File; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: DirCount.java * @time: 2019/10/15 15:58 * @desc: 使用面向對象:統計文件夾大小 */ public class DirCount { // 大小 private long len; // 文件夾路徑 private String path; // 源 private File src; // 文件的個數 private int fileSize; // 文件夾的個數 private int dirSize; public DirCount(String path){ this.path = path; this.src = new File(path); count(this.src); } private void count(File src){ // 獲取大小 if(null != src && src.exists()){ if(src.isFile()){ this.len += src.length(); this.fileSize++; }else{ this.dirSize++; for(File s: src.listFiles()){ count(s); } } } } public long getLen() { return len; } public int getFileSize() { return fileSize; } public int getDirSize() { return dirSize; } public static void main(String[] args){ DirCount dir = new DirCount("F:\\BookStudy"); System.out.println(dir.getLen()); System.out.println("文件的數量" + "--->" + dir.getFileSize()); System.out.println("文件夾的數量" + "--->" + dir.getDirSize()); DirCount dir2 = new DirCount("F:\\BookStudy\\else"); System.out.println(dir2.getLen()); System.out.println("文件的數量" + "--->" + dir2.getFileSize()); System.out.println("文件夾的數量" + "--->" + dir2.getDirSize()); } }
編碼和解碼
import java.io.UnsupportedEncodingException; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: ContentEncode.java * @time: 2019/10/15 16:26 * @desc: 編碼:字符串-->字節;解碼:字節-->字符串 */ public class ContentEncode { public static void main(String[] args) throws UnsupportedEncodingException { String msg = "你怕不是個鐵憨憨"; // 編碼:字節數組 byte[] datas = msg.getBytes(); System.out.println(datas); // 中文utf-8:一個字符佔3個字節;默認使用工程的字符集 System.out.println(datas.length); // 編碼:其餘字符集 datas = msg.getBytes("UTF-16LE"); System.out.println(datas.length); datas = msg.getBytes("GBK"); System.out.println(datas.length); // 解碼 msg = new String(datas, 0, datas.length, "gbk"); System.out.println(msg); } }
亂碼的緣由:
裝飾器模式原理剖析
模擬聲音
/** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: DecorateTest01.java * @time: 2019/10/18 15:53 * @desc: 實現放大器對聲音放大的功能 */ public class DecorateTest01 { public static void main(String[] args){ Person p = new Person(); p.say(); // 裝飾 Amplifier am = new Amplifier(p); am.say(); } } interface Say{ void say(); } class Person implements Say{ // 屬性 private int voice = 10; @Override public void say() { System.out.println("人的聲音爲:" + this.getVoice()); } public int getVoice() { return voice; } public void setVoice(int voice) { this.voice = voice; } } class Amplifier implements Say{ private Person p; Amplifier(Person p){ this.p = p; } @Override public void say() { System.out.println("人的聲音爲:" + p.getVoice()*100); System.out.println("噪音..."); } }
模擬咖啡
/** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: DecorateTest02.java * @time: 2019/10/18 15:53 * @desc: 模擬咖啡 */ public class DecorateTest02 { public static void main(String[] args){ Drink coffee = new Coffee(); Drink suger = new Suger(coffee); // 裝飾 System.out.println(suger.info() + "-->" + suger.cost()); Drink milk = new Milk(coffee); // 裝飾 System.out.println(milk.info() + "-->" + milk.cost()); Drink mixed = new Milk(suger); // 裝飾 System.out.println(mixed.info() + "-->" + mixed.cost()); } } // 抽象組件 interface Drink{ double cost(); // 費用 String info(); // 說明 } // 具體組件 class Coffee implements Drink{ private String name = "原味咖啡"; @Override public double cost() { return 10; } @Override public String info() { return name; } } // 抽象裝飾類 abstract class Decorate implements Drink{ // 對抽象組件的引用 private Drink drink; public Decorate(Drink drink){ this.drink = drink; } @Override public double cost() { return this.drink.cost(); } @Override public String info() { return this.drink.info(); } } // 具體裝飾類 class Milk extends Decorate{ public Milk(Drink drink) { super(drink); } @Override public double cost() { return super.cost()*4; } @Override public String info() { return super.info() + "加入了牛奶"; } } class Suger extends Decorate{ public Suger(Drink drink) { super(drink); } @Override public double cost() { return super.cost()*2; } @Override public String info() { return super.info() + "加入了糖"; } }
字節緩衝流(輸入輸出同理)
import java.io.*; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TestIO07.java * @time: 2019/10/17 16:39 * @desc: 加入緩衝流(只須要釋放底層的is) */ public class BufferedTest02 { public static void main(String[] args){ // 1. 建立源 File src = new File("abc.txt"); // 2. 選擇流 InputStream is = null; try{ is = new BufferedInputStream(new FileInputStream(src)); // 3. 操做(讀取) byte[] flush = new byte[1024]; // 接受長度 int len = -1; while((len=is.read(flush)) != -1){ // 字節數組 --> 字符串(解碼) String str = new String(flush, 0, len); System.out.println(str); } }catch(FileNotFoundException e){ e.printStackTrace(); }catch (IOException e){ e.printStackTrace(); }finally{ // 4. 釋放資源 if (null != is) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
字符緩衝流(輸入輸出同理)
import java.io.*; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TestIO07.java * @time: 2019/10/17 16:39 * @desc: 文件字符輸入流 加入緩衝流 */ public class BufferedTest03 { public static void main(String[] args){ // 1. 建立源 File src = new File("abc.txt"); // 2. 選擇流 BufferedReader reader = null; try{ reader = new BufferedReader(new FileReader(src)); // 3. 操做(讀取) String line = null; while((line = reader.readLine()) != null){ System.out.println(line); } }catch(FileNotFoundException e){ e.printStackTrace(); }catch (IOException e){ e.printStackTrace(); }finally{ // 4. 釋放資源 if (null != reader) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
轉換流:InputStreamReader OutputStreamWriter
import java.io.*; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: ConvertTest1.java * @time: 2019/10/19 14:48 * @desc: 轉換流:InputStreamReader OutputStreamWriter */ public class ConvertTest1 { public static void main(String[] args){ // 操做System.in和System.out try(BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));){ // 循環獲取鍵盤的輸入(exit退出),輸入此內容 String msg = ""; while(!msg.equals("exit")){ msg = reader.readLine(); // 循環讀取 writer.write(msg); // 循環寫出 writer.newLine(); writer.flush(); // 強制刷新 } } catch (IOException e) { System.out.println("操做異常"); } } }
import java.io.*; import java.net.URL; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: ConvertTest2.java * @time: 2019/10/19 14:48 * @desc: 轉換流:InputStreamReader OutputStreamWriter */ public class ConvertTest2 { public static void main(String[] args) { // 中文亂碼 test1(); // 中文不是亂碼 test2(); // 效率更高 test3(); } public static void test1(){ // 操做網絡流,下載百度的源代碼 try(InputStream is = new URL("http://www.baidu.com").openStream();){ int temp; while((temp=is.read()) != -1){ System.out.print((char)temp); } } catch (IOException e) { System.out.println("操做異常"); } } public static void test2(){ // 操做網絡流,下載百度的源代碼 try(InputStreamReader is = new InputStreamReader(new URL("http://www.baidu.com").openStream(), "UTF-8")){ int temp; while((temp=is.read()) != -1){ System.out.print((char)temp); } } catch (IOException e) { System.out.println("操做異常"); } } public static void test3(){ // 操做網絡流,下載百度的源代碼 try(BufferedReader reader = new BufferedReader( new InputStreamReader( new URL("http://www.baidu.com").openStream(), "UTF-8" ) ); BufferedWriter writer = new BufferedWriter( new OutputStreamWriter( new FileOutputStream("baidu.html"), "UTF-8" ) ) ){ String msg; while((msg = reader.readLine()) != null){ writer.write(msg); // 字符集不統一,字節數不夠出現亂碼 writer.newLine(); } } catch (IOException e) { System.out.println("操做異常"); } } }
數據流
import com.sun.xml.internal.messaging.saaj.util.ByteInputStream; import java.io.*; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: DataTest1.java * @time: 2019/10/19 15:57 * @desc: 數據流 */ public class DataTest1 { public static void main(String[] args) throws IOException { // 寫出 ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream( new BufferedOutputStream(baos) ); // 操做數據類型 + 數據 dos.writeUTF("編碼辛酸淚啊"); dos.writeInt(18); dos.writeBoolean(false); dos.writeChar('a'); dos.flush(); byte[] datas = baos.toByteArray(); System.out.println(datas.length); // 讀取 DataInputStream dis = new DataInputStream( new BufferedInputStream( new ByteArrayInputStream(datas) ) ); // 順序與寫出一致 String msg = dis.readUTF(); int age = dis.readInt(); boolean flag = dis.readBoolean(); char ch = dis.readChar(); System.out.println(flag); } }
對象流
將對象數據序列化並反序列化
import java.io.*; import java.util.Date; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: DataTest1.java * @time: 2019/10/19 15:57 * @desc: 對象流 */ public class ObjectTest1 { public static void main(String[] args) throws IOException, ClassNotFoundException { // 寫出:序列化 ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream dos = new ObjectOutputStream( new BufferedOutputStream(baos) ); // 操做數據類型 + 數據 dos.writeUTF("編碼辛酸淚"); dos.writeInt(18); dos.writeBoolean(false); dos.writeChar('a'); // 對象 dos.writeObject("誰解其中味"); dos.writeObject(new Date()); Employee emp = new Employee("馬雲", 400); dos.writeObject(emp); dos.flush(); byte[] datas = baos.toByteArray(); System.out.println(datas.length); // 讀取:反序列化 ObjectInputStream dis = new ObjectInputStream( new BufferedInputStream( new ByteArrayInputStream(datas) ) ); // 順序與寫出一致 String msg = dis.readUTF(); int age = dis.readInt(); boolean flag = dis.readBoolean(); char ch = dis.readChar(); System.out.println(flag); // 對象的數據還原 Object str = dis.readObject(); Object date = dis.readObject(); Object employee = dis.readObject(); if (str instanceof String){ String strObj = (String) str; System.out.println(strObj); } if (date instanceof Date){ Date strObj = (Date) date; System.out.println(strObj); } if (employee instanceof Employee){ Employee strObj = (Employee) employee; System.out.println(strObj.getName() + "-->" + strObj.getSalary()); } } } // javabean 封裝數據 class Employee implements java.io.Serializable{ private transient String name; // 該數據不須要序列化 private double salary; public Employee() { } public Employee(String name, double salary) { this.name = name; this.salary = salary; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } }
在上面的基礎上,序列化成文件並反序列化
import java.io.*; import java.util.Date; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: DataTest2.java * @time: 2019/10/19 15:57 * @desc: 對象流 */ public class ObjectTest2 { public static void main(String[] args) throws IOException, ClassNotFoundException { // 寫出:序列化 ObjectOutputStream oos = new ObjectOutputStream( new BufferedOutputStream( new FileOutputStream("obj.txt") ) ); // 操做數據類型 + 數據 oos.writeUTF("編碼辛酸淚"); oos.writeInt(18); oos.writeBoolean(false); oos.writeChar('a'); // 對象 oos.writeObject("誰解其中味"); oos.writeObject(new Date()); Employee emp = new Employee("馬雲", 400); oos.writeObject(emp); oos.flush(); oos.close(); // 讀取:反序列化 ObjectInputStream ois = new ObjectInputStream( new BufferedInputStream( new FileInputStream("obj.txt") ) ); // 順序與寫出一致 String msg = ois.readUTF(); int age = ois.readInt(); boolean flag = ois.readBoolean(); char ch = ois.readChar(); System.out.println(flag); // 對象的數據還原 Object str = ois.readObject(); Object date = ois.readObject(); Object employee = ois.readObject(); if (str instanceof String){ String strObj = (String) str; System.out.println(strObj); } if (date instanceof Date){ Date strObj = (Date) date; System.out.println(strObj); } if (employee instanceof Employee){ Employee strObj = (Employee) employee; System.out.println(strObj.getName() + "-->" + strObj.getSalary()); } } }
打印流
PrintStream
import java.io.*; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: PrintTest1.java * @time: 2019/10/20 15:43 * @desc: 打印流 */ public class PrintTest1 { public static void main(String[] args) throws FileNotFoundException { // 打印流System.out PrintStream ps = System.out; ps.println("打印流"); ps.println(true); ps = new PrintStream( new BufferedOutputStream( new FileOutputStream("print.txt") ), true ); ps.println("打印流"); ps.println(true); ps.close(); // 重定向輸出端 System.setOut(ps); System.out.println("change"); // 重定向回控制檯 System.setOut( new PrintStream( new BufferedOutputStream( new FileOutputStream(FileDescriptor.out) ), true ) ); System.out.println("i am backing..."); } }
PrintWriter
import java.io.*; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: PrintTest2.java * @time: 2019/10/20 15:43 * @desc: 打印流 */ public class PrintTest2 { public static void main(String[] args) throws FileNotFoundException { PrintWriter pw = new PrintWriter( new BufferedOutputStream( new FileOutputStream("print.txt") ), true ); pw.println("打印流"); pw.println(true); pw.close(); } }
文件分割
隨機讀取和寫入流
import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.util.Random; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: RanTest1.java * @time: 2019/10/21 9:01 * @desc: 隨機讀取和寫入流 RandomAccessFile */ public class RanTest1 { public static void main(String[] args) throws IOException { // 分多少塊 File src = new File("D:\\李添的數據哦!!!\\BookStudy\\else\\JAVAPro\\src\\PrintTest2.java"); // 總長度 long len = src.length(); // 每塊大小 int blockSize = 240; // 塊數:多少塊 int size = (int)Math.ceil(len*1.0/blockSize); System.out.println(size); int beginPos = 0; int actualSize = (int)(blockSize>len?len:blockSize); for(int i=0; i<size; i++){ beginPos = i*blockSize; if(i == size-1){ // 最後一塊 actualSize = (int)len; }else{ actualSize = blockSize; // 剩餘量 len -= actualSize; } System.out.println(i + "-->" + beginPos + "-->" + actualSize); test1(i, beginPos, actualSize); } } // 指定起始位置,讀取剩餘指定長度內容 public static void test1(int i, int beginPos, int actualSize) throws IOException { RandomAccessFile raf = new RandomAccessFile(new File("D:\\李添的數據哦!!!\\BookStudy\\else\\JAVAPro\\src\\PrintTest2.java"), "r"); // 指定起始位置 // int beginPos = 2; // 實際大小 // int actualSize = 128; // 隨機讀取 raf.seek(beginPos); byte[] flush = new byte[124]; // 接受長度 int len = -1; while((len = raf.read(flush)) != -1){ if (actualSize > len){ // 實際大小大於接受長度,則獲取本次讀取的全部內容 System.out.println(new String(flush, 0, len)); actualSize -= len; }else{ System.out.println(new String(flush, 0, actualSize)); break; } } raf.close(); } }
增長輸出流
import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: RanTest1.java * @time: 2019/10/21 9:01 * @desc: 隨機讀取和寫入流 RandomAccessFile 並增長輸出流 */ public class RanTest2 { public static void main(String[] args) throws IOException { // 分多少塊 File src = new File("D:\\李添的數據哦!!!\\BookStudy\\else\\JAVAPro\\src\\PrintTest2.java"); // 總長度 long len = src.length(); // 每塊大小 int blockSize = 240; // 塊數:多少塊 int size = (int)Math.ceil(len*1.0/blockSize); System.out.println(size); int beginPos = 0; int actualSize = (int)(blockSize>len?len:blockSize); for(int i=0; i<size; i++){ beginPos = i*blockSize; if(i == size-1){ // 最後一塊 actualSize = (int)len; }else{ actualSize = blockSize; // 剩餘量 len -= actualSize; } System.out.println(i + "-->" + beginPos + "-->" + actualSize); test1(i, beginPos, actualSize); } } // 指定起始位置,讀取剩餘指定長度內容 public static void test1(int i, int beginPos, int actualSize) throws IOException { RandomAccessFile raf = new RandomAccessFile(new File("D:\\李添的數據哦!!!\\BookStudy\\else\\JAVAPro\\src\\PrintTest2.java"), "r"); RandomAccessFile raf2 = new RandomAccessFile(new File("Print_Copy_" + i + ".java"), "rw"); // 指定起始位置 // int beginPos = 2; // 實際大小 // int actualSize = 128; // 隨機讀取 raf.seek(beginPos); byte[] flush = new byte[124]; // 接受長度 int len = -1; while((len = raf.read(flush)) != -1){ if (actualSize > len){ // 實際大小大於接受長度,則獲取本次讀取的全部內容 raf2.write(flush, 0, len); actualSize -= len; }else{ raf2.write(flush, 0, actualSize); break; } } raf2.close(); raf.close(); } }
對RanTest進行封裝,功能是拆分文件,面向對象思想封裝
import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.util.ArrayList; import java.util.List; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: SplitFile.java * @time: 2019/10/21 9:35 * @desc: 對RanTest進行封裝,功能是拆分文件,面向對象思想封裝 */ public class SplitFile { // 源頭 private File src; // 目的地(文件夾) private String destDir; // 全部分割後的文件存儲路徑 private List<String> destPaths; // 每塊大小 private int blockSize; // 塊數:多少塊 private int size; public SplitFile(String srcPath, String destDir, int blockSize){ this.src = new File(srcPath); this.destDir = destDir; this.blockSize = blockSize; this.destPaths = new ArrayList<>(); // 初始化 init(); } // 初始化 private void init(){ // 總長度 long len = this.src.length(); // 塊數:多少塊 this.size = (int)Math.ceil(len*1.0/blockSize); // 路徑 for(int i=0; i<size; i++){ this.destPaths.add(this.destDir + "/" + i + "-" + this.src.getName()); } } // 分割 public void split() throws IOException { /* 1. 計算每一塊起始位置及大小 2. 分割 */ // 總長度 long len = this.src.length(); // 每塊大小 int size = (int)Math.ceil(len*1.0/blockSize); System.out.println(size); int beginPos = 0; int actualSize = (int)(this.blockSize>len?len:this.blockSize); for(int i=0; i<size; i++){ beginPos = i*blockSize; if(i == size-1){ // 最後一塊 actualSize = (int)len; }else{ actualSize = blockSize; // 剩餘量 len -= actualSize; } splitDetail(i, beginPos, actualSize); } } // 指定起始位置,讀取剩餘指定長度內容 private void splitDetail(int i, int beginPos, int actualSize) throws IOException { RandomAccessFile raf = new RandomAccessFile((this.src), "r"); RandomAccessFile raf2 = new RandomAccessFile((this.destPaths.get(i)), "rw"); raf.seek(beginPos); byte[] flush = new byte[124]; // 接受長度 int len = -1; while((len = raf.read(flush)) != -1){ if (actualSize > len){ // 實際大小大於接受長度,則獲取本次讀取的全部內容 raf2.write(flush, 0, len); actualSize -= len; }else{ raf2.write(flush, 0, actualSize); break; } } raf2.close(); raf.close(); } public static void main(String[] args) throws IOException { SplitFile sf = new SplitFile("test.png", "dest", 1024*10); sf.split(); } }
增長文件的合併功能
import java.io.*; import java.util.ArrayList; import java.util.List; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: SplitFile.java * @time: 2019/10/21 9:35 * @desc: 對RanTest進行封裝,功能是拆分文件,面向對象思想封裝 */ public class SplitFile { // 源頭 private File src; // 目的地(文件夾) private String destDir; // 全部分割後的文件存儲路徑 private List<String> destPaths; // 每塊大小 private int blockSize; // 塊數:多少塊 private int size; public SplitFile(String srcPath, String destDir, int blockSize){ this.src = new File(srcPath); this.destDir = destDir; this.blockSize = blockSize; this.destPaths = new ArrayList<>(); // 初始化 init(); } // 初始化 private void init(){ // 總長度 long len = this.src.length(); // 塊數:多少塊 this.size = (int)Math.ceil(len*1.0/blockSize); // 路徑 for(int i=0; i<size; i++){ this.destPaths.add(this.destDir + "/" + i + "-" + this.src.getName()); } } // 分割 public void split() throws IOException { /* 1. 計算每一塊起始位置及大小 2. 分割 */ // 總長度 long len = this.src.length(); // 每塊大小 int size = (int)Math.ceil(len*1.0/blockSize); System.out.println(size); int beginPos = 0; int actualSize = (int)(this.blockSize>len?len:this.blockSize); for(int i=0; i<size; i++){ beginPos = i*blockSize; if(i == size-1){ // 最後一塊 actualSize = (int)len; }else{ actualSize = blockSize; // 剩餘量 len -= actualSize; } splitDetail(i, beginPos, actualSize); } } // 指定起始位置,讀取剩餘指定長度內容 private void splitDetail(int i, int beginPos, int actualSize) throws IOException { RandomAccessFile raf = new RandomAccessFile((this.src), "r"); RandomAccessFile raf2 = new RandomAccessFile((this.destPaths.get(i)), "rw"); raf.seek(beginPos); byte[] flush = new byte[124]; // 接受長度 int len = -1; while((len = raf.read(flush)) != -1){ if (actualSize > len){ // 實際大小大於接受長度,則獲取本次讀取的全部內容 raf2.write(flush, 0, len); actualSize -= len; }else{ raf2.write(flush, 0, actualSize); break; } } raf2.close(); raf.close(); } // 文件的合併 private void merge(String destPath) throws IOException { // 輸出流 OutputStream os = new BufferedOutputStream( new FileOutputStream(destPath, true) ); // 輸入流 for (int i = 0; i < destPaths.size(); i++) { InputStream is = new BufferedInputStream((new FileInputStream(destPaths.get(i)))); // 拷貝 byte[] flush = new byte[1024]; int len = -1; while((len = is.read(flush)) != -1){ os.write(flush, 0, len); } os.flush(); is.close(); } os.close(); } public static void main(String[] args) throws IOException { SplitFile sf = new SplitFile("test.png", "dest", 1024*10); sf.split(); sf.merge("merge.png"); } }
利用SequenceInputStream增長文件合併功能
import java.io.*; import java.util.ArrayList; import java.util.List; import java.util.Vector; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: SplitFile.java * @time: 2019/10/21 9:35 * @desc: 對RanTest進行封裝,功能是拆分文件,面向對象思想封裝 */ public class SplitFile { // 源頭 private File src; // 目的地(文件夾) private String destDir; // 全部分割後的文件存儲路徑 private List<String> destPaths; // 每塊大小 private int blockSize; // 塊數:多少塊 private int size; public SplitFile(String srcPath, String destDir, int blockSize){ this.src = new File(srcPath); this.destDir = destDir; this.blockSize = blockSize; this.destPaths = new ArrayList<>(); // 初始化 init(); } // 初始化 private void init(){ // 總長度 long len = this.src.length(); // 塊數:多少塊 this.size = (int)Math.ceil(len*1.0/blockSize); // 路徑 for(int i=0; i<size; i++){ this.destPaths.add(this.destDir + "/" + i + "-" + this.src.getName()); } } // 分割 public void split() throws IOException { /* 1. 計算每一塊起始位置及大小 2. 分割 */ // 總長度 long len = this.src.length(); // 每塊大小 int size = (int)Math.ceil(len*1.0/blockSize); System.out.println(size); int beginPos = 0; int actualSize = (int)(this.blockSize>len?len:this.blockSize); for(int i=0; i<size; i++){ beginPos = i*blockSize; if(i == size-1){ // 最後一塊 actualSize = (int)len; }else{ actualSize = blockSize; // 剩餘量 len -= actualSize; } splitDetail(i, beginPos, actualSize); } } // 指定起始位置,讀取剩餘指定長度內容 private void splitDetail(int i, int beginPos, int actualSize) throws IOException { RandomAccessFile raf = new RandomAccessFile((this.src), "r"); RandomAccessFile raf2 = new RandomAccessFile((this.destPaths.get(i)), "rw"); raf.seek(beginPos); byte[] flush = new byte[124]; // 接受長度 int len = -1; while((len = raf.read(flush)) != -1){ if (actualSize > len){ // 實際大小大於接受長度,則獲取本次讀取的全部內容 raf2.write(flush, 0, len); actualSize -= len; }else{ raf2.write(flush, 0, actualSize); break; } } raf2.close(); raf.close(); } // 文件的合併 private void merge(String destPath) throws IOException { // 輸出流 OutputStream os = new BufferedOutputStream( new FileOutputStream(destPath, true) ); // 輸入流 for (int i = 0; i < destPaths.size(); i++) { InputStream is = new BufferedInputStream((new FileInputStream(destPaths.get(i)))); // 拷貝 byte[] flush = new byte[1024]; int len = -1; while((len = is.read(flush)) != -1){ os.write(flush, 0, len); } os.flush(); is.close(); } os.close(); } // 利用合併流來進行文件的合併 private void seq_merge(String destPath) throws IOException { // 輸出流 OutputStream os = new BufferedOutputStream( new FileOutputStream(destPath, true) ); Vector<InputStream> vi = new Vector<InputStream>(); SequenceInputStream sis = null; // 輸入流 for (int i = 0; i < destPaths.size(); i++) { InputStream is = new BufferedInputStream((new FileInputStream(destPaths.get(i)))); } sis = new SequenceInputStream(vi.elements()); // 拷貝 byte[] flush = new byte[1024]; int len = -1; while((len = sis.read(flush)) != -1){ os.write(flush, 0, len); } os.flush(); sis.close(); os.close(); } public static void main(String[] args) throws IOException { SplitFile sf = new SplitFile("test.png", "dest", 1024*10); sf.split(); sf.seq_merge("merge-seq.png"); } }
經常使用核心操做和拷貝核心操做
import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.io.LineIterator; import org.apache.commons.io.filefilter.DirectoryFileFilter; import org.apache.commons.io.filefilter.EmptyFileFilter; import org.apache.commons.io.filefilter.FileFilterUtils; import org.apache.commons.io.filefilter.SuffixFileFilter; import javax.imageio.stream.FileCacheImageInputStream; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: CIOTest1.java * @time: 2019/10/22 16:00 * @desc: */ public class CIOTest1 { public static void main(String[] args) throws IOException { // 文件大小 long len = FileUtils.sizeOf(new File("D:\\李添的數據哦!!!\\BookStudy\\else\\JAVAPro\\src\\CIOTest1.java")); System.out.println(len); // 目錄大小 len = FileUtils.sizeOf(new File("D:\\李添的數據哦!!!\\BookStudy")); System.out.println(len); // 列出子孫集 /* 第一個參數:目標路徑 第二個參數:過濾文件: NOT_EMPTY,即只要非空文件 SuffixFileFilter,即只要該後綴名的文件 第三個參數:過濾目錄: INSTANCE,即只看子孫集 */ Collection<File> files = FileUtils.listFiles( new File("D:\\李添的數據哦!!!\\BookStudy\\else\\JAVAPro"), FileFilterUtils.or(EmptyFileFilter.NOT_EMPTY, new SuffixFileFilter("java"), new SuffixFileFilter("class")), DirectoryFileFilter.INSTANCE ); for (File file : files) { System.out.println(file.getAbsolutePath()); } // 讀取文件內容 String path = "D:\\李添的數據哦!!!\\BookStudy\\else\\【參考】3. 代碼快捷鍵操做.md"; String msg = FileUtils.readFileToString(new File(path), "UTF-8"); System.out.println(msg); byte[] datas = FileUtils.readFileToByteArray(new File(path)); System.out.println(datas.length); // 逐行讀取 List<String> msgs = FileUtils.readLines(new File((path)), "UTF-8"); for (String str : msgs) { System.out.println(str); } // 逐行讀取2 LineIterator it = FileUtils.lineIterator(new File(path), "UTF-8"); while (it.hasNext()) { System.out.println(it.nextLine()); } // 寫出內容到文件 FileUtils.write(new File("happy.txt"), "學習是一件偉大的事業\n", "UTF-8"); FileUtils.writeStringToFile(new File("happy.txt"), "學習是一件辛苦的事業\n", "UTF-8", true); FileUtils.writeByteArrayToFile(new File("happy.txt"), "學習是一件快樂的事業\n".getBytes("UTF-8"), true); // 寫出列表 List<String> dd = new ArrayList<>(); dd.add("馬雲"); dd.add("馬化騰"); dd.add("禮拜"); FileUtils.writeLines(new File("happy.txt"), dd, "-", true); // 拷貝 FileUtils.copyFile(new File("test.png"), new File("p-copy.png")); // 複製文件到目錄 FileUtils.copyFileToDirectory(new File("test.png"), new File("lib")); // 複製目錄到目錄下 FileUtils.copyDirectoryToDirectory(new File("lib"), new File("lib2")); // 複製當前路徑的某個目錄到當前目錄的新目錄 FileUtils.copyDirectory(new File("lib"), new File("lib2")); // 拷貝URL內容 // 方法1:保存網上的圖片到本地文件 String url = "https://img-blog.csdnimg.cn/2019062009044675.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzIxNTc5MDQ1,size_16,color_FFFFFF,t_70"; FileUtils.copyURLToFile(new URL(url), new File("what.jpg")); // 方法2:獲取網頁的源碼 String dat = IOUtils.toString(new URL("http://www.baidu.com"), "UTF-8"); System.out.println(dat); } }
Process與Thread
核心概念
少用繼承多用實現,由於java裏面只能單繼承
線程Thread的使用方式
start方法不保證當即運行,由cpu調用
/** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: ThreadStudy01.java * @time: 2019/10/25 12:37 * @desc: 進程學習1 */ public class StartThread1 extends Thread{ public void run(){ for (int i = 0; i < 20; i++) { System.out.println("一邊聽歌一邊敲代碼。"); } } public static void main(String[] args) throws InterruptedException { // 建立子類對象 StartThread1 st = new StartThread1(); // 啓動 st.start(); // run是普通方法的調用 // st.run(); for (int i = 0; i < 20; i++) { System.out.println("coding。"); Thread.sleep(1); } } }
建立線程方式1:利用線程下載圖片案例
/** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TDownloader.java * @time: 2019/10/28 15:58 * @desc: 進程學習2:下載圖片 */ public class TDownloader extends Thread{ // 遠程路徑 private String url; // 存儲名字 private String name; public TDownloader(String url, String name) { this.url = url; this.name = name; } @Override public void run() { WebDownloader wd = new WebDownloader(); wd.download(url, name); System.out.println(name); } public static void main(String[] args){ TDownloader td1 = new TDownloader("https://img-blog.csdnimg.cn/20181107085145510.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0hhcHB5Um9ja2luZw==,size_16,color_FFFFFF,t_70", "lstm.png"); TDownloader td2 = new TDownloader("https://img-blog.csdnimg.cn/20181107095455442.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0hhcHB5Um9ja2luZw==,size_16,color_FFFFFF,t_70", "peephole_connection.png"); TDownloader td3 = new TDownloader("https://img-blog.csdnimg.cn/20181107101049389.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0hhcHB5Um9ja2luZw==,size_16,color_FFFFFF,t_70", "gru.png"); // 啓動三個線程 td1.start(); td2.start(); td3.start(); } }
利用線程方式2:(推薦使用這種方式)
/** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: ThreadStudy01.java * @time: 2019/10/25 12:37 * @desc: 進程學習3 */ public class StartRun1 implements Runnable { public void run() { for (int i = 0; i < 20; i++) { System.out.println("一邊聽歌一邊敲代碼。"); } } public static void main(String[] args) throws InterruptedException { /* // 建立實現類對象 StartRun1 sr = new StartRun1(); // 建立代理類對象 Thread t = new Thread(sr); // 啓動 t.start(); // run是普通方法的調用 // st.run(); */ // 利用匿名對象 new Thread(new StartRun1()).start(); for (int i = 0; i < 20; i++) { System.out.println("coding。"); Thread.sleep(1); } } }
/** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TDownloader.java * @time: 2019/10/28 15:58 * @desc: 進程學習2:下載圖片 */ public class IDownloader implements Runnable { // 遠程路徑 private String url; // 存儲名字 private String name; public IDownloader(String url, String name) { this.url = url; this.name = name; } @Override public void run() { WebDownloader wd = new WebDownloader(); wd.download(url, name); System.out.println(name); } public static void main(String[] args) { IDownloader td1 = new IDownloader("https://img-blog.csdnimg.cn/20181107085145510.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0hhcHB5Um9ja2luZw==,size_16,color_FFFFFF,t_70", "lstm.png"); IDownloader td2 = new IDownloader("https://img-blog.csdnimg.cn/20181107095455442.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0hhcHB5Um9ja2luZw==,size_16,color_FFFFFF,t_70", "peephole_connection.png"); IDownloader td3 = new IDownloader("https://img-blog.csdnimg.cn/20181107101049389.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0hhcHB5Um9ja2luZw==,size_16,color_FFFFFF,t_70", "gru.png"); // 啓動三個線程 new Thread(td1).start(); new Thread(td2).start(); new Thread(td3).start(); } }
共享資源:模擬買票
/** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: Web12306.java * @time: 2019/10/30 12:36 * @desc: 共享資源:模擬買票 */ public class Web12306 implements Runnable { // 票數 private int ticketNums = 99; @Override public void run() { while(true){ if(ticketNums<0){ break; } try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "-->" + ticketNums--); } } public static void main(String[] args){ // 一份資源 Web12306 web = new Web12306(); // 多個代理 new Thread(web, "張三").start(); new Thread(web, "李四").start(); new Thread(web, "王五").start(); } }
共享資源:模擬龜兔賽跑
/** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: Racer.java * @time: 2019/10/30 14:55 * @desc: 共享資源:模擬龜兔賽跑 */ public class Racer implements Runnable { private String winner; // 勝利者 @Override public void run() { for (int steps = 1; steps <= 100; steps++) { // 模擬休息 if(Thread.currentThread().getName().equals("rabit") && steps % 10 == 0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + "-->" + steps); // 比賽是否結束 boolean flag = gameOver(steps); if (flag) { break; } } } private boolean gameOver(int steps) { if (winner != null) { // 存在勝利者 return true; } else { if (steps == 100) { winner = Thread.currentThread().getName(); System.out.println("winner==>" + winner); return true; } } return false; } public static void main(String[] args) { Racer racer = new Racer(); new Thread(racer, "tortoise").start(); new Thread(racer, "rabbit").start(); } }
Callable:能拋出異常,有返回值(瞭解)
import com.sun.org.apache.xpath.internal.operations.Bool; import jdk.nashorn.internal.codegen.CompilerConstants; import java.util.concurrent.*; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TDownloader.java * @time: 2019/10/28 15:58 * @desc: Callable瞭解學習 */ public class CDownloader implements Callable<Boolean> { // 遠程路徑 private String url; // 存儲名字 private String name; public CDownloader(String url, String name) { this.url = url; this.name = name; } @Override public Boolean call() throws Exception { WebDownloader wd = new WebDownloader(); wd.download(url, name); System.out.println(name); return true; } public static void main(String[] args) throws ExecutionException, InterruptedException { CDownloader cd1 = new CDownloader("https://img-blog.csdnimg.cn/20181107085145510.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0hhcHB5Um9ja2luZw==,size_16,color_FFFFFF,t_70", "lstm.png"); CDownloader cd2 = new CDownloader("https://img-blog.csdnimg.cn/20181107095455442.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0hhcHB5Um9ja2luZw==,size_16,color_FFFFFF,t_70", "peephole_connection.png"); CDownloader cd3 = new CDownloader("https://img-blog.csdnimg.cn/20181107101049389.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0hhcHB5Um9ja2luZw==,size_16,color_FFFFFF,t_70", "gru.png"); // 建立執行服務 ExecutorService ser = Executors.newFixedThreadPool(3); // 提交執行 Future<Boolean> result1 = ser.submit(cd1); Future<Boolean> result2 = ser.submit(cd2); Future<Boolean> result3 = ser.submit(cd3); // 獲取結果 boolean r1 = result1.get(); boolean r2 = result1.get(); boolean r3 = result1.get(); // 關閉服務 ser.shutdownNow(); } }
建立線程有幾種方式:經常使用的有兩種,繼承Thread類,重寫Runnable接口。還有一種方式,JUC併發包下,實現Callable接口。
靜態代理設計模式
/** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: StaticProxy.java * @time: 2019/10/30 15:29 * @desc: 靜態代理設計模式學習 */ public class StaticProxy { public static void main(String[] args) { new WeddingCompany(new You()).happyMarry(); } } interface Marry { void happyMarry(); } // 真實角色 class You implements Marry { @Override public void happyMarry() { System.out.println("你和你的廣寒仙子本月了..."); } } //代理角色,婚慶公司 class WeddingCompany implements Marry { // 真實角色 private Marry target; public WeddingCompany(Marry target) { this.target = target; } @Override public void happyMarry() { ready(); this.target.happyMarry(); after(); } private void ready() { System.out.println("佈置豬窩..."); } private void after() { System.out.println("鬧玉兔..."); } }
Lambda表達式 簡化線程(用一次)的使用
/** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: LambdaThread.java * @time: 2019/10/30 16:00 * @desc: Lambda表達式 簡化線程(用一次)的使用 */ public class LambdaThread { // 類中類:靜態內部類 static class Test implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("一邊聽歌"); } } } public static void main(String[] args) { new Thread(new Test()).start(); // 方法中類:局部內部類 class Test2 implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("一邊聽歌"); } } } new Thread(new Test2()).start(); // 參數中類:匿名內部類 new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println("一邊聽歌"); } } }).start(); // jdk8簡化匿名內部類,lambda new Thread( () -> { for (int i = 0; i < 20; i++) { System.out.println("一邊聽歌"); } } ).start(); } }
lambda推導:必須存在類型
/** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: LambdaTest1.java * @time: 2019/10/31 15:18 * @desc: lambda推導 */ public class LambdaTest1 { static class Like2 implements ILike { public void lambda() { System.out.println("2. 我喜歡你大爺!"); } } public static void main(String[] args) { class Like3 implements ILike { public void lambda() { System.out.println("3. 我喜歡你大爺!"); } } // 外部類 ILike like = new Like(); like.lambda(); // 靜態內部類 like = new Like2(); like.lambda(); // 方法內部類 like = new Like3(); like.lambda(); // 匿名類 like = new ILike() { @Override public void lambda() { System.out.println("4. 我喜歡你大爺!"); } }; like.lambda(); // lambda like = () -> { System.out.println("5. 我喜歡你大爺!"); }; like.lambda(); } } interface ILike { void lambda(); } class Like implements ILike { @Override public void lambda() { System.out.println("1. 我喜歡你大爺!"); } }
lambda推導 + 參數
/** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: LambdaTest1.java * @time: 2019/10/31 15:18 * @desc: lambda推導 + 參數 */ public class LambdaTest2 { public static void main(String[] args) { ILove love = (int a) -> { System.out.println("偶買噶!-->" + a); }; love.lambda(100); // 參數類型能夠省略 ILove love2 = s -> { System.out.println("偶買噶!-->" + s); }; love2.lambda(10); // 花括號也能夠省略 ILove love3 = s -> System.out.println("偶買噶!-->" + s); love3.lambda(1); } } interface ILove { void lambda(int a); } class Love implements ILove { @Override public void lambda(int a) { System.out.println("偶買噶!-->" + a); } } /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: LambdaTest1.java * @time: 2019/10/31 15:18 * @desc: lambda推導 + 參數 */ public class LambdaTest2 { public static void main(String[] args) { ILove love = (int a) -> { System.out.println("偶買噶!-->" + a); }; love.lambda(100); // 參數類型能夠省略 ILove love2 = s -> { System.out.println("偶買噶!-->" + s); }; love2.lambda(10); // 花括號也能夠省略 ILove love3 = s -> System.out.println("偶買噶!-->" + s); love3.lambda(1); } } interface ILove { void lambda(int a); } class Love implements ILove { @Override public void lambda(int a) { System.out.println("偶買噶!-->" + a); } }
lambda推導 + 參數 + 返回值
/** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: LambdaTest1.java * @time: 2019/10/31 15:18 * @desc: lambda推導 + 參數 + 返回值 */ public class LambdaTest3 { public static void main(String[] args) { IInterest in = (int q, int p) -> { System.out.println(q + p); return q + p; }; in.lambda(100, 50); // 簡化版本 IInterest in2 = (q, p) -> q + p / 2; System.out.println(in2.lambda(10, 20)); } } interface IInterest { int lambda(int a, int b); } // 參考,下面內容能夠不要 class Interest implements IInterest { @Override public int lambda(int aa, int bb) { System.out.println(aa + bb); return aa + bb; } }
lambda推導實現線程
/** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: LambdaTest1.java * @time: 2019/10/31 15:18 * @desc: lambda推導實現線程 */ public class LambdaTest4 { public static void main(String[] args) { new Thread(() -> { System.out.println("一邊學習lambda"); }).start(); // 簡化:花括號能夠不要 new Thread(() -> System.out.println("一邊淚流滿面")).start(); // 若是是多個語句,就不能省略 new Thread(() -> { for (int i = 0; i < 20; i++) { System.out.println("我瘋了,你呢?"); } }).start(); } }
一個線程對象在它的生命週期內,須要經歷5個狀態。
新生狀態(New)
用new關鍵字創建一個線程對象後,該線程對象就處於新生狀態。處於新生狀態的線程有本身的內存空間,經過調用start方法進入就緒狀態。
就緒狀態(Runnable)
處於就緒狀態的線程已經具有了運行條件,可是尚未被分配到CPU,處於「線程就緒隊列」,等待系統爲其分配CPU。就緒狀態並非執行狀態,當系統選定一個等待執行的Thread對象後,它就會進入執行狀態。一旦得到CPU,線程就進入運行狀態並自動調用本身的run方法。有4中緣由會致使線程進入就緒狀態:
新建線程:調用start()方法,進入就緒狀態;
阻塞線程:阻塞解除,進入就緒狀態;
運行線程:調用yield()方法,直接進入就緒狀態;
運行線程:JVM將CPU資源從本線程切換到其餘線程。
運行狀態(Running)
在運行狀態的線程執行本身run方法中的代碼,直到調用其餘方法而終止或等待某資源而阻塞或完成任務而死亡。若是在給定的時間片內沒有執行結束,就會被系統給換下來回到就緒狀態。也可能因爲某些「致使阻塞的事件」而進入阻塞狀態。
阻塞狀態(Blocked)
阻塞指的是暫停一個線程的執行以等待某個條件發生(如某資源就緒)。有4種緣由會致使阻塞:
執行sleep(int millsecond)方法,使當前線程休眠,進入阻塞狀態。當指定的時間到了後,線程進入就緒狀態。
執行wait()方法,使當前線程進入阻塞狀態。當使用nofity()方法喚醒這個線程後,它進入就緒狀態。
線程運行時,某個操做進入阻塞狀態,好比執行IO流操做(read()/write()方法自己就是阻塞的方法)。只有當引發該操做阻塞的緣由消失後,線程進入就緒狀態。
join()線程聯合: 當某個線程等待另外一個線程執行結束後,才能繼續執行時,使用join()方法。
死亡狀態(Terminated)
死亡狀態是線程生命週期中的最後一個階段。線程死亡的緣由有兩個。一個是正常運行的線程完成了它run()方法內的所有工做; 另外一個是線程被強制終止,如經過執行stop()或destroy()方法來終止一個線程(注:stop()/destroy()方法已經被JDK廢棄,不推薦使用)。
當一個線程進入死亡狀態之後,就不能再回到其它狀態了。
線程的終止
不要使用stop和destroy
/** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TerminateThread.java * @time: 2019/11/1 14:32 * @desc: 終止線程 */ public class TerminateThread implements Runnable { // 1. 設置標識,標記線程體是否能夠運行 private boolean flag = true; private String name; public TerminateThread(String name) { this.name = name; } @Override public void run() { int i = 0; // 2. 關聯標識,true-->運行,False-->中止 while (flag) { System.out.println(name + "-->" + i++); } } // 3. 對外提供方法改變標識 public void terminate() { this.flag = false; } public static void main(String[] args) { TerminateThread tt = new TerminateThread("你大爺"); new Thread(tt).start(); for (int i = 0; i < 99; i++) { if (i == 88){ tt.terminate(); // 線程終止 System.out.println("tt game over!"); } System.out.println("main-->" + i); } } }
線程的暫停-sleep: 可讓正在運行的線程進入阻塞狀態,直到休眠時間滿了,進入就緒狀態。
import java.text.SimpleDateFormat; import java.util.Date; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: BlockedSleep1.java * @time: 2019/11/1 14:46 * @desc: sleep模擬倒計時 */ public class BlockedSleep1 { public static void main(String[] args) throws InterruptedException { // 倒計時 Date endTime = new Date(System.currentTimeMillis() + 1000 * 10); long end = endTime.getTime(); while (true) { System.out.println(new SimpleDateFormat("mm:ss").format(endTime)); Thread.sleep(1000); endTime = new Date(endTime.getTime()-1000); if(end-10000 > endTime.getTime()){ break; } } } public static void test() throws InterruptedException { // 倒數10個數,1秒一個 int num = 10; while (true) { Thread.sleep(1000); System.out.println(num--); } } }
線程的暫停-yield: 可讓正在運行的線程直接進入就緒狀態,讓出CPU的使用權。
import org.omg.PortableServer.THREAD_POLICY_ID; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: YieldDemo1.java * @time: 2019/11/1 14:55 * @desc: yield禮讓線程,暫停線程,直接進入就緒狀態不是阻塞狀態 */ public class YieldDemo1 { public static void main(String[] args) { MyYield my = new MyYield(); new Thread(my, "a").start(); new Thread(my, "b").start(); // lambda實現 new Thread(() -> { for (int i = 0; i < 100; i++) { System.out.println("lambda..." + i); } }).start(); for (int i = 0; i < 100; i++) { if (i % 20 == 0) { Thread.yield(); // main禮讓 } System.out.println("main..." + i); } } } class MyYield implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + "-->start"); Thread.yield(); // 禮讓 System.out.println(Thread.currentThread().getName() + "-->end"); } }
線程的聯合-join:合併線程,插隊線程。
import sun.java2d.loops.TransformHelper; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: BlockedJoin1.java * @time: 2019/11/1 15:05 * @desc: 爸爸和兒子買菸的故事 */ public class BlockedJoin1 { public static void main(String[] args){ new Father().start(); } } class Father extends Thread{ @Override public void run() { System.out.println("想抽菸,發現沒了"); System.out.println("讓兒子去買中華"); Thread t = new Son(); t.start(); try { t.join(); // father被阻塞 System.out.println("老爸接過煙,把零錢給了兒子"); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("孩子走丟了,老爸出去找孩子去了..."); } } } class Son extends Thread{ @Override public void run() { System.out.println("接過老爸的錢出去了..."); System.out.println("路邊有個遊戲廳,玩了10秒"); for (int i = 0; i < 10; i++) { System.out.println(i+"秒過去了..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("趕忙買菸去..."); System.out.println("手拿一包中華回家了..."); } }
觀察線程的各個狀態
/** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: AllState.java * @time: 2019/11/1 15:22 * @desc: 觀察線程的各個狀態 */ public class AllState { public static void main(String[] args) { Thread t = new Thread(() -> { for (int i = 0; i < 5; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("..."); }); // 觀察狀態 Thread.State state = t.getState(); System.out.println(state); // NEW t.start(); state = t.getState(); System.out.println(state); // RUNNABLE while (state != Thread.State.TERMINATED) { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } state = t.getState(); // TIMED_WAITING System.out.println(state); } state = t.getState(); // TERMINATED System.out.println(state); } }
NORM_PRIORITY 5
MIN_PRIORITY 1
MAX_PRIORITY 10
/** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: PriorityTest1.java * @time: 2019/11/4 12:38 * @desc: 多線程優先級 */ public class PriorityTest1 { public static void main(String[] args) { MyPriority mp = new MyPriority(); Thread t1 = new Thread(mp); Thread t2 = new Thread(mp); Thread t3 = new Thread(mp); Thread t4 = new Thread(mp); Thread t5 = new Thread(mp); Thread t6 = new Thread(mp); t1.setPriority(Thread.MAX_PRIORITY); t2.setPriority(Thread.MAX_PRIORITY); t3.setPriority(Thread.MAX_PRIORITY); t4.setPriority(Thread.MIN_PRIORITY); t5.setPriority(Thread.MIN_PRIORITY); t6.setPriority(Thread.MIN_PRIORITY); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); t6.start(); } } class MyPriority implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority()); Thread.yield(); } }
是爲用戶線程服務的;JVM中止不用等待守護線程執行完畢
默認:用戶線程,JVM等待用戶線程執行完畢纔會中止
import org.omg.PortableServer.THREAD_POLICY_ID; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: DaemonTest.java * @time: 2019/11/4 13:35 * @desc: 守護線程學習 */ public class DaemonTest { public static void main(String[] args) { Thread t1 = new Thread(new You1()); t1.run(); Thread t2 = new Thread(new God1()); // 將用戶線程調整爲守護線程 t2.setDaemon(true); t2.start(); } } class You1 extends Thread { @Override public void run() { for (int i = 0; i < 365 * 100; i++) { System.out.println("happy life!"); } System.out.println("ooo..."); } } class God1 extends Thread { @Override public void run() { for (;true;) { System.out.println("bless you!"); } } }
經常使用方法
案例
/** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: InfoTest.java * @time: 2019/11/4 13:46 * @desc: 獲取線程基本信息的方法 */ public class InfoTest { public static void main(String[] args) throws InterruptedException { // 線程是否活着 System.out.println(Thread.currentThread().isAlive()); // 設置名稱:真是角色+代理角色 MyInfo info = new MyInfo("戰鬥機"); Thread t = new Thread(info); t.setName("公雞"); t.start(); Thread.sleep(1000); System.out.println(t.isAlive()); } } class MyInfo implements Runnable{ private String name; public MyInfo(String name) { this.name = name; } @Override public void run() { System.out.println(Thread.currentThread().getName() + "-->" + name); } }
線程不安全案例1
package com.sxt.thread; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: UnsafeTest.java * @time: 2019/11/4 13:57 * @desc: 線程同步 */ public class UnsafeTest { public static void main(String[] args) { // 帳戶 Account account = new Account(100, "結婚禮金"); Drawing you = new Drawing(account, 80, "可悲的你"); Drawing wife = new Drawing(account, 90, "happy的她"); you.start(); wife.start(); } } // 帳戶 class Account { int money; String name; public Account(int money, String name) { this.money = money; this.name = name; } } // 模擬取款 class Drawing extends Thread { // 取錢的帳戶 Account accout; // 取多少錢 int drawingMoney; // 口袋裏的總數 int packetTotal; public Drawing(Account accout, int drawingMoney, String name) { super(name); this.accout = accout; this.drawingMoney = drawingMoney; } @Override public void run() { if(accout.money - drawingMoney < 0){ return; } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } accout.money -= drawingMoney; packetTotal += drawingMoney; System.out.println(this.getName() + "-->帳戶餘額爲:" + accout.money); System.out.println(this.getName() + "-->口袋裏的錢爲:" + packetTotal); } }
線程不安全案例2
package com.sxt.thread; import java.util.ArrayList; import java.util.List; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: UnsafeTest.java * @time: 2019/11/4 13:57 * @desc: 線程同步 */ public class UnsafeTest2 { public static void main(String[] args) { List<String> list = new ArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ list.add(Thread.currentThread().getName()); }).start(); } System.out.println(list.size()); } }
鎖機制
爲了保證數據在方法中被訪問時的正確性,在訪問時加入鎖機制(synchronized),當一個線程得到對象的排它鎖,獨佔資源,其餘線程必須等待,使用後釋放鎖便可。存在如下問題:
線程安全:在併發時保證數據的正確性、效率儘量高(synchronized)
樣例1:
package com.sxt.thread; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: UnsafeTest.java * @time: 2019/11/4 13:57 * @desc: 線程同步 */ public class SafeTest { public static void main(String[] args) { // 帳戶 Account account = new Account(100, "結婚禮金"); SafeDrawing you = new SafeDrawing(account, 80, "可悲的你"); SafeDrawing wife = new SafeDrawing(account, 90, "happy的她"); you.start(); wife.start(); } } // 模擬取款 class SafeDrawing extends Thread { // 取錢的帳戶 Account accout; // 取多少錢 int drawingMoney; // 口袋裏的總數 int packetTotal; public SafeDrawing(Account accout, int drawingMoney, String name) { super(name); this.accout = accout; this.drawingMoney = drawingMoney; } @Override public void run() { test(); } public void test() { if (accout.money <= 0) { return; } synchronized (accout) { if (accout.money - drawingMoney < 0) { return; } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } accout.money -= drawingMoney; packetTotal += drawingMoney; System.out.println(this.getName() + "-->帳戶餘額爲:" + accout.money); System.out.println(this.getName() + "-->口袋裏的錢爲:" + packetTotal); } } }
樣例2
package com.sxt.thread; import java.util.ArrayList; import java.util.List; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: UnsafeTest.java * @time: 2019/11/4 13:57 * @desc: 線程同步 */ public class SafeTest2 { public static void main(String[] args) { List<String> list = new ArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(() -> { // 同步塊 synchronized (list) { list.add(Thread.currentThread().getName()); } }).start(); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); } }
雙重檢測:考慮臨界值的問題
package com.sxt.thread; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: Web12306.java * @time: 2019/10/30 12:36 * @desc: 線程安全買票 */ public class Safe12306 implements Runnable { // 票數 private int ticketNums = 10; private boolean flag = true; @Override public void run() { while (flag) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } test(); } } private void test() { if (ticketNums <= 0) { // 考慮的是沒有票的狀況 flag = false; return; } synchronized (this) { if (ticketNums <= 0) { // 考慮的是最後一張票的狀況 flag = false; return; } try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "-->" + ticketNums--); } } public static void main(String[] args) { // 一份資源 Safe12306 web = new Safe12306(); // 多個代理 new Thread(web, "張三").start(); new Thread(web, "李四").start(); new Thread(web, "王五").start(); } }
案例1:快樂影院
package com.sxt.thread; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: HappyCinema.java * @time: 2019/11/5 12:57 * @desc: 快樂電影院搶座位案例 */ public class HappyCinema { public static void main(String[] args) { Cinema c = new Cinema(2, "happy sxt"); new Thread(new Customer(c, 2), "老高").start(); new Thread(new Customer(c, 1), "老李").start(); } } class Customer implements Runnable { Cinema cinema; int seats; public Customer(Cinema cinema, int seats) { this.cinema = cinema; this.seats = seats; } @Override public void run() { synchronized (cinema) { boolean flag = cinema.bookTickets(seats); if (flag) { System.out.println("出票成功" + Thread.currentThread().getName() + "-<位置爲:" + seats); } else { System.out.println("出票失敗" + Thread.currentThread().getName() + "-<位置不夠!"); } } } } class Cinema { // 可用的位置 int available; // 名稱 String name; public Cinema(int available, String name) { this.available = available; this.name = name; } // 購票 public boolean bookTickets(int seats) { System.out.println("可用位置爲:" + available); if (seats > available) { return false; } available -= seats; return true; } }
案例2:快樂影院真實List座位
package com.sxt.thread; import java.util.ArrayList; import java.util.List; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: HappyCinema.java * @time: 2019/11/5 12:57 * @desc: 快樂電影院搶座位案例 */ public class HappyCinema2 { public static void main(String[] args) { // 可用位置 List<Integer> available = new ArrayList<>(); for (int i = 1; i < 8; i++) { available.add(i); } // 顧客須要的位置 List<Integer> seats1 = new ArrayList<>(); seats1.add(1); seats1.add(2); List<Integer> seats2 = new ArrayList<>(); seats2.add(4); seats2.add(5); seats2.add(6); SxtCinema c = new SxtCinema(available, "happy sxt"); new Thread(new HappyCustomer(c, seats1), "老高").start(); new Thread(new HappyCustomer(c, seats2), "老李").start(); } } class HappyCustomer implements Runnable { SxtCinema cinema; List<Integer> seats; public HappyCustomer(SxtCinema cinema, List<Integer> seats) { this.cinema = cinema; this.seats = seats; } @Override public void run() { synchronized (cinema) { boolean flag = cinema.bookTickets(seats); if (flag) { System.out.println("出票成功" + Thread.currentThread().getName() + "-<位置爲:" + seats); } else { System.out.println("出票失敗" + Thread.currentThread().getName() + "-<位置不夠!"); } } } } class SxtCinema { // 可用的位置 List<Integer> available; // 名稱 String name; public SxtCinema(List<Integer> available, String name) { this.available = available; this.name = name; } // 購票 public boolean bookTickets(List<Integer> seats) { System.out.println("可用位置爲:" + available); List<Integer> copy = new ArrayList<>(); copy.addAll(available); // 相減 copy.removeAll(seats); // 判斷大小 if (available.size() - copy.size() != seats.size()) { return false; } // 成功 available = copy; return true; } }
案例3:快樂火車票
package com.sxt.thread; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: Happy12306.java * @time: 2019/11/7 19:24 * @desc: 快樂火車票 */ public class Happy12306 { public static void main(String[] args) { Web12306 c = new Web12306(2, "happy sxt"); new Passenger(c, "老高", 2).start(); new Passenger(c, "老李", 1).start(); } } // 乘客 class Passenger extends Thread { int seats; public Passenger(Runnable target, String name, int seats) { super(target, name); this.seats = seats; } } // 火車票網 class Web12306 implements Runnable { // 可用的位置 int available; // 名稱 String name; public Web12306(int available, String name) { this.available = available; this.name = name; } @Override public void run() { Passenger p = (Passenger) Thread.currentThread(); boolean flag = this.bookTickets(p.seats); if (flag) { System.out.println("出票成功" + Thread.currentThread().getName() + "-<位置爲:" + p.seats); } else { System.out.println("出票失敗" + Thread.currentThread().getName() + "-<位置不夠!"); } } // 購票 public synchronized boolean bookTickets(int seats) { System.out.println("可用位置爲:" + available); if (seats > available) { return false; } available -= seats; return true; } }
併發容器:import java.util.concurrent.CopyOnWriteArrayList
package com.sxt.thread; import java.util.concurrent.CopyOnWriteArrayList; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: SynContainer.java * @time: 2019/11/8 14:09 * @desc: 線程同步:併發容器 */ public class SynContainer { public static void main(String[] args) { CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(() -> { // 同步塊 list.add(Thread.currentThread().getName()); }).start(); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); } }
死鎖指的是:多個線程各自佔有一些共享資源,而且互相等待其餘線程佔有的資源才能進行,而致使兩個或者多個線程都在等待對方釋放資源,都中止執行的情形。
避免方式:不要在同一個代碼塊中持有多個對象鎖。
死鎖案例:
package com.sxt.thread; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: DeadLock.java * @time: 2019/11/8 14:16 * @desc: 死鎖 */ public class DeadLock { public static void main(String[] args) { Makeup g1 = new Makeup(1, "豐光"); Makeup g2 = new Makeup(2, "師兄"); g1.start(); g2.start(); } } // 口紅 class Lipstick { } // 鏡子 class Mirror { } // 化妝 class Makeup extends Thread { static Lipstick lip = new Lipstick(); static Mirror mir = new Mirror(); // 選擇 int choice; // 名字 String girlname; public Makeup(int choice, String girlname) { this.choice = choice; this.girlname = girlname; } @Override public void run() { // 化妝 makeup(); } private void makeup() { // 相互持有對方的對象鎖,這樣纔有可能形成死鎖 if (choice == 1) { // 得到口紅的鎖 synchronized (lip) { System.out.println(this.girlname + "-->塗口紅"); // 1秒後想擁有鏡子的鎖 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (mir) { System.out.println(this.girlname + "-->照鏡子"); } } } else { synchronized (mir) { System.out.println(this.girlname + "-->照鏡子"); // 2秒後想擁有口紅的鎖 try { Thread.sleep(1100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lip) { System.out.println(this.girlname + "-->塗口紅"); } } } } }
死鎖的解決案例:
package com.sxt.thread; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: DeadLock.java * @time: 2019/11/8 14:16 * @desc: 解決死鎖 */ public class DeadLock2 { public static void main(String[] args) { Makeup2 g1 = new Makeup2(1, "豐光"); Makeup2 g2 = new Makeup2(2, "師兄"); g1.start(); g2.start(); } } // 化妝 class Makeup2 extends Thread { static Lipstick lip = new Lipstick(); static Mirror mir = new Mirror(); // 選擇 int choice; // 名字 String girlname; public Makeup2(int choice, String girlname) { this.choice = choice; this.girlname = girlname; } @Override public void run() { // 化妝 makeup(); } private void makeup() { // 相互持有對方的對象鎖,這樣纔有可能形成死鎖 if (choice == 1) { // 得到口紅的鎖 synchronized (lip) { System.out.println(this.girlname + "-->塗口紅"); // 1秒後想擁有鏡子的鎖 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } synchronized (mir) { System.out.println(this.girlname + "-->照鏡子"); } } else { synchronized (mir) { System.out.println(this.girlname + "-->照鏡子"); // 2秒後想擁有口紅的鎖 try { Thread.sleep(1100); } catch (InterruptedException e) { e.printStackTrace(); } } synchronized (lip) { System.out.println(this.girlname + "-->塗口紅"); } } } }
生產者消費者模式
view簡介
在生產者消費者問題中,僅有synchronized是不夠的
實現生產者消費者的方法:
實現方式:用wait()等待,notify()喚醒
管程法:藉助緩衝區
package com.sxt.cooperation; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: CoTest1.java * @time: 2019/11/8 15:36 * @desc: 協做模型:生產者消費者實現方式1:管程法 */ public class CoTest1 { public static void main(String[] args) { SynContainer container = new SynContainer(); new Productor(container).start(); new Consumer(container).start(); } } // 生產者 class Productor extends Thread { SynContainer container; public Productor(SynContainer container) { this.container = container; } @Override public void run() { // 開始生產 for (int i = 0; i < 100; i++) { System.out.println("生產-->第" + i + "個饅頭"); container.push(new SteamedBun(i)); } } } // 消費者 class Consumer extends Thread { SynContainer container; public Consumer(SynContainer container) { this.container = container; } @Override public void run() { // 開始消費 for (int i = 0; i < 1000; i++) { System.out.println("消費-->第" + container.pop().id + "個饅頭"); } } } // 緩衝區 class SynContainer { SteamedBun[] buns = new SteamedBun[10]; int count = 0; // 存儲:生產 public synchronized void push(SteamedBun bun) { // 什麼時候能生產:容器存在空間 if (count == buns.length) { try { // 線程阻塞,消費者通知生產解除 this.wait(); } catch (InterruptedException e) { } } // 存在空間,能夠生產 buns[count++] = bun; // 存在數據了,能夠通知消費了 this.notifyAll(); } // 獲取:消費 public synchronized SteamedBun pop() { // 什麼時候消費:容器中是否存在數據,存在數據則能夠消費,沒有數據就只能等待 if (count == 0) { try { // 線程阻塞:生產者通知消費則接觸阻塞 this.wait(); } catch (InterruptedException e) { } } SteamedBun bun = buns[--count]; // 存在空間,能夠喚醒對方生產 this.notifyAll(); return bun; } } // 數據。饅頭 class SteamedBun { int id; public SteamedBun(int id) { this.id = id; } }
信號燈法:藉助標誌位
package com.sxt.cooperation; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: CoTest2.java * @time: 2019/11/8 16:38 * @desc: 信號燈法 */ public class CoTest2 { public static void main(String[] args) { Tv tv = new Tv(); new Player(tv).start(); new Watcher(tv).start(); } } // 生產者:演員 class Player extends Thread { Tv tv; public Player(Tv tv) { this.tv = tv; } @Override public void run() { for (int i = 0; i < 20; i++) { if (i % 2 == 0) { this.tv.play("奇葩說"); } else { this.tv.play("倚天屠龍記"); } } } } // 消費者:觀衆 class Watcher extends Thread { Tv tv; public Watcher(Tv tv) { this.tv = tv; } @Override public void run() { for (int i = 0; i < 20; i++) { tv.watch(); } } } // 同一個資源:電視 class Tv { String voice; // T:演員表演,觀衆等待;F:觀衆觀看,演員等待 boolean flag = true; // 表演 public synchronized void play(String voice) { // 演員等待 if (!flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("表演了" + voice); this.voice = voice; // 喚醒 this.notifyAll(); this.flag = !this.flag; } // 觀看 public synchronized void watch() { // 觀衆等待 if (flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("聽到了" + voice); // 喚醒 this.notifyAll(); this.flag = !this.flag; } }
定時調度(簡單):Timer和TimerTask類
package com.sxt.cooperation; import com.sun.deploy.cache.CacheEntry; import com.sun.deploy.security.MozillaMyKeyStore; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Timer; import java.util.TimerTask; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TimerTest1.java * @time: 2019/11/9 18:27 * @desc: 定時調度 */ public class TimerTest1 { public static void main(String[] args) { Timer timer = new Timer(); // 執行安排 // 執行一次 timer.schedule(new MyTask(), 1000); // 執行屢次 timer.schedule(new MyTask(), 1000, 200); // 指定時間執行 Calendar cal = new GregorianCalendar(2099, 11, 3, 11, 22, 22); timer.schedule(new MyTask(), cal.getTime(), 200); } } class MyTask extends TimerTask { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("放空大腦休息一下子~"); } } }
定時調度(複雜):QUARTZ
package com.sxt.others; import static org.quartz.DateBuilder.evenSecondDate; import static org.quartz.JobBuilder.newJob; import static org.quartz.TriggerBuilder.newTrigger; import static org.quartz.SimpleScheduleBuilder.simpleSchedule; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerFactory; import org.quartz.Trigger; import org.quartz.impl.StdSchedulerFactory; import java.util.Date; /** * quartz學習入門 */ public class QuartzTest { public void run() throws Exception { // 1. 建立Scheduler的工廠 SchedulerFactory sf = new StdSchedulerFactory(); // 2. 從工廠中獲取調度器 Scheduler sched = sf.getScheduler(); // 時間 Date runTime = evenSecondDate(new Date()); // 3. 建立JobDetail JobDetail job = newJob(HelloJob.class).withIdentity("job1", "group1").build(); // 4. 觸發器 // Trigger trigger = newTrigger().withIdentity("trigger1", "group1").startAt(runTime).build(); // 4 | 2:若是想要循環屢次呢,每5秒一次,循環三次 Trigger trigger = newTrigger().withIdentity("trigger1", "group1").startAt(runTime) .withSchedule(simpleSchedule().withIntervalInSeconds(5).withRepeatCount(2)).build(); // 5. 註冊任務和觸發條件 sched.scheduleJob(job, trigger); // 6. 啓動 sched.start(); try { // 5秒後中止 Thread.sleep(30L * 1000L); // executing... } catch (Exception e) { } // shut down the scheduler sched.shutdown(true); } public static void main(String[] args) throws Exception { QuartzTest example = new QuartzTest(); example.run(); } }
指令重排HappenBefore
volatile:
保證線程間變量的可見性,即保證數據的同步。
volatile是不錯的機制,可是volatile不能保證原子性。
package com.sxt.others; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: VolatileTest.java * @time: 2019/11/11 9:29 * @desc: volatile測試 * 不加volatile則程序不會停,加了以後會停 */ public class VolatileTest { private volatile static int num = 0; public static void main(String[] args) throws InterruptedException { new Thread(() -> { while(num == 0){ // 此處不要編寫代碼,這是爲了讓系統沒有時間更新數據 } }).start(); Thread.sleep(1000); num = 1; } }
dcl單例模式
構造器私有化 --> 避免外部new構造器
提供私有的靜態屬性 --> 存儲對象的地址
提供公共的靜態方法 --> 獲取屬性
package com.sxt.others; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: DoubleCheckedLocking.java * @time: 2019/11/11 9:34 * @desc: 單例模式 */ public class DoubleCheckedLocking { // 2. 提供私有的靜態屬性 // 沒有volatile其餘線程可能訪問一個沒有初始化的對象 private static volatile DoubleCheckedLocking instance; // 1. 構造器私有化 private DoubleCheckedLocking() { } // 3. 提供公共的靜態方法 --> 獲取屬性 public static DoubleCheckedLocking getInstance() { // 再次檢測,避免沒必要要的同步,已經存在對象 if (null != instance) { return instance; } synchronized (DoubleCheckedLocking.class) { if (null == instance) { instance = new DoubleCheckedLocking(); // new一個對象的時候,要作的三件事情 // 開闢空間;初始化對象信息;返回對象的地址給引用 // 因此這裏可能出現指令重排 } return instance; } } public static void main(String[] args) { Thread t = new Thread(() -> { System.out.println(DoubleCheckedLocking.getInstance()); }); t.start(); System.out.println(DoubleCheckedLocking.getInstance()); } }
ThreadLocal
表示的是每一個線程自身的存儲本地、局部區域
方法:get/set/initialValue
package com.sxt.others; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: ThreadLocalTest.java * @time: 2019/11/11 9:52 * @desc: ThreadLocal */ public class ThreadLocalTest { // private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>(); // 更改初始值 // private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){ // @Override // protected Integer initialValue() { // return 200; // } // }; // 簡化上面代碼 private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 200); public static void main(String[] args) { // 獲取值,初始值爲null System.out.println(Thread.currentThread().getName() + "-->" + threadLocal.get()); // 設置值 threadLocal.set(99); System.out.println(Thread.currentThread().getName() + "-->" + threadLocal.get()); new Thread(new MyRun()).start(); new Thread(new MyRun()).start(); } public static class MyRun implements Runnable { @Override public void run() { threadLocal.set((int)(Math.random()*99)); System.out.println(Thread.currentThread().getName() + "-->" + threadLocal.get()); } } }
每一個線程只使用自身的數據,更改不會影響其餘線程
package com.sxt.others; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: ThreadLocalTest2.java * @time: 2019/11/11 10:06 * @desc: 取數據 */ public class ThreadLocalTest2 { private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 1); public static void main(String[] args) { for (int i = 0; i < 5; i++) { new Thread(new MyRun()).start(); } } public static class MyRun implements Runnable { @Override public void run() { Integer left = threadLocal.get(); System.out.println(Thread.currentThread().getName() + "獲得了-->" + left); threadLocal.set(left - 1); System.out.println(Thread.currentThread().getName() + "還剩下-->" + threadLocal.get()); } } }
ThreadLocal:分析上下文環境
構造器:哪裏調用,就屬於哪裏,找線程體
run方法:本線程本身的
package com.sxt.others; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: ThreadLocalTest3.java * @time: 2019/11/11 10:11 * @desc: 分析上下文環境 */ public class ThreadLocalTest3 { private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 1); public static void main(String[] args) { new Thread(new MyRun()).start(); new Thread(new MyRun()).start(); } public static class MyRun implements Runnable { public MyRun() { // 屬於main線程 threadLocal.set(-100); System.out.println(Thread.currentThread().getName() + "-->" + threadLocal.get()); } @Override public void run() { // 屬於其餘線程 System.out.println(Thread.currentThread().getName() + "-->" + threadLocal.get()); } } }
InheritableThreadLocal:繼承上下文環境的數據,拷貝一份給子線程
package com.sxt.others; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: ThreadLocalTest4.java * @time: 2019/11/11 10:25 * @desc: InheritableThreadLocal:繼承上下文環境的數據,拷貝一份給子線程。起點 */ public class ThreadLocalTest4 { private static ThreadLocal<Integer> threadLocal = new InheritableThreadLocal<>(); public static void main(String[] args) { threadLocal.set(2); System.out.println(Thread.currentThread().getName() + "-->" + threadLocal.get()); // 線程由main線程開闢 new Thread(() -> { System.out.println(Thread.currentThread().getName() + "-->" + threadLocal.get()); // 可是既然是拷貝,因此想改仍是互不影響的 threadLocal.set(200); System.out.println(Thread.currentThread().getName() + "-->" + threadLocal.get()); }).start(); } }
可重入鎖:鎖能夠延續使用 + 計數器:ReentrantLock
CAS(Compare and Swap)比較並交換:
參考連接:CAS樂觀鎖
悲觀鎖:synchronized是獨佔鎖即悲觀鎖,會致使其餘全部須要鎖的線程掛起,等待持有鎖的線程釋放鎖。
樂觀鎖:每次不加鎖而是假設沒有衝突而去完成某項操做,若是由於衝突失敗就重試,直到成功爲止。
package com.sxt.others; import java.util.concurrent.atomic.AtomicInteger; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: CASTest.java * @time: 2019/11/11 10:51 * @desc: CAS */ public class CASTest { // 庫存 private static AtomicInteger stock = new AtomicInteger(3); public static void main(String[] args){ for (int i = 0; i < 5; i++) { new Thread(()->{ // 模擬網絡延時 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Integer left = stock.decrementAndGet(); if(left<1){ System.out.println("搶完了..."); return; } System.out.println(Thread.currentThread().getName() + "搶了一個商品" + "-->還剩" + left); }).start(); } } }
網絡:通信協議+通訊接口
網絡分層:OSI(Open System Interconnect)開放系統互連參考模型
網絡分層:OSI網絡通訊協議模型只是一個參考模型,而TCP/IP協議是事實上的標準。TCP/IP協議參考了OSI模型,可是並無嚴格按照OSI規定的七層標準去劃分,而只劃分了四層。
數據封裝與解封:
數據封裝(Data Encapsulation)是指將協議數據單元(PDU)封裝在一組協議頭和協議尾中的過程。在OSI七層參考模型中,每層主要負責與其它機器上的對等層進行通訊。該過程是在協議數據單元(PDU)中實現的,其中每層的PDU通常由本層的協議頭、協議尾和數據封裝構成。
(1) 應用層將數據交給傳輸層,傳輸層添加上TCP的控制信息(稱爲TCP頭部),這個數據單元稱爲段(Segment),加入控制信息的過程稱爲封裝。而後,將段交給網絡層。
(2) 網絡層接收到段,再添加上IP頭部,這個數據單元稱爲包(Packet)。而後,將包交給數據鏈路層。
(3) 數據鏈路層接收到包,再添加上MAC頭部和尾部,這個數據單元稱爲幀(Frame)。而後,將幀交給物理層。
(4) 物理層將接收到的數據轉化爲比特流,而後在網線中傳送。
(1) 物理層接收到比特流,通過處理後將數據交給數據鏈路層。
(2) 數據鏈路層將接收到的數據轉化爲數據幀,再除去MAC頭部和尾部,這個除去控制信息的過程稱爲解封,而後將包交給網絡層。
(3) 網絡層接收到包,再除去IP頭部,而後將段交給傳輸層。
(4) 傳輸層接收到段,再除去TCP頭部,而後將數據交給應用層。
從以上傳輸過程當中,能夠總結出如下規則:
(1) 發送方數據處理的方式是從高層到底層,逐層進行數據封裝。
(2) 接收方數據處理的方式是從底層到高層,逐層進行數據解封裝。
接收方的每一層只把對該層有意義的數據拿走,或者說每一層只能處理髮送方同等層的數據,而後把其他的部分傳遞給上一層,這就是對等層通訊的概念。
IP地址:用來標識網絡中的一個通訊實體的地址。通訊實體能夠是計算機、路由器等。 好比互聯網的每一個服務器都要有本身的IP地址,而每一個局域網的計算機要通訊也要配置IP地址。路由器是鏈接兩個或多個網絡的網絡設備。
目前主流使用的IP地址是IPV4,可是隨着網絡規模的不斷擴大,IPV4面臨着枯竭的危險,因此推出了IPV6。
IPV4:32位地址,並以8位爲一個單位,分紅四部分,以點分十進制表示,如192.168.0.1。由於8位二進制的計數範圍是00000000---11111111,對應十進制的0-255,因此-4.278.4.1是錯誤的IPV4地址。
IPV6:128位(16個字節)寫成8個16位的無符號整數,每一個整數用四個十六進制位表示,每一個數之間用冒號(:)分開,如:3ffe:3201:1401:1280:c8ff:fe4d:db39:1984
注意事項
192.168.0.0--192.168.255.255爲私有地址,屬於非註冊地址,專門爲組織機構內部使用。
InetAddress:
兩個成員方法
端口:
IP地址用來標識一臺計算機,可是一臺計算機上可能提供多種網絡應用程序,如何來區分這些不一樣的程序呢?這就要用到端口。
端口是虛擬的概念,並非說在主機上真的有若干個端口。經過端口,能夠在一個主機上運行多個網絡應用程序。 端口的表示是一個16位的二進制整數,對應十進制的0-65535。
Oracle、MySQL、Tomcat、QQ、msn、迅雷、電驢、360等網絡程序都有本身的端口。
查看命令
須要掌握的知識:
InetSocketAddress
package com.sxt.loc; import java.net.InetSocketAddress; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: PortTest.java * @time: 2019/11/12 14:24 * @desc: 端口 */ public class PortTest { public static void main(String[] args){ // 包含端口 InetSocketAddress socketAddress1 = new InetSocketAddress("127.0.0.1", 8080); InetSocketAddress socketAddress2 = new InetSocketAddress("localhost", 9000); System.out.println(socketAddress1.getHostName()); System.out.println(socketAddress1.getAddress()); System.out.println(socketAddress1.getPort()); System.out.println(socketAddress2.getHostName()); System.out.println(socketAddress2.getAddress()); System.out.println(socketAddress2.getPort()); } }
URL:
package com.sxt.loc; import java.net.MalformedURLException; import java.net.URL; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: URLTest.java * @time: 2019/11/14 9:27 * @desc: URL練習 */ public class URLTest { public static void main(String[] args) throws MalformedURLException { URL url = new URL("http://www.baidu.com:80/index.html?uname=shsxt&age=18#a"); // 獲取四個值 System.out.println("協議:" + url.getProtocol()); System.out.println("域名|ip:" + url.getHost()); System.out.println("端口:" + url.getPort()); System.out.println("請求資源1:" + url.getFile()); System.out.println("請求資源2:" + url.getPath()); // 參數 System.out.println("參數:" + url.getQuery()); // 錨點 System.out.println("錨點:" + url.getRef()); } }
爬蟲
簡單爬蟲
package com.sxt.loc; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: SpiderTest1.java * @time: 2019/11/14 10:20 * @desc: 網絡爬蟲 */ public class SpiderTest1 { public static void main(String[] args) throws IOException { // 獲取URL URL url = new URL("https://www.jd.com"); // 下載資源 InputStream is = url.openStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8")); String msg = null; while(null != (msg=br.readLine())){ System.out.println(msg); } } }
若是爬蟲被拒絕,能夠模擬瀏覽器爬蟲
package com.sxt.loc; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: SpiderTest2.java * @time: 2019/11/14 10:26 * @desc: 網絡爬蟲,對於那些403拒絕的,模擬瀏覽器 */ public class SpiderTest2 { public static void main(String[] args) throws IOException { // 獲取URL URL url = new URL("https://www.dianping.com"); // http協議打開 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); // 設置請求方式 conn.setRequestMethod("GET"); conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"); BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8")); String msg = null; while (null != (msg = br.readLine())) { System.out.println(msg); } } }
Socket:
咱們開發的網絡應用程序位於應用層,TCP和UDP屬於傳輸層協議,在應用層如何使用傳輸層的服務呢?在應用層和傳輸層之間,則是使用套接Socket來進行分離。
套接字(Socket)就像是傳輸層爲應用層開的一個小口,應用程序經過這個小口向遠程發送數據,或者接收遠程發來的數據;而這個小口之內,也就是數據進入這個口以後,或者數據從這個口出來以前,是不知道也不須要知道的,也不會關心它如何傳輸,這屬於網絡其它層次工做。
Socket實際是傳輸層供給應用層的編程接口。Socket就是應用層與傳輸層之間的橋樑。使用Socket編程能夠開發客戶機和服務器應用程序,能夠在本地網絡上進行通訊,也可經過Internet在全球範圍內通訊。
TCP協議和UDP協議的聯繫和區別
TCP協議和UDP協議是傳輸層的兩種協議。Socket是傳輸層供給應用層的編程接口,因此Socket編程就分爲TCP編程和UDP編程兩類。
在網絡通信中,TCP方式就相似於撥打電話,使用該種方式進行網絡通信時,須要創建專門的虛擬鏈接,而後進行可靠的數據傳輸,若是數據發送失敗,則客戶端會自動重發該數據。而UDP方式就相似於發送短信,使用這種方式進行網絡通信時,不須要創建專門的虛擬鏈接,傳輸也不是很可靠,若是發送失敗則客戶端沒法得到。
這兩種傳輸方式都在實際的網絡編程中使用,重要的數據通常使用TCP方式進行數據傳輸,而大量的非核心數據則能夠經過UDP方式進行傳遞,在一些程序中甚至結合使用這兩種方式進行數據傳遞。
因爲TCP須要創建專用的虛擬鏈接以及確認傳輸是否正確,因此使用TCP方式的速度稍微慢一些,並且傳輸時產生的數據量要比UDP稍微大一些。
總結
TCP是面向鏈接的,傳輸數據安全,穩定,效率相對較低。
UDP是面向無鏈接的,傳輸數據不安全,效率較高。
接收端
package com.sxt.udp; import java.net.DatagramPacket; import java.net.DatagramSocket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: UDPServer.java * @time: 2019/11/14 14:14 * @desc: 接收端 */ public class UDPServer { public static void main(String[] args) throws Exception{ System.out.println("接收方啓動中..."); // 1. 使用DatagramSocket指定端口,建立接收端 DatagramSocket server = new DatagramSocket(9999); // 2. 準備容器,封裝成DatagramPacket包裹 byte[] container = new byte[1024*60]; DatagramPacket packet = new DatagramPacket(container, 0, container.length); // 3. 阻塞式接受包裹receeive(DatagramPacket p) // 阻塞式 server.receive(packet); // 4. 分析數據,byte[] getData,getLength() byte[] datas = packet.getData(); int len = packet.getLength(); System.out.println(new String(datas, 0, len)); // 5. 釋放資源 server.close(); } }
發送端
package com.sxt.udp; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; import java.net.SocketException; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: UDPClient.java * @time: 2019/11/14 14:14 * @desc: 發送端 */ public class UDPClient { public static void main(String[] args) throws IOException { System.out.println("發送方啓動中..."); // 1. 使用DatagramSocket指定端口,建立發送端 DatagramSocket client = new DatagramSocket(8888); // 2. 準備數據,必定轉成字節數組 String data = "上海尚學堂"; byte[] datas = data.getBytes(); // 3. 封裝成DatagramPacket包裹,須要指定目的地 DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress("localhost", 9999)); // 4. 發送包裹send(DatagramPacket p) client.send(packet); // 5. 釋放資源 client.close(); } }
注意:Address already in use: Cannot bind,同一個協議下端口不容許衝突
操做基本數據類型使用Data流
接收端
package com.sxt.udp; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.net.DatagramPacket; import java.net.DatagramSocket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: UDPServer.java * @time: 2019/11/14 14:14 * @desc: 接收端 */ public class UDPTypeServer { public static void main(String[] args) throws Exception{ System.out.println("接收方啓動中..."); // 1. 使用DatagramSocket指定端口,建立接收端 DatagramSocket server = new DatagramSocket(9999); // 2. 準備容器,封裝成DatagramPacket包裹 byte[] container = new byte[1024*60]; DatagramPacket packet = new DatagramPacket(container, 0, container.length); // 3. 阻塞式接受包裹receeive(DatagramPacket p) // 阻塞式 server.receive(packet); // 4. 分析數據,將字節數組還原爲對應的類型便可 byte[] datas = packet.getData(); int len = packet.getLength(); DataInputStream dis = new DataInputStream(new BufferedInputStream(new ByteArrayInputStream(datas))); // 順序與寫出一致 String msg = dis.readUTF(); boolean flag = dis.readBoolean(); System.out.println(msg + "-->" + flag); // 5. 釋放資源 server.close(); } }
發送端
package com.sxt.udp; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: UDPClient.java * @time: 2019/11/14 14:14 * @desc: 發送端 */ public class UDPTypeClient { public static void main(String[] args) throws IOException { System.out.println("發送方啓動中..."); // 1. 使用DatagramSocket指定端口,建立發送端 DatagramSocket client = new DatagramSocket(8888); // 2. 將基本類型,轉成字節數組 ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(baos)); // 操做類型+數據 dos.writeUTF("上海尚學堂"); dos.writeBoolean(false); dos.flush(); byte[] datas = baos.toByteArray(); // 3. 封裝成DatagramPacket包裹,須要指定目的地 DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress("localhost", 9999)); // 4. 發送包裹send(DatagramPacket p) client.send(packet); // 5. 釋放資源 client.close(); } }
操做引用數據類型使用Object流
操做文件經過將文件轉換成字節數組
實現屢次交流,單方面聊天
發送端:
package com.sxt.udp; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: UDPClient.java * @time: 2019/11/14 14:14 * @desc: 發送端 */ public class UDPTalkClient { public static void main(String[] args) throws IOException { System.out.println("發送方啓動中..."); // 1. 使用DatagramSocket指定端口,建立發送端 DatagramSocket client = new DatagramSocket(8888); // 2. 準備數據,必定轉成字節數組 BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); while (true) { String data = reader.readLine(); byte[] datas = data.getBytes(); // 3. 封裝成DatagramPacket包裹,須要指定目的地 DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress("localhost", 9999)); // 4. 發送包裹send(DatagramPacket p) client.send(packet); if (data.equals("q")) { break; } } // 5. 釋放資源 client.close(); } }
接收端:
package com.sxt.udp; import java.net.DatagramPacket; import java.net.DatagramSocket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: UDPServer.java * @time: 2019/11/14 14:14 * @desc: 接收端 */ public class UDPTalkServer { public static void main(String[] args) throws Exception { System.out.println("接收方啓動中..."); // 1. 使用DatagramSocket指定端口,建立接收端 DatagramSocket server = new DatagramSocket(9999); while (true) { // 2. 準備容器,封裝成DatagramPacket包裹 byte[] container = new byte[1024 * 60]; DatagramPacket packet = new DatagramPacket(container, 0, container.length); // 3. 阻塞式接受包裹receeive(DatagramPacket p) // 阻塞式 server.receive(packet); // 4. 分析數據,byte[] getData,getLength() byte[] datas = packet.getData(); int len = packet.getLength(); String data = new String(datas, 0, len); System.out.println(data); if (data.equals("q")) { break; } } // 5. 釋放資源 server.close(); } }
在線諮詢
發送端:
package com.sxt.udp; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; import java.net.SocketException; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TalkSend.java * @time: 2019/11/16 20:03 * @desc: 使用面向對象封裝 */ public class TalkSend implements Runnable { private DatagramSocket client; private BufferedReader reader; private String toIP; private int toPort; public TalkSend(int port, String toIP, int toPort) { this.toIP = toIP; this.toPort = toPort; try { client = new DatagramSocket(port); reader = new BufferedReader((new InputStreamReader(System.in))); } catch (SocketException e) { e.printStackTrace(); } } @Override public void run() { while (true) { String data = null; try { data = reader.readLine(); } catch (IOException e) { e.printStackTrace(); } byte[] datas = data.getBytes(); // 3. 封裝成DatagramPacket包裹,須要指定目的地 DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress(this.toIP, this.toPort)); // 4. 發送包裹send(DatagramPacket p) try { client.send(packet); } catch (IOException e) { e.printStackTrace(); } if (data.equals("q")) { break; } } // 5. 釋放資源 client.close(); } }
接收端
package com.sxt.udp; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TalkReceive.java * @time: 2019/11/16 20:11 * @desc: 封裝接收器 */ public class TalkReceive implements Runnable { private DatagramSocket server; private String from; public TalkReceive(int port, String from) { this.from = from; try { server = new DatagramSocket(port); } catch (SocketException e) { e.printStackTrace(); } } @Override public void run() { while (true) { // 2. 準備容器,封裝成DatagramPacket包裹 byte[] container = new byte[1024 * 60]; DatagramPacket packet = new DatagramPacket(container, 0, container.length); // 3. 阻塞式接受包裹receeive(DatagramPacket p) // 阻塞式 try { server.receive(packet); } catch (IOException e) { e.printStackTrace(); } // 4. 分析數據,byte[] getData,getLength() byte[] datas = packet.getData(); int len = packet.getLength(); String data = new String(datas, 0, len); System.out.println(from + "說:" + data); if (data.equals("q")) { break; } } // 5. 釋放資源 server.close(); } }
模擬學生
package com.sxt.udp; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TalkStudent.java * @time: 2019/11/16 20:13 * @desc: 模擬學生端 */ public class TalkStudent { public static void main(String[] args) { System.out.println("學生加入聊天室..."); new Thread(new TalkSend(7777, "localhost", 9999)).start(); new Thread(new TalkReceive(8888, "老師")).start(); } }
模擬老師
package com.sxt.udp; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: TalkTeacher.java * @time: 2019/11/16 20:13 * @desc: 模擬老師端 */ public class TalkTeacher { public static void main(String[] args) { System.out.println("老師加入聊天室..."); new Thread(new TalkReceive(9999, "學生")).start(); new Thread(new TalkSend(5555, "localhost", 8888)).start(); } }
建立服務器
建立客戶端
基本步驟
服務器
package com.sxt.tcp; import java.io.DataInputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: Server.java * @time: 2019/11/18 14:45 * @desc: 熟悉流程,建立服務器 */ public class Server { public static void main(String[] args) throws IOException { System.out.println("-----Server-----"); // 1. 指定端口,使用ServerSocket建立服務器 ServerSocket server = new ServerSocket(8888); // 2. 阻塞式等待鏈接 accept Socket client = server.accept(); System.out.println("一個客戶端創建了鏈接..."); // 3. 操做:輸入輸出流操做 DataInputStream dis = new DataInputStream(client.getInputStream()); String data = dis.readUTF(); System.out.println(data); // 4. 釋放資源 dis.close(); client.close(); // 關閉服務器的話 server.close(); } }
客戶端
package com.sxt.tcp; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: Client.java * @time: 2019/11/18 14:50 * @desc: 建立客戶端 */ public class Client { public static void main(String[] args) throws IOException { System.out.println("-----Client-----"); // 1. 創建鏈接:使用Socket建立客戶端 + 服務的地址和端口 Socket client = new Socket("localhost", 8888); // 2. 操做:輸入輸出流操做 DataOutputStream dos = new DataOutputStream(client.getOutputStream()); String data = "Hello"; dos.writeUTF(data); dos.flush(); // 3. 釋放資源 dos.close(); client.close(); } }
模擬登錄 單向
服務器
package com.sxt.tcp; import java.io.DataInputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: LoginServer.java * @time: 2019/11/18 15:13 * @desc: 模擬登錄 單向 服務器 */ public class LoginServer { public static void main(String[] args) throws IOException { System.out.println("-----Server-----"); // 1. 指定端口,使用ServerSocket建立服務器 ServerSocket server = new ServerSocket(8888); // 2. 阻塞式等待鏈接 accept Socket client = server.accept(); System.out.println("一個客戶端創建了鏈接..."); // 3. 操做:輸入輸出流操做 DataInputStream dis = new DataInputStream(client.getInputStream()); String datas = dis.readUTF(); // 分析 String[] dataArray = datas.split("&"); for(String info: dataArray){ String[] userInfo = info.split("="); System.out.println(userInfo[0] + "-->" + userInfo[1]); } // 4. 釋放資源 dis.close(); client.close(); // 關閉服務器的話 server.close(); } }
客戶端
package com.sxt.tcp; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: LoginClient.java * @time: 2019/11/18 15:13 * @desc: 模擬登錄 單向 客戶端 */ public class LoginClient { public static void main(String[] args) throws IOException { System.out.println("-----Client-----"); BufferedReader console = new BufferedReader(new InputStreamReader((System.in))); System.out.println("請輸入用戶名:"); String uname = console.readLine(); System.out.println("請輸入密碼:"); String upwd = console.readLine(); // 1. 創建鏈接:使用Socket建立客戶端 + 服務的地址和端口 Socket client = new Socket("localhost", 8888); // 2. 操做:輸入輸出流操做 DataOutputStream dos = new DataOutputStream(client.getOutputStream()); dos.writeUTF("uname=" + uname + "&upwd=" + upwd); dos.flush(); // 3. 釋放資源 dos.close(); client.close(); } }
模擬登錄 雙向
服務器
package com.sxt.tcp; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: LoginTwoWayServer.java * @time: 2019/11/18 15:23 * @desc: 模擬登錄 雙向 服務器 */ public class LoginTwoWayServer { public static void main(String[] args) throws IOException { System.out.println("-----Server-----"); // 1. 指定端口,使用ServerSocket建立服務器 ServerSocket server = new ServerSocket(8888); // 2. 阻塞式等待鏈接 accept Socket client = server.accept(); System.out.println("一個客戶端創建了鏈接..."); // 3. 操做:輸入輸出流操做 DataInputStream dis = new DataInputStream(client.getInputStream()); String datas = dis.readUTF(); String uname = ""; String upwd = ""; // 分析 String[] dataArray = datas.split("&"); for (String info : dataArray) { String[] userInfo = info.split("="); if (userInfo[0].equals("uname")) { System.out.println("你的用戶名爲:" + userInfo[1]); uname = userInfo[1]; } else if (userInfo[0].equals("upwd")) { System.out.println("你的密碼爲:" + userInfo[1]); upwd = userInfo[1]; } } // 輸出 DataOutputStream dos = new DataOutputStream(client.getOutputStream()); if (uname.equals("litian") && upwd.equals("123")) { dos.writeUTF("登錄成功,歡迎回來!"); } else { dos.writeUTF("登錄失敗,用戶名或密碼錯誤!"); } dos.flush(); // 4. 釋放資源 dis.close(); client.close(); // 關閉服務器的話 server.close(); } }
客戶端
package com.sxt.tcp; import java.io.*; import java.net.Socket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: LoginTwoWayClient.java * @time: 2019/11/18 15:23 * @desc: 模擬登錄 雙向 客戶端 */ public class LoginTwoWayClient { public static void main(String[] args) throws IOException { System.out.println("-----Client-----"); BufferedReader console = new BufferedReader(new InputStreamReader((System.in))); System.out.println("請輸入用戶名:"); String uname = console.readLine(); System.out.println("請輸入密碼:"); String upwd = console.readLine(); // 1. 創建鏈接:使用Socket建立客戶端 + 服務的地址和端口 Socket client = new Socket("localhost", 8888); // 2. 操做:輸入輸出流操做 DataOutputStream dos = new DataOutputStream(client.getOutputStream()); dos.writeUTF("uname=" + uname + "&upwd=" + upwd); dos.flush(); // 接受 DataInputStream dis = new DataInputStream(client.getInputStream()); String result = dis.readUTF(); System.out.println(result); // 3. 釋放資源 dos.close(); client.close(); } }
文件上傳
服務器
package com.sxt.tcp; import java.io.*; import java.net.ServerSocket; import java.net.Socket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: FileServer.java * @time: 2019/11/18 15:32 * @desc: 服務器:存儲文件 */ public class FileServer { public static void main(String[] args) throws IOException { System.out.println("-----Server-----"); // 1. 指定端口,使用ServerSocket建立服務器 ServerSocket server = new ServerSocket(8888); // 2. 阻塞式等待鏈接 accept Socket client = server.accept(); System.out.println("一個客戶端創建了鏈接..."); // 3. 操做:文件拷貝 存儲 InputStream is = new BufferedInputStream(client.getInputStream()); OutputStream os = new BufferedOutputStream(new FileOutputStream("./快樂保存.jpg")); byte[] flush = new byte[1024]; int len = -1; while ((len = is.read(flush)) != -1) { os.write(flush, 0, len); } // 4. 釋放資源 os.close(); is.close(); client.close(); // 關閉服務器的話 server.close(); } }
客戶端
package com.sxt.tcp; import java.io.*; import java.net.Socket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: FileClient.java * @time: 2019/11/18 15:32 * @desc: 客戶端:上傳文件 */ public class FileClient { public static void main(String[] args) throws IOException { System.out.println("-----Client-----"); // 1. 創建鏈接:使用Socket建立客戶端 + 服務的地址和端口 Socket client = new Socket("localhost", 8888); // 2. 操做:文件拷貝 上傳 InputStream is = new BufferedInputStream(new FileInputStream("./快樂.jpg")); OutputStream os = new BufferedOutputStream(client.getOutputStream()); byte[] flush = new byte[1024]; int len = -1; while ((len = is.read(flush)) != -1) { os.write(flush, 0, len); } // 3. 釋放資源 os.close(); is.close(); client.close(); } }
多用戶登錄
服務器
package com.sxt.tcp; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: LoginMultiServer.java * @time: 2019/11/19 9:18 * @desc: */ public class LoginMultiServer { public static void main(String[] args) throws IOException { System.out.println("-----Server-----"); // 1. 指定端口,使用ServerSocket建立服務器 ServerSocket server = new ServerSocket(8888); boolean isRunning = true; while (isRunning) { // 2. 阻塞式等待鏈接 accept Socket client = server.accept(); System.out.println("一個客戶端創建了鏈接..."); new Thread(new Channel(client)).start(); } // 關閉服務器的話 server.close(); } static class Channel implements Runnable { private Socket client; // 輸入流封裝 private DataInputStream dis; // 輸出流封裝 private DataOutputStream dos; public Channel(Socket client) { this.client = client; try { dis = new DataInputStream(client.getInputStream()); dos = new DataOutputStream(client.getOutputStream()); } catch (IOException e) { release(); } } @Override public void run() { // 3. 操做:輸入輸出流操做 String uname = ""; String upwd = ""; // 分析 String datas = receive(); String[] dataArray = datas.split("&"); for (String info : dataArray) { String[] userInfo = info.split("="); if (userInfo[0].equals("uname")) { System.out.println("你的用戶名爲:" + userInfo[1]); uname = userInfo[1]; } else if (userInfo[0].equals("upwd")) { System.out.println("你的密碼爲:" + userInfo[1]); upwd = userInfo[1]; } } if (uname.equals("litian") && upwd.equals("123")) { send("登錄成功,歡迎回來!"); } else { send("登錄失敗,用戶名或密碼錯誤!"); } release(); } // 接受數據 private String receive() { String datas = ""; try { datas = dis.readUTF(); } catch (IOException e) { e.printStackTrace(); } return datas; } // 發送數據 private void send(String msg) { try { dos.writeUTF(msg); dos.flush(); } catch (IOException e) { e.printStackTrace(); } } // 釋放資源 private void release() { // 4. 釋放資源 try { if (null != dos) { dos.close(); } } catch (IOException e) { e.printStackTrace(); } try { if (null != dis) { dis.close(); } } catch (IOException e) { e.printStackTrace(); } try { if (null != client) { client.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
客戶端
package com.sxt.tcp; import java.io.*; import java.net.Socket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: LoginMultiClient.java * @time: 2019/11/19 9:18 * @desc: */ public class LoginMultiClient { public static void main(String[] args) throws IOException { System.out.println("-----Client-----"); // 1. 創建鏈接:使用Socket建立客戶端 + 服務的地址和端口 Socket client = new Socket("localhost", 8888); // 2. 操做:輸入輸出流操做 new Send(client).send(); new Receive(client).receive(); // 3. 釋放資源 client.close(); } static class Send { private Socket client; private DataOutputStream dos; private BufferedReader console; private String msg; public Send(Socket client) { console = new BufferedReader(new InputStreamReader((System.in))); this.client = client; this.msg = init(); try { dos = new DataOutputStream(client.getOutputStream()); } catch (IOException e) { e.printStackTrace(); } } private String init() { try { System.out.println("請輸入用戶名:"); String uname = console.readLine(); System.out.println("請輸入密碼:"); String upwd = console.readLine(); return "uname=" + uname + "&upwd=" + upwd; } catch (IOException e) { e.printStackTrace(); return "???"; } } public void send() { try { dos.writeUTF(msg); dos.flush(); } catch (IOException e) { e.printStackTrace(); } } } static class Receive { private Socket client; private DataInputStream dis; public Receive(Socket client) { this.client = client; try { dis = new DataInputStream(client.getInputStream()); } catch (IOException e) { e.printStackTrace(); } } public void receive() { String result = null; try { result = dis.readUTF(); System.out.println(result); } catch (IOException e) { e.printStackTrace(); } } } }
實現一個客戶能夠正常收發信息
服務端
package com.sxt.chat1; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: Chat.java * @time: 2019/11/19 10:45 * @desc: 在線聊天室:服務端 * 目標:實現一個客戶能夠正常收發信息 */ public class Chat { public static void main(String[] args) throws IOException { System.out.println("-----Server-----"); // 1. 指定端口,使用ServerSocket建立服務器 ServerSocket server = new ServerSocket(8888); // 2. 阻塞式等待鏈接 accept Socket client = server.accept(); System.out.println("一個客戶端創建了鏈接..."); // 3. 接收消息 DataInputStream dis = new DataInputStream(client.getInputStream()); String msg = dis.readUTF(); // 4. 返回消息 DataOutputStream dos = new DataOutputStream(client.getOutputStream()); dos.writeUTF(msg); dos.flush(); // 5. 釋放資源 dos.close(); dis.close(); client.close(); } }
客戶端
package com.sxt.chat1; import java.io.*; import java.net.ServerSocket; import java.net.Socket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: Client.java * @time: 2019/11/19 10:45 * @desc: 在線聊天室:客戶端 * 目標:實現一個客戶能夠正常收發信息 */ public class Client { public static void main(String[] args) throws IOException { System.out.println("-----Client-----"); // 1. 創建鏈接:使用Socket建立客戶端 + 服務的地址和端口 Socket client = new Socket("localhost", 8888); // 2. 客戶端發送消息 BufferedReader console = new BufferedReader(new InputStreamReader((System.in))); String msg = console.readLine(); DataOutputStream dos = new DataOutputStream(client.getOutputStream()); dos.writeUTF(msg); dos.flush(); // 3. 獲取消息 DataInputStream dis = new DataInputStream(client.getInputStream()); msg = dis.readUTF(); System.out.println(msg); // 4. 釋放資源 dos.close(); dis.close(); client.close(); } }
實現一個客戶能夠正常收發多人信息——基礎簡易版
服務端
package com.sxt.chat1; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: MultiChat.java * @time: 2019/11/19 14:57 * @desc: 在線聊天室:服務端 * 目標:實現一個客戶能夠正常收發多人信息 */ public class MultiChat { public static void main(String[] args) throws IOException { System.out.println("-----Server-----"); // 1. 指定端口,使用ServerSocket建立服務器 ServerSocket server = new ServerSocket(8888); // 2. 阻塞式等待鏈接 accept Socket client = server.accept(); System.out.println("一個客戶端創建了鏈接..."); DataInputStream dis = new DataInputStream(client.getInputStream()); DataOutputStream dos = new DataOutputStream(client.getOutputStream()); boolean isRunning = true; while (isRunning) { // 3. 接收消息 String msg = dis.readUTF(); // 4. 返回消息 dos.writeUTF(msg); dos.flush(); } // 5. 釋放資源 dos.close(); dis.close(); client.close(); } }
客戶端
package com.sxt.chat1; import java.io.*; import java.net.Socket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: MultiClient.java * @time: 2019/11/19 14:57 * @desc: 在線聊天室:客戶端 * 目標:實現一個客戶能夠正常收發多條信息 */ public class MultiClient { public static void main(String[] args) throws IOException { System.out.println("-----Client-----"); // 1. 創建鏈接:使用Socket建立客戶端 + 服務的地址和端口 Socket client = new Socket("localhost", 8888); // 2. 客戶端發送消息 BufferedReader console = new BufferedReader(new InputStreamReader((System.in))); DataOutputStream dos = new DataOutputStream(client.getOutputStream()); DataInputStream dis = new DataInputStream(client.getInputStream()); boolean isRunning = true; while (isRunning) { String msg = console.readLine(); dos.writeUTF(msg); dos.flush(); // 3. 獲取消息 msg = dis.readUTF(); System.out.println(msg); } // 4. 釋放資源 dos.close(); dis.close(); client.close(); } }
使用多線程實現多個客戶能夠正常收發多人信息——oop封裝版
問題:其餘客戶必須等待以前的客戶退出,才能繼續排隊
工具類:釋放資源
package com.sxt.chat3; import java.io.Closeable; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: SxtUtils.java * @time: 2019/11/25 13:40 * @desc: 工具類:釋放資源 */ public class SxtUtils { public static void close(Closeable... targets){ for (Closeable target: targets){ try{ if(null != target){ target.close(); } }catch (Exception e){ e.printStackTrace(); } } } }
在線聊天室:服務端
package com.sxt.chat3; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: MultiChat.java * @time: 2019/11/19 14:57 * @desc: 在線聊天室:服務端 * 目標:封裝:使用多線程實現多個客戶能夠正常收發多人信息 */ public class MultiChat { public static void main(String[] args) throws IOException { System.out.println("-----Server-----"); // 1. 指定端口,使用ServerSocket建立服務器 ServerSocket server = new ServerSocket(8888); // 2. 阻塞式等待鏈接 accept while (true) { Socket client = server.accept(); System.out.println("一個客戶端創建了鏈接..."); new Thread(new Channel(client)).start(); } } // 一個客戶表明一個Channel static class Channel implements Runnable { private DataInputStream dis; private DataOutputStream dos; private Socket client; private boolean isRunning; public Channel(Socket client) { this.client = client; try { dis = new DataInputStream(client.getInputStream()); dos = new DataOutputStream(client.getOutputStream()); isRunning = true; } catch (IOException e) { release(); } } // 接受消息 private String receive() { String msg = ""; try { msg = dis.readUTF(); } catch (IOException e) { release(); } return msg; } // 發送消息 private void send(String msg) { try { dos.writeUTF(msg); dos.flush(); } catch (IOException e) { release(); } } // 釋放資源 private void release() { this.isRunning = false; SxtUtils.close(dis, dos, client); } @Override public void run() { while(isRunning){ String msg = receive(); if(!msg.equals("")){ send(msg); } } } } }
使用多線程封裝了發送端
package com.sxt.chat3; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: Send.java * @time: 2019/11/25 14:37 * @desc: 使用多線程封裝了發送端 */ public class Send implements Runnable { private BufferedReader console; private DataOutputStream dos; private Socket client; private boolean isRunning; public Send(Socket client) { this.client = client; console = new BufferedReader(new InputStreamReader(System.in)); try { dos = new DataOutputStream(client.getOutputStream()); isRunning = true; } catch (IOException e) { this.release(); } } @Override public void run() { while (isRunning) { String msg = getStrFromConsole(); if (!msg.equals("")) { send(msg); } } } private void send(String msg) { try { dos.writeUTF(msg); dos.flush(); } catch (IOException e) { release(); } } private String getStrFromConsole() { try { return console.readLine(); } catch (IOException e) { release(); } return ""; } // 釋放資源 private void release() { this.isRunning = false; SxtUtils.close(dos, client); } }
使用多線程封裝了接收端
package com.sxt.chat3; import java.io.DataInputStream; import java.io.IOException; import java.net.Socket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: Receive.java * @time: 2019/11/25 14:37 * @desc: 使用多線程封裝了接收端 */ public class Receive implements Runnable { private Socket client; private boolean isRunning; private DataInputStream dis; public Receive(Socket client) { this.client = client; try { dis = new DataInputStream(client.getInputStream()); isRunning = true; } catch (IOException e) { release(); } } // 接受消息 private String receive() { String msg = ""; try { msg = dis.readUTF(); } catch (IOException e) { release(); } return msg; } @Override public void run() { while (isRunning) { String msg = receive(); if (!msg.equals("")) { System.out.println(msg); } } } // 釋放資源 private void release() { this.isRunning = false; SxtUtils.close(dis, client); } }
在線聊天室:客戶端
package com.sxt.chat3; import java.io.*; import java.net.Socket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: MultiClient.java * @time: 2019/11/19 14:57 * @desc: 在線聊天室:客戶端 * 目標:封裝:使用多線程實現多個客戶能夠正常收發多條信息 */ public class MultiClient { public static void main(String[] args) throws IOException { System.out.println("-----Client-----"); // 1. 創建鏈接:使用Socket建立客戶端 + 服務的地址和端口 Socket client = new Socket("localhost", 8888); // 2. 客戶端發送消息 new Thread(new Send(client)).start(); new Thread(new Receive(client)).start(); } }
手寫聊天室——羣聊過渡版
服務器
package com.sxt.chat4; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.CopyOnWriteArrayList; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: MultiChat.java * @time: 2019/11/19 14:57 * @desc: 在線聊天室:服務端 * 目標:加入容器實現羣聊 */ public class Chat { private static CopyOnWriteArrayList<Channel> all = new CopyOnWriteArrayList<>(); public static void main(String[] args) throws IOException { System.out.println("-----Server-----"); // 1. 指定端口,使用ServerSocket建立服務器 ServerSocket server = new ServerSocket(8888); // 2. 阻塞式等待鏈接 accept while (true) { Socket client = server.accept(); System.out.println("一個客戶端創建了鏈接..."); Channel c = new Channel(client); // 管理全部的成員 all.add(c); new Thread(c).start(); } } // 一個客戶表明一個Channel static class Channel implements Runnable { private DataInputStream dis; private DataOutputStream dos; private Socket client; private boolean isRunning; private String name; public Channel(Socket client) { this.client = client; try { dis = new DataInputStream(client.getInputStream()); dos = new DataOutputStream(client.getOutputStream()); isRunning = true; // 獲取名稱 this.name = receive(); // 歡迎你的到來 this.send("歡迎你的到來"); sendOthers(this.name + "來了shsxt聊天室", true); } catch (IOException e) { release(); } } // 接受消息 private String receive() { String msg = ""; try { msg = dis.readUTF(); } catch (IOException e) { release(); } return msg; } // 發送消息 private void send(String msg) { try { dos.writeUTF(msg); dos.flush(); } catch (IOException e) { release(); } } // 羣聊 private void sendOthers(String msg, boolean isSys) { for(Channel other: all){ if(other == this){ // 本身 continue; } if(!isSys) { // 羣聊消息 other.send(this.name + "說:" + msg); }else{ // 系統消息 other.send(msg); } } } // 釋放資源 private void release() { this.isRunning = false; SxtUtils.close(dis, dos, client); // 退出 all.remove(this); sendOthers(this.name + "離開了...", true); } @Override public void run() { while (isRunning) { String msg = receive(); if (!msg.equals("")) { // send(msg); sendOthers(msg, false); } } } } }
客戶端
package com.sxt.chat4; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: MultiClient.java * @time: 2019/11/19 14:57 * @desc: 在線聊天室:客戶端 * 目標:加入容器實現羣聊 */ public class Client { public static void main(String[] args) throws IOException { System.out.println("-----Client-----"); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); System.out.println("請輸入用戶名:"); String name = br.readLine(); // 1. 創建鏈接:使用Socket建立客戶端 + 服務的地址和端口 Socket client = new Socket("localhost", 8888); // 2. 客戶端發送消息 new Thread(new Send(client, name)).start(); new Thread(new Receive(client)).start(); } }
工具類同上
發送端
package com.sxt.chat4; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: Send.java * @time: 2019/11/25 14:37 * @desc: 使用多線程封裝了發送端 */ public class Send implements Runnable { private BufferedReader console; private DataOutputStream dos; private Socket client; private boolean isRunning; private String name; public Send(Socket client, String name) { this.client = client; this.name = name; console = new BufferedReader(new InputStreamReader(System.in)); try { dos = new DataOutputStream(client.getOutputStream()); // 發送名稱 send(name); isRunning = true; } catch (IOException e) { this.release(); } } @Override public void run() { while (isRunning) { String msg = getStrFromConsole(); if (!msg.equals("")) { send(msg); } } } private void send(String msg) { try { dos.writeUTF(msg); dos.flush(); } catch (IOException e) { release(); } } private String getStrFromConsole() { try { return console.readLine(); } catch (IOException e) { release(); } return ""; } // 釋放資源 private void release() { this.isRunning = false; SxtUtils.close(dos, client); } }
接收端
package com.sxt.chat4; import java.io.DataInputStream; import java.io.IOException; import java.net.Socket; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: Receive.java * @time: 2019/11/25 14:37 * @desc: 使用多線程封裝了接收端 */ public class Receive implements Runnable { private Socket client; private boolean isRunning; private DataInputStream dis; public Receive(Socket client) { this.client = client; try { dis = new DataInputStream(client.getInputStream()); isRunning = true; } catch (IOException e) { release(); } } // 接受消息 private String receive() { String msg = ""; try { msg = dis.readUTF(); } catch (IOException e) { release(); } return msg; } @Override public void run() { while (isRunning) { String msg = receive(); if (!msg.equals("")) { System.out.println(msg); } } } // 釋放資源 private void release() { this.isRunning = false; SxtUtils.close(dis, client); } }
實現私聊
修改客戶端爲:
package com.sxt.chat4; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.CopyOnWriteArrayList; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: MultiChat.java * @time: 2019/11/19 14:57 * @desc: 在線聊天室:服務端 * 目標:加入容器實現羣聊 */ public class Chat { private static CopyOnWriteArrayList<Channel> all = new CopyOnWriteArrayList<>(); public static void main(String[] args) throws IOException { System.out.println("-----Server-----"); // 1. 指定端口,使用ServerSocket建立服務器 ServerSocket server = new ServerSocket(8888); // 2. 阻塞式等待鏈接 accept while (true) { Socket client = server.accept(); System.out.println("一個客戶端創建了鏈接..."); Channel c = new Channel(client); // 管理全部的成員 all.add(c); new Thread(c).start(); } } // 一個客戶表明一個Channel static class Channel implements Runnable { private DataInputStream dis; private DataOutputStream dos; private Socket client; private boolean isRunning; private String name; public Channel(Socket client) { this.client = client; try { dis = new DataInputStream(client.getInputStream()); dos = new DataOutputStream(client.getOutputStream()); isRunning = true; // 獲取名稱 this.name = receive(); // 歡迎你的到來 this.send("歡迎你的到來"); sendOthers(this.name + "來了shsxt聊天室", true); } catch (IOException e) { release(); } } // 接受消息 private String receive() { String msg = ""; try { msg = dis.readUTF(); } catch (IOException e) { release(); } return msg; } // 發送消息 private void send(String msg) { try { dos.writeUTF(msg); dos.flush(); } catch (IOException e) { release(); } } // 羣聊 private void sendOthers(String msg, boolean isSys) { boolean isPrivate = msg.startsWith("@"); if(isPrivate){ // 私聊 int idx = msg.indexOf(":"); // 獲取目標和數據 String targetName = msg.substring(1, idx); msg = msg.substring(idx+1); for(Channel other: all){ if(other.name.equals(targetName)){ other.send(this.name + "悄悄的對你說:" + msg); break; } } }else{ for(Channel other: all){ if(other == this){ // 本身 continue; } if(!isSys) { // 羣聊消息 other.send(this.name + "說:" + msg); }else{ // 系統消息 other.send(msg); } } } } // 釋放資源 private void release() { this.isRunning = false; SxtUtils.close(dis, dos, client); // 退出 all.remove(this); sendOthers(this.name + "離開了...", true); } @Override public void run() { while (isRunning) { String msg = receive(); if (!msg.equals("")) { // send(msg); sendOthers(msg, false); } } } } }
一些常量
package com.sxt.planegame2; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: Constant.java * @time: 2019/11/28 10:15 * @desc: 定義常量的類 */ public class Constant { public static final int GAME_WIDTH = 500; public static final int GAME_HEIGHT = 500; }
物體父類
package com.sxt.planegame2; import java.awt.*; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: GameObject.java * @time: 2019/11/28 9:15 * @desc: */ public class GameObject { Image img; double x, y; int speed; int width, height; public void drawSelf(Graphics g){ g.drawImage(img, (int)x, (int)y, null); } public GameObject(Image img, double x, double y) { this.img = img; this.x = x; this.y = y; } public GameObject(){ } public Rectangle getRect(){ // 返回物體所在的矩形,便於後續的碰撞檢測 return new Rectangle((int)x, (int)y, width, height); } }
炮彈類
package com.sxt.planegame2; import java.awt.*; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: Shell.java * @time: 2019/11/28 10:08 * @desc: 炮彈類 */ public class Shell extends GameObject { double degree; public Shell() { x = 200; y = 200; width = 10; height = 10; speed = 3; degree = Math.random() * Math.PI * 2; } public void draw(Graphics g) { Color c = g.getColor(); g.setColor(Color.yellow); g.fillOval((int) x, (int) y, width, height); // 炮彈沿着任意角度去飛 x += speed * Math.cos(degree); y += speed * Math.sin(degree); // 炮彈遇到牆壁以後反彈 if (x < 0 || x > Constant.GAME_WIDTH - width) { degree = Math.PI - degree; } if (y < 30 || y > Constant.GAME_HEIGHT - height) { degree = -degree; } g.setColor(c); } }
飛機類
package com.sxt.planegame2; import java.awt.*; import java.awt.event.KeyEvent; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: Plane.java * @time: 2019/11/28 9:27 * @desc: */ public class Plane extends GameObject { boolean left, up, right, down; boolean live = true; public Plane(Image img, double x, double y) { super(img, x, y); speed = 10; width = img.getWidth(null); height = img.getHeight(null); } @Override public void drawSelf(Graphics g) { if(live){ g.drawImage(img, (int) x, (int) y, null); if (left) { x -= speed; } if (right) { x += speed; } if (up) { y -= speed; } if (down) { y += speed; } }else{ } } // 按下某個鍵增長相應的方向 public void addDirection(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_LEFT: left = true; break; case KeyEvent.VK_UP: up = true; break; case KeyEvent.VK_RIGHT: right = true; break; case KeyEvent.VK_DOWN: down = true; break; } } // 擡起某個鍵取消相應的方向 public void minusDirection(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_LEFT: left = false; break; case KeyEvent.VK_UP: up = false; break; case KeyEvent.VK_RIGHT: right = false; break; case KeyEvent.VK_DOWN: down = false; break; } } }
爆炸類
package com.sxt.planegame2; import java.awt.*; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: Explode.java * @time: 2019/11/28 14:59 * @desc: 爆炸效果 */ public class Explode { double x, y; static Image[] imgs = new Image[16]; static { for (int i = 0; i < 16; i++) { imgs[i] = GameUtil.getImage("images/explode/e" + (i + 1) + ".gif"); imgs[i].getWidth(null); } } int count; public void draw(Graphics g) { if (count <= 15) { g.drawImage(imgs[count], (int) x, (int) y, null); count++; } } public Explode(double x, double y) { this.x = x; this.y = y; } }
主程序類
package com.sxt.planegame2; import javax.swing.*; import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.Date; /** * @author: Li Tian * @contact: litian_cup@163.com * @software: IntelliJ IDEA * @file: MyGameFrame.java * @time: 2019/11/27 10:33 * @desc: 飛機遊戲的主窗口 */ public class GameFrame extends Frame { Image planeImg = GameUtil.getImage("images/plane.png"); Image bg = GameUtil.getImage("images/bg.jpg"); Plane plane = new Plane(planeImg, 250, 250); Shell[] shells = new Shell[50]; Explode bao; Date startTime = new Date(); Date endTime; int period; // 遊戲持續的時間 @Override public void paint(Graphics g) { // paint是自動調用 g.drawImage(bg, 0, 0, null); plane.drawSelf(g); // 畫出全部的炮彈 for (int i = 0; i < shells.length; i++) { shells[i].draw(g); boolean peng = shells[i].getRect().intersects(plane.getRect()); if (peng) { plane.live = false; // 只須要生成一次爆炸效果就ok if (bao == null) { bao = new Explode(plane.x, plane.y); endTime = new Date(); period = (int) ((endTime.getTime() - startTime.getTime()) / 1000); } bao.draw(g); } if(!plane.live){ g.setColor(Color.red); Font f = new Font("宋體", Font.BOLD, 20); g.setFont(f); g.drawString("時間:" + period + "秒", (int) plane.x, (int) plane.y); } } } // 幫助咱們反覆重畫窗口! class PaintThread extends Thread { @Override public void run() { while (true) { // 重畫 repaint(); try { Thread.sleep(40); } catch (InterruptedException e) { e.printStackTrace(); } } } } // 增長鍵盤監聽內部類 class KeyMonitor extends KeyAdapter { @Override public void keyPressed(KeyEvent e) { plane.addDirection(e); } @Override public void keyReleased(KeyEvent e) { plane.minusDirection(e); } } // 初始化窗口 public void launchFrame() { this.setTitle("李英俊本俊的打飛機遊戲"); this.setVisible(true); this.setSize(Constant.GAME_WIDTH, Constant.GAME_HEIGHT); this.setLocation(300, 300); // 點x就關閉程序了 this.addWindowListener( new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.exit(0); } } ); // 啓動重畫窗口的線程 new PaintThread().start(); // 啓動重畫窗口的線程 addKeyListener(new KeyMonitor()); // 給窗口增長鍵盤的監聽 // 初始化50個炮彈 for (int i = 0; i < shells.length; i++) { shells[i] = new Shell(); } } public static void main(String[] args) { GameFrame f = new GameFrame(); f.launchFrame(); } private Image offScreenImage = null; @Override public void update(Graphics g) { if (offScreenImage == null) offScreenImage = this.createImage(Constant.GAME_WIDTH, Constant.GAME_HEIGHT);//這是遊戲窗口的寬度和高度 Graphics gOff = offScreenImage.getGraphics(); paint(gOff); g.drawImage(offScreenImage, 0, 0, null); } }
個人CSDN:https://blog.csdn.net/qq_21579045
個人博客園:https://www.cnblogs.com/lyjun/
個人Github:https://github.com/TinyHandsome
紙上得來終覺淺,絕知此事要躬行~
歡迎你們過來OB~
by 李英俊小朋友