第13章 IO流

一、 Java IO 流的概念

​ Java中的IO(input和output)是实现输入和输出的基础,可以方便的实现数据的输入和输出操作。java.ioopen in new window 包下提供了大量的供我们使用的操作【流】的方法和接口,用于进行各类数据的处理和传输。

​ 计算机的输入和输出都是通过二进制来完成的。在网络中我们要传递数据就要将数据【流化】,换句话说就是将文件、复杂的对象转化成能够在网络上传输的一个个的0和1,我在这里先画几幅图帮助大家理解一下。

文件在磁盘的输入输出:

image-20210812143541971
image-20210812143541971

文件在网络中的输入输出:

image-20210812143720659
image-20210812143720659

内存中的对象的输入输出:

image-20210812144810455
image-20210812144810455

二、文件的操作

1、文件路径

正斜杠,又称左斜杠,符号是"/";反斜杠,也称右斜杠,符号是"\" 。

在Unix/Linux中,路径的分隔采用正斜杠"/",比如"/home/hutaow";而在Windows中,路径分隔采用反斜杠"",比如"C:\Windows\System"

image-20210910104203903
image-20210910104203903

在Java当中反斜杠代表的是转义:

比如:

制表符(也叫制表位)的功能是在不使用表格的情况下在垂直方向按列对齐文本,就是咱们的Tab键。

  • \" 将双引号转义为真正的双引号

  • ‘\r’ (回车):即将光标回到当前行的行首(而不会换到下一行),之后的输出会把之前的输出覆盖

  • ‘\n’ 换行,换到当前位置的下一位置,而不会回到行首

2、File类简介

​ 在 Java 中,File 类是 java.ioopen in new window 包中唯一代表磁盘文件本身的对象。File 类定义了一些与平台无关的方法来操作文件,File类主要用来获取或处理与磁盘文件相关的信息,像文件名、 文件路径、访问权限和修改日期等,还可以浏览子目录层次结构。   File 类表示处理文件和文件系统的相关信息。也就是说,File 类不具有从文件读取信息和向文件写入信息的功能,它仅描述文件本身的属性。

3、构造方法

构造器描述
File(String pathname)通过将给定路径名字符串来创建一个新 File 实例
File(String parent,String child)根据指定的父路径和文件路径创建一个新File对象实例
File(File parent,String child)根据指定的父路径对象和文件路径创建一个新的File对象实例

其实很简单的,其实这个意思:

From: 元动力
1
File file = new File("D:\\code\\a.txt");
From: 元动力
1
File file = new File("D:\\code\\","a.txt");
From: 元动力
1
2
File file = new File("D:\\code");
File child = new File(file,"a.txt");

4、File类创建和删除功能

booleancreateNewFile()指定路径不存在该文件时创建文件,返回true 否则false
booleanmkdir()当指定的单击文件夹不存在时创建文件夹并返回true 否则false
booleanmkdirs()当指定的多级文件夹在某一级文件夹不存在时,创建多级文件夹并返回true 否则false
booleandelete()删除文件或者删除单级文件夹

5、File类的判断功能

booleanexists()判断指定路径的文件或文件夹是否为空
booleanisAbsolute()判断当前路径是否是绝对路径
booleanisDirectory()判断当前的目录是否存在
booleanisFile()判断当前的目录是否是一个文件
booleanisHidden()判断当前路径是否是一隐藏文件

6、File类的获取功能和修改名字功能

FilegetAbsoluteFile()获取文件的绝对路径,返回File对象
StringgetAbsolutePath()获取文件的绝对路径,返回路径的字符串
StringgetParent()获取当前路径的父级路径,以字符串形式返回该父级路径
StringgetName()获取文件或文件夹的名称
StringgetPath()获取File对象中封装的路径
longlastModified()以毫秒值返回最后修改时间
longlength()返回文件的字节数
booleanrenameTo(File dest)将当前File对象所指向的路径修改为指定File所指向的路径

7、文件夹列表操作

