第13章 IO流 一、 Java IO 流的概念 Java中的IO(input和output)是实现输入和输出的基础,可以方便的实现数据的输入和输出操作。java.ioopen in new window 包下提供了大量的供我们使用的操作【流】的方法和接口,用于进行各类数据的处理和传输。
计算机的输入和输出都是通过二进制来完成的。在网络中我们要传递数据就要将数据【流化】,换句话说就是将文件、复杂的对象转化成能够在网络上传输的一个个的0和1,我在这里先画几幅图帮助大家理解一下。
文件在磁盘的输入输出:
image-20210812143541971 文件在网络中的输入输出:
image-20210812143720659 内存中的对象的输入输出:
image-20210812144810455 二、文件的操作 1、文件路径 正斜杠,又称左斜杠,符号是"/";反斜杠,也称右斜杠,符号是"\" 。
在Unix/Linux中,路径的分隔采用正斜杠"/",比如"/home/hutaow";而在Windows中,路径分隔采用反斜杠"",比如"C:\Windows\System"
image-20210910104203903 在Java当中反斜杠代表的是转义:
比如:
制表符(也叫制表位)的功能是在不使用表格的情况下在垂直方向按列对齐文本,就是咱们的Tab键。
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类创建和删除功能 boolean createNewFile() 指定路径不存在该文件时创建文件,返回true 否则false boolean mkdir() 当指定的单击文件夹不存在时创建文件夹并返回true 否则false boolean mkdirs() 当指定的多级文件夹在某一级文件夹不存在时,创建多级文件夹并返回true 否则false boolean delete() 删除文件或者删除单级文件夹
5、File类的判断功能 boolean exists() 判断指定路径的文件或文件夹是否为空 boolean isAbsolute() 判断当前路径是否是绝对路径 boolean isDirectory() 判断当前的目录是否存在 boolean isFile() 判断当前的目录是否是一个文件 boolean isHidden() 判断当前路径是否是一隐藏文件
6、File类的获取功能和修改名字功能 File getAbsoluteFile() 获取文件的绝对路径,返回File对象 String getAbsolutePath() 获取文件的绝对路径,返回路径的字符串 String getParent() 获取当前路径的父级路径,以字符串形式返回该父级路径 String getName() 获取文件或文件夹的名称 String getPath() 获取File对象中封装的路径 long lastModified() 以毫秒值返回最后修改时间 long length() 返回文件的字节数 boolean renameTo(File dest) 将当前File对象所指向的路径修改为指定File所指向的路径
7、文件夹列表操作 返回值 方法 描述 String list() 得到这个文件夹下的所有文件,返回路径数组 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-20210812161248102 当然系统级别的方法调用我们可以暂时不用考虑。但是我们确确实实看到一个文件在传输过程中经历了很多次的拷贝,IO的性能本来就不是很高,所以后来又有了零拷贝、Nio等技术,这些知识点我们计划在附加课讲解。
2 、按照操作单元划分 字节流:是一个字节一个字节的读取或写入 字符流:是一个字符一个字符的读取或写入,一个字符就是两个字节,主要用来处理字符。 3、 按照角色划分 image-20210812162235651 4、Java输入/输出流体系中常用的流的分类表 | 分类 | 字节输入流 |字节输出流|字符输入流|字符输出流|
分类 字节输入流 字节输出流 字符输入流 字符输出流 抽象基类 InputStream OutputStream Reader Writer 访问文件 FileInputStream FileOutputStream FileReader FileWriter 访问数组 ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArrayWriter 访问字符串 StringReader StringWriter 缓冲流(处理) BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter 操作对象 ObjectInputStream ObjectOutputStream
四、流的案例 1、继承结构 InputStream和OutputStream
image-20210812164824415 Reader和Writer
image-20210812164846302 2、流到底怎么用 (1)将一个流对象插在一个节点上: 其实通过名字我们就可以很好的理解了:FileInputStream就是怼在文件上的输入流啊!
From: 元动力 1 public abstract class InputStream implements Closeable
InputStream本身是抽象类,我们需要使用它的子类去构造对象:
From: 元动力 1 InputStream inputStream = new FileInputStream (file);
既然是输入流就要一点一点的往内存里读数据啊!
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 我们不妨尝试一下:
我的D盘的code目录下新建一个文本:
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 我们想向深入走一步,看看源码:
但是发现,源码目前位置看不了了,这些方法都带有native,这更加说明了读文件一定是JVM调用系统方法读取的。
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 );
我们发现文件中被写入的是一个a
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 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 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{ 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; }
如果反序列化使用的版本号与序列化时使用的不一致,反序列化会报InvalidClassException’异常。
img 序列化版本号可自由指定,如果不指定,JVM会根据类信息自己计算一个版本号,这样随着class的升级、代码的修改等因素无法正确反序列化;
不指定版本号另一个明显隐患是,不利于jvm间的移植,可能class文件没有更改,但不同jvm可能计算的规则不一样,这样也会导致无法反序列化。
什么情况下需要修改serialVersionUID呢:
3、总结 所有需要网络传输的对象都需要实现序列化接口。 对象的类名、实例变量(包括基本类型,数组,对其他对象的引用)都会被序列化;方法、类变量、transient实例变量都不会被序列化。 如果想让某个变量不被序列化,使用transient修饰。 序列化对象的引用类型成员变量,也必须是可序列化的,否则,会报错。 反序列化时必须有序列化对象的class文件。 同一对象序列化多次,只有第一次序列化为二进制流,以后都只是保存序列化编号,不会重复序列化。 建议所有可序列化的类加上serialVersionUID 版本号,方便项目升级。 Intellij idea用快捷键自动生成序列化id,类继承了Serializable接口之后,使用alt+enter快捷键自动创建序列化id
方法:进入setting→inspections→serialization issues→选择图中的选项。serializable class without ‘serialVersionUID’
image-20210910161445055 4、深拷贝 (1)对象的引用改变:
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
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
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 第一列是编号,第二列是商品名称,第三列是价格。
骨架代码:
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(); 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 () { 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 () { 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 () { 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 ); } 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 ){ 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 + '}' ; } } }