IO部分的知識比較零散,而且一般是配合其它程序來共同完成功能的。爲了清楚起見,筆者還是按照知識點逐一列舉代碼,並寫一些自己的評論。
1. 文件類File
1.1 創建新文件
import java.io.*;
class Test
{
public static void main(String[] args) throws IOException
{
File f = new File("F:" + File.separator + "test.txt");
f.createNewFile();
}
}
這裏的文件路徑名沒有像視頻中使用\\分隔符,而是用File.separator這個File類中的一個靜態屬性字符串來作爲分隔符。這樣可以避免分隔符不兼容問題,而且路徑分層更清晰一些。createNewFile()方法要特別注意,只有調用它的File類對象的路徑名指向的文件不存在時,纔會創建新文件。已有的文件不會被替換。1.2 刪除文件/目錄
import java.io.*;
class Test
{
public static void main(String[] args) throws IOException
{
//File f = new File("F:" + File.separator + "dir_to_be_deleted");
File f = new File("F:" + File.separator + "test.txt");
if(f.exists())
f.delete();
else
System.out.println("File not found.");
}
}
delete()方法不僅可以刪除文件,也可以刪除目錄,不過這個目錄必須是空的。這樣做可以減少目錄被誤刪除的可能性,但是也像後面例子裏表示的,使得刪除目錄需要先遞歸刪除文件,使得操作複雜化。1.3 創建目錄
import java.io.*;
class Test
{
public static void main(String[] args) throws IOException
{
File f = new File("F:" + File.separator + "dir_to_be_built");
f.mkdir();
}
}
1.4 列出目錄中的下級文件和目錄
import java.io.*;
class Test
{
public static void main(String[] args) throws IOException
{
File f = new File("F:" + File.separator);
File[] str = f.listFiles();
for (int i = 0; i < str.length; i++)
{
System.out.println(str[i]);
}
}
}
listFiles()返回一個File類型的數組,數組元素就是要查找的下級文件和目錄名。如果調用這個方法的文件類對象f使用的是絕對路徑,則返回的也是絕對路徑,否則是相對路徑。1.5 列出目錄全部內容
import java.io.*;
class Test
{
public static void main(String[] args)
{
File f = new File("F:" + File.separator);
}
//遞歸開始
public static void print(File f)
{
if(f != null)
{
if(f.isDirectory()) //判斷爲目錄則遞歸查找所有下級文件和目錄
{
File[] fileArr = f.listFiles();
if(fileArr != null)
{
for (int i = 0; i < fileArr.length; i++)
{
//遞歸調用
print(fileArr[i]);
}
}
}
else
{
System.out.println(f);
}
}
}
}
遞歸時要特別注意排除引用爲空的情況。當發生IO錯誤時listFiles()方法會返回null值。這裏不能像前面程序在main方法上拋出異常,只能選擇性地忽略null值繼續查找直到所有文件和目錄都查找完畢。2. 文件字符流類 FileReader/FileWriter
2.1 向文件寫入數據
import java.io.*;
class Test
{
public static void main(String[] args) throws IOException
{
//第二個參數默認爲false,覆蓋原文件。若爲true,追加在文件後。
File f = new File("F:" + File.separator + "test.txt");
Writer out = new FileWriter(f); //向下轉型
String str = "hello";
out.write(str); //接收字符,字符數組或字符串作爲參數
out.close(); //關閉流,將緩衝區內容輸出到文件
}
}
如果使用FileWriter類的另一個構造方法,可以更簡便寫作:Writer out = new FileWriter("F:" + File.separator + "test.txt");2.2 從文件中讀取數據,無緩衝區
import java.io.*;
class Test
{
public static void main(String[] args) throws IOException
{
Reader r = new FileReader("F:" + File.separator + "test" + File.separator + "test2.java");
int c;
//read方法返回當前讀到的單個字符的ASCII碼,用int型表示
while((c = r.read()) != - 1) //讀到 - 1表示結尾
{
System.out.print((char) c); //顯示字符需要將ASCII碼轉換回字符類型
}
r.close();
}
}
2.3 從文件中讀取數據,有緩衝區
class Test
{
public static void main(String[] args) throws IOException
{
Reader r = new FileReader("F:" + File.separator + "test" + File.separator + "test2.java");
char[] buf = new char[100]; //讀緩衝區
int len = 0;
//read方法在有數據寫入緩衝區時返回寫入數據的大小,沒有數據寫入緩衝區時(說明已到末尾)返回 -1
while((len = r.read(buf)) != - 1)
{
System.out.print(new String(buf, 0, len));
System.out.println(len);
}
r.close();
}
}
2.2和2.3兩個例子涉及了兩個重載的read方法。無參read方法返回的是當前讀到的字符的ASCII碼,-1爲結尾。有參read方法接收緩衝區引用作爲參數,返回的是當次讀取所佔緩衝區的大小,也是 -1爲結尾。爲提高效率,應儘量用緩衝區。3. 文件字節流類FileInputStream/FileOutputStream
3.1 向文件寫入數據
import java.io.*;
class Test
{
public static void main(String[] args) throws IOException
{
OutputStream o = new FileOutputStream("F:" + File.separator + "test.txt"); //加上第二個參數true則會追加,否則覆蓋
String str = "text";
byte[] arr = str.getBytes();
o.write(arr[0]);
o.write(str.getBytes()); //接收字節或字節數組作爲參數
o.flush(); //將寫緩衝區內容輸出到文件
o.close();
}
}
3.2 從文件中讀取數據,無緩衝區
import java.io.*;
class Test
{
public static void main(String[] args) throws IOException
{
InputStream in = new FileInputStream("F:" + File.separator + "test.txt");
int Byte = 0;
while((Byte = in.read()) != - 1) //無參read方法返回int型數據來表示當前讀到的字節
{
System.out.print((char)Byte); //如果是文本,字節數據是字符的ASCII碼
}
in.close();
}
}
3.3 從文件中讀取數據,有緩衝區
import java.io.*;
class Test
{
public static void main(String[] args) throws IOException
{
InputStream in = new FileInputStream("F:" + File.separator + "test.txt");
byte[] buf = new byte[256]; //字節數組作緩衝區
int len = 0;
while((len = in.read(buf)) != - 1) //有參read方法返回讀完當前緩衝區後讀取的長度
{
System.out.print(new String(buf, 0, len));
}
in.close();
}
}
3. 字符流與字節流的比較
4. 轉換流InputStreamReader/OutputStreamWriter
轉換流是溝通字符流和字節流的橋樑,使得我們可以將從硬盤/內存讀入的字節流轉換成易於處理的字符流,處理完畢後轉回字節流寫回硬盤/內存。
4.1 輸出字符流轉換爲字節流
import java.io.*;
class Test
{
public static void main(String[] args) throws IOException
{
//轉換流作爲文件字節輸出流的包裝被賦值給字符輸出流
Writer w = new OutputStreamWriter(new FileOutputStream("F:" + File.separator + "test.txt"));
//後面的操作與字符輸出流相同
String str = "text";
w.write(str);
w.flush();
w.close();
}
}
這裏也用到了向下轉型。OutputStreamWriter是Writer類的子類。4.2 輸入字節流轉換爲字符流
import java.io.*;
class Test
{
public static void main(String[] args) throws IOException
{
//轉換流作爲文件字節輸入流的包裝被賦值給字符輸入流
Reader in = new InputStreamReader(new FileInputStream("F:" + File.separator + "test.txt"));
char[] buf = new char[256]; //字符數組作緩衝區
int len = 0;
while((len = in.read(buf)) != - 1) //有參read方法返回讀完當前緩衝區後讀取的長度
{
System.out.print(new String(buf, 0, len));
}
in.close();
}
}
InputStreamReader是Reader類的子類。5. 緩衝流類BufferedReader, BufferedWriter, BufferedInputStream, BufferedOutputStream
5.1 字符緩衝流
import java.io.*;
class Test
{
public static void main(String[] args) throws IOException
{
//標準輸入流是字節流,需要先轉換成字符流再連接字符緩衝流
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
//標準輸出流是字節流,需要先轉換成字符流再連接字符緩衝流
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
String str = null;
while((str = bufr.readLine()) != null) //一次讀取一行
{
if(str.equals("end")) //從屏幕輸入時要自定義結束標誌
break;
bufw.write(str);
bufw.write("\n"); //或者用bufw.newLine()來輸入行分隔符
bufw.flush();
}
bufr.close();
bufw.close();
}
}
兩種字符緩衝流有8192字符的緩衝區。BufferedReader在讀取文本文件時,會先儘量從文件中讀入字符並置入緩衝區,之後調用read()方法,會先從緩衝區中讀取。如果緩衝區數據不足,纔會從文件中讀取。BufferedWriter在寫入文件時,會將數據先寫入緩衝區,緩衝區滿後再寫入到文件。5.2 字節緩衝流
import java.io.*;
class Test
{
public static void main(String[] args) throws IOException
{
//原文件
File src = new File("E:" + File.separator + "src.jpg");
//複製到
File dest = new File("F:" + File.separator + "dest.jpg");
//用字節輸入緩衝流包裝字節輸入節點流
BufferedInputStream bufi = new BufferedInputStream(new FileInputStream(src));
//用字節輸出緩衝流包裝字節輸出節點流
BufferedOutputStream bufo = new BufferedOutputStream(new FileOutputStream(dest));
//暫存讀到的字節
int tmp = 0;
//循環讀寫直到遇到文件結束標誌
while((tmp = bufi.read()) != - 1)
{
bufo.write(tmp);
}
bufi.close();
bufo.close();
}
}
6.其它包裝流
6.1 數據操作流DataOutputStream, DataInputStream類
import java.io.*;
class Test
{
public static void main(String[] args) throws IOException
{
//數據輸出流包裝了文件輸出流以便向其寫入基本類型數據
DataOutputStream out = new DataOutputStream(new FileOutputStream("F:" + File.separator + "test.txt"));
//用一個字節寫入boolean值
boolean b = true;
out.writeBoolean(b);
//用一個字節寫入byte值
byte B = 2;
out.writeByte(B);
//用兩個字節寫入char值
char c = 'A';
out.writeChar(c);
//用四個字節寫入int值
int i = 0;
out.writeInt(i);
//用四個字節寫入float值
float f = 2.0f;
out.writeFloat(f);
//用八個字節寫入double值
double d = 2.0;
out.writeDouble(d);
out.close();
}
}
用DataInputStream包裝字節輸入節點流可以讀出這些基本類型數據:import java.io.*;
class Test
{
public static void main(String[] args) throws IOException
{
//數據輸入流包裝了文件輸入流以便從其讀入基本類型數據
DataInputStream in = new DataInputStream(new FileInputStream("F:" + File.separator + "test.txt"));
//用一個字節讀入boolean值
boolean b = in.readBoolean();
System.out.println(b);
//用一個字節讀入byte值
byte B = in.readByte();
System.out.println(B);
//用兩個字節讀入char值
char c = in.readChar();
System.out.println(c);
//用四個字節讀入int值
int i = in.readInt();
System.out.println(i);
//用四個字節讀入float值
float f = in.readFloat();
System.out.println(f);
//用八個字節讀入double值
double d = in.readDouble();
System.out.println(d);
in.close();
}
}
輸出:true
2
A
0
2.0
2.0
6.2 邏輯串聯流SequenceInputStream
import java.io.*;
import java.util.*;
class Test
{
public static void main(String[] args) throws IOException
{
//創建串聯流
SequenceInputStream sis = null;
//創建輸出流
BufferedOutputStream bufo = null;
//創建Vector框架, 將要合併的節點輸入流加入框架中
Vector<InputStream> v = new Vector<InputStream>();
//新的流依次添加到Vector的末尾
v.addElement(new FileInputStream("F:" + File.separator + "sub1.txt"));
v.addElement(new FileInputStream("F:" + File.separator + "sub2.txt"));
v.addElement(new FileInputStream("F:" + File.separator + "sub3.txt"));
//返回Vector的枚舉給枚舉接口用於生成枚舉對象
Enumeration<InputStream> e = v.elements();
//串聯流來自於枚舉對象
sis = new SequenceInputStream(e);
//輸出流輸出到指定文件
bufo = new BufferedOutputStream(new FileOutputStream("F:" + File.separator + "sum.txt"));
//字節緩衝區
byte[] buf = new byte[1024];
//當前一次讀取的數據量
int len = 0;
//讀取合併後的流並寫入目標文件
while((len = sis.read(buf)) != - 1)
{
bufo.write(buf, 0, len);
bufo.flush(); //輸出緩衝區的內容
}
}
}
6.3 對象序列化流ObjectInputStream, ObjectOutputStream
import java.io.*;
public class Test
{
public static void main(String[] args) throws Exception
{
Student stu1 = new Student("Jack", 21);
Student stu2 = new Student("Tom", 22);
//序列化流包裝文件輸出流以便將序列化的Student類對象輸出到文件
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream("F:" + File.separator + "object.txt"));
//輸出對象
oo.writeObject(stu1);
oo.writeObject(stu2);
//關閉流
oo.close();
//序列化流包裝文件輸入流以便將序列化的Student類對象信息從文件讀入到對應引用
ObjectInputStream oi = new ObjectInputStream(new FileInputStream("F:" + File.separator + "object.txt"));
//讀入對象, readObject()方法返回Object類型,必須強制轉換
Student r1 = (Student) oi.readObject();
Student r2 = (Student) oi.readObject();
//顯示讀入的內容
System.out.println(r1);
System.out.println(r2);
//關閉流
oi.close();
}
}
class Student implements Serializable //實現序列化接口,具有序列化能力
{
String name = null;
int age = 0;
public Student(String name, int age)
{
this.name = name;
this.age = age;
}
@Override
public String toString()
{
return "name : " + name + ", age : " + age;
}
}
輸出:name : Jack, age : 21
name : Tom, age : 22
6.4 打印流PrintStream
import java.io.*;
public class Test
{
public static void main(String[] args) throws IOException
{
//此時輸出到屏幕
System.out.println("screen");
//out是個static類型的引用,只需修改一次, 將重定向目標作爲構造參數傳給PrintStream構造方法
System.setOut(new PrintStream(new FileOutputStream("F:" + File.separator + "redirect.txt")));
System.out.println("redirected to file");
}
}