IO流
概述
Java中採用IO流的方式來進行數據傳輸,IO流分爲兩種:
1)字節流的抽象基流:InputStream和OutputStream
2)字符流的抽象基流:Reader和Writer
P.S.
此四個類派生出來的子類名稱都是以父類名作爲子類名的後綴,以前綴爲其功能;如InputStream子類FileInputStream;Reader子類FileReader
記住:如果要操作文字數據,建議優先考慮字符流。
而且要將數據從內存寫到硬盤上,要使用字符流中的輸出流:Writer。
硬盤的數據基本體現是文件,希望找到一個可以操作文件的Writer:FileWriter
字符流
字符流輸入:
用例:
public class Test1 {
public static void main(String[] args) throws IOException {
File file = new File("d://IO");
if(!file.exists()){
//不存在則創建路徑
file.mkdirs();
}
FileWriter fw = new FileWriter(file+"/123.txt");
fw.write("你好,字符流輸入!");
//刷新緩存到指定文件
fw.flush();
fw.close();
}
}
若更改爲:FileWriter fw = new FileWriter(file+”/123.txt”,true);
表示: 如果爲 true,則將字節寫入文件末尾處,而不是寫入文件開始處 。
加ture常用來進行文件續寫。
字符流輸出
1.單個字符讀取
public class Test2 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("d://IO/123.txt");
int c = 0;
while((c= fr.read())!=-1){
//這裏read的返回值是char值的int型,可以轉型爲char類型
System.out.print((char)c);
}
}
}
2.字符數組讀取
public class Test3 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("d://IO/123.txt");
char []c = new char[1024];
//返回值是讀取的長度
int len = fr.read(c);
System.out.println(new String(c, 0, len));
}
}
練習:
需求:
將d盤一個文本文件複製到f盤、
複製的原理:
其實就是將c盤下的文件數據存儲到e盤的一個文件中。
步驟:
1、在e盤創建一個文件。用於存儲c盤文件中的數據。
2、定義讀取流和c盤文件關聯。
3、通過不斷的讀寫完成數據存儲。
4、關閉資源。
/**
單字符傳輸
*/
public class Test4 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("D:\\IO\\123.txt");
FileWriter fw = new FileWriter("f:\\copy123.txt");
//單個字符傳送
int c ;
while((c=fr.read())!=-1){
/**public void write(int c)
* c - 指定要寫入字符的 int。
*/
fw.write(c);
}
fr.close();
fw.close();
}
}
public class Test5 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("D:\\IO\\123.txt");
FileWriter fw = new FileWriter("f:\\copy123.txt");
// 字符數組傳送
char[] c = new char[1024];
String buf = null;
int len = 0;
while ((len = fr.read(c)) != -1) {
buf = new String(c, 0, len);
fw.write(buf);
}
fw.close();
fr.close();
}
}
字符流的緩衝區——BufferedReader和BufferedWriter
使用注意:
1.使用緩衝區技術是爲了解決性能問題,提高效率
2.需要先建立流對象,再將流對象交給緩衝區構造函數去處理
3. 記住,只要用到緩衝區,就要記得刷新。(關閉流同樣會刷新,但爲了排除意外事故,保證數據存在,建議寫入一次就刷新一次)
如:bufw.flush();
4.小知識:BufferedWriter緩衝區中提供了一個跨平臺的換行符:newLine();可以在不同操作系統上調用,用作數據換行。
如:bufw.newLine();
5.讀取流緩衝區BufferedReader
BufferedReader.readLine():另外開闢了一個緩衝區,存儲的是原緩衝區一行的數據,不包含換行符。所以實際使用中常使用BufferedReader.newLine();方法換行
練習:使用字符緩衝區複製文本
public class Test6 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("D:\\IO\\Hello.java"));
BufferedWriter bw = new BufferedWriter(new FileWriter("f:\\CopyHello.java"));
String buf = null;
while((buf = br.readLine())!=null){
bw.write(buf);
//添加換行,否則輸出文本沒有格式
bw.newLine();
// //使用緩衝區的刷新方法將數據刷目的地中
}
bw.close();
br.close();
}
}
練習:模仿一個BufferedReader的readLine方法
/*
需求:根據readLine方法原理,模擬BufferedReader寫一個自己的MyBufferedReader
*/
import java.io.*;
//自定義緩衝類
class MyBufferedReader extends Reader
{
private Reader r;//定義接收的流對象
MyBufferedReader(Reader r)
{
this.r=r;
}
//自定義整行讀取
public String myReadLine()throws IOException
{
//創建一個容器,用來存儲一行的字符
StringBuilder sb =new StringBuilder();
//一個字符一個字符讀取
for (int ch=0;(ch=r.read())!=-1 ; )
{
if(ch=='\r')//如果遇到回車符換行符,則繼續
continue;
if(ch=='\n')//如果遇到換行符,表示該行讀取完畢
return sb.toString();
else
sb.append((char)ch);//將該行的字符添加到容器
}
if(sb.length()!=0)//如果讀取結束,容器中還有字符,則返回元素
return sb.toString();
return null;
}
//複寫父類中的抽象方法
public int read(char[] cbuf, int off, int len) throws IOException
{
return r.read(cbuf,off,len);
}
//複寫父類的close方法
public void close()throws IOException
{
r.close();
}
}
//測試MyBufferedReader
public class Test7
{
public static void main(String[] args)
{
MyBufferedReader mbr=null;
try
{
mbr=new MyBufferedReader(new FileReader("D:\\IO\\Hello.java"));
for (String line=null;(line=mbr.myReadLine())!=null ; )
{
System.out.println(line);//顯示效果
}
}
catch (IOException e)
{
throw new RuntimeException("讀取數據失敗");
}
finally
{
try
{
if(mbr!=null)
mbr.close();
}
catch (IOException e)
{
throw new RuntimeException("讀取流關閉失敗");
}
}
}
}
LineNumberReader
此類定義了方法 setLineNumber(int) 和 getLineNumber(),它們可分別用於設置和獲取當前行號。
換行符('\n')、回車符('\r')
/*
需求:利用LineNumberReader的特有方法去設置和獲取文件中數據的行號
*/
public class Test8 {
public static void main(String[] args) {
LineNumberReader lnr = null;
try {
// 將讀取流對象傳入
lnr = new LineNumberReader(new FileReader("D:\\IO\\Hello.java"));
lnr.setLineNumber(10);// 設置開始行號
for (String line = null; (line = lnr.readLine()) != null;) {
System.out.println(lnr.getLineNumber() + ":" + line);// 打印每行行號和字符
}
} catch (IOException e) {
throw new RuntimeException("讀取數據失敗");
} finally {
try {
if (lnr != null)
lnr.close();
} catch (IOException e) {
throw new RuntimeException("讀取流關閉失敗");
}
}
}
}
裝飾設計模式
通過多態進行一個功能的增強,不要僅僅使用繼承,而應多使用多態。
字節流
1.基本操作與字符流類相同。但它不僅可以操作字符,還可以操作其他媒體文件。
2.由於媒體文件數據中都是以字節存儲的,所以,字節流對象可直接對媒體文件的數據寫入到文件中,而可以不用再進行刷流動作。
3.讀寫字節流:InputStream 輸入流(讀)
OutputStream 輸出流(寫)
練習:複製圖片等媒體資源
public class Test1 {
public static void main(String[] args) throws IOException {
InputStream in = new FileInputStream("d:\\IO\\ME.JPG");
OutputStream out = new FileOutputStream("f:\\CopyME.JPG");
/**
* 另種寫法:不推薦
* byte []b= new byte[in.available()];
* 若in.available()值過大,會導致內存溢出
* p.s.
* in.available():返回文件中的字節個數
*/
// 推薦寫法,自定義字符緩衝區
byte[] b = new byte[1024];
int len = 0;
while ((len = in.read(b)) != -1) {
out.write(b, 0, len);
}
out.close();
in.close();
}
}
字節流緩衝區
練習:自定義字節流讀取緩衝區
/*
自定義字節流讀取緩衝區
思路:
1、定義一個固定長度的數組
2、定義一個指針和計數器用於讀取數組長度,和計數數組元素是否取完爲0
3、每次將字節數據存入元素要先將數組中的元素取完
*/
/*
自定義字節流讀取緩衝區
思路:
1、定義一個固定長度的數組
2、定義一個指針和計數器用於讀取數組長度,和計數數組元素是否取完爲0
3、每次將字節數據存入元素要先將數組中的元素取完
*/
import java.io.*;
class MyBufferedInputStream
{
private InputStream in;
private byte[] by=new byte[1024];
private int count=0,pos=0;
MyBufferedInputStream(InputStream in)
{
this.in=in;
}
//自定義讀方法,一次讀一個字節
public int myRead()throws IOException
{
//通過in對象讀取硬盤上數據,並存儲by中。
//存儲在數組中的數據被讀取完,再通過in對象從硬盤上讀取數據
if(count==0)
{
count=in.read(by);
if(count<0)//文件數據全部被讀取出來了
return -1;
pos=0;//初始化指針
byte b=by[pos];
count--;//每被讀一個字節,表示數組中的字節數少一個
pos++;//指針加1
return b&255;//返回的byte類型提升爲int類型,字節數增加,且高24位被補1,原字節數據改變。
//通過與上255,主動將byte類型提升爲int類型,將高24位補0,原字節數據不變。
//而在輸出字節流寫入數據時,只寫該int類型數據的最低8位。
}
else if(count>0)//如果數組中的數據沒被讀取完,則繼續讀取
{
byte b=by[pos];
count--;
pos++;
return b&0xff;
}
return -1;
}
//自定義關閉資源方法
public void close()throws IOException
{
in.close();
}
}
//測試自定義輸入字節流緩衝區
public class Test2
{
public static void main(String[] args)
{
long start=System.currentTimeMillis();
//利用字節流的緩衝區進行復制
copy_2();
long end=System.currentTimeMillis();
System.out.println("複製共用時:"+(end-start)+"毫秒");
}
//使用字節流的緩衝區進行復制
public static void copy_2()
{
BufferedOutputStream bout=null;
MyBufferedInputStream bin=null;
try
{
//關聯複製文件輸入流對象到緩衝區
bin=new MyBufferedInputStream(new FileInputStream("d:\\IO\\ME.JPG"));
//指定文件粘貼位置的輸出流對象到緩衝區
bout=new BufferedOutputStream(new FileOutputStream("f:\\CopyME.JPG"));
int by=0;
while((by=bin.myRead())!=-1)
{
bout.write(by);//將緩衝區中的數據寫入指定文件中
}
}
catch(IOException e)
{
throw new RuntimeException("MP3複製失敗");
}
finally
{
try
{
if(bin!=null)
bin.close();//關閉輸入字節流
}
catch(IOException e)
{
throw new RuntimeException("讀取字節流關閉失敗");
}
try
{
if(bout!=null)
bout.close();//關閉輸出字節流
}
catch(IOException e)
{
throw new RuntimeException("寫入字節流關閉失敗");
}
}
}
}
流操作
鍵盤錄入
一、鍵盤錄入
1.標準輸入輸出流
System.in:對應的標準輸入設備,鍵盤。
Ssytem.out:對應的是標準的輸出設備,控制檯。
System.in的類型是InputStream.
System.out的類型是PrintStream是OutputStream的子類FilterOutputStream的子類。
2.改進
由於鍵盤錄入是字節流,效率較低。可不可以使用整行讀取,那麼需要藉助readLine方法,但是這個是字符流的方法。所以,需要將字節流轉換成字符流。
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
練習:
/**
* 需求:將鍵盤錄入的數據轉換成大寫輸出,顯示在控制檯,當輸入over時,表示結束
源:鍵盤錄入。
目的:控制檯。
*/
public class Test1 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
String s = null;
while((s = br.readLine())!=null){
if(s.equals("over"))
break;
bw.write(s.toUpperCase());
bw.flush();
}
}
}
練習:
/**
* 需求:想把鍵盤錄入的數據存儲到一個文件中。
* 源:鍵盤
* 目的:文件
* 把錄入的數據按照指定的編碼表(UTF-8),將數據存到文件中。
* 需求:想要將一個文件的數據打印在控制檯上。
* 源:文件
* 目的:控制檯
*/
public class Test2 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new FileWriter("f:\\MyTest.txt"));
String s = null;
while((s = br.readLine())!=null){
if(s.equals("over"))
break;
bw.write(s);
bw.flush();
bw.newLine();
}
bw.close();
br.close();
}
}
流的操作規律
1、
源:鍵盤錄入。
目的:控制檯。
2、需求:想把鍵盤錄入的數據存儲到一個文件中。
源:鍵盤
目的:文件。
使用字節流通向字符流的轉換流(橋樑):InputStreamReader
3、需求:想要將一個文件的數據打印在控制檯上。
源:文件
目的:控制檯
使用字符流通向字節流的轉換流(橋樑):OutputStreamWriter
4、流操作的基本規律:
最痛苦的就是流對象有很多,不知道該用哪一個。
通過三個明確來完成:
4.1 明確源和目的。
源:輸入流。InputStream Reader
目的:輸出流。OutputStream Writer
4.2 操作的數據是否是純文本。
是:字符流
否:字節流
4.3 當體系明確後,再明確要使用哪個具體的對象。通過設備來進行區分:
源設備:內存,硬盤,鍵盤
目的設備:內存,硬盤,控制檯
5、規律體現
5.1 將一個文本文件中數據存儲到另一個文件中。複製文件。
1)源:因爲是源,所以使用讀取流:InputStream和Reader
明確體系:是否操作文本:是,Reader
明確設備:明確要使用該體系中的哪個對象:硬盤上的一個文件。Reader體系中可以操作文件的對象是FileReader
是否需要提高效率:是,加入Reader體系中緩衝區 BufferedReader.
FileReader fr = new FileReader("a.txt");
BufferedReader bufr = new BufferedReader(fr);
2)目的:輸出流:OutputStream和Writer
明確體系:是否操作文本:是,Writer
明確設備:明確要使用該體系中的哪個對象:硬盤上的一個文件。Writer體系中可以操作文件的對象FileWriter。
是否需要提高效率:是,加入Writer體系中緩衝區 BufferedWriter
FileWriter fw = new FileWriter("b.txt");
BufferedWriter bufw = new BufferedWriter(fw);
練習:將一個圖片文件中數據存儲到另一個文件中。複製文件。要按照以上格式自己完成三個明確。
1)源:輸入流,InputStream和Reader
是否是文本?否,InputStream
源設備:硬盤上的一個文件。InputSteam體系中可以操作文件的對象是FileInputSteam
是否需要提供效率:是,BufferedInputStream
BufferedInputSteambis=newBufferedInputStream(newFileInputStream("c:/users/asus/desktop/1.jpg"));
2)目的:輸出流,OutputStream和Writer
是否是文本?否,OutputStream
源設備:硬盤上的文件,FileOutputStream
是否需要提高效率:是,加入BufferedOutputStream
BufferedOutputStreambos=newBufferedOutputStream(newFileOutputStream("c:/users/asus/desktop/2.jpg"));
5.2 需求:將鍵盤錄入的數據保存到一個文件中。
1)源:InputStream和Reader
是不是純文本?是,Reader
設備:鍵盤。對應的對象是System.in。——爲了操作鍵盤的文本數據方便。轉成字符流按照字符串操作是最方便的。所以既然明確了Reader,那麼就將System.in轉換成Reader。用Reader體系中轉換流,InputStreamReader
InputStreamReaderisr = new InputStreamReader(System.in);
需要提高效率嗎?需要,BufferedReader
BufferedReaderbufr = new BufferedReader(isr);
2)目的:OutputStream Writer
是否是存文本?是!Writer。
設備:硬盤。一個文件。使用 FileWriter。
FileWriter fw = newFileWriter("c.txt");
需要提高效率嗎?需要。
BufferedWriter bufw = new BufferedWriter(fw);
5.3 擴展:想要把錄入的數據按照指定的編碼表(UTF-8)(默認編碼表是GBK),將數據存到文件中。
目的:OutputStream Writer
是否是存文本?是!Writer。
設備:硬盤上的一個文件。使用 FileWriter。——但是FileWriter是使用的默認編碼表:GBK。而存儲時,需要加入指定編碼表utf-8。而指定的編碼表只有轉換流可以指定。所以要使用的對象是OutputStreamWriter。
該轉換流對象要接收一個字節輸出流,而且還可以操作的文件的字節輸出流:FileOutputStream
OutputStreamWriter osw =new OutputStreamWriter(newFileOutputStream("d.txt"),"UTF-8");
需要高效嗎?需要,BufferedWriter
BufferedWriter bufw = new BufferedWriter(osw);
記住:
轉換流什麼使用?
字符和字節之間的橋樑。通常,涉及到字符編碼轉換時,需要用到轉換流。
練習:將一個文本數據打印在控制檯上。要按照以上格式自己完成三個明確。
1)源:InputStreamReader
是文本?是:Reader
設備:硬盤。上的文件:FileReader
是否需要提高效率?是:BufferedReader
BufferedReader br=new BufferedReader(newFileReader("1.txt"));
2)目的:OutputStream Writer
是文本?是:Writer
設備:控制檯。對應對象System.out。由於System.out對應的是字節流,所以利用OutputSteamWriter轉換流
是否提高效率?是:BufferedWriter
BufferedWriter bw =new BufferedWriter(newOutputStreamWriter(system.out));
練習:帶編碼的流操作
/**
* 帶編碼集的操作
* @author LQX
*
*/
public class Test3 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in, "UTF-8"));
//指定UTF-8編碼
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("f://MyTest.java"), "UTF-8");
String s = null;
while((s = br.readLine())!=null){
if(s.equals("over"))
break;
osw.write(s+"\r\n");
osw.flush();
}
}
}
public class Test4 {
public static void main(String[] args) throws IOException {
/*BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("f:\\MyTest.java")));
String s = null;
while((s = br.readLine())!=null){
System.out.println(s);
}*/
//該文件在創建時我指定了編碼爲UTF-8,所以多出來是亂碼
FileReader fr = new FileReader("f:\\MyTest.java");
char []c = new char[1024];
/* int len = 0;
while((len = fr.read(c))!=-1){
System.out.println(new String(c, 0, len));
}*/
//修改後,設定了編碼爲UTF-8,能正確讀出來
InputStreamReader isr = new InputStreamReader(new FileInputStream("f:\\MyTest.java"), "UTF-8");
int len = 0;
while((len = isr.read(c))!=-1){
System.out.println(new String(c, 0, len));
}
}
}
什麼時候使用流轉換?
1.目標設備是字節流,但操作是字符流,使用轉換流作爲橋樑,提高效率。
2.涉及文本字符編碼表時,必須使用轉換流,因爲只有它才提供自定義編碼。