Java基础(IO)
所有操作都在内存,不能长时间保存,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等修饰符前。