IO流介紹
IO流,什麼是IO?
I : Input
O : Output
通過IO可以完成硬盤文件的讀和寫。
IO流的分類
有多種分類方式:
一種方式是按照流的方向進行分類:
以內存作爲參照物,
往內存中去,叫做輸入(Input)。或者叫做讀(Read)。
從內存中出來,叫做輸出(Output)。或者叫做寫(Write)。
另一種方式是按照讀取數據方式不同進行分類:
有的流是按照字節的方式讀取數據,一次讀取1個字節byte,等同於一次讀取8個二進制位。
這種流是萬能的,什麼類型的文件都可以讀取。包括:文本文件,圖片,聲音文件,視頻文件等....
假設文件file1.txt,採用字節流的話是這樣讀的:
a中國bc張三fe
第一次讀:一個字節,正好讀到'a'
第二次讀:一個字節,正好讀到'中'字符的一半。
第三次讀:一個字節,正好讀到'中'字符的另外一半。
有的流是按照字符的方式讀取數據的,一次讀取一個字符,這種流是爲了方便讀取
普通文本文件而存在的,這種流不能讀取:圖片、聲音、視頻等文件。只能讀取純
文本文件,連word文件都無法讀取。
假設文件file1.txt,採用字符流的話是這樣讀的:
a中國bc張三fe
第一次讀:'a'字符('a'字符在windows系統中佔用1個字節。)
第二次讀:'中'字符('中'字符在windows系統中佔用2個字節。)
綜上所述:流的分類
輸入流、輸出流
字節流、字符流
IO流需要掌握的知識點
Java中的IO流都已經寫好了,我們程序員不需要關心,我們最主要還是掌握:
在java中已經提供了哪些流,每個流的特點是什麼,每個流對象上的常用方法有哪些?
java中所有的流都是在:java.io.*;下。
java中主要還是研究:
怎麼new流對象。
調用流對象的哪個方法是讀,哪個方法是寫。
java IO流這塊有四大家族:
四大家族的首領:
java.io.InputStream 字節輸入流
java.io.OutputStream 字節輸出流
java.io.Reader 字符輸入流
java.io.Writer 字符輸出流
四大家族的首領都是抽象類。(abstract class)
所有的流都實現了:
java.io.Closeable接口,都是可關閉的,都有close()方法。
流畢竟是一個管道,這個是內存和硬盤之間的通道,用完之後一定要關閉,
不然會耗費(佔用)很多資源。養成好習慣,用完流一定要關閉。
所有的輸出流都實現了:
java.io.Flushable接口,都是可刷新的,都有flush()方法。
養成一個好習慣,輸出流在最終輸出之後,一定要記得flush()
刷新一下。這個刷新表示將通道/管道當中剩餘未輸出的數據
強行輸出完(清空管道!)刷新的作用就是清空管道。
注意:如果沒有flush()可能會導致丟失數據。
注意:在java中只要“類名”以Stream結尾的都是字節流。以“Reader/Writer”結尾的都是字符流。
java.io包下需要掌握的流有16個:
文件專屬:
java.io.FileInputStream(掌握)
java.io.FileOutputStream(掌握)
java.io.FileReader
java.io.FileWriter
轉換流:(將字節流轉換成字符流)
java.io.InputStreamReader
java.io.OutputStreamWriter
緩衝流專屬:
java.io.BufferedReader
java.io.BufferedWriter
java.io.BufferedInputStream
java.io.BufferedOutputStream
數據流專屬:
java.io.DataInputStream
java.io.DataOutputStream
標準輸出流:
java.io.PrintWriter
java.io.PrintStream(掌握)
對象專屬流:
java.io.ObjectInputStream(掌握)
java.io.ObjectOutputStream(掌握)
FileInputStream(字節輸入流)
java.io.FileInputStream:
1、文件字節輸入流,萬能的,任何類型的文件都可以採用這個流來讀。
2、字節的方式,完成輸入的操作,完成讀的操作(硬盤---> 內存)
代碼演示(常用方法):
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
int read(byte[] b)
一次最多讀取 b.length 個字節。
減少硬盤和內存的交互,提高程序的執行效率。
往byte[]數組當中讀。
*/
public class FileInputStreamTest03 {
public static void main(String[] args) {
FileInputStream fis = null;
try {
// 相對路徑的話呢?相對路徑一定是從當前所在的位置作爲起點開始找!
// IDEA默認的當前路徑是哪裏?工程Project的根就是IDEA的默認當前路徑。
//fis = new FileInputStream("tempfile3");
//fis = new FileInputStream("chapter23/tempfile2");
//fis = new FileInputStream("chapter23/src/tempfile3");
fis = new FileInputStream("chapter23/src/com/bjpowernode/java/io/tempfile4");
// 開始讀,採用byte數組,一次讀取多個字節。最多讀取“數組.length”個字節。
byte[] bytes = new byte[4]; // 準備一個4個長度的byte數組,一次最多讀取4個字節。
// 這個方法的返回值是:讀取到的字節數量。(不是字節本身)
int readCount = fis.read(bytes);
System.out.println(readCount); // 第一次讀到了4個字節。
// 將字節數組全部轉換成字符串
//System.out.println(new String(bytes)); // abcd
// 不應該全部都轉換,應該是讀取了多少個字節,轉換多少個。
System.out.println(new String(bytes,0, readCount));
readCount = fis.read(bytes); // 第二次只能讀取到2個字節。
System.out.println(readCount); // 2
// 將字節數組全部轉換成字符串
//System.out.println(new String(bytes)); // efcd
// 不應該全部都轉換,應該是讀取了多少個字節,轉換多少個。
System.out.println(new String(bytes,0, readCount));
readCount = fis.read(bytes); // 1個字節都沒有讀取到返回-1
System.out.println(readCount); // -1
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
代碼演示(完整程序):
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest04 {
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("chapter23/src/tempfile3");
// 準備一個byte數組
byte[] bytes = new byte[4];
/*while(true){
int readCount = fis.read(bytes);
if(readCount == -1){
break;
}
// 把byte數組轉換成字符串,讀到多少個轉換多少個。
System.out.print(new String(bytes, 0, readCount));
}*/
int readCount = 0;
while((readCount = fis.read(bytes)) != -1) {
System.out.print(new String(bytes, 0, readCount));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
代碼演示(available和skip方法):
FileInputStream類的其它常用方法:
int available():返回流當中剩餘的沒有讀到的字節數量
long skip(long n):跳過幾個字節不讀。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest05 {
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("tempfile");
System.out.println("總字節數量:" + fis.available());
// 讀1個字節
//int readByte = fis.read();
// 還剩下可以讀的字節數量是:5
//System.out.println("剩下多少個字節沒有讀:" + fis.available());
// 這個方法有什麼用?
//byte[] bytes = new byte[fis.available()]; // 這種方式不太適合太大的文件,因爲byte[]數組不能太大。
// 不需要循環了。
// 直接讀一次就行了。
//int readCount = fis.read(bytes); // 6
//System.out.println(new String(bytes)); // abcdef
// skip跳過幾個字節不讀取,這個方法也可能以後會用!
fis.skip(3);
System.out.println(fis.read()); //100
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
FileOutputStream(字節輸出流)
* 文件字節輸出流,負責寫。
* 從內存到硬盤。
* 寫完後需要刷新:變量名.flush();
代碼演示(文件新建):
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStreamTest01 {
public static void main(String[] args) {
FileOutputStream fos = null;
try {
// myfile文件不存在的時候會自動新建!
// 這種方式謹慎使用,這種方式會先將原文件清空,然後重新寫入。
//fos = new FileOutputStream("myfile");
//fos = new FileOutputStream("chapter23/src/tempfile3");
// 以追加的方式在文件末尾寫入。不會清空原文件內容。
fos = new FileOutputStream("chapter23/src/tempfile3", true);
// 開始寫。
byte[] bytes = {97, 98, 99, 100};
// 將byte數組全部寫出!
fos.write(bytes); // abcd
// 將byte數組的一部分寫出!
fos.write(bytes, 0, 2); // 再寫出ab
// 字符串
String s = "我是一箇中國人,我驕傲!!!";
// 將字符串轉換成byte數組。
byte[] bs = s.getBytes();
// 寫
fos.write(bs);
// 寫完之後,最後一定要刷新
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
FileInputStream和FileOutputStream完成文件拷貝
使用FileInputStream + FileOutputStream完成文件的拷貝。
拷貝的過程應該是一邊讀,一邊寫。
使用以上的字節流拷貝文件的時候,文件類型隨意,萬能的。什麼樣的文件都能拷貝。
代碼演示(文件拷貝):
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Copy01 {
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
// 創建一個輸入流對象
fis = new FileInputStream("D:\\course\\02-JavaSE\\video\\chapter01\\動力節點-JavaSE-杜聚賓-001-文件擴展名的顯示.avi");
// 創建一個輸出流對象
fos = new FileOutputStream("C:\\動力節點-JavaSE-杜聚賓-001-文件擴展名的顯示.avi");
// 最核心的:一邊讀,一邊寫
byte[] bytes = new byte[1024 * 1024]; // 1MB(一次最多拷貝1MB。)
int readCount = 0;
while((readCount = fis.read(bytes)) != -1) {
fos.write(bytes, 0, readCount);
}
// 刷新,輸出流最後要刷新
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 分開try,不要一起try。
// 一起try的時候,其中一個出現異常,可能會影響到另一個流的關閉。
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
FileRead(字符輸入流):
FileReader:
文件字符輸入流,只能讀取普通文本。
讀取文本內容時,比較方便,快捷。
代碼演示:
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest {
public static void main(String[] args) {
FileReader reader = null;
try {
// 創建文件字符輸入流
reader = new FileReader("tempfile");
//準備一個char數組
char[] chars = new char[4];
// 往char數組中讀
reader.read(chars); // 按照字符的方式讀取:第一次e,第二次f,第三次 風....
for(char c : chars) {
System.out.println(c);
}
/*// 開始讀
char[] chars = new char[4]; // 一次讀取4個字符
int readCount = 0;
while((readCount = reader.read(chars)) != -1) {
System.out.print(new String(chars,0,readCount));
}*/
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
FileWrite(字符輸入流)
FileReader:
文件字符輸入流,只能讀取普通文本。
讀取文本內容時,比較方便,快捷。
代碼演示:
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterTest {
public static void main(String[] args) {
FileWriter out = null;
try {
// 創建文件字符輸出流對象
//out = new FileWriter("file");
out = new FileWriter("file", true);
// 開始寫。
char[] chars = {'我','是','中','國','人'};
out.write(chars);
out.write(chars, 2, 3);
out.write("我是一名java軟件工程師!");
// 寫出一個換行符。
out.write("\n");
out.write("hello world!");
// 刷新
out.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
FileRead + FileWrite 拷貝文本
使用FileReader FileWriter進行拷貝的話,只能拷貝“普通文本”文件。
代碼演示(文本拷貝):
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Copy02 {
public static void main(String[] args) {
FileReader in = null;
FileWriter out = null;
try {
// 讀
in = new FileReader("chapter23/src/com/bjpowernode/java/io/Copy02.java");
// 寫
out = new FileWriter("Copy02.java");
// 一邊讀一邊寫:
char[] chars = new char[1024 * 512]; // 1MB
int readCount = 0;
while((readCount = in.read(chars)) != -1){
out.write(chars, 0, readCount);
}
// 刷新
out.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
BufferedReader,BufferedWrite(帶有緩衝區的字符輸入流):
BufferedReader:
帶有緩衝區的字符輸入流。
使用這個流的時候不需要自定義char數組,或者說不需要自定義byte數組。自帶緩衝。
代碼演示:
import java.io.BufferedReader;
import java.io.FileReader;
public class BufferedReaderTest01 {
public static void main(String[] args) throws Exception{
FileReader reader = new FileReader("Copy02.java");
// 當一個流的構造方法中需要一個流的時候,這個被傳進來的流叫做:節點流。
// 外部負責包裝的這個流,叫做:包裝流,還有一個名字叫做:處理流。
// 像當前這個程序來說:FileReader就是一個節點流。BufferedReader就是包裝流/處理流。
BufferedReader br = new BufferedReader(reader);
// 讀一行
/*String firstLine = br.readLine();
System.out.println(firstLine);
String secondLine = br.readLine();
System.out.println(secondLine);
String line3 = br.readLine();
System.out.println(line3);*/
// br.readLine()方法讀取一個文本行,但不帶換行符。
String s = null;
while((s = br.readLine()) != null){
System.out.print(s);
}
// 關閉流
// 對於包裝流來說,只需要關閉最外層流就行,裏面的節點流會自動關閉。(可以看源代碼。)
br.close();
}
}
InputStreamReader,OutputStreamWrite(轉換流):
代碼演示(讀取Read):
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
/*
轉換流:InputStreamReader
*/
public class BufferedReaderTest02 {
public static void main(String[] args) throws Exception{
/*// 字節流
FileInputStream in = new FileInputStream("Copy02.java");
// 通過轉換流轉換(InputStreamReader將字節流轉換成字符流。)
// in是節點流。reader是包裝流。
InputStreamReader reader = new InputStreamReader(in);
// 這個構造方法只能傳一個字符流。不能傳字節流。
// reader是節點流。br是包裝流。
BufferedReader br = new BufferedReader(reader);*/
// 合併
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("Copy02.java")));
String line = null;
while((line = br.readLine()) != null){
System.out.println(line);
}
// 關閉最外層
br.close();
}
}
代碼演示(寫入Write):
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.OutputStreamWriter;
/*
BufferedWriter:帶有緩衝的字符輸出流。
OutputStreamWriter:轉換流
*/
public class BufferedWriterTest {
public static void main(String[] args) throws Exception{
// 帶有緩衝區的字符輸出流
//BufferedWriter out = new BufferedWriter(new FileWriter("copy"));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("copy", true)));
// 開始寫。
out.write("hello world!");
out.write("\n");
out.write("hello kitty!");
// 刷新
out.flush();
// 關閉最外層
out.close();
}
}
DataOutputStream(數據字節輸出流)
java.io.DataOutputStream:數據專屬的流。
這個流可以將數據連同數據的類型一併寫入文件。
注意:這個文件不是普通文本文檔。(這個文件使用記事本打不開。)
代碼演示:
import java.io.DataOutputStream;
import java.io.FileOutputStream;
public class DataOutputStreamTest {
public static void main(String[] args) throws Exception{
// 創建數據專屬的字節輸出流
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data"));
// 寫數據
byte b = 100;
short s = 200;
int i = 300;
long l = 400L;
float f = 3.0F;
double d = 3.14;
boolean sex = false;
char c = 'a';
// 寫
dos.writeByte(b); // 把數據以及數據的類型一併寫入到文件當中。
dos.writeShort(s);
dos.writeInt(i);
dos.writeLong(l);
dos.writeFloat(f);
dos.writeDouble(d);
dos.writeBoolean(sex);
dos.writeChar(c);
// 刷新
dos.flush();
// 關閉最外層
dos.close();
}
}
DataInputStream(數據字節輸入流)
DataOutputStream寫的文件,只能使用DataInputStream去讀。並且讀的時候你需要提前知道寫入的順序。
讀的順序需要和寫的順序一致。纔可以正常取出數據。
代碼演示():
import java.io.DataInputStream;
import java.io.FileInputStream;
public class DataInputStreamTest01 {
public static void main(String[] args) throws Exception{
DataInputStream dis = new DataInputStream(new FileInputStream("data"));
// 開始讀
byte b = dis.readByte();
short s = dis.readShort();
int i = dis.readInt();
long l = dis.readLong();
float f = dis.readFloat();
double d = dis.readDouble();
boolean sex = dis.readBoolean();
char c = dis.readChar();
System.out.println(b);
System.out.println(s);
System.out.println(i + 1000);
System.out.println(l);
System.out.println(f);
System.out.println(d);
System.out.println(sex);
System.out.println(c);
dis.close();
}
}
PrintStream(標準的字節輸出流)
默認輸出到控制檯。
代碼演示
import java.io.FileOutputStream;
import java.io.PrintStream;
public class PrintStreamTest {
public static void main(String[] args) throws Exception{
// 聯合起來寫
System.out.println("hello world!");
// 分開寫
PrintStream ps = System.out;
ps.println("hello zhangsan");
ps.println("hello lisi");
ps.println("hello wangwu");
// 標準輸出流不需要手動close()關閉。
// 可以改變標準輸出流的輸出方向嗎? 可以
/*
// 這些是之前System類使用過的方法和屬性。
System.gc();
System.currentTimeMillis();
PrintStream ps2 = System.out;
System.exit(0);
System.arraycopy(....);
*/
// 標準輸出流不再指向控制檯,指向“log”文件。
PrintStream printStream = new PrintStream(new FileOutputStream("log"));
// 修改輸出方向,將輸出方向修改到"log"文件。
System.setOut(printStream);
// 再輸出
System.out.println("hello world");
System.out.println("hello kitty");
System.out.println("hello zhangsan");
}
}
class LogTest {
public static void main(String[] args) {
//測試工具類是否好用
Logger.log("調用了System類的gc()方法,建議啓動垃圾回收");
Logger.log("調用了UserService的doSome()方法");
Logger.log("用戶嘗試進行登錄,驗證失敗");
Logger.log("我非常喜歡這個記錄日誌的工具哦!");
}
}
代碼演示(日誌工具):
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Logger {
/*
記錄日誌的方法。
*/
public static void log(String msg) {
try {
// 指向一個日誌文件
PrintStream out = new PrintStream(new FileOutputStream("log.txt", true));
// 改變輸出方向
System.setOut(out);
// 日期當前時間
Date nowTime = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String strTime = sdf.format(nowTime);
System.out.println(strTime + ": " + msg);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
序列化(ObjectOutputStream,ObjectInputStream)
1、java.io.NotSerializableException:
Student對象不支持序列化!!!!
2、參與序列化和反序列化的對象,必須實現Serializable接口。
3、注意:通過源代碼發現,Serializable接口只是一個標誌接口:
public interface Serializable {
}
這個接口當中什麼代碼都沒有。
那麼它起到一個什麼作用呢?
起到標識的作用,標誌的作用,java虛擬機看到這個類實現了這個接口,可能會對這個類進行特殊待遇。
Serializable這個標誌接口是給java虛擬機參考的,java虛擬機看到這個接口之後,會爲該類自動生成
一個序列化版本號。
4、序列化版本號有什麼用呢?
java.io.InvalidClassException:
com.bjpowernode.java.bean.Student;
local class incompatible:
stream classdesc serialVersionUID = -684255398724514298(十年後),
local class serialVersionUID = -3463447116624555755(十年前)
java語言中是採用什麼機制來區分類的?
第一:首先通過類名進行比對,如果類名不一樣,肯定不是同一個類。
第二:如果類名一樣,再怎麼進行類的區別?靠序列化版本號進行區分。
小鵬編寫了一個類:com.bjpowernode.java.bean.Student implements Serializable
胡浪編寫了一個類:com.bjpowernode.java.bean.Student implements Serializable
不同的人編寫了同一個類,但“這兩個類確實不是同一個類”。這個時候序列化版本就起上作用了。
對於java虛擬機來說,java虛擬機是可以區分開這兩個類的,因爲這兩個類都實現了Serializable接口,
都有默認的序列化版本號,他們的序列化版本號不一樣。所以區分開了。(這是自動生成序列化版本號的好處)
請思考?
這種自動生成序列化版本號有什麼缺陷?
這種自動生成的序列化版本號缺點是:一旦代碼確定之後,不能進行後續的修改,
因爲只要修改,必然會重新編譯,此時會生成全新的序列化版本號,這個時候java
虛擬機會認爲這是一個全新的類。(這樣就不好了!)
最終結論:
凡是一個類實現了Serializable接口,建議給該類提供一個固定不變的序列化版本號。
這樣,以後這個類即使代碼修改了,但是版本號不變,java虛擬機會認爲是同一個類。
代碼演示(ObjectOutputStream,ObjectInputStream):
import com.bjpowernode.java.bean.Student;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class ObjectOutputStreamTest01 {
public static void main(String[] args) throws Exception{
// 創建java對象
Student s = new Student(1111, "zhangsan");
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("students"));
// 序列化對象
oos.writeObject(s);
// 刷新
oos.flush();
// 關閉
oos.close();
}
}
Student類
import java.io.Serializable;
public class Student implements Serializable {
// IDEA工具自動生成序列化版本號。
//private static final long serialVersionUID = -7998917368642754840L;
// Java虛擬機看到Serializable接口之後,會自動生成一個序列化版本號。
// 這裏沒有手動寫出來,java虛擬機會默認提供這個序列化版本號。
// 建議將序列化版本號手動的寫出來。不建議自動生成
private static final long serialVersionUID = 1L; // java虛擬機識別一個類的時候先通過類名,如果類名一致,再通過序列化版本號。
private int no;
//private String name;
// 過了很久,Student這個類源代碼改動了。
// 源代碼改動之後,需要重新編譯,編譯之後生成了全新的字節碼文件。
// 並且class文件再次運行的時候,java虛擬機生成的序列化版本號也會發生相應的改變。
private int age;
private String email;
private String address;
public Student() {
}
public Student(int no, String name) {
this.no = no;
//this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
/*public String getName() {
return name;
}*/
/*public void setName(String name) {
this.name = name;
}*/
/*@Override
public String toString() {
return "Student{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}*/
@Override
public String toString() {
return "Student{" +
"no=" + no +
", age=" + age +
", email='" + email + '\'' +
", address='" + address + '\'' +
'}';
}
}
反序列化
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class ObjectInputStreamTest01 {
public static void main(String[] args) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("students"));
// 開始反序列化,讀
Object obj = ois.readObject();
// 反序列化回來是一個學生對象,所以會調用學生對象的toString方法。
System.out.println(obj);
ois.close();
}
}
代碼演示(序列化集合):
import com.bjpowernode.java.bean.User;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
/*
一次序列化多個對象呢?
可以,可以將對象放到集合當中,序列化集合。
提示:
參與序列化的ArrayList集合以及集合中的元素User都需要實現 java.io.Serializable接口。
*/
public class ObjectOutputStreamTest02 {
public static void main(String[] args) throws Exception{
List<User> userList = new ArrayList<>();
userList.add(new User(1,"zhangsan"));
userList.add(new User(2, "lisi"));
userList.add(new User(3, "wangwu"));
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("users"));
// 序列化一個集合,這個集合對象中放了很多其他對象。
oos.writeObject(userList);
oos.flush();
oos.close();
}
}
User類
import java.io.Serializable;
public class User implements Serializable {
private int no;
// transient關鍵字表示遊離的,不參與序列化。
private transient String name; // name不參與序列化操作!
public User() {
}
public User(int no, String name) {
this.no = no;
this.name = name;
}
@Override
public String toString() {
return "User{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
反序列化集合
import com.bjpowernode.java.bean.User;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.List;
public class ObjectInputStreamTest02 {
public static void main(String[] args) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("users"));
//Object obj = ois.readObject();
//System.out.println(obj instanceof List);
List<User> userList = (List<User>)ois.readObject();
for(User user : userList){
System.out.println(user);
}
ois.close();
}
}
java.io.File類。
File
1、File類和四大家族沒有關係,所以File類不能完成文件的讀和寫。
2、File對象代表什麼?
文件和目錄路徑名的抽象表示形式。
C:\Drivers 這是一個File對象
C:\Drivers\Lan\Realtek\Readme.txt 也是File對象。
一個File對象有可能對應的是目錄,也可能是文件。
File只是一個路徑名的抽象表示形式。
3、需要掌握File類中常用的方法
代碼演示(File常用方法):
import java.io.File;
public class FileTest01 {
public static void main(String[] args) throws Exception {
// 創建一個File對象
File f1 = new File("D:\\file");
// 判斷是否存在!
System.out.println(f1.exists());
// 如果D:\file不存在,則以文件的形式創建出來
/*if(!f1.exists()) {
// 以文件形式新建
f1.createNewFile();
}*/
// 如果D:\file不存在,則以目錄的形式創建出來
/*if(!f1.exists()) {
// 以目錄的形式新建。
f1.mkdir();
}*/
// 可以創建多重目錄嗎?
File f2 = new File("D:/a/b/c/d/e/f");
/*if(!f2.exists()) {
// 多重目錄的形式新建。
f2.mkdirs();
}*/
File f3 = new File("D:\\course\\01-開課\\學習方法.txt");
// 獲取文件的父路徑
String parentPath = f3.getParent();
System.out.println(parentPath); //D:\course\01-開課
File parentFile = f3.getParentFile();
System.out.println("獲取絕對路徑:" + parentFile.getAbsolutePath());
File f4 = new File("copy");
System.out.println("絕對路徑:" + f4.getAbsolutePath()); // C:\Users\Administrator\IdeaProjects\javase\copy
}
}
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
public class FileTest02 {
public static void main(String[] args) {
File f1 = new File("D:\\course\\01-開課\\開學典禮.ppt");
// 獲取文件名
System.out.println("文件名:" + f1.getName());
// 判斷是否是一個目錄
System.out.println(f1.isDirectory()); // false
// 判斷是否是一個文件
System.out.println(f1.isFile()); // true
// 獲取文件最後一次修改時間
long haoMiao = f1.lastModified(); // 這個毫秒是從1970年到現在的總毫秒數。
// 將總毫秒數轉換成日期?????
Date time = new Date(haoMiao);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String strTime = sdf.format(time);
System.out.println(strTime);
// 獲取文件大小
System.out.println(f1.length()); //216064字節。
}
}
代碼演示(listFiles方法:獲取當前目錄所有子文件方法):
import java.io.File;
public class FileTest03 {
public static void main(String[] args) {
// File[] listFiles()
// 獲取當前目錄下所有的子文件。
File f = new File("D:\\course\\01-開課");
File[] files = f.listFiles();
// foreach
for(File file : files){
//System.out.println(file.getAbsolutePath());
System.out.println(file.getName());
}
}
}
IO流+Properties集合的聯合使用
IO+Properties的聯合應用。
非常好的一個設計理念:
以後經常改變的數據,可以單獨寫到一個文件中,使用程序動態讀取。
將來只需要修改這個文件的內容,java代碼不需要改動,不需要重新
編譯,服務器也不需要重啓。就可以拿到動態的信息。
類似於以上機制的這種文件被稱爲配置文件。
並且當配置文件中的內容格式是:
key1=value
key2=value
的時候,我們把這種配置文件叫做屬性配置文件。
java規範中有要求:屬性配置文件建議以.properties結尾,但這不是必須的。
這種以.properties結尾的文件在java中被稱爲:屬性配置文件。
其中Properties是專門存放屬性配置文件內容的一個類。
代碼演示:
import java.io.FileReader;
import java.util.Properties;
public class IoPropertiesTest01 {
public static void main(String[] args) throws Exception{
/*
Properties是一個Map集合,key和value都是String類型。
想將userinfo文件中的數據加載到Properties對象當中。
*/
// 新建一個輸入流對象
FileReader reader = new FileReader("chapter23/userinfo.properties");
// 新建一個Map集合
Properties pro = new Properties();
// 調用Properties對象的load方法將文件中的數據加載到Map集合中。
pro.load(reader); // 文件中的數據順着管道加載到Map集合中,其中等號=左邊做key,右邊做value
// 通過key來獲取value呢?
String username = pro.getProperty("username");
System.out.println(username);
String password = pro.getProperty("password");
System.out.println(password);
String data = pro.getProperty("data");
System.out.println(data);
String usernamex = pro.getProperty("usernamex");
System.out.println(usernamex);
}
}