八、IO
文章目录
12_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();
13_格式化输出
有 2 个类可以实现格式化输出
PrintStream
PrintWriter
它们有 3 个常用方法:print
、println
、format
print
、write
的区别
write(97)
写入的是字符'a'
print(97)
写入的是字符串"97"
PrintStream
System.out
、System.err
是 PrintStream
类型的实例
- 属于标准输出流(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
- 那么
println
、printf
、format
方法内部就会自动调用flush
方法
PrintWriter writer = new PrintWriter(
new FileOutputStream(new File("F:/1.txt")), true);
14_数据流(Data Streams)
有 2 个数据流:DataInputStream
、DataOutputStream
,支持基本类型、字符串类型的 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();
15_对象流(Object Streams)、序列化(Serialization)
有 2 个对象流:ObjectInputStream
、ObjectOutputStream
,支持引用类型的 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;
}
@Override
public 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;
}
@Override
public 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;
}
@Override
public 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; }
@Override
public 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]
16_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();
}
}