本章簡單介紹幾個常見的io流派生弓叛。
1:數(shù)據(jù)操作流(操作基本類型數(shù)據(jù)的流)(理解)
(1)可以操作基本類型的數(shù)據(jù)
(2)流對象名稱
DataInputStream
DataOutputStream
代碼示例:
private static void read() throws IOException {
// DataInputStream(InputStream in)
// 創(chuàng)建數(shù)據(jù)輸入流對象
DataInputStream dis = new DataInputStream(
new FileInputStream("dos.txt"));
// 讀數(shù)據(jù)
byte b = dis.readByte();
short s = dis.readShort();
int i = dis.readInt();
long l = dis.readLong();
float f = dis.readFloat();
double d = dis.readDouble();
char c = dis.readChar();
boolean bb = dis.readBoolean();
// 釋放資源
dis.close();
System.out.println(b);
System.out.println(s);
System.out.println(i);
System.out.println(l);
System.out.println(f);
System.out.println(d);
System.out.println(c);
System.out.println(bb);
}
private static void write()throws IOException {
DataOutputStream dos = new DataOutputStream(new FileOutputStream("dos.txt"));
dos.writeByte(10);
dos.writeShort(100);
dos.writeInt(1000);
dos.writeLong(10000);
dos.writeFloat(12.34F);
dos.writeDouble(12.56);
dos.writeChar('a');
dos.writeBoolean(true);
dos.close();
}
2:內(nèi)存操作流(理解)
用于處理臨時存儲信息的璃弄,程序結束哥捕,數(shù)據(jù)就從內(nèi)存中消失牧抽。
(1)有些時候我們操作完畢后,未必需要產(chǎn)生一個文件遥赚,就可以使用內(nèi)存操作流扬舒。
(2)三種
A:ByteArrayInputStream,ByteArrayOutputStream
B:CharArrayReader,CharArrayWriter
C:StringReader,StringWriter
代碼示例:
//ByteArray
private static void fun1() throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// 寫數(shù)據(jù)
for (int x = 0; x < 10; x++) {
bos.write(("hello" + x).getBytes());
}
byte[] bytes = bos.toByteArray();
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
int by = 0;
while ((by = bis.read()) != -1) {
System.out.print((char) by);
}
}
//CharArray
private static void fun2() throws IOException {
CharArrayWriter caw = new CharArrayWriter();
caw.write("hello");
char[] chars =caw.toCharArray();
CharArrayReader car = new CharArrayReader(chars);
//car.read(chars);
System.out.println(chars);
int len = 0;
while ((len = car.read()) != -1) {
System.out.print((char)len);
}
}
//String
private static void fun3() throws IOException {
StringWriter sw = new StringWriter();
sw.write("hello");
StringReader sr = new StringReader(sw.toString());
int len = 0;
while ((len = sr.read()) != -1) {
System.out.print((char)len);
}
}
注:查看源碼,其close并不起作用凫佛,所以無需關閉呼巴。
3:打印流(掌握)
(1)字節(jié)打印流,字符打印流
(2)特點:
A:只操作目的地,不操作數(shù)據(jù)源
B:可以操作任意類型的數(shù)據(jù)
C:如果啟用了自動刷新御蒲,在調(diào)用println()方法的時候,能夠換行并刷新
D:可以直接操作文件
問題:哪些流可以直接操作文件呢?
看API诊赊,如果其構造方法能夠同時接收File和String類型的參數(shù)厚满,一般都是可以直接操作文件的
(3)復制文本文件
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
PrintWriter pw = new PrintWriter(new FileWriter("b.txt"),true);
String line = null;
while((line=br.readLine())!=null) {
pw.println(line);
}
pw.close();
br.close();
PrintStream是OutputStream的子類,PrintWriter是Writer的子類碧磅,兩者處于對等的位置上碘箍,所以它們的API是非常相似的遵馆。
區(qū)別無非一個是字節(jié)打印流,一個是字符打印流丰榴。更多的應用會在以后詳細擴展货邓。
需要注意:
字節(jié)打印流還是標準輸出流的對象哦。
4:標準輸入輸出流(理解)
(1)System類下面有這樣的兩個字段
in 標準輸入流
out 標準輸出流
(2)三種鍵盤錄入方式
A:main方法的args接收參數(shù)
B:System.in通過BufferedReader進行包裝
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
C:Scanner
Scanner sc = new Scanner(System.in);
(3)輸出語句的原理和如何使用字符流輸出數(shù)據(jù)
A:原理
System.out.println("helloworld");
PrintStream ps = System.out;
ps.println("helloworld");
B:把System.out用字符緩沖流包裝一下使用
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
代碼示例:
在scanner沒有出現(xiàn)以前四濒,我們是這樣鍵盤錄入的:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("請輸入名字:");
String data =br.readLine();
System.out.println(data);
System.out.println("請輸入數(shù)字:");
String i = br.readLine();
System.out.println(Integer.parseInt(i));
5:隨機訪問流(理解)
(1)可以按照文件指針的位置寫數(shù)據(jù)和讀數(shù)據(jù)换况。
(2)案例:
A:寫數(shù)據(jù)
B:讀數(shù)據(jù)
C:獲取和改變文件指針的位置
代碼示例:
/*
* 隨機訪問流:
* RandomAccessFile類不屬于流,是Object類的子類盗蟆。
* 但它融合了InputStream和OutputStream的功能戈二。
* 支持對文件的隨機訪問讀取和寫入。
*
* public RandomAccessFile(String name,String mode):第一個參數(shù)是文件路徑喳资,第二個參數(shù)是操作文件的模式觉吭。
* 模式有四種,我們最常用的一種叫"rw",這種方式表示我既可以寫數(shù)據(jù)仆邓,也可以讀取數(shù)據(jù)鲜滩。
訪問模式:
"r" 以只讀方式打開。調(diào)用結果對象的任何 write 方法都將導致拋出 IOException节值。
"rw" 打開以便讀取和寫入徙硅。如果該文件尚不存在,則嘗試創(chuàng)建該文件察署。
"rws" 打開以便讀取和寫入闷游,對于 "rw",還要求對文件的內(nèi)容或元數(shù)據(jù)的每個更新都同步寫入到底層存儲設備贴汪。
"rwd" 打開以便讀取和寫入脐往,對于 "rw",還要求對文件內(nèi)容的每個更新都同步寫入到底層存儲設備扳埂。
一般使用rw模式业簿。
*/
public class RandomAccessFileDemo {
public static void main(String[] args) throws IOException {
// write();
read();
}
private static void read() throws IOException {
// 創(chuàng)建隨機訪問流對象
RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw");
int i = raf.readInt();
System.out.println(i);
// 該文件指針可以通過 getFilePointer方法讀取,并通過 seek 方法設置阳懂。
System.out.println("當前文件的指針位置是:" + raf.getFilePointer());
char ch = raf.readChar();
System.out.println(ch);
System.out.println("當前文件的指針位置是:" + raf.getFilePointer());
String s = raf.readUTF();
System.out.println(s);
System.out.println("當前文件的指針位置是:" + raf.getFilePointer());
// 我不想重頭開始了梅尤,我就要讀取a,怎么辦呢?
raf.seek(4);
ch = raf.readChar();
System.out.println(ch);
}
private static void write() throws IOException {
// 創(chuàng)建隨機訪問流對象
RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw");
// 怎么玩呢?
raf.writeInt(100);
raf.writeChar('a');
raf.writeUTF("中國");
raf.close();
}
}
6:合并流(理解)
(1)把多個輸入流的數(shù)據(jù)寫到一個輸出流中岩调。
(2)構造方法:
A:SequenceInputStream(InputStream s1, InputStream s2)
B:SequenceInputStream(Enumeration<? extends InputStream> e)
合并復制1:
/*復制:
* 以前的操作:
* a.txt -- b.txt
* c.txt -- d.txt
*
* 現(xiàn)在想要:
* a.txt+b.txt -- ab.txt
*/
public class SequenceInputStreamDemo {
public static void main(String[] args) throws IOException {
FileInputStream f1 = new FileInputStream("a.txt");
FileInputStream f2 = new FileInputStream("b.txt");
SequenceInputStream sis = new SequenceInputStream(f1,f2);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("ab.txt"));
byte[] bytes = new byte[1024];
int len = 0 ;
while ((len = sis.read(bytes)) != -1) {
bos.write(bytes,0,len);
}
bos.close();
sis.close();
}
}
合并復制2:
/*
* 以前的操作:
* a.txt -- b.txt
* c.txt -- d.txt
* e.txt -- f.txt
*
* 現(xiàn)在想要:
* a.txt+b.txt+c.txt -- abc.txt
*/
public class SequenceInputStreamDemo2 {
public static void main(String[] args) throws IOException {
Vector<InputStream> v = new Vector<>();
InputStream s1 = new FileInputStream("a.txt");
InputStream s2 = new FileInputStream("b.txt");
InputStream s3 = new FileInputStream("c.txt");
v.add(s1);
v.add(s2);
v.add(s3);
Enumeration<InputStream> en = v.elements();
SequenceInputStream sis = new SequenceInputStream(en);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("abc.txt"));
byte[] bytes = new byte[1024];
int len = 0 ;
while ((len = sis.read(bytes)) != -1) {
bos.write(bytes,0,len);
}
bos.close();
sis.close();
}
}
7:序列化流(理解)
(1)可以把對象寫入文本文件或者在網(wǎng)絡中傳輸
(2)如何實現(xiàn)序列化呢?
讓被序列化的對象所屬類實現(xiàn)序列化接口巷燥。
該接口是一個標記接口。沒有功能需要實現(xiàn)号枕。
(3)注意問題:
把數(shù)據(jù)寫到文件后缰揪,在去修改類會產(chǎn)生一個問題。
如何解決該問題呢?
在類文件中葱淳,給出一個固定的序列化id值钝腺。
而且抛姑,這樣也可以解決黃色警告線問題
(4)面試題:
什么時候序列化?
如何實現(xiàn)序列化?
什么是反序列化?
代碼示例:
Person.java
/*
* NotSerializableException:未序列化異常
*
* 類通過實現(xiàn) java.io.Serializable 接口以啟用其序列化功能。未實現(xiàn)此接口的類將無法使其任何狀態(tài)序列化或反序列化艳狐。
* 該接口居然沒有任何方法定硝,類似于這種沒有方法的接口被稱為標記接口。
*
* java.io.InvalidClassException:
* cn.itcast_07.Person; local class incompatible:
* stream classdesc serialVersionUID = -2071565876962058344,
* local class serialVersionUID = -8345153069362641443
*
* 為什么會有問題呢?
* Person類實現(xiàn)了序列化接口毫目,那么它本身也應該有一個標記值蔬啡。
* 這個標記值假設是100。
* 開始的時候:
* Person.class -- id=100
* wirte數(shù)據(jù): oos.txt -- id=100
* read數(shù)據(jù): oos.txt -- id=100
*
* 現(xiàn)在:
* Person.class -- id=200
* wirte數(shù)據(jù): oos.txt -- id=100
* read數(shù)據(jù): oos.txt -- id=100
*
* 我們要知道的是:
* 看到類實現(xiàn)了序列化接口的時候蒜茴,要想解決黃色警告線問題星爪,就可以自動產(chǎn)生一個序列化id值。
* 而且產(chǎn)生這個值以后粉私,我們對類進行任何改動顽腾,它讀取以前的數(shù)據(jù)是沒有問題的。
*
* 注意:
* 我一個類中可能有很多的成員變量诺核,有些我不想進行序列化抄肖。請問該怎么辦呢?
* 使用transient關鍵字聲明不需要序列化的成員變量
*/
public class Person implements Serializable {
private static final long serialVersionUID = 5816344743154801933L;
private String name;
// private int age;
private transient int age;
// int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
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 + "]";
}
}
ObjectStreamDemo.java
/*
* 序列化流:把對象按照流一樣的方式存入文本文件或者在網(wǎng)絡中傳輸。對象 -- 流數(shù)據(jù)(ObjectOutputStream)
* 反序列化流:把文本文件中的流對象數(shù)據(jù)或者網(wǎng)絡中的流對象數(shù)據(jù)還原成對象窖杀。流數(shù)據(jù) -- 對象(ObjectInputStream)
*/
public class ObjectStreamDemo {
public static void main(String[] args) throws IOException,
ClassNotFoundException {
// 由于我們要對對象進行序列化漓摩,所以我們先自定義一個類
// 序列化數(shù)據(jù)其實就是把對象寫到文本文件
// write();
read();
}
private static void read() throws IOException, ClassNotFoundException {
// 創(chuàng)建反序列化對象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
"oos.txt"));
// 還原對象
Object obj = ois.readObject();
// 釋放資源
ois.close();
// 輸出對象
System.out.println(obj);
}
private static void write() throws IOException {
// 創(chuàng)建序列化流對象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
"oos.txt"));
// 創(chuàng)建對象
Person p = new Person("林青霞", 27);
// public final void writeObject(Object obj)
oos.writeObject(p);
// 釋放資源
oos.close();
}
}
8:Properties(理解)
(1)是一個集合類,Hashtable的子類
(2)特有功能
A:public Object setProperty(String key,String value)
B:public String getProperty(String key)
C:public Set<String> stringPropertyNames()
(3)和IO流結合的方法
把鍵值對形式的文本文件內(nèi)容加載到集合中
public void load(Reader reader)
public void load(InputStream inStream)
把集合中的數(shù)據(jù)存儲到文本文件中
public void store(Writer writer,String comments)
public void store(OutputStream out,String comments)
(4)案例:
寫一個程序?qū)崿F(xiàn)控制猜數(shù)字小游戲程序不能玩超過5次(用Properties控制玩的次數(shù))
Properties當做map來用:
Properties prop = new Properties();
// 添加元素
prop.put("it002", "hello");
prop.put("it001", "world");
prop.put("it003", "java");
// 遍歷集合
Set<Object> set = prop.keySet();
for (Object key : set) {
Object value = prop.get(key);
System.out.println(key + "---" + value);
}
properties的特殊功能:
// 創(chuàng)建集合對象
Properties prop = new Properties();
// 添加元素
prop.setProperty("張三", "30");
prop.setProperty("李四", "40");
prop.setProperty("王五", "50");
// public Set<String> stringPropertyNames():獲取所有的鍵的集合
Set<String> set = prop.stringPropertyNames();
for (String key : set) {
String value = prop.getProperty(key);
System.out.println(key + "---" + value);
}
properties和io流的聯(lián)合:
private static void myStore() throws IOException {
// 創(chuàng)建集合對象
Properties prop = new Properties();
prop.setProperty("林青霞", "27");
prop.setProperty("武鑫", "30");
prop.setProperty("劉曉曲", "18");
//public void store(Writer writer,String comments):把集合中的數(shù)據(jù)存儲到文件
Writer w = new FileWriter("name1.properties");
prop.store(w, null);//第二個參數(shù)是列表的描述
w.close();
}
private static void myLoad() throws IOException {
Properties prop = new Properties();
// public void load(Reader reader):把文件中的數(shù)據(jù)讀取到集合中
// 注意:這個文件的數(shù)據(jù)必須是鍵值對形式
Reader r = new FileReader("name1.properties");
prop.load(r);
r.close();
System.out.println("prop:" + prop);
}
案例猜數(shù)字游戲:
游戲源碼:
public class GuessNumber {
private GuessNumber() {
}
public static void start() {
// 產(chǎn)生一個隨機數(shù)
int number = (int) (Math.random() * 100) + 1;
// 定義一個統(tǒng)計變量
int count = 0;
while (true) {
// 鍵盤錄入一個數(shù)據(jù)
Scanner sc = new Scanner(System.in);
System.out.println("請輸入數(shù)據(jù)(1-100):");
int guessNumber = sc.nextInt();
count++;
// 判斷
if (guessNumber > number) {
System.out.println("你猜的數(shù)據(jù)" + guessNumber + "大了");
} else if (guessNumber < number) {
System.out.println("你猜的數(shù)據(jù)" + guessNumber + "小了");
} else {
System.out.println("恭喜你入客," + count + "次就猜中了");
break;
}
}
}
}
用properties將游戲次數(shù)記錄在文本中:
public class PropertiesTest2 {
public static void main(String[] args) throws IOException {
Reader reader =new FileReader("count.properties");
Properties pro = new Properties();
pro.load(reader);
reader.close();
String count =pro.getProperty("count");
int num =Integer.parseInt(count);
if (num>=3){
System.out.println("您已經(jīng)免費玩過三次了管毙,再玩請付費!");
}
else {
num++;
pro.setProperty("count",String.valueOf(num));
Writer writer = new FileWriter("count.properties");
pro.store(writer,null);
writer.close();
GuessNumber.start();
}
}
}
一般的單機版游戲桌硫,都會有.properties文件來記錄本地游戲狀態(tài)夭咬。當然實際情況中這個文件是加密的。在后面的框架學習中铆隘,我們有時候會數(shù)據(jù)庫的配置放到.properties文件中并且加密卓舵。
9:NIO(了解)
(1)JDK4出現(xiàn)的NIO,對以前的IO操作進行了優(yōu)化膀钠,提供了效率掏湾。但是大部分我們看到的還是以前的IO
(2)JDK7的NIO的使用
Path:路徑
Paths:通過靜態(tài)方法返回一個路徑
Files:提供了常見的功能
復制文本文件
把集合中的數(shù)據(jù)寫到文本文件
代碼示例:新型復制:
/*
* nio包在JDK4出現(xiàn),提供了IO流的操作效率肿嘲。
*
* JDK7的之后的nio:
* Path:路徑
* Paths:有一個靜態(tài)方法返回一個路徑
* public static Path get(URI uri)
* Files:提供了靜態(tài)方法供我們使用
* public static long copy(Path source,OutputStream out):復制文件
* public static Path write(Path path,Iterable<? extends CharSequence> lines,Charset cs,OpenOption... options)
*/
public class NIODemo {
public static void main(String[] args) throws IOException {
//復制
//Files.copy(Paths.get("a.txt"),new FileOutputStream("newa.txt"));
ArrayList<String> array = new ArrayList<String>();
array.add("hello");
array.add("world");
array.add("java");
Files.write(Paths.get("f.txt"),array, Charset.defaultCharset());
}
}
現(xiàn)在jdk已經(jīng)出到9了融击,io流也有了更多的變化。給我最大的感受是操作更方便了雳窟。有時間會整理一下java8砚嘴,java9的一些新特性。
io流的基礎回顧就告一段落了,淺嘗輒止际长。無論是做學問,做科研都不可能一下子做到很全兴泥,很好工育。循序漸進,實踐中慢慢總結搓彻。io流用到的地方很多如绸,上傳下載,傳輸旭贬,設計模式等怔接。基礎打扎實了稀轨,才能玩更高端的扼脐。
以上是本人學習筆記整理,重溫java經(jīng)典奋刽,歡迎各位同道中人批評指正瓦侮。
源碼碼云地址:
https://gitee.com/stefanpy/java
夢回io流完整目錄: