本文主要介紹Java IO中的其他幾種流:
- 標(biāo)準(zhǔn)輸入、輸出流
- 打印流
- 數(shù)據(jù)流
- 對象流
- 隨機(jī)存取文件流
標(biāo)準(zhǔn)輸入、輸出流
簡介
System.in
:標(biāo)準(zhǔn)的輸入流庸推,默認(rèn)從鍵盤輸入蒜茴。
System.out
:標(biāo)準(zhǔn)的輸出流,默認(rèn)從控制臺輸出疆栏。
主要方法
System
類的setIn(InputStream is)
方式重新指定輸入的流曾掂。
System
類的setOut(PrintStream ps)
方式重新指定輸出的流。
使用示例
從鍵盤輸入字符串壁顶,要求將讀取到的整行字符串轉(zhuǎn)成大寫輸出珠洗。然后繼續(xù)進(jìn)行輸入操作,直至當(dāng)輸入e
或者exit
時若专,退出程序许蓖。
設(shè)計(jì)思路
方法一:使用Scanner
實(shí)現(xiàn),調(diào)用next()
返回一個字符串调衰。
方法二:使用System.in
實(shí)現(xiàn)膊爪。System.in
---> 轉(zhuǎn)換流 ---> BufferedReader
的readLine()
public class OtherStream {
public static void main(String[] args) {
BufferedReader br = null;
try {
// System.in:為InputStream類型,讀取從鍵盤輸入的字符串嚎莉,使用轉(zhuǎn)換流
InputStreamReader isr = new InputStreamReader(System.in);
br = new BufferedReader(isr);
while (true) {
System.out.println("請輸入字符串:");
String data = br.readLine();
if ("e".equalsIgnoreCase(data) || "exit".equalsIgnoreCase(data)) {
System.out.println("程序結(jié)束");
break;
}
String upperCase = data.toUpperCase();
System.out.println(upperCase);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
小練習(xí)
設(shè)計(jì)實(shí)現(xiàn)Scanner
類
public class MyInput {
// 從鍵盤讀取字符串
public static String readString() {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// 聲明并初始化字符串
String string = "";
// 從鍵盤獲取字符串
try {
string = br.readLine();
} catch (IOException ex) {
System.out.println(ex);
}
// 返回從鍵盤獲取的字符串
return string;
}
// 從鍵盤讀取一個int值
public static int readInt() {
return Integer.parseInt(readString());
}
// 從鍵盤讀取double值
public static double readDouble() {
return Double.parseDouble(readString());
}
// 從鍵盤讀取byte值
public static double readByte() {
return Byte.parseByte(readString());
}
// 從鍵盤讀取short值
public static double readShort() {
return Short.parseShort(readString());
}
// 從鍵盤讀取long值
public static double readLong() {
return Long.parseLong(readString());
}
// 從鍵盤讀取float值
public static double readFloat() {
return Float.parseFloat(readString());
}
}
打印流
PrintStream
和PrintWriter
說明:
- 提供了一系列重載的
print()
和println()
方法米酬,用于多種數(shù)據(jù)類型的輸出 -
System.out
返回的是PrintStream
的實(shí)例
public class OtherStreamTest {
// 打印流
@Test
public void test() {
PrintStream ps = null;
try {
FileOutputStream fos = new FileOutputStream(new File("D:\\io\\hello.txt"));
// 創(chuàng)建打印輸出流,設(shè)置為自動刷新模式(寫入換行符或字節(jié)‘\n’時都會花心緩沖區(qū))
ps = new PrintStream(fos, true);
if (ps != null) { // 把標(biāo)準(zhǔn)輸出流(控制臺輸出)改成輸出到文件
System.setOut(ps);
}
for (int i = 0; i <= 255; i++) { // 輸出ASCII字符
System.out.print((char) i);
if (i % 50 == 0) { // 每50個數(shù)據(jù)一行
System.out.println(); // 換行
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (ps != null) {
ps.close();
}
}
}
}
數(shù)據(jù)流
DataInputStream
和DataOutputStream
作用: 用于讀取或?qū)懗龌緮?shù)據(jù)類型的變量或字符串
示例代碼:
將內(nèi)存中的字符串趋箩、基本數(shù)據(jù)類型的變量寫出到文件中赃额。
@Test
public void test2() {
DataOutputStream dos = null;
try {
// 1.創(chuàng)建對象
dos = new DataOutputStream(new FileOutputStream("D:\\io\\data.txt"));
// 2.數(shù)據(jù)輸出
dos.writeUTF("Bruce");
dos.flush(); // 刷新操作加派,將內(nèi)存的數(shù)據(jù)寫入到文件
dos.writeInt(23);
dos.flush();
dos.writeBoolean(true);
dos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 3.關(guān)閉流
if (dos != null) {
try {
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
將文件中存儲的基本數(shù)據(jù)類型變量和字符串讀取到內(nèi)存中,保存在變量中跳芳。
/*
注意點(diǎn):讀取不同類型的數(shù)據(jù)的順序要與當(dāng)初寫入文件時芍锦,保存的數(shù)據(jù)的順序一致!
*/
@Test
public void test3() {
DataInputStream dis = null;
try {
// 1.創(chuàng)建對象
dis = new DataInputStream(new FileInputStream("D:\\io\\data.txt"));
// 2.從文件中讀入數(shù)據(jù)飞盆,讀取的順序要和當(dāng)初寫入時一致
String name = dis.readUTF();
int age = dis.readInt();
boolean isMale = dis.readBoolean();
System.out.println("name: " + name);
System.out.println("age: " + age);
System.out.println("isMale: " + isMale);
} catch (IOException e) {
e.printStackTrace();
} finally {
// 3. 關(guān)閉資源
if (dis != null) {
try {
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
對象流
ObjectInputStream
和ObjectOutputStream
作用
-
ObjectOutputStream
:內(nèi)存中的對象--->存儲中的文件娄琉、通過網(wǎng)絡(luò)傳輸出去:序列化過程。 -
ObjectInputStream
:存儲中的文件吓歇、通過網(wǎng)絡(luò)接收過來 --->內(nèi)存中的對象:反序列化過程车胡。
對象的序列化
- 對象序列化機(jī)制允許把內(nèi)存中的Java對象轉(zhuǎn)換成平臺無關(guān)的二進(jìn)制流,從而允許把這種二進(jìn)制流持久地保存在磁盤上,或通過網(wǎng)絡(luò)將這種二進(jìn)制流傳輸?shù)搅硪粋€網(wǎng)絡(luò)節(jié)點(diǎn)。當(dāng)其它程序獲取了這種二進(jìn)制流堡赔,就可以恢復(fù)成原來的Java對象魁袜。
- 序列化的好處在于可將任何實(shí)現(xiàn)了
Serializable
接口的對象轉(zhuǎn)化為字節(jié)數(shù)據(jù),使其在保存和傳輸時可被還原。 - 序列化是RMI(Remote Method Invoke-遠(yuǎn)程方法調(diào)用)過程的參數(shù)和返回值都必須實(shí)現(xiàn)的機(jī)制,RMI是JavaEE的基礎(chǔ)。因此序列化機(jī)制是JavaEE平臺的基礎(chǔ)簇搅。
- 如果需要讓某個對象支持序列化機(jī)制,則必須讓對象所屬的類及其屬性是可序列化的软吐,為了讓某個類是可序列化的瘩将,該類必須實(shí)現(xiàn)如下兩個接口之一。否則凹耙,會拋出
NotserializableEXception
異常Serializable
Externalizable
- 凡是實(shí)現(xiàn)
Serializable
接口的類都有一個表示序列化版本標(biāo)識符的靜態(tài)變量:-
private static final long serialVersionUID
; -
serialVersionUID
用來表明類的不同版本間的兼容性姿现。簡言之,其目的是以序列化對象進(jìn)行版本控制肖抱,有關(guān)各版本反序列化時是否兼容 - 如果類沒有顯示定義這個靜態(tài)常量备典,它的值是Java運(yùn)行時環(huán)境根據(jù)類的內(nèi)部細(xì)節(jié)自動生成的。若類的實(shí)例變量做了修改意述,
serialVersionUID
可能發(fā)生變化提佣。故建議顯式聲明。
-
- 簡單來說荤崇,Java的序列化機(jī)制是通過在運(yùn)行時判斷類的
serialversionUID
來驗(yàn)證版本一致性的拌屏。在進(jìn)行反序列化時,JVM
會把傳來的字節(jié)流中的serialversionUID
與本地相應(yīng)實(shí)體類的serialversionUID
進(jìn)行比較术荤,如果相同就認(rèn)為是一致的倚喂,可以進(jìn)行反序列化,否則就會出現(xiàn)序列化版本不一致的異常喜每。(InvalidCastException
)
實(shí)現(xiàn)序列化的對象所屬的類需要滿足
- 需要實(shí)現(xiàn)接口:
Serializable
(標(biāo)識接口) - 當(dāng)前類提供一個全局常量:
serialVersionUID
(序列版本號) - 除了當(dāng)前
Person
類需要實(shí)現(xiàn)Serializable
接口之外务唐,還必須保證其內(nèi)部所屬性也必須是可序列化的雳攘。(默認(rèn)情況下带兜,基本數(shù)據(jù)類型可序列化)
補(bǔ)充:ObjectOutputStream
和ObjectInputStream
不能序列化static
和transient
修飾的成員變量
對象流的使用
序列化代碼實(shí)現(xiàn)
要求被序列化對象必須實(shí)現(xiàn)序列化
@Test
public void test1() {
ObjectOutputStream oos = null;
try {
// 1.創(chuàng)建對象枫笛,創(chuàng)建流
oos = new ObjectOutputStream(new FileOutputStream("D:\\io\\object.dat"));
// 2.操作流
oos.writeObject(new String("億貧如洗王道長"));
oos.flush(); // 刷新操作
oos.writeObject(new Person("馮寶寶", 18));
oos.flush();
oos.writeObject(new Person("王也", 18));
oos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 關(guān)閉流
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Person
類:
public class Person implements Serializable { // 不實(shí)現(xiàn)Serializable接口不能序列化
private static final long serialVersionUID = -7226360431328584153L;
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.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;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
反序列化代碼實(shí)現(xiàn)
@Test
public void test2() {
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("D:\\io\\object.dat"));
Object obj = ois.readObject();
String str = (String) obj;
Person p1 = (Person) ois.readObject();
Person p2 = (Person) ois.readObject();
System.out.println(str);
System.out.println(p1);
System.out.println(p2);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
任意存取文件流
RandomAccessFile
的使用
簡介
-
RandomAccessFile
直接繼承于java.lang.Object
類,實(shí)現(xiàn)了DataInput
和DataOutput
接口 -
RandomAccessFile
既可以作為一個輸入流刚照,又可以作為一個輸出流 -
RandomAccessFile
類支持“隨機(jī)訪問”的方式刑巧,程序可以直接跳到文件的任意地方來讀、寫文件- 支持只訪問文件的部分內(nèi)容
- 可以向已存在的文件后追加內(nèi)容
-
RandomAccessFile
對象包含一個記錄指針无畔,用以標(biāo)示當(dāng)前讀寫處的位置 -
RandomaccessFile
類對象可以自由移動記錄指針:-
long getFilePointer()
:獲取文件記錄指針的當(dāng)前位置 -
void seek(long pos)
:將文件記錄指針定位到pos
位置
-
構(gòu)造器
public RandomAccessFile(File file,String mode)
public RandomAccessFile(String name,String mode)
使用說明
- 如果
RandomAccessFile
作為輸出流時啊楚,寫出到的文件如果不存在,則在執(zhí)行過程中自動創(chuàng)建浑彰。 - 如果寫出到的文件存在恭理,則會對原文件內(nèi)容進(jìn)行覆蓋。(默認(rèn)情況下郭变,從頭覆蓋)
- 可以通過相關(guān)的操作颜价,實(shí)現(xiàn)
RandomAccessFile
“插入”數(shù)據(jù)的效果。借助seek(int pos)
方法 - 創(chuàng)建
RandomAccessFile
類實(shí)例需要指定一個mode
參數(shù)诉濒,該參數(shù)指定RandomAccessFile
的訪問模式:-
r
:以只讀方式打開 -
rw
:打開以便讀取和寫入 -
rwd
:打開以便讀取和寫入周伦;同步文件內(nèi)容的更新 -
rws
:打開以便讀取和寫入;同步文件內(nèi)容和元數(shù)據(jù)的更新
-
- 如果模式為只讀
r
未荒,則不會創(chuàng)建文件专挪,而是會去讀取一個已經(jīng)存在的文件,讀取的文件不存在則會出現(xiàn)異常片排。如果模式為rw
讀寫寨腔,文件不存在則會去創(chuàng)建文件,存在則不會創(chuàng)建率寡。
使用示例
文件的讀取和寫出操作
@Test
public void test1() {
RandomAccessFile raf1 = null;
RandomAccessFile raf2 = null;
try {
//1.創(chuàng)建對象脆侮,創(chuàng)建流
raf1 = new RandomAccessFile(new File("test.jpg"),"r");
raf2 = new RandomAccessFile(new File("test1.jpg"),"rw");
//2.操作流
byte[] buffer = new byte[1024];
int len;
while((len = raf1.read(buffer)) != -1){
raf2.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.關(guān)閉流
if(raf1 != null){
try {
raf1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(raf2 != null){
try {
raf2.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
使用RandomAccessFile實(shí)現(xiàn)數(shù)據(jù)的插入效果
@Test
public void test2() {
RandomAccessFile raf1 = null;
try {
raf1 = new RandomAccessFile(new File("D:\\io\\hello.txt"), "rw");
raf1.seek(3); // 將指針調(diào)到角標(biāo)為3的位置
// 方式一:保存指針3后面的所有數(shù)據(jù)到StringBuilder中
// StringBuilder builder = new StringBuilder((int) new File("D:\\io\\hello.txt").length());
// byte[] buffer = new byte[1024];
// int len;
// while ((len = raf1.read(buffer)) != -1) {
// builder.append(new String(buffer, 0, len));
// }
// // 將指針調(diào)回角標(biāo)為3的位置
// raf1.seek(3);
// // 寫入"xyz"
// raf1.write("xyz".getBytes());
// // 從“xyz”的后面開始,將StringBuilder中的數(shù)據(jù)寫入到文件中去
// raf1.write(builder.toString().getBytes());
// 方式二
ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 內(nèi)部是一個數(shù)組勇劣,類似于StringBuilder
byte[] buffer = new byte[20];
int len;
while ((len = raf1.read(buffer)) != -1) {
baos.write(buffer);
}
// 將指針調(diào)回角標(biāo)為3的位置
raf1.seek(3);
// 寫入"xyz"
raf1.write("xyz".getBytes());
// 從“xyz”的后面開始靖避,將baos中的數(shù)據(jù)寫入到文件中去
raf1.write(baos.toString().getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (raf1 != null) {
try {
raf1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}