寫在前面 1.概念 IO流用來處理設備之間的數據傳輸 Java對數據的操作是通過流的方式 Java用於操作流的類都在IO包中 流按流向分為兩種:輸入流,輸出流 流按操作類型分為兩種: 位元組流 : 位元組流可以操作任何數據,因為在電腦中任何數據都是以位元組的形式存儲的 字元流 : 字元流只能操作純字元數 ...
- - - - - - - - - - - - - - -
寫在前面
- - - - - - - - - - - - - - -
- 1.概念
- IO流用來處理設備之間的數據傳輸
- Java對數據的操作是通過流的方式
- Java用於操作流的類都在IO包中
- 流按流向分為兩種:輸入流,輸出流
- 流按操作類型分為兩種:
- 位元組流 : 位元組流可以操作任何數據,因為在電腦中任何數據都是以位元組的形式存儲的
- 字元流 : 字元流只能操作純字元數據,比較方便
- 2.IO流常用父類
- 位元組流的抽象父類:
InputStream
OutputStream
- 字元流的抽象父類:
Reader
Writer
- 位元組流的抽象父類:
- 3.IO程式書寫
- 使用前,導入IO包中的類
- 使用時,進行IO異常處理
- 使用後,釋放資源
- - - - - - - - - - - - - - -
目 錄
- - - - - - - - - - - - - - -
4.實現了緩衝區的BufferedInputStream和BufferOutputStream
25.定義一個文件輸入流,調用read(byte[] b)方法,將a.txt文件中的內容列印出來(byte數組大小限製為5)
38.Properties的load()和store()功能
- - - - - - - - - - - - - - -
read()一次讀取一個位元組
FileInputStream fis = new FileInputStream("烏合之眾.txt");
//創建一個文件輸入流對象,並關聯烏合之眾.txt
int b;
//定義變數,記錄每次讀到的位元組
while((b = fis.read()) != -1) {
//將每次讀到的位元組賦值給b並判斷是否是-1
System.out.println(b);
//列印每一個位元組
}
fis.close();
//關閉流釋放資源
read()方法返回值為什麼是int
read()
方法讀取的是一個位元組,為什麼返回是int
,而不是byte
- 因為位元組輸入流可以操作任意類型的文件,比如圖片音頻等,這些都是以二進位形式的存儲的,如果每次讀取都返回
byte
,有可能在讀到中間的時候遇到11111111
,那麼這11111111
是byte
類型的-1
,程式是遇到-1就會停止
,後面的數據就讀不到了。所以在讀取的時候用int
類型接收,會在其前面補上24個0湊足4個位元組,那麼byte
類型的-1
就變成int
類型的255
了這樣可以保證整個數據讀完,而結束標記的-1
就是int
類型。
定義小數組實現緩衝
write(byte[] b)
write(byte[] b, int off, int len)
寫出有效的位元組個數定義小數組的標準格式
FileInputStream fis = new FileInputStream("李志 - 梵高先生.flac");
FileOutputStream fos = new FileOutputStream("梵高先生.flac");
int len;
byte arr[] = new byte[8*1024];
//自定義位元組數組
while((len=fis.read(arr))!=-1){
fos.write(arr, 0, len);
//寫出位元組數組寫出有效個位元組個數
}
fis.close();
fos.close();
實現了緩衝區的BufferedInputStream和BufferOutputStream
BufferedInputStream
BufferedInputStream
內置了一個緩衝區(數組)- 從
BufferedInputStream
中讀取一個位元組時,BufferedInputStream
會一次性從文件中讀取8192
個, 存在緩衝區中, 然後返回給程式一個字元。 - 程式再次讀取時, 就不用找文件了, 直接從緩衝區中獲取。
- 直到緩衝區中所有的都被使用過, 才重新從文件中讀取
8192
個
BufferedOutputStream
BufferedOutputStream
也內置了一個緩衝區(數組)- 程式向流中寫出位元組時, 不會直接寫到文件, 先寫到緩衝區中,
- 直到緩衝區寫滿,
BufferedOutputStream
才會把緩衝區中的數據一次性寫到文件里。
- 組合流過濾器實現拷貝
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("李志 - 梵高先生.flac"));
//創建緩衝區對FileInputStream對象的裝飾
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("2004-梵高先生.flac"));
//創建緩衝區對FileOutputStream對象的裝飾
int b;
while((b = bis.read()) != -1) {
bos.write(b);
}
bis.close();//只關裝飾後的對象即可
bos.close();
- 小數組的讀寫和帶
Buffered
的讀取哪個更快?- 定義小數組如果是
8192
個位元組大小和Buffered
比較的話,定義小數組會略勝一籌,因為讀和寫操作的是同一個數組,而Buffered
操作的是兩個數組
.
- 定義小數組如果是
flush方法和close方法
flush()
方法- 用來刷新緩衝區的,刷新後可以再次寫出
close()
方法- 用來關閉流釋放資源的的,如果是帶緩衝區的流對象的
close()
方法,不但會關閉流,還會再關閉流之前刷新緩衝區,關閉後不能再寫出
- 用來關閉流釋放資源的的,如果是帶緩衝區的流對象的
位元組流讀寫中文
- 位元組流讀取中文的問題
- 位元組流在讀中文的時候有可能會讀到半個中文,造成亂碼
- 位元組流寫出中文的問題
- 位元組流直接操作的位元組,所以寫出中文必須將字元串轉換成位元組數組
- 寫出回車換行 write("\r\n".getBytes());
流的標準處理異常代碼1.6版本及其以前
- try finally嵌套
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("from.txt");
fos = new FileOutputStream("to.txt");
int b;
while((b = fis.read()) != -1) {
fos.write(b);
}
} finally {
try {
if(fis != null)
fis.close();
}finally {
if(fos != null)
fos.close();
}
}
流的標準處理異常代碼1.7版本之後
- try close
try(
FileInputStream fis = new FileInputStream("from.txt");
FileOutputStream fos = new FileOutputStream("to.txt");
){
int b;
while((b = fis.read()) != -1) {
fos.write(b);
}
}
- 在try()中創建的流對象必須實現了AutoCloseable這個介面,如果實現了,在try後面的
{. . .}
執行後就會自動調用流對象的close方法將流關掉.
拷貝文件
- 在控制台錄入文件的路徑,將文件拷貝到當前項目下
Scanner sc = new Scanner(System.in);
System.out.println("請輸入一個文件路徑");
String line = sc.nextLine(); //將鍵盤錄入的文件路徑存儲在line中
File file = new File(line); //封裝成File對象
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(file.getName());
int len;
byte[] arr = new byte[8192]; //定義緩衝區
while((len = fis.read(arr)) != -1) {
fos.write(arr,0,len);
}
fis.close();
fos.close();
錄入數據拷貝到文件
- 將鍵盤錄入的數據拷貝到當前項目下的text.txt文件中,鍵盤錄入數據當遇到quit時就退出
Scanner sc = new Scanner(System.in);
FileOutputStream fos = new FileOutputStream("text.txt");
System.out.println("請輸入:");
while(true) {
String line = sc.nextLine();
if("quit".equals(line))
break;
fos.write(line.getBytes());
fos.write("\r\n".getBytes());
}
fos.close();
字元流 FileReader
- 字元流是什麼
- 字元流是可以直接讀寫字元的IO流
- 字元流讀取
字元
, 就要先讀取到位元組
數據, 然後轉為字元
. 如果要寫出字元, 需要把字元轉為位元組再寫出.
- FileReader
- FileReader類的read()方法可以按照字元大小讀取
FileReader fr = new FileReader("from.txt");
//創建輸入流對象,關聯from.txt
int ch;
while((ch = fr.read()) != -1) {
//將讀到的字元賦值給ch
System.out.println((char)ch);
//將讀到的字元強轉後列印
}
fr.close();
//關流
字元流 FileWriter
- FileWriter類的write()方法可以自動把
字元
轉為位元組
寫出
FileWriter fw = new FileWriter("to.txt");
fw.write("write");
fw.close();
字元流的拷貝
FileReader fr = new FileReader("from.txt");
FileWriter fw = new FileWriter("to.txt");
int ch;
while((ch = fr.read()) != -1) {
fw.write(ch);
}
fr.close();
fw.close();
什麼情況下使用字元流
- 字元流也可以拷貝文本文件, 但不推薦使用。因為讀取時會把位元組轉為字元, 寫出時還要把字元轉回位元組。
- 程式需要讀取一段文本, 或者需要寫出一段文本的時候可以使用字元流。讀取的時候是按照字元的大小讀取的,不會出現讀取半個中文,造成亂碼的情況。寫出的時候可以直接將字元串寫出,不用轉換為位元組數組。
字元流是否可以拷貝非純文本的文件
- 不可以拷貝非純文本的文件
- 因為在讀的時候會將位元組轉換為字元,在轉換過程中,可能找不到對應的字元,就會用
"?"
代替,寫出的時候會將"?"
字元轉換成位元組寫出去。如此這般,寫出之後的文件就錯亂了。
- 因為在讀的時候會將位元組轉換為字元,在轉換過程中,可能找不到對應的字元,就會用
自定義字元數組的拷貝
FileReader fr = new FileReader("form.txt");
//創建字元輸入流,關聯aaa.txt
FileWriter fw = new FileWriter("to.txt");
//創建字元輸出流,關聯bbb.txt
int len;
char[] arr = new char[1024*8];
//創建字元數組
while((len = fr.read(arr)) != -1) {
//將數據讀到字元數組中
fw.write(arr, 0, len);
//從字元數組將數據寫到文件上
}
fr.close();
//關流釋放資源
fw.close();
帶緩衝的字元流
BufferedReader
的read()
方法讀取字元時會一次讀取若幹字元到緩衝區, 然後逐個返回給程式, 減少讀取次數, 以期提高效率。BufferedWriter
的write()
方法寫出字元時會先寫到緩衝區, 緩衝區寫滿時才會寫到文件, 減少寫入次數, 以期提高效率。
BufferedReader br = new BufferedReader(new FileReader("form.txt"));
//創建字元輸入流對象,關聯aaa.txt
BufferedWriter bw = new BufferedWriter(new FileWriter("to.txt"));
//創建字元輸出流對象,關聯bbb.txt
int ch;
while((ch = br.read()) != -1) {
//read一次,會先將緩衝區讀滿,從緩衝去中一個一個的返給臨時變數ch
bw.write(ch);
//write一次,是將數據裝到字元數組,裝滿後再一起寫出去
}
br.close();
//關流
bw.close();
readLine()和newLine()方法
BufferedReader
的readLine()
方法可以讀取一行字元(不包含換行符號)BufferedWriter
的newLine()
可以輸出一個跨平臺的換行符號"\r\n"
BufferedReader br = new BufferedReader(new FileReader("aaa.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("bbb.txt"));
String line;
while((line = br.readLine()) != null) {
bw.write(line);
//bw.write(""); //只支持windows系統
bw.newLine(); //跨平臺的
}
br.close();
bw.close();
LineNumberReader
LineNumberReader
是BufferedReader
的子類, 具有相同的功能, 並且可以統計行號- 調用
getLineNumber()
方法可以獲取當前行號 - 調用
setLineNumber()
方法可以設置當前行號
- 調用
LineNumberReader lnr = new LineNumberReader(new FileReader("form.txt"));
String line;
lnr.setLineNumber(100); //設置行號
while((line = lnr.readLine()) != null) {
System.out.println(lnr.getLineNumber() + ":" + line);//獲取行號
}
lnr.close();
裝飾設計模式
interface Coder {
public void code();
}
class Persion implements Coder {
@Override
public void code() {
System.out.println("It's none of my business during the daytime");
System.out.println("Write java at night");
}
}
class XPersion implements Coder {
private Persion s;
//被包裝的類的引用
public XPersion (Persion s) {
//構造方法將被包裝的對象作為參數傳入
this.s = s;
}
@Override
public void code() {
//對其原有功能進行升級
s.code();
System.out.println("Get cervical spondum buff");
System.out.println("......");
System.out.println("sudden death");
System.out.println("......");
}
}
使用指定的碼表讀寫字元
- FileReader是使用預設碼表讀取文件, 如果需要使用指定碼表讀取, 那麼可以使用
InputStreamReader(位元組流,編碼表)
- FileWriter是使用預設碼表寫出文件, 如果需要使用指定碼表寫出, 那麼可以使用
OutputStreamWriter(位元組流,編碼表)
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("UTF-8.txt"), "UTF-8"));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("GBK.txt"), "GBK"));
int ch;
while((ch = br.read()) != -1) {
bw.write(ch);
}
br.close();
bw.close();
序列流
- 1.什麼是序列流
- 序列流可以把多個位元組輸入流整合成一個, 從序列流中讀取數據時, 將從被整合的第一個流開始讀, 讀完一個之後繼續讀第二個, 以此類推.
- 2.使用方式
- 整合兩個:
SequenceInputStream(InputStream, InputStream)
- 整合兩個:
FileInputStream fis1 = new FileInputStream("a.txt");
//創建輸入流對象,關聯a.txt
FileInputStream fis2 = new FileInputStream("b.txt");
//創建輸入流對象,關聯b.txt
SequenceInputStream sis = new SequenceInputStream(fis1, fis2);
//將兩個流整合成一個流
FileOutputStream fos = new FileOutputStream("c.txt");
//創建輸出流對象,關聯c.txt
int b;
while((b = sis.read()) != -1) {
//用整合後的輸入流
fos.write(b);
//寫到指定文件上
}
sis.close();
fos.close();
序列流整合多個
- 整合多個:
SequenceInputStream(Enumeration)
FileInputStream fis1 = new FileInputStream("a.txt");
//創建輸入流對象,關聯a.txt
FileInputStream fis2 = new FileInputStream("b.txt");
//創建輸入流對象,關聯b.txt
FileInputStream fis3 = new FileInputStream("c.txt");
//創建輸入流對象,關聯c.txt
Vector<InputStream> v = new Vector<>();
//創建vector集合對象
v.add(fis1);
//將流對象添加
v.add(fis2);
v.add(fis3);
Enumeration<InputStream> en = v.elements();
//獲取枚舉引用
SequenceInputStream sis = new SequenceInputStream(en);
//en傳遞給SequenceInputStream的構造方法
FileOutputStream fos = new FileOutputStream("d.txt");
int b;
while((b = sis.read()) != -1) {
fos.write(b);
}
sis.close();
fos.close();
記憶體輸出流
- 1.什麼是記憶體輸出流
- 該輸出流可以向記憶體中寫數據, 把記憶體當作一個緩衝區, 寫出之後可以一次性獲取所有數據
- 2.使用方式
- 創建對象:
new ByteArrayOutputStream()
- 寫出數據:
write(int), write(byte[])
- 獲取數據:
toByteArray()
- 創建對象:
FileInputStream fis = new FileInputStream("a.txt");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int b;
while((b = fis.read()) != -1) {
baos.write(b);
}
//byte[] newArr = baos.toByteArray();
//將記憶體緩衝區中所有的位元組存儲在newArr中
//System.out.println(new String(newArr));
System.out.println(baos);
fis.close();
定義一個文件輸入流,調用read(byte[] b)方法,將a.txt文件中的內容列印出來(byte數組大小限製為5)
FileInputStream fis = new FileInputStream("a.txt");
//創建位元組輸入流,關聯a.txt
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//創建記憶體輸出流
byte[] arr = new byte[5];
//創建位元組數組,大小為5
int len;
while((len = fis.read(arr)) != -1) {
//將文件上的數據讀到位元組數組中
baos.write(arr, 0, len);
//將位元組數組的數據寫到記憶體緩衝區中
}
System.out.println(baos);
//將記憶體緩衝區的內容轉換為字元串列印
fis.close();
對象操作流ObjecOutputStream
- 1.什麼是對象操作流
- 該流可以將一個對象寫出, 或者讀取一個對象到程式中. 也就是序列化和反序列化的操作.
- 2.使用方式
- 寫出:
new ObjectOutputStream(OutputStream)
,writeObject()
- 寫出:
public class ObjectOutputStream {
/**
* @param args
* @throws IOException
* 將對象寫出,序列化
*/
public static void main(String[] args) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("e.txt"));//創建對象輸出流
oos.writeObject(p1);
oos.writeObject(p2);
oos.close();
}
}
對象操作流ObjectInputStream
- 讀取:
new ObjectInputStream(InputStream)
,readObject()
public class ObjectInputStream {
/**
* @param args
* @throws IOException
* @throws ClassNotFoundException
* @throws FileNotFoundException
* 讀取對象,反序列化
*/
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("e.txt"));
Person p1 = (Person) ois.readObject();
Person p2 = (Person) ois.readObject();
System.out.println(p1);
System.out.println(p2);
ois.close();
}
}
對象操作流優化
- 將對象存儲在集合中寫出
Person p1 = new Person("Tom", 20);
Person p2 = new Person("Jerry", 22);
Person p3 = new Person("Jack", 10);
Person p4 = new Person("Herry", 20);
ArrayList<Person> list = new ArrayList<>();
list.add(p1);
list.add(p2);
list.add(p3);
list.add(p4);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("f.txt"));
oos.writeObject(list);
//寫出集合對象
oos.close();
- 讀取到的是一個集合對象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("f.txt"));
ArrayList<Person> list = (ArrayList<Person>)ois.readObject();
//泛型在運行期會被擦除,索引運行期相當於沒有泛型
//想去掉黃色可以加註解@SuppressWarnings("unchecked")
for (Person person : list) {
System.out.println(person);
}
ois.close();
id號
- 要寫出的對象必須實現Serializable介面才能被序列化
- 不用必須加id號
列印流的概述和特點
- 1.什麼是列印流
- 該流可以很方便的將對象的
toString()
結果輸出, 並且自動加上換行, 而且可以使用自動刷出的模式 System.out
就是一個PrintStream
, 其預設向控制台輸出信息
- 該流可以很方便的將對象的
PrintStream ps = System.out;
ps.println(97);
//底層用的是Integer.toString(x),將x轉換為數字字元串列印
ps.println("a string");
ps.println(new Person("Tom", 20));
Person p = null;
ps.println(p); //如果是null,就返回null,如果不是null,就調用對象的toString()
- 2.使用方式
- 列印:
print()
,println()
- 自動刷出:
PrintWriter(OutputStream out, boolean autoFlush, String encoding)
- 列印:
PrintWriter pw = new PrintWriter(new FileOutputStream("g.txt"), true);
//如果為 true,則 println、printf 或 format 方法將刷新輸出緩衝區
pw.write(97);
pw.print("Hello");
pw.println("你好");
pw.close();
標準輸入輸出流概述和輸出語句
- 1.什麼是標準輸入輸出流
System.in
是InputStream
, 標準輸入流, 預設可以從鍵盤輸入讀取位元組數據System.out
是PrintStream
, 標準輸出流, 預設可以向Console中輸出字元和位元組數據
- 2.修改標準輸入輸出流
- 修改輸入流:
System.setIn(InputStream)
- 修改輸出流:
System.setOut(PrintStream)
- 修改輸入流:
System.setIn(new FileInputStream("a.txt"));
//修改標準輸入流
System.setOut(new PrintStream("b.txt"));
//修改標準輸出流
InputStream in = System.in;
//獲取標準輸入流
PrintStream ps = System.out;
//獲取標準輸出流
int b;
while((b = in.read()) != -1) {
//從a.txt上讀取位元組
ps.write(b);
//將數據寫到b.txt上
}
in.close();
ps.close();
修改標準輸入輸出流拷貝圖片
System.setIn(new FileInputStream("png.png"));
//改變標準輸入流
System.setOut(new PrintStream("copy.png"));
//改變標準輸出流
InputStream is = System.in;
//獲取標準輸入流
PrintStream ps = System.out;
//獲取標準輸出流
int len;
byte[] arr = new byte[1024 * 8];
while((len = is.read(arr)) != -1) {
ps.write(arr, 0, len);
}
is.close();
ps.close();
兩種方式實現鍵盤錄入
- A:
BufferedReader
的readLine
方法。BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
- B:
Scanner
隨機訪問流概述和讀寫數據
- A:隨機訪問流概述
RandomAccessFile
類不屬於流,是Object
類的子類。但它融合了InputStream
和OutputStream
的功能。- 支持對隨機訪問文件的讀取和寫入。
- B:
read()
,write()
,seek()
數據輸入輸出流
- 1.什麼是數據輸入輸出流
DataInputStream
,DataOutputStream
可以按照基本數據類型大小讀寫數據- 例如按Long大小寫出一個數字, 寫出時該數據占8位元組. 讀取的時候也可以按照Long類型讀取, 一次讀取8個位元組.
- 2.使用方式
DataOutputStream(OutputStream)
,writeInt()
,writeLong()
DataInputStream(InputStream)
,readInt()
,readLong()
DataOutputStream dos = new DataOutputStream(new FileOutputStream("b.txt"));
dos.writeInt(998);
dos.writeInt(1998);
dos.writeInt(2998);
dos.close();
DataInputStream dis = new DataInputStream(new FileInputStream("b.txt"));
int x = dis.readInt();
int y = dis.readInt();
int z = dis.readInt();
System.out.println(x);
System.out.println(y);
System.out.println(z);
dis.close();
Properties的概述和作為Map集合的使用
- A:Properties的概述
- Properties 類表示了一個持久的屬性集。
- Properties 可保存在流中或從流中載入。
- 屬性列表中每個鍵及其對應值都是一個字元串。
Properties prop = new Properties();
prop.put("abc", 123);
System.out.println(prop);
獲取Properties中的每一個鍵
- A:Properties的特殊功能
public Object setProperty(String key,String value)
public String getProperty(String key)
public Enumeration<String> stringPropertyNames()
Properties prop = new Properties();
prop.setProperty("name", "Tom");
prop.setProperty("tel", "18000000000");
//System.out.println(prop);
Enumeration<String> en = (Enumeration<String>) prop.propertyNames();
while(en.hasMoreElements()) {
String key = en.nextElement(); //獲取Properties中的每一個鍵
String value = prop.getProperty(key); //根據鍵獲取值
System.out.println(key + "="+ value);
}
Properties的load()和store()功能
Properties prop = new Properties();
prop.load(new FileInputStream("config.properties"));
//將文件上的鍵值對讀取到集合中
prop.setProperty("tel", "18912345678");
prop.store(new FileOutputStream("config.properties"), null);
//第二個參數是對列表參數的描述,可以給值,也可以給null
System.out.println(prop);
Output:
{tel=18912345678}
(-̇᷇̂ᴥ ̇᷇̂-) ↓