java IO流-java學習筆記
1:登入註冊IO版本案例(掌握)
要求,對著寫一遍。
cn.itcast.pojo User
cn.itcast.dao UserDao
cn.itcast.dao.impl UserDaoImpl(實現我不管)
cn.itcast.game GuessNumber
cn.itcast.test UserTest
2:資料操作流(操作基本型別資料的流)(理解)
(1)可以操作基本型別的資料
(2)流物件名稱
- 資料輸入流:DataInputStream
* DataInputStream(InputStream in)
- 資料輸出流:DataOutputStream
* DataOutputStream(OutputStream out)
public class DataStreamDemo {
public static void main(String[] args) throws IOException {
// 寫
// write();
// 讀
read();
}
private static void read() throws IOException {
// DataInputStream(InputStream in)
// 建立資料輸入流物件
DataInputStream dis = new DataInputStream(
new FileInputStream("dos.txt"));
// 讀資料//注意要對應
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(OutputStream out)
// 建立資料輸出流物件
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();
}
}
3:記憶體操作流(理解)
(1)有些時候我們操作完畢後,未必需要產生一個檔案,就可以使用記憶體操作流。
(2)三種
* 記憶體操作流:用於處理臨時儲存資訊的,程式結束,資料就從記憶體中消失。
* 位元組陣列:
* ByteArrayInputStream
* ByteArrayOutputStream
* 字元陣列:
* CharArrayReader
* CharArrayWriter
* 字串:
* StringReader
* StringWriter
public class ByteArrayStreamDemo {
public static void main(String[] args) throws IOException {
// 寫資料
// ByteArrayOutputStream()
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 寫資料
for (int x = 0; x < 10; x++) {
baos.write(("hello" + x).getBytes());
}
// 釋放資源
// 通過檢視原始碼我們知道這裡什麼都沒做,所以根本需要close()
// baos.close();
// public byte[] toByteArray()
byte[] bys = baos.toByteArray();
// 讀資料
// ByteArrayInputStream(byte[] buf)
ByteArrayInputStream bais = new ByteArrayInputStream(bys);
int by = 0;
while ((by = bais.read()) != -1) {
System.out.print((char) by);
}
// bais.close();
}
}
4:列印流(掌握)
(1)位元組列印流,字元列印流
(2)特點:
* 列印流
* 位元組流列印流 PrintStream
* 字元列印流 PrintWriter
*
* 列印流的特點:
- A:只有寫資料的,沒有讀取資料。只能操作目的地,不能操作資料來源。
- B:可以操作任意型別的資料。
- C:如果啟動了自動重新整理,能夠自動重新整理。
- D:該流是可以直接操作文字檔案的。
- 哪些流物件是可以直接操作文字檔案的呢?
- FileInputStream
- FileOutputStream
- FileReader
- FileWriter
- PrintStream
- PrintWriter
* 看API,查流物件的構造方法,如果同時有File型別和String型別的引數,一般來說就是可以直接操作檔案的。
*
* 流:
* 基本流:就是能夠直接讀寫檔案的
* 高階流:在基本流基礎上提供了一些其他的功能
* 1:可以操作任意型別的資料。
* print()
* println()
* 2:啟動自動重新整理
- PrintWriter pw = new PrintWriter(new FileWriter("pw2.txt"), true);
- 還是應該呼叫println()的方法才可以
- 這個時候不僅僅自動重新整理了,還實現了資料的換行。
- println()
- 其實等價于于:
- write();
- newLine();
- flush();
public class PrintWriterDemo2 {
public static void main(String[] args) throws IOException {
// 建立列印流物件
// PrintWriter pw = new PrintWriter("pw2.txt");
PrintWriter pw = new PrintWriter(new FileWriter("pw2.txt"), true);
// write()是搞不定的,怎麼辦呢?
// 我們就應該看看它的新方法
// pw.print(true);
// pw.print(100);
// pw.print("hello");
pw.println("hello");
pw.println(true);
pw.println(100);
pw.close();
}
}
(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) {
- println(line);
}
pw.close();
br.close();
5:標準輸入輸出流(理解)
(1)System類下面有這樣的兩個欄位
in 標準輸入流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("請輸入一個字串:");
String line = br.readLine();
System.out.println("你輸入的字串是:" + line);
System.out.println("請輸入一個整數:");
// int i = Integer.parseInt(br.readLine());
line = br.readLine();
int i = Integer.parseInt(line);
System.out.println("你輸入的整數是:" + i);
}
}
out 標準輸出流
* 標準輸入輸出流
* System類中的兩個成員變數:
* public static final InputStream in “標準”輸入流。
* public static final PrintStream out “標準”輸出流。
*
* InputStream is = System.in;
* PrintStream ps = System.out;
public class SystemOutDemo {
public static void main(String[] args) {
// 有這裡的講解我們就知道了,這個輸出語句其本質是IO流操作,把資料輸出到控制檯。
System.out.println("helloworld");
// 獲取標準輸出流物件
PrintStream ps = System.out;
ps.println("helloworld");
ps.println();
// ps.print();//這個方法不存在
// System.out.println();
// System.out.print();
}
}
轉換流的應用。
public class SystemOutDemo2 {
public static void main(String[] args) throws IOException {
// 獲取標準輸入流
// // PrintStream ps = System.out;
// // OutputStream os = ps;
// OutputStream os = System.out; // 多型
// // 我能不能按照剛才使用標準輸入流的方式一樣把資料輸出到控制檯呢?
// OutputStreamWriter osw = new OutputStreamWriter(os);
// BufferedWriter bw = new BufferedWriter(osw);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
System.out));
bw.write("hello");
bw.newLine();
// bw.flush();
bw.write("world");
bw.newLine();
// bw.flush();
bw.write("java");
bw.newLine();
bw.flush();
bw.close();
}
}
(2)三種鍵盤錄入方式
A:main方法的args接收引數
B:System.in通過BufferedReader進行包裝
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
C:Scanner
Scanner sc = new Scanner(System.in);
(3)輸出語句的原理和如何使用字元流輸出資料
A:原理
System.out.println("helloworld");
PrintStream ps = System.out;
ps.println("helloworld");
B:把System.out用字元緩衝流包裝一下使用
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
6:隨機訪問流(理解)
(1)可以按照檔案指標的位置寫資料和讀資料。
(2)案例:
A:寫資料
B:讀資料
C:獲取和改變檔案指標的位置
* 隨機訪問流:
* RandomAccessFile類不屬於流,是Object類的子類。
* 但它融合了InputStream和OutputStream的功能。
* 支援對檔案的隨機訪問讀取和寫入。
*
* public RandomAccessFile(String name,String mode):第一個引數是檔案路徑,第二個引數是操作檔案的模式。
* 模式有四種,我們最常用的一種叫"rw",這種方式表示我既可以寫資料,也可以讀取資料
*/
public class RandomAccessFileDemo {
public static void main(String[] args) throws IOException {
// write();
read();
}
private static void read() throws IOException {
// 建立隨機訪問流物件
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 {
// 建立隨機訪問流物件
RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw");
// 怎麼玩呢?
raf.writeInt(100);
raf.writeChar('a');
raf.writeUTF("中國");
raf.close();
}
}
7:合併流(理解)
(1)把多個輸入流的資料寫到一個輸出流中。
(2)構造方法:
A:SequenceInputStream(InputStream s1, InputStream s2)
public class SequenceInputStreamDemo {
public static void main(String[] args) throws IOException {
// SequenceInputStream(InputStream s1, InputStream s2)
// 需求:把ByteArrayStreamDemo.java和DataStreamDemo.java的內容複製到Copy.java中
InputStream s1 = new FileInputStream("ByteArrayStreamDemo.java");
InputStream s2 = new FileInputStream("DataStreamDemo.java");
SequenceInputStream sis = new SequenceInputStream(s1, s2);
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("Copy.java"));
// 如何寫讀寫呢,其實很簡單,你就按照以前怎麼讀寫,現在還是怎麼讀寫
byte[] bys = new byte[1024];
int len = 0;
while ((len = sis.read(bys)) != -1) {
bos.write(bys, 0, len);
}
bos.close();
sis.close();
}
}
B:SequenceInputStream(Enumeration<? extends InputStream> e)
public class SequenceInputStreamDemo2 {
public static void main(String[] args) throws IOException {
// 需求:把下面的三個檔案的內容複製到Copy.java中
// ByteArrayStreamDemo.java,CopyFileDemo.java,DataStreamDemo.java
// SequenceInputStream(Enumeration e)
// 通過簡單的回顧我們知道了Enumeration是Vector中的一個方法的返回值型別。
// Enumeration<E> elements()
Vector<InputStream> v = new Vector<InputStream>();
InputStream s1 = new FileInputStream("ByteArrayStreamDemo.java");
InputStream s2 = new FileInputStream("CopyFileDemo.java");
InputStream s3 = new FileInputStream("DataStreamDemo.java");
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("Copy.java"));
// 如何寫讀寫呢,其實很簡單,你就按照以前怎麼讀寫,現在還是怎麼讀寫
byte[] bys = new byte[1024];
int len = 0;
while ((len = sis.read(bys)) != -1) {
bos.write(bys, 0, len);
}
bos.close();
sis.close();
}
}
8:序列化流(理解)
(1)可以把物件寫入文字檔案或者在網路中傳輸
(2)如何實現序列化呢?
讓被序列化的物件所屬類實現序列化介面。
該介面是一個標記介面。沒有功能需要實現。
(3)注意問題:
把資料寫到檔案後,在去修改類會產生一個問題。
如何解決該問題呢?
在類檔案中,給出一個固定的序列化id值。
而且,這樣也可以解決黃色警告線問題
* 序列化流:把物件按照流一樣的方式存入文字檔案或者在網路中傳輸。物件 -- 流資料(ObjectOutputStream)
* 反序列化流:把文字檔案中的流物件資料或者網路中的流物件資料還原成物件。流資料 -- 物件(ObjectInputStream)
public class ObjectStreamDemo {
public static void main(String[] args) throws IOException,
ClassNotFoundException {
// 由於我們要對物件進行序列化,所以我們先自定義一個類
// 序列化資料其實就是把物件寫到文字檔案
// write();
read();
}
private static void read() throws IOException, ClassNotFoundException {
// 建立反序列化物件
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
"oos.txt"));
// 還原物件
Object obj = ois.readObject();
// 釋放資源
ois.close();
// 輸出物件
System.out.println(obj);
}
private static void write() throws IOException {
// 建立序列化流物件
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
"oos.txt"));
// 建立物件
Person p = new Person("林青霞", 27);
// public final void writeObject(Object obj)
oos.writeObject(p);
// 釋放資源
oos.close();
}
}
/*
* NotSerializableException:未序列化異常
*
* 類通過實現 java.io.Serializable 介面以啟用其序列化功能。未實現此介面的類將無法使其任何狀態序列化或反序列化。
* 該介面居然沒有任何方法,類似於這種沒有方法的介面被稱為標記介面。
*
* java.io.InvalidClassException:
* cn.itcast_07.Person; local class incompatible:
* stream classdesc serialVersionUID = -2071565876962058344,
* local class serialVersionUID = -8345153069362641443
*
* 為什麼會有問題呢?
* Person類實現了序列化介面,那麼它本身也應該有一個標記值。
* 這個標記值假設是100。
* 開始的時候:
* Person.class -- id=100
* wirte資料: oos.txt -- id=100
* read資料: oos.txt -- id=100
*
* 現在:
* Person.class -- id=200
* wirte資料: oos.txt -- id=100
* read資料: oos.txt -- id=100
- 我們在實際開發中,可能還需要使用以前寫過的資料,不能重新寫入。怎麼辦呢?
- 回想一下原因是因為它們的id值不匹配。
- 每次修改java檔案的內容的時候,class檔案的id值都會發生改變。
- 而讀取檔案的時候,會和class檔案中的id值進行匹配。所以,就會出問題。
- 但是呢,如果我有辦法,讓這個id值在java檔案中是一個固定的值,這樣,你修改檔案的時候,這個id值還會發生改變嗎?
- 不會。現在的關鍵是我如何能夠知道這個id值如何表示的呢?
- 不用擔心,你不用記住,也沒關係,點選滑鼠即可。
- 你難道沒有看到黃色警告線嗎?
- 我們要知道的是:
- 看到類實現了序列化介面的時候,要想解決黃色警告線問題,就可以自動產生一個序列化id值。
- 而且產生這個值以後,我們對類進行任何改動,它讀取以前的資料是沒有問題的。
- *
- 注意:
- 我一個類中可能有很多的成員變數,有些我不想進行序列化。請問該怎麼辦呢?
- 使用transient關鍵字宣告不需要序列化的成員變數
public class Person implements Serializable {
private static final long serialVersionUID = -2071565876962058344L;
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 + "]";
}
}
(4)面試題:
什麼時候序列化?
如何實現序列化?
什麼是反序列化?
9:Properties(理解)
(1)是一個集合類,Hashtable的子類
(2)特有功能
A:public Object setProperty(String key,String value)
B:public String getProperty(String key)
C:public Set<String> stringPropertyNames()
- 特殊功能:
- public Object setProperty(String key,String value):新增元素
- public String getProperty(String key):獲取元素
- public Set<String> stringPropertyNames():獲取所有的鍵的集合
public class PropertiesDemo2 {
public static void main(String[] args) {
// 建立集合物件
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);
}
}
}
/*
* class Hashtalbe<K,V> { public V put(K key,V value) { ... } }
*
* class Properties extends Hashtable { public V setProperty(String key,String
* value) { return put(key,value); } }
(3)和IO流結合的方法
把鍵值對形式的文字檔案內容載入到集合中
public void load(Reader reader)
public void load(InputStream inStream)
把集合中的資料儲存到文字檔案中
public void store(Writer writer,String comments)
public void store(OutputStream out,String comments)
這裡的集合必須是Properties集合:
* public void load(Reader reader):把檔案中的資料讀取到集合中
* public void store(Writer writer,String comments):把集合中的資料儲存到檔案
*
* 單機版遊戲:
* 進度儲存和載入。
* 三國群英傳,三國志,仙劍奇俠傳...
*
* 呂布=1
* 方天畫戟=1
*/
public class PropertiesDemo3 {
public static void main(String[] args) throws IOException {
// myLoad();
myStore();
}
private static void myStore() throws IOException {
// 建立集合物件
Properties prop = new Properties();
prop.setProperty("林青霞", "27");
prop.setProperty("武鑫", "30");
prop.setProperty("劉曉曲", "18");
//public void store(Writer writer,String comments):把集合中的資料儲存到檔案
Writer w = new FileWriter("name.txt");
prop.store(w, "helloworld");
w.close();
}
private static void myLoad() throws IOException {
Properties prop = new Properties();
// public void load(Reader reader):把檔案中的資料讀取到集合中
// 注意:這個檔案的資料必須是鍵值對形式
Reader r = new FileReader("prop.txt");
prop.load(r);
r.close();
System.out.println("prop:" + prop);
}
}
(4)案例:
A:根據給定的檔案判斷是否有鍵為"lisi"的,如果有就修改其值為100
- 我有一個文字檔案(user.txt),我知道資料是鍵值對形式的,但是不知道內容是什麼。
- 請寫一個程式判斷是否有“lisi”這樣的鍵存在,如果有就改變其實為”100”
- *
- 分析:
- A:把檔案中的資料載入到集合中
- B:遍歷集合,獲取得到每一個鍵
- C:判斷鍵是否有為"lisi"的,如果有就修改其值為"100"
- D:把集合中的資料重新儲存到檔案中
*/
public class PropertiesTest {
public static void main(String[] args) throws IOException {
// 把檔案中的資料載入到集合中
Properties prop = new Properties();
Reader r = new FileReader("user.txt");
prop.load(r);
r.close();
// 遍歷集合,獲取得到每一個鍵
Set<String> set = prop.stringPropertyNames();
for (String key : set) {
// 判斷鍵是否有為"lisi"的,如果有就修改其值為"100"
if ("lisi".equals(key)) {
prop.setProperty(key, "100");
break;
}
}
// 把集合中的資料重新儲存到檔案中
Writer w = new FileWriter("user.txt");
prop.store(w, null);
w.close();
}
}
B:寫一個程式實現控制猜數字小遊戲程式不能玩超過5次
* 我有一個猜數字小遊戲的程式,請寫一個程式實現在測試類中只能用5次,超過5次提示:遊戲試玩已結束,請付費。
public class PropertiesTest2 {
public static void main(String[] args) throws IOException {
// 讀取某個地方的資料,如果次數不大於5,可以繼續玩。否則就提示"遊戲試玩已結束,請付費。"
// 建立一個檔案
// File file = new File("count.txt");
// if (!file.exists()) {
// file.createNewFile();
// }
// 把資料載入到集合中
Properties prop = new Properties();
Reader r = new FileReader("count.txt");
prop.load(r);
r.close();
// 我自己的程式,我當然知道里面的鍵是誰
String value = prop.getProperty("count");
int number = Integer.parseInt(value);
if (number > 5) {
System.out.println("遊戲試玩已結束,請付費。");
System.exit(0);
} else {
number++;
prop.setProperty("count",String.valueOf(number));
Writer w = new FileWriter("count.txt");
prop.store(w, null);
w.close();
GuessNumber.start();
}
}
}
(瞭解)
(1)JDK4出現的NIO,對以前的IO操作進行了優化,提供了效率。但是大部分我們看到的還是以前的IO
(2)JDK7的NIO的使用
Path:路徑
Paths:通過靜態方法返回一個路徑
Files:提供了常見的功能
複製文字檔案
把集合中的資料寫到文字檔案
* nio包在JDK4出現,提供了IO流的操作效率。但是目前還不是大範圍的使用。
* 有空的話瞭解下,有問題再問我。
* JDK7的之後的nio:
* Path:路徑
* Paths:有一個靜態方法返回一個路徑
* public static Path get(URI uri)
* Files:提供了靜態方法供我們使用
* 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 {
// public static long copy(Path source,OutputStream out)
// Files.copy(Paths.get("ByteArrayStreamDemo.java"), new
// FileOutputStream(
// "Copy.java"));
ArrayList<String> array = new ArrayList<String>();
array.add("hello");
array.add("world");
array.add("java");
Files.write(Paths.get("array.txt"), array, Charset.forName("GBK"));
}
}