Java學習筆記——IO流與文件
標籤: IO
、Stream
、File
文章目錄
一、文件(File類
)
1. File
對象創建方法
Java
文件類以抽象的方式代表文件名和目錄路徑名。該類主要用於文件和目錄的創建、文件的查找和文件的刪除等。File
類提供管理文件或目錄的方法。File
實例表示真實文件系統中的一個文件或者目錄,可以通過以下四種構造方法創建一個File
對象。
(1). File(File parent, String child)
:參數parent
表示根路徑,參數child
表示子路徑。例如:
File file = new File(new File("src"), "test.txt");
(2). File(String pathname)
:參數pathname
表示文件路徑或者目錄路徑。例如:
File file = new File("src/test.txt"); //等價於(1)中例子
(3). File(String parent, String child)
:參數parent
表示根路徑,參數child
表示子路徑。例如:
File file = new File("src", "test.txt"); //等價於(2)中例子
(4). File(URI uri)
:參數uri
表示路徑。例如:
File file = new File(new URI("src/test.txt")); //等價於(3)中例子
只處理一個文件,使用第二個或者第四個構造方法更方便;如處理一個公共目錄的若干子目錄或文件,那麼使用第一個或者第三個更方便。
2. File
類常用方法
假設現在在/com/hzj/
目錄下有個test.txt
文件,內容如下:
qwertyuiopasdfghjklzxcvbnm
(1). boolean exists()
:判斷該文件或者目錄是否存在。
(2). long length()
:返回該文件的長度。
(3). String getName()
:返回該文件名字。
(4). boolean createNewFile()
:創建指定文件。
(5). boolean delete()
:刪除指定文件或者空目錄。
(6). boolean mkdir()
:創建一個目錄(不連續創建),即如果該目錄的父目錄不存在話,那麼就會創建失敗。
(7). boolean mkdirs()
:創建一個目錄(連續創建),如果該目錄的父目錄不存在話,那麼還會創建所有的父目錄。
(8). String[] list()
:返回指定目錄下所有文件名或者子目錄名所組成的字符串數組。
(9). long lastModified()
:返回指定文件最後一次被修改的時間(從1970年1月1日凌晨12點到這個文件的修改時間
之間所經歷的毫秒數)。
(10). String getPath()
:返回指定文件或者目錄的路徑。
(11). String getAbsolutePath()
:返回指定文件或者目錄的絕對路徑。
(12). boolean renameTo(File dest)
:修改該目錄或文件名字爲指定的值。
File parent = new File("/com/hzj");
File child = new File(parent, "test.txt");
File file = new File("/com/hzj/test2.txt");
File mkdir_test = new File("/com/hzj/aaa/bbb");
System.out.println(child.exists()); // true
System.out.println(child.length()); // 26
System.out.println(child.getName()); // test.txt
System.out.println(file.createNewFile()); // true(創建file文件)
System.out.println(file.delete()); // true(刪除file文件)
System.out.println(mkdir_test.mkdir()); // false(沒有aaa目錄)
System.out.println(mkdir_test.mkdirs()); // true(連續創建)
System.out.println(parent.list()); // test.txt aaa
System.out.println(parent.renameTo(new File("/com/jzh"))); // true
二、IO
流(Stream
)
- 在Java中,把一組有序的數據序列稱爲流。程序的主要任務就是操縱數據。
- 根據流中最小的數據單元,可以把流分爲字節流和字符流。
- 根據操作的方向,可以把流分爲輸入流和輸出流兩種。程序從輸入流讀取數據,向輸出流寫出數據。示例圖如下:
一定要搞清楚什麼時候用輸入流,什麼時候用輸出流。通俗的講,就是把Java
程序看爲主體,如果要讀文件,就是文件系統 --> Java程序
,看箭頭方法可知,從文件系統出,進入Java
程序,即相對於Java
程序就是input
,如果要寫文件,就是Java程序 --> 文件系統
,看箭頭方法可知,從Java
程序出,進入文件系統,即相對於Java
程序就是output
。
下面是各個流之間的層次關係(放大了看):
1. 字節流InputStream
&& OutputStream
InputStream
和OutputStream
類處理的是字節流,也就是說,數據流中的最小單元爲一個字節,它包括8
個二進制位。
(1)輸入流(InputStream
)
主要方法:
(1). int available()
:返回可以從輸入流中讀取的字節數目。
(2). abstract int read()
:從輸入流讀取一個8
位的字節,把它轉換爲0-255
之間的整數,並返回這一整數。如果遇到輸入流的結尾,則返回-1
。
(3). int read(byte[] b)
:從輸入流讀取若干個字節,把它們保存到參數b
指定的字節數組中。返回的整數表示讀取的字節數。如果遇到輸入流的結尾,則返回-1
。使用byte[]
數組可以降低物理讀取次數。byte[]
長度最大可以取到整個要讀取的數據的長度。
(4). int read(byte[] b, int off, int len)
:從輸入流讀取若干個字節,把它們保存到參數b
指定的字節數組中。返回的整數表示讀取的字節數。參數off
指定在字節數組中開始保存數據的起始下標(默認爲0
),參數len
指定讀取的字節數目。返回的整數表示實現讀取的字節數。如果遇到輸入流的結尾,則返回-1
。
(5). void close()
:關閉輸入流,InputStream
類本身的close()
方法不執行任何操作。它的一些子類覆蓋了close()
方法,在close()
方法中釋放和流有關的系統資源。
(6). long skip(long n)
:從輸入流中跳過參數n
指定數目的字節。
(7). void mark(int readlimit)
:標記此輸入流中的當前位置。
(8). boolean markSupported()
:返回此輸入流是否支持標記(mark()
)和重置(reset()
)方法。
常用子類:
InputStream
是個抽象類,常用的子類有:ByteArrayInputStream
,DataInputStream
,BufferedInputStream
,PipedInputStream
,FileInputStream
,ObjectInputStream
。
(2)輸出流(OutputStream
)
主要方法:
(1). void close()
:關閉輸出流。OutputStream
類本身的close()
方法不執行任何操作。它的一些子類覆蓋了close()
方法,在close()
方法中釋放和流有關的系統資源。
(2). void flush()
:OutputStream
類本身的flush()
方法不執行任何操作,它的一些帶有緩衝區的子類(比如BufferedOutputStream
和PrintStream
類)覆蓋了flush()
方法。通過帶緩衝區的輸出流寫數據時,數據先保存在緩衝區中,積累到一定程度纔會真正寫到輸出流中。緩衝區通常用字節數組實現,實際上是指一塊內存空間。flush
:即使內容沒有達到緩衝的標準大小,仍然輸出到輸出流中。只要使用到buffer
的流,就一定要進行flush
,否則最後很可能有數據沒有完全達到要求而不能輸出。
(3). void write(byte[] b)
:把參數b
指定的字節數組中的所有字節寫到輸出流。
(4). void write(byte[] b, int off, int len)
:把參數b
指定的字節數組中的所有字節寫到輸出流,參數off
指定字節數組的起始下標,從這個位置開始輸出由參數len指定數目的字節。
(5). abstract void write(int b)
:向輸出流寫入一個字節。在向文件或控制檯寫數據時,採用上面兩個write
方法可以減少進行物理讀文件或鍵盤的次數,因此能提高I/O
操作的效率。
常用子類:
OutputStream
是個抽象類,常用的子類有:ByteArrayOutputStream
,DataOutputStream
,BufferedOutputStream
,PipedOutputStream
,FileOutputStream
,ObjectOutputStream
。
(3)各輸入流、輸出流用法詳解
(1). ByteArrayInputStream
&& ByteArrayOutputStream
ByteArrayInputStream
:把字節數組轉換爲輸入流。ByteArrayInputStream
類有兩個默認的構造方法:
ByteArrayInputStream(byte[] b)
: 使用一個字節數組當中所有的數據做爲數據源,程序可以像輸入流方式一樣讀取字節,可以看做一個虛擬的文件,用文件的方式去讀取它裏面的數據。ByteArrayInputStream(byte[] b,int offset,int length)
: 從數組當中的第offset
(offset
從0
開始)開始,一直取出length
個這個字節做爲數據源。
ByteArrayOutputStream
:字節數組輸出流在內存中創建一個字節數組緩衝區,所有發送到輸出流的數據保存在該字節數組緩衝區中。
主要方法:
byte[] toByteArray()
:創建一個新分配的字節數組。數組的大小和當前輸出流的大小,內容是當前輸出流的拷貝。即把輸出流轉換爲字節數組。void writeTo(OutputStream out)
:將此字節數組輸出流的全部內容寫入到指定的輸出流參數中。
示例(將字節數組轉換爲輸入流,將輸出流轉換成字節數組):
System.out.println("-----使用ByteArrayInputStream(byte[] b)構建對象------");
byte[] b1 = new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
InputStream bis1 = new ByteArrayInputStream(b1);
System.out.println(bis1.available()); // 8
int n1;
bis1.skip(2); // 跳過兩個字符
while ((n1 = bis1.read()) != -1) {
System.out.print((char) n1 + " "); // c d e f g h
}
bis1.close();
System.out.println();
System.out.println("----使用ByteArrayInputStream(byte[] b,int offset,int length)構建對象-----");
byte[] b2 = new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
ByteArrayInputStream bis2 = new ByteArrayInputStream(b2, 2, 3);
System.out.println(bis2.available()); // 3
int n2;
while ((n2 = bis2.read()) != -1) {
System.out.print((char) n2 + " "); // c d e
}
bis2.close();
System.out.println();
System.out.println("----利用read(byte[] b)讀取數據-----");
byte[] b3 = new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
byte[] b4 = new byte[3];
ByteArrayInputStream bis3 = new ByteArrayInputStream(b3);
System.out.println(bis3.available()); // 8
int n3;
while ((n3 = bis3.read(b4)) != -1) { // 利用read(byte[] b)方法時,返回值是當前讀的字符個數
System.out.print(n3 + " "); // 3 3 2
}
bis3.close();
System.out.println();
System.out.println("----ByteArrayOutputStream用法-----");
byte[] b5 = new byte[10];
ByteArrayOutputStream bos5 = new ByteArrayOutputStream();
bos5.write('a');
bos5.write('b');
bos5.write('c');
bos5.write('d');
bos5.write('e');
bos5.write('f');
bos5.flush();
b5 = bos5.toByteArray(); // 將輸出流轉換成字節數組
for (byte b : b5) {
System.out.print((char) b); // abcdef
}
ByteArrayOutputStream bos6 = new ByteArrayOutputStream();
bos5.writeTo(bos6); // 將bos5字節數組輸出流的全部內容寫入到指定的輸出流參數(bos6)中。
b5 = bos6.toByteArray(); // 將輸出流轉換成字節數組
for (byte b : b5) {
System.out.print((char) b); // abcdef
}
(2). FileInputStream
&& FileOutputStream
FileInputStream
:從文件中讀取數據。FileInputStream
類常用的兩個構造方法:
FileInputStream(File file)
:使用一個文件對象來創建一個輸入流對象來讀取文件。首先得使用File()
方法來創建一個文件對象。FileInputStream(String name)
:使用字符串類型的文件名來創建一個輸入流對象來讀取文件。
FileOutputStream
:向文件中寫入數據。如果該流在打開文件進行輸出前,目標文件不存在,那麼該流會創建該文件。FileOutputStream
類的常用構造方法:
FileOutputStream(File file)
:使用一個文件對象來創建一個輸出流來寫文件。首先得使用File()
方法來創建一個文件對象。FileOutputStream(String name)
:使用字符串類型的文件名來創建一個輸出流對象來寫文件。FileOutputStream(File file, boolean append)
:使用一個文件對象來創建一個輸出流來寫文件,同時給定一個append參數(默認false
),用來指定是追加還是覆蓋。FileOutputStream(String name, boolean append)
:使用字符串類型的文件名來創建一個輸出流對象來寫文件,同時給定一個append參數(默認false
),用來指定是追加還是覆蓋。
示例(向文件中寫入文件,並讀取出來):
File file = new File("src/note/file_test.txt");
OutputStream out = new FileOutputStream(file); // 寫文件
out.write('h');
out.write('e');
out.write('l');
out.write('l');
out.write('o');
InputStream in = new FileInputStream(file); // 讀文件
System.out.println("可讀取" + in.available() + "字節數"); // 可讀取5字節數
System.out.println(in.markSupported()); // false (不支持標記)
int n;
while ((n = in.read()) != -1) {
System.out.print((char) n); // hello
}
in.close(); // 關閉資源
out.close(); // 關閉資源
(3). DataInputStream
&& DataOutputStream
DataInputStream
:用於讀取基本類型數據,如int
, float
, long
, double
和boolean
等。構造方法爲:
DataInputStream(InputStream in)
:需要傳入一個InputStream
對象,通常傳入FileInputStream
對象。
常用方法:
int skipBytes(int n)
:從輸入流中跳過n
個字節的數據。boolean readBoolean()
:從輸入流中讀取1
個字節,將它轉換爲boolean
類型的數據。byte readByte()
:從輸入流中讀取1
個字節,將它轉換爲byte
類型的數據。char readChar()
:從輸入流中讀取2
個字節,將它轉換爲char
類型的數據。double readDouble()
:從輸入流中讀取8
個字節,將它轉換爲double
類型的數據。float readFloat()
:從輸入流中讀取4
個字節,將它轉換爲float
類型的數據。int readInt()
:從輸入流中讀取4
個字節,將它轉換爲int
類型的數據。long readLong()
:從輸入流中讀取8
個字節,將它轉換爲long
類型的數據。short readShort()
:從輸入流中讀取2
個字節,將它轉換爲short
類型的數據。String readUTF()
:從輸入流中讀取1
到3
個字節的串,將它轉換爲UTF-8
字符編碼的字符串。
DataOutputStream
:用於寫入基本數據類型。如int
, float
, long
, double
和boolean
等。構造方法爲:
DataOutputStream(OutputStream out)
:需要傳入一個OutputStream
對象,通常傳入FileOutputStream
對象。
常用方法如下(這些方法都是將指定的基本數據類型以字節的方式寫入到輸出流):
void writeBoolean(boolean v)
void writeByte(int v)
void writeChar(int v)
void writeDouble(double v)
void writeFloat(float v)
void writeInt(int v)
void writeLong(long v)
void writeShort(int v)
void writeUTF(String str)
:寫入的時候,首先會寫入兩個字節,代表字符串的長度,然後再以字節的方式把字符串寫入。可查看writeUTF()
源代碼理解。
示例(向文件中寫入指定類型的數據,並按照指定數據類型讀取出來):
// -----------寫入DataOutputStream-----------
OutputStream fos = new FileOutputStream("src/note/data_test.txt");
DataOutputStream dos = new DataOutputStream(fos);
dos.writeInt(1);
dos.writeLong(100);
dos.writeUTF("hello world!");
dos.writeDouble(1.23);
dos.writeBoolean(false);
dos.writeByte('a');
dos.writeChar('b');
dos.writeFloat(1.1f);
// -----------從DataOutputStream讀出-----------
InputStream fis = new FileInputStream("src/note/data_test.txt");
DataInputStream dis = new DataInputStream(fis);
System.out.println(dis.readInt());
System.out.println(dis.readLong());
System.out.println(dis.readUTF());
System.out.println(dis.readDouble());
System.out.println(dis.readBoolean());
System.out.println(dis.readByte());
System.out.println(dis.readChar());
System.out.println(dis.readFloat());
/* 結果:
1
100
hello world!
1.23
false
97
b
1.1
*/
如果需要跳過某些類型數據,只需利用skipBytes(n)
方法跳過該類型所佔字節數即可。其中String
類型比較特殊,需要使用skipBytes(out.readShort())
,因爲writeUTF()
會先寫入一個short
類型的數據,代表String
的長度。示例:
// -----------寫入DataOutputStream-----------
OutputStream fos = new FileOutputStream("src/note/data_test.txt");
DataOutputStream dos = new DataOutputStream(fos);
dos.writeInt(1);
dos.writeLong(100);
dos.writeUTF("hello world!");
dos.writeDouble(1.23);
// -----------從DataOutputStream讀出-----------
InputStream fis = new FileInputStream("src/note/data_test.txt");
DataInputStream dis = new DataInputStream(fis);
// System.out.println(dis.readInt());
dis.skipBytes(4); // 跳過int類型,int佔4個字節
System.out.println(dis.readLong());
// System.out.println(dis.readUTF());
dis.skipBytes(dis.readShort()); // 跳過String類型的數據
System.out.println(dis.readDouble());
/* 結果:
100
1.23
*/
(4). ObjectInputStream
&& ObjectOutputStream
ObjectInputStream
:對象的反序列化(對象需要實現Serializable
序列化接口)。構造方法:
ObjectInputStream(InputStream in)
:需要傳入一個InputStream
對象,可以傳入FileInputStream
對象。
常用方法:
Object readObject()
:讀取一個對象。
ObjectOutputStream
:對象的序列化(對象需要實現Serializable
序列化接口)。構造方法:
ObjectOutputStream(OutputStream out)
:需要傳入一個OutputStream
對象,可以傳入FileInputStream
對象。
常用方法:
void writeObject(Object obj)
:寫入一個對象。
示例(對象的序列化與反序列化,利用文件流來實現數據持久化):
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
public class Test {
public static void main(String[] args) throws IOException {
// 藉助FileOutputStream來實現對象的持久化存儲,即存入文件
// -----------寫入ObjectOutputStream-----------
OutputStream fos = new FileOutputStream("src/note/data_test.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(new Student("張三"));
oos.close();
fos.close();
// -----------從DataOutputStream讀出-----------
InputStream fis = new FileInputStream("src/note/data_test.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
try {
Student s = (Student) ois.readObject();
System.out.println(s); // Student [name=張三]
fis.close();
ois.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class Student implements Serializable { // Stduent類,實現Serializable接口
private String name;
public Student() {
}
public Student(String name) {
super();
this.name = name;
}
@Override
public String toString() {
return "Student [name=" + name + "]";
}
}
(5). PipedInputStream
&& PipedOutputStream
管道流,用於線程間的通信。一個線程的PipedInputStream
對象從另外一個線程的PipedOutputStream
對象讀取輸入。要使管道流有用,必須同時構造管道輸入流和管道輸出流。
PipedInputStream
:管道輸入流從一個管道輸出流中讀取數據。
PipedOutputStream
:管道輸出流給管道輸入流傳輸數據。
示例(利用多線程實現管道傳輸Student
對象):
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.Serializable;
/*
* 開啓兩個線程,一個用於管道輸出流用於寫入Student對象,另一個用於管道輸入流讀取Student對象
*/
public class Test {
public static void main(String[] args) throws IOException {
PipedOutputStream pos = new PipedOutputStream();
PipedInputStream pis = new PipedInputStream(pos);
PipedOutputStreamThread thread1 = new PipedOutputStreamThread(pos);
PipedInputStreamThread thread2 = new PipedInputStreamThread(pis);
thread1.start();
thread2.start();
}
}
class PipedOutputStreamThread extends Thread { // 管道輸出流線程
PipedOutputStream pos;
public PipedOutputStreamThread(PipedOutputStream pos) {
this.pos = pos;
}
@Override
public void run() {
try {
// 用ObjectOutputStream包裝管道輸出流,使之有序列化對象功能
ObjectOutputStream oos = new ObjectOutputStream(pos);
oos.writeObject(new Student("李四"));
oos.close();
pos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class PipedInputStreamThread extends Thread { // 管道輸入線程
PipedInputStream pis;
public PipedInputStreamThread(PipedInputStream pis) {
this.pis = pis;
}
@Override
public void run() {
try {
// 用ObjectInputStream包裝管道輸入流,使之有反序列化對象功能
ObjectInputStream ois = new ObjectInputStream(pis);
Student s = (Student) ois.readObject();
System.out.println(s); // Student [name=李四]
pis.close();
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Student implements Serializable {
private String name;
public Student() {
}
public Student(String name) {
super();
this.name = name;
}
@Override
public String toString() {
return "Student [name=" + name + "]";
}
}
(6). BufferedInputStream
&& BufferedOutputStream
BufferedInputStream
:字節緩衝輸入流。BufferedInputStream
類覆蓋了被過濾的輸入流的讀數據行爲,利用緩衝區來提高讀數據的效率。BufferedInputStream
類先把一批數據讀入到緩衝區,接下來read()
方法只需要從緩衝區內獲取數據,就能減少物理性讀取數據的次數。構造方法:
BufferedInputStream(InputStream in)
:參數in
指定需要被過濾的輸入流。BufferedInputStream(InputStream in, int size)
:參數in
指定需要被過濾的輸入流。參數size
指定緩衝區的大小,以字節爲單位。
BufferedOutputStream
:字節緩衝輸出流。BufferedOutputStream
類覆蓋了被過濾的輸出流的寫數據行爲,利用緩衝區來提高寫數據的效率。BufferedOutputStream
類先把一批數據寫入到緩衝區,接下來只需要flush()
將所有緩衝的輸出字節寫出到底層輸出流中,就能減少物理性存數據的次數。構造方法:
public BufferedOutputStream(OutputStream out)
:採用的默認的緩衝區大小 ,來構造一個字節緩衝輸出流對象。public BufferedOutputStream(OutputStream out,int size)
:指定size
緩衝區大小構造緩衝輸出流對象。
主要方法:
(1). void flush()
:刷新此緩衝的輸出流,讓所有緩衝的輸出字節被寫出到底層輸出流中。
(2). void close()
:關閉此輸出流並釋放與此流有關的所有系統資源。FilterOutputStream
的 close
方法先調用其flush
方法,然後調用其基礎輸出流的close
方法。
示例:
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("test.txt"));
bos.write("qwertyuiopasdfghjklzxcvbnm".getBytes());
bos.flush();
bos.close();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("test.txt"));
int n;
while ((n = bis.read()) != -1) {
System.out.print((char) n); // qwertyuiopasdfghjklzxcvbnm
}
bis.close();
2. 字符流Reader
&& Writer
在許多應用場合,Java
應用程序需要讀寫文本文件。在文本文件中存放了採用特定字符編碼的字符,爲了便於讀於各種字符編碼的字符,java.io
包中提供了Reader
/Writer
類,它們分別表示字符輸入流和字符輸出流。
- 在不同的操作系統有着不同的字符編碼,
windows
默認是gbk
或gbk2312
,unix
是utf-8
,mac
是euc_cn
。- 在處理字符流時,最主要的問題是進行字符編碼的轉換。
Java
語言採用Unicode
字符編碼。對於每一個字符,Java
虛擬機會爲其分配兩個字節的內存。而在文本文件中,字符有可能採用其他類型的編碼,比如GBK
和UTF-8
字符編碼等。
在默認情況下,Reader
和Writer
會在本地平臺的字符編碼和Unicode
字符編碼之間進行編碼轉換。
如果要輸入或輸出採用特定類型編碼的字符串,可以使用InputStreamReader
類和OutputStreamWriter
類。在它們的構造方法中可以指定輸入流或輸出流的字符編碼。
(1)輸入流(Reader
)
Reader
類能夠將輸入流中採用其他編碼類型的字符轉換爲Unicode
字符,然後在內存中爲這些Unicode
字符分配內存。
主要方法:
abstract void close()
:關閉流並釋放與其關聯的所有系統資源。void mark(int readAheadLimit)
:標記流中的當前位置。boolean markSupported()
:判斷此流是否支持mark()
操作。int read()
:讀一個字符。int read(char[] cbuf)
:將字符讀入數組。abstract int read(char[] cbuf, int off, int len)
:將字符讀入數組的一部分。int read(CharBuffer target)
:嘗試將字符讀入指定的字符緩衝區。boolean ready()
:判斷此流是否可以讀取。void reset()
:重置流。long skip(long n)
:跳過指定長度字符。
常用子類:
Reader
是個抽象類,常見子類有:
CharArrayReader
:把字符數組轉換爲Reader
,從字符數組中讀取字符。BufferedReader
:過濾器,爲其他Reader
提供讀緩衝區,此外,它的readLine()
方法能夠讀入一行字符串。StringReader
:把字符串轉換爲Reader
,從字符串中讀取字符。PipedReader
:連接一個PipedWriter
。PushBackReader
:能把讀到的字符壓回到緩衝區中,通常用做編譯器的掃描器,在程序中一般很少使用它。InputStreamReader
:過濾器,把InputStream
轉換爲Reader
,可以指定字符編碼。FileReader
:從文件中讀取字符。
(2)輸出流(Writer
)
Writer
類能夠把內存中的Unicode
字符轉換爲其他編碼類型的字符,再寫到輸出流中。
主要方法:
Writer append(char c)
:將指定的字符追加到此writer
。Writer append(CharSequence csq)
:將指定的字符序列追加到此writer
。Writer append(CharSequence csq, int start, int end)
:將指定字符序列的子序列追加到此write
rabstract void close()
:關閉流,關閉前先flush()
。abstract void flush()
:刷新流。void write(char[] cbuf)
:向Writer
中寫一個字符數組。abstract void write(char[] cbuf, int off, int len)
:向Writer
中寫一個字符數組的一部分。void write(int c)
:向Writer
中寫一個字符。void write(String str)
:向Writer
中寫一個字符串。void write(String str, int off, int len)
:向Writer
中寫一個字符串的一部分。
常用子類:
Writer
是個抽象類,常用的子類有:
BufferedWriter
:帶有緩衝的Writer
。OutputStreamWriter
:過濾器,把Writer
轉換爲OutputStream
,可以指定字符編碼。PrinterWriter
:打印流。StringWriter
:將Writer
轉化爲字符串。PipedWriter
:連接一個PipedReader
。CharArrayWriter
:將Writer
寫入字符數組。FileWriter
:將字符寫入文件。
(3)各輸入流、輸出流用法詳解
(1). InputStreamReader
&& OutputStreamWriter
InputStreamReader
:字符輸入流。InputStreamReader
類把InputStream
類型轉換爲Reader
類型,即把字節輸入流轉換成字符輸入流。構造方法:
InputStreamReader(InputStream in)
:按照本地平臺的字符編碼讀取輸入流中的字符。InputStreamReader(InputStream in, String charsetName)
:按照指定的字符編碼讀取輸入流中的字符。
示例(兩種方式創建InputStreamReader
對象,將FileInputStream
字節流轉換爲InputStreamReader
字符流):
//--------------用InputStreamReader(InputStream in)構建對象--------------
// 創建FileInputStream對象(字節輸入流)
InputStream fis = new FileInputStream("src/note/test.txt");
// 創建InputStreamReader對象,把FileInputStream字節流轉換爲字符流
Reader isr = new InputStreamReader(fis);
int n;
while ((n = isr.read()) != -1) {
System.out.print((char) n); // hello,我是張三。
}
isr.close();
fis.close();
//---------------InputStreamReader(InputStream in, String charsetName)構件對象-------------
// 創建FileInputStream對象(字節輸入流)
InputStream fis1 = new FileInputStream("src/note/test.txt");
// 創建InputStreamReader對象,把FileInputStream字節流轉換爲字符流,並且制定字符編碼爲utf-8
Reader isr1 = new InputStreamReader(fis1, "utf-8");
int n1;
while ((n1 = isr1.read()) != -1) {
System.out.print((char) n1); // hello,我是張三。
}
isr1.close();
fis1.close();
OutputStreamWriter
:字符輸出流。OutputStreamWriter
類把OutputStream
類型轉換爲Writer
類型,即把字節輸出流轉換成字符輸出流。構造方法:
OutputStreamWriter(OutputStream out)
:創建使用默認字符編碼的OutputStreamWriter
。OutputStreamWriter(OutputStream out, Charset cs)
:創建一個使用給定字符集的OutputStreamWriter
。OutputStreamWriter(OutputStream out, CharsetEncoder enc)
:創建使用給定charset
編碼器的OutputStreamWriter
。OutputStreamWriter(OutputStream out, String charsetName)
:創建一個使用指定charset
的OutputStreamWriter
。
示例(用OutputStreamWriter
向文件中寫入字符、字符串):
// 創建文件輸出字節流
OutputStream fos = new FileOutputStream("src/note/test.txt");
// 將文件輸出字節流轉換爲字符流
OutputStreamWriter osw = new OutputStreamWriter(fos);
// 向流中添加字符
osw.append('胡');
// 向流中寫入字符串
osw.write("hello world");
osw.flush();
(2). FileReader
&& FileWriter
FileReader
:InputStreamReader
的一個子類,用於從文件中讀取字符數據。該類只能按照本地平臺的字符編碼來讀取數據,用戶不能指定其他字符編碼類型。構造方法:
FileReader(File file)
:參數file
指定需要讀取的文件。FileReader(String name)
:參數name
指定需要讀取的文件的路徑。
FileWriter
:OutputStreamWriter
的一個子類,用於向文件中寫入字符數據。該類只能按照本地平臺的字符編碼來寫入數據,用戶不能指定其他字符編碼類型。構造方法:
FileWriter(File file)
:參數file
指定需要寫入的文件。FileWriter(File file, boolean append)
:參數file
指定需要寫入的文件,參數append
代表是追加還是覆蓋。FileWriter(FileDescriptor fd)
:構造與文件描述符關聯的FileWriter
對象。FileWriter(String fileName)
:參數fileName
指定需要寫入的文件的路徑。FileWriter(String fileName, boolean append)
:指定需要寫入的文件的路徑,參數append
代表是追加還是覆蓋。
示例(用FileWriter
向文件寫入字符,並用FileReader
讀取出來):
FileWriter fw = new FileWriter("src/note/test.txt");
fw.write("hello world!");
fw.flush();
fw.close();
FileReader fr = new FileReader("src/note/test.txt");
int n;
while((n = fr.read()) != -1) {
System.out.print((char)n); // hello world!
}
fr.close();
(3). BufferedReader
&& BufferedWriter
帶有緩存的字符輸入輸出流,常用於讀寫文件,效率高。BufferedWriter
提供了newLine()
方法,BufferedReader
提供了readLine()
方法。
BufferedReader
:帶有緩存的字符輸出流。構造方法:
BufferedReader(Reader in)
:創建使用默認大小的輸入緩衝區的緩衝字符輸入流。需要傳入一個Reader
類型的參數,可以傳入FileReader
類型,可以理解爲給FileReader
添加新功能(讀一行)。BufferedReader(Reader in, int sz)
:創建使用指定大小的輸入緩衝區的緩衝字符輸入流。需要傳入一個Reader
類型的參數,可以傳入FileReader
類型,可以理解爲給FileReader
添加新功能(讀一行)。
BufferedWriter
:帶有緩存的字符輸入流。構造方法:
BufferedWriter(Writer out)
:創建使用默認大小的輸入緩衝區的緩衝字符輸出流。需要傳入一個Writer
類型的參數,可以傳入FileWriter
類型,可以理解爲給FileWriter
添加新功能(寫一行)。BufferedWriter(Writer out, int sz)
:創建使用指定大小的輸入緩衝區的緩衝字符輸出流。需要傳入一個Writer
類型的參數,可以傳入FileWriter
類型,可以理解爲給FileWriter
添加新功能(寫一行)。
示例(利用BufferedReader
和BufferedWriter
實現文件的讀寫,寫一行、讀一行):
FileWriter fw = new FileWriter("src/note/test.txt");
BufferedWriter bw = new BufferedWriter(fw);
bw.write("hello world!");
bw.newLine(); // 另起一行
bw.write("第二行");
bw.flush();
bw.close();
fw.close();
FileReader fr = new FileReader("src/note/test.txt");
BufferedReader br = new BufferedReader(fr);
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
// hello world!
// 第二行
}
br.close();
fr.close();
三、 實例
1. 本地文件傳輸(網絡傳輸文件基礎)
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/**
* @author HZJ
* @date 2019年6月14日 下午2:42:15
* @description 實現文件的複製功能,inPath代表輸入的文件路徑,outPath代表輸出的文件路徑。
*/
public class Test {
private static String inPath = "src/note/in_test.jpg"; // 輸入圖片路徑
private static String outPath = "src/note/out_test.jpg"; // 輸出圖片路徑
public static void main(String[] args) throws Exception {
// 創建輸入文件對象
File in_file = new File(inPath);
if(!in_file.exists()) {
throw new Exception("文件不存在!");
}
// 創建文件輸入流(字節流)
FileInputStream in = new FileInputStream(in_file);
// 創建輸出文件對象
File out_file = new File(outPath);
// 創建文件輸出流(字節流)
FileOutputStream out = new FileOutputStream(out_file);
byte[] b = new byte[1024]; // 利用一個數組緩存,提高讀取效率
int n;
while ((n = in.read(b)) != -1) { // n = -1 說明已經讀到流的末尾了
// 注意這裏第三個參數不能是數組b的長度(1024),否則最後一次循環可能會多寫數據到out流中,
// 因爲文件的可讀字節不一定是b的長度(1024)的整數倍。
out.write(b, 0, n);
}
out.flush();
out.close();
in.close();
}
}
2. 網絡傳輸文件
要先啓動Server.java
,再啓動Client.java
。
Client.java
:
package note;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
public class Client {
// 服務器ip地址
private static String ip = "127.0.0.1";
// 服務器端口
private static int port = 10000;
//本地文件,即需要發送的文件
private static String client_file = "src/note/client.jpg";
public static void main(String[] args) {
try {
System.out.println("正在連接服務器。。。");
// 連接服務器
Socket socket = new Socket(ip, port);
System.out.println("與服務器連接成功!準備發送文件。。。");
// 獲取套接字輸出流
OutputStream out = socket.getOutputStream();
// 獲取本地文件輸入流
FileInputStream in = new FileInputStream(client_file);
// 緩存數組
byte[] b = new byte[1024];
// 每次讀出的字節長度
int n;
while ((n = in.read(b)) != -1) {
// 將字節數組b中長度爲n的子數組寫入out
out.write(b, 0, n);
}
out.flush();
out.close();
in.close();
socket.close();
System.out.println("文件發送成功!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
Server.java
:
package note;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
// 監聽10000端口號
private static int port = 10000;
// 本地文件,即接收到的文件存放的地址
private static String server_file = "src/note/server.jpg";
public static void main(String[] args) {
try {
System.out.println("服務器啓動中。。。");
// 監聽10000端口號,等待客戶端連接
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("服務器啓動成功,監聽" + port + "端口,等待客戶端連接。。。");
// 與客戶端建立連接
Socket server = serverSocket.accept();
System.out.println("與客戶端成功建立連接,接收文件中。。。");
// 獲取套接字輸入流
InputStream in = server.getInputStream();
// 獲取本地文件輸出流
FileOutputStream out = new FileOutputStream(server_file);
// 緩存數組
byte[] b = new byte[1024];
// 每次讀出的字節長度
int n;
while ((n = in.read(b)) != -1) {
// 將字節數組b中長度爲n的子數組寫入out
out.write(b, 0, n);
}
out.flush();
out.close();
in.close();
server.close();
serverSocket.close();
System.out.println("服務器接收文件成功!文件路徑:" + server_file);
} catch (IOException e) {
e.printStackTrace();
}
}
}