- 第19章 IO流
- 檔案
- 常用的檔案操作
- 建立檔案物件相關構造器和方法
- 獲取檔案的相關資訊
- 目錄的操作和檔案刪除
- IO 流原理及流的分類
- Java IO 流原理
- 流的分類
- IO 流體系圖-常用的類
- IO 流體系圖
- 檔案 VS 流
- FileInputStream 介紹
- FileInputStream 應用例項
- FileOutputStream 介紹
- FileOutputStream 應用例項
- FileOutputStream 應用例項2
- FileReader 和FileWriter 介紹
- FileReader 相關方法
- FileWriter 相關方法
- FileReader 和FileWriter 應用案例
- 節點流和處理流
- 基本介紹
- 節點流和處理流一覽圖
- 節點流和處理流的區別和聯絡
- 處理流的功能
- 處理流 BufferedReader 和BufferedWriter
- 應用案例
- 處理流BufferedInputStream 和BufferedOutputStream
- 應用案例
- 物件流ObjectInputStream 和ObjectOutputStream
- 物件流介紹
- 應用案例
- 標準輸入輸出流
- 介紹
- 應用案例1
- 應用案例2
- 轉換流InputStreamReader 和OutputStreamWriter
- 應用案例
- 列印流PrintStream 和PrintWriter
- Properties 類
- 基本介紹
- 應用案例
- 本章作業
第19章 IO流
檔案
檔案,對我們並不陌生,檔案是儲存資料的地方。檔案在程式中是以流的形式來操作的。
流:資料在資料來源(檔案)和程式(記憶體)之間經歷的路徑
輸入流:資料從資料來源(檔案)到程式(記憶體)的路徑
輸出流:資料從程式(記憶體)到資料來源(檔案)的路徑
常用的檔案操作
建立檔案物件相關構造器和方法
new File(String pathname)
//根據路徑構建一個File物件new File(File parent, String child)
//根據父目錄檔案+子路徑構建newFile(String parent, String child)
//根據父目錄+子路徑構建
package com.hspedu.file;
import org.junit.jupiter.api.Test;
import java.io.*;
/**
* 演示建立檔案
*/
public class FileCreate {
public static void main(String[] args) {
}
//方式1 new File(String pathname)
@Test
public void create01() {
String filePath = "e:\\news1.txt";
File file = new File(filePath);
try {
file.createNewFile();
System.out.println("檔案建立成功");
} catch (IOException e) {
e.printStackTrace();
}
}
//方式2 new File(File parent,String child) //根據父目錄檔案+子路徑構建
//e:\\news2.txt
@Test
public void create02() {
File parentFile = new File("e:\\");
String fileName = "news2.txt";
//這裡的file物件,在java程式中,只是一個物件
//只有執行了createNewFile 方法,才會真正的,在磁碟建立該檔案
File file = new File(parentFile, fileName);
try {
file.createNewFile();
System.out.println("建立成功~");
} catch (IOException e) {
e.printStackTrace();
}
}
//方式3 new File(String parent,String child) //根據父目錄+子路徑構建
@Test
public void create03() {
//String parentPath = "e:\\";
String parentPath = "e:\\";
String fileName = "news4.txt";
File file = new File(parentPath, fileName);
try {
file.createNewFile();
System.out.println("建立成功~");
} catch (IOException e) {
e.printStackTrace();
}
}
//下面四個都是抽象類
//InputStream
//OutputStream
//Reader //字元輸入流
//Writer //字元輸出流
}
獲取檔案的相關資訊
getName
、getAbsolutePath
、getParent
、length
、exists
、isFile
、isDirectory
package com.hspedu.file;
import org.junit.jupiter.api.Test;
import java.io.File;
public class FileInformation {
public static void main(String[] args) {
}
// 獲取檔案的資訊
@Test
public void info() {
// 先建立檔案物件
File file = new File("e:\\news1.txt");
// 呼叫相應的方法,得到對應資訊
System.out.println("檔名字=" + file.getName());
// getName、getAbsolutePath、getParent、length、exists、isFile、isDirectory
System.out.println("檔案絕對路徑=" + file.getAbsolutePath());
System.out.println("檔案父級目錄=" + file.getParent());
System.out.println("檔案大小(位元組)=" + file.length());
System.out.println("檔案是否存在=" + file.exists());//T
System.out.println("是不是一個檔案=" + file.isFile());//T
System.out.println("是不是一個目錄=" + file.isDirectory());//F
}
}
目錄的操作和檔案刪除
mkdir建立一級目錄、mkdirs建立多級目錄、delete刪除空目錄或檔案
應用案例演示
- 判斷
d:\\news1.txt
是否存在,如果存在就刪除 - 判斷
D:\\ldemo02
是否存在,存在就刪除,否則提示不存在. - 判斷
D:\\demo\\a\\b\\c
目錄是否存在,如果存在就提示已經存在,否則就建立
package com.hspedu.file;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
public class Directory_ {
public static void main(String[] args) {
//
}
//判斷 d:\\news1.txt 是否存在,如果存在就刪除
@Test
public void m1() {
String filePath = "e:\\news1.txt";
File file = new File(filePath);
if (file.exists()) {
if (file.delete()) {
System.out.println(filePath + "刪除成功");
} else {
System.out.println(filePath + "刪除失敗");
}
} else {
System.out.println("該檔案不存在...");
}
}
//判斷 D:\\demo02 是否存在,存在就刪除,否則提示不存在
//這裡我們需要體會到,在java程式設計中,目錄也被當做檔案
@Test
public void m2() {
String filePath = "D:\\demo02";
File file = new File(filePath);
if (file.exists()) {
if (file.delete()) {
System.out.println(filePath + "刪除成功");
} else {
System.out.println(filePath + "刪除失敗");
}
} else {
System.out.println("該目錄不存在...");
}
}
//判斷 D:\\demo\\a\\b\\c 目錄是否存在,如果存在就提示已經存在,否則就建立
@Test
public void m3() {
String directoryPath = "D:\\demo\\a\\b\\c";
File file = new File(directoryPath);
if (file.exists()) {
System.out.println(directoryPath + "存在..");
} else {
if (file.mkdirs()) { //建立一級目錄使用mkdir() ,建立多級目錄使用mkdirs()
System.out.println(directoryPath + "建立成功..");
} else {
System.out.println(directoryPath + "建立失敗...");
}
}
}
}
IO 流原理及流的分類
Java IO 流原理
-
I/O 是 Input/Output 的縮寫,I/O技術是非常實用的技術,用於處理資料傳輸。
如讀/寫檔案,網路通訊等。 -
Java程式中,對於資料的輸入/輸出操作以”流(stream)”的方式進行。
-
java.io包下提供了各種“流”類和介面,用以獲取不同種類的資料,並透過方法輸入或輸出資料
-
輸入input:讀取外部資料(磁碟、光碟等儲存裝置的資料)到程式(記憶體)中。
-
輸出output:將程式(記憶體)資料輸出到磁碟、光碟等儲存裝置中
流的分類
- 按運算元據單位不同分為:位元組流(8 bit)(二進位制檔案例如聲音影片word等可以無損操作),字元流(按字元)(文字檔案)。
- 按資料流的流向不同分為:輸入流,輸出流。
- 按流的角色的不同分為:節點流,處理流 / 包裝流。
- Java的IO流共涉及40多個類,實際上非常規則,都是從如上4個抽象基類派生的。
- 由這四個類派生出來的子類名稱都是以其父類名作為子類名字尾。
IO 流體系圖-常用的類
IO 流體系圖
檔案 VS 流
FileInputStream 介紹
FileInputStream 應用例項
請使用FileInputStream 讀取hello.txt 檔案,並將檔案內容顯示到控制檯.
當然,注意一個漢字通常是由3個bytes構成的,而read只會讀取一個byte的資料,因此如果用read則會亂碼,建議文字檔案用字元流處理。
package com.hspedu.inputstream_;
import org.junit.jupiter.api.Test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* 演示FileInputStream的使用(位元組輸入流 檔案--> 程式)
*/
public class FileInputStream_ {
public static void main(String[] args) {
}
/**
* 演示讀取檔案...
* 單個位元組的讀取,效率比較低
* -> 使用 read(byte[] b)
*/
@Test
public void readFile01() {
String filePath = "e:\\hello.txt";
int readData = 0;
FileInputStream fileInputStream = null;
try {
//建立 FileInputStream 物件,用於讀取 檔案
fileInputStream = new FileInputStream(filePath);
//從該輸入流讀取一個位元組的資料。 如果沒有輸入可用,此方法將阻止。
//如果返回-1 , 表示讀取完畢
while ((readData = fileInputStream.read()) != -1) {
System.out.print((char)readData);//轉成char顯示
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//關閉檔案流,釋放資源.
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 使用 read(byte[] b) 讀取檔案,提高效率
*/
@Test
public void readFile02() {
String filePath = "e:\\hello.txt";
//位元組陣列
byte[] buf = new byte[8]; //一次讀取8個位元組.
int readLen = 0;
FileInputStream fileInputStream = null;
try {
//建立 FileInputStream 物件,用於讀取 檔案
fileInputStream = new FileInputStream(filePath);
//從該輸入流讀取最多b.length位元組的資料到位元組陣列。 此方法將阻塞,直到某些輸入可用。
//如果返回-1 , 表示讀取完畢
//如果讀取正常, 返回實際讀取的位元組數
while ((readLen = fileInputStream.read(buf)) != -1) {
System.out.print(new String(buf, 0, readLen));//顯示
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//關閉檔案流,釋放資源.
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
FileOutputStream 介紹
FileOutputStream 應用例項
請使用FileOutputStream 在a.txt 檔案,中寫入“hello,world”. 如果檔案不存在,會建立檔案(注意:前提是目錄已經存在.)
package com.hspedu.outputstream_;
import org.junit.jupiter.api.Test;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStream01 {
public static void main(String[] args) {
}
/**
* 演示使用 FileOutputStream 將資料寫到檔案中,
* 如果該檔案不存在,則建立該檔案
*/
@Test
public void writeFile() {
//建立 FileOutputStream物件
String filePath = "e:\\a.txt";
FileOutputStream fileOutputStream = null;
try {
//得到 FileOutputStream物件 物件
//老師說明
//1. new FileOutputStream(filePath) 建立方式,當寫入內容是,會覆蓋原來的內容
//2. new FileOutputStream(filePath, true) 建立方式,當寫入內容是,是追加到檔案後面
fileOutputStream = new FileOutputStream(filePath, true);
//寫入一個位元組
//fileOutputStream.write('H');//
//寫入字串
String str = "hello,world!";
//str.getBytes() 可以把 字串-> 位元組陣列
//fileOutputStream.write(str.getBytes());
/*
write(byte[] b, int off, int len) 將 len位元組從位於偏移量 off的指定位元組陣列寫入此檔案輸出流
*/
fileOutputStream.write(str.getBytes(), 0, 3);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
FileOutputStream 應用例項2
程式設計完成圖片/音樂的複製.
package com.hspedu.outputstream_;
import com.hspedu.inputstream_.FileInputStream_;
import java.io.*;
public class FileCopy {
public static void main(String[] args) {
//完成 檔案複製,將 e:\\Koala.jpg 複製 c:\\
//思路分析
//1. 建立檔案的輸入流 , 將檔案讀入到程式
//2. 建立檔案的輸出流, 將讀取到的檔案資料,寫入到指定的檔案.
String srcFilePath = "e:\\Koala.jpg";
String destFilePath = "e:\\Koala3.jpg";
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
try {
fileInputStream = new FileInputStream(srcFilePath);
fileOutputStream = new FileOutputStream(destFilePath);
//定義一個位元組陣列,提高讀取效果
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = fileInputStream.read(buf)) != -1) {
//讀取到後,就寫入到檔案 透過 fileOutputStream
//即,是一邊讀,一邊寫
fileOutputStream.write(buf, 0, readLen);//一定要使用這個方法
// 不能用 fileOutputStream.write(buf) 因為最後有可能讀不夠而出錯
}
System.out.println("複製ok");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//關閉輸入流和輸出流,釋放資源
if (fileInputStream != null) {
fileInputStream.close();
}
if (fileOutputStream != null) {
fileOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
FileReader 和FileWriter 介紹
FileReader 相關方法
- new FileReader(File/String)
- read: 每次讀取單個字元,返回該字元,如果到檔案末尾返回-1
- read(charD): 批次讀取多個字元到陣列,返回讀取到的字元數,如果到檔案未尾返回-1
相關API:
- new String(charD):將char[]轉換成String
- new String(charl,off,len):將char[]的指定部分轉換成String
FileWriter 相關方法
- new FileWriter(File/String):覆蓋模式,相當於流的指標在首端
- new FileWriter(File/String,true):追加模式,相當於流的指標在尾端
- write(int):寫入單個字元
- write(char):寫入指定陣列
- write(charl.off,.len):寫入指定陣列的指定部分6) write (string):寫入整個字串
- write(string,off,len):寫入字串的指定部分
相關APl: String類: toCharArray:將String轉換成char[]
注意: FileWriter使用後,必須要關閉(close)或重新整理(flush),否則寫入不到指定的檔案!因為僅僅到了Java程式記憶體中。
FileReader 和FileWriter 應用案例
- 使用FileReader 從story.txt 讀取內容,並顯示
package com.hspedu.reader_;
import org.junit.jupiter.api.Test;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class FileReader_ {
public static void main(String[] args) {
}
/**
* 單個字元讀取檔案
*/
@Test
public void readFile01() {
String filePath = "e:\\story.txt";
FileReader fileReader = null;
int data = 0;
//1. 建立FileReader物件
try {
fileReader = new FileReader(filePath);
//迴圈讀取 使用read, 單個字元讀取
while ((data = fileReader.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileReader != null) {
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 字元陣列讀取檔案
*/
@Test
public void readFile02() {
System.out.println("~~~readFile02 ~~~");
String filePath = "e:\\story.txt";
FileReader fileReader = null;
int readLen = 0;
char[] buf = new char[8];
//1. 建立FileReader物件
try {
fileReader = new FileReader(filePath);
//迴圈讀取 使用read(buf), 返回的是實際讀取到的字元數
//如果返回-1, 說明到檔案結束
while ((readLen = fileReader.read(buf)) != -1) {
System.out.print(new String(buf, 0, readLen));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileReader != null) {
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 使用FileWriter 將“風雨之後,定見彩虹” 寫入到note.txt 檔案中, 注意細節.
package com.hspedu.writer_;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriter_ {
public static void main(String[] args) {
String filePath = "e:\\note.txt";
//建立FileWriter物件
FileWriter fileWriter = null;
char[] chars = {'a', 'b', 'c'};
try {
fileWriter = new FileWriter(filePath);//預設是覆蓋寫入
// 3) write(int):寫入單個字元
fileWriter.write('H');
// 4) write(char[]):寫入指定陣列
fileWriter.write(chars);
// 5) write(char[],off,len):寫入指定陣列的指定部分
fileWriter.write("教育".toCharArray(), 0, 3);
// 6) write(string):寫入整個字串
fileWriter.write(" 你好北京~");
fileWriter.write("風雨之後,定見彩虹");
// 7) write(string,off,len):寫入字串的指定部分
fileWriter.write("上海天津", 0, 2); // 輸出上海,因為從offset0開始,寫入兩個字元
//在資料量大的情況下,可以使用迴圈操作.
} catch (IOException e) {
e.printStackTrace();
} finally {
// 對應FileWriter , 一定要關閉流,或者flush才能真正的把資料寫入到檔案
// 看原始碼就知道原因.
/*
看看程式碼
private void writeBytes() throws IOException {
this.bb.flip();
int var1 = this.bb.limit();
int var2 = this.bb.position();
assert var2 <= var1;
int var3 = var2 <= var1 ? var1 - var2 : 0;
if (var3 > 0) {
if (this.ch != null) {
assert this.ch.write(this.bb) == var3 : var3;
} else {
this.out.write(this.bb.array(), this.bb.arrayOffset() + var2, var3);
}
}
this.bb.clear();
}
*/
try {
//fileWriter.flush();
//關閉檔案流,等價於 flush() + 關閉
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("程式結束...");
}
}
節點流和處理流
基本介紹
-
節點流可以從一個特定的資料來源讀寫資料,如FileReader、FileWriter
-
處理流(也叫包裝流)是“連線”在已存在的流(節點流或處理流)之上,為程式提供更為強大的讀寫功能,也更加靈活,如BufferedReader、BufferedWriter
節點流和處理流一覽圖
節點流和處理流的區別和聯絡
- 節點流是底層流/低階流,直接跟資料來源相接。
- 處理流(包裝流)包裝節點流,既可以消除不同節點流的實現差異,也可以提供更方
便的方法來完成輸入輸出。 - 處理流(也叫包裝流)對節點流進行包裝,使用了修飾器設計模式,不會直接與資料
源相連[模擬修飾器設計模式]
package com.hspedu;
public class StringReader_ extends Reader_ {
public void readString() {
System.out.println("讀取字串..");
}
}
package com.hspedu;
public class FileReader_ extends Reader_ {
public void readFile() {
System.out.println("對檔案進行讀取...");
}
}
package com.hspedu;
public abstract class Reader_ { //抽象類
public void readFile() {
}
public void readString() {
}
//在Reader_ 抽象類,使用read方法統一管理.
//後面在呼叫時,利於物件動態繫結機制, 繫結到對應的實現子類即可.
//public abstract void read();
}
package com.hspedu;
/**
* 做成處理流/包裝流
*/
public class BufferedReader_ extends Reader_{
private Reader_ reader_; //屬性是 Reader_型別
//接收Reader_ 子類物件
public BufferedReader_(Reader_ reader_) {
this.reader_ = reader_;
}
public void readFile() { //封裝一層
reader_.readFile();
}
//讓方法更加靈活, 多次讀取檔案, 或者加緩衝byte[] ....
public void readFiles(int num) {
for(int i = 0; i < num; i++) {
reader_.readFile();
}
}
//擴充套件 readString, 批次處理字串資料
public void readStrings(int num) {
for(int i = 0; i <num; i++) {
reader_.readString();
}
}
}
package com.hspedu;
import java.io.*;
public class Test_ {
public static void main(String[] args) {
BufferedReader_ bufferedReader_ = new BufferedReader_(new FileReader_());
bufferedReader_.readFiles(10);
//bufferedReader_.readFile();
//Serializable
//Externalizable
//ObjectInputStream
//ObjectOutputStream
//這次希望透過 BufferedReader_ 多次讀取字串
BufferedReader_ bufferedReader_2 = new BufferedReader_(new StringReader_());
bufferedReader_2.readStrings(5);
}
}
處理流的功能
- 效能的提高:主要以增加緩衝的方式來提高輸入輸出的效率。
- 操作的便捷:處理流可能提供了一系列便捷的方法來一次輸入輸出大批次的資料,使用更加靈活方便。
處理流 BufferedReader 和BufferedWriter
BufferedReader 和 BufferedWriter屬於字元流,是按照字元來讀取資料的
關閉時處理流,只需要關閉外層流即可(因為真正工作的是內層流,關閉時只需要關閉外層處理流,會自動關閉內層流)【原始碼】
應用案例
- 使用BufferedReader讀取文字檔案,並顯示在控制檯
package com.hspedu.reader_;
import java.io.BufferedReader;
import java.io.FileReader;
/**
* 演示bufferedReader 使用
*/
public class BufferedReader_ {
public static void main(String[] args) throws Exception {
String filePath = "e:\\a.java";
//建立bufferedReader
BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
//讀取
String line; //按行讀取, 效率高
//說明
//1. bufferedReader.readLine() 是按行讀取檔案
//2. 當返回null 時,表示檔案讀取完畢
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
//關閉流, 這裡注意,只需要關閉 BufferedReader ,因為底層會自動的去關閉 節點流
//FileReader。
/*
public void close() throws IOException {
synchronized (lock) {
if (in == null)
return;
try {
in.close();//in 就是我們傳入的 new FileReader(filePath), 關閉了.
} finally {
in = null;
cb = null;
}
}
}
*/
bufferedReader.close();
}
}
- 使用BufferedWriter將” hello,教育”,寫入到檔案中
package com.hspedu.writer_;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
/**
* 演示BufferedWriter的使用
*/
public class BufferedWriter_ {
public static void main(String[] args) throws IOException {
String filePath = "e:\\ok.txt";
//建立BufferedWriter
//說明:
//1. new FileWriter(filePath, true) 表示以追加的方式寫入
//2. new FileWriter(filePath) , 表示以覆蓋的方式寫入
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));
bufferedWriter.write("hello, 教育!");
bufferedWriter.newLine();//插入一個和系統相關的換行
bufferedWriter.write("hello2, 教育!");
bufferedWriter.newLine();
bufferedWriter.write("hello3, 教育!");
bufferedWriter.newLine();
//說明:關閉外層流即可 , 傳入的 new FileWriter(filePath) ,會在底層關閉
bufferedWriter.close();
}
}
- 綜合使用BufferedReader和 BufferedWriter完成文字檔案複製,注意檔案編碼。
package com.hspedu.writer_;
import java.io.*;
public class BufferedCopy_ {
public static void main(String[] args) {
//1. BufferedReader 和 BufferedWriter 是按照字元操作
//2. 不要去操作 二進位制檔案[聲音,影片,doc, pdf ], 可能造成檔案損壞
//BufferedInputStream
//BufferedOutputStream
String srcFilePath = "e:\\a.java";
String destFilePath = "e:\\a2.java";
// String srcFilePath = "e:\\0245_零基礎學Java_引出this.avi";
// String destFilePath = "e:\\a2.avi";
BufferedReader br = null;
BufferedWriter bw = null;
String line;
try {
br = new BufferedReader(new FileReader(srcFilePath));
bw = new BufferedWriter(new FileWriter(destFilePath));
//說明: readLine 讀取一行內容,但是沒有換行
while ((line = br.readLine()) != null) {
//每讀取一行,就寫入
bw.write(line);
//插入一個換行
bw.newLine();
}
System.out.println("複製完畢...");
} catch (IOException e) {
e.printStackTrace();
} finally {
//關閉流
try {
if(br != null) {
br.close();
}
if(bw != null) {
bw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
處理流BufferedInputStream 和BufferedOutputStream
BufferedInputStream是位元組流在建立BufferedInputStream時,會建立一個內部緩衝區陣列。
BufferedOutputStream是位元組流, 實現緩衝的輸出流, 可以將多個位元組寫入底層輸出流中,而不必對每次位元組寫入呼叫底層系統。
應用案例
要求: 程式設計完成圖片/音樂的複製(要求使用Buffered..流).
package com.hspedu.outputstream_;
import java.io.*;
/**
* 演示使用BufferedOutputStream 和 BufferedInputStream使用
* 使用他們,可以完成二進位制檔案複製.
* 思考:位元組流可以操作二進位制檔案,可以操作文字檔案嗎?當然可以
*/
public class BufferedCopy02 {
public static void main(String[] args) {
// String srcFilePath = "e:\\Koala.jpg";
// String destFilePath = "e:\\hsp.jpg";
// String srcFilePath = "e:\\0245_零基礎學Java_引出this.avi";
// String destFilePath = "e:\\hsp.avi";
String srcFilePath = "e:\\a.java";
String destFilePath = "e:\\a3.java";
//建立BufferedOutputStream物件BufferedInputStream物件
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//因為 FileInputStream 是 InputStream 子類
bis = new BufferedInputStream(new FileInputStream(srcFilePath));
bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
//迴圈的讀取檔案,並寫入到 destFilePath
byte[] buff = new byte[1024];
int readLen = 0;
//當返回 -1 時,就表示檔案讀取完畢
while ((readLen = bis.read(buff)) != -1) {
bos.write(buff, 0, readLen);
}
System.out.println("檔案複製完畢~~~");
} catch (IOException e) {
e.printStackTrace();
} finally {
//關閉流 , 關閉外層的處理流即可,底層會去關閉節點流
try {
if(bis != null) {
bis.close();
}
if(bos != null) {
bos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
物件流ObjectInputStream 和ObjectOutputStream
看一個需求:不僅需要儲存值,還需要儲存資料型別。
-
將int num = 100這個 int資料儲存到檔案中,注意不是100 數字,而是int 100,並且,能夠從檔案中直接恢復int 100
-
將Dog dog = new Dog(“小黃”,3)這個 dog物件儲存到檔案中,並且能夠從檔案恢復.
-
上面的要求,就是能夠將基本資料型別或者物件進行序列化和反序列化操作
序列化和反序列化
- 序列化就是在儲存資料時,儲存資料的值和資料型別
- 反序列化就是在恢復資料時,恢復資料的值和資料型別
- 需要讓某個物件支援序列化機制,則必須讓其類是可序列化的,為了讓某個類是可序列化的,該類必須實現如下兩個介面之一:
- Serializable //這是一個標記介面,沒有方法
- Externalizable //該介面有方法需要實現,因此我們一般實現上面的介面
物件流介紹
功能:提供了對基本型別或物件型別的序列化和反序列化的方法
- ObjectOutputStream 提供序列化功能
- ObjectInputStream 提供反序列化功能
應用案例
- 使用ObjectOutputStream序列化基本資料型別和一個Dog物件(name, age),並
儲存到data.dat檔案中。
package com.hspedu.outputstream_;
import java.io.Serializable;
// 如果需要序列化某個類的物件,實現 Serializable
public class Dog implements Serializable {
private String name;
private int age;
// 序列化物件時,預設將裡面所有屬性都進行序列化,但除了static或transient修飾的成員
private static String nation;
private transient String color;
// 序列化物件時,要求裡面屬性的型別也需要實現序列化介面
private Master master = new Master();
//serialVersionUID 序列化的版本號,可以提高相容性
private static final long serialVersionUID = 1L;
public Dog(String name, int age, String nation, String color) {
this.name = name;
this.age = age;
this.color = color;
this.nation = nation;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
'}' + nation + " " +master;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package com.hspedu.outputstream_;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* 演示ObjectOutputStream的使用, 完成資料的序列化
*/
public class ObjectOutStream_ {
public static void main(String[] args) throws Exception {
//序列化後,儲存的檔案格式,不是存文字,而是按照他的格式來儲存
String filePath = "e:\\data.dat";
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
//序列化資料到 e:\data.dat
oos.writeInt(100);// int -> Integer (Integer 實現了 Serializable)
oos.writeBoolean(true);// boolean -> Boolean (實現了 Serializable)
oos.writeChar('a');// char -> Character (實現了 Serializable)
oos.writeDouble(9.5);// double -> Double (實現了 Serializable)
oos.writeUTF("教育");//String
//儲存一個dog物件
oos.writeObject(new Dog("旺財", 10, "日本", "白色"));
oos.close();
System.out.println("資料儲存完畢(序列化形式)");
}
}
- 使用ObjectlnputStream 讀取data.dat 並反序列化恢復資料
package com.hspedu.inputstream_;
import com.hspedu.outputstream_.Dog;
import java.io.*;
public class ObjectInputStream_ {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//指定反序列化的檔案
String filePath = "e:\\data.dat";
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
//讀取
//1. 讀取(反序列化)的順序需要和你儲存資料(序列化)的順序一致,因為儲存按順序儲存什麼樣的型別,讀取的時候也就要按照相應的型別。
//2. 否則會出現異常
System.out.println(ois.readInt());
System.out.println(ois.readBoolean());
System.out.println(ois.readChar());
System.out.println(ois.readDouble());
System.out.println(ois.readUTF());
//dog 的編譯型別是 Object , dog 的執行型別是 Dog
Object dog = ois.readObject();
System.out.println("執行型別=" + dog.getClass());
System.out.println("dog資訊=" + dog);//底層 Object -> Dog
//這裡是特別重要的細節:
//1. 如果我們希望呼叫Dog的方法, 需要向下轉型
//2. 需要我們將Dog類的定義,放在到可以引用的位置
Dog dog2 = (Dog)dog;
System.out.println(dog2.getName()); //旺財..
//關閉流, 關閉外層流即可,底層會關閉 FileInputStream 流
ois.close();
}
}
package com.hspedu.outputstream_;
import java.io.Serializable;
// 如果需要序列化某個類的物件,實現 Serializable
public class Dog implements Serializable {
private String name;
private int age;
// 序列化物件時,預設將裡面所有屬性都進行序列化,但除了static或transient修飾的成員
private static String nation;
private transient String color;
// 序列化物件時,要求裡面屬性的型別也需要實現序列化介面
private Master master = new Master();
//serialVersionUID 序列化的版本號,可以提高相容性
private static final long serialVersionUID = 1L;
public Dog(String name, int age, String nation, String color) {
this.name = name;
this.age = age;
this.color = color;
this.nation = nation;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
'}' + nation + " " +master;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
注意事項和細節說明
-
讀寫順序要一致。
-
要求序列化或反序列化物件,需要實現
Serializable
。 -
序列化的類中建議新增
SerialVersionUID
,為了提高版本的相容性。當加入新屬性時,序列化和反序列化會認為是原來的修改版,而不會認為是一個全新的類。private static final long serialVersionUID = 1L;
-
序列化物件時,預設將裡面所有屬性都進行序列化,但除了static或transient修飾的成員。也就是序列化並不儲存
static
或transient
修飾的資訊。 -
序列化物件時,要求裡面屬性的型別也需要實現序列化介面。
-
序列化具備可繼承性,也就是如果某類已經實現了序列化,則它的所有子類也已經預設實現了序列化。
標準輸入輸出流
介紹
應用案例1
傳統方法System.out.println("");是使用out 物件將資料輸出到顯示器
應用案例2
傳統的方法, Scanner是從標準輸入鍵盤接收資料
package com.hspedu.standard;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Scanner;
public class InputAndOutput {
public static void main(String[] args) {
//System 類的 public final static InputStream in = null;
// System.in 編譯型別 InputStream
// System.in 執行型別 BufferedInputStream
// 表示的是標準輸入 鍵盤
System.out.println(System.in.getClass());
//1. System.out public final static PrintStream out = null;
//2. 編譯型別 PrintStream
//3. 執行型別 PrintStream
//4. 表示標準輸出 顯示器
System.out.println(System.out.getClass());
System.out.println("hello, 教育");
Scanner scanner = new Scanner(System.in);
System.out.println("輸入內容");
String next = scanner.next();
System.out.println("next=" + next);
}
}
轉換流InputStreamReader 和OutputStreamWriter
檔案亂碼問題,引出學習轉換流必要性。
可以這麼理解:相當於把InputStream轉為Reader,把OutputStream轉為Writer。
- InputStreamReader:Reader的子類,可以將InputStream(位元組流)包裝成(轉換)Reader(字元流)
- OutputStreamWriter:Writer的子類,實現將OutputStream(位元組流)
包裝成Writer(字元流) - 當處理純文字資料時,如果使用字元流效率更高,並且可以有效解決中文
問題,所以建議將位元組流轉換成字元流。 - 可以在使用時指定編碼格式(比如utf-8, gbk , gb2312,ISO8859-1等)
應用案例
1.程式設計將位元組流FilelnputStream包裝成(轉換成)字元流InputStreamReader,對
檔案進行讀取(按照utf-8/gbk格式),進而包裝成 BufferedReader
package com.hspedu.transformation;
import java.io.*;
/**
* 演示使用 InputStreamReader 轉換流解決中文亂碼問題
* 將位元組流 FileInputStream 轉成字元流 InputStreamReader, 指定編碼 gbk/utf-8
*/
public class InputStreamReader_ {
public static void main(String[] args) throws IOException {
String filePath = "e:\\a.txt";
//解讀
//1. 把 FileInputStream 轉成 InputStreamReader
//2. 指定編碼 gbk
//InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "gbk");
//3. 把 InputStreamReader 傳入 BufferedReader
//BufferedReader br = new BufferedReader(isr);
//將2 和 3 合在一起
BufferedReader br = new BufferedReader(new InputStreamReader(
new FileInputStream(filePath), "gbk"));
//4. 讀取
String s = br.readLine();
System.out.println("讀取內容=" + s);
//5. 關閉外層流
br.close();
}
}
- 程式設計將位元組流 FileOutputStream包裝成(轉換成)字元流OutputStreamWriter,對檔案進行寫入(按照gbk格式,可以指定其他,比如utf-8)
package com.hspedu.transformation;
import java.io.*;
/**
* 演示 OutputStreamWriter 使用
* 把FileOutputStream 位元組流,轉成字元流 OutputStreamWriter
* 指定處理的編碼 gbk/utf-8/utf8
*/
public class OutputStreamWriter_ {
public static void main(String[] args) throws IOException {
String filePath = "e:\\hsp.txt";
String charSet = "utf-8";
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), charSet);
osw.write("hi, 教育");
osw.close();
System.out.println("按照 " + charSet + " 儲存檔案成功~");
}
}
列印流PrintStream 和PrintWriter
列印流只有輸出流,沒有輸入流
package com.hspedu.printstream;
import java.io.IOException;
import java.io.PrintStream;
/**
* 演示PrintStream (位元組列印流/輸出流)
*/
public class PrintStream_ {
public static void main(String[] args) throws IOException {
PrintStream out = System.out;
//在預設情況下,PrintStream 輸出資料的位置是 標準輸出,即顯示器
/*
public void print(String s) {
if (s == null) {
s = "null";
}
write(s); //!!!
}
*/
out.print("john, hello");
// 因為print底層使用的是write , 所以我們可以直接呼叫write進行列印/輸出
out.write("你好".getBytes());
out.close();
//我們可以去修改列印流輸出的位置/裝置
//1. 輸出修改成到 "e:\\f1.txt"
//2. "hello, 教育~" 就會輸出到 e:\f1.txt
//3. 原始碼:
// public static void setOut(PrintStream out) {
// checkIO();
// setOut0(out); // native 方法,修改了out
// }
System.setOut(new PrintStream("e:\\f1.txt"));
System.out.println("hello, 教育~");
}
}
package com.hspedu.transformation;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 演示 PrintWriter 使用方式
*/
public class PrintWriter_ {
public static void main(String[] args) throws IOException {
//PrintWriter printWriter = new PrintWriter(System.out); // 輸出到顯示器
PrintWriter printWriter = new PrintWriter(new FileWriter("e:\\f2.txt"));
printWriter.print("hi, 北京你好~~~~");
printWriter.close();//flush + 關閉流, 才會將資料寫入到檔案..
}
}
Properties 類
看一個需求
如下一個配置檔案mysql.properties
- ip=192.168.0.13
- user=root
- pwd=12345
請問程式設計讀取ip. user 和pwd的值是多少
分析
- 傳統的方法
- 使用Properties類可以方便實現
package com.hspedu.properties_;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class Properties01 {
public static void main(String[] args) throws IOException {
//讀取mysql.properties 檔案,並得到ip, user 和 pwd
BufferedReader br = new BufferedReader(new FileReader("src\\mysql.properties"));
String line = "";
while ((line = br.readLine()) != null) { //迴圈讀取
String[] split = line.split("=");
//如果我們要求指定的ip值
if("ip".equals(split[0])) {
System.out.println(split[0] + "值是: " + split[1]);
}
}
br.close();
}
}
基本介紹
-
專門用於讀寫配置檔案的集合類
配置檔案的格式:
鍵=值
鍵=值
-
注意:鍵值對不需要有空格,值不需要用引號一起來。預設型別是String
- Properties的常見方法
- load:載入配置檔案的鍵值對到Properties物件
- list:將資料顯示到指定裝置
- getProperty(key):根據鍵獲取值
- setProperty(key,value):設定鍵值對到Properties物件
- store:將Properties中的鍵值對儲存到配置檔案,在idea中,儲存資訊到配置檔案,如果含有中文,會儲存為unicode碼
http://tool.chinaz.com/tools/unicode.aspx unicode碼查詢工具
應用案例
1.使用Properties類完成對mysql.properties的讀取,看老師程式碼演示
package com.hspedu.properties_;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
public class Properties02 {
public static void main(String[] args) throws IOException {
//使用Properties 類來讀取mysql.properties 檔案
//1. 建立Properties 物件
Properties properties = new Properties();
//2. 載入指定配置檔案
properties.load(new FileReader("src\\mysql.properties"));
//3. 把k-v顯示控制檯
properties.list(System.out);
//4. 根據key 獲取對應的值
String user = properties.getProperty("user");
String pwd = properties.getProperty("pwd");
System.out.println("使用者名稱=" + user);
System.out.println("密碼是=" + pwd);
}
}
2.使用Properties類新增key-val 到新檔案mysql2.properties中
3.使用Properties類完成對 mysq12.properties 的讀取,並修改某個key-val
package com.hspedu.properties_;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
public class Properties03 {
public static void main(String[] args) throws IOException {
//使用Properties 類來建立 配置檔案, 修改配置檔案內容
Properties properties = new Properties();
//建立
//1. 如果該檔案沒有key 就是建立!!
//2. 如果該檔案有key ,就是修改!!
/*
Properties 父類是 Hashtable , 底層就是Hashtable 核心方法
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;//如果key 存在,就替換
return old;
}
}
addEntry(hash, key, value, index);//如果是新k, 就addEntry
return null;
}
*/
properties.setProperty("charset", "utf8");
properties.setProperty("user", "湯姆");//注意儲存時,是中文的 unicode碼值
properties.setProperty("pwd", "888888");
//將k-v 儲存檔案中即可
properties.store(new FileOutputStream("src\\mysql2.properties"), null);
System.out.println("儲存配置檔案成功~");
}
}
本章作業
1.程式設計題
(1)在判斷e盤下是否有資料夾mytemp ,如果沒有就建立mytemp
(2)在e:llmytemp目錄下,建立檔案hello.txt
(3)如果hello.txt已經存在,提示該檔案已經存在,就不要再重複建立了
(4)並且在hello.txt檔案中,寫入hello,world~
package com.hspedu.homework;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class Homework01 {
public static void main(String[] args) throws IOException {
/**
*(1) 在判斷 e 盤下是否有資料夾mytemp ,如果沒有就建立mytemp
*(2) 在e:\\mytemp 目錄下, 建立檔案 hello.txt
*(3) 如果hello.txt 已經存在,提示該檔案已經存在,就不要再重複建立了
*(4) 並且在hello.txt 檔案中,寫入 hello,world~
*/
String directoryPath = "e:\\mytemp";
File file = new File(directoryPath);
if(!file.exists()) {
//建立
if(file.mkdirs()) {
System.out.println("建立 " + directoryPath + " 建立成功" );
}else {
System.out.println("建立 " + directoryPath + " 建立失敗");
}
}
String filePath = directoryPath + "\\hello.txt";// e:\mytemp\hello.txt
file = new File(filePath);
if(!file.exists()) {
//建立檔案
if(file.createNewFile()) {
System.out.println(filePath + " 建立成功~");
//如果檔案存在,我們就使用BufferedWriter 字元輸入流寫入內容
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file));
bufferedWriter.write("hello, world~~ 教育");
bufferedWriter.close();
} else {
System.out.println(filePath + " 建立失敗~");
}
} else {
//如果檔案已經存在,給出提示資訊
System.out.println(filePath + " 已經存在,不在重複建立...");
}
}
}
2.程式設計題
要求:使用BufferedReader讀取一個文字檔案,為每行加上行號,再連同內容一併輸出到螢幕上。
//如果把檔案的編碼改成了 gbk,出現中文亂碼,大家思考如何解決
//1.預設是按照utf-8處理,開始沒有亂碼
//2.提示:使用我們的轉換流,將FilelnputStream -> InputStreamReader[可以指定編碼]- >BufferedReader ...
package com.hspedu.homework;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class Homework02 {
public static void main(String[] args) {
/**
* 要求: 使用BufferedReader讀取一個文字檔案,為每行加上行號,
* 再連同內容一併輸出到螢幕上。
*/
String filePath = "e:\\a.txt";
BufferedReader br = null;
String line = "";
int lineNum = 0;
try {
br = new BufferedReader(new FileReader(filePath));
while ((line = br.readLine()) != null) { //迴圈讀取
System.out.println(++lineNum + line);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3.程式設計題
(1)要編寫一個dog.properties
- name=tom
- age=5
- color=red
(2)編寫Dog 類(name,age,color)建立一個dog物件,讀取dog.properties用相應的內容完成屬性初始化,並輸出
(3)將建立的Dog物件,序列化到檔案dog.dat 檔案
package com.hspedu.homework;
import org.junit.jupiter.api.Test;
import java.io.*;
import java.util.Properties;
public class Homework03 {
public static void main(String[] args) throws IOException {
/**
* (1) 要編寫一個dog.properties name=tom age=5 color=red
* (2) 編寫 Dog 類(name,age,color) 建立一個dog物件,讀取dog.properties 用相應的內容完成屬性初始化, 並輸出
* (3) 將建立的Dog 物件 ,序列化到 檔案 e:\\dog.dat 檔案
*/
String filePath = "src\\dog.properties";
Properties properties = new Properties();
properties.load(new FileReader(filePath));
String name = properties.get("name") + ""; //Object -> String
int age = Integer.parseInt(properties.get("age") + "");// Object -> int
String color = properties.get("color") + "";//Object -> String
Dog dog = new Dog(name, age, color);
System.out.println("===dog物件資訊====");
System.out.println(dog);
//將建立的Dog 物件 ,序列化到 檔案 dog.dat 檔案
String serFilePath = "e:\\dog.dat";
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(serFilePath));
oos.writeObject(dog);
//關閉流
oos.close();
System.out.println("dog物件,序列化完成...");
}
//在編寫一個方法,反序列化dog
@Test
public void m1() throws IOException, ClassNotFoundException {
String serFilePath = "e:\\dog.dat";
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(serFilePath));
Dog dog = (Dog)ois.readObject();
System.out.println("===反序列化後 dog====");
System.out.println(dog);
ois.close();
}
}
class Dog implements Serializable{
private String name;
private int age;
private String color;
public Dog(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
'}';
}
}