IO流(上)
一、概述:
IO流:即Input、Output流,輸入、輸出流。在java中,對數據的操作通過流的形式完成;流可以按照操作對象的不同分爲字符流(專門用於操作純文本文件)和字節流(非純文本文件)兩種,也可以按照流向的不同分爲輸入流和輸出流,這裏的輸入和輸出都是相對於內存而言的。
總之:IO流專門用來處理設備之間的數據傳輸;字符流=字節流+默認的編碼。
本章要介紹的字符流類如下圖所示:
本章要介紹的字節流類如下圖所示:
二、字符流
字符流有默認的編碼,一般用於操作純文本的數據。
字符流的兩個抽象基類:Reader(讀取數據),Writer(寫出數據,內存寫到外設中);這兩個抽象基類提供了對於字符流操作的基本方法,但有些方法是抽象的,必須由子類去覆蓋。一切操作字符流的類都直接或者間接繼承Reader、Writer抽象基類;這些子類的後綴是父類名稱,前綴是具體的功能,例如:FileReader、BufferedReader、FileWriter、BufferedWriter等等。
既然IO流用於操作數據,那麼數據的最常見體現形式就是:文件。在IO流中,有專門的類作文件數據的流對象,這兩個類就是FileReader、FileWriter。
flush()方法:調用write方法,只是把數據寫入到流中;而flush方法用於將流中的數據刷到目的文件中;注意:讀取流沒有刷新這種說法。
爲了能更加清晰、更加深刻的理解,下面主要以代碼的形式介紹:
往磁盤文件中寫入數據:
package itheima.day18;
import java.io.FileWriter;
import java.io.IOException;
//需求:在硬盤上,創建一個文件並寫入一些文字數據
public class FileWriterDemo {
public static void main(String[] args) {
FileWriter fw = null;
try {
// append爲true時,不覆蓋原文件
fw = new FileWriter("D:\\java\\IODemoFile\\demo.txt"/*,true*/);
fw.write("堅韌的民族主義者---蔣中正;");
fw.flush();
fw.write("抗日英雄----李宗仁;");
fw.write("當代小諸葛---白崇禧;");
fw.flush();
} catch (IOException e) {
throw new RuntimeException("文件讀寫失敗");
}finally{
try {
if(fw!=null)
fw.close();
} catch (IOException e) {
throw new RuntimeException("文件關閉失敗");
}
}
}
}
1、讀取一個.java文件,並打印到控制檯上:
package itheima.day18;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest {
public static void main(String[] args) {
FileReader fr = null;
try {
fr = new FileReader("D:\\java\\workspace1\\JavaFoundation\\src\\itheima\\day18\\FileReaderTest.java");
char[] buf = new char[1024];
int num=0;
while((num=fr.read(buf))!=-1){
System.out.print(new String(buf,0,num));
}
} catch (FileNotFoundException e) {
throw new RuntimeException("文件未找到異常!");
} catch (IOException e) {
throw new RuntimeException("IO讀寫異常!");
}finally{
try {
if(fr!=null)
fr.close();
} catch (IOException e) {
throw new RuntimeException("文件關閉失敗!");
}
}
}
}
2、複製一個文件:
package itheima.day18;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
//需求:將c盤一個文件複製到D盤(主要是演示文件的複製)
// 複製原理:其實就是將C盤下的文件數據存儲到D盤的一個文件中。
// 步驟:
// 1、在D盤創建一個文件,用於存儲c盤文件中的數據
// 2、定義讀取流和C盤文件關聯
// 3、通過不斷的讀寫完成數據的存儲
public class CopyTest {
public static void main(String[] args) {
copy_2();
}
public static void copy_1(){
FileWriter fw =null;
FileReader fr = null;
try {
fw = new FileWriter("D:\\java\\IODemoFile\\CopyTest.java");
fr = new FileReader("D:\\java\\workspace1\\JavaFoundation\\src\\itheima\\day18\\CopyTest.java");
int ch =0;
while((ch =fr.read())!=-1){
fw.write((char)ch);
fw.flush();
}
} catch (FileNotFoundException e) {
throw new RuntimeException("文件未找到異常!");
} catch (IOException e) {
throw new RuntimeException("IO讀取異常!");
}finally{
try {
if(fw!=null)
fw.close();
} catch (IOException e) {
throw new RuntimeException("寫入文件關閉失敗!");
}
try {
if(fr!=null)
fr.close();
} catch (IOException e) {
throw new RuntimeException("讀取文件關閉異常!");
}
}
}
public static void copy_2(){
FileWriter fw =null;
FileReader fr = null;
try {
fw = new FileWriter("D:\\java\\IODemoFile\\CopyTest.java");
fr = new FileReader("D:\\java\\workspace1\\JavaFoundation\\src\\itheima\\day18\\CopyTest.java");
char[] buf = new char[1024];
int len =0;
while((len =fr.read(buf))!=-1){
fw.write(buf,0,len);
fw.flush();
}
} catch (FileNotFoundException e) {
throw new RuntimeException("文件未找到異常!");
} catch (IOException e) {
throw new RuntimeException("IO讀寫異常!");
}finally{
try {
if(fw!=null)
fw.close();
} catch (IOException e) {
throw new RuntimeException("寫入文件關閉失敗!");
}
try {
if(fr!=null)
fr.close();
} catch (IOException e) {
throw new RuntimeException("讀取文件關閉失敗!");
}
}
}
}
字符流的緩衝區:緩衝區的出現是爲了提高對數據的讀寫效率;對應類:BufferedReader、BufferedWriter,注意:緩衝區只是在流的基礎上對流的功能進行增強,所以必須首先有流對象。
BufferedWriter:將文本寫入字符輸出流,緩衝各個字符,從而提供單個字符、數組和字符串的高效寫入,最重要的增加了newLine()方法。
BufferedReader:從字符輸入流中讀取文本,緩衝各個字符,從而實現字符、數組和行的高效讀取,最重要的是增加了readLine()方法。
void newLine(): 寫入一個行分隔符。
String readLine():讀取一個文本行,只返回數據,不返回回車符。
readLine()方法的原理:無論是讀一行、獲取多個字符,還是讀取一個字符,其實最終都是在硬盤上一個一個的讀取。所以readLine()方法最終使用的還是read()一次讀一個的方法。
3、通過緩衝區複製一個.java文件:
package itheima.day19;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
//通過緩衝區複製一個.java文件
public class CopyTextByBuffered {
public static void main(String[] args) {
BufferedReader bufr = null;
BufferedWriter bufw = null;
try {
bufr =new BufferedReader(
new FileReader(
"D:\\java\\workspace1\\JavaFoundation\\src\\itheima\\day19\\CopyTextByBuffered.java"));
bufw = new BufferedWriter(
new FileWriter("D:\\java\\IODemoFile\\CopyTestByBuf.java"));
String line =null;
while((line = bufr.readLine())!=null){
System.out.println(line);
bufw.write(line);
bufw.newLine();
bufw.flush();
}
} catch (IOException e) {
throw new RuntimeException("文件讀取失敗!");
}finally{
try {
if(bufr!=null)
bufr.close();
}catch (IOException e) {
throw new RuntimeException("文件讀取關閉失敗!");
}
try {
if(bufw!=null)
bufw.close();
}catch (IOException e) {
throw new RuntimeException("文件寫出關閉失敗!");
}
}
System.out.println("複製完成!!!");
}
}
裝飾設計模式:當想要對已有的對象進行功能增強時,可以定義一個類,將已有對象傳入,基於已有的功能,並提供加強功能;那麼自定義的該類成爲裝飾類。裝飾類通常會通過構造方法接收被裝飾的對象,並基於被裝飾對象的功能,提供更強的功能。
裝飾設計模式比繼承更加靈活,不僅避免了繼承體系的臃腫,還降低了類與類之間的耦合關係。裝飾類因爲只是增強已有對象,具備的功能和已有對象的功能是相同的,只不過提供了更強的功能,所以裝飾類和被裝飾類通常都屬於同一個體系中。
4、模擬帶有ReadLine功能的BufferedReader:
package itheima.day19;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
// 定義MyBufferedReader,myReadLine方法
public class MyBufferedReaderDemo
{
public static void main(String[] args) {
MyBufferedReader myBuf = null;
try {
FileReader fr = new FileReader("D:\\java\\IODemoFile\\CopyTestByBuf.java");
myBuf = new MyBufferedReader(fr);
String line = null;
while((line = myBuf.myReadLine())!=null){
System.out.println(line);
}
} catch (FileNotFoundException e) {
throw new RuntimeException("要讀取的文件不存在!");
} catch (IOException e) {
throw new RuntimeException("文件讀寫失敗!");
}finally{
try {
if(myBuf!=null)
myBuf.myClose();
} catch (IOException e) {
System.out.println(e.toString());
}
}
}
}
class MyBufferedReader extends Reader
{
private Reader r;
MyBufferedReader(Reader r){
this.r = r;
}
public String myReadLine() throws IOException{
.
StringBuilder sb = new StringBuilder();
int ch =0;
while((ch =r.read())!= -1){
if(ch =='\r')
continue;
if(ch =='\n')
return sb.toString();
sb.append((char)ch);
}
if(sb.length()!=0)
return sb.toString();
return null;
}
public void myClose() throws IOException{
r.close();
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
return r.read(cbuf,off,len);
}
@Override
public void close() throws IOException {
r.close();
}
}
5、模擬帶有行號功能的LineNumberReader:
package itheima.day19;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import itheima.day19.MyBufferedReader;
public class MyLineNumberReaderDemo {
public static void main(String[] args) {
MyLineNumberReader mylnr = null;
try {
FileReader fr = new FileReader("src\\itheima\\day19\\LineNumberReaderDemo.java");
mylnr = new MyLineNumberReader(fr);
String line = null;
mylnr.setLineNumber(200);
while((line = mylnr.myReadLine())!=null){
System.out.println(mylnr.getLineNumber()+"::"+line);
}
} catch (FileNotFoundException e) {
throw new RuntimeException("文件未找到異常!");
} catch (IOException e) {
throw new RuntimeException("文件讀寫異常!");
}finally{
try {
if(mylnr!=null)
mylnr.myClose();
} catch (IOException e) {
throw new RuntimeException("文件關閉失敗!");
}
}
}
}
class MyLineNumberReader extends MyBufferedReader
{
private int lineNumber;
MyLineNumberReader(Reader r){
super(r);
}
public String myReadLine() throws IOException{
lineNumber++;
return super.myReadLine();
}
public void setLineNumber(int lineNumber){
this.lineNumber = lineNumber;
}
public int getLineNumber(){
return lineNumber;
}
}
三、字節流
字符流用於操作純文本文件,但在計算機中有些文件不是文本類型的,比如:圖片、mp3、電影等等,這時就要用到字節流操作。字節流的基本操作與字符流相同,但它不僅可以操作字符,還可以操作其他媒體文件。
字節流的兩個抽象基類:InputStream:,輸入流,相對於內存而言,數據流入到內存中,讀取;OutputStream:輸出流,數據從內存流出到外設中,寫出。
OutputStream類中也有write方法,可是使用的是字節數組,而字符流用的是字符數組。
帶緩衝區的字節流的讀取原理如下圖:
6、複製圖片:
package itheima.day19;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
//複製一個圖片
public class CopyPic {
public static void main(String[] args) {
copyPicture();
}
public static void copyPicture(){
FileOutputStream fos = null;
FileInputStream fis = null;
try {
fis = new FileInputStream("抗日英雄:李宗仁.jpg");
fos = new FileOutputStream("D:\\java\\IODemoFile\\李宗仁:臺兒莊.jpg");
fos.write("abcd".getBytes());
byte[] buf = new byte[1024];
int len =0;
while((len = fis.read(buf))!=-1){
fos.write(buf,0,len);
}
} catch (FileNotFoundException e) {
throw new RuntimeException("文件未找到異常!");
} catch (IOException e) {
throw new RuntimeException("文件讀寫異常!");
}finally{
try {
if(fos!=null)
fos.close();
} catch (IOException e) {
throw new RuntimeException("寫入文件關閉失敗!");
}
try {
if(fis!=null)
fis.close();
} catch (IOException e) {
throw new RuntimeException("讀取文件關閉失敗!");
}
}
}
}
7、複製mp3:
package itheima.day19;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
//複製mp3,通過緩衝區
public class CopyMP3 {
public static void main(String[] args) {
long start = System.currentTimeMillis();
copy_1();
long end = System.currentTimeMillis();
System.out.println(end - start+"毫秒");
}
public static void copy_1(){
BufferedInputStream bufis = null;
BufferedOutputStream bufos = null;
try {
bufis =
new BufferedInputStream(
new FileInputStream(
"D:\\靜心--佛家歌曲\\王菲《心經》第三屆世界佛教論壇.mp3"));
bufos =
new BufferedOutputStream(
new FileOutputStream("D:\\java\\IODemoFile\\心經.mp3"));
int by =0;
while((by = bufis.read())!=-1){
bufos.write(by);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(bufos!=null)
bufos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(bufis!=null)
bufis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void copy_2(){
MyBufferedInputStream bufis = null;
BufferedOutputStream bufos = null;
try {
bufis = new MyBufferedInputStream(
new FileInputStream("D:\\靜心--佛家歌曲\\王菲《心經》第三屆世界佛教論壇.mp3"));
bufos = new BufferedOutputStream(
new FileOutputStream("D:\\java\\IODemoFile\\心經11.mp3"));
int by =0;
while((by = bufis.myRead())!=-1){
bufos.write(by);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(bufos!=null)
bufos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(bufis!=null)
bufis.myClose();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//模擬BufferedInputStream類
//字節流緩衝區的基本原理:
class MyBufferedInputStream{
private InputStream in;
private byte[] buf = new byte[1024];
private int pos =0 ,count = 0;
MyBufferedInputStream(InputStream in){
this.in = in;
}
// 爲何返回int 而不是byte?
// byte -1 ---> int -1(自動類型提升)
// 1111 1111 --> 1111....1111(32)位
// 很有可能流會讀到連續八個1,返回byte類型的-1,會與結束標識混淆!
public int myRead() throws IOException{
// 先將數據讀取到字節數組buf中
if(count ==0){
count = in.read(buf);
if(count<0)
<span style="white-space:pre"> </span> return -1;
// 一個一個讀取,指針向前,數量減少
pos =0;
byte b= buf[pos];
count--;
pos++;
return b&0xff;
}
else if(count>0){
byte b = buf[pos];
count--;
pos++;
return b&0xff;
}
return -1;
}
public void myClose() throws IOException{
in.close();
}
}
四、轉換流
當字節流中的數據都是字符時,轉成字符流操作更加高效。轉換流就是字符流與字節流之間的橋樑,主要是方便字符流與字節流之間的操作。因爲有了字符流之後才需要轉換,所以轉換流定義在字符流體系中。
InputStreamReader:字節流通向字符流的橋樑;
OutputStreamWriter:字符流通向字節流的橋樑。
標準輸入輸出流:在System類中,有兩個字段:in、out,它們分別代表系統標準的輸入和輸出設備;默認的輸入設備對應的是鍵盤,輸出設備對應的是控制檯。System.in的類型是InputStream,System.out的類型是PrintStream;PrintStream是OutputStream的子類。可以通過System.setIn();System.setOut()改變標準輸入輸出設備。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
8、讀取鍵盤錄入:
package itheima.day19;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
//讀取鍵盤錄入。
// System.out:對應的設備是標準輸出設備,控制檯。
// System.in:對應的設備是標準輸入設備,鍵盤。
//需求:通過鍵盤錄入數據,
// 當錄入一行數據後,就將改行數據打印
// over,停止錄入。
public class ReadIn {
public static void main(String[] args) throws IOException {
System.setIn(new FileInputStream("src\\itheima\\day19\\ReadIn.java"));
<span style="white-space:pre"> </span> System.setOut(new PrintStream("zz.txt"));
InputStream in = System.in;
InputStreamReader isr = new InputStreamReader(in);
BufferedReader bufr = new BufferedReader(isr);
OutputStream out = System.out;
OutputStreamWriter osw = new OutputStreamWriter(out);
BufferedWriter bufw = new BufferedWriter(osw);
String line = null;
while((line = bufr.readLine())!=null){
if("over".equalsIgnoreCase(line))
break;
// System.out.println(line.toUpperCase());
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
bufr.close();
}
public static void method_1() throws IOException{
InputStream in = System.in;
StringBuilder sb = new StringBuilder();
while(true){
int ch = in.read();
if(ch=='\r')
continue;
if(ch=='\n'){
String s = sb.toString();
if("over".equals(s))
break;
System.out.println(s.toUpperCase());
sb.delete(0, sb.length());
}
else{
sb.append((char)ch);
}
}
}
}
9、異常信息的日誌存儲:
package itheima.day19;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;
//異常的日誌信息
public class ExceptionInfoDemo {
public static void main(String[] args) {
try {
int[] arr = new int[2];
System.out.println(arr[3]);
} catch (Exception e) {
try {
// e.printStackTrace(new PrintStream("a.txt"));
// log4j 專門處理異常日誌的工具包
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
String s = sdf.format(d);
PrintStream ps =new PrintStream("exception.log");
ps.println(s);
System.setOut(ps);
e.printStackTrace(System.out);
} catch (FileNotFoundException e1) {
throw new RuntimeException("日誌文件創建失敗!");
}
}
}
}
流操作的基本規律:流對象很多,不知道用哪個;可以通過下面三個明確來完成。
1、明確源和目的,源:輸入流,InputStream、Reader;目的:輸出流,OutputStream
Writer。
2、操作的是否是純文本?是:字符流;否:字節流。
3、當體系明確以後,再明確使用哪個具體的對象,通過設備來進行區分:
源:內存、硬盤、鍵盤;目的:內存、硬盤、控制檯。
例如:1、將一個文本文件中的數據存儲到另一個文件中:複製文件。
源:因爲是源,所以使用讀取流。InputStream Reader;是否操作文本文件?是:這時就可以選擇Reader,這樣體系就明確了。
接下來明確要使用該體系中的那個對象?明確設備:硬盤,上的一個文件。
Reader體系中可以操作文件的對象是FileReader
是否需要提高效率?
是,加入Reader體系中的緩衝區BufferedReader
FileReaderfr = new FileReader(“a.txt”)
BufferedReaderbufr = new BufferedReader(fr);
目的:OutputStream Writer ;是否是純文本?是:Writer
設備:硬盤,一個文件,Writer體系中可以操作文件的對象FileWriter。
是否需要提高效率?是!加入Writer體系中緩衝區BUfferedWriter
FileWriterfw = new FileWriter(“b.txt”);
BufferedWriterbufw = new BufferedWriter(fw);