返回值方法描述
Stringlist()得到这个文件夹下的所有文件,返回路径数组
String[]list(FilenameFilter filter)通过过滤器过滤文件,过滤通过文件名过滤,返回路径数组
File[]listFiles()得到这个文件夹下的所有文件,返回文件数组
File[]listFiles(FileFilter filter)通过过滤器过滤文件,过滤通过文件过滤,返回文件数组
File[]listFiles(FilenameFilter filter)通过过滤器过滤文件,过滤通过文件名过滤,返回文件数组

8、作业

列出D:\code\image文件夹下的所有的图片:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class ListAllPng {

public static void main(String[] args) throws IOException {
listAll(new File("D:\\code\\image"));
}

// 单独列出方法获取目录下的图片
public static void listAll(File parent) {
MyFilter myFilter = new MyFilter();
File[] children = parent.listFiles(myFilter);

for (int i = 0; i < children.length; i++) {
// 如果子文件是个文件夹,则递归调用
if(!children[i].isFile()){
listAll(children[i]);
} else {
System.out.println(children[i].getName());
}
}
}

//定义文件过滤器
static class MyFilter implements FilenameFilter{
@Override
public boolean accept(File dir, String name) {
return name.contains(".png") || dir.isDirectory();
}
}
}

三、 IO流的分类:

​ Java中一切皆对象,流也是对象,在学习之前我们不妨先看分类和概念,至于是哪个类其实没那么重要。

​ 其实说到流,我们能想到流水,其实这已经很形象了,水从汪洋大海流入湖泊就是要通过河流。如果你还不知道,接着往下看。

​ 其实到目前为止,我们对流已经有了基本的概念,接下来我们就要深入学习流了。按照不同的分类方式,可以把流分为不同的类型。常用的分类有三种:

1、 按照流向分

  • 输入流: 只能从中读取数据,而不能向其写入数据。
  • 输出流:只能向其写入数据,而不能向其读取数据。
image-20210812160823989
image-20210812160823989

其实计算机在读取文件的时候是很麻烦的:

image-20210812161248102
image-20210812161248102

​ 当然系统级别的方法调用我们可以暂时不用考虑。但是我们确确实实看到一个文件在传输过程中经历了很多次的拷贝,IO的性能本来就不是很高,所以后来又有了零拷贝、Nio等技术,这些知识点我们计划在附加课讲解。

2 、按照操作单元划分

  • 字节流:是一个字节一个字节的读取或写入
  • 字符流:是一个字符一个字符的读取或写入,一个字符就是两个字节,主要用来处理字符。

3、 按照角色划分

  • 节点流:直接从/向一个特定的IO设备(如磁盘,网络)读/写数据的流,称为节点流。

  • 处理流:“连接”在已存在的流(节点流或处理流)之上通过对数据的处理为程序提供更为强大的读写功能的流。

image-20210812162235651
image-20210812162235651

4、Java输入/输出流体系中常用的流的分类表

| 分类 | 字节输入流 |字节输出流|字符输入流|字符输出流|

分类字节输入流字节输出流字符输入流字符输出流
抽象基类InputStreamOutputStreamReaderWriter
访问文件FileInputStreamFileOutputStreamFileReaderFileWriter
访问数组ByteArrayInputStreamByteArrayOutputStreamCharArrayReaderCharArrayWriter
访问字符串StringReaderStringWriter
缓冲流(处理)BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter
操作对象ObjectInputStreamObjectOutputStream

四、流的案例

1、继承结构

InputStream和OutputStream

image-20210812164824415
image-20210812164824415

Reader和Writer

image-20210812164846302
image-20210812164846302

2、流到底怎么用

(1)将一个流对象插在一个节点上:

其实通过名字我们就可以很好的理解了:FileInputStream就是怼在文件上的输入流啊!

From: 元动力
1
public abstract class InputStream implements Closeable

InputStream本身是抽象类,我们需要使用它的子类去构造对象:

From: 元动力
1
InputStream inputStream = new FileInputStream(file);

既然是输入流就要一点一点的往内存里读数据啊!

image-20210812165322884
image-20210812165322884

其实inputStream的方法并不多,关键在于几个read方法,管子已经插上了,接下来就是读了。

From: 元动力
1
2
3
4
5
6
7
8
// 读一个字节
int read = inputStream.read();

