IO

文章目录

  • IO
    • 简介
    • File
      • 分隔符、大小写
      • 常用方法
      • 练习:搜索、删除、剪切
    • 字符集(Character Set)
    • 字符编码(Character Encoding)
      • 字符编码比较
      • 乱码
    • 字节流(Byte Streams)
      • FileOutStream
      • FileInputStream
    • 练习
      • 将内存中的数据写入文件
      • 从文件读取数据到内存
      • 复制
    • try、with、resources
    • 字符流(Character Streams)
      • 字符流结构预览
      • FileWriter
      • FileReader
    • 练习
      • 将文本文件的内容逐个字符打印出来
    • 缓冲流(Buffered Streams)
      • 使用、`close`、`flush`
    • 练习
      • AI代码
      • 用缓冲流逐个打印字符
      • 转换文本文件编码
    • Scanner
    • 格式化输出
      • `PrintStream`
      • `PrintWriter`
    • 数据流(Data Streams)
    • 对象流(Object Streams)、序列化(Serialization)
      • 对象的序列化和反序列化
      • serialVersionUID
      • transient
      • 序列化与反序列化
      • `ObjectOutputStream` – 序列化
      • `ObjectInputStream` – 反序列化
    • Files工具类

简介

I/O流 全称是 Input/Output Stream,译为“输入/输出流”。

不管什么数据,本质上都是二进制而已,所谓 I/O流,就是将这些 01 二进制进行传输

I/O流的常用类型都在 java.io 包中:

类型 输入流 输出流
字节流(Byte Streams) InputStream OutputStream
字符流(Character Streams) Reader Writer
缓冲流(Buffered Streams) BufferedInputStreamBufferedReader BufferedOutputStreamBufferedWriter
数据流(Data Streams) DataInputStream DataOutputStream
对象流(Object Streams) ObjectInputStream ObjectOutputStream

File

分隔符、大小写

一个 File 对象就代表一个文件目录(文件夹)

// file1、file2都能访问test.txt文件
File file1 = new File("F:\\Files\\Texts\\test.text");
File file2 = new File("F:/Files/Texts/test.text");

名字分隔符(name separator):File.separator

  • 在 UNIX、Linux、Mac 系统中:正斜杠 /
  • 在 Windows 系统中:反斜杠 \

路径分隔符(path separator):File.pathSeparator

  • 在 UNIX、Linux、Mac 系统中:冒号:
  • 在 Windows 系统中:分号;

是否区分大小写

  • 在 Windows、Mac 系统中:文件名、目录名不区分大小写
  • 在 UNIX、Linux 系统中:文件名、目录名区分大小写

常用方法

String getName() //获取文件或目录的名称
String getParent() //获取父路径
File getParentFile() //获取父文件
String getPath() //获取路径
String getAbsolutePath() //获取绝对路径
File getAbsoluteFile() //获取绝对路径形式的文件
long lastModified() //最后一次修改的时间
long lenght() //文件的大小(不支持目录)
  • 获取文件或目录的名称:String getName()
  • 获取父路径:String getParent()
  • 获取父文件:File getParentFile()
  • 获取路径:String getPath()
  • 获取绝对路径:String getAbsolutePath()
  • 获取绝对路径形式的文件:File getAbsoluteFile()
  • 最后一次修改的时间:long lastModified()
  • 文件的大小(不支持目录):long length()
boolean isAbsolute()
boolean exists()
boolean isDirectory()
boolean isFile()
boolean isHidden()
boolean canRead()
boolean canWrite()
  • 是否绝对路径:boolean isAbsolute()
  • 文件或目录是否存在:boolean exists()
  • 是否目录(文件夹):boolean isDirectory()
  • 是否文件:boolean isFile()
  • 是否隐藏:boolean isHidden()
  • 是否可读:boolean canRead()
  • 是否可写:boolean canwrite()
String[] list() //获取当前目录下所有文件、目录的名称
String[] list(FilenameFilter filter)
File[] listFiles() //获取当前目录下所有文件、目录
File[] listFiles(FilenameFilter filter)
File[] listFiles(FileFilter filter)
  • 获取当前目录下所有文件、目录的名称
    String[] list()
    String[] list(FilenameFilter filter)
  • 获取当前目录下所有文件、目录
    File[] listFiles()
    File[] listFiles(FilenameFilter filter)
    File[] listFiles(FileFilter filter)
boolean createNewFile() //创建文件(不会覆盖旧文件)
boolean delete() //删除文件或空目录(不经过回收站)
boolean mkdir() //创建当前目录
boolean mkdirs() //创建当前目录(包括不存在的父目录)
boolean renameTo(File dest) //剪切到新路径
  • 创建文件(不会覆盖旧文件):boolean createNewFile()
  • 删除文件或空目录(不经过回收站,慎重!):boolean delete()
  • 创建当前目录:boolean mkdir()
  • 创建当前目录(包括不存在的父目录):boolean mkdirs()
  • 剪切到新路径(可以改名,很有用):boolean renameTo(File dest)
boolean setLastModified(long time)
boolean setReadOnly()
boolean setWritable(boolean writable, boolean ownerOnly)
boolean setWritable(boolean writable)
boolean setReadable(boolean readable, boolean ownerOnly)
boolean setReadable(boolean readable)
  • 设置修改日期:boolean setLastModified(long time)
  • 设置只读权限:boolean setReadOnly()
  • 设置权限:
    boolean setWritable(boolean writable, boolean ownerOnly)
    boolean setWritable(boolean writable)
  • 设置权限:
    boolean setReadable(boolean readable, boolean ownerOnly)
    boolean setReadable(boolean readable)

练习:搜索、删除、剪切

