關於Java中的IO
概念
IO流就是內存與存儲設備之間傳輸數據的通道,管道。
流的分類
1.按方向(以JVM虛擬機爲參照物)
輸入流:將存儲設備中的內容讀入到內存中
輸出流:將內存中的內容寫入到存儲設備中
2.按單位(字節|字符)
字節流:以字節爲單位,可以操作所有類型的文件。
字符流:以字符爲單位,只能操作文本類型的文件。
3.按功能
節點流:具有基本的讀寫功能。
過濾流:在節點流的基礎上,增加新的功能。
節點流:完成數據讀寫 過濾流:爲其他流增強功能
IO編程的步驟
1.創建節點流
2.包裝過濾流
3.讀寫數據
4.關閉流
BIO
概念:
Java BIO (blocking I/O):同步並阻塞,服務器實現模式爲一個連接一個線程,即客戶端有連接請求時服務器端就需要啓動一個線程進行處理,如果這個連接不做任何事情會造成不必要的線程開銷,當然可以通過線程池機制改善。
應用場景:
BIO方式適用於連接數目比較小且固定的架構,這種方式對服務器資源要求比較高,併發侷限於應用中,JDK1.4以前的唯一選擇,但程序直觀簡單易理解。
字節流
InputStream字節輸入流的父類
InputStream裏的方法
int read() 讀一個字節 返回值:讀到的字節數據,會維護讀取文件的指針文件內容的位置,返回-1表示讀取結束。
int read(byte [] bs):嘗試讀滿bs數組,返回值:讀到的字節的個數 返回-1表示結束。
int read(byte [] bs ,int start ,int length):嘗試讀滿bs數組中的一段
FileInputStream文件輸入,功能屬於節點流
如果文件不存在,會拋出FileNotFoundException
OutputStream字節輸出流的父類
OutputStream裏的方法
write(int a) :(byte)a 寫一個字節
write(byte [] bs):及那個bs數組中所有的數據寫出
write(byte [] bs ,int start ,int length):將 bs數組中的一段寫出
FileOutputStream文件輸出,功能屬於節點流
如果文件已存在就會覆蓋源文件,如果文件不存在創建文件,如果在文件中追加內容就在聲明的構造方法中設置一個參數true
文件的拷貝
//第一種 效率極低
FileInputStream fis = new FileInputStream("輸入文件路徑");
FileOutputStream fos = new FileOutputStream("輸出文件路徑");
while(true){
int a = fis.read();
if(a==-1)break;
fos.wirte(a);
}
fis.close();
fos.close();
//第二種 效率高
FileInputStream fis = new FileInputStream("輸入文件路徑");
FileOutputStream fos = new FileOutputStream("輸出文件路徑");
byte [] bs = new byte[1024];
while(true){
int len = fis.read(bs);
if(len==-1)break;
fos.wirte(bs,0,lne);
}
fis.close();
fos.close();
過濾流
DataOutputStream : 過濾流 增強功能 可以寫出八種基本類型和字符串
//輸出一個Long類型的長整數
FileOutputStream fos = new FileOutputStream("文件路徑");
DataOutputStream out = new DataOutputStream(fos);
long a = 1234567890987654321L;
out.writeLong(a);
out.close();
DataInputStream過濾流 增強功能 從文件中讀入八種基本類型及字符串
//從文件中讀入一個Long類型的長整數
FileInputStream fis = new FileInputStream("文件路徑");
DataInputStream in = new DataInputStream(fis);
Long a = in.readLong();
System.out.println(a);
in.close();
緩衝流
提高IO效率 減少磁盤訪問次數 數據儲存正在緩衝區中,flush是將緩衝區的內容寫入文件中,也可以直接close
BufferedOutputStream/BufferedInputStream
FileOutputStream fos = new FileOutputStream("文件路徑");
BufferedOutputStream bos = new BufferedOutputStream(fos);
out.flush();//刷新緩衝區
out.close();//關閉資源
//第三種 使用緩衝流寫文件拷貝
FileInputStream fis = new FileInputStream("文件路徑");
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("文件路徑");
BufferedOutputStream bos = new BufferedOutputStream(fos);
byte [] bs = new byte [1024];
while(true){
int len = bis.read(bs);
bis.flush();
if(len==-1)break;
bos.write(bs,0,len);
bos.flush();
}
bis.close();
bos.close();
PrintStream 字節緩衝過濾流
ObjectOutputStream/ObjectInputStream 過濾流 通過流傳續對象:對象序列化 用transient修飾的屬性爲臨時屬性 不參與對象序列化
增強了緩衝區功能 增加了讀寫對象的功能 增強了讀寫八種基本類型和字符串的功能
readObject() 從流中讀取對象
writeObject(Object obj) 向流中寫入一個對象
package io流.過濾流;
import java.io.*;
/**
* Created by 拂曉 on 2019/9/22:10:53
*/
public class TestObjectStream {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Student jj = new Student("jj", 18, 60.5);
Student aa = new Student("aa", 18, 60.5);
Student bb = new Student("bb", 18, 60.5);
//輸出對象
FileOutputStream fos = new FileOutputStream("文件路徑");
ObjectOutputStream out = new ObjectOutputStream(fos);
out.writeObject(jj);
out.writeObject(aa);
out.close();
//讀入對象
FileInputStream fis = new FileInputStream("文件路徑");
ObjectInputStream in = new ObjectInputStream(fis);
while (true) {
Student s1 = (Student) in.readObject();
Student s2 = (Student) in.readObject();
in.close();
System.out.println(s1);
System.out.println(s2);
}
}
}
class Student implements Serializable{
String name ;
int age;
double score;
public Student() {
}
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
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 getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
}
關於對象序列化的細節
如何在不知道文件中有多少個對象的情況下,將所有的對象讀出來?
FileInputStream fis = new FileInputStream("文件路徑");
ObjectInputStream in = new ObjectInputStream(fis);
try{//利用抓取異常判斷文件是否讀完 跳出循環
while(true){
Object o = in.readObject();
System.out.Println(o);
}
}catch(Exception e){
System.out.println("文件讀完了")
}finaly{
in.close();
}
對象的反序列化
如果該類沒有實現序列化接口,那麼會通過調用該類的無參構造實現反序列化,重建對象,如果實現了序列化接口,那麼會通過序列化的規則重建該對象,不調構造方法。
自定義序列化
需要實現Serializable的子接口Externalizable,因爲自定義了序列化規則所以反序列化時就不會使用默認的反序列化規則,而是使用調用無參構造使用自定義的序列化規則
package io流.過濾流;
import java.io.*;
/**
* Created by 拂曉 on 2019/9/22:10:53
*/
public class TestObjectStream {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Student jj = new Student("jj", 18, 60.5);
Student aa = new Student("aa", 18, 60.5);
Student bb = new Student("bb", 18, 60.5);
//輸出對象
FileOutputStream fos = new FileOutputStream("文件路徑");
ObjectOutputStream out = new ObjectOutputStream(fos);
out.writeObject(jj);
out.writeObject(aa);
out.close();
//讀入對象
FileInputStream fis = new FileInputStream("文件路徑");
ObjectInputStream in = new ObjectInputStream(fis);
while (true) {
Student s1 = (Student) in.readObject();
Student s2 = (Student) in.readObject();
in.close();
System.out.println(s1);
System.out.println(s2);
}
}
}
class Student implements Externalizable{
String name ;
int age;
double score;
public Student() {
}
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
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 getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(name);
out.writeInt(age);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = in.readUTF();
age = in.readInt();
}
}
對象的淺克隆和深克隆
克隆 要調用Object.clone()方法 , 實現Cloneable接口 覆蓋clone方法
淺克隆:只克隆對象,無法實現克隆 對象裏的對象屬性
深克隆:不僅克隆該類對象,也克隆 對象裏的對象屬性
ByteArrayOutputStream / ByteArrayInputStream
往內存裏的字節數組輸出和輸入
字符流
可以幫我們解決字符的編碼問題。
Reader / Write 所有字符流的父類
FileReader / FileWrite 文件字符流
BufferedReader / BufferedWriter 緩衝 過濾流
PrintWirter 緩衝
使用字符流輸出和讀入數據
//使用字符流輸出和讀入數據
FileWrite fe = new FileWrite("文件路徑");
PrintWriter out = new PrintWrite(fe);
out.println("白日依山盡");
out.Println("黃河入海流");
out.println("欲窮千里目");
out.println("更上一層樓");
out.close();
FileReader fr = new FIleReader("文件路徑");
BufferedReader in = new BufferedReader(fr);
while(true){
String str = in.readLine();//讀一行
if(str==null)break;
System.out.println(str);
}
in.close();
字節流轉字符流
OutputStreamWrite / InputStreamWrite 過濾流
橋轉換類,將字節流轉換爲字符流
//使用將字節流轉成字符流輸出和讀入數據
OutputStream fos = new FileOutputStream("文件路徑)//節點流
Writer ow = new OutputStreamWrite(fos,"UTF-8");//橋轉換流 可以指定編碼方式
//PrintWriter 1.5之後可以直接指定文件名輸出 ,但是不能指定編碼方式了
PrintWriter out = new PrintWrite(ow);//過濾流
out.println("白日依山盡");
out.Println("黃河入海流");
out.println("欲窮千里目");
out.println("更上一層樓");
out.close();
InputStream fis = new FileInputStream("文件路徑")
Reader rd = new InputStreamReader(fis,"UTF-8");
BufferedReader in = new BufferedReader(rd);
while(true){
String str = in.readLine();//讀一行
if(str==null)break;
System.out.println(str);
}
in.close();
File類
IO流: 對文件中的內容進行操作。
File 類:對文件自身進行操作,代表磁盤上的一個文件或者一個目錄,例如刪除文件,文件重命名等。
File f = new File("文件名");//創建文件對象
f.createNewFile(); //創建文件
f.delete(); //刪除文件或者目錄,但是隻能刪除空目錄
f.mkdir(); //創建目錄
f.renameTo("new 文件名");//文件重命名
f.setReadOnly(); //設置文件爲只讀
f.getName(); //獲取文件名含擴展名
f.exists(); //判斷文件或目錄是否存在,存在返回true,不存在返回false
f.getAbsolutePath(); //獲得絕對路徑
f.listFiles(); //獲取當前目錄下所有的文件,文件夾
f.isFile(); //判斷File對象所對應的是否爲文件,而不是目錄
f.isDirectory(); //判斷File對象所對應的是否爲目錄,而不是文件
如何遞歸刪除文件
//創建要刪除的文件目錄對象
File f = new File("文件路徑");
deleteDir(f);//調用刪除方法
//聲明一個方法 準備遞歸調用
public static void deleteDir(File dir){
File [] fs = dir.listFiles();//獲取到目錄下的所有子目錄及文件
for(File f : fs){
if(f.isFile())f.delete();
if(f.isDirectory()){
deleteDir(f);
}
}
dir.delete();
}
如何獲取一個目錄下所有的.java文件
//列出dir目錄中所有的.java文件
static void listJavaFiles(File dir) {
dir.listFiles(new FileFilter(){//實現文件對象的文件過濾器
public boolean accept(File f) {
if(f.isDirectory()) return true;
if(f.isFile()) {
String name = f.getName();
return name.endWith(".java");
}
return false;
}
});
for(File f : fs){
if(f.isFile())System.out.println(f.getAbsolutePath());
if(f.isDirectory()) listJavaFiles(f);
}
}
NIO
概念
Java NIO (non-blocking I/O): Java1.4後出現的NIO, 同步非阻塞,服務器實現模式爲一個請求一個線程,即客戶端發送的連接請求都會註冊到多路複用器上,多路複用器輪詢到連接有I/O請求時才啓動一個線程進行處理。
應用場景:
NIO方式適用於連接數目多且連接比較短(輕操作)的架構,比如聊天服務器,併發侷限於應用中,編程比較複雜,JDK1.4開始支持。
ByteBuffer 獲取buffer對象的常用方法
//創建容量爲20的Buffer緩衝區對象
ByteBuffer buffer = ByteBuffer.allocate(20);
//返回一個字節數組的ByteBuffer對象
ByteBuffer buffer = ByteBuffer.wrap(byte [] bs);
三個指針
position:當前讀寫位置
limit:限制
capacity:容量
flip(): 把position重置爲0 調用的時機是從寫模式 -> 到讀模式時
clear(): 把position和limit都重置爲0 調用的時機是從讀模式 -> 到寫模式時
使用NIO寫數據
//聲明一個文件輸出的節點流
FileOutputStream fos = new FileOutputStream("文件路徑");
//變成了NIO文件輸出通道
FileChannel channel = fos.getChannel();
ByteBuffer buffer = ByteBuffer.wrap("HelloWorld".getBytes());
channel.write(buffer);
channel.close();
使用NIO讀數據
FileInputStream fis = new FileInputStream(文件路徑);
FileChannel channel = fis.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(10);
while(true){
int len = channel.read(buffer);
if(len==-1)break;
buffer.flip();
while(buffer.hasRemaining()){
buffer.get();//獲取一個字節
System.out.println((cahr)(buffer.get()));
}
buffer.clear();
}
channel.close();
NIO的字符流
Charset cs = Charset.forName("字符集");
//將字符串按照對應的字符集做編碼 從字符到字節
ByteBuffer bb = cs.encode("輸出的字符串內容");
//從字節到字符 解碼
CharBuffer cb = cs.decode(bb);
NIO的文件拷貝
第一種
ByteBuffer buffer = ByteBuffer.allocate(1024);
FileInputStream fis = new FileInputStream("文件路徑");
FileOutputStream fos = new FileOutputStream("文件路徑");
Filechannel channel1= fis.getChannel();
FileChannel channel2 =fos.getChannel();
while(true){
int a = channel1.read(buffer);
if(a==-1)break;
buffer.flip();//從寫模式切換到讀模式
channel2.write(buffer);
buffer.clear();//從讀模式切換到寫模式
}
channel1.close();
channel2.close();
第二種
使用MappedByteBuffer 虛擬內存映射 將大塊數據放入內存的技術
FileInputStream fis = new FileInputStream("文件路徑");
FileOutputStream fos = new FileOutputStream("文件路徑");
Filechannel channel1= fis.getChannel();
FileChannel channel2 =fos.getChannel();
MappedByteBuffer buffer = channel1.map(FileChannel.MapMode,READ_ONLY,0,channel1.size());
channel2.write(buffer);
channel1.close();
channel2.close();
文件拷貝最快的方法
FileInputStream fis = new FileInputStream("文件路徑");
FileOutputStream fos = new FileOutputStream("文件路徑");
Filechannel channel1= fis.getChannel();
FileChannel channel2 =fos.getChannel();
channel1.transferTo(0,channel1.size(),channel2);
channel1.close();
channel2.close();
AIO(待補充)
概念:
Java AIO(NIO.2) (Asynchronous I/O) : 異步非阻塞,服務器實現模式爲一個有效請求一個線程,客戶端的I/O請求都是由OS先完成了再通知服務器應用去啓動線程進行處理,
應用場景:
AIO方式使用於連接數目多且連接比較長(重操作)的架構,比如相冊服務器,充分調用OS參與併發操作,編程比較複雜,JDK7開始支持。