// 一次性读1024个字节到那个内存数组
int read = inputStream.read(new byte[1024]);

// 从第0个字节开始读,读120个字节
int read = inputStream.read(new byte[1024],0,120);

(2)使用read()方法读取

它的读取流程大概是这个样子的,inputStream内部有一个游标,它会记录目前读到哪里了,看下图:

image-20210812173337468
image-20210812173337468

我们不妨尝试一下:

我的D盘的code目录下新建一个文本:

image-20210812170548644
image-20210812170548644

我知道:read返回-1时就代表文件读完了,所以我写了如下代码:

From: 元动力
1
2
3
4
5
6
7
8
9
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("D:/code/a.txt");
int read;
while ((read =inputStream.read()) != -1){
System.out.print(read+" ");
}
}
72 101 108 108 111 32 87 111 114 108 100 33
H e l l 0 W o r l d !

read就是每次读出的字节,直到-1就停止。

小tips:一个流我读完了一次还能读第二次吗?

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("D:/code/a.txt");
int read;

while ((read =inputStream.read()) != -1){
System.out.print(read+" ");
}
System.out.println("再读一次---------------");
while ((read =inputStream.read()) != -1){
System.out.print(read+" ");
}
}

72 101 108 108 111 32 87 111 114 108 100 33
再读一次---------------

我们发现一个流读完了就没有了,就不能在读了。当然文档里有mark和reset方法,我们在系统中测试是不可用的。

From: 元动力
1
System.out.println(inputStream.markSupported());

(3)使用read(byte[] byte)读取

From: 元动力
1
2
3
4
5
6
7
8
9
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("D:/code/a.txt");
int read;

byte[] buf = new byte[3];
while ((read =inputStream.read(buf)) != -1){
System.out.print(read+" ");
}
}
image-20210812173613721
image-20210812173613721

我们想向深入走一步,看看源码:

但是发现,源码目前位置看不了了,这些方法都带有native,这更加说明了读文件一定是JVM调用系统方法读取的。

image-20210812171830458
image-20210812171830458

(4)输出流的使用

我们要学会举一反三,其实他们的区别就是一个读,一个写嘛,我写一个例子就好了。

有一个小知识点:

​ 在定义文件输出流时,有两个参数,第二个如果是true代表追加文件,如果false代表覆盖文件,意思就是如果人家这个文件原来有内容,就覆盖的没了,这一点要注意。

From: 元动力
1
OutputStream outputStream = new FileOutputStream("D:/code/a.txt",true);
From: 元动力
1
2
3
4
5
OutputStream outputStream = new FileOutputStream("D:/code/a.txt",true);
// 一个字节一个字节的写
outputStream.write(97);

// 97是一个字节啊

我们发现文件中被写入的是一个a

image-20210812175544376
image-20210812175544376
From: 元动力
1
2
3
OutputStream outputStream = new FileOutputStream("D:/code/b.txt",true);
// 直接将一个字节数组写出
outputStream.write("Hello World".getBytes());

我们不妨升级一下,一个文件的拷贝程序就写好了。

From: 元动力
1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("D:/code/a.txt");
OutputStream outputStream = new FileOutputStream("D:/code/b.txt",true);

byte[] buf = new byte[3];
int len;
while ((len =inputStream.read(buf)) != -1){
outputStream.write(buf,0,len);
}
}

(5)资源的释放

一个IO流的标准写法是什么呢?

我们发现IO有以下几点需要我们处理:

1、绝大部分的对IO的操作都需要处理可能出现的IO异常。

image-20210812175913456
image-20210812175913456

2、我们发现不管是inputStream还是outputStream都有一个close方法,IO是需要消耗系统资源的,每一个stream都需要系统分配资源,是弥足珍贵的,所以没有流一旦使用完成就一定要关闭资源。