import java.io.File;
import java.util.function.Consumer;public class Files {/*** 搜索目录下的所有文件* @param dir 目录* @param operation 执行的操作*/public static void search(File dir, Consumer<File> operation){if(dir == null || operation == null) return;// 目录不存在 或者 传入的是文件, 不执行操作if(!dir.exists() || dir.isFile()) return; File[] subfiles = dir.listFiles();for (File sf : subfiles) {operation.accept(sf);if(sf.isFile()) continue; // 如果是文件,跳过,进行后面的搜索search(sf, operation); // 如果是文件夹, 递归搜索该文件夹}}/*** 删除文件夹* @param file*/public static void delete(File file){// 传入null 或者  文件夹不存在, 不执行操作if(file == null || !file.exists()) return;clean(file);file.delete();}/*** 清空文件夹中文件* @param dir*/public static void clean(File dir){// 如果文件夹不存在 或者 传入的是文件, 不执行操作if(dir == null || !dir.exists() || dir.isFile()) return;File[] subfiles = dir.listFiles();for (File sf : subfiles) {delete(sf);}}/*** 剪切文件或文件夹到目标路径*/public static void move(File src, File dest){if(src == null || dest == null) return;// 如果源File不存在 或者 目标File 已经存在, 不执行操作if(!src.exists() || dest.exists()) return;mkparents(dest);src.renameTo(dest);}/*** 创建父路径*/private static void mkparents(File file){File parent = file.getParentFile();if(parent == null) return;parent.mkdirs();}
}
import java.io.File;
import java.util.function.Consumer;public class Main {public static void main(String[] args) throws Exception {// file1、file2都能访问test.txt文件// File file1 = new File("F:\\");File file = new File("F:/");Files.search(file, (sub) -> {if (sub.isFile()) { // 是文件System.out.println("\t文件:" + sub.getName());} else {System.out.println("目录:" + sub.getName());}});}
}

这个测试类可以像下面这样读取整个F盘的文件及文件夹

【读取文件夹】待上传

字符集(Character Set)

在计算机里面

  • 一个中文汉字是一个字符
  • 一个英文字母是一个字符
  • 一个阿拉伯数字是一个字符
  • 一个标点符号是一个字符

字符集(简称 Charset):由字符组成的集合字符集(Character Set)

常见的字符集有:

  • ASCII:128个字符(包括了英文字母大小写、阿拉伯数字等)
  • ISO-8859-1:支持欧洲的部分语言文字,在有些环境也叫 Latin-1
  • GB2312:支持中文(包括了 6763 个汉字)
  • ASCII:128个字符(包括了英文字母大小写、阿拉伯数字等)
  • ISO-8859-1:支持欧洲的部分语言文字,在有些环境也叫 Latin-1
  • GB2312:支持中文(包括了 6763 个汉字)

ISO-8859-1GB2312BIG5GBKGB18030Unicode 中都已经包括了 ASCII 中的所有字符

字符编码(Character Encoding)

每个字符集都有对应的字符编码,它决定了每个字符如何转成二进制存储在计算机中。

  • 一个字节 = 8位二进制,即 1Byte = 8bit
  • 关于一个字符等于多少字节,是不确定的,主要看编码方式

ASCII:单字节编码,编码范围是 0x00 ~ 0x7F (0 ~ 127)

ISO-8859-1:单字节编码,编码范围是 0x00 ~ 0xFF

  • 0x00 ~ 0x7F 和 ASCII 一致,0x80 ~ 0x9F 是控制字符,0xA0 ~ 0xFF 是文字符号

GB2312BIG5GBK:采用双字节表示一个汉字

GB18030:采用单字节、双字节、四字节表示一个字符

Unicode:有 UnicodeUTF-8UTF-16UTF-32 等编码,最常用的是 UTF-8 编码

  • UTF-8 采用单字节、双字节、三字节、四字节表示一个字符

字符编码比较

String str = "MJ码哥";
str.getBytes("ASCII");          // [ 77, 74, 63, 63 ]
str.getBytes("ISO-8859-1");     // [ 77, 74, 63, 63 ]
str.getBytes("GB2312");         // [ 77, 74, -62, -21, -72, -25 ]
str.getBytes("BIG5");           // [ 77, 74, 63, -83, -12 ]
str.getBytes("GBK");            // [ 77, 74, -62, -21, -72, -25 ]
str.getBytes("GB18030");        // [ 77, 74, -62, -21, -72, -25 ]
str.getBytes("UTF-8");          // [ 77, 74, -25, -96, -127, -27, -109, -91 ]

如果 String.getBytes 方法没有传参,就使用 JVM 的默认字符编码,一般跟随 main 方法所在文件的字符编码

可以通过 Charset.defaultCharset 方法获取 JVM 的默认字符编码

  • Charset 类的全名是 java.nio.charset.Charset

乱码

计算机中真正的代码其实是二进制,所以转到它是编码,它转别的就是解码

  • 【字符串】转为【二进制】的过程:编码(Encode)
  • 【二进制】转为【字符串】的过程:解码(Decode)

编码、解码时使用的字符编码必须要保持一致,否则会造成乱码

String str1 = "Java不难";// UTF-8编码为二进制: [74, 97, 118, 97, -28, -72, -115, -23, -102, -66]
byte[] bytes = str1.getBytes("UTF-8");
// GB18030解码: Java涓嶉毦
String str2 = new String(bytes, "GB18030");
// UTF-8解码: Java不难
String str3 = new String(bytes, "UTF-8");

字节流(Byte Streams)

字节流的特点:

  • 一次只读写一个字节
  • 最终都继承自 InputSteamOutputStream

常用的是字节流有 FileInputStreamFileOutputStream

字节流结构预览:

FileOutStream

// 字节流
// true表示追加内容,并非覆盖原来内容
FileOutputStream fos = new FileOutputStream("F:/1.txt", true);
fos.write("MJ码哥".getBytes("GBK"));
// 可以写直接字节
fos.write(74); // M
fos.write(77); // J
fos.close();
MJ码哥MJ

FileInputStream

FileInputStream is = new FileInputStream("F:/1.txt");
// 读取第1个字节
int byte1 = is.read();
// 读取第2个字节
int byte2 = is.read();
is.close();
InputStream is = new FileInputStream("F:/2.txt");
byte[] bytes = new byte[1024];
// read返回实际读取的字节数
int len = is.read(bytes);
is.close();

练习

将内存中的数据写入文件

/*** 将内存中的数据写入文件*/
public static void write(byte[] bytes, File file){if(bytes == null || file ==null) return;if(file.exists()) return; // 如果文件已经存在,直接结束,不覆盖mkparents(file);try(FileOutputStream fos = new FileOutputStream(file)){fos.write(bytes);} catch (IOException e) {e.printStackTrace();}
}

从文件读取数据到内存

/*** 将数据从文件读入到内存中*/
public static byte[] read(File file){if(file == null || !file.exists()) return null;if(file.isDirectory()) return null; // 不读取文件夹try(FileInputStream fis = new FileInputStream(file)){byte[] bytes = new byte[(int)file.length()];fis.read(bytes);return bytes;} catch (IOException e) {e.printStackTrace();return null;}
};

复制

/*** 复制(只限于文件)*/
public static void copy(File src, File dest){if(src == null || dest == null) return;if(!src.exists() || dest.exists()) return;if(src.isDirectory()) return; // 不拷贝文件夹mkparents(dest);try(FileInputStream fis = new FileInputStream(src);FileOutputStream fos = new FileOutputStream(dest);){byte[] bytes = new byte[8192];int len;while((len = fis.read(bytes)) != -1){fos.write(bytes, 0, len);}} catch (IOException e) {e.printStackTrace();}
}

try、with、resources

从Java7开始推出的try-with-resources语句(可以没有catchfinally

try(资源1; 资源2; ...) {} catch (Exception e) {} finally {}

可以在try后面的小括号中声明一个或多个资源(resource)

  • 实现了 java.lang.AutoCloseable 接口的实例,都可以称之为是资源

不管try中的语句是正常还是意外结束

  • 最终都会自动按顺序调用每一个资源的 close 方法(close 方法的调用顺序与资源的声明顺序相反
  • 调用完所有资源的 close 方法后,再执行 finally 中的语句

字符流(Character Streams)

字符流的特点:

  • 一次只读写一个字符
  • 最终都继承自 ReaderWriter

常用的是字符流有 FileReaderFileWriter

  • 注意:这 2 个类只适合文本文件,比如 .txt.java 等这类文件

字符流结构预览

FileWriter

Writer writer = new FileWriter("F:/2.txt");
writer.write('M');
writer.write('J');
writer.write('码');
writer.write('哥');
writer.close();
Writer writer = new FileWriter("F:/2.txt");
writer.write("MJ");
writer.write("码哥".toCharArray());
writer.close();

FileReader

Reader reader = new FileReader("F:/2.txt");
// 读取第1个字符
int c1 = reader.read();
// 读取第2个字符
int c2 = reader.read();
reader.close();
Reader reader = new FileReader("F:/2.txt");
char[] chars = new char[1024];
// read方法返回实际读取的字符数
int len = reader.read(chars);
reader.close();

练习

将文本文件的内容逐个字符打印出来

这个需求不太适合使用字节流

/*** 练习 – 将文本文件的内容逐个字符打印出来*/
public static void writeChar(File file){if(file == null || file.isDirectory()) return;try(FileReader reader = new FileReader(file)) {int c;while((c = reader.read()) != -1){System.out.print((char)c);Thread.sleep(100);}} catch (IOException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();}
}

缓冲流(Buffered Streams)

前面的字节流、字符流,都是无缓冲的 I/O 流每个读写操作均由底层操作系统直接处理

  • 每个读写操作通常会触发磁盘访问,因此大量的读写操作,可能会使程序的效率大大降低

为了减少读写操作带来的开销,Java 实现了缓冲的 I/O 流

  • 缓冲输入流
    从缓冲区读取数据,并且只有当缓冲区为空时才调用本地的输入 API(Native Input API)
  • 缓冲输出流
    将数据写入缓冲区,并且只有当缓冲区已满时才调用本地的输出 API(Native Output API)

上述 4 个缓冲流的默认缓冲区大小8192 字节(8KB),可以通过构造方法传参设置缓冲区大小

使用、closeflush

缓冲流的常见使用方式:将无缓冲流传递给缓冲流的构造方法将无缓冲流包装成缓冲流

  • 如果把无缓冲流比作是一个无装备的士兵,那么缓冲流就是一个有强力装备的士兵
File file = new File("F:/1.txt");
InputStream is = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(is, 16384);
bis.close();
File file = new File("F:/1.txt");
BufferedWriter writer = new BufferedWriter(new FileWriter(file));
writer.write("111");
writer.newLine();
writer.write("222");
writer.close();

只需要执行缓冲流的 close 方法,不需要执行缓冲流内部包装的无缓冲流的 close 方法

调用缓冲输出流的 flush 方法,会强制调用本地的输出 API,将缓冲区的数据真正写入到文件中

  • 缓冲输出流的 close 方法内部会调用一次 flush 方法

练习

AI代码

  • 标准输入流(Standard InputStream):控制台的输入、键盘的输入,System.in

  • 标准输出流(Standard OutputStream):控制台的输出、屏幕的输出,System.out

  • 利用 InputStreamReader 可以实现 【字节输入流】转【字符输入流】

  • 同理,利用 OutputStreamWriter 可以实现 【字节输出流】转【字符输出流】

// InputStream -> InputStreamReader -> BufferedReader
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String line;
while((line = reader.readLine()) != null){line = line.replace("你", "朕");line = line.replace("吗", "");line = line.replace("么", "");line = line.replace("?", "!");line = line.replace("?", "!");System.out.println("\t" + line);
}
reader.close();

用缓冲流逐个打印字符

/*** 练习 - 缓冲流逐行打印字符串*/
public static void writeLine(File file){try(BufferedReader reader = new BufferedReader(new FileReader(file))){String line;while((line = reader.readLine()) != null){System.out.println(line);Thread.sleep(100);}} catch (IOException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();}
}

转换文本文件编码

各种流的操作很灵活,方法特别多。

方法1:

File file = new File("F:/gb2312.txt");
File file2 = new File("F:/utf-8.txt");
try(BufferedReader reader = new BufferedReader(// 利用 InputStreamReader 将字节流转字符流new InputStreamReader(new FileInputStream(file), "GB2312"));BufferedWriter writer = new BufferedWriter(// 利用 InputStreamReader 将字节流转字符流new OutputStreamWriter(new FileOutputStream(file2), "UTF-8"));
){String line;while((line = reader.readLine()) != null){writer.write(line);writer.newLine();}
}

方法2:

File file = new File("F:/gb2312.txt");
File file2 = new File("F:/utf-8.txt");
try(BufferedReader reader = new BufferedReader(new InputStreamReader(// 字节流转字符流new FileInputStream(file), "GB2312"));BufferedOutputStream writer = new BufferedOutputStream(new FileOutputStream(file2));
){String line;while((line = reader.readLine()) != null){writer.write((line + "\n").getBytes("utf-8"));}
}

方法3:

File file = new File("F:/gb2312.txt");
File file2 = new File("F:/utf-8.txt");
try(BufferedInputStream reader = new BufferedInputStream(new FileInputStream(file));BufferedOutputStream writer = new BufferedOutputStream(new FileOutputStream(file2));
){int len;String string;byte[] bytes = new byte[4196];while((len = reader.read(bytes)) != -1){string = new String(bytes, "gb2312");writer.write(string.getBytes("utf-8"), 0, len);System.out.println(string);}
}

方法4:

File file = new File("F:/gb2312.txt");
File file2 = new File("F:/utf-8.txt");
try(BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "GB2312"));BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file2), "UTF-8"));
){char[] chars = new char[1024];int len;while((len = reader.read(chars)) != -1){writer.write(chars, 0, len);}
}

补充

  • 程序可以使用我们已经多次使用的包装习惯将未缓冲的流转换为缓冲的流,其中将未缓冲的对象传递给缓冲流类的构造函数
  • 在CopyCharacters实例中,你可以通过以下方式修改构造函数调用以使用缓冲的I/O
// 暗示我们 -> FileReader是未缓冲的流
// 但实际上 -> FileReader是带缓冲的
// 虽然表面上没有Buffered,但是它依然带缓冲
inputSteram = new BufferedReader(new FileReader("xanadu.txt"));
outputSteram = new BufferedWriter(new FileWriter("characteroutput.txt"));

Scanner

java.util.Scanner 是一个可以使用正则表达式解析基本类型字符串的简单文本扫描器

  • 它默认利用空白空格\制表符\行终止符)作为分隔符将输入分隔成多个 token
Scanner(InputStream source)
Scanner(Readable source)
Scanner(File source)
Scanner(String source)
Scanner sc = new Scanner("jack rose kate");
while(sc.hasNext()){System.out.println(sc.next());
}
sc.close();
jack
rose
kate
Scanner sc = new Scanner("jack 666 888 ak47");
System.out.println(sc.next()); // jack
System.out.println(sc.nextInt()); // 666
System.out.println(sc.nextDouble()); // 888.0
// 利用正则表达式分隔文本
System.out.println(sc.next("[a-z]{2}\\d{2}")); // ak47
sc.close();
jack
666
888
ak47
  • Scanner.useDelimiter 方法可以自定义分隔符
Scannersc = new Scanner("aa 1 bb 22 cc33dd");
sc.useDelimiter("\\s*\\d+\\s*");
while(sc.hasNext()){System.out.println(sc.next());
}
// aa bb cc dd
sc.close();
aa
bb
cc
dd
Scanner sc = new Scanner("aa11bb22cc");
sc.useDelimiter(""); // 任意字符都被分隔
while(sc.hasNext()) {System.out.println(sc.next());
} // a a 1 1 b b 2 2 c c
sc.close();
a
a
1
1
b
b
2
2
c
c
  • Scanner - 标准输入流
Scannersc = new Scanner(System.in);
System.out.print("请输入第1个整数:");
int n1 = sc.nextInt();
System.out.print("请输入第2个整数:");
int n2 = sc.nextInt();
System.out.format("%d + %d = %d%n", n1, n2, n1 + n2);
sc.close();
请输入第1个整数:6
请输入第2个整数:8
6 + 8 = 14

Scanner – AI 代码

Scanner sc = new Scanner(System.in);
while(sc.hasNextLine()){String str = sc.nextLine();str = str.replace("你", "朕");str = str.replace("吗", "");str = str.replace("么", "");str = str.replace("?", "!");str = str.replace("?", "!");System.out.println("\t" + str);
}
sc.close();

格式化输出

有 2 个类可以实现格式化输出

  • PrintStream
  • PrintWriter

它们有 3 个常用方法:printprintlnformat

printwrite 的区别

  • write(97) 写入的是字符 'a'
  • print(97) 写入的是字符串 "97"

PrintStream

System.outSystem.errPrintStream 类型的实例

  • 属于标准输出流(Standard Ouput Stream)
  • 比如输出到屏幕、控制台(Console)

PrintStream 是字节流,但它内部利用字符流对象来模拟字符流的许多功能

PrintWriter

平时若要创建格式化的输出流,一般使用 PrintWriter,它是字符流

String name = "Jack";
int age = 20;PrintWriter writer = new PrintWriter("F:/1.txt");
writer.format("My name is %s,age is %d", name, age);
writer.close();

可以通过构造方法设置 PrintWriter.autoflush 为 true

  • 那么 printlnprintfformat 方法内部就会自动调用 flush 方法
PrintWriter writer = new PrintWriter(new FileOutputStream(new File("F:/1.txt")), true);

数据流(Data Streams)

有 2 个数据流:DataInputStreamDataOutputStream,支持基本类型字符串类型的 I / O 操作

  • DataOutputStream 将数据写到文件当中
int age = 20;
int money = 3000;
doubleheight = 1.75;
String name = "Jack";DataOutputStream dos = new DataOutputStream(
new FileOutputStream("F:/66.txt"));
dos.writeInt(age);
dos.writeInt(money);
dos.writeDouble(height);
dos.writeUTF(name);
dos.close();
  • DataInputStream 将数据从文件当中读取数据
DataInputStream dis = new DataInputStream(
new FileInputStream("F:/66.txt"));
System.out.println(dis.readInt());
System.out.println(dis.readInt());
System.out.println(dis.readDouble());
System.out.println(dis.readUTF());
dis.close();

对象流(Object Streams)、序列化(Serialization)

有 2 个对象流:ObjectInputStreamObjectOutputStream,支持引用类型I / O 操作

只有实现了 java.io.Serializable 接口的类才能使用对象流进行 I / O 操作

  • 否则会抛出 java.io.NotSerializableException 异常

Serializable 是一个标记接口(Maker Interface),不要求实现任何方法

对象的序列化和反序列化

序列化(Serialization)

  • 将对象转换为可以存储或传输的数据
  • 利用 ObjectOutputStream 可以实现对象的序列化

反序列化(Deserialization )

  • 从序列化后的数据中恢复出对象
  • 利用 ObjectInputStream 可以实现对象的反序列化

若将对象比作是一座冰雕

  • 序列化:将冰雕融化成水
  • 反序列化:将融化后的水恢复成冰雕

serialVersionUID

每一个可序列化类都有一个 serialVersionUID,相当于类的版本号

  • 默认情况下会根据类的详细信息计算出 serialVersionUID 的值,根据编译器实现的不同可能千差万别
  • 一旦类的信息发生修改,serialVersionUID 的值就会发生变化

如果序列化、反序列时的 serialVersionUID 不一致

  • 会认定为序列化、反序列时的类不兼容,会抛出 java.io.InvalidClassException 异常

强烈建议每一个可序列化类都自定义 serialVersionUID,不要使用它的默认值

  • 必须是 static final long
  • 建议声明为 private
  • 如果没有自定义 serialVersionUID,编译器会发出 “serial” 警告

transient

transient 修饰的实例变量不会被序列化

public class Dog implements Serializable {private transient int age; // age不会被序列化private String name;public Dog(int age, String name) {this.age = age;this.name = name;}@Overridepublic String toString() {return "Dog [age=" + age + ", name=" + name + "]";}
}
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("F:/d.txt"));
oos.writeObject(new Dog(5, "Larry"));ObjectInputStream ois = new ObjectInputStream(new FileInputStream("F:/d.txt"));
System.out.println(ois.readObject()); // Dog [age=0, name=Larry],age没有被序列化ois.close();
Dog [age=0, name=Larry]

