IO異常的處理
JDK7前處理
在jdk1.7之前使用try catch finally 處理流中的異常
import java.io.FileWriter;
import java.io.IOException;
/*
在jdk1.7之前使用try catch finally 處理流中的異常
格式:
try{
可能會產出異常的代碼
}catch(異常類變量 變量名){
異常的處理邏輯
}finally{
一定會指定的代碼
資源釋放
}
*/
public class Demo01TryCatch {
public static void main(String[] args) {
//提高變量fw的作用域,讓finally可以使用
//變量在定義的時候,可以沒有值,但是使用的時候必須有值
//fw = new FileWriter("09_IOAndProperties\\g.txt",true); 執行失敗,fw沒有值,fw.close會報錯
FileWriter fw = null;
try{
//可能會產出異常的代碼
fw = new FileWriter("w:\\09_IOAndProperties\\g.txt",true);
for (int i = 0; i <10 ; i++) {
fw.write("HelloWorld"+i+"\r\n");
}
}catch(IOException e){
//異常的處理邏輯
System.out.println(e);
}finally {
//一定會指定的代碼
//創建對象失敗了,fw的默認值就是null,null是不能調用方法的 ,
會拋出NullPointerException,需要增加一個判斷,不是null在把資源釋放
if(fw!=null){
try {
//fw.close方法聲明拋出了IOException異常對象,
所以我們就的處理這個異常對象,要麼throws,要麼try catch
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
JDK7的新特性
在try的後邊可以增加一個(),在括號中可以定義流對象,那麼這個流對象的作用域就在try中有效,try中的代碼執行完畢,會自動把流對象釋放,不用寫finally。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*
格式:
try(定義流對象;定義流對象....){
可能會產出異常的代碼
}catch(異常類變量 變量名){
異常的處理邏輯
}
*/
public class Demo02JDK7 {
public static void main(String[] args) {
try(//1.創建一個字節輸入流對象,構造方法中綁定要讀取的數據源
FileInputStream fis = new FileInputStream("c:\\1.jpg");
//2.創建一個字節輸出流對象,構造方法中綁定要寫入的目的地
FileOutputStream fos = new FileOutputStream("d:\\1.jpg");){
//可能會產出異常的代碼
//一次讀取一個字節寫入一個字節的方式
//3.使用字節輸入流對象中的方法read讀取文件
int len = 0;
while((len = fis.read())!=-1){
//4.使用字節輸出流中的方法write,把讀取到的字節寫入到目的地的文件中
fos.write(len);
}
}catch (IOException e){
//異常的處理邏輯
System.out.println(e);
}
}
}
JDK9新特性
- try的前邊可以定義流對象
- 在try後邊的()中可以直接引入流對象的名稱(變量名)
- 在try代碼執行完畢之後,流對象也可以釋放掉,不用寫finally
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*
格式:
A a = new A();
B b = new B();
try(a,b){
可能會產出異常的代碼
}catch(異常類變量 變量名){
異常的處理邏輯
}
*/
public class Demo03JDK9 {
public static void main(String[] args) throws IOException {
//1.創建一個字節輸入流對象,構造方法中綁定要讀取的數據源
FileInputStream fis = new FileInputStream("c:\\1.jpg");
//2.創建一個字節輸出流對象,構造方法中綁定要寫入的目的地
FileOutputStream fos = new FileOutputStream("d:\\1.jpg");
try(fis;fos){
//一次讀取一個字節寫入一個字節的方式
//3.使用字節輸入流對象中的方法read讀取文件
int len = 0;
while((len = fis.read())!=-1){
//4.使用字節輸出流中的方法write,把讀取到的字節寫入到目的地的文件中
fos.write(len);
}
}catch (IOException e){
System.out.println(e);
}
//fos.write(1);//Stream Closed
}
}
IO特殊操作流
打印流 PrintStream類
PrintStream extends OutputStream,平時我們在控制檯打印輸出,是調用 print 方法和 println 方法完成的,這兩個方法都來自於 java.io.PrintStream 類,該類能夠方便地打印各種數據類型的值,是一種便捷的輸出方式。 PrintStream 爲其他輸出流添加了功能,使它們能夠方便地打印各種數據值表示形式。
PrintStream特點:
- 只負責數據的輸出,不負責數據的讀取
- 與其他輸出流不同,PrintStream 永遠不會拋出 IOException
- 有特有的方法,print,println
構造方法:
- PrintStream(File file):輸出的目的地是一個文件
- PrintStream(OutputStream out):輸出的目的地是一個字節輸出流
- PrintStream(String fileName) :輸出的目的地是一個文件路徑
繼承自父類的成員方法:
- public void close() :關閉此輸出流並釋放與此流相關聯的任何系統資源。
- public void flush() :刷新此輸出流並強制任何緩衝的輸出字節被寫出。
- public void write(byte[] b):將 b.length字節從指定的字節數組寫入此輸出流。
- public void write(byte[] b, int off, int len) :從指定的字節數組寫入 len字節,從偏移量 off開始輸出到此輸出流。
- public abstract void write(int b) :將指定的字節輸出流。
注意:
- 如果使用繼承自父類的write方法寫數據,那麼查看數據的時候會查詢編碼表 97->a
- 如果使用自己特有的方法print/println方法寫數據,寫的數據原樣輸出 97->97
import java.io.FileNotFoundException;
import java.io.PrintStream;
public class Demo01PrintStream {
public static void main(String[] args) throws FileNotFoundException {
//System.out.println("HelloWorld");
//創建打印流PrintStream對象,構造方法中綁定要輸出的目的地
PrintStream ps = new PrintStream("10_IO\\print.txt");
//如果使用繼承自父類的write方法寫數據,那麼查看數據的時候會查詢編碼表 97->a
ps.write(97);
//如果使用自己特有的方法print/println方法寫數據,寫的數據原樣輸出 97->97
ps.println(97);
ps.println(8.8);
ps.println('a');
ps.println("HelloWorld");
ps.println(true);
//釋放資源
ps.close();
}
}
可以改變輸出語句的目的地(打印流的流向), 輸出語句,默認在控制檯輸出
package com.itheima.demo05.PrintStream;
import java.io.FileNotFoundException;
import java.io.PrintStream;
/*
使用System.setOut方法改變輸出語句的目的地改爲參數中傳遞的打印流的目的地
static void setOut(PrintStream out)
重新分配“標準”輸出流。
*/
public class Demo02PrintStream {
public static void main(String[] args) throws FileNotFoundException {
System.out.println("我是在控制檯輸出");
PrintStream ps = new PrintStream("10_IO\\目的地是打印流.txt");
System.setOut(ps);//把輸出語句的目的地改變爲打印流的目的地
System.out.println("我在打印流的目的地中輸出");
ps.close();
}
}
對象序列化流
序列化概述 :Java 提供了一種對象序列化的機制。用一個字節序列可以表示一個對象,該字節序列包含該 對象的數據 、 對象的 類型 和 對象中存儲的屬性 等信息。字節序列寫出到文件之後,相當於文件中持久保存了一個對象的信息。反之,該字節序列還可以從文件中讀取回來,重構對象,對它進行反序列化。 對象的數據 、 對象的類型 和 對象中 存儲的數據 信息,都可以用來在內存中創建對象。看圖理解序列化:
ObjectOutputStream類
概述:
- java.io.ObjectOutputStream extends OutputStream
- ObjectOutputStream:對象的序列化流
- 作用:把對象以流的方式寫入到文件中保存
構造方法:
- ObjectOutputStream(OutputStream out) 創建寫入指定 OutputStream 的 ObjectOutputStream。參數: OutputStream out:字節輸出流
特有的成員方法:
- void writeObject(Object obj) 將指定的對象寫入 ObjectOutputStream。
注意事項
- 一個對象要想被序列化,該對象所屬的類必須必須實現Serializable 接口
- Serializable是一個標記接口,實現該接口,不需要重寫任何方法
凡是實現Serializable接口的類都有一個表示序列化版本標識符的靜態變量:
- private static final long serialVersionUID;
- serialVersionUID用來表明類的不同版本間的兼容性。簡言之,其目的是以序列化對象 進行版本控制,有關各版本反序列化時是否兼容。
- 如果類沒有顯示定義這個靜態常量,它的值是Java運行時環境根據類的內部細節自 動生成的。若類的實例變量做了修改,serialVersionUID 可能發生變化。故建議, 顯式聲明。
簡單來說,Java的序列化機制是通過在運行時判斷類的serialVersionUID來驗 證版本一致性的。在進行反序列化時,JVM會把傳來的字節流中的 serialVersionUID與本地相應實體類的serialVersionUID進行比較,如果相同 就認爲是一致的,可以進行反序列化,否則就會出現序列化版本不一致的異 常。(InvalidCastException)
使用步驟:
- 創建ObjectOutputStream對象,構造方法中傳遞字節輸出流
- 使用ObjectOutputStream對象中的方法writeObject,把對象寫入到文件中
- 釋放資源
package demo01;
import java.io.Serializable;
/*
序列化和反序列化的時候,會拋出NotSerializableException沒有序列化異常
類通過實現 java.io.Serializable 接口以啓用其序列化功能。未實現此接口的類將無法使其任何狀態序列化或反序列化。
Serializable接口也叫標記型接口
要進行序列化和反序列化的類必須實現Serializable接口,就會給類添加一個標記
當我們進行序列化和反序列化的時候,就會檢測類上是否有這個標記
有:就可以序列化和反序列化
沒有:就會拋出 NotSerializableException異常
static關鍵字:靜態關鍵字
靜態優先於非靜態加載到內存中(靜態優先於對象進入到內存中)
被static修飾的成員變量不能被序列化的,序列化的都是對象
private static int age;
oos.writeObject(new Person("小美女",18));
Object o = ois.readObject();
Person{name='小美女', age=0}
transient關鍵字:瞬態關鍵字
被transient修飾成員變量,不能被序列化
private transient int age;
oos.writeObject(new Person("小美女",18));
Object o = ois.readObject();
Person{name='小美女', age=0}
*/
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
//private static int age;
//private transient int age;
public int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
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;
}
}
ObjectInputStream類
使用序列化流,永久保存數據
package demo01;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
/*
java.io.ObjectOutputStream extends OutputStream
ObjectOutputStream:對象的序列化流
作用:把對象以流的方式寫入到文件中保存
構造方法:
ObjectOutputStream(OutputStream out) 創建寫入指定 OutputStream 的 ObjectOutputStream。
參數:
OutputStream out:字節輸出流
特有的成員方法:
void writeObject(Object obj) 將指定的對象寫入 ObjectOutputStream。
使用步驟:
1.創建ObjectOutputStream對象,構造方法中傳遞字節輸出流
2.使用ObjectOutputStream對象中的方法writeObject,把對象寫入到文件中
3.釋放資源
*/
public class Demo01ObjectOutputStream {
public static void main(String[] args) throws IOException {
//1.創建ObjectOutputStream對象,構造方法中傳遞字節輸出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\person.txt"));
//2.使用ObjectOutputStream對象中的方法writeObject,把對象寫入到文件中
oos.writeObject(new Person("小美女",18));
//3.釋放資源
oos.close();
}
}
使用反序列化流,讀取數據
package demo01;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
/*
java.io.ObjectInputStream extends InputStream
ObjectInputStream:對象的反序列化流
作用:把文件中保存的對象,以流的方式讀取出來使用
構造方法:
ObjectInputStream(InputStream in) 創建從指定 InputStream 讀取的 ObjectInputStream。
參數:
InputStream in:字節輸入流
特有的成員方法:
Object readObject() 從 ObjectInputStream 讀取對象。
使用步驟:
1.創建ObjectInputStream對象,構造方法中傳遞字節輸入流
2.使用ObjectInputStream對象中的方法readObject讀取保存對象的文件
3.釋放資源
4.使用讀取出來的對象(打印)
readObject方法聲明拋出了ClassNotFoundException(class文件找不到異常)
當不存在對象的class文件時拋出此異常
反序列化的前提:
1.類必須實現Serializable
2.必須存在類對應的class文件
*/
public class Demo02ObjectInputStream {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//1.創建ObjectInputStream對象,構造方法中傳遞字節輸入流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\person.txt"));
//2.使用ObjectInputStream對象中的方法readObject讀取保存對象的文件
Object o = ois.readObject();
//3.釋放資源
ois.close();
//4.使用讀取出來的對象(打印)
System.out.println(o);
Person p = (Person)o;
System.out.println(p.getName()+p.getAge());
}
}
屬性集(Properties類 )
概述 :java.util.Properties 繼承於 Hashtable ,來表示一個持久的屬性集。它使用鍵值結構存儲數據,每個鍵及其 對應值都是一個字符串。該類也被許多Java類使用,比如獲取系統屬性時, System.getProperties 方法就是返回 一個 Properties 對象。
- public Properties() :創建一個空的屬性列表。
基本的存儲方法
- public Object setProperty(String key, String value) : 保存一對屬性。
- public String getProperty(String key) :使用此屬性列表中指定的鍵搜索屬性值。
- public Set<String> stringPropertyNames() :所有鍵的名稱的集合。
方法演示:
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
import java.util.Set;
/*
java.util.Properties集合 extends Hashtable<k,v> implements Map<k,v>
Properties 類表示了一個持久的屬性集。Properties 可保存在流中或從流中加載。
Properties集合是一個唯一和IO流相結合的集合
可以使用Properties集合中的方法store,把集合中的臨時數據,持久化寫入到硬盤中存儲
可以使用Properties集合中的方法load,把硬盤中保存的文件(鍵值對),讀取到集合中使用
屬性列表中每個鍵及其對應值都是一個字符串。
Properties集合是一個雙列集合,key和value默認都是字符串
*/
public class Demo01Properties {
public static void main(String[] args) throws IOException {
}
/*
可以使用Properties集合中的方法load,把硬盤中保存的文件(鍵值對),讀取到集合中使用
void load(InputStream inStream)
void load(Reader reader)
參數:
InputStream inStream:字節輸入流,不能讀取含有中文的鍵值對
Reader reader:字符輸入流,能讀取含有中文的鍵值對
使用步驟:
1.創建Properties集合對象
2.使用Properties集合對象中的方法load讀取保存鍵值對的文件
3.遍歷Properties集合
注意:
1.存儲鍵值對的文件中,鍵與值默認的連接符號可以使用=,空格(其他符號)
2.存儲鍵值對的文件中,可以使用#進行註釋,被註釋的鍵值對不會再被讀取
3.存儲鍵值對的文件中,鍵與值默認都是字符串,不用再加引號
*/
private static void show03() throws IOException {
//1.創建Properties集合對象
Properties prop = new Properties();
//2.使用Properties集合對象中的方法load讀取保存鍵值對的文件
prop.load(new FileReader("09_IOAndProperties\\prop.txt"));
//prop.load(new FileInputStream("09_IOAndProperties\\prop.txt"));
//3.遍歷Properties集合
Set<String> set = prop.stringPropertyNames();
for (String key : set) {
String value = prop.getProperty(key);
System.out.println(key+"="+value);
}
}
/*
可以使用Properties集合中的方法store,把集合中的臨時數據,持久化寫入到硬盤中存儲
void store(OutputStream out, String comments)
void store(Writer writer, String comments)
參數:
OutputStream out:字節輸出流,不能寫入中文
Writer writer:字符輸出流,可以寫中文
String comments:註釋,用來解釋說明保存的文件是做什麼用的
不能使用中文,會產生亂碼,默認是Unicode編碼
一般使用""空字符串
使用步驟:
1.創建Properties集合對象,添加數據
2.創建字節輸出流/字符輸出流對象,構造方法中綁定要輸出的目的地
3.使用Properties集合中的方法store,把集合中的臨時數據,持久化寫入到硬盤中存儲
4.釋放資源
*/
private static void show02() throws IOException {
//1.創建Properties集合對象,添加數據
Properties prop = new Properties();
prop.setProperty("趙麗穎","168");
prop.setProperty("迪麗熱巴","165");
prop.setProperty("古力娜扎","160");
//2.創建字節輸出流/字符輸出流對象,構造方法中綁定要輸出的目的地
//FileWriter fw = new FileWriter("09_IOAndProperties\\prop.txt");
//3.使用Properties集合中的方法store,把集合中的臨時數據,持久化寫入到硬盤中存儲
//prop.store(fw,"save data");
//4.釋放資源
//fw.close();
prop.store(new FileOutputStream("09_IOAndProperties\\prop2.txt"),"");
}
/*
使用Properties集合存儲數據,遍歷取出Properties集合中的數據
Properties集合是一個雙列集合,key和value默認都是字符串
Properties集合有一些操作字符串的特有方法
Object setProperty(String key, String value) 調用 Hashtable 的方法 put。
String getProperty(String key) 通過key找到value值,此方法相當於Map集合中的get(key)方法
Set<String> stringPropertyNames()
返回此屬性列表中的鍵集,其中該鍵及其對應值是字符串,此方法相當於Map集合中的keySet方法
*/
private static void show01() {
//創建Properties集合對象
Properties prop = new Properties();
//使用setProperty往集合中添加數據
prop.setProperty("趙麗穎","168");
prop.setProperty("迪麗熱巴","165");
prop.setProperty("古力娜扎","160");
//prop.put(1,true);
//使用stringPropertyNames把Properties集合中的鍵取出,存儲到一個Set集合中
Set<String> set = prop.stringPropertyNames();
//遍歷Set集合,取出Properties集合的每一個鍵
for (String key : set) {
//使用getProperty方法通過key獲取value
String value = prop.getProperty(key);
System.out.println(key+"="+value);
}
}
}
Properties和IO流相結合的方法
和IO流結合的方法