IO流
IO流用來處理裝置之間的資料傳輸,傳輸是通過流的方式
Java用于操作流的對象都在java.io包中
流就是指一連串流動的字元,以先進先出的方式發送資訊的通道。
流的方式
按照流的方向劃分
輸入流:将外部資料源的資料轉換成流,程式通過讀取流中的資料,完成對資料源讀取的通路
輸出流:将流中的資料轉換到對應的資料源中,程式通過向流中寫入資料,完成對資料源寫入
按照類型劃分
位元組流:以位元組為機關(8位),可以通路所有檔案
字元流:以字元為機關(16位Unicode),隻能通路文本檔案
輸出流
比如我們經常使用的 System.out.println("cocci"); 就是将字元串輸出到控制台中,或者說輸出到螢幕中,這裡的螢幕就是輸出裝置,由程式将cocci通過流輸出到目的地。
輸出裝置除了螢幕,還有列印機、檔案等。
輸入流
比如使用鍵盤接收資料 Scanner sc = new Scanner(System.in); 這裡的System.in就是輸入流,程式從資料源這裡指鍵盤去讀取資料通過流輸入到程式當中。
輸入裝置除了鍵盤,還有掃描器、檔案等。
File類
檔案和目錄路徑名的抽象表示形式,表示磁盤上的檔案或目錄
檔案:可認為是相關記錄或放在一起的資料的集合
構造方法
示例:以下三種方式等價,各有不同的使用場景
//Windows中路徑分隔可使用/或者\\
File file1 = new File("d:\\java\\a.txt");
File file2 = new File("d:\\java","a.txt");
File file3 = new File(new File("d:\\"),"java\\a.txt");
常用方法
注意事項
delete()方法在删除目錄時如果其内有内容則無法删除,需要先清空目錄下的内容,再删除目錄本身
isDirectory()和isFile()方法在判斷是否為檔案或者目錄時,如果檔案或目錄不存在則傳回false
createNewFile()方法在建立檔案時,如果檔案所在的目錄不存在則建立失敗并抛出異常
String[] list()和 File[] listFiles() 都是傳回對象包含的所有的檔案和目錄,傳回類型不同
檔案與目錄的相關操作
public static void main(String[] args) {
File file=new File("d:\\test\\java");
if(file.exists()){
System.out.println("目錄存在,删除目錄");
file.delete(); //隻會删除一級目錄,即java
}else{
boolean b=file.mkdirs(); //建立多級目錄
System.out.println("建立檔案目錄結果:" + b);
}
File file2=new File("d:\\test\\a.txt");
if(file2.exists()){
System.out.println("檔案存在,删除檔案");
file2.delete();
}else{
try {
file2.createNewFile(); //建立檔案
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("建立檔案");
}
//重命名,把a.txt重命名為b.txt
//file2.renameTo(new File("d:\\test\\b.txt"));
//把b.txt剪切到java目錄下并重命名為c.txt
file2.renameTo(new File("d:\\test\\java\\c.txt"));
}
擷取File對象的所有子檔案和目錄
public class TestFile {
public static void main(String[] args) {
//擷取磁盤的所有根目錄
File[] files =File.listRoots();
for(int i=0;i
System.out.println(files[i].getPath());
//輸出的是 C:\ D:\
}
TestFile tf = new TestFile();
//列印Silly目錄下的所有子目錄和檔案
File file=new File("d:\\Silly");
tf.printFile(file,"");
}
public void printFile(File file,String str){
System.out.println(str + file.getName());
if(file.isDirectory()){
File[] files=file.listFiles();
for(int i=0;i
//遞歸調用
printFile(files[i], str+"\t");
}
}
}
}
位元組流
位元組輸入流
InputStream,此抽象類是表示位元組輸入流的所有類的超類,其主要子類如下
位元組輸出流
OutputStream,此抽象類是位元組輸出流的所有類的超類,其主要子類如下
過濾器輸入輸出流
FileInputStream
從檔案系統中的某個檔案中獲得輸入位元組
用于讀取諸如圖像資料之類的原始位元組流
構造方法
主要方法
read方法不帶參數的傳回值為讀取的單個位元組值,帶參數的傳回值表示讀取的位元組長度。如果傳回值為-1,則表示已經達到檔案末尾!
FileOutputStream
構造方法
主要方法
示範Demo
public class IOTest {
public static void main(String[] args) {
OutputStream fos = null;
InputStream fis = null;
try {
fos=new FileOutputStream("d:\\silly.txt");
String str="最好的我們隔了一整個青春";
byte[] words=str.getBytes(); //把字元串編碼成位元組序列
//寫入操作
fos.write(words, 0, words.length);
System.out.println("寫入成功!");
fis = new FileInputStream("d:\\silly.txt");
StringBuilder sb = new StringBuilder();
byte[] buf = new byte[1024]; //位元組數組緩存資料
int n = 0; //記錄讀取的位元組長度
//循環讀取資料
while((n = fis.read(buf)) != -1){
//這裡使用三個參數的構造方法,因為最後一次讀取的長度可能達不到buf數組的長度
//是以根據實際讀取的長度n去構造對象更合理
sb.append(new String(buf, 0, n));
buf = new byte[1024]; //重新初始化,避免資料重複
}
System.out.println(sb.toString());
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
//釋放資源
if(fos != null) fos.close();
if(fis != null) fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
檔案複制
public static void main(String[] args) {
//将d盤的圖檔複制到桌面
InputStream is = null;
OutputStream os = null;
try {
//執行個體化輸入輸出流對象
is = new FileInputStream("d:\\girl.jpg");
os = new FileOutputStream("C:\\Users\\ruoxiyuan\\Desktop\\mm.jpg");
//定義一個2048位元組的緩存
byte[] buffer=new byte[2048];
int len= 0; //讀取的位元組長度
while((len = is.read(buffer)) != -1){
//最後一次讀取的長度len不一定能達到buffer數組的長度,也就是空間有可能富餘
//如果直接使用write(buffer)方法可能導緻寫入更多的位元組,新檔案會稍大
os.write(buffer, 0, len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
//釋放資源
if(is != null) is.close();
if(os != null) os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
字元流
字元輸入流
Reader,此抽象類是表示字元輸入流的所有類的超類,其主要子類如下
字元輸出流
Writer,此抽象類是表示字元輸出流的所有類的超類,其主要子類如下
位元組字元轉換流
InputStreamReader:是位元組流通向字元流的橋梁,它使用指定的 charset 讀取位元組并将其解碼為字元。
OutputStreamWriter:是字元流通向位元組流的橋梁,它使用指定的 charset 将寫入其中的字元編碼成位元組。
其使用的字元集可以由構造方法指定,或者使用平台預設的字元集。
FileReader
用來讀取字元檔案的便捷類,使用預設字元集進行編碼,InputStreamReader的子類
構造方法
常用方法
FileWriter
用來寫入字元檔案的便捷類,使用預設字元集進行編碼,OutputStreamWriter的子類
構造方法
如果使用new FileWriter(file),已有檔案内容此時會被清空,設定第二個參數為true表示追加内容
常用方法
示範Demo
public class IOTest {
public static void main(String[] args) {
try {
FileWriter writer = new FileWriter("d:\\silly.txt");
String str = "成功的人隻會去做他們該做的事情,隻會面對他們的困難,不會有抱怨,"
+ "更不會有羨慕,因為他們心中有目标,理想,動力以及那顆沉澱的心";
writer.write(str);
writer.flush(); //重新整理緩沖區
FileReader reader = new FileReader("d:\\silly.txt");
char[] buffer = new char[1024];//定義緩沖區
int len = 0;
while((len = reader.read(buffer)) != -1){
String res = new String(buffer, 0, len);
System.out.println(res);
}
writer.close();
reader.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
緩沖流
使用緩沖流可以提高讀寫速度,注意緩沖流在調用write方法寫入資料時,是寫入的緩沖區,如果緩沖區滿了自動執行寫操作,緩沖區不滿則需要執行flush方法強制寫入到輸出裝置,調用close方法也會強制寫入
位元組緩沖流
緩沖輸入流BufferedInputStream,緩沖輸出BufferedOutputStream
隻是對位元組流進行了包裝,使用方式和位元組流基本一緻
構造方法:
public BufferedInputStream(InputStream in)
public BufferedOutputStream(OutputStream out)
字元緩沖流
緩沖輸入流BufferedReader
構造方法:public BufferedReader(Reader in)
特殊方法:String readLine() 讀取一個文本行
緩沖輸出流BufferedWriter
構造方法:public BufferedWriter(Writer out)
特殊方法:void newLine() 寫入一個行分隔符
示範Demo
public class IOTest {
public static void main(String[] args) {
try {
FileWriter writer = new FileWriter("d:\\silly.txt");
BufferedWriter bw = new BufferedWriter(writer);
bw.write("書山有路勤為徑");
bw.newLine(); //寫入換行符(根據平台寫入對應的換行符)
bw.write("學海無涯苦作舟");
bw.newLine();
//windows下使用\r\n也能換行
bw.write("世間安得兩全法" + "\r\n");
bw.write("不負如來不負卿");
bw.flush(); //必須重新整理緩沖區
String record = null; //存儲檔案的内容
int count = 0; //記錄行數
FileReader reader = new FileReader("d:\\silly.txt");
BufferedReader br = new BufferedReader(reader);
//每次讀取一整行資料,傳回值為空時說明讀取到檔案末尾
while((record = br.readLine()) != null){
count++;
System.out.println("目前行數"+count+":"+record);
}
bw.close();
br.close();
writer.close();
reader.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
對象序列化與反序列化
序列化:把Java對象轉換為位元組序列的過程
反序列化:把位元組序列恢複為Java對象的過程
隻有實作了Serializable接口的類的對象才能被序列化
序列化用途
把對象的位元組序列永久儲存在硬碟上,通常放在一個檔案中
在網絡上傳送對象的位元組序列
相關類
對象輸入流類:ObjectInPutStream
對象輸出流類:ObjectOutPutStream
序列化步驟
建立一個對象輸出流:ObjectOutPutStream out = new ObjectOutPutStream(OutPutStream out);
調用方法對對象進行序列化 out.writeObject(Object obj); 把對象序列化,得到位元組序列寫入流中
重新整理緩沖并關閉流 out.flush(); out.close();
反序列化步驟
建立一個對象輸入流:ObjectInPutStream in = new ObjectInPutStream(InPutStream in);
調用方法 in.readObject(); 讀取字元序列,把參數反序列化成對象,傳回該對象
關閉流 in.close();
示範Demo
定義一個學生類實作Serializable接口
public class Student implements Serializable{
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
實作對象序列化與反序列化
public class IOTest {
public void save(Student stu){
OutputStream os=null;
try {
os = new FileOutputStream("d:\\stu.dat");
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(stu);
//寫入其他類型資料
oos.writeBoolean(true);
oos.flush();
oos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(os!=null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public Student read(){
InputStream is=null;
Student stu=null;
try {
is = new FileInputStream("d:\\stu.dat");
ObjectInputStream ois = new ObjectInputStream(is);
stu = (Student)ois.readObject();
//注意讀取順序要和寫入順序保持一緻
System.out.println(ois.readBoolean());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally{
if(is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return stu;
}
public static void main(String[] args) {
Student stu = new Student("小小", 18);
IOTest test = new IOTest();
test.save(stu);
Student res = test.read();
System.out.println(res);
//true
//Student [name=小小, age=18]
}
}
Serializable接口
實作該接口會按預設方式進行序列化和反序列化
預設方式序列化
這種序列化方式僅對非transient的執行個體變量進行序列化
不會序列化對象的transient的執行個體變量,也不會序列化靜态變量
預設方式反序列化
如果記憶體中對象所屬的類還沒有被加載,那麼會先加載并初始化這個類,如果classpath中不存在該類檔案,那麼抛出ClassNotFoundException;
在反序列化時不會調用類的任何構造方法
如果希望控制類的序列化方式添加如下方法
private void writeObject(ObjectOutputStream out) throws IOException
private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException
可序列化類添加該方法後進行可序列化操作會執行該方法,否則按預設方式執行。在以上方法中可以先調用預設的defaultWriteObject()和read方法
序列化說明
對于包含敏感資訊的對象,可以先加密後再序列化,反序列化時需要解密
預設序列化方式會序列化整個對象圖,需要遞歸周遊對象圖,如果對象圖複雜,遞歸周遊需要消耗大量空間和時間,圖内部資料結構為雙向清單
實際應用中,如果對某些成員變量改為transient類型,可以節省空間和時間,提高可序列化的性能