序列化与反序列化

import java.io.Serializable;public class Car implements Serializable {private static final long serialVersionUID = 1L;private double price;private String band;public Car(double price, String band) {this.price = price;this.band = band;}@Overridepublic String toString() {return "Car [price=" + price + ", band=" + band + "]";}
}
import java.io.Serializable;public class Book implements Serializable {private static final long serialVersionUID = 1L;private double price;private String name;public Book(double price, String name) {super();this.price = price;this.name = name;}@Overridepublic String toString() {return "Book [price=" + price + ", name=" + name + "]";}
}
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;public class Person implements Serializable {private static final long serialVersionUID = 1L;private int age;private String name;private Car car;private List<Book> books = new ArrayList<>();public Person(int age, String name) {this.age = age;this.name = name;}public List<Book> getBooks(){ return books; }public void setCar(Car car) { this.car = car; }@Overridepublic String toString() {return "Person [age=" + age + ", name=" + name + ", car=" + car + ", books=" + books + "]";}
}

ObjectOutputStream – 序列化

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("F:/p.txt"));Person p = new Person(20, "Jack");
p.setCar(new Car(305.6, "Bently"));
p.getBooks().add(new Book(19.9, "Java"));
p.getBooks().add(new Book(38.8, "C++"));
oos.writeObject(p);Car c = new Car(107.8, "BMW");
oos.writeObject(c);oos.close();

