day20(IO(位元組流))
###20.01_IO流(IO流概述及其分類)
* 1.概念
* IO流用來處理裝置之間的資料傳輸
* Java對資料的操作是通過流的方式
* Java用於操作流的類都在IO包中
* 流按流向分為兩種:輸入流,輸出流。
* 流按操作型別分為兩種:
* 位元組流 : 位元組流可以操作任何資料,因為在計算機中任何資料都是以位元組的形式儲存的
* 字元流 : 字元流只能操作純字元資料,比較方便。
* 2.IO流常用父類
* 位元組流的抽象父類:
* InputStream
* OutputStream
* 字元流的抽象父類:
* Reader
* Writer
* 3.IO程式書寫
* 使用前,匯入IO包中的類
* 使用時,進行IO異常處理
* 使用後,釋放資源
###20.02_IO流(FileInputStream)
* read()一次讀取一個位元組
*
FileInputStream fis = new FileInputStream("aaa.txt"); //建立一個檔案輸入流物件,並關聯aaa.txt
int b; //定義變數,記錄每次讀到的位元組
while((b = fis.read()) != -1) { //將每次讀到的位元組賦值給b並判斷是否是-1
System.out.println(b); //列印每一個位元組
}
fis.close(); //關閉流釋放資源
###20.03_IO流(read()方法返回值為什麼是int)
* read()方法讀取的是一個位元組,為什麼返回是int,而不是byte
*
因為位元組輸入流可以操作任意型別的檔案,比如圖片音訊等,這些檔案底層都是以二進位制形式的儲存的,如果每次讀取都返回byte,有可能在讀到中間的時候遇到111111111
那麼這11111111是byte型別的-1,我們的程式是遇到-1就會停止不讀了,後面的資料就讀不到了,所以在讀取的時候用int型別接收,如果11111111會在其前面補上
24個0湊足4個位元組,那麼byte型別的-1就變成int型別的255了這樣可以保證整個資料讀完,而結束標記的-1就是int型別
* @throws IOException
* read()方法讀取的是一個位元組,為什麼返回是int,而不是byte
*
* 00010100 00100100 01000001 11111111 0000000
*
* 10000001 byte型別-1的原碼
* 11111110 -1的反碼
* 11111111 -1的補碼
*
* 00000000 00000000 00000000 11111111
###20.04_IO流(FileOutputStream)
* write()一次寫出一個位元組
*
FileOutputStream fos = new FileOutputStream("bbb.txt"); //如果沒有bbb.txt,會創建出一個
//fos.write(97); //雖然寫出的是一個int數,但是在寫出的時候會將前面的24個0去掉,所以寫出的是一個byte
fos.write(98);
fos.write(99);
fos.close();
###20.05_IO流(FileOutputStream追加)
* A:案例演示
* FileOutputStream的構造方法寫出資料如何實現資料的追加寫入
*
FileOutputStream fos = new FileOutputStream("bbb.txt",true); //如果沒有bbb.txt,會創建出一個
//fos.write(97); //雖然寫出的是一個int數,但是在寫出的時候會將前面的24個0去掉,所以寫出的一個byte
fos.write(98);
fos.write(99);
fos.close();
###20.06_IO流(拷貝圖片)
* FileInputStream讀取
* FileOutputStream寫出
FileInputStream fis = new FileInputStream("致青春.mp3"); //建立輸入流物件,關聯致青春.mp3
FileOutputStream fos = new FileOutputStream("copy.mp3");//建立輸出流物件,關聯copy.mp3
int b;
while((b = fis.read()) != -1) {
fos.write(b);
}
fis.close();
fos.close();
案例:
public static void main(String[] args) throws IOException {
//demo1();
demo2();
}
//拷貝mp3音樂
public static void demo2() throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("致青春.mp3"); //建立輸入流物件,關聯致青春.mp3
FileOutputStream fos = new FileOutputStream("copy.mp3"); //建立輸出流物件,關聯copy.mp3
int b;
while((b = fis.read()) != -1) { //在不斷的讀取每一個位元組
fos.write(b); //將每一個位元組寫出
}
fis.close(); //關流釋放資源
fos.close();
}
//拷貝圖片
public static void demo1() throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("雙元.jpg"); //建立輸入流物件,關聯雙元.jpg
FileOutputStream fos = new FileOutputStream("copy.jpg"); //建立輸出流物件,關聯copy.jpg
int b;
while((b = fis.read()) != -1) { //在不斷的讀取每一個位元組
fos.write(b); //將每一個位元組寫出
}
fis.close(); //關流釋放資源
fos.close();
}
###20.07_IO流(拷貝音訊檔案畫原理圖)
* A:案例演示
* 位元組流一次讀寫一個位元組複製音訊
* 弊端:效率太低
###20.08_IO流(位元組陣列拷貝之available()方法)
* A:案例演示
* int read(byte[] b):一次讀取一個位元組陣列
* write(byte[] b):一次寫出一個位元組陣列
* available()獲取讀的檔案所有的位元組個數
* 弊端:有可能會記憶體溢位
FileInputStream fis = new FileInputStream("致青春.mp3");
FileOutputStream fos = new FileOutputStream("copy.mp3");
byte[] arr = new byte[fis.available()]; //根據檔案大小做一個位元組陣列
fis.read(arr); //將檔案上的所有位元組讀取到陣列中
fos.write(arr); //將陣列中的所有位元組一次寫到了檔案上
fis.close();
fos.close();
public static void main(String[] args) throws IOException {
demo3();
}
public static void demo3() throws FileNotFoundException, IOException {
//第二種拷貝,不推薦使用,因為有可能會導致記憶體溢位
FileInputStream fis = new FileInputStream("致青春.mp3"); //建立輸入流物件,關聯致青春.mp3
FileOutputStream fos = new FileOutputStream("copy.mp3"); //建立輸出流物件,關聯copy.mp3
//int len = fis.available();
//System.out.println(len);
byte[] arr = new byte[fis.available()]; //建立與檔案一樣大小的位元組陣列
fis.read(arr); //將檔案上的位元組讀取到記憶體中
fos.write(arr); //將位元組陣列中的位元組資料寫到檔案上
fis.close();
fos.close();
}
###20.09_IO流(定義小陣列)
* write(byte[] b)
* write(byte[] b, int off, int len)寫出有效的位元組個數
public static void main(String[] args) throws IOException {
//demo1();
demo2();
}
public static void demo2() throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("xxx.txt");
FileOutputStream fos = new FileOutputStream("yyy.txt");
byte[] arr = new byte[2];
int len;
while((len = fis.read(arr)) != -1) {
fos.write(arr,0,len);
}
fis.close();
fos.close();
}
public static void demo1() throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("xxx.txt");
byte[] arr = new byte[2];
int a = fis.read(arr); //將檔案上的位元組讀取到位元組陣列中
System.out.println(a); //讀到的有效位元組個數
for (byte b : arr) { //第一次獲取到檔案上的a和b
System.out.println(b);
}
System.out.println("-----------------------");
int c = fis.read(arr);
System.out.println(c);
for (byte b : arr) {
System.out.println(b);
}
fis.close();
}
###20.10_IO流(定義小陣列的標準格式)
* A:案例演示
* 位元組流一次讀寫一個位元組陣列複製圖片和視訊
FileInputStream fis = new FileInputStream("致青春.mp3");
FileOutputStream fos = new FileOutputStream("copy.mp3");
int len;
byte[] arr = new byte[1024 * 8]; //自定義位元組陣列
while((len = fis.read(arr)) != -1) {
//fos.write(arr);
fos.write(arr, 0, len); //寫出位元組陣列寫出有效個位元組個數
}
fis.close();
fos.close();
###20.11_IO流(BufferedInputStream和BufferOutputStream拷貝)
* A:緩衝思想
* 位元組流一次讀寫一個數組的速度明顯比一次讀寫一個位元組的速度快很多,
* 這是加入了陣列這樣的緩衝區效果,java本身在設計的時候,
* 也考慮到了這樣的設計思想(裝飾設計模式後面講解),所以提供了位元組緩衝區流
* B.BufferedInputStream
* BufferedInputStream內建了一個緩衝區(陣列)
* 從BufferedInputStream中讀取一個位元組時
* BufferedInputStream會一次性從檔案中讀取8192個, 存在緩衝區中, 返回給程式一個
* 程式再次讀取時, 就不用找檔案了, 直接從緩衝區中獲取
* 直到緩衝區中所有的都被使用過, 才重新從檔案中讀取8192個
* C.BufferedOutputStream
* BufferedOutputStream也內建了一個緩衝區(陣列)
* 程式向流中寫出位元組時, 不會直接寫到檔案, 先寫到緩衝區中
* 直到緩衝區寫滿, BufferedOutputStream才會把緩衝區中的資料一次性寫到檔案裡
* D.拷貝的程式碼
FileInputStream fis = new FileInputStream("致青春.mp3"); //建立檔案輸入流物件,關聯致青春.mp3
BufferedInputStream bis = new BufferedInputStream(fis); //建立緩衝區對fis裝飾
FileOutputStream fos = new FileOutputStream("copy.mp3"); //建立輸出流物件,關聯copy.mp3
BufferedOutputStream bos = new BufferedOutputStream(fos); //建立緩衝區對fos裝飾
int b;
while((b = bis.read()) != -1) {
bos.write(b);
}
bis.close(); //只關裝飾後的物件即可
bos.close();
* E.小陣列的讀寫和帶Buffered的讀取哪個更快?
* 定義小陣列如果是8192個位元組大小和Buffered比較的話
* 定義小陣列會略勝一籌,因為讀和寫操作的是同一個陣列
* 而Buffered操作的是兩個陣列
###20.12_IO流(flush和close方法的區別)
* flush()方法
* 用來重新整理緩衝區的,重新整理後可以再次寫出
* close()方法
* 用來關閉流釋放資源的的,如果是帶緩衝區的流物件的close()方法,不但會關閉流,還會再關閉流之前重新整理緩衝區,關閉後不能再寫出
public static void main(String[] args) throws IOException {
//flush和close方法的區別
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("致青春.mp3"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.mp3"));
int b;
//只要裝滿了,會自動重新整理,只需要重新整理最後寫入的一部分
while((b = bis.read()) != -1) {
bos.write(b);
//bos.flush();
}
//bos.flush();
bis.close();
bos.close();
}
###20.13_IO流(位元組流讀寫中文)
* 位元組流讀取中文的問題
* 位元組流在讀中文的時候有可能會讀到半個中文,造成亂碼
* 位元組流寫出中文的問題
* 位元組流直接操作的位元組,所以寫出中文必須將字串轉換成位元組陣列
* 寫出回車換行 write("\r\n".getBytes());
public static void main(String[] args) throws IOException {
//demo1();
FileOutputStream fos = new FileOutputStream("zzz.txt");
fos.write("我讀書少,你不要騙我".getBytes());
fos.write("\r\n".getBytes());
fos.close();
}
public static void demo1() throws FileNotFoundException, IOException {
//yyy.txt採用gbk編碼
//假設yyy.txt中存放"你好你好"
FileInputStream fis = new FileInputStream("yyy.txt");
//byte[] arr = new byte[3];
byte[] arr = new byte[4];
int len;
while((len = fis.read(arr)) != -1) {
System.out.println(new String(arr,0,len));
}
fis.close();
}
###20.14_IO流(流的標準處理異常程式碼1.6版本及其以前)
* try finally巢狀
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("aaa.txt");
fos = new FileOutputStream("bbb.txt");
int b;
while((b = fis.read()) != -1) {
fos.write(b);
}
} finally {
try {
if(fis != null)
fis.close();
}finally {
if(fos != null)
fos.close();
}
}
###20.15_IO流(流的標準處理異常程式碼1.7版本)
* try close
//當把流物件寫在小括號裡時,執行完大括號中的讀寫的程式碼後,會自動關流
try(//只有具備了自動關閉功能的物件才能寫在這裡
FileInputStream fis = new FileInputStream("aaa.txt");
FileOutputStream fos = new FileOutputStream("bbb.txt");
MyClose mc = new MyClose();
){
int b;
while((b = fis.read()) != -1) {
fos.write(b);
}
}
* 原理
* 在try()中建立的流物件必須實現了AutoCloseable這個介面,如果實現了,在try後面的{}(讀寫程式碼)執行後就會自動呼叫,流物件的close方法將流關掉
###20.16_IO流(圖片加密)
* 給圖片加密
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.jpg"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.jpg"));
int b;
while((b = bis.read()) != -1) {
bos.write(b ^ 123);
}
bis.close();
bos.close();
###20.17_IO流(拷貝檔案)
* 在控制檯錄入檔案的路徑,將檔案拷貝到當前專案下
分析:
* 1,定義方法對鍵盤錄入的路徑進行判斷,如果是檔案就返回
* 2,在主方法中接收該檔案
* 3,讀和寫該檔案
Scanner sc = new Scanner(System.in);
System.out.println("請輸入一個檔案路徑");
String line = sc.nextLine(); //將鍵盤錄入的檔案路徑儲存在line中
File file = new File(line); //封裝成File物件
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(file.getName());
int len;
byte[] arr = new byte[8192]; //定義緩衝區
while((len = fis.read(arr)) != -1) {
fos.write(arr,0,len);
}
fis.close();
fos.close();
###20.18_IO流(錄入資料拷貝到檔案)
* 將鍵盤錄入的資料拷貝到當前專案下的text.txt檔案中,鍵盤錄入資料當遇到quit時就退出
分析:
* 1,建立鍵盤錄入物件
* 2,建立輸出流物件,關聯text.txt檔案
* 3,定義無限迴圈
* 4,遇到quit退出迴圈
* 5,如果不quit,就將內容寫出
* 6,關閉流
public static void main(String[] args) throws IOException {
//1,建立鍵盤錄入物件
Scanner sc = new Scanner(System.in);
//2,建立輸出流物件,關聯text.txt檔案
FileOutputStream fos = new FileOutputStream("text.txt");
System.out.println("請輸入資料:");
//3,定義無限迴圈
while(true) {
String line = sc.nextLine(); //將鍵盤錄入的資料儲存在line中
//4,遇到quit退出迴圈
if("quit".equals(line)) {
break;
}
//5,如果不quit,就將內容寫出
fos.write(line.getBytes()); //字串寫出必須轉換成位元組陣列
fos.write("\r\n".getBytes());
}
//6,關閉流
fos.close();
}