【java】输入输出流
輸入輸出應用編程
Input/Output:指跨越出了JVM 的邊界,與外界數據的源頭或者目標數據源進行數據交換
- Java程序會從各種地方輸入數據,比如文件,磁盤,網絡,其它程序中
- Java會采用不同的方式輸出執行結果,比如屏幕、文件、磁盤、網絡等
輸入/輸出是針對JVM 而言
輸入輸出種的流模型
在Java程序中,對于數據的輸入輸出操作以流Stream方式進行,JavaSE提供各種各樣的類用于使用相同的方法獲取不同類型的數據,程序中通過標準的方法輸入或者輸出數據
流是處理輸入/輸出的一個潔凈的方法,它不需要代碼理解鍵盤和網絡的不同。Java中流的實現是基于java.io包定義的類層次結構的
流概念
java.io包通過數據流、序列化和文件系統為用戶提供一種完成I/O操作的輸入/輸出流
數據流是指所有的數據通信通道
流是字節或字符數據的數據源或目的,用以隱藏數據傳輸細節,可以從流讀取數據或將數據寫到流
Java程序不能直接操縱I/O設備,而是在程序和設備之間加入了一個中間介質,這就是流。IO輸入輸出通常指數據在內部存儲器和外部存儲器或其它周邊設備之間的輸入輸出
流是數據傳輸的抽象表達,與具體設備無關。程序一旦建立了流,就可以不用理會起點或終點是何種設備
建立流實際上就是建立數據傳輸通道,將起點和終點連接起來
Java程序通過流來完成輸入/輸出,它是生產或消費信息的抽象
流通過Java的輸入/輸出系統與物理設備鏈接。盡管與它們鏈接的物理設備不盡相同,但是所有流的行為具有同樣的方式
相同的輸入/輸出類和方法適用于所有類型的外部設備。這意味著一個輸入流能夠抽象多種不同類
型的輸入, 從磁盤文件,從鍵盤或從網絡套接字獲取輸入,一個輸出流可以輸出到控制臺,磁盤文件或相連的網絡。流是處理輸入/輸出的一個潔凈的方法,它不需要代碼理解鍵盤和網絡的不同。
流的分類
從Java不同版本上來說,流可以分為BIO、NIO和AIO三大類。Java 中的 BIO、NIO和 AIO 理解為是 Java語言對操作系統的各種 IO 模型的封裝。程序員在使用這些 API 的時候,不需要關心操作系統層面的知
識,也不需要根據不同操作系統編寫不同的代碼。只需要使用Java的API就可以了。
- BIO即同步阻塞I/O模式,數據的讀取寫入必須阻塞在一個線程內等待其完成。
- NIO即同步非阻塞,一個線程不斷的輪詢每個輸入輸出的狀態改變,如果有狀態發生了改變,則進行下一步的操作。
- AIO即異步非阻塞I/O模型,無需一個線程去輪詢所有IO操作的狀態改變,在相應的狀態改變后,系統會通知對應的線程來處理。
同步/異步關注的是消息通信機制。
- 同步Synchronous是指發起一個調用后,調用方必須等待此調用返回結果后才能繼續執行。
- 異步Asynchronous是指發起一個調用后,調用方可繼續執行后續操作,被調用者執行結束主動給調用方返回結果。
阻塞/非阻塞關注的是程序在等待調用結果時的狀態,區別在于第一步發起 IO 請求后是否會被阻塞。
- 阻塞是指調用結果返回前,當前線程會被掛起,調用線程只有在得到結果之后才返回。
- 非阻塞是指在調用結果返回前,不影響當前線程執行其他操作,也就是不會阻塞當前線程。
BIO是一種同步阻塞的通信模式,是一個比較傳統的通信方式,模式簡單、使用方便,但并發處理能力低,通信耗時,依賴網速。
NIO(Non-Bock) IO 是一種同步非阻塞的通信模式,針對網絡傳輸效能優化的新功能。
AIO(Asynchronous IO)是一種異步非阻塞的通信模式。
- 同步阻塞的工作模式是先來到廚房,開始燒水,并在水壺旁等待直到水開了為止
- 同步非阻塞的工作模式是指來到廚房,開始燒水,這次不在水壺旁等著,回到客廳看電視,每隔幾分鐘去檢查水是否燒開了
- 異步非阻塞的工作模式是指來到廚房,開始燒水,回到客廳看電視,直到聽到水燒開后水壺的提示音。
同步vs異步:指的是被調用方以何種方式返回調用結果?(我們如何知道水壺中的水燒開了,是主動發現還是水壺被動提醒)
阻塞vs非阻塞:指的是調用方在調用后的狀態是否是阻塞?(我們把水壺燒水開關打開后,我們是否在水壺旁等待水燒開)
適用場景
- BIO適用于連接數目比較小且固定的架構,這種方式對服務器資源要求比較高,并發局限于應用中,JDK1.4以前的唯一選擇,但程序直觀簡單易理解
- NIO適用于連接數目比較多且連接比較短的架構,比如聊天服務器,并發局限于應用中,編程比較復雜,JDK1.4開始支持
- AIO方式適用于連接數目多且連接比較長的架構,比如相冊服務器,充分調用OS參與并發操作,編程比較復雜,JDK7開始支持。
- 按流向分為輸入流和輸出流,可以從輸入流讀取數據但不能寫,要從輸入流讀取數據,則必須有一個與這個流相關的字符源
- 按傳輸單位分為字節流和字符流
- Java具備平臺無關性,這里的字節是指8位,字符是16位
- 字節流從InputStream/OutputStream派生出來,以字節為基本處理單位,一般用于操作二進制數據,字節次序是有意義的
- 字符流從Reader/Writer派生出來的,以16位的Unicode碼表示字符為基本處理單位,一般用于操作字符數據
- 使用橋接流可以實現兩個流之間的轉換
- 按功能還可以分為節點流和過濾流
- 節點流:負責數據源和程序之間建立連接,結點流對特定的地方讀寫
- 過濾流:用于給節點增加功能,過濾流使用結點流進行輸入/輸出并添加附加功能
過濾流的構造方式是以其他流位參數構造(這樣的設計模式稱為裝飾模式)。Java的IO流使用裝飾器模式,將IO流分成底層節點流和上層處理流。其中節點流用于和底層的物理存儲節點直接關聯。過濾流是連接在已存在的流之上,通過對數據的處理為程序提供更為強大的讀寫功能。
注意:I/O流是一類很寶貴的資源,使用完后必須調用close()方法關閉流并釋放資源。在關閉流時只用關閉最外層的流
字符流就是字節流讀取文字字節數據后,不直接操作而是先查指定的編碼表以獲取對應的文字。簡單的說:字符流 = 字節流 + 編碼表
- 字符流的兩個頂層父類:Reader和Writer
- 字節流的兩個頂層父類:InputStream和OutputStream
- 輸入輸出靠reader和writer, inputstream和outputstream四個類和子類支持
- 讀入的內容有對象,字符,圖像和聲音等
Java輸入輸入靠reader、writer、InputStream、OutputStream四個類和子類支持。JDK的I/O包中使用Decorator模式,并運用此模式,實現一個新的輸出流類
擴展類的功能
擴展一個類功能的最簡單方式就是繼承
繼承
public interface IShape { void draw(); }需要追加功能的類實現
public class Circle implements IShape { @Override public void draw() { System.out.println("我畫一個圓"); } }通過繼承添加子類用于追加功能
public class CircleExtends extends Circle { @Override public void draw() { System.out.println("我使用紅色"); // 追加的功能 super.draw(); //調用父類中被覆蓋的方法 System.out.println("我收齊圖片"); // 追加的功能 } }測試程序
IShape s1=new CircleExtends(); s1.draw();最大的問題是耦合
裝飾模式
裝飾器模式Decorator Pattern允許向一個現有的對象添加新的功能,同時又不改變其結構。這種類型的設計模式屬于結構型模式,它是作為現有的類的一個包裝。
- 意圖:動態地給一個對象添加一些額外的職責。就增加功能來說,裝飾器模式相比生成子類更為靈活
- 主要解決:一般的為了擴展一個類經常使用繼承方式實現,由于繼承為類引入靜態特征,并且隨著擴展功能的增多,子類會很膨脹
- 優點:裝飾類和被裝飾類可以獨立發展,不會相互耦合,裝飾模式是繼承的一個替代模式,裝飾模式可以動態擴展一個實現類的功能
- 缺點:多層裝飾比較復雜
- 使用場景: 1、擴展一個類的功能。 2、動態增加功能,動態撤銷。
注意事項:可代替繼承。
要求:
1、被裝飾方(多個)有個抽象角色—接口、抽象類
2、裝飾方實現接口(繼承抽象類),并將裝飾方定義為屬性
Decorator模式又名包裝器Wrapper,它的主要用途在于給一個對象動態的添加一些額外的職責。與生成子類相比,它更具有靈活性
裝飾器模式中的四種角色:被裝飾對象、裝飾對象、裝飾器Decorator、公共接口或抽象類
被裝飾方的抽象角色,未來可能會有多種實現
具體的被裝飾方實現,這里可能會有多個實現類,而且具體實現方式不相同
public class Circle implements IShape{ public void draw(){ System.out.println("畫一個圓") } }裝飾抽象角色:未來裝飾方可以當作IShape類直接進行調用
public abstract class DecorateShape implements IShape{ private IShape shape;//裝飾方,就是需要動態追加功能的對象 public DecorateShape(IShape shape){ this.shape=shape; } //當調用裝飾方的draw方法時,實際上最終工作的是被裝飾方,裝飾方只是在被裝飾方執行動作時添 加額外的處理 public void draw(){ shape.draw(); } }具體的裝飾角色
public class RedDecorateShape extends DecorateShape{ public RedDecorateShape(IShape shape){ super(shape); } //父類中并沒有添加新功能,在子類中重新覆蓋定義追加具體的功能 public void draw(){ super.draw(); useRed();//就是具體動態追加的功能 } private void useRed(){ System.out.println("使用紅色的邊線"); } }測試:
IShape circle=new Circle();//被裝飾方對象,就是需要給它動態添加功能 IShape decorate=new RedDecorateShape(circle);//裝飾方對象,它給circle對象添加功能 decorate.draw(); //實際應用中可能會簡化寫成 IShape test=new RedDecorateShape(new Circle());在IO流定義中使用裝飾模式
Java.io包中大量的使用了設計模式中的裝飾模式decorator和適配器模式adapter,使IO庫具有很強的對稱性和可擴展性,對稱性表現在I/O的對稱和byte/char流的對稱上。
抽象角色
這里節點流就是這個抽象角色的具體實現,例如FileInputStream
裝飾(抽象)角色用于通過繼承定義過濾流
public class FilterInputStream extends InputStream{ protected volatile InputStream in; protected FilterInputStream(InputStream in){ this.in=in; } public int read()throws IOException{ return in.read(); } }裝飾的具體角色真正添加功能,例如BufferedInputStream就是給節點流上添加緩存
public class BufferedInputStream extends FilterInputStream{ public BufferedInputStream(InputStream in){ this(in, DEFAULT_BUFFER_SIZE); } public synchronized int read() throws IOException{ if(pos>=count) ...File類
- 類File提供了一種與機器無關的方式來描述一個文件對象的屬性
- 對于目錄,Java把它簡單的處理為一種特殊的文件,即文件名的列表
- java.io.File用于封裝和平臺無關的文件夾和文件夾對象,例如獲取一個文件的字節數。這個寫法是針對windows平臺的,如果使用mac或者linux平臺,則需要使用
- 通過類File中提供的方法,可以得到文件或者目錄的描述信息,包括名稱、所在路徑、可讀性等,還可以生成新的文件、目錄、改變文件名、刪除文件、列出一個目錄中所有的文件或者與某個模式匹配的文件等
常見的構造方法
- File(String pathName)以path為路徑創建File對象,如果pathname是相對路勁,則默認的當前路徑在系統屬性user.dir中存儲
- File(String parent,String child) 這里文件對象的路徑為相對于parent路徑的child路徑
需要記憶的方法
// File ff=new File("e:\\綜合面試.txt"); 絕對路徑的寫法,其中的路徑分割符可以使用 \\或者/ File ff = new File("e:/綜合面試.txt"); System.out.println(ff.getAbsolutePath());//用于獲取文件對象的絕對路徑 e:\綜合面 試.txt //判斷正確的前提是必須存在 System.out.println(ff.isDirectory()); //false 用于判斷當前文件對象是否為文件夾 System.out.println(ff.isFile());//true 用于判斷當前文件對象是否為文件 System.out.println(ff.length());//獲取指定文件對象的字節數,單位為字節 System.out.println(ff.exists());//是否存在對應的文件對象,返回true則表示存在 ff.delete()//刪除文件或者文件夾,注意Java并不能保證刪除一定成功 ff.mkdirs()//創建多級文件夾,而使用mkdir只能創建一層文件夾 //實際編程中很少使用的方法 listFiles():File[]//獲取當前文件夾的所有子文件信息,子文件為File對象靜態屬性
Separator存儲當前系統的路徑分隔符
注意: \ 在字符串中為轉義字符,如果需要使用 \ 則必須寫成 \ 。一般推薦使用/
File f1=new File("c:\\windows\\setupact.log"); File f2=new File("c:/windows/setupact.log"); //這里的/等價于上面的\\ File f3=new File("c:"+File.separator+"windows"+File.separator+"setupact.log"); System.out.println(File.pathSeparator);//路徑分隔符;如果寫多個路徑使用;分割 System.out.println(File.separator);//目錄的分隔符\訪問文件名的方法
- getName():String獲取文件名稱
- getPath():String獲取路徑,如果構建File時使用的是相對路徑,則這里返回的就是相對路徑;如果創建時參數為絕對路徑,則返回絕對路徑
- getAbsolutePath():String獲取絕對路徑
- getParent():String獲取當前文件對象的上級File對象,如果構建文件對象時使用的是相對路徑,則這里返回為null
文件檢測相關方法
- canWrite():boolean 是否可寫
- canRead():boolean 是否可讀
- 首先exists()存在性判斷,存在時再進行是否文件或者文件夾的判斷
- isFile():boolean 是否是文件,因為File對象可以封裝文件和文件夾
- isDirectory():boolean是否為文件夾
- isAbsolute():boolean是否為絕對路徑
- lastModified():long文件的最后修改時間
常見操作
- createNewFile():boolean創建一個新文件
這個方法中有個受檢型異常需要進行處理,處理方法:try/catch結構或者在方法上throws拋出異常
如果文件不存在則返回true,表示創建成功;如果文件已經存在則返回false,表示創建失敗
File ff = new File("abc.txt"); if (!ff.exists()) ff.createNewFile(); System.out.println(ff.getAbsolutePath());- static createTempFile(String prefix, string suffix):File 這個臨時文件名稱會有隨機內容
臨時文件的默認存放位置為操作系統默認的臨時文件位置
c:\Users\Administrator\AppData\Local\Temp\
- exists():boolean文件或者文件夾是否存在
- length():long獲取文件大小,單位為字節,如果是文件夾則返回值沒有指定
- renameTo(File):boolean修改名稱,可以修改文件名和文件夾名稱
- delete():boolean 刪除文件。也可以刪除文件夾,要求文件夾為空,不能有文件和子文件夾
注意:因為具體的文件或者文件夾的刪除操作是由操作系統負責實現的,所以Java不能保證一定能夠刪除成功!
deleteOnExit():void退出系統時自動刪除
- 目錄操作方法
mkdir():boolean只能創建一層文件夾,如果創建d:/a1/a2/a3時,當父文件夾d:/a1/a2不存在時則創建失敗
mkdirs():boolean自動創建多級文件夾
- list():String[]獲取當前文件夾的所有子文件信息
listFiles():File[]獲取當前文件夾的所有子文件信息,子文件為File對象
- static listRoots():File[]列出系統的所有的根路徑
Path和Paths以及Files
Path接口代表一個平臺無關的平臺路徑
Files提供工具方法操作文件
Paths提供創建Path的靜態工廠方法
Path相關方法
Path path=Paths.get("c:","ddd","eee"); //對應c:\dddd\eee路徑 Path path=Paths.get("."); //獲取當前路徑對象 path.toAbsolutePath()//獲取path對應的絕對路徑 path.toAbsolutePath().getRoot()//獲取path對應的根路徑Files工具方法
復制文件Files.copy(Paths.get(“T1.java”), new FileOutputStream(“a.txt”))
一次性讀取文件的所有行 List lines=Files.readAllLines()
總結
以上是生活随笔為你收集整理的【java】输入输出流的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Kaggle TMDB电影数据分析项目实
- 下一篇: 【论文笔记】(FGSM公式推导)Expl