ObjectInputStream – 反序列化

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("F:/p.txt"));
Person p = (Person)ois.readObject();
System.out.println(p);Car c = (Car)ois.readObject();
System.out.println(c);ois.close();
Person [age=20, name=Jack, car=null, books=[Book [price=19.9, name=Java], Book [price=38.8, name=C++]]]
Car [price=107.8, band=BMW]

Files工具类

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Reader;
import java.util.function.Consumer;public class Files {/*** 搜索目录下的所有文件* @param dir 目录* @param operation 执行的操作*/public static void search(File dir, Consumer<File> operation){if(dir == null || operation == null) return;// 目录不存在 或者 传入的是文件, 不执行操作if(!dir.exists() || dir.isFile()) return; File[] subfiles = dir.listFiles();for (File sf : subfiles) {operation.accept(sf);if(sf.isFile()) continue; // 如果是文件,跳过,进行后面的搜索search(sf, operation); // 如果是文件夹, 递归搜索该文件夹}}/*** 删除文件夹* @param file*/public static void delete(File file){// 传入null 或者  文件夹不存在, 不执行操作if(file == null || !file.exists()) return;clean(file);file.delete();}/*** 清空文件夹中文件* @param dir*/public static void clean(File dir){// 如果文件夹不存在 或者 传入的是文件, 不执行操作if(dir == null || !dir.exists() || dir.isFile()) return;File[] subfiles = dir.listFiles();for (File sf : subfiles) {delete(sf);}}/*** 剪切文件或文件夹到目标路径*/public static void move(File src, File dest){if(src == null || dest == null) return;// 如果源File不存在 或者 目标File 已经存在, 不执行操作if(!src.exists() || dest.exists()) return;mkparents(dest);src.renameTo(dest);}/*** 将内存中的数据写入文件*/public static void write(byte[] bytes, File file){if(bytes == null || file ==null) return;if(file.exists()) return; // 如果文件已经存在,直接结束,不覆盖mkparents(file);try(FileOutputStream fos = new FileOutputStream(file)){fos.write(bytes);} catch (IOException e) {e.printStackTrace();}}/*** 将数据从文件读入到内存中*/public static byte[] read(File file){if(file == null || !file.exists()) return null;if(file.isDirectory()) return null; // 不读取文件夹try(FileInputStream fis = new FileInputStream(file)){byte[] bytes = new byte[(int)file.length()];fis.read(bytes);return bytes;} catch (IOException e) {e.printStackTrace();return null;}};/*** 复制(只限于文件)*/public static void copy(File src, File dest){if(src == null || dest == null) return;if(!src.exists() || dest.exists()) return;if(src.isDirectory()) return; // 不拷贝文件夹mkparents(dest);try(FileInputStream fis = new FileInputStream(src);FileOutputStream fos = new FileOutputStream(dest);){byte[] bytes = new byte[8192];int len;while((len = fis.read(bytes)) != -1){fos.write(bytes, 0, len);}} catch (IOException e) {e.printStackTrace();}}/*** 练习 – 将文本文件的内容逐个字符打印出来*/public static void writeChar(File file){if(file == null || file.isDirectory()) return;try(FileReader reader = new FileReader(file)) {int c;while((c = reader.read()) != -1){System.out.print((char)c);Thread.sleep(100);}} catch (IOException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();}}/*** 练习 - 缓冲流逐行打印字符串*/public static void writeLine(File file){try(BufferedReader reader = new BufferedReader(new FileReader(file))){String line;while((line = reader.readLine()) != null){System.out.println(line);Thread.sleep(100);}} catch (IOException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();}}/*** 创建父路径*/private static void mkparents(File file){File parent = file.getParentFile();if(parent == null) return;parent.mkdirs();}
}

【Java-IO】File、搜索删除剪切、字符集、字符编码、字节流、将内存中的数据写入文件、字符流、缓冲流、Scanner、格式化输出、数据流、对象流、序列化与反序列化、Files工具类相关推荐

  1. java.io.file.sync_Java(25)IO流和File类

    IO流+File类 File类 讲IO流之前先来讲以下File类.Java的标准库Java.io提供了File类来操作文件和目录.操作可以有:新建.删除.重命名等,但是不能访问文件本身的内容,如果想要 ...

  2. 获取文本上字符出现的次数,把数据写入文件

    /*** @author 影子* @create 2021-12-19-11:49** * 练习3:获取文本上字符出现的次数,把数据写入文件* ** * 思路:* * 1.遍历文本每一个字符* * 2 ...

  3. Stream流、FiLe和IO流、IO流(字节流-拷贝文件_和_字符流-读取文本中的数据写入文本文件中)9-10-11

    package com.streamdemo; import java.util.ArrayList; import java.util.List; /*** 体验Stream流** 创建一个集合,存 ...

  4. 33.JAVA编程思想——JAVA IO File类

    33.JAVA编程思想--JAVA IO File类 RandomAccessFile用于包括了已知长度记录的文件.以便我们能用 seek()从一条记录移至还有一条:然后读取或改动那些记录. 各记录的 ...

  5. java:IO流(缓冲流、对象流、控制台IO、转换流、java.io.File 类 )

    目录 一.IO 流的结构体系 二.缓冲流:BufferedInputStream & BufferedOutputStream 三.对象流:ObjectInputStream & Ob ...

  6. Java IO: File

    转载自  Java IO: File 译文链接 作者: Jakob Jenkov 译者: 李璟(jlee381344197@gmail.com) Java IO API中的FIle类可以让你访问底层文 ...

  7. 利用java.io.File类实现遍历本地磁盘上指定盘符或文件夹的所有的文件

    2016-11-18 这是本人的第一篇随笔博客,纠结了半天还是选择自己学的时候比较用心的一些知识点上.利用java.io.File类指定本地的文件夹进行遍历所有的文件. package org.lxm ...

  8. Java文件类– java.io.File

    Java File class is at the center of Java IO operations. Java File类是Java IO操作的中心. Java文件类 (Java File ...

  9. 使用java.io.File操作文件及文件夹

    如果有兴趣了解更多相关内容,可以来我的个人网站看看:eyes++的个人空间 一:File类的概述 java有个io包,java用于操作流的对象都在io包中,io流简单来说就是input和output流 ...

最新文章

  1. another app is currently holding the yum lock;waiting for it to exit解决
  2. clojure 中使用 actor
  3. Mysql索引底层实现
  4. linux 操作系统详解,Linux操作系统详解
  5. 利用 squid 反向代理提高网站性能
  6. malloc和free的深层次分析
  7. Spring Security——org.springframework.security.oauth:spring-security-oauth2项目已过时解决方案
  8. 机器学习(一)—— 线性回归
  9. python怎么返回上一行代码_一行Python代码能做出哪些神器的事情
  10. 一家踏实做产品,在疫情下销售增速仍达35%的公司,年报长啥样?
  11. jsp页面播放服务器视频
  12. Mariadb /MySQL max_allowed_packet设置及问题
  13. Linux stat命令和AIX istat命令 (查看文件修改时间)
  14. 读取xml节点的数据总结(.net 2.0)
  15. Dijkstra最短路径算法
  16. 如何使用XShell将本机文件上传到虚拟机上?
  17. 不同行业本地SEO和地域性SEO技巧
  18. 中国象棋---棋盘/棋子
  19. 用Qt图形视图框架开发拼图游戏
  20. python对外正式发布年份_python正式对外发布的年份

热门文章

  1. 探秘之阿里云 OSS 数据存储 API
  2. [转]关于 UTC , GMT 和 BST 夏令时
  3. 数值统计(多实例测试),统计给定的n个数中,负数、零和正数的个数
  4. 艾永亮:产品做完后,第一步就是马上推广吗?
  5. MIMIC-III数据集介绍
  6. java输出希腊字母_java 命令行窗口输出希腊字母表
  7. comma是什么键(trailing comma是什么意思)
  8. linux主机名和工作组的修改方法
  9. 我的Vue.js的学习之旅记录(1)
  10. Ubuntu 14.10 安装 hadoop-2.6.0单机配置和伪分布式配置