经过反复修改我们写出了如下代码:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public static void main(String[] args) {
// 定义资源
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = new FileInputStream("D:/code/a.txt");
outputStream = new FileOutputStream("D:/code/b.txt",true);

byte[] buf = new byte[3];
int len;
while ((len =inputStream.read(buf)) != -1){
outputStream.write(buf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
// 最终无论如何,都要释放资源
} finally {
if(inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(outputStream != null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

【AutoCloseable接口的好处】

以上代码如此繁杂,jdk1.7之后,很多资源类的类都实现了AutoCloseable接口

实现了这个接口的类可以在try中定义资源,并会主动释放资源:

这样就极大的简化了代码的编写,但是你这么写了可能会有人看不懂呦!

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String[] args) {
try(InputStream inputStream = new FileInputStream("D:/code/a.txt");
OutputStream outputStream= new FileOutputStream("D:/code/b.txt",true)) {
byte[] buf = new byte[3];
int len;
while ((len =inputStream.read(buf)) != -1){
outputStream.write(buf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
}
}

3、案例(作业)

(1)字符流读文件

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void testReader() throws Exception{
//怼了一个输入流到文件上
Reader reader = new FileReader("E:\\test\\a\\word.txt");
BufferedReader br = new BufferedReader(reader);
String str;
while ((str = br.readLine()) != null){
System.out.println(str);
}
reader.close();
br.close();
}

(2)向文件里写内容

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//这个用main方法测吧
public void testWriter() throws Exception{
//怼了一个输入流到文件上
Writer writer = new FileWriter("E:\\test\\a\\writer.txt");
BufferedWriter bw = new BufferedWriter(writer);
Scanner scanner = new Scanner(System.in);

while (true){
System.out.print("请输入:");
String words = scanner.next();
bw.write(words);
bw.flush();
}
}

五、序列化和反序列化

1、对象序列化

  • 序列化:将对象写入到IO流中,说的简单一点就是将内存模型的对象变成字节数字,可以进行存储和传输。
  • 反序列化:从IO流中恢复对象,将存储在磁盘或者从网络接收的数据恢复成对象模型。
  • 使用场景:所有可在网络上传输的对象都必须是可序列化的,否则会出错;所有需要保存到磁盘的Java对象都必须是可序列化的。

该对象必须实现Serializable接口,才能被序列化。

我们的

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import java.io.Serializable;


public class User implements Serializable {
private String name;

private int age;

private int gander;

public User(String name, int age, int gander) {
this.name = name;
this.age = age;
this.gander = gander;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public int getGander() {
return gander;
}

public void setGander(int gander) {
this.gander = gander;
}
}

From: 元动力
1
2
3
4
5
6
7
8
9
10
@Test
public void testObjectOut() throws Exception{
//怼了一个string
InputStream is = new FileInputStream("E:\\test\\a\\user.txt");
ObjectInputStream oi = new ObjectInputStream(is);
User user = (User)(oi.readObject());
System.out.println(user.getName());
is.close();
oi.close();
}

2、序列化版本号

​ 我们知道,反序列化必须拥有class文件,但随着项目的升级,class文件也会升级,序列化怎么保证升级前后的兼容性呢?

​ Java序列化提供了一个``private static final long serialVersionUID` 的序列化版本号,只要版本号相同,即使更改了序列化属性,对象也可以正确被反序列化回来。

From: 元动力
1
2
3
4
5
6
7
public class Person implements Serializable {
//序列化版本号
private static final long serialVersionUID = 1111013L;
private String name;
private int age;
//省略构造方法及get,set
}

​ 如果反序列化使用的版本号与序列化时使用的不一致,反序列化会报InvalidClassException’异常。

img
img

​ 序列化版本号可自由指定,如果不指定,JVM会根据类信息自己计算一个版本号,这样随着class的升级、代码的修改等因素无法正确反序列化;

​ 不指定版本号另一个明显隐患是,不利于jvm间的移植,可能class文件没有更改,但不同jvm可能计算的规则不一样,这样也会导致无法反序列化。

什么情况下需要修改serialVersionUID呢:

  • 如果只是修改了方法,反序列化不容影响,则无需修改版本号;

  • 如果只是修改了静态变量,瞬态变量(transient修饰的变量),反序列化不受影响,无需修改版本号。

3、总结

  1. 所有需要网络传输的对象都需要实现序列化接口。
  2. 对象的类名、实例变量(包括基本类型,数组,对其他对象的引用)都会被序列化;方法、类变量、transient实例变量都不会被序列化。
  3. 如果想让某个变量不被序列化,使用transient修饰。
  4. 序列化对象的引用类型成员变量,也必须是可序列化的,否则,会报错。
  5. 反序列化时必须有序列化对象的class文件。
  6. 同一对象序列化多次,只有第一次序列化为二进制流,以后都只是保存序列化编号,不会重复序列化。
  7. 建议所有可序列化的类加上serialVersionUID 版本号,方便项目升级。

Intellij idea用快捷键自动生成序列化id,类继承了Serializable接口之后,使用alt+enter快捷键自动创建序列化id

方法:进入setting→inspections→serialization issues→选择图中的选项。serializable class without ‘serialVersionUID’

image-20210910161445055
image-20210910161445055

4、深拷贝

(1)对象的引用改变:

image-20210910182910093
image-20210910182910093
From: 元动力
1
2
3
4
5
6
7
8
public void deepCopyTest() throws  CloneNotSupportedException {
User user = new User(12, "zhagnsna");
user.setDog(new Dog(2));
User user1 = user;

user.getDog().setAge(23);
System.out.println(user1);
}

(2)浅拷贝:实现clonable接口,重写clone方法。

image-20210910182958539
image-20210910182958539
From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class User implements Serializable,Cloneable {


@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

@Test
public void deepCopyTest() throws CloneNotSupportedException {
User user = new User(12, "zhagnsna");
user.setDog(new Dog(2));
User user1 = (User)user.clone();

user.getDog().setAge(23);
System.out.println(user1);
}

深拷贝:使用对象流先写入byte数组,再读出来。

image-20210910183035282
image-20210910183035282
From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Test
public void deepCopyTest2() throws CloneNotSupportedException, IOException, ClassNotFoundException {
User user = new User(12, "zhangsan");
user.setDog(new Dog(2));

// 将对象写到字节数组当中
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(user);
// 获取字节数组
byte[] bytes = outputStream.toByteArray();
// 用输入流读出来
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
Object object = objectInputStream.readObject();
User user1 = (User) object;

user.setAge(44);
user.getDog().setAge(11);
System.out.println(user);
System.out.println(user1);

}

六、大作业:

写一个程序,能够给一个商品文件进行增、删、改、查。

image-20210812182213790
image-20210812182213790

第一列是编号,第二列是商品名称,第三列是价格。

骨架代码:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import java.util.Scanner;

public class Shop {

private static Scanner scanner = new Scanner(System.in);

public static void main(String[] args) {

while (true) {
System.out.println("请选择功能:1、插入新商品 2、删除商品 3、修改商品 4、查找一个商品 5、退出");
int function = scanner.nextInt();
switch (function){
case 1:
insert();
break;
case 2:
delete();
break;
case 3:
update();
break;
case 4:
findOne();
break;
case 5:
System.exit(-1);
break;
}
}
}

private static void findOne() {
System.out.println("请输入商品编号:");
int id = scanner.nextInt();
// 思路一:一行一行的读,找到为止

// 思路二:全部读到内存,内存里找
}

private static void update() {
System.out.println("请输入商品编号:");
// 全部拿出来,更新后覆盖
}

private static void delete() {
System.out.println("请输入商品编号:");
// 全部拿出来,删除后覆盖
}

private static void insert() {
System.out.println("请输入商品编号:");
// 最简单,直接追加
}

private static class Goods{
private int id;
private String name;
private int price;

public Goods() {
}

public Goods(int id, String name, int price) {
this.id = id;
this.name = name;
this.price = price;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getPrice() {
return price;
}

public void setPrice(int price) {
this.price = price;
}
}
}

代码:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
package com.ydlclass;

import java.io.*;
import java.util.*;

public class Shop {
private static Scanner scanner = new Scanner(System.in);
public static final String PATH = "D:\\code\\image\\a\\1.txt";

public static void main(String[] args) {

while (true) {
System.out.println("请选择功能:1、插入新商品 2、删除商品 3、修改商品 4、查看商品 4、查看一个商品 5、退出");
int function = scanner.nextInt();
switch (function){
case 1:
insert();
break;
case 2:
delete();
break;
case 3:
update();
break;
case 4:
check();
break;
case 5:
findOne();
break;
case 6:
System.exit(-1);
break;
}
}
}

private static void findOne() {
System.out.println("请输入商品编号:");
int id = scanner.nextInt();
// 函数式编程找到对应id的商品
Optional<Goods> first = findAllGoods().stream().filter(g -> g.getId() == id).findFirst();
if (first.isPresent()){
Goods goods = first.get();
System.out.println(goods.getId() + " " + goods.getName() + " " + goods.getPrice());
} else {
System.out.println("您需要的商品不存在");
}
}

private static void check() {
List<Goods> allGoods = findAllGoods();
for (Goods goods : allGoods) {
System.out.println(goods.getId() + " " + goods.getName() + " " + goods.getPrice());
}
}

private static void update() {
// 输入商品id
System.out.println("请输入商品编号:");
int id = scanner.nextInt();
System.out.println("请输入商品名字:");
String name = scanner.next();
System.out.println("请输入商品价格:");
int price = scanner.nextInt();
// 内存中删除对应的商品
List<Goods> allGoods = findAllGoods();
Iterator<Goods> iterator = allGoods.iterator();
while (iterator.hasNext()){
Goods goods = iterator.next();
if(goods.getId() == id){
goods.setName(name);
goods.setPrice(price);
}
}

// 刷入磁盘
writeGoods(allGoods,false);
}

private static void delete() {
// 输入商品id
System.out.println("请输入商品编号:");
int id = scanner.nextInt();
// 内存中删除对应的商品
List<Goods> allGoods = findAllGoods();
Iterator<Goods> iterator = allGoods.iterator();
while (iterator.hasNext()){
Goods goods = iterator.next();
if(goods.getId() == id){
iterator.remove();
}
}

// 刷入磁盘
writeGoods(allGoods,false);
}

private static void insert() {
// 防止id重复
boolean flag = true;
Integer id = null;
while (flag) {
System.out.println("请输入商品编号:");
id = scanner.nextInt();
final Integer i = id;
Optional<Goods> first = findAllGoods().stream().filter(p -> p.getId() == i).findFirst();
flag = first.isPresent();
if (flag) {
System.out.println("该编号已经存在!");
}
}

System.out.println("请输入商品名字:");
String name = scanner.next();
System.out.println("请输入商品价格:");
int price = scanner.nextInt();
// 最简单,直接追加
writeGoods(Arrays.asList(new Goods(id,name,price)),true);
}

/**
* 从磁盘中获取所有的数据
* @return
*/
private static List<Goods> findAllGoods(){
List<Goods> goodsList = new ArrayList<>();
try (Reader reader = new FileReader(Shop.PATH);
BufferedReader bufferedReader = new BufferedReader(reader);
){
String goodsStr;
while ((goodsStr = bufferedReader.readLine()) != null){
// 1 肥皂 23
String[] goodsElem = goodsStr.split(" ");
Goods goods = new Goods(
Integer.parseInt(goodsElem[0]),
goodsElem[1],
Integer.parseInt(goodsElem[2]));
goodsList.add(goods);
}

} catch (IOException e){
e.printStackTrace();
}
return goodsList;
}

private static void writeGoods(List<Goods> allGoods,boolean append){
try( Writer writer = new FileWriter(Shop.PATH,append);
BufferedWriter bufferedWriter = new BufferedWriter(writer);
) {
for (Goods goods : allGoods) {
bufferedWriter.write(goods.getId() + " " + goods.getName() + " " + goods.getPrice());
bufferedWriter.newLine();
}

} catch (IOException e){
e.printStackTrace();
}
}

private static class Goods{
private int id;
private String name;
private int price;

public Goods() {
}

public Goods(int id, String name, int price) {
this.id = id;
this.name = name;
this.price = price;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getPrice() {
return price;
}

public void setPrice(int price) {
this.price = price;
}

@Override
public String toString() {
return "Goods{" +
"id=" + id +
", name='" + name + '\'' +
", price=" + price +
'}';
}
}
}


本站由 钟意 使用 Stellar 1.28.1 主题创建。
又拍云 提供CDN加速/云存储服务
vercel 提供托管服务
湘ICP备2023019799号-1
总访问 次 | 本页访问