问题: 程序运行期间 数据不能"持久化"保存。 保证不会丢失数据。 如何能够实现数据持久化? 磁盘----> 文件夹(目录) 文件(维护数据) java.io.*
1. File(方法)
方法 | 作用 |
String getName() | 获得文件名称 |
String getPath() | 获得相对路径 |
String getAbstroutePath() | 获得绝对路径 |
long length() | 获得文件字节大小 |
String[] list() | 获得目录下面的一级资源(资源名称) |
File[] listFiles() | 获得目录下面的一级资源 |
createNewFile() | 创建文件 |
mkdir()/mkdis() | 创建目录 |
boolean exits() | 是否存在 |
isDirectory()/isFile() |
代表磁盘里面 存在或者不存在的 目录或者文件。
static String pathSeparator 系统的环境变量的配置相关的数据 static char pathSeparatorChar 在windows: ; 在linux: : "D:\Program Files\JetBrains" static String separator 路径的盘符分隔符 window: \ 在linux: / static char separatorChar
File(String pathname) 根据指定的文件/目录的路径创建File对象 File(String parent, String child) parent: 父级目录路径 child: 子级目录/子级文件路径 File(File parent, String child)
1.1 代表文件
//File类代表文件 private static void demo1() { //1.创建File对象---->磁盘里面文件 File file = new File("E:\\test\\a.txt"); //2.获得File对象的一些基本的属性的信息 System.out.println("文件名称:" + file.getName()); //路径: 相对路径 + 绝对路径 //绝对路径: E:\test\a.txt http://www.baidu.com http://127.0.0.1:8080/demo/a.html //相对路径: a/b/c/a.txt 目前项目/相对于项目部署服务器路径 System.out.println("文件绝对路径:" + file.getAbsolutePath()); System.out.println("文件相对路径:" + file.getPath()); System.out.println("文件大小:" + file.length());//文件内容大小 字节 System.out.println("上一次操作文件的时间:" + file.lastModified());//时间的毫秒数 System.out.println("上一次操作文件的时间:" + new Date(file.lastModified()));//时间的毫秒数 转换成 年月日的时间数据 Date //3.对File进行一些判断 System.out.println(file.exists());//判断文件是否存在 System.out.println(file.isFile());//判断file对象是否是文件 //4.操作文件的权限 System.out.println(file.setReadOnly());//设置文件只读 //5.删除文件 System.out.println(file.delete());//删除磁盘文件 }
创建文件
private static void demo2() { //都用绝对路径 //文件不存在 创建文件 //src\a.txt File file = new File("src/com/javasm/io/b.txt"); try { if (!file.exists()) { System.out.println(file.createNewFile()); } } catch (IOException e) { e.printStackTrace(); } }
1.2 代表目录
//File代表是目录 private static void demo3() { //1.创建目录对象 //File directory = new File("E:\\test\\a\\b"); File directory = new File("E:\\test\\a", "b"); System.out.println(directory.getName()); System.out.println(directory.getAbsolutePath()); System.out.println(directory.getPath()); System.out.println(directory.isHidden()); System.out.println(directory.exists()); System.out.println(directory.isDirectory()); }
private static void demo5() { File file = new File("src"+File.separator+"demo.txt"); //对指定的文件改名 //路径不一致: 剪切+重命名 System.out.println(file.renameTo(new File("src/demo/a/hello.txt"))); } private static void demo4() { File directory = new File("src", "demo/a/b"); //创建目录 //directory.mkdir() 创建1级目录 /*if (!directory.exists()) { System.out.println(directory.mkdirs());//创建>1级目录 }*/ //删除目录 System.out.println(directory.delete());//删除文件 删除目录(里面没有资源) }
1.3 查找子级资源
展示指定父级目录下的所有的子级的资源。
//File类里面的方法 : 获得指定父级目录下的一级资源 String[] list = directory.list();//获得指定目录下的所有的子级资源名称(文件名称/目录名称) System.out.println(Arrays.toString(list)); File[] files = directory.listFiles();//获得指定目录下的所有的子级资源对象(文件对象/目录对象) System.out.println(Arrays.toString(files));
2. IO
File类只能操作文件或者目录一些属性。无法操作文件里面的数据。
场景: 上传文件/下载文件 数据导入导出....
操作文件里面数据: 1. 读取文件里面的数据 read 2. 将数据写入文件中 write IO: 实现对文件数据“读写”功能。 I: input Stream 输入流 O: output Stream 输出流 分析: 程序/代码 数据 磁盘文件 使用程序将数据写入磁盘 ----> OutputStream write 使用程序读取磁盘数据----->InputStream read
流 从流向划分: 输入和输出 计算机里面 存储的文件都属于2进制文件 里面存储内容都是以byte存储的。 一个汉字: 不同的编码格式下 占据的字节不太一样的。 从操作内容划分: 1. 字节流: 字节输入流 字节输出流----> 字节---->可以操作计算机里面的任意类型的文件 2. 字符流: 字符输入流 字符输出流 -----> 字符---->只能操作纯文本文件。 文本文件: 纯文本文件:txt java ini 带样式的文本文件: doc ppt poi
3. 字节流
public abstract class InputStream extends Object implements Closeable //类实现Closeable 证明这个类是一个资源(物理资源) //释放资源------> finally close()
3.1 字节输入流 InputStream
读取文件数据。 read
常用的功能方法:
int read() 一次读取一个字节 返回值: 读取的一个字节的内容 -1(读到文件末尾) int read(byte[] b) 一次读取指定b.length个字节 内容存储到了字节数组中了 返回值: 读取到的有效的字节数量 -1 int read(byte[] b, int off, int len) 一次读取b.length个字节内容 off: 从数组的指定索引开始存储 len: 存储len个字节数据 (数组里面存储多少个与len有关系) 返回值: len的数据 或者-1 int available() 获得流里面的有效的字节数量 等价与 File.length() void close() 释放资源
FileInputStream(String name) FileInputStream(File file) 使用特定的文件对象/文件路径创建字节输入流对象
1. read()
private static void demo1() { //读取指定文件里面的数据----> 不要操作中文 String filePath = "src/info.txt"; //读取 InputStream inputStream = null; try { //1.创建字节输入流对象 并获得文件字节数据 inputStream = new FileInputStream(filePath); //2.读取流里面字节数据 /*int data = inputStream.read(); //System.out.println("读取到的第一个字节:" + (char)data); //读完所有的字节数据 读到文件末尾 -1 while (data != -1) { System.out.print((char) data); data = inputStream.read(); }*/ int result; while ((result = inputStream.read()) != -1) { System.out.print((char) result); } } catch (IOException e) { e.printStackTrace(); } finally { //释放资源 try { if (inputStream != null) inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
2. try...with...resources
private static void demo3() { //1.7+ 提供了一种更加优雅的方式"释放流对象" 自动调用close //try....with...resources //资源释放就不需要编写了 不需要finally try ( //创建流对象-----> 必须实现Closeable InputStream inputStream = new FileInputStream("src/info.txt"); MyClass myClass = new MyClass(); ) { System.out.println(inputStream.read()); } catch (IOException e) { e.printStackTrace(); } }
private static void demo4() throws FileNotFoundException { InputStream inputStream = new FileInputStream("src/info.txt"); MyClass myClass = new MyClass(); try (inputStream; myClass;) { inputStream.read(); } catch (IOException e) { e.printStackTrace(); } }
3. read(byte[] by)
private static void demo5() throws FileNotFoundException { //对于输入流而言: read 文件必须要存在的 FileInputStream inputStream = new FileInputStream("src\\com\\javasm\\io\\InputStreamDemo.java"); try (inputStream) { //读取数据 byte[] bytes = new byte[1024 * 5];//一般都是1024的整数倍 理论上来说 空间越大 速度越快 //int result = inputStream.read(bytes); //一次读取1024个字节内容 读取到的字节的数据都存储到了bytes数组中 //result: 维护读取到的有效的字节个数 //有效的字节的数据是多少个? 不一定是length //System.out.println(result); //System.out.println(Arrays.toString(bytes)); //1.循环遍历字节数组 将每一个字节转换成char result次 //2.将字节数组转换成字符串的数据(看不懂的数据转换成看的懂字符串) //String s = new String(bytes, 0, result); //System.out.println(s); //循环读取指定的文件的数据 int len; while ((len = inputStream.read(bytes)) != -1) { System.out.print(new String(bytes, 0, len)); } //100个字节 //read() 读取一个字节 并输出打印一次 //length=50 //read(bytes[]) 读取一个字节 输出打印2次 } catch (IOException e) { e.printStackTrace(); } }
3.2 字节输出流 OutputStream
将流里面的数据写入文件中。 write
public abstract class OutputStream extends Object implements Closeable, Flushable
void write(int b) 一次写一个字节 void write(byte[] b) 一次写入b.length个字节 void write(byte[] b, int off, int len) void close() ----->提前flush void flush() 将缓冲的数据刷出到文件中
FileOutputStream(String name) FileOutputStream(File file) FileOutputStream(String name, boolean append) FileOutputStream(File file, boolean append) append: 追加数据到文件末尾 还是直接覆盖源文件数据 默认: false
1. write
public static void main(String[] args) throws FileNotFoundException { //src/a.txt //对于输出流 文件不存在? 是否会报错? 不会报错 文件会自动创建(File.createNewFile()) //一定要保证父级目录要存在 //读写内容: 出现乱码 编码格式不符 //字节流读取字符(汉字)数据 OutputStream outputStream = new FileOutputStream("src/a.txt", true); try (outputStream) { outputStream.write("张三".getBytes()); outputStream.write(97); outputStream.write('\n'); outputStream.write("我们".getBytes(Charset.forName("UTF-8"))); //outputStream.write("世界那么大".getBytes(), 0, 5); } catch (IOException e) { e.printStackTrace(); } }
3.3 模拟上传头像
复制粘贴
分析: 1. 告诉我你的文件在哪里?=====> 上传的文件的路径 2. 读取用户传的数据 写入服务器中一个文件里面 2.1 保证文件名称唯一性 3. 服务器里面的一个文件: 文件的内容 与用户要传的内容一致
/** * @author: Lisa * @className: UploadImageDemo * @description: 模拟用户上传头像 * @date: 2022/3/7 15:29 * @version: 0.1 * @since: jdk11 */ public class UploadImageDemo { private static final String PARENT_DIRECTORY = "upload/user/";//根目录 /** * 上传文件 * * @param sourceFilePath 源文件路径 * @return 上传成功之后 文件所在的服务器路径 upload/user/a.jpg */ public static String upload(String sourceFilePath) throws FileNotFoundException { Objects.requireNonNull(sourceFilePath); //读取源文件数据: InputStream read //写入目标文件中 OutputStream write InputStream inputStream = new FileInputStream(sourceFilePath); //获得目标文件文件名称 //1. 获得原文件名称 最后\之后的数据 弊端: 不同的用户可能会上传重名的文件 //String fileName = sourceFilePath.substring(sourceFilePath.lastIndexOf(File.separator) + 1); //2. 保证名称唯一性 UUID //3.用户量增加 所有文件都在一个目录中 读取文件性能比较低----> 分目录存储图片 //目录打散 // 动态创建目录 以当前时间为目录名称 2022-03-07 String curDate = LocalDate.now().toString(); //判断目录是否存在 File.exsits() File directory = new File(PARENT_DIRECTORY, curDate); if (!directory.exists()) { System.out.println("1111111111111111111"); directory.mkdirs(); } //获得原文件后缀 String extension = sourceFilePath.substring(sourceFilePath.lastIndexOf(".")); String targetFileName = UUID.randomUUID().toString().replaceAll("-", "") + extension; OutputStream outputStream = new FileOutputStream(new File(directory, targetFileName)); try (inputStream; outputStream) { //循环读写文件数据 //1.读一个字节 写一个字节 小文件ok 大文件有问题 //2.缓冲----> byte[] byte[] bytes = new byte[1024 * 10]; int len; while ((len = inputStream.read(bytes)) != -1) { //outputStream.write(len); outputStream.write(bytes, 0, len); } } catch (IOException e) { e.printStackTrace(); } System.out.println("文件上传成功"); return PARENT_DIRECTORY + curDate + "/" + targetFileName; } //4588 public static void main(String[] args) throws FileNotFoundException { long begin = System.currentTimeMillis(); String path = "D:\\chaoyue.jpg"; path = "E:\\pic\\chaoyue.jpg"; path = "E:\\tools\\IntelliJ IDEA\\ideaIU-2020.1.1.exe"; System.out.println(upload(path));//回显图片 long end = System.currentTimeMillis(); System.out.println(end - begin); // System.out.println(UUID.randomUUID().toString().replaceAll("-","")); } }
3.4 高效字节流
高效: 相比普通字节流 性能较高。 底层自带缓冲。默认为8192个字节。
BufferedInputStream: 高效字节输入流
BufferedOutputStream: 高效字节输出流
- 装饰者设计模式
BufferedInputStream(InputStream in) 将基本的字节输入流转换成高效输入流对象 BufferedOutputStream(OutputStream out) 将基本的字节输出流转换成高效字节输出流
public static void main(String[] args) throws FileNotFoundException { //模拟文件上传: long begin = System.currentTimeMillis(); String path = "E:\\tools\\IntelliJ IDEA\\ideaIU-2020.1.1.exe"; BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(path)); BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream("D:\\a.exe")); try (inputStream; outputStream) { //循环读写 int len; byte[] bytes = new byte[1024 * 5]; //自定义的缓冲 >底层的缓冲 就不使用底层的缓冲 //自定义的缓冲 < 底层的缓冲 8192 while ((len = inputStream.read(bytes)) != -1) { //一次读取一个字节 一次写一次字节 outputStream.write(bytes,0,len);//将1个字节写入缓冲 满的时候 调用底层的flush } } catch (IOException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println(end - begin);//8 }
4. 字符流
主要是操作文本文件。 字节+编码格式。 操作内容是字符数据。
4.1 字符输入流 Reader
文件必须要存在。
int read() int read(char[] cbuf) abstract int read(char[] cbuf, int off, int len)
FileReader(String fileName) FileReader(File file)
4.2 字符输出流 Writer
public abstract class Writer extends Object implements Appendable, Closeable, Flushable
abstract void close() 关闭流,先刷新。 abstract void flush() 刷新流。 void write(char[] cbuf) 写入一个字符数组。 abstract void write(char[] cbuf, int off, int len) 写入字符数组的一部分。 void write(int c) 写一个字符 void write(String str) 写一个字符串 void write(String str, int off, int len)
FileWriter(String fileName) FileWriter(String fileName, boolean append)
1. 注册用户
private static int idIndex = 1000; private static void userRegister() throws IOException { //模拟用户注册: 持久化报存用户信息 //将每个用户信息保存在userInfo.txt id-name-pass-age @Cleanup Scanner input = new Scanner(System.in); //将用户写入文件 输出流。 字节输出流 字符输出流 Writer---->FileWriter @Cleanup FileWriter writer = new FileWriter("src/userInfo.txt", true); List<UserInfo> userInfoList = findAllUser(); if (!userInfoList.isEmpty()) { idIndex = userInfoList.get(userInfoList.size() - 1).getId(); } String answer; do { System.out.println("请录入用户name:"); String name = input.nextLine(); System.out.println("请录入用户pass:"); String pass = input.nextLine(); System.out.println("请录入用户age:"); String age = input.nextLine(); //新注册的用户信息 id到底是多少? 看之前有没有注册过 //没有注册过 writer.write(String.join("-", String.valueOf(++idIndex), name, pass, age));//1024 writer.write("\n"); writer.flush(); System.out.println("是否继续?y/n"); answer = input.nextLine(); } while (Objects.equals("y", answer)); System.out.println("注册结束"); }
4.3 高效字符输入流
1. 查询用户
public static List<UserInfo> findAllUser() throws IOException { //读取---> 查询所有的用户--->获得最后一个用户id //将每一行的记录装配成一个个的用户对象 //读取文件里面的每一行记录 read 输入流 字节输入流 字符输入流 //装饰基本字符流的功能 ----> 一次读取一行----> 高效字符流 BufferedReader //1. 底层有缓冲 8192个字符 //2. 方法更加好用 一次读取一行 List<UserInfo> userInfoList = new ArrayList<>(10); @Cleanup BufferedReader reader = new BufferedReader(new FileReader("src/userInfo.txt")); //String line = reader.readLine();//null 读取文件末尾 String line; while ((line = reader.readLine()) != null) { String[] infoArray = line.split("-"); UserInfo userInfo = new UserInfo(); userInfo.setId(Integer.parseInt(infoArray[0])); userInfo.setName(infoArray[1]); userInfo.setPass(infoArray[2]); userInfo.setAge(Integer.parseInt(infoArray[3])); userInfoList.add(userInfo); } return userInfoList; }
5. 其它流
1. 数据流(字节流)
读写字面量类型(基本数据类型+String)==的数据。
必须先写。 再去读。
public class DataDemo { public static void main(String[] args) { //注册用户: id-name-pass-age FileWriter.write(String str) //读取一个用户信息: BufferedReader.readLine() split() Integer.parseInt() try { testRead(); } catch (FileNotFoundException e) { e.printStackTrace(); } } //对于数据流 场景: writeUTF/readUTF //网络数据传输: 传输的都是字节内容 使用writeUTF/readUTF避免出现乱码的问题、 private static void testWrite() throws FileNotFoundException { //1.创建数据输出流对象 DataOutputStream dataOutput = new DataOutputStream(new FileOutputStream("src/b.txt")); try (dataOutput) { dataOutput.writeInt(1001); dataOutput.writeUTF("张三"); dataOutput.writeDouble(89999.567); dataOutput.writeByte(20); dataOutput.writeBoolean(true); } catch (IOException e) { e.printStackTrace(); } } private static void testRead() throws FileNotFoundException { //1.创建数据输入流对象 DataInputStream dataInputStream = new DataInputStream(new FileInputStream("src/b.txt")); User user = new User(); try (dataInputStream) { user.setId(dataInputStream.readInt());//4 user.setName(dataInputStream.readUTF()); user.setBalance(dataInputStream.readDouble()); user.setAge(dataInputStream.readByte()); System.out.println(dataInputStream.readBoolean()); System.out.println(user); //java.io.EOFException end of file } catch (IOException e) { e.printStackTrace(); } } private static void testRead1() throws FileNotFoundException { //弊端: 每读一个数据 都要进行类型的强制转换 BufferedReader reader = new BufferedReader(new FileReader("src/user.txt")); User user = new User(); try (reader) { String id = reader.readLine(); String name = reader.readLine(); String balance = reader.readLine(); String age = reader.readLine(); user.setId(Integer.parseInt(id)); user.setName(name); user.setAge(Byte.parseByte(age)); user.setBalance(Double.parseDouble(balance)); System.out.println(user); } catch (IOException e) { e.printStackTrace(); } } }
2. 对象流(字节流)
读写对象。 必须先写。 再去读。
ObjectInputStream(反序列化流-----> 读取文件里面对象)
ObjectOutputStream(序列化流---> 将对象写入文件)
void writeObject(Object obj) Object readObject()
2.1 写对象
private static void demo1() { //对于数据流 弊端: 读取多次 获得每个属性的信息 赋值多次 //直接读写一个完整的对象信息? //对象流也是在数据流基础之上 丰富了一些功能。除了可以操作字面量类型 还可以操作对象类型。 try { ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream("src/a.ini")); Student student = new Student(); student.setId(1); student.setName("张三"); student.setAge((byte) 20); student.setBalance(100000.123); objectOutput.writeObject(student); objectOutput.close(); } catch (IOException e) { e.printStackTrace(); } }
没有支持序列化异常。
java.io.NotSerializableException: com.lisa.io.Student at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1185) at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:349) at com.lisa.io.ObjectDemo.demo1(ObjectDemo.java:32) at com.lisa.io.ObjectDemo.main(ObjectDemo.java:18)
解决
//Serializable是一个标记接口 jvm可以将这个类型的对象 写入文件中 @Data public class Student implements Serializable { private Integer id; private String name; private double balance; private byte age; }
2.2 读对象
private static void demo2() { //读取文件里面的对象 try { ObjectInput objectInput = new ObjectInputStream(new FileInputStream("src/a.ini")); Student student = (Student) objectInput.readObject(); System.out.println("读取到的文件对象:" + student); objectInput.close(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { //jvm里面找不到这个类型的class文件 //1.1 java文件没有被编译 //1.2 可能没有引入第三方的类库 e.printStackTrace(); } }
2.3 问题
在读取对象中: 1.1 读取的对象是否与原对象一致? 不一致 不一致? 对象的创建有没有执行构造? 没有执行构造 jvm自身的规则 1.2 读取的对象的属性数据是否与源对象一致? 一样的 1.3 任意一个对象的数据改变 是否会影响其他对象? 不影响的 类似深克隆
只要修改的java程序代码 在没有重新写的前提下 直接读取对象
java.io.InvalidClassException: com.lisa.io.Student; local class incompatible: stream classdesc serialVersionUID = -5811316772388944105, local class serialVersionUID = -7066445612577640178 at java.base/java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:689) at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2025) at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1875) at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2206) at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1692) at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:499) at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:457) at com.lisa.io.ObjectDemo.demo2(ObjectDemo.java:25) at com.lisa.io.ObjectDemo.main(ObjectDemo.java:18)
原因: Stream里面class文件的版本与本地编译出来的版本不一致
解决方案:
1.修改一次 重新write一次 重新read (很麻烦 生产阶段 不能这么做)
2.固定一个class文件的版本号id
//Serializable是一个标记接口 jvm可以将这个类型的对象 写入文件中 //修改java文件 都需要重新编译新的class 产生新的class文件的版本号 serialVersionUID 唯一的 //在正常开发中: 记住一点 封装数据类上面 一定要实现 Serializable 并提供版本id //类实现 Serializable接口在开发中 肯定是没有问题的(Redis/RMI) //不实现Serializable 程序就有可能出现问题。
3. 转换流(字符流)
字节流 和字符流之间的相互转换。
3.1 字节转字符
将网络上一些小说章节内容 下载到本地。----->将小说数据写入磁盘文件中,
写: 输出流 字节输出流 OutputStream 字符输出流 Writer 一次读取一行 一次写一行 BufferedReader BufferedWriter/FileWriter
private static void downloadNovel() throws IOException { String novelPath = "https://read.qidian.com/chapter/WS49qjrNDI7v7_WH3wfEdQ2/uNA4HnHXD0P6ItTi_ILQ7A2/"; novelPath = "https://read.qidian.com/chapter/OD1kAAaaLgMKgXB091LLaA2/eGiW7wfkoWTM5j8_3RRvhw2/"; //novelPath = "http://book.zongheng.com/chapter/1099697/64049078.html"; //网络上的所有的数据都是字节流。 //获得万维网的资源指针。有网。 URL--->万维网资源指针。 //URL url = new URL(novelPath);//读取网络资源 InputStream //InputStream inputStream = url.openStream();//网络的数据都在inputStream //在读取的功能里面: 程序本应该使用字节流进行读取 //但是我们想使用字符流进行高效读取。 //转换: 字节流读取转换成字符流读取 //高效字符流实现读写功能 BufferedReader reader = new BufferedReader(new InputStreamReader(new URL(novelPath).openStream(), StandardCharsets.UTF_8)); BufferedWriter writer = new BufferedWriter(new FileWriter("src/novel1.txt")); try (reader; writer) { //循环读写 String line; while ((line = reader.readLine()) != null) { //System.out.println(line); if (line.contains("class=\"read-content j_readContent\"")) { reader.readLine(); reader.readLine(); line = reader.readLine(); writer.write(line.replaceAll("<p>","\n")); break; } } } System.out.println("下载成功"); }
3.2 字符转字节
OutputStreamWriter
private static void demo1() throws FileNotFoundException { //读取指定文件的数据 在控制台打印输出---->将数据写在控制台 //read 输入流 字节输入流 字符输入流Reader BufferedReader //write 输出流 BufferedReader reader = new BufferedReader(new FileReader("src/novel.txt")); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out)); //底层实现: 将数据打印在控制台---->System.out 字节输出流 try (reader;writer) { String line; while ((line = reader.readLine()) != null) { writer.write(line);//字符输出流写 转成底层的字节输出流 writer.newLine(); } } catch (IOException e) { e.printStackTrace(); } }
4. Properties
属性集。 属性名称----属性值。 key---value
核心作用: 读取核心资源配置文件的数据。
//配置文件: properties xml yml/yaml private static void demo4() { Properties properties = new Properties(); try { //加载文件的数据 properties.load(new FileInputStream("src/user.txt")); } catch (IOException e) { e.printStackTrace(); } System.out.println(properties); String name = properties.getProperty("name"); System.out.println(name); //乱码了---->解码 手动解码 String String decodeName = new String(name.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); System.out.println(decodeName); }
private static void demo5() { Properties properties = new Properties(); try { //加载文件的数据 properties.load(new FileInputStream("src/user.properties")); } catch (IOException e) { e.printStackTrace(); } System.out.println(properties); }
private static void demo6() { Properties properties = new Properties(); properties.setProperty("id", "1001"); properties.setProperty("score", "90"); properties.setProperty("studentName", "李雷"); //将properties里面的数据写入文件中 try { properties.store(new FileOutputStream("src/student.properties"),"哈哈哈哈哈"); } catch (IOException e) { e.printStackTrace(); } }
6. 单例模式问题
一个进程里面只有1个类对象。
public class Admin implements Cloneable, Serializable { private static final long serialVersionUID = 1486187929375171608L; private Admin() { } private static final Admin admin = new Admin(); public static Admin getInstance() { return admin; } public Object readResolve() { return admin; } @Override public Admin clone() { /*Admin admin = null; try { admin = (Admin) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return admin;*/ return this; } }
public static void main(String[] args) { //保证admin的单例 Admin admin = Admin.getInstance(); System.out.println("原对象:" + admin); /*Admin cloneAdmin = admin.clone(); System.out.println("克隆的对象:" + cloneAdmin);*/ //序列化----> ObjectOutputStream String path = "src/aa.txt"; try { ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream(path)); objectOutput.writeObject(admin); objectOutput.close(); } catch (IOException e) { e.printStackTrace(); } //反序列化: ObjectInputStream try { ObjectInputStream objectInput = new ObjectInputStream(new FileInputStream(path)); Admin readAdmin = (Admin) objectInput.readObject(); System.out.println("读取的对象:" + readAdmin); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } //单例的最佳实现方案: 枚举类 //获得对象方式: //1. new //2. 克隆: 打破单例 避免打破单例: return this; //3. 序列化: 打破单例 readResolve() //4. 反射 }
7.课堂练习
1. File类
public class FileDemo1 { public static void main(String[] args) { demo6(); } private static void demo6() { System.out.println(File.separator); //路径盘符分隔符 (不同系统不同) System.out.println(File.separatorChar); //路径盘符分隔符(不同系统不同) System.out.println(File.pathSeparator); //路径分隔符(不同系统不同) System.out.println(File.pathSeparatorChar); //路径分隔符(不同系统不同) } private static void demo5() { //重命名 file.renameTo File file = new File("src"+File.separator+"demo.txt"); //File.separator 路径盘符分隔符\ file.renameTo(new File("src/demo.txt")) //若路径名都不同,还要对指定文件重命名,相当于剪切,粘贴,重命名三合一 System.out.println(file.renameTo(new File("src/demo/a/hello.txt"))); } private static void demo4() { //目录不存在 创建目录 File directory = new File("src", "demo/a/b"); //directory.mkdir() 创建1级目录 /*if (!directory.exists()) { System.out.println(directory.mkdirs()); //创建多级目录 }*/ //删除目录 System.out.println(directory.delete());//删除文件 删除目录(里面没有资源) 若b里没资源,只能删了b,删不到demo,连a都删不了 } private static void demo3() { //File代表是目录时的操作 //1.创建目录对象 //File directory = new File("E:\\test\\a\\b"); //File无参的构造创建对象 File directory = new File("E:\\test\\a", "b"); //File有参的构造创建对象(父级路径,子级路径) System.out.println(directory.getName()); System.out.println(directory.getAbsolutePath()); System.out.println(directory.getPath()); System.out.println(directory.isHidden()); System.out.println(directory.exists()); System.out.println(directory.isDirectory()); //目录的名称,相对绝对路径,是否隐藏,是否存在,是否为目录。 } private static void demo2() { //文件不存在 创建文件 File file = new File("src/com/javasm/io/b.txt"); try { if (!file.exists()) { System.out.println(file.createNewFile()); //创建文件 } } catch (IOException e) { e.printStackTrace(); } System.out.println("文件名称:" + file.getName()); System.out.println("文件绝对路径:" + file.getAbsolutePath()); System.out.println("文件相对路径:" + file.getPath()); System.out.println("文件大小:" + file.length()); //文件内容大小(以字节大小表示) System.out.println("上一次操作文件的时间:" + file.lastModified()); //时间的毫秒数 System.out.println("上一次操作文件的时间:" + new Date(file.lastModified())); } private static void demo1() { //File类代表文件时的操作 //1.创建File对象---->磁盘里面文件 File file = new File("E:\\test\\a.txt"); //2.获得File对象的一些基本的属性的信息 System.out.println("文件名称:" + file.getName()); //路径: 相对路径 + 绝对路径 //绝对路径: E:\test\a.txt http://www.baidu.com http://127.0.0.1:8080/demo/a.html //相对路径: a/b/c/a.txt 目前项目/相对于项目部署服务器路径 System.out.println("文件绝对路径:" + file.getAbsolutePath()); System.out.println("文件相对路径:" + file.getPath()); System.out.println("文件大小:" + file.length());//文件内容大小 字节 System.out.println("上一次操作文件的时间:" + file.lastModified());//时间的毫秒数 System.out.println("上一次操作文件的时间:" + new Date(file.lastModified()));//时间的毫秒数 转换成 年月日的时间数据 Date //3.对File进行一些判断 System.out.println(file.exists()); //判断文件是否存在 System.out.println(file.isFile()); //判断file对象是否是文件 //4.操作文件的权限 System.out.println(file.setReadOnly()); //设置文件只读 //5.删除文件 System.out.println(file.delete()); //删除磁盘文件 } }
2. File类案例
展示指定父级目录下的所有的子级的资源。
拿到父级目录下的所有子级资源
//流程:1.创建目录对象 2.获得父级路径下面的1级资源(可能为目录,也可能为文件) 3.若2的一级资源为目录,拿二级资源 ...(递归) public class FindChildDemo { public static void main(String[] args) { String parentDirectoryPath = "E:\\workspace\\one\\day19"; //父级路径 //要展示day19下的所有资源,展示效果 // |-.idea // ||-.libraries //... // |-.out // |-.src //|-.day19.iml File directory = new File(parentDirectoryPath); listChild(directory,"|-"); } private static void listChild(File directory,String s) { //directory为父级路径,该路径是活的,在3中就是2的一级目录 //获得指定父级目录下的一级资源 两种方法 //String[] list = directory.list(); //获得指定目录下的所有子级资源名称(文件名称和目录名称) File[] childArray = directory.listFiles(); //获得指定目录下的所有子级资源对象(文件对象和目录对象),该方法还可拿其下面的资源 for (File child : childArray) { System.out.println(s+child.getName()); //判断子级是否为目录,若为目录,继续往下拿 if (child.isDirectory()){ //再去获得子级资源,用递归(自己调自己所在的方法逻辑) listChild(child,"|"+s); //每递归一次就加一次||- } } } }
拿到父级目录下的所有java文件
//过滤掉不符合条件的一些资源 public class FindChildDemo { public static void main(String[] args) { String parentDirectoryPath = "E:\\workspace\\one\\day19"; File directory = new File(parentDirectoryPath); listChild(directory,"|-"); } private static void listChild(File directory,String s) { File[] childArray = directory.listFiles(); for (File child : childArray) { String childName = child.getName(); if (child.isDirectory()){ //System.out.println(s+childName); //这行可不要,但输出的java文件可能并不知道出自哪个目录,层级会乱 listChild(child,"|"+s); }else{ //这里是文件 if (childName.endsWith("java")){ System.out.println(s+childName); } } } } }
用list去做:
展示指定父级目录下的所有的子级的资源
public class FindChildDemo { public static void main(String[] args) { String parentDirectoryPath = "E:\\workspace\\one\\day19"; File directory = new File(parentDirectoryPath); listChild(directory,"|-"); } private static void listChild(File directory,String s) { //这里用list去做 String[] nameArray = directory.list(); for (String childName : nameArray) { File child = new File(directory,childName); //这里需要带两个参的构造,将父级路径传入 File里只能传路径 System.out.println(s+childName); if(child.isDirectory()){ listChild(child,"|-"+s); } } } }
3.InputStream流(输入流)-----对应读read
class MyClass implements Closeable { @Override public void close() throws IOException { System.out.println("我被关闭了......"); } } public class InputStreamDemo { public static void main(String[] args) { } private static void demo6() throws FileNotFoundException { FileInputStream inputStream = new FileInputStream("src/info.txt"); try (inputStream) { byte[] bytes = new byte[5]; //public int read(byte b[], int off, int len) //read带三个参数的 //off:指定的索引位置存储字节内容 //len: 存储字节个数 int result = inputStream.read(bytes, 0, bytes.length); System.out.println(Arrays.toString(bytes)); System.out.println(result); } catch (IOException e) { e.printStackTrace(); } } private static void demo5() throws FileNotFoundException { //对于输入流而言: read 文件必须要存在的 FileInputStream inputStream = new FileInputStream("src\\com\\javasm\\io\\InputStreamDemo.java"); try (inputStream) { //读取数据 byte[] bytes = new byte[1024]; //一般都是1024的整数倍,理论上来说,空间越大,速度越快 //int result = inputStream.read(bytes); //read参数为字节数组 //一次读取1024个字节内容 读取到的字节的数据都存储到了bytes数组中 //result: 维护读取到的有效的字节个数 //有效的字节的数据是多少个? 不一定是length //System.out.println(result); //System.out.println(Arrays.toString(bytes)); //1.循环遍历字节数组 将每一个字节转换成char result次 //2.将字节数组转换成字符串的数据(看不懂的数据转换成看的懂的字符串) //String s = new String(bytes, 0, result); //System.out.println(s); //循环读取指定的文件的数据 int len; while ((len = inputStream.read(bytes)) != -1) { //还未读到文件末尾 System.out.print(new String(bytes, 0, len)); } } catch (IOException e) { e.printStackTrace(); } } private static void demo4() throws FileNotFoundException { //抛异常 InputStream inputStream = new FileInputStream("src/info.txt"); MyClass myClass = new MyClass(); try (inputStream; myClass;) { //1.7+之后,可在小括号里加多个需要释放资源的对象 inputStream.read(); } catch (IOException e) { e.printStackTrace(); } } private static void demo3() { //1.7+ 提供了一种更加优雅的方式"释放流对象",会自动调用close //try....with...resources 资源释放就不需要编写了 不需要finally try ( //try()里放的是需要释放资源的对象,要求这些对象必须实现Closeable InputStream inputStream = new FileInputStream("src/info.txt"); MyClass myClass = new MyClass(); //也实现了Closeable接口 ) { System.out.println(inputStream.read()); } catch (IOException e) { e.printStackTrace(); } //不再需要finally释放资源,已自动调用close //如何证明close被自动关闭 让MyClass实现Closeable接口,并重写close方法。第一次运行try里不加MyClass myClass = new MyClass(); //的代码,然后在try里加该代码,若真的关闭了,自然会走MyClass重写的方法 } private static void demo2() { InputStream inputStream = null; try { //1.创建字节输入流对象 inputStream = new FileInputStream("src/info.txt"); //文件不存在,无法读取 System.out.println(inputStream.read()); } catch (IOException e) { e.printStackTrace(); } finally { try { if (inputStream != null) inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } private static void demo1() { //读取指定文件里面的数据----> 不要操作中文 read(读)----inputStream输入流 String filePath = "src/info.txt"; //读取 InputStream inputStream = null; try { //1.创建字节输入流对象 并获得文件字节数据 inputStream = new FileInputStream(filePath); //多态创建 //2.读取流里面字节数据 /*int data = inputStream.read(); //无参的,一次读一个字节,返一个字节内容 读完所有的字节数据,读到文件末尾,返-1 //System.out.println("读取到的第一个字节:" + (char)data); //循环去读 while (data != -1) { System.out.print((char) data); data = inputStream.read(); }*/ int result; while ((result = inputStream.read()) != -1) { System.out.print((char) result); } } catch (IOException e) { e.printStackTrace(); } finally { //释放资源 try { if (inputStream != null) inputStream.close(); //不为空关闭 } catch (IOException e) { e.printStackTrace(); } } } }
4.OutputStream流(输出流)-----对应写write
只要调close,就会提前flush()。 public class OutputStreamDemo { public static void main(String[] args) throws FileNotFoundException { //src/a.txt //对于输出流 文件不存在? 是否会报错? 不会报错 文件会自动创建(File.createNewFile()) //一定要保证父级目录要存在 src/demo/a.txt 连父级目录demo都没有,则出错。不可能连父级目录也创建出来 //读写内容: 出现乱码 编码格式不符 //字节流读取字符数据,仅对于汉字,可能会出现乱码。 OutputStream outputStream = new FileOutputStream("src/a.txt", true); //不加true,默认覆盖。加true,在文件末尾添加 try (outputStream) { outputStream.write(97); //a outputStream.write("张三".getBytes()); outputStream.write("97".getBytes()); outputStream.write('\n'); outputStream.write("我们".getBytes(Charset.forName("UTF-8"))); //在指定格式下编码 //outputStream.write("世界那么大".getBytes(), 0, 5); } catch (IOException e) { e.printStackTrace(); } } }
5. 字节流案例--文件上传
模拟用户上传头像
//流程:1.告知文件在哪?--上传文件路径 2.读取用户传的数据,写入服务器中的一个文件里(要保证文件名称唯一) public class UploadImageDemo { private static final String PARENT_DIRECTORY = "upload/user/"; //假设要将文件传到day19里的upload文件夹里的user文件夹里 public static String upload(String sourceFilePath) throws FileNotFoundException { //返回值是上传成功后的文件路径 形参是告知的要上传的文件路径 Objects.requireNonNull(sourceFilePath); //文件很大,循环读写文件数据 //1.读取源文件数据 InputStream //2.写入目标文件中 OutputStream InputStream inputStream = new FileInputStream(sourceFilePath); //获得目标文件名称 String fileName = sourceFilePath.substring(sourceFilePath.lastIndexOf(File.separator) + 1); //获得源文件名称,最后\之后的数据 OutputStream outputStream = new FileOutputStream(PARENT_DIRECTORY+fileName); //目标文件路径 try (inputStream;outputStream){ //循环读写数据 int len; //有效字节数 while ((len=inputStream.read())!=-1){ outputStream.write(len); } } catch (IOException e) { e.printStackTrace(); } System.out.println("文件上传成功"); return PARENT_DIRECTORY+fileName; //PARENT_DIRECTORY+文件名+后缀 } public static void main(String[] args) throws FileNotFoundException { String path = "E:\\io\\tom.jpg"; //要上传的文件路径 System.out.println(upload(path)); //传过去要上传路径,返回上传后的路径。(回显图片) } }
不同用户上传重名文件时:
public class UploadImageDemo { private static final String PARENT_DIRECTORY = "upload/user/"; public static String upload(String sourceFilePath) throws FileNotFoundException { Objects.requireNonNull(sourceFilePath); InputStream inputStream = new FileInputStream(sourceFilePath); //String fileName = sourceFilePath.substring(sourceFilePath.lastIndexOf(File.separator) + 1); //保证名称唯一性,用UUID(128位的无符号的16进制的字符串,几乎不可能重复),一般不要其中的- String extension = sourceFilePath.substring(sourceFilePath.lastIndexOf(".")); String targetFileName = UUID.randomUUID().toString().replaceAll("-","")+extension; OutputStream outputStream = new FileOutputStream(PARENT_DIRECTORY+targetFileName); try (inputStream;outputStream){ int len; while ((len=inputStream.read())!=-1){ outputStream.write(len); } } catch (IOException e) { e.printStackTrace(); } System.out.println("文件上传成功"); return PARENT_DIRECTORY+targetFileName; } public static void main(String[] args) throws FileNotFoundException { //String path = "E:\\io\\tom.jpg"; String path1 = "D:\\io\\tom.jpg"; //另一个用户又上传了一个同名的图片 System.out.println(upload(path1)); } }
若上传大量文件,将文件传到多个目录中:
public class UploadImageDemo { private static final String PARENT_DIRECTORY = "upload/user/"; public static String upload(String sourceFilePath) throws FileNotFoundException { Objects.requireNonNull(sourceFilePath); InputStream inputStream = new FileInputStream(sourceFilePath); //用户量增加,解决所有文件都在同一个目录的问题,用分目录存储图片 //一般以时间命名目录 //动态创建目录,目录名以当前时间为目录名称 2022-03-07这样的格式 String curDate = LocalDate.now().toString(); //当前时间 //判断目录是否存在 File directory = new File(PARENT_DIRECTORY,curDate); if(!directory.exists()){ directory.mkdirs(); //若不存在,创建目录 } String extension = sourceFilePath.substring(sourceFilePath.lastIndexOf(".")); String targetFileName = UUID.randomUUID().toString().replaceAll("-","")+extension; OutputStream outputStream = new FileOutputStream(new File(directory,targetFileName)); //这里也变动 try (inputStream;outputStream){ int len; while ((len=inputStream.read())!=-1){ outputStream.write(len); } } catch (IOException e) { e.printStackTrace(); } System.out.println("文件上传成功"); return PARENT_DIRECTORY+curDate+"/"+targetFileName; //多了一级目录 } public static void main(String[] args) throws FileNotFoundException { //String path = "E:\\io\\tom.jpg"; String path1 = "D:\\io\\tom.jpg"; System.out.println(upload(path1)); } }
若用户量更多,以后还有目录打散(利用哈希表)的方法。
若上传文件很大:
public class UploadImageDemo { private static final String PARENT_DIRECTORY = "upload/user/"; public static String upload(String sourceFilePath) throws FileNotFoundException { Objects.requireNonNull(sourceFilePath); InputStream inputStream = new FileInputStream(sourceFilePath); String curDate = LocalDate.now().toString(); File directory = new File(PARENT_DIRECTORY,curDate); if(!directory.exists()){ directory.mkdirs(); } String extension = sourceFilePath.substring(sourceFilePath.lastIndexOf(".")); String targetFileName = UUID.randomUUID().toString().replaceAll("-","")+extension; OutputStream outputStream = new FileOutputStream(new File(directory,targetFileName)); try (inputStream;outputStream){ //目前是读一个字节就写一个字节,很慢 //优化,来个缓冲,就是byte[] (再大的是多任务,多线程完成的) byte[] bytes = new byte[1024]; //一次读1024个字节放到字节数组中 int len; while ((len=inputStream.read(bytes))!=-1){ outputStream.write(bytes,0,len); //这里用带三个参的,给有效字节,若用write(byte[]),写的东西肯定大于源文件 } } catch (IOException e) { e.printStackTrace(); } System.out.println("文件上传成功"); return PARENT_DIRECTORY+curDate+"/"+targetFileName; } public static void main(String[] args) throws FileNotFoundException { long begin = System.nanoTime(); //开始时间,纳秒 //String path = "E:\\io\\tom.jpg"; String path1 = "D:\\io\\tom.jpg"; System.out.println(upload(path1)); long end = System.nanoTime(); System.out.println(end-begin); //计算上传时间 } }
大文件,不用缓冲,用高效流(底层自带缓冲,8192)替换也可以:
public class BufferDemo { public static void main(String[] args) throws FileNotFoundException { //模拟文件上传: long begin = System.currentTimeMillis(); String path = "E:\\tools\\IntelliJ IDEA\\ideaIU-2020.1.1.exe"; BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(path)); BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream("D:\\a.exe")); try (inputStream; outputStream) { //循环读写 int len; byte[] bytes = new byte[1024 * 5]; //底层自带数组,若自定义数组 //自定义的缓冲 >底层的缓冲 就不使用底层的缓冲 //自定义的缓冲 < 底层的缓冲 8192 while ((len = inputStream.read(bytes)) != -1) { //一次读取一个字节 一次写一次字节 outputStream.write(bytes,0,len); //将1个字节写入缓冲,当缓冲满的时候,调用底层的flush,写到文件 } } catch (IOException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println(end - begin); //8 } }
6. 字符流
public class ReaderDemo { public static void main(String[] args) { try { demo2(); } catch (IOException e) { e.printStackTrace(); } } private static void demo2() throws IOException { //创建字符输出流对象 @Cleanup //close注解 FileWriter writer = new FileWriter("src/a.txt"); writer.write('a'); writer.write('\n'); writer.write('好'); writer.write('\n'); writer.write(97); writer.write("hello world 你好 世界"); } private static void demo1() throws FileNotFoundException { //1.创建字符输入流对象 Reader Reader reader = new FileReader("src/info.txt"); try (reader) { //2.循环读取文件数据 //reader.read(); //一次读取一个字符 -1 /*int data = reader.read(); System.out.println((char) data);*/ /*int len; while ((len=reader.read())!=-1){ System.out.print((char)len); }*/ //reader.read(char[] chs); 一次读取length个字符数据 返回值: 有效的字符个数 /* char[] chars = new char[20]; int len = reader.read(chars); //将chars数组转字符串 System.out.println(new String(chars, 0, len));*/ /*char[] chars = new char[20]; int len; while ((len=reader.read(chars))!=-1){ System.out.print(new String(chars, 0, len)); }*/ } catch (IOException e) { e.printStackTrace(); } } }
7. 字符流案例
模拟用户注册,持久化保存
public class UserDemo { public static void main(String[] args) throws IOException { userRegister(); } private static int idIndex = 1001; private static void userRegister() throws IOException { //模拟用户注册:持久化保存用户信息 //将每个用户信息保存到userInfo.txt id-name-pass-age 一行存一个信息 @Cleanup FileWriter writer = new FileWriter("src/userInfo.txt",true); //将用户信息写入文件中 @Cleanup Scanner input = new Scanner(System.in); String answer; do { System.out.println("请录入用户name"); String name = input.nextLine(); System.out.println("请录入用户pass"); String pass = input.nextLine(); System.out.println("请录入用户age"); String age = input.nextLine(); writer.write(String.join("-",String.valueOf(idIndex++),name,pass,age)); writer.write("\n"); //换行 writer.flush(); //写一次看一次 System.out.println("是否继续?y/n"); answer = input.nextLine(); } while (Objects.equals("y",answer)); System.out.println("注册结束"); } }
多个用户注册结束,再次注册,id会从1001开始,如何解决?
@Data class UserInfo{ private Integer id; private String name; private String pass; private Integer age; } public class UserDemo { public static void main(String[] args) throws IOException { userRegister(); } public static List<UserInfo> findAllUser() throws IOException { //查询所有用户,获得最后一个用户的id //将每一行记录装配成一个个用户对象(读取文件里的每一行记录,分割,再装配) List<UserInfo> userInfoList = new ArrayList<>(10); @Cleanup BufferedReader reader = new BufferedReader(new FileReader("src/userInfo.txt")); //String line = reader.readLine(); //读到末尾为null String line; while ((line=reader.readLine())!=null){ String[] infoArray = line.split("-"); UserInfo userInfo = new UserInfo(); userInfo.setId(Integer.parseInt(infoArray[0])); userInfo.setName(infoArray[1]); userInfo.setPass(infoArray[2]); userInfo.setAge(Integer.parseInt(infoArray[3])); userInfoList.add(userInfo); } return userInfoList; } private static int idIndex = 1001; private static void userRegister() throws IOException { //模拟用户注册:持久化保存用户信息 //将每个用户信息保存到userInfo.txt id-name-pass-age 一行存一个信息 @Cleanup FileWriter writer = new FileWriter("src/userInfo.txt",true); //将用户信息写入文件中 List<UserInfo> userInfoList = findAllUser(); if(!userInfoList.isEmpty()){ idIndex=userInfoList.get(userInfoList.size()-1).getId(); } @Cleanup Scanner input = new Scanner(System.in); String answer; do { System.out.println("请录入用户name"); String name = input.nextLine(); System.out.println("请录入用户pass"); String pass = input.nextLine(); System.out.println("请录入用户age"); String age = input.nextLine(); //新注册用户信息id,要看之前注册过没有 writer.write(String.join("-",String.valueOf(++idIndex),name,pass,age)); writer.write("\n"); //换行 writer.flush(); //写一次看一次 System.out.println("是否继续?y/n"); answer = input.nextLine(); } while (Objects.equals("y",answer)); System.out.println("注册结束"); } }
8. 数据流
//注册用户: id-name-pass-age 每次拿,需要读一行,然后分割,再转类型 //如何直接拿,不需要转型 用数据流(也是字节流,但一般的字节流操作汉字会出现乱码) @Data class User { private Integer id; private String name; private double balance; private byte age; } public class DataDemo { public static void main(String[] args) { //注册用户: id-name-pass-age FileWriter.write(String str) //读取一个用户信息: BufferedReader.readLine() split() Integer.parseInt() try { testRead(); } catch (FileNotFoundException e) { e.printStackTrace(); } } //对于数据流 最常用的场景: writeUTF/readUTF (写一个汉字,读一个汉字) //何时用它? 网络数据传输: 传输的都是字节内容 使用writeUTF/readUTF避免出现乱码的问题 private static void testWrite() throws FileNotFoundException { //1.创建数据输出流对象 将数据从任意类型转为一系列字节,并写入二进制流 这是写给dataInputStream看的 DataOutputStream dataOutput = new DataOutputStream(new FileOutputStream("src/b.txt")); try (dataOutput) { dataOutput.writeInt(1001); dataOutput.writeUTF("张三"); dataOutput.writeDouble(89999.567); dataOutput.writeByte(20); dataOutput.writeBoolean(true); } catch (IOException e) { e.printStackTrace(); } } private static void testRead() throws FileNotFoundException { //1.创建数据输入流对象 DataInputStream dataInputStream = new DataInputStream(new FileInputStream("src/b.txt")); User user = new User(); //可以读,但前提要先写出来,才能读 人工写的它不认识 从二进制流里读数据 try (dataInputStream) { user.setId(dataInputStream.readInt()); //每次读4个字节 user.setName(dataInputStream.readUTF()); user.setBalance(dataInputStream.readDouble()); user.setAge(dataInputStream.readByte()); System.out.println(dataInputStream.readBoolean()); System.out.println(user); //未先写就读,报此异常 java.io.EOFException end of file 读到文件末尾也没发现这些东西 } catch (IOException e) { e.printStackTrace(); } } private static void testRead1() throws FileNotFoundException { //用以前的方法做 //弊端: 每读一个数据 都要进行类型的强制转换 BufferedReader reader = new BufferedReader(new FileReader("src/user.txt")); User user = new User(); try (reader) { String id = reader.readLine(); String name = reader.readLine(); String balance = reader.readLine(); String age = reader.readLine(); user.setId(Integer.parseInt(id)); user.setName(name); user.setAge(Byte.parseByte(age)); user.setBalance(Double.parseDouble(balance)); System.out.println(user); } catch (IOException e) { e.printStackTrace(); } } }
9. 对象流
//Serializable是一个标记接口 jvm可以将这个类型的对象写入文件中 //修改java文件都需要重新编译新的class,产生新的class文件的版本号serialVersionUID,版本号唯一的,通过它可找到class文件 //在正常开发中: 记住一点 封装数据类(用户类,商品类等)上面 一定要实现 Serializable 并提供版本id //类实现 Serializable接口在开发中 肯定是没有问题的(后期的Redis缓存/远程接口访问RMI) //不实现Serializable 程序就有可能出现问题。 @Setter @Getter @ToString public class Student implements Serializable { private static final long serialVersionUID = -1775089732470255694L; //版本号的id是自动生成的 编译工作是IDEA调javac做的,想生成id,告诉IDEA(让idea提醒我们自动生成id值) //setting--Editor--inspections--java--serialization issues--serializable class without ‘serialVersionUID’--选中 private Integer id; private String name; private double balance; private int age; private int num; public Student() { System.out.println("无参构造....."); } }
//对象流也要求先写才能读 public class ObjectDemo { public static void main(String[] args) { demo2(); } private static void demo2() { //读取文件里面的对象 try { ObjectInput objectInput = new ObjectInputStream(new FileInputStream("src/a.ini")); Student student = (Student) objectInput.readObject(); System.out.println("读取到的文件对象:" + student); //读取到的文件对象:com.lisa.io.Student@7d9d1a19 objectInput.close(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { //ClassNotFoundException 找不到字节码文件,也就是jvm里面找不到这个类型的class文件 //出现该异常有两种原因: //1.1 java文件没有被编译(类没有被编译) //1.2 可能没有引入第三方的类库(架包) e.printStackTrace(); } } private static void demo1() { //对于数据流 弊端: 读取多次 获得每个属性的信息 赋值多次 //对象流:直接读写一个完整的对象信息 //对象流也是在数据流基础之上丰富了一些功能。除了可以操作字面量类型 还可以操作对象类型。 try { ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream("src/a.ini")); //无参构造..... //写入的对象:com.lisa.io.Student@38082d64 Student student = new Student(); System.out.println("写入的对象:"+student); student.setId(1); student.setName("张三"); //student.setAge((byte) 20); student.setBalance(100000.123); objectOutput.writeObject(student); objectOutput.close(); } catch (IOException e) { e.printStackTrace(); } //只写完运行会报 NotSerializableException 没有支持序列化异常 //这里做的事情:将对象写入磁盘文件中。 对于jvm,若操作对象 这个对象类型必须支持序列化(序列化跟writeObject有关) //之前学的克隆: 克隆一个对象 也是jvm底层做的 要克隆的前提: 实现Cloneable接口 //类似的,想序列化student,必须让它支持序列化,告诉jvm,student可以被序列化,故student要实现Serializable接口 //序列化流就是ObjectOutputStream,将对象写入文件 //反序列化流就是ObjectinputStream,读取文件里的对象 } }
10. 转换流
字节转字符----将网络上的小说章节内容,下载到本地
字符转字节----打印输出
public class NovelDemo { public static void main(String[] args) { try { demo1(); } catch (FileNotFoundException e) { e.printStackTrace(); } } private static void demo1() throws FileNotFoundException { //需求:读取指定文件的数据,在控制台打印输出(将数据写在控制台) //读取是read 输入流 字节输入流 字符输入流 比较合适的是字符输入流,从中选BufferedReader //打印是write 输出流 对应的用Bufferedwriter BufferedReader reader = new BufferedReader(new FileReader("src/novel.txt")); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out)); //写到控制台上去,之前是new FileWriter //OutputStreamWriter字符转字节 //System.out.println("fasf") 为何调此命令可将内容打印到控制台,是字符串的println做的 //out是printStream,继承FilterOutputStream字节输出流 关键还是在out //底层实现: 将数据打印在控制台---->System.out 字节输出流 try (reader;writer) { String line; while ((line = reader.readLine()) != null) { //循环去读 writer.write(line); //字符输出流写,但其实底层是转成字节输出流 一次写一行 因为想输出到控制台得结合打印流完成 writer.newLine(); //换行 } } catch (IOException e) { e.printStackTrace(); } } private static void downloadNovel() throws IOException { //字节转字符场景:拿取网络资源,且和转换流相关的一般用BufferedReader和BufferedWriter 已成模板 String novelPath = "https://read.qidian.com/chapter/WS49qjrNDI7v7_WH3wfEdQ2/uNA4HnHXD0P6ItTi_ILQ7A2/"; //小说章节路径(肯定加密了) novelPath = "https://read.qidian.com/chapter/OD1kAAaaLgMKgXB091LLaA2/eGiW7wfkoWTM5j8_3RRvhw2/"; //novelPath = "http://book.zongheng.com/chapter/1099697/64049078.html"; //全加密,这个就没法下载 //网络上的所有的数据都是字节流。 //想获得万维网的资源指针。条件:1.有网。2.获得万维网资源指针URL(统一资源定位符),拿到该指针就能拿到网络上的数据 //URL url = new URL(novelPath); //url也是个类,在java.net 读取网络资源,用InputStream //InputStream inputStream = url.openStream(); //此时网络的数据都在inputStream //在读取的功能里面: 程序本应该使用字节流进行读取 但是我们想使用字符流进行高效读取 //转换: 字节流读取转换成字符流读取 //高效字符流实现读写功能 //BufferedReader reader = new BufferedReader(new FileReader(novelPath) FileReader是找磁盘文件,故不能这样写 BufferedReader reader = new BufferedReader(new InputStreamReader(new URL(novelPath).openStream(), StandardCharsets.UTF_8)); //new URL(novelPath).openStream()是字节流,需转字符流,同时指定编码格式(不能随便给,看人家编码格式给的什么) BufferedWriter writer = new BufferedWriter(new FileWriter("src/novel1.txt")); try (reader; writer) { //循环读写 String line; while ((line = reader.readLine()) != null) { //每次读一行 //System.out.println(line); if (line.contains("class=\"read-content j_readContent\"")) { //这是看着下载的模板进项修改,去除没用部分 reader.readLine(); reader.readLine(); line = reader.readLine(); writer.write(line.replaceAll("<p>", "\n")); break; } } } System.out.println("下载成功"); } }
11. Properties
一个能和IO结合使用的类,是hashTable的一个子类,称为属性集
一般存 属性名=属性值
和map方法一样,但一般不用
public class PropertiesDemo { public static void main(String[] args) { demo6(); } private static void demo6() { Properties properties = new Properties(); properties.setProperty("id", "1001"); properties.setProperty("score", "90"); properties.setProperty("studentName", "李雷"); //将properties里面的数据写入文件中 try { properties.store(new FileOutputStream("src/student.properties"),"哈哈哈哈哈"); //第二个参数是注释 } catch (IOException e) { e.printStackTrace(); } } private static void demo5() { Properties properties = new Properties(); try { //加载文件的数据 properties.load(new FileInputStream("src/user.properties")); } catch (IOException e) { e.printStackTrace(); } //加载properties文件,出现汉字也会乱码,此时改idea配置,改后不再乱 //setting--搜encoding--File Encodings--全改成utf-8 System.out.println(properties); } //配置文件后缀: properties xml yml/yaml private static void demo4() { //用properties读取demo3()的文件数据 Properties properties = new Properties(); try { //加载文件的数据 properties.load(new FileInputStream("src/user.txt")); } catch (IOException e) { e.printStackTrace(); } System.out.println(properties); String name = properties.getProperty("name"); //想要value,直接给key System.out.println(name); //将jim改成中文的李四,结果乱码了,此时要解码 手动解码,用String properties底层默认ISO_8859_1编码格式 String decodeName = new String(name.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); //转成UTF-8 System.out.println(decodeName); } private static void demo3() throws FileNotFoundException { //Properties:体现在读取核心配置文件的数据 //要求文件里面数据格式是固定的:key=value,且一行中只有一组数据 //假设一个配置文件里的内容如下: //id=1001 //name=jim //age=20 //如何获得id name age的数据 //读: 字节/字符输入流 Reader FileReader(一次读一个字符或字符数组,不合适) BufferedReader(读一行) BufferedReader reader = new BufferedReader(new FileReader("src/user.txt")); try (reader) { String line; while ((line = reader.readLine()) != null) { String[] array = line.split("="); System.out.println(array[0] + "---" + array[1]); } } catch (IOException e) { e.printStackTrace(); } } private static void demo2() { Properties properties = new Properties(); //一般推荐使用以下的方法 properties.setProperty("id", "1001"); //一般参数是String类型 properties.setProperty("name", "张三"); properties.setProperty("age", "20"); System.out.println(properties.getProperty("id")); System.out.println(properties.getProperty("name")); System.out.println(properties.getProperty("age")); //相比map集合方法: 类型转换的次数少一些 //但仍不是最重要的方法,要结合IO用 } private static void demo1() { //针对于Properties: 以下方法 都不用 //回顾以下map集合的方法 Properties properties = new Properties(); //存储数据 properties.put("id", 1001); properties.put("name", "张三"); properties.put("age", 20); //获得数据 /*int id = (int) properties.get("id"); String name = (String) properties.get("name"); int age = (int) properties.get("age"); System.out.println(id); System.out.println(name); System.out.println(age);*/ //遍历Properties里面所有的数据 //properties.forEach((k,v)-> System.out.println(k+"----"+v)); /*Set<Map.Entry<Object, Object>> entrySet = properties.entrySet(); for (Map.Entry<Object, Object> entry : entrySet) { System.out.println(entry.getKey() + "---" + entry.getValue()); }*/ } }
12. 单例模式问题
//单例模式是保证单实例的 public class Admin implements Cloneable, Serializable { private static final long serialVersionUID = 1486187929375171608L; private Admin() { //1.构造方法私有化 } private static final Admin admin = new Admin(); //饿汉,一上来就new public static Admin getInstance() { //提供对外访问的入口 return admin; } public Object readResolve() { return admin; } @Override public Admin clone() { /*Admin admin = null; try { admin = (Admin) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return admin;*/ return this; } }
public class Demo { public static void main(String[] args) { //目的:保证admin的单例 Admin admin = Admin.getInstance(); //获得一个实例 System.out.println("原对象:" + admin); /*Admin cloneAdmin = admin.clone(); //克隆一个对象,此时Admin要实现Cloneable接口,且重写clone方法 System.out.println("克隆的对象:" + cloneAdmin);*/ //产生了新的对象 //序列化----> ObjectOutputStream 写 String path = "src/aa.txt"; try { ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream(path)); objectOutput.writeObject(admin); objectOutput.close(); } catch (IOException e) { e.printStackTrace(); } //反序列化: ObjectInputStream 读 try { ObjectInputStream objectInput = new ObjectInputStream(new FileInputStream(path)); Admin readAdmin = (Admin) objectInput.readObject(); System.out.println("读取的对象:" + readAdmin); //又产生了新的对象 } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } //维护单例的最佳实现方案: 枚举类 //获得对象方式: //1. new //2. 克隆: 可以打破单例 为避免打破单例,在重写的克隆方法里写: return this/Admin; 就这一句话 //3. 序列化: 可以打破单例 为避免打破单例,需在单例模式里提供readResolve()方法,这个只能返Admin,不能用this //4. 反射,也能打破单例 } }
8.课后作业
1.使用IO技术,创建一个目录,然后复制一个文件到该目录
public class Exercise { public static void main(String[] args) { //将src/a.txt 复制到 exercise/xxx.txt File file1 = new File("src/a.txt"); //要复制的文件(源文件) File file2 = new File("src/b.txt"); //复制后的文件(目标文件) try { exercise1(file1, file2); } catch (IOException e) { e.printStackTrace(); } } private static void exercise1(File sourceFile, File targetFile) throws IOException { //高效字符输入流 BufferedReader reader = new BufferedReader(new FileReader(sourceFile)); //高效字符输出流 BufferedWriter writer = new BufferedWriter(new FileWriter(targetFile)); try (reader; writer) { String line; while ((line = reader.readLine()) != null) { writer.write(line); writer.newLine(); } } catch (IOException e) { e.printStackTrace(); } }
2.使用IO技术,开发出一个控制台的资源管理器!要求:从命令行输入一个路径!如果存在,将该目录下所有的文件和文件夹列举出来,如果不存在则输出不存在该路径。
public class Exercise { public static void main(String[] args) { String path = "E:\\workspace\\one\\day24"; File directory = new File(path); exercise2(directory, "|-"); } private static void exercise2(File directory, String s) { if (!directory.exists()) { System.out.println(directory + "路径不存在 无法查询子级"); return; } File[] files = directory.listFiles(); for (File child : files) { System.out.println(s + child.getName()); if (child.isDirectory()) { exercise2(child, "| " + s); } } } }
3.基于转换流,从控制台输入一些字符串,并将该类信息保存到日志文件”log.txt”中去
public class Exercise { public static void main(String[] args) { try{ exercise3(); }catch(IOException e){ e.printStackTrace(); } } private static void exercise3() throws IOException { //读取用户在控制台录入的数据 输入流 read BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); //in是个InputStream流 BufferedWriter writer = new BufferedWriter(new FileWriter("src/log.txt")); System.out.println("请录入一些数据:"); try (reader; writer) { //释放资源 while (true) { String line = reader.readLine(); //拿到录入的每行内容 if ("bye".equals(line)) { //假设最后是bye break; } writer.write(line); writer.newLine(); } } catch (IOException e) { e.printStackTrace(); } } }
4.从控制台进行输入用户名以及用户密码,判断是否登录成功!要求准确的用户名和密码存在配置文件中!
public class Exercise { public static void main(String[] args) { login(); } private static void login() { Scanner input = new Scanner(System.in); System.out.println("录入name:"); String name = input.nextLine(); System.out.println("录入pass:"); String pass = input.nextLine(); //获得配置文件里面的数据 Properties properties = new Properties(); try { properties.load(new FileInputStream("src/user.properties")); } catch (IOException e) { e.printStackTrace(); } String username = properties.getProperty("username"); String password = properties.getProperty("password"); if (!name.equals(username) || !pass.equals(password)) { System.out.println("登录失败"); input.close(); return; } System.out.println("登录成功,欢迎你:" + username); input.close(); } }
public class Exercise4 { public static void main(String[] args) { demo(); } private static void demo() { Properties properties = new Properties(); try { //properties.load(new FileInputStream("src/userinfo.properties")); 前期这样写 //后期这样写 properties.load(Exercise4.class.getClassLoader().getResourceAsStream("userinfo.properties")); } catch (IOException e) { e.printStackTrace(); } System.out.println(properties); } }
5.创建一个学生类,包含属性:学号、姓名、性别,包含show()方法用于显示学生的详细信息。
创建测试类,在控制台上显示添加学生信息,要求程序循环运行,并依次提示接收学生类的所有属性值,保存到学生对象中,再将学生对象保存到集合对象中,并提示“是否继续添加(y/n):”,如果选择“y”则继续添加,否则退出循环,并将保存学生数据的集合对象通过序列化保存到“student.dat”文件中。
实现从“student.dat”文件中反序列化保存学生数据的集合对象,并遍历打印输出学生信息。
class Student implements Serializable { private Integer id; private String name; @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + '}'; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } public class Exercise { public static void main(String[] args) { try { exercise4(); } catch (IOException e) { e.printStackTrace(); } } private static void exercise5() throws IOException, ClassNotFoundException { ObjectInput objectInput = new ObjectInputStream(new FileInputStream("src/student.dat")); List<Student> list = (List<Student>) objectInput.readObject(); list.forEach(System.out::println); objectInput.close(); } private static void exercise4() throws IOException { Scanner input = new Scanner(System.in); int idIndex = 1001; //id自增,只需录入name List<Student> studentList = new ArrayList<>(10); String s; do { System.out.println("录入name:"); String name = input.nextLine(); Student student = new Student(); student.setId(idIndex++); student.setName(name); studentList.add(student); System.out.println("是否继续添加学生信息?y/n"); s = input.nextLine(); } while (Objects.equals("y", s)); //将集合对象写入文件中---->序列化 ObjectOutputStream ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream("src/student.dat")); try(objectOutput){ objectOutput.writeObject(studentList); System.out.println("序列化成功"); }catch (IOException e){ e.printStackTrace(); } } }
class Student implements Serializable { //要加版本号,上个同理。 这里省略 private Integer id; private String name; @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + '}'; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } public class Exercise { public static void main(String[] args) { try { exercise4(); } catch (IOException e) { e.printStackTrace(); } } private static void exercise5() throws IOException, ClassNotFoundException { ObjectInput objectInput = new ObjectInputStream(new FileInputStream("src/student.dat")); Object object; while ((object = objectInput.readObject()) != null) { //readObject() 没有条件进行判断是否读取到末尾 ,故这里没意义 System.out.println(object); } //对于序列化而言: 只写一个 以及 只读一次 //若写入多次 写多个对象 用集合 //java.io.EOFException 当前线程就会终止 objectInput.close(); } private static void exercise4() throws IOException { Scanner input = new Scanner(System.in); int idIndex = 1001; ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream("src/student.dat")); String s; do { System.out.println("录入name:"); String name = input.nextLine(); Student student = new Student(); student.setId(idIndex++); student.setName(name); objectOutput.writeObject(student); System.out.println("是否继续添加学生信息?y/n"); s = input.nextLine(); } while (Objects.equals("y", s)); } }
6.已知文件a.txt文件中的内容为“AAbcdea22dferwplkCC321ou1”,请编写程序读取该文件内容,要求去掉重复字母(区分大小写字母)并按照自然排序顺序后输出到b.txt文件中。即b.txt文件内容应为”123ACabcdefklopruw”这样的顺序输出。
public class Exercise { public static void main(String[] args) { try { exercise6(); } catch (IOException e) { e.printStackTrace(); } } private static void exercise6() throws IOException { //1.读取log.txt文件内容 read 123ACabcdefklopruw 字符输入流 Reader List<String> list = new ArrayList<>(10); FileReader reader = new FileReader("src/log.txt"); //2.将读取到的每个字符 存储集合中 List Set---> TreeSet Map int len; while ((len = reader.read()) != -1) { list.add(String.valueOf((char) len)); } System.out.println(list); list = list.stream().sorted().distinct().collect(Collectors.toList()); System.out.println(list); //遍历list集合 将每个字符写入文件中 略 reader.close(); } }
7. 读取任意txt文件内容,并统计出这个文本中每个字符以及每个字符出现的次数, 并以以下格式: 字符=次数 持久化保存文件中。
public class Exercise { public static void main(String[] args) { try { exercise7(); } catch (IOException e) { e.printStackTrace(); } } private static void exercise7() throws IOException { //Map ---> Properties.store() 将内容直接存到文件里 Properties properties = new Properties(); FileReader reader = new FileReader("src/log.txt"); int len; while ((len = reader.read()) != -1) { String key = String.valueOf((char) len); String countStr = properties.getProperty(key); if (countStr == null) { countStr = "1"; } else { countStr = String.valueOf(Integer.parseInt(countStr) + 1); } properties.setProperty(key, countStr); } properties.store(new FileOutputStream("src/b.txt"), ""); reader.close(); } }
8.使用集合相关的功能,存储10个1-50(含50)的随机偶数元素,要求数字不能重复,添加完成后从大到小倒序遍历输出到控制台并使用IO流将集合中的元素按指定格式输出到当指定文件中,例如: 48,44,40,38,34,30,26……
public class Exercise { public static void main(String[] args) { try { exercise8(); } catch (IOException e) { e.printStackTrace(); } } private static void exercise8() throws IOException { ThreadLocalRandom random = ThreadLocalRandom.current(); //获得一个随机数对象 List<Integer> list = new ArrayList<>(10); for (int i = 0; i < 10; i++) { int num = random.nextInt(1, 51); if (num % 2 == 0) { list.add(num); } else { i--; } } list.sort(Comparator.comparing(Integer::intValue).reversed()); FileWriter writer = new FileWriter("src/b.txt"); for (Integer num : list) { writer.write(num.toString()); writer.write(","); //最后会多个逗号 } writer.close(); //List<Integer> integers = random.ints(10, 1, 51).boxed().collect(Collectors.toList()); 若啥都不管,只随机10个 } }
9.已知student_info.txt文件中有如下数据:(姓名-年龄-总分)
* 张三-21-98
* 李四-23-97
* 王五-25-100
* 赵六-15-100
* 孙七-19-93
运用IO技术获取将该文件中的数据分别封装成5个Student(姓名为String类型,年龄为int类型,总分为int类型 )对象存入集合中(需要自己定义Student类)
要求:根据学生的总分进行排序(降序),如果分数相同则比较年龄,年龄较大的排在前面。并显示排序之后的结果。
public class StudentInfo { private String name; private Integer age; private Integer score; @Override public String toString() { return "StudentInfo{" + "name='" + name + '\'' + ", age=" + age + ", score=" + score + '}'; } 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 Integer getScore() { return score; } public void setScore(Integer score) { this.score = score; } }
public class Exercise { public static void main(String[] args) { try { exercise9(); } catch (IOException e) { e.printStackTrace(); } } private static void exercise9() throws IOException { //5个Student(姓名为String类型,年龄为int类型,总分为int类型 )对象存入集合中 BufferedReader reader = new BufferedReader(new FileReader("src/b.txt")); String info; List<StudentInfo> list = new ArrayList<>(10); while ((info = reader.readLine()) != null) { String[] array = info.split("-"); StudentInfo studentInfo = new StudentInfo(); studentInfo.setName(array[0]); studentInfo.setAge(Integer.parseInt(array[1])); studentInfo.setScore(Integer.parseInt(array[2])); list.add(studentInfo); } //根据学生的总分进行排序(降序) list.sort(Comparator.comparing(StudentInfo::getScore).reversed().thenComparing(StudentInfo::getAge)); list.forEach(System.out::println); } }