File文件基礎
文件與目錄抽象路徑名稱的表示,其構造方法有四個
File(File parent,String child):從抽象父目錄下創建一個File實例。
File(String parent,String child):從父目錄下創建一個File實例。
File(String pathname):從指定路徑下創建一個File實例。
File(URI path):從URI轉換成抽象路徑下創建一個File實例。
listFiles()方法:返回一個抽象路徑名數組,這些抽象路徑名錶示目錄中的子項(文件或目錄)
File file = new File("/home/colin");
File[] childs = file.listFiles();
listFiles(FileFilter filter):FileFilter是抽象路徑名的過濾器,可以用於返回過濾器要求的子目錄
File file = new File("/home/colin");
//有沒有考慮過,java中不允許實現接口實例,但是在匿名內部類中卻實現了接口,比如這個FileFilter接口
//實際上如果查看編譯後的代碼(會發現編譯後的代碼中有一個class文件,這個class實現了這個接口,並重寫了這個接口的方法)
//所以這是一種虛擬實例化接口,或者理解爲間接實例化接口。
File[] childs = file.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.getName().startsWith(".");
}
});
還有一個listFiles(FilenameFileter filter):FilenameFile用於檢測指定文件是否包含在特定目錄下
其他API相對簡單了,獲取文件狀態、創建刪除文件
RandomAccessFile隨機讀寫文件
File 實例只涉及到對文件的操作,java提供了可以對文件隨機訪問的操作,訪問包括讀和寫操作,這種實現是基於指針的操作。
兩個構造方法:
RandomAccessFile(File name,String model)
RandomAccessFile(String name,String model)
model指定的是對文件隨機訪問操作模式,有兩種模式分別是隻讀模式和讀寫模式,"r"表示文件的訪問是隻讀的,"rw"表示讀和寫模式。
RandomAccessFile file = new RandomAccessFile("/home/colin","rw");
寫數據:
write(byte[] b):將指定自己數組b寫到隨機讀寫文件中,寫到指針的位置
write(byte[] b,int off,int len):指定了起始位置和長度
write(int n):將n的低八位寫到文件中去
他還有寫入特定特性的方法:writeBoolean(boolean b)、writeInt(int n)、writeDouble(double v)、writeChart(chart c)、writeCharts(String s)、writeByte(int n)、writeBytes(String s)、writeFloat(Float t)等
讀數據:
int read():讀取1byte(8位)放到int的低八位中,高24爲爲0。如果讀到-1,說明讀到了文件的末尾
int read(byte[] b):最多讀取b.length個字節到數組中,返回值爲實際讀取到的字節量
int read(byte[] b,int off, int length):指定讀取數組的起始位置和讀取長度
相應的還有讀取特定類型數據的read
readBoolean()、readFloat()等等,特別還有一個readLine()讀取當前位置的一行
這些都是從當前指針位置開始讀取的
釋放關聯的資源:void close()
獲取當前RandomAccessFile的指針:long getFilePoint(),以字節爲單位,比如如果一個Int是4位
移動當前指針位置:void seek(long pos)
跳過n個字節:int sikpBytes(int n):返回跳過的實際字節數,n爲負數不跳過任何字節
RandomAccessFile file = null;
try {
file = new RandomAccessFile("/home/colin/test.txt","rw");
//byte[] b = "this is sample RandomAccessFile".getBytes();
//file.read(b);
byte[] b = new byte[10];
while(file.read(b) >0){
System.out.println(new String(b));
System.out.println(file.getFilePointer()); //獲取文件指針
file.skipBytes(1); //每次讀取跳過一個字節
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally{
if(file != null)
file.close();
}
基本IO
輸入是指從外界進入程序方向,即當我們需要讀取數據的時候,使用輸入。
輸出是指從程序發送到外界方向,通常我們需要寫出數據到外界,所以輸出是用來寫數據的。
流的分類
根據單位:字節流(8bit,圖片視頻等)和字符流(16bit,文本)
根據角色:節點流(直接作用在文本上的流,直接與特定的地方(磁盤、內存)相連)和處理流(在節點流外層的流,是對一個已經存在流的連接和封裝,在所封裝的流的功能上實現數據的讀寫)
java中40多種流都是從4個基類派生出來的
字節流 字符流
輸入:InputStream Reader
輸出:OutputStream Writer
InputStream和OutputStream
二者都是字節流的抽象基類,定義了基本的read和write方法,read()、read(byte[] a,int off,int length)、read(byte[] b)、write(int b)、write(byte[] b)、write(byte[] b,int off ,int len)
Reader和Writer
reader和writer都是字符流的抽象基類,定義了讀取字符的read和write。read()、read(char[] cbuf)、read(char[] cbuf,int off, int len)、write(char c)、write(char[] cbuf)、cbuf(cbuf,int off ,int len)
文件流
文件字節流:
FileOutputStream可以使用以下幾種方法構造:
FileOUtputStream(File file)
FileOutputStream(String name)
指定寫入的文件,如果該文件存在的化會清除該文件上面的內容
FileOutputStream(File file,boolean append)
FileOutputStream(String name,boolean append)
如果指定append參數爲true,則如果寫入的文件存在,是以追加的方式進行寫入的
FileInputStream可以使用以下方法構造:
FileInputStream(File file)
FileOutputStream(String name)
文件字節流實現了inputStream和outputStream的基本read和write
注意如果讀取的時候返回-1,則說明讀取到了EOF
利用文件流實現文件的複製:
File file1 = new File("/home/colin/hello.py");
File file2 = new File("/home/colin/hello2.py");
FileInputStream input = null;
FileOutputStream output = null;
try {
input = new FileInputStream(file1);
output = new FileOutputStream(file2);
byte[] b = new byte[10];
int len = -1;
while((len=input.read(b))!=-1){
output.write(b, 0, len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
//先關輸入流,再關輸出流
if(inputput != null)
inputput.close();
if(output != null)
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
FileReader和FileWriter與FileInputStream和FileOutputStream使用情況一樣。
緩衝流
BufferedInputStream原理:緩衝字節輸入流,內部維護一個緩衝區,該流會儘可能一次性讀取若干字節到緩衝區,然後逐一返回知道緩衝區中的數據被全部讀取完畢,從而減少讀取次數,提高讀取效率,BIS就是一個處理流,該流提供了緩衝功能。他還提供了mark、reset和skip方法
提供的構造方法:
BufferedInputStream(InputStream input)
BufferedInputStream(InputStream input,int size):size指定緩衝區大小
BufferedOutputStream原理:緩衝輸出流,內部維護一個緩衝區,每當向該流寫入數據的時候,都會現將數據存儲緩衝區中,當緩衝區已滿的時候,緩衝流會將數據一次性全部寫出。
提供的構造方法:
BufferedOutputStream(OutputStream out)
BufferedOutputStream(OutputStream out,int size):指定緩衝區大小
BufferedOutputStream只提供了write和flush()方法,flush()清空緩衝區,將緩衝區中的數據強制寫入。
利用緩衝流實現文件複製:
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream(new File("/home/colin/hello.py")));
bos = new BufferedOutputStream(new FileOutputStream(new File("/home/colin/hello3.py")));
int len = -1;
byte[] b = new byte[10];
while((len=bis.read(b)) !=-1){
bos.write(b, 0, len);
}
bos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
//先關輸入流,再關輸出流
if(bis != null)
bis.close();
if(bos != null)
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
BufferedReader和BufferedWriter與BufferedInputStream和BufferedOutputStream實現原理是一樣的,但是BufferedWriter提供了newLine(),寫入換行符的方法
對象流
對象序列化:對象存在內存中,有時候需要將對象保存到磁盤上,或者將對象進行傳輸等這樣的操作。這時候需要將對象轉化爲一個字節序列,這個過程稱爲對象序列化。
對象反序列化:有時候需要將字節序列轉化成爲對應的對象,這個過程成爲對象的反序列化。
ObjectOutputStream:用來將對象進行序列化的輸出流
提供的構造方法:
ObjectOutputStream()
ObjectOutputStream(OutputStream out)
使用其write方法,將對象轉化成爲一個字節序列後寫出,write方法提供了諸如writeInt()等此類方法。
ObjectInputStream:用來將對象反序列化
提供的構造方法:
ObjectInputstream()
ObjectInputStream(InputStream input)
使用其提供的readObject()方法,讀取字節並轉化爲對應的對象
這個整體思路是要對對象進行傳遞,需要將對象轉換成爲字節流以便傳輸,所以首先通過ObjectOutputStream進行寫入,將對象寫成字節進行傳輸。傳遞到目標位置後需要讀取,這時候將傳輸的字節轉換成爲對象。
如果使用ObjectOutputStream進行序列化寫入,需要序列化對象實現Serializable接口,該接口並沒有任何方法只是序列化標誌。通常實現該接口需要給出一個serialVersionUID,表明該類版本,若不顯示聲明編譯器也會經過計算給出一個serialVersionUID,但是不同編譯器實現有所不同,所以如果想要跨平臺,都應該顯示聲明版本號。當類的對象序列化到磁盤上面,之後隨之需求改變,改變了類的屬性,那麼反序列化就會出現InvalidClassException,這樣就會造成不兼容問題。但當serialVersionUID相同時,會將不一樣的Field以type的預設值反序列化,可以避開不兼容問題
transient關鍵詞:當我們對對對象進行序列化後,得帶的字節序列往往比較大,有時我們在對一個對象進行序列化時可以忽略某些不必要的屬性,從而對序列化後得到的字節序列瘦身,可以將其聲明爲transient,這樣這些屬性在序列化時會被忽略。
實例:
/*
*構造Emp類,對其對象進行序列化與反序列化
*/
public class Emp implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
private double salary;
private transient String description; //使用transient修飾,在反序列化時輸出null
public Emp(String name,int age,double salary,String description){
this.name = name;
this.age = age;
this.salary = salary;
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return "Emp [name="+name+",age="+age+",salary="+salary+",description="+description+"]";
}
}
/*
* 將Emp對象序列化,並且存儲
*/
public static void testOos(){
ObjectOutputStream oos = null;
try {
FileOutputStream fos = new FileOutputStream(new File("/home/colin/oos.obj"));
oos = new ObjectOutputStream(fos);
Emp emp = new Emp("colin", 23, 13000, "I want you!!!");
oos.writeObject(emp);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(oos != null)
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
* 將存儲的Emp對象反序列輸出
*/
public static void testOis(){
ObjectInputStream ois = null;
try {
FileInputStream fis = new FileInputStream(new File("/home/colin/oos.obj"));
ois = new ObjectInputStream(fis);
Emp emp = (Emp)ois.readObject();
System.out.println(emp.getName()+","+emp.getAge()+","+emp.getSalary()+","+emp.getDescription());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}