所有操作都在內存,不能長時間保存,IO主要在硬盤,可以長時間保存。
一、File類
File類被定義為文件和目錄路徑名的抽象表示形式,這是因為 File 類既可以表示文件也可以表示目錄,他們都通過對應的路徑來描述。
提供構造函數創建一個 File 類對象,則該對象就是指定文件的引用,可以通過該對象對文件操作。
常用方法:
package day8;import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;/*
* File類是文件和目錄的抽象表示形式
* 1、目錄 - 文件夾
* 2、如何創建File類的對象
* File f = new File("文件或目錄的路徑);
* 路徑的分隔符:
* 1、Windows下的路徑分隔符是“\“,但是”\“在Java中表示轉義字符,和其后的字符結合表示特定的含義
* 使用"\\"表示”\“自身;
* 2、"\"和操作系統無關的路徑分隔符
*
* */
//在桌面創建一個文本文件:
public class MyTest01 {public static void main(String[] args) {File f1 = new File("C:\\Users\\s'w'n\\Desktop\\1.txt");System.out.println(f1);//File f2 = new File("C:/Users/s'w'n/Desktop/1.txt");//System.out.println(f2);//File類的常見方法System.out.println("是否隱藏:" + f1.isHidden());System.out.println("是否可讀:" + f1.canRead());System.out.println("是否可寫:" + f1.canWrite());System.out.println("絕對路徑:" + f1.getAbsolutePath());System.out.println("文件名:" + f1.getName());System.out.println("是否是文件夾/目錄:" + f1.isDirectory());System.out.println("是否是文件:" + f1.isFile());/** lastModified() -- 返回時間戳 -- 1970-1-1 0:0:0 毫秒值* 時間戳 --> String xxxx-xx-xx xx:xx:xx* 1、時間戳 --> Date* 2、Date --> String* */long l = f1.lastModified();//時間戳 --> DateDate date = new Date(l);//Date --> StringSimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String str = sdf.format(date);System.out.println("最后修改時間:" + str);System.out.println("文件大小:" + f1.length());System.out.println("是否存在:" + f1.exists());//沒有也是可以的File f2 = new File("C:\\Users\\s'w'n\\Desktop\\2.txt");System.out.println("是否存在:" + f2.exists());File f3 = new File("C:\\Users\\s'w'n\\Desktop\\abcd1234");if(!f3.exists()){//創建文件夾f3.mkdir();}else {f3.delete();}}
}
package day8;
import java.io.File;
public class MyTest02 {/** 列出f下的所有的文件和文件夾*//*public static void listAll(File f){File[] fileArr = f.listFiles();for (File file : fileArr) {System.out.println(file);//判斷file是否是文件夾 是-繼續再遍歷if (file.isDirectory()) {listAll(file);}}}*/public static void listAll(String tag,File f){File[] fileArr = f.listFiles();for (File file : fileArr) {System.out.println(tag + file.getName());//判斷file是否是文件夾 是-繼續再遍歷if (file.isDirectory()) {listAll("\t" + tag, file);}}}public static void main(String[] args) throws Exception {File f = new File("C:/Users/s'w'n/Desktop/test123");//獲取下一級的文件和文件夾/*File[] fileArr = f.listFiles();for (File file : fileArr) {System.out.println(file);}*///listAll(f);listAll("|--",f);}
}
二、IO流概述
2.1、IO流_什么是IO流
I/O是Input/Output的縮寫,I/O技術是非常實用的技術,用于處理設備之間的數據傳輸。如讀/寫文件,網絡通訊等。
IO流是一組有序的,有起點和終點的數據集合,是對數據傳輸的總稱和抽象。
IO作用:1人機交互,2文件數據讀取寫入,數據持久化保存。
IO流的源和目的地:
內存
控制臺
磁盤文件
網絡端點
關于Input 和 Output:
Input讀取外部數據(磁盤、光盤等存儲設備的數據)到程序(內存)中;
Output將程序(內存)數據輸出到磁盤、光盤等存儲設備中。
2.2、IO流分類
1 按照處理的數據單元不同:
字節流:操作的數據單元是8位字節,InputStream、OutputStream。二進制文件(聲音、圖片、視頻)、文本文件;
字符流:操作的數據單元是16位字符,Reader、Writer,通常用于處理文本文件。
2 按照數據流向不同:
輸入流:只能從中讀取數據,而不能向其寫入數據。InputStream、Reader;
輸出流:只能向其寫入數據,而不能從中讀取數據。OutputStream、Writer;
輸入、輸出都是從內存的角度進行劃分,內存-->硬盤,輸出流;硬盤-->內存,輸入流
2.3、IO流處理的流程。
1、打開流
2、在流中數據傳輸(輸入/輸出 讀/寫)
3、關閉流
在程序中打開的文件 IO 資源不屬于內存里的資源,垃圾回收機制無法回收該資源,所以應該顯式關閉文件 IO 資源。
三、字節流和字符流
操作的數據單元是8位字節,主要涉及兩個抽象類:InputStream、OutputStream.
3.1、InputStream
只有read方法
package day8;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;public class MyTest03 {/** read()* read(byte[] b);* read(byte[] b,int offset,int length)* 共同點:* 1、都是從輸入流讀取數據* 2、讀取到文件的末尾,都返回-1* 不同點:* 1、read()返回每次讀取到的那一個字節* 2、read(byte[] b)返回讀取到的字節數量,將讀取到的數據放在了b這個數組中* read(byte[], 0, b.length);* 3、read(byte[] b,int offset,int length)返回讀取到的字節數量,* 將讀取到的數據放在了b這個數組中(從數組的第offset這個位置開始存放),讀取length個字節* */public static void main(String[] args) throws IOException {//打開流/創建流FileInputStream in = new FileInputStream("C:/Users/s'w'n/Desktop/1.txt");byte[] arr = new byte[1024]; //保存讀取到數據的數組/緩沖區int len = 0;//表示每次讀取到的數據的長度/字節數 返回值:每次運行讀取到的字節數可能比(1024小) 即小于arr.length(),要用len//使用IO流進行讀寫操作while(true){/** 讀取數據到arr數組中,將讀取到的內容從0號位置開始存放,最多讀取arr.length()個字節*/len = in.read(arr,0,arr.length);if(len == -1){break;}//byte[] --> String//!!!要用lenString s = new String(arr,0,len);System.out.print(s);}//關閉流in.close();}
}
package day8;import java.io.FileInputStream;
//只有一個參數的,只能傳數組
public class MyTest04 {public static void main(String[] args) throws Exception {//打開流/創建流FileInputStream in = new FileInputStream("C:/Users/s'w'n/Desktop/1.txt");byte[] arr = new byte[1024]; //保存讀取到數據的數組/緩沖區int len = 0;//表示每次讀取到的數據的長度/字節數 返回值:每次運行讀取到的字節數可能比(1024小) 即小于arr.length(),要用len//使用IO流進行讀寫操作while(true){len = in.read(arr);if(len == -1){break;}//byte[] --> StringString s = new String(arr, 0, len);System.out.print(s);}//關閉流in.close();}
}
3.2、OutputStream
write()方法 字節寫入輸出流
flush()方法 刷新此輸出流并強制寫出所有緩沖的輸出字節,調用此方法指示應將這些字節立即寫入它們預期的目標;
close()方法 關閉此輸出流并釋放與該流關聯的所有系統資源。
package day8;import java.io.FileInputStream;
import java.io.FileOutputStream;public class MyTest04 {public static void main(String[] args) throws Exception {/** OutputStream* write* */FileOutputStream out = new FileOutputStream("C:/Users/s'w'n/Desktop/2.txt");//String --> 文件//String --> byteString str = "哈哈哈哈哈哈哈哈哈啊哈哈";//變內容會把上一次的覆蓋byte[] arr = str.getBytes();//將數組中的內容寫入文件out.write(arr, 0, arr.length);//關閉流out.flush();}
}
package day8;import java.io.FileOutputStream;public class MyTest05 {public static void main(String[] args) throws Exception {/** OutputStream* write* 第二個參數如果為true,是以追加的方式寫入數據 log日志* */FileOutputStream out = new FileOutputStream("C:/Users/s'w'n/Desktop/2.txt",true);//String --> 文件//String --> byteString str = "hello world";//變內容會把上一次的覆蓋byte[] arr = str.getBytes();//將數組中的內容寫入文件out.write(arr, 0, arr.length);//關閉流out.flush();}
}
3.3、文件拷貝
字節流?
package day8;import java.io.FileInputStream;
import java.io.FileOutputStream;public class MyTest06 {public static void main(String[] args) throws Exception {//創建流FileInputStream in = new FileInputStream("C:/Users/s'w'n/Desktop/1.txt");FileOutputStream out = new FileOutputStream("C:/Users/s'w'n/Desktop/1.txt");int len = 0;//接收讀取到的長度byte[] arr = new byte[1024];//接收讀取到的數據//讀取寫入while (true){len = in.read(arr);if (len == -1){break;}out.write(arr, 0, len);}//關閉流 -- 多個流 -- 后打開的先關閉out.close();in.close();}
}
字符流?
package day11;import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;/*
* 拷貝2*/
public class MyTest01 {public static void main(String[] args) throws Exception {FileReader in = new FileReader("C:/Users/s'w'n/Desktop/1.txt");FileWriter out = new FileWriter("C:/Users/s'w'n/Desktop/3.txt");int len = 0; //讀取到的字符個數char[] arr = new char[1024];while(true){len = in.read(arr);if(len == -1){break;}out.write(arr, 0, len);}out.close();in.close();}
}
3.4、圖片拷貝
package day8;import java.io.FileInputStream;
import java.io.FileOutputStream;//復制圖片
public class MyTest07 {public static void main(String[] args) throws Exception {//創建流FileInputStream in = new FileInputStream("C:/Users/s'w'n/Desktop/屏幕截圖 2025-03-17 192045.png");FileOutputStream out = new FileOutputStream("C:/Users/s'w'n/Desktop/2.png");int len = 0;byte[] arr = new byte[1024];//讀取寫入while (true){len = in.read(arr);if(len == -1){break;}out.write(arr, 0, len);}//關閉流out.close();in.close();}
}
四、緩沖流
緩沖流:
把頻繁的和硬盤的交互改為了和內存的交互。
Java API提供的帶緩沖功能的流類,在使用這些流類時,會創建一個內部緩沖區數組,默認使用8192個字節或字符的緩沖區。
緩沖流要套接在相應的節點流之上。
根據數據操作單位可以把緩沖流分為:
BufferedInputStream和BufferedOutputStream;
BufferedReader和BufferedWriter。
關閉流的順序和打開流的順序相反。只需關閉最外層流即可。
?4.1 文件拷貝
package day11;import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;/*
緩沖流
* 拷貝3*/
public class MyTest01 {public static void main(String[] args) throws Exception {BufferedReader in = new BufferedReader(new FileReader("C:/Users/s'w'n/Desktop/1.txt"));BufferedWriter out = new BufferedWriter(new FileWriter("C:/Users/s'w'n/Desktop/4.txt"));int len = 0;char[] arr = new char[1024];while(true) {len = in.read(arr);if(len == -1) {break;}out.write(arr, 0, len);}in.close();out.close();}
}
4.2 按行讀取
package day11;import java.io.BufferedReader;
import java.io.FileReader;/*
* 緩沖流
* 按行讀取
* */
public class MyTest02 {public static void main(String[] args) throws Exception{BufferedReader in = new BufferedReader(new FileReader("C:/Users/s'w'n/Desktop/1.txt"));String line = null;while(true){//按行讀取,讀取到末尾返回nullline = in.readLine();if(line == null) {break;}System.out.println(line);}}
}
五、通訊錄管理系統_IO版_讀取
相關源碼及其注釋如下:
package day4;
/*
有需求 -- 才去設計類
自定義表示通訊錄單條信息的類*/
public class PhoneBookItem {//屬性private String name;//姓名private String gender;//性別private int age;//年齡private String phone;//電話private String qq;//QQprivate String addr;//地址//構造方法public PhoneBookItem() {}public PhoneBookItem(String name, String gender, int age, String phone, String qq, String addr) {this.name = name;this.gender = gender;this.age = age;this.phone = phone;this.qq = qq;this.addr = addr;}//方法public String getName() {return name;}public void setName(String name) {this.name = name;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getPhone() {return phone;}public void setPhone(String phone) {this.phone = phone;}public String getQq() {return qq;}public void setQq(String qq) {this.qq = qq;}public String getAddr() {return addr;}public void setAddr(String addr) {this.addr = addr;}/** 返回對象的字符串表示形式 -- 返回一個字符串表示對象的信息* 打印對象時,系統會自動調用這個方法* */public String toString() {return "姓名:" + this.name+ " 性別:" + this.gender+ " 年齡:" + this.age+ " 電話:" + this.phone+ " QQ:" + this.qq+ " 地址:" + this.addr;}
}
package day4;
import java.io.*;
import java.util.ArrayList;/*
對通訊錄的信息管理的類分層 - 特定的模塊特定的事情*/
public class PhoneBookManager{//屬性//ArrayListprivate ArrayList<PhoneBookItem> arr = new ArrayList<>();/*IO 部分* 啟動 - 讀取文件 - 讀取文件中的通訊錄信息到ArrayList中* 退出 - 保存文件 - 將ArrayList中的數據寫入到文件** 文件的格式* 1、每個通訊錄的信息單獨占據一行* 2、每一行保存屬性的順序要一致 zs|sd|11|22|33* 3、字符串分割 String[] arr = split("\|");* *///讀取文件 -- 讀取文件中的通訊錄信息到ArrayList中public void init() throws Exception {BufferedReader in = new BufferedReader(new FileReader("C:/Users/s'w'n/Desktop/phonebookconfig.txt"));String line = null;while (true){line = in.readLine();if (line == null){break;}//line --> PhoneBookItem --> ArrayListString[] strArr = line.split("\\|");PhoneBookItem item = new PhoneBookItem(strArr[0], strArr[1], Integer.parseInt(strArr[2]), strArr[3], strArr[4],strArr[5]);}in.close();}//保存文件public void save() throws Exception {//ArrayList --> String --> 文件BufferedWriter out = new BufferedWriter(new FileWriter("C:/Users/s'w/Desktop/phonebookconfig.txt"));for (PhoneBookItem item : arr) {//PhoneBookItem --> 特定格式的字符串StringBuffer sbf = new StringBuffer();sbf.append(item.getName());sbf.append("|");sbf.append(item.getGender());sbf.append("|");sbf.append(item.getAge());sbf.append("|");sbf.append(item.getPhone());sbf.append("|");sbf.append(item.getQq());sbf.append("|");sbf.append(item.getAddr());String s = sbf.toString();out.write(s);//換行out.newLine();}out.close();}//添加 -- 尋找對象數組中第一個為null的位置并賦值public boolean add(PhoneBookItem item) {return arr.add(item);}//刪除public boolean del(String name) {for (int i = 0; i < arr.size(); i++) {if(arr.get(i) != null && arr.get(i).getName().equals(name)) {arr.remove(i);return true;}}return false;}/** name - 被修改項的名字* newItem - 修改之后的所有的信息*///修改public boolean update(String name, PhoneBookItem newItem) {for (int i = 0; i < arr.size(); i++) {if(arr.get(i) != null && arr.get(i).getName().equals(name)) {arr.set(i, newItem);return true;}}return false;}//查詢所有public ArrayList<PhoneBookItem> findAll(){return this.arr;}//根據姓名查詢public PhoneBookItem findByName(String name) {for (int i = 0; i <arr.size() ; i++) {if(arr.get(i) != null && arr.get(i).getName().equals(name)) {return arr.get(i);//要是找到值為null的,null.getName()...程序會崩潰結束}}return null;}}
package day4;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Scanner;
/*
負責顯示/和用戶交互*/
public class UI {public static void help(){System.out.println("-----------------------------通訊錄管理系統-------------------------------");System.out.println("1.添加 2.刪除 3.修改 4.查詢所有 5.根據姓名查詢 0.退出");System.out.println("-----------------------------通訊錄管理系統-------------------------------");System.out.println("請選擇業務:");}public static void main(String[] args) throws Exception {Scanner sc = new Scanner(System.in);//創建管理類對象PhoneBookManager phoneBookManager = new PhoneBookManager();phoneBookManager.init();while(true) {//打印幫助菜單help();//讀取用戶輸入int choose = sc.nextInt();switch(choose) {case 1://添加//輸入姓名System.out.println("-----------添加通訊錄----------");System.out.print("姓名:");//如果是println的話就自動換行了String nameAdd = sc.next();/*根據姓名查詢是否已經存在1、是 - 提示已經存在2、否 - 允許繼續輸入*/if (phoneBookManager.findByName(nameAdd) != null) {System.out.println("用戶已經存在");break;}System.out.print("性別");String genderAdd = sc.next();System.out.print("年齡");int ageAdd = sc.nextInt();System.out.print("電話");String phoneAdd = sc.next();System.out.print("QQ");String qqAdd = sc.next();System.out.print("地址");String addrAdd = sc.next();//根據用戶的輸入創建單條信息的對象PhoneBookItem phoneBookItem = new PhoneBookItem(nameAdd, genderAdd, ageAdd, phoneAdd, qqAdd, addrAdd);//添加if(phoneBookManager.add(phoneBookItem)) {//打印被添加的信息System.out.println(phoneBookItem);//上面一行打印時會自動調用 System.out.println(phoneBookItem.toString());System.out.println("添加成功");}else{System.out.println("通訊錄空間不足無法添加");}break;case 2://刪除System.out.println("----------刪除通訊錄----------");System.out.println("請輸入要刪除的姓名:");String nameDel = sc.next();if(phoneBookManager.del(nameDel)) {System.out.println("刪除成功");}else{System.out.println("系統中不存在該姓名,無法刪除");}break;case 3://修改System.out.println("----------修改通訊錄----------");/** 1、判斷被修改的項是否存在* 2、判斷修改之后的名字與其他項的名字是否重復* */System.out.println("請輸入被修改項的姓名:");String oldName = sc.next();//判斷被修改的項是否存在PhoneBookItem oldItem = phoneBookManager.findByName(oldName);if(oldItem == null) {System.out.println("被修改項不存在無法修改");break;}System.out.println("請根據提示輸入修改之后的信息:");System.out.println("姓名:");String newName = sc.next();/** 修改之后的名字不能和其他項的名字重復* |* 如何判斷修改之后的名字是其他項的名字* |* 根據修改之后的名字查詢,如果能夠查詢出信息并且和被修改項的地址不同,就能說明是其他項 -- 不允許修改* *///根據修改之后的名字查詢PhoneBookItem newItem = phoneBookManager.findByName(newName);if(newItem != null && oldItem != newItem) {System.out.println("修改之后的姓名和其他項重名,不允許修改");break;}System.out.print("性別");String newGender = sc.next();System.out.print("年齡");int newAge = sc.nextInt();System.out.print("電話");String newPhone = sc.next();System.out.print("QQ");String newQq = sc.next();System.out.print("地址");String newAddr = sc.next();PhoneBookItem updateItem = new PhoneBookItem(newName, newGender, newAge, newPhone, newQq, newAddr);phoneBookManager.update(oldName, updateItem);System.out.println("修改成功");break;case 4://查詢所有System.out.println("----------查詢所有通訊錄----------");ArrayList<PhoneBookItem> arr = phoneBookManager.findAll();for (PhoneBookItem item : arr) {if(item != null) {System.out.println(item);}}break;case 5:System.out.println("----------根據姓名查詢通訊錄----------");System.out.print("姓名:");String nameSearch = sc.next();PhoneBookItem bookItemSearch = phoneBookManager.findByName(nameSearch);if(bookItemSearch == null) {System.out.println("通訊錄中不存在該姓名對應的信息");break;}else{System.out.println(bookItemSearch);}break;case 0://將內存中的數據寫入到文件phoneBookManager.save();//退出System.exit(0);default:System.out.println("請根據幫助菜單選擇功能");}}}
}
六、序列化和反序列化
6.1、簡介
序列化是將對象的狀態信息轉換為可以存儲或傳輸的形式的過程。在Java中,序列化主要用于對象的持久化存儲和網絡傳輸。反序列化則是將這些存儲或傳輸的格式轉換回對象的過程
持久化傳輸:將對象存儲到文件或數據庫中。
網絡傳輸:通過網絡發送對象,接收端可以恢復對象。
分布式系統:在分布式系統中,對象的序列化和反序列化是實現遠程通信的基礎。
6.2、序列化
在Java中,序列化是通過實現java.io.Serializable接口來實現的。
步驟:
1、實現Serializable接口:讓你的類實現此接口。
2、定義一個統一的序列化ID:為了防止序列化版本沖突,通常在類中定義一個名為serialVersionUID的靜態常量。
3、使用ObjectOutputStream:通過ObjectOutputStream將對象寫入輸出流。
6.3 序列化操作_寫入文件
import java.io.Serializable;public class Student implements Serializable {private static final long serialVersionUID = 1L;private String name; //姓名private Integer age;private String gender;private String username; //用戶名private String password;public Student() {}public Student(String name, Integer age, String gender, String username, String password) {this.name = name;this.age = age;this.gender = gender;this.username = username;this.password = password;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", gender='" + gender + '\'' +", username='" + username + '\'' +", password='" + password + '\'' +'}';}
}
package day8;import java.io.FileOutputStream;
import java.io.ObjectOutputStream;public class MyTest07 {public static void main(String[] args) throws Exception {/** 創建兩個學生對象* 通過序列化的方式保存到文件* */Student s1 = new Student("zs", 10, "男", "zs", "123");Student s2 = new Student("ls", 10, "男", "zs", "123");ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("C:/Users/s'w'n/Desktop/1.txt"));out.writeObject(s1);out.writeObject(s2);out.close();}
}
6.4、反序列化
要是讀到末尾了再讀程序就會崩潰,異常。
package day8;import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;public class MyTest06 {public static void main(String[] args) throws Exception {//創建流ObjectInputStream in = new ObjectInputStream(new FileInputStream("C:/Users/s'w'n/Desktop/1.txt"));//讀取Object o1 = in.readObject();Object o2 = in.readObject();System.out.println(o1);System.out.println(o2);//關閉流in.close();}
}
6.5、transient關鍵字
transient關鍵字是一個修飾符,用于標記類中的某些字段,這些字段在序列化過程中不會被序列化。
使用場景:
敏感信息:比如密碼、令牌等敏感信息,你可能不希望它們被序列化。
資源消耗大的字段:比如文件句柄、網絡連接等。這些資源在序列化時可能不需要保存。
臨時數據:一些只在運行時有用,而不需要持久化存儲的數據。
可以標在private等修飾符前。