一、文件

1.1 什么是文件

文件是保存数据的地方,最常见比如 word 文档,,txt 文件,excel文件,mp4文件等。它既可以保存一张图片、也可以保存视频、声音……

1.2 文件流

文件在程序中是以流的形式来操作的
IO 流 - 图1
流:数据在数据源(文件)和程序(内存)之间经历的路径
输入流:数据从数据源(文件)到程序(内存)的路径
输出流:数据从程序(内存)到数据源(文件)的路径

二、常用的文件操作

2.1 创建文件对象相关构造器和方法

相关方法
new File(String pathname) //根据路径构建一个 File对象
new File(File parent, String child) //根据父目录文件 + 子路径构建
new File(String parent, String child) //根据父目录 + 子路径构建

createNewFile() 创建新文件

应用案例演示

  1. package com.hspedu.file;
  2. import org.junit.jupiter.api.Test;
  3. import java.io.File;
  4. import java.io.IOException;
  5. /**
  6. * @author HarborGao
  7. * @version 1.0
  8. */
  9. public class FileCreate {
  10. public static void main(String[] args) {
  11. }
  12. //方式一
  13. @Test
  14. public void create01() {
  15. String filePath = "E:\\news1.txt";
  16. File file = new File(filePath);
  17. try {
  18. file.createNewFile();
  19. System.out.println("文件创建成功");
  20. } catch (IOException e) {
  21. e.printStackTrace();
  22. }
  23. }
  24. //方式二
  25. @Test
  26. public void create02() {
  27. File parentFilePath = new File("e:\\");
  28. String fileName = "news2.txt";
  29. File file = new File(parentFilePath, fileName);
  30. try {
  31. file.createNewFile();
  32. System.out.println("文件创建成功");
  33. } catch (IOException e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. //方式三
  38. @Test
  39. public void create03() {
  40. String parentPath = "E:/";
  41. String filePath = "news3.txt";
  42. File file = new File(parentPath,filePath);
  43. try {
  44. file.createNewFile();
  45. System.out.println("文件创建成功");
  46. } catch (IOException e) {
  47. e.printStackTrace();
  48. }
  49. }
  50. }

2.2 获取文件的相关信息

getName 获取文件名
getAbsolutePath 获取文件绝对路径
getParent 获取父级目录
length 文件大小(字节)
exists 文件是否存在
isFile 是否是一个文件
isDirectory 是否是一个目录

案例演示

  1. package com.hspedu.file;
  2. import org.junit.jupiter.api.Test;
  3. import java.io.File;
  4. /**
  5. * @author HarborGao
  6. * @version 1.0
  7. */
  8. public class FileInformation {
  9. public static void main(String[] args) {
  10. }
  11. @Test
  12. public void info() {
  13. File file = new File("e:\\news1.txt");
  14. System.out.println("文件名=" + file.getName());
  15. System.out.println("文件绝对路径=" + file.getAbsolutePath());
  16. System.out.println("文件父级目录=" + file.getParent());
  17. System.out.println("是否是一个文件=" + file.isFile());
  18. System.out.println("是否是一个目录=" + file.isDirectory());
  19. System.out.println("文件是否存在=" + file.exists());
  20. System.out.println("文件大小(字节)=" + file.length());
  21. }
  22. }

2.3 目录的操作和文件删除

mkdir 创建一级目录
mkdirs 创建多级目录
delete 删除空目录或文件

案例演示

  1. package com.hspedu.file;
  2. import org.junit.jupiter.api.Test;
  3. import java.io.File;
  4. import java.io.IOException;
  5. /**
  6. * @author HarborGao
  7. * @version 1.0
  8. */
  9. public class Directory {
  10. public static void main(String[] args) {
  11. }
  12. @Test
  13. public void m1() {
  14. String filePath = "e:\\news1.txt";
  15. File file = new File(filePath);
  16. if (file.exists()) {
  17. if (file.delete()) {
  18. System.out.println(filePath + "删除成功");
  19. } else {
  20. System.out.println(filePath + "删除失败");
  21. }
  22. } else {
  23. System.out.println("该文件不存在...");
  24. }
  25. }
  26. @Test
  27. public void m2() {
  28. String filePath = "e:\\demo02";
  29. File file = new File(filePath);
  30. if (file.exists()) {
  31. if (file.delete()) {
  32. System.out.println(filePath + "删除成功");
  33. } else {
  34. System.out.println(filePath + "删除失败");
  35. }
  36. } else {
  37. System.out.println("该目录不存在...");
  38. }
  39. }
  40. @Test
  41. public void m3() {
  42. String directoryPath = "e:\\demo\\a\\b\\c";
  43. File file = new File(directoryPath);
  44. if (file.exists()) {
  45. System.out.println("该目录已存在");
  46. } else {
  47. if (file.mkdirs()) {
  48. System.out.println(directoryPath + "目录创建成功");
  49. } else {
  50. System.out.println(directoryPath + "目录创建失败");
  51. }
  52. }
  53. }
  54. }

三、IO 流原理及流的分类

3.1 Java IO 流原理

  1. I / O是Input / Output 的缩写,I / O技术是非常实用的技术,用于处理数据传输。如读/写文件,网络通讯等
  2. Java程序中,对于数据的输入、输出操作以“流(Stream)”的方式进行
  3. java.io 包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过方法输入或输出数据
  4. 输入 input:读取外部数据(如磁盘、光盘等存储设备中的数据)到程序(内存)中
  5. 输出 output:将程序(内存)数据输出到磁盘、光盘等存储设备中

3.2 流的分类

按操作数据单位不同分为:字节流(8 bit)二进制文件,字符流(按字符)文本文件
按数据流的流向不同分为:输入流、输出流
按流的角色的不同分为:节点流,处理流/包装流

(抽象基类) 字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer
  1. Java的 IO流共涉及40多个类,实际上非常规则,都是从如上4个抽象基类派生的
  2. 由这4个类派生出来的子类名称都是以其父类名作为子类名后缀

四、IO 流体系图 - 常用的类

4.1 IO 流体系图

image.png

4.2 文件 VS 流

文件是存储在磁盘等媒介上的
而 流 是用来传输文件的

可以将文件看成商品,磁盘看成物流中心,流看成快递员,内存看成买家
image.png

4.3 FileInputStream 介绍

FileInputStream:文件字节输入流 用于读取磁盘上的文件到内存,按字节读取

应用案例:使用 FileInputStream 读取 hello.txt文件,并将文件内容显示到控制台

  1. package com.hspedu.file;
  2. import org.junit.jupiter.api.Test;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.FileNotFoundException;
  6. import java.io.IOException;
  7. /**
  8. * @author HarborGao
  9. * @version 1.0
  10. * 演示 FileInputStream 的使用(字节输入流 文件 -> 程序)
  11. */
  12. public class FileInputStream_ {
  13. public static void main(String[] args) {
  14. }
  15. /**
  16. * 演示读取文件
  17. * 单个字节读取,效率比较低
  18. * -> 使用 read(byte[] b)
  19. */
  20. @Test
  21. public void readFile01() {
  22. String filePath = "e:\\hello.txt";
  23. int readDate = 0;
  24. FileInputStream fileInputStream = null;
  25. try {
  26. //创建 FileInputStream 对象,用于读取 文件
  27. fileInputStream = new FileInputStream(filePath);
  28. //从该输入流读取一个字节的数据。如果没有输入可用,此方法将阻止。
  29. //如果返回-1,表示读取完毕
  30. while ((readDate = fileInputStream.read()) != -1) {
  31. System.out.print((char) readDate); //转成char显示
  32. }
  33. } catch (IOException e) {
  34. e.printStackTrace();
  35. } finally {
  36. //关闭文件流,释放资源
  37. try {
  38. fileInputStream.close();
  39. } catch (IOException e) {
  40. e.printStackTrace();
  41. }
  42. }
  43. }
  44. /**
  45. * 演示读取文件
  46. * -> 使用 read(byte[] b) 读取文件,提高效率
  47. */
  48. @Test
  49. public void readFile02() {
  50. String filePath = "e:\\hello.txt";
  51. //字节数组
  52. byte[] buf = new byte[8]; //一次读取8个字节
  53. int readLen = 0;
  54. FileInputStream fileInputStream = null;
  55. try {
  56. //创建 FileInputStream 对象,用于读取 文件
  57. fileInputStream = new FileInputStream(filePath);
  58. //从该输入流最多读取8个字节的数据到字节数组。此方法将阻塞,直到某些输入可用
  59. //如果返回-1,表示读取完毕
  60. //如果读取正常,返回实际读取的字节数
  61. while ((readLen = fileInputStream.read(buf)) != -1) {
  62. System.out.print(new String(buf,0,readLen)); //转成char显示
  63. }
  64. } catch (IOException e) {
  65. e.printStackTrace();
  66. } finally {
  67. //关闭文件流,释放资源
  68. try {
  69. fileInputStream.close();
  70. } catch (IOException e) {
  71. e.printStackTrace();
  72. }
  73. }
  74. }
  75. }

4.4 FileOutputStream 介绍

FileInputStream:文件字节输出流 用于将程序(内存)中的数据写入到磁盘上的文件中,按字节写入

应用案例:使用 FileOutputStream 在 a.txt 文件中写入 “hello,world”,如果文件不存在,会创建文件

  1. package com.hspedu.outputstream_;
  2. import org.junit.jupiter.api.Test;
  3. import java.io.FileNotFoundException;
  4. import java.io.FileOutputStream;
  5. import java.io.IOException;
  6. import java.nio.charset.StandardCharsets;
  7. /**
  8. * @author HarborGao
  9. * @version 1.0
  10. */
  11. public class FileOutputStream_ {
  12. public static void main(String[] args) {
  13. }
  14. /**
  15. * 演示 FileOutputStream 将数据写入文件中
  16. * 如果该文件不存在,则创建该文件
  17. * 说明:
  18. * 1. new FileOutputStream(filePath) 创建方式,当写入内容时,会覆盖原来的内容
  19. * 2. new FileOutputStream(filePath,true) 创建方式,当写入内容时,是在文件末尾追加内容
  20. */
  21. @Test
  22. public void writeFile() {
  23. //创建FileOutputStream对象
  24. String filePath = "e:\\a.txt";
  25. FileOutputStream fileOutputStream = null;
  26. try {
  27. fileOutputStream = new FileOutputStream(filePath);
  28. //写入一个字节
  29. //fileOutputStream.write('a');
  30. //写入字符串
  31. String str = "hello,world!";
  32. //getBytes() 可以把字符串转成字节数组
  33. //fileOutputStream.write(str.getBytes());
  34. //写入字符串,从指定索引写入指定长度的字节
  35. fileOutputStream.write(str.getBytes(),2,6);
  36. } catch (IOException e) {
  37. e.printStackTrace();
  38. } finally {
  39. try {
  40. fileOutputStream.close();
  41. } catch (IOException e) {
  42. e.printStackTrace();
  43. }
  44. }
  45. }
  46. }

4.5 编程练习:拷贝

按照前面学习的内容,完成图片/音乐的拷贝。

  1. package com.hspedu.outputstream_;
  2. import org.junit.jupiter.api.Test;
  3. import java.io.*;
  4. /**
  5. * @author HarborGao
  6. * @version 1.0
  7. */
  8. public class FileCopy {
  9. public static void main(String[] args) {
  10. }
  11. @Test
  12. public void copy() {
  13. String filePath = "e:\\546.jpg";
  14. String newFilePath = "d:\\tom.jpg";
  15. FileInputStream fileInputStream = null;
  16. FileOutputStream fileOutputStream = null;
  17. byte[] bc = new byte[1024];
  18. int readLen = 0;
  19. try {
  20. fileInputStream = new FileInputStream(filePath);
  21. fileOutputStream = new FileOutputStream(newFilePath);
  22. while ((readLen = fileInputStream.read(bc)) != -1) {
  23. fileOutputStream.write(bc,0,readLen);
  24. }
  25. System.out.println("拷贝成功~");
  26. } catch (IOException e) {
  27. e.printStackTrace();
  28. } finally {
  29. try {
  30. if (fileInputStream != null) {
  31. fileInputStream.close();
  32. }
  33. if (fileOutputStream != null) {
  34. fileOutputStream.close();
  35. }
  36. } catch (IOException e) {
  37. e.printStackTrace();
  38. }
  39. }
  40. }
  41. }

4.6 FileReader 介绍

FileReader:文件字符输入流 用于读取磁盘上的文件到内存,按字符读取
image.png
FileReader 相关方法:

  1. new FileReader(File/String)
  2. read:每次读取单个字符,返回该字符,如果到文件末尾返回 -1
  3. read(char[]):批量读取多个字符到数组,返回读取到的字符数,如果到文件末尾返回-1

FileReader 相关API:

  1. new String(char[]):将 char[] 转换成 String
  2. new String(char[],off,len):将 char[] 的指定部分转换成 String

应用案例:使用 FileReader 从 story.txt 读取内容,并显示

  1. package com.hspedu.reader_;
  2. import java.io.FileNotFoundException;
  3. import java.io.FileReader;
  4. import java.io.IOException;
  5. /**
  6. * @author HarborGao
  7. * @version 1.0
  8. */
  9. public class FileReader_ {
  10. public static void main(String[] args) {
  11. String filePath = "e:\\story.txt";
  12. FileReader fileReader = null;
  13. int Date = 0;
  14. char[] chars = new char[10];
  15. int readLen = 0;
  16. try {
  17. fileReader = new FileReader(filePath);
  18. //单个字符读取
  19. // while ((Date = fileReader.read()) != -1) {
  20. // System.out.print((char) Date);
  21. // }
  22. //多个字符读取
  23. while ((readLen = fileReader.read(chars)) != -1) {
  24. System.out.print(new String(chars,0,readLen));
  25. }
  26. } catch (IOException e) {
  27. e.printStackTrace();
  28. } finally {
  29. try {
  30. if (fileReader != null) {
  31. fileReader.close();
  32. }
  33. } catch (IOException e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. }
  38. }

4.7 FileWriter 介绍

FileWriter:文件字符输出流 用于将程序(内存)中的数据写入到磁盘上的文件中,按字符写入
image.png
FileWriter 常用方法:

  1. new FileWriter(File/String):覆盖模式,相当于流的指针在首端
  2. new FileWriter(File/String, true):追加模式,相当于流的指针在尾端
  3. write(int):写入单个字符
  4. write(char[]):写入指定字符数组
  5. write(char[], off, len):写入指定数组的指定部分
  6. write(String):写入整个字符串
  7. write(string, off, len):写入字符串的指定部分

FileWriter 相关API:
String类: toCharArray:将String转换成char[]

注意:
FileWriter 使用后,必须要关闭(close)或者刷新(flush),否则写入不到指定的文件!

应用案例:使用 FileWriter 将”风雨过后,定见彩虹”写入到 note.txt 文件中,注意细节。

  1. package com.hspedu.writer_;
  2. import java.io.FileWriter;
  3. import java.io.IOException;
  4. /**
  5. * @author HarborGao
  6. * @version 1.0
  7. */
  8. public class FileWriter_ {
  9. public static void main(String[] args) {
  10. String filePath = "e:\\note.txt";
  11. FileWriter fileWriter = null;
  12. String content = "风雨过后,定见彩虹";
  13. try {
  14. //fileWriter = new FileWriter(filePath);
  15. fileWriter = new FileWriter(filePath,true);
  16. // write(int):写入单个字符
  17. fileWriter.write('H');
  18. // write(char[]):写入指定字符数组
  19. fileWriter.write(content.toCharArray());
  20. // write(char[], off, len):写入指定数组的指定部分
  21. fileWriter.write(content.toCharArray(),2,5);
  22. // write(String):写入整个字符串
  23. fileWriter.write(content);
  24. // write(string, off, len):写入字符串的指定部分
  25. fileWriter.write(content,2,5);
  26. } catch (IOException e) {
  27. e.printStackTrace();
  28. } finally {
  29. try {
  30. if (fileWriter != null) {
  31. //fileWriter.flush();
  32. //.close() 关闭文件流,等价于 flush + 关闭
  33. fileWriter.close();
  34. }
  35. } catch (IOException e) {
  36. e.printStackTrace();
  37. }
  38. }
  39. }
  40. }

五、节点流和处理流

5.1 基本介绍

  1. 节点流可以从一个特定的数据源读取数据,如 FileReader、FileWriter

IO 流 - 图6

  1. 处理流(也叫包装流)是“连接”在已存在的流(节点流或处理流)之上,为程序提供更为强大的读写功能,也更加灵活,如 BufferedReader、BufferWriter

IO 流 - 图7

5.2 节点流和处理流一览图

image.png

5.3 节点流和处理流的区别和联系

  1. 节点流是底层流/低级流,直接跟数据打交道。
  2. 处理流(包装流)包装节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出。
  3. 处理流对节点流进行包装,使用了修饰器设计模式,不会直接与数据源打交道

模拟修饰器设计模式

  1. package com.hspedu;
  2. /**
  3. * @author HarborGao
  4. * @version 1.0
  5. * 模拟Reader
  6. */
  7. public abstract class Reader_ {
  8. public void readFile() {}
  9. public void readString() {}
  10. }
  1. package com.hspedu;
  2. /**
  3. * @author HarborGao
  4. * @version 1.0
  5. * 模拟Reader的子类 FileReader 节点流
  6. */
  7. public class FileReader_ extends Reader_{
  8. public void readFile() {
  9. System.out.println("对文件进行读取...");
  10. }
  11. }
  1. package com.hspedu;
  2. /**
  3. * @author HarborGao
  4. * @version 1.0
  5. * 模拟Reader的子类 StringReader 节点流
  6. */
  7. public class StringReader_ extends Reader_{
  8. public void readString() {
  9. System.out.println("读取字符串...");
  10. }
  11. }
  1. package com.hspedu;
  2. /**
  3. * @author HarborGao
  4. * @version 1.0
  5. * 模拟 BufferedReader 处理流(包装流) 修饰器设计模式
  6. */
  7. public class BufferedReader_ extends Reader_{
  8. private Reader_ reader_; //属性,Reader_类
  9. public BufferedReader_(Reader_ reader_) {
  10. this.reader_ = reader_;
  11. }
  12. public void readFile() { //读取一次
  13. reader_.readFile();
  14. }
  15. //让方法更加灵活: 多次读取文件,或者加缓冲 char[]...
  16. public void readFiles(int num) {
  17. for (int i = 0; i < num; i++) {
  18. reader_.readFile();
  19. }
  20. }
  21. //扩展: 批量处理字符串数据
  22. public void readStrings(int num) {
  23. for (int i = 0; i < num; i++) {
  24. reader_.readString();
  25. }
  26. }
  27. }
  1. package com.hspedu;
  2. /**
  3. * @author HarborGao
  4. * @version 1.0
  5. */
  6. public class Test_ {
  7. public static void main(String[] args) {
  8. BufferedReader_ bufferedReader_ = new BufferedReader_(new FileReader_());
  9. bufferedReader_.readFiles(10);
  10. BufferedReader_ bufferedReader_1 = new BufferedReader_(new StringReader_());
  11. bufferedReader_1.readStrings(5);
  12. }
  13. }

5.4 处理流的功能主要体现

  1. 性能的提高:主要以增加缓冲的方式来提高输入输出的效率
  2. 操作的便捷:处理流 提供了一系列便捷的方法来一次输入输出大批量的数据,使用更加方便灵活

5.5 处理流 - BufferedReader 和 BufferedWriter

  1. BufferedReader 和 BufferedWriter 数据字符流,是按照字符来读取和写入数据的
    注意:处理字节流文件(图片、音频等二进制文件)可能会造成数据损失
  2. 关闭时,只需要关闭外层流(处理流)即可,内层的节点流或者处理流会随着外层的关闭而关闭

应用案例1:使用BufferredReader 读取文本文件,并显示在控制台

  1. package com.hspedu.reader_;
  2. import java.io.BufferedReader;
  3. import java.io.FileNotFoundException;
  4. import java.io.FileReader;
  5. /**
  6. * @author HarborGao
  7. * @version 1.0
  8. * 演示 BufferedReader 使用
  9. */
  10. public class BufferedReader_ {
  11. public static void main(String[] args) throws Exception {
  12. String filePath = "e:\\story.txt";
  13. //创建BufferedReader
  14. BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
  15. //读取
  16. String line;
  17. //说明:
  18. //1. readLine() 方法是按行读取,效率高
  19. //2. 当返回 null 时,表示文件读取完毕
  20. while ((line = bufferedReader.readLine()) != null) {
  21. System.out.println(line);
  22. }
  23. //关闭流,这里注意,只需要关闭 bufferedReader,因为底层会自动关闭节点流FileReader
  24. bufferedReader.close();
  25. }
  26. }

应用案例2:使用 BufferedWriter 将”hello,世界!”,写入到文件中

  1. package com.hspedu.reader_;
  2. import java.io.BufferedWriter;
  3. import java.io.FileWriter;
  4. import java.io.IOException;
  5. /**
  6. * @author HarborGao
  7. * @version 1.0
  8. * 演示 BufferedWriter 使用
  9. */
  10. public class BufferedWriter_ {
  11. public static void main(String[] args) throws IOException {
  12. String filePath = "e:\\ok.txt";
  13. //创建 BufferedWriter 对象
  14. BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath,true));
  15. bufferedWriter.write("hello,世界!");
  16. bufferedWriter.newLine();//插入一个和系统相关的换行
  17. bufferedWriter.write("hello2,世界!");
  18. bufferedWriter.newLine();
  19. bufferedWriter.write("hello3,世界!");
  20. bufferedWriter.newLine();
  21. //说明: 关闭外层流 bufferedReader 即可,因为底层会自动关闭节点流FileWriter
  22. bufferedWriter.close();
  23. }
  24. }

应用案例3:使用 BufferedReader 和 BufferedWriter 完成文本文件拷贝,注意文件编码

  1. package com.hspedu.writer_;
  2. import java.io.*;
  3. /**
  4. * @author HarborGao
  5. * @version 1.0
  6. */
  7. public class BufferedCopy_ {
  8. public static void main(String[] args) {
  9. String srcFilePath = "e:\\story.txt";
  10. String destFilePath = "e:\\demo\\storyCopy.txt";
  11. BufferedReader br = null;
  12. BufferedWriter bw = null;
  13. String line;
  14. try {
  15. br = new BufferedReader(new FileReader(srcFilePath));
  16. bw = new BufferedWriter(new FileWriter(destFilePath));
  17. //读取并写入
  18. //说明: readLine() 是读取一行内容,但是没有带换行
  19. while ((line = br.readLine()) != null) {
  20. //每读取一行,就写入
  21. bw.write(line);
  22. bw.newLine();
  23. }
  24. System.out.println("拷贝完毕!");
  25. } catch (IOException e) {
  26. e.printStackTrace();
  27. } finally {
  28. try {
  29. if (br != null) {
  30. br.close();
  31. }
  32. if (bw != null) {
  33. bw.close();
  34. }
  35. } catch (IOException e) {
  36. e.printStackTrace();
  37. }
  38. }
  39. }
  40. }

5.6 处理流 - BufferedInputStream 和 BufferedOutputStream

  1. BufferedInputStream 是字节流,在创建 BufferedInputStream 时,会创建一个内部缓冲区数组。
  2. BufferedInputStream 是字节流,实现缓冲的输出流,可以将多个字节写入底层输出流中,而不必对每次字节写入调用底层系统。

image.png
image.png

应用案例:编程完成图片/音乐 的拷贝(要求使用 Buffered…流)

  1. package com.hspedu.outputstream_;
  2. import java.io.*;
  3. /**
  4. * @author HarborGao
  5. * @version 1.0
  6. * 演示 BufferedInputStream 和 BufferedOutputStream 使用
  7. * 可以实现 二进制文件的拷贝
  8. * 思考: 字节流可以操作二进制文件,可以操作文本文件吗? 可以。文本文件底层也是二进制文件,编码不同而已,读取时注意
  9. */
  10. public class BufferedIOStreamCopy_ {
  11. public static void main(String[] args) {
  12. String srcFilePath = "e:\\546.jpg";
  13. String destFilePath = "e:\\123.jpg";
  14. BufferedInputStream bis = null;
  15. BufferedOutputStream bos = null;
  16. try {
  17. bis = new BufferedInputStream(new FileInputStream(srcFilePath));
  18. bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
  19. //循环的读取文件并写入到新文件
  20. byte[] buff = new byte[1024];
  21. int readLen;
  22. while ((readLen = bis.read(buff)) != -1) {
  23. bos.write(buff,0,readLen);
  24. }
  25. System.out.println("文件拷贝成功");
  26. } catch (IOException e) {
  27. e.printStackTrace();
  28. } finally {
  29. try {
  30. if (bis != null) {
  31. bis.close();
  32. }
  33. if (bos != null) {
  34. bos.close();
  35. }
  36. } catch (IOException e) {
  37. e.printStackTrace();
  38. }
  39. }
  40. }
  41. }

5.7 对象流 - ObjectInputStream 和 ObjectOutputStream

看一个需求:

  1. 将 int num = 100 这个 int 数据保存到文件中,注意不是 100 数字,而是 int 100,并且,能够从文件中直接恢复 int 100
  2. 将 Dog dog = new Dog(“小黄”, 3)这个dog对象 保存到文件中,并且能够从文件恢复
  3. 上面的要求,就是 能够将 基本数据类型 或者 对象 进行序列化 和 反序列化操作

序列化和反序列化:

  1. 序列化就是在保存数据时,保存数据的值和数据类型
  2. 反序列化就是在恢复数据时,恢复数据的值和数据类型
  3. 需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:
    1. Serializable //这是一个标记接口,没有方法,一般情况下推荐使用
    2. Externalizable //需实现其方法

ObjectInputStream 和 ObjectOutputStream 基本介绍
image.png image.png
功能:提供了对基本类型或对象类型的序列化和反序列化的方法
ObjectOutputStream: 提供序列化功能
ObjectInputStream: 提供反序列化功能
image.png image.png

应用案例1:使用 ObjectOutputStream 序列化 基本数据类型和一个 Dog对象(name,age),并保存到
data.dat 文件中

  1. package com.hspedu.outputstream_;
  2. import java.io.FileOutputStream;
  3. import java.io.ObjectOutputStream;
  4. /**
  5. * @author HarborGao
  6. * @version 1.0
  7. * 演示 ObjectOutputStream 使用,完成数据的序列化
  8. */
  9. public class ObjectOutputStream_ {
  10. public static void main(String[] args) throws Exception{
  11. //序列化后,保存的文件格式,不是存文本,而是指定的数据保存格式 .dat
  12. String filePath = "e:\\data.dat";
  13. ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
  14. //序列化数据到 e:\data.dat
  15. oos.writeInt(100); //自动装箱
  16. oos.writeBoolean(true);
  17. oos.writeChar('a');
  18. oos.writeDouble(9.5);
  19. oos.writeUTF("韩顺平教育");
  20. //保存一个 Dog对象
  21. Dog dog = new Dog("大黄", 5);
  22. oos.writeObject(dog);
  23. oos.close();
  24. System.out.println("数据保存完毕(序列化形式)");
  25. }
  26. }
  1. package com.hspedu.outputstream_;
  2. import java.io.Serializable;
  3. /**
  4. * @author HarborGao
  5. * @version 1.0
  6. */
  7. //如果想让类是可序列化的,必须实现 Serializable 或 Externalizable 接口
  8. public class Dog implements Serializable {
  9. private String name;
  10. private int age;
  11. public Dog(String name, int age) {
  12. this.name = name;
  13. this.age = age;
  14. }
  15. @Override
  16. public String toString() {
  17. return "Dog{" +
  18. "name='" + name + '\'' +
  19. ", age=" + age +
  20. '}';
  21. }
  22. public String getName() {
  23. return name;
  24. }
  25. public void setName(String name) {
  26. this.name = name;
  27. }
  28. public int getAge() {
  29. return age;
  30. }
  31. public void setAge(int age) {
  32. this.age = age;
  33. }
  34. }

应用案例2:使用 ObjectInputStream 读取 data.dat 并反序列化恢复数据

  1. package com.hspedu.inputstream_;
  2. import com.hspedu.outputstream_.Dog; //引入Dog类
  3. import java.io.FileInputStream;
  4. import java.io.ObjectInputStream;
  5. /**
  6. * @author HarborGao
  7. * @version 1.0
  8. */
  9. public class ObjectInputStream_ {
  10. public static void main(String[] args) throws Exception {
  11. //指定反序列化的文件
  12. String filePath = "e:\\data.dat";
  13. ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
  14. //读取
  15. //解读:
  16. //1. 读取(反序列化)的顺序需要和保存数据(序列化)的顺序一致
  17. //2. 否则会出现异常
  18. System.out.println(ois.readInt());
  19. System.out.println(ois.readBoolean());
  20. System.out.println(ois.readChar());
  21. System.out.println(ois.readDouble());
  22. System.out.println(ois.readUTF());
  23. Object dog = ois.readObject();
  24. System.out.println("运行类型=" + dog.getClass());
  25. System.out.println("dog信息=" + dog);
  26. //细节说明:
  27. //1. 如果我们希望调用 Dog方法,需要向下转型
  28. //2. 并且需要将 Dog类的定义,拷贝到可以引用的位置
  29. Dog dog2 = (Dog)dog;
  30. System.out.println(dog2.getName());
  31. //关闭流,关闭外层即可
  32. ois.close();
  33. }
  34. }

5.8 对象处理流的注意事项和细节说明

  1. 读写顺序要一致
  2. 要求实现序列化或反序列化对象,需要实现 Serializable
  3. 序列化的类中建议添加 SerialVersionUID,为了提高版本的兼容性
  4. 序列化对象时,默认将里面所有的属性都进行序列化,但除了 static 或 transient 修饰的成员
  5. 序列化对象时,要求里面属性的类型也需要实现序列化接口
  6. 序列化具备可继承性,也就是如果某类已经实现了序列化,则它所有子类也已经默认实现了序列化

5.9 标准输入输出流


名称 编译类型 默认设备 运行类型
System.in 标准输入 InputStream 键盘 BufferedInputStream
System.out 标准输出 PrintStream 显示器 PrintStream

5.10 转换流 - InputStreamReader 和 OutputStreamWriter

  1. package com.hspedu.transformation_;
  2. import java.io.BufferedReader;
  3. import java.io.FileReader;
  4. import java.io.IOException;
  5. /**
  6. * @author HarborGao
  7. * @version 1.0
  8. */
  9. public class CodeQuestion_ {
  10. public static void main(String[] args) throws IOException {
  11. //读取 e:\\a.txt 文件到程序
  12. //思路:
  13. //1. 创建字符输入流 BufferedReader [处理流]
  14. //2. 使用 BufferedReader 对象读取 a.txt
  15. //3. 默认情况下,读取文件是按照 utf-8 编码
  16. String filePath = "e:\\a.txt"; //假设源文件是 gbk编码的,含有中文
  17. BufferedReader br = new BufferedReader(new FileReader(filePath));
  18. String s = br.readLine();
  19. System.out.println("读取的内容:" + s); //会发现出现乱码
  20. br.close();
  21. }
  22. }

InputStreamReader 和 OutputStreamWriter 介绍

  1. InputStreamReader:Reader 的子类,可以将 InputStream(字节流)包装成(转换) Reader(字符流)
  2. OutputStreamWriter:Writer的子类,实现将 OutputStream(字节流)包装成 Writer(字符流)
  3. 当处理纯文本数据时,如果使用字符流效率更高,并且可以有效解决中文问题,所以建议将字节流转换成字符流
  4. 可以在使用时指定编码格式(比如 utf-8, gbk, gb2312, ISO8859-1 等)

应用案例1:编程将 字节流 FileInputStream 包装成(转换成)字符流 InputStreamReader 对文件进行读取
按照 utf-8/gbk 格式,进而再包装成 BufferedReader

  1. package com.hspedu.transformation_;
  2. import java.io.BufferedReader;
  3. import java.io.FileInputStream;
  4. import java.io.IOException;
  5. import java.io.InputStreamReader;
  6. /**
  7. * @author HarborGao
  8. * @version 1.0
  9. * 演示使用 InputStreamReader 转换流解决中文乱码问题
  10. * 将字节流 FileInputStream 转换成 字符流 InputStreamReader,指定编码 utf-8 / gbk
  11. */
  12. public class InputStreamReader_ {
  13. public static void main(String[] args) throws IOException {
  14. String filePath = "e:\\a.txt";
  15. //解读:
  16. //1. 将FileInputStream 转成 InputStreamReader
  17. //2. 制定了编码 "gbk"
  18. InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath),"gbk");
  19. //3. 把 InputStreamReader 传入 BufferedReader
  20. BufferedReader br = new BufferedReader(isr);
  21. //将 2 和 3 合并成一步
  22. BufferedReader br2 = new BufferedReader(new InputStreamReader(new FileInputStream(filePath),"gbk"));
  23. //4. 读取
  24. System.out.println( br.readLine());
  25. //5. 关闭外层
  26. br.close();
  27. }
  28. }

应用案例2:编程将 字节流 FileOutputStream 包装成(转换成)字符流 OutputStreamWriter 对文件进行写入
按照 utf-8/gbk 格式

  1. package com.hspedu.transformation_;
  2. import java.io.FileOutputStream;
  3. import java.io.IOException;
  4. import java.io.OutputStreamWriter;
  5. /**
  6. * @author HarborGao
  7. * @version 1.0
  8. * 演示 OutputStreamWriter 使用
  9. * 将 字节流FileOutputStream 转换成 字符流OutputStreamWriter
  10. * 指定处理的编码 gnk/utf-8/utf8
  11. */
  12. public class OutputStreamWriter_ {
  13. public static void main(String[] args) throws IOException {
  14. String filePath = "E:\\b.txt";
  15. String charSet = "utf8";
  16. OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), charSet);
  17. osw.write("hi,世界");
  18. osw.close();
  19. System.out.println("保存成功");
  20. }
  21. }

六、打印流 - PrintStream 和 PrintWriter

6.1 PrintStream 字节打印输出流

  1. package com.hspedu.printstream_;
  2. import java.io.IOException;
  3. import java.io.PrintStream;
  4. /**
  5. * @author HarborGao
  6. * @version 1.0
  7. * 演示 PrintStream 字节打印输出流 使用
  8. */
  9. public class PrintStream_ {
  10. public static void main(String[] args) throws IOException {
  11. PrintStream out = System.out;
  12. //在默认情况下, PrintStream 打印的位置是标准输出 即显示器
  13. /*
  14. public void print(String s) {
  15. if (s == null) {
  16. s = "null";
  17. }
  18. write(s);
  19. }
  20. */
  21. out.println("hello,java");
  22. //因为 print 底层使用的是 write(),所以我们也可以直接调用其完成打印输出
  23. out.write("java,你好".getBytes());
  24. out.close();
  25. //我们可以修改打印流输出的位置/设备
  26. System.setOut(new PrintStream("e:\\f1.txt"));
  27. System.out.println("hello,韩顺平教育~");
  28. }
  29. }

6.2 PrintWriter 字符打印输出流

  1. package com.hspedu.printwriter_;
  2. import java.io.FileWriter;
  3. import java.io.IOException;
  4. import java.io.PrintWriter;
  5. /**
  6. * @author HarborGao
  7. * @version 1.0
  8. */
  9. public class PrintWriter_ {
  10. public static void main(String[] args) throws IOException {
  11. //PrintWriter printWriter = new PrintWriter(System.out);
  12. PrintWriter printWriter = new PrintWriter(new FileWriter("e:\\f2.txt"));
  13. printWriter.print("hi,北京~");
  14. printWriter.close();
  15. }
  16. }

七、Properties 类

7.1 看一个需求

有如下一个配置文件 mysql.properties
ip=192.168.0.13
user=root
pwd=12345
请 编程读取 ip、user、pwd 的值

传统方法:

  1. package com.hspedu.properties_;
  2. import java.io.BufferedReader;
  3. import java.io.FileReader;
  4. import java.io.IOException;
  5. /**
  6. * @author HarborGao
  7. * @version 1.0
  8. */
  9. public class Properties01 {
  10. public static void main(String[] args) throws IOException {
  11. BufferedReader br = new BufferedReader(new FileReader("src\\mysql.properties"));
  12. String s;
  13. while ((s = br.readLine()) != null) {
  14. String[] split = s.split("=");
  15. System.out.println(split[0] + "的值是 " + split[1]);
  16. }
  17. br.close();
  18. }
  19. }

问题:

  1. 读取数据麻烦,需要自己定义读取方法
  2. 数据修改比较麻烦

7.2 基本介绍

  1. Properties 类 是专门用于读写配置文件的集合类 ,其父类是Hashtable
    配置文件的格式:
    键=值
    键=值

  2. 注意:键值对不需要有空格,值不需要用引号包起来,默认类型是String

7.3 Properties 类的常见方法

方法 功能
load 加载配置文件的键值对到 Properties 对象
list 将数据显示到指定设备
getProperty(key) 根据键获取值
setProperty(key, value) 设置键值对到 Properties 对象
store 将 Properties 中的键值对储存到配置文件。在IDEA中,保存信息到配置文件,如果含有中文,会储存为 Unicode码

7.4 应用案例

  1. 使用 Properties 类完成对 mysql.properties 的读取
  2. 使用 Properties 类添加 key-value 到新文件 mysql2.properties 中
  3. 使用 Properties 类完成对 mysql.properties 的读取,并修改某个 key-value ```java package com.hspedu.properties_;

import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.Properties;

/**

  • @author HarborGao
  • @version 1.0 */ public class Properties02 { public static void main(String[] args) throws IOException {
    1. //1. 使用 Properties 类完成对 mysql.properties 的读取
    2. Properties properties = new Properties();
    3. //加载指定配置文件
    4. properties.load(new FileReader("src\\mysql.properties"));
    5. //把key-value 显示到控制台
    6. properties.list(System.out);
    7. //根据 key 获取对应的 value
    8. String ip = properties.getProperty("ip");
    9. System.out.println("ip是 " + ip);
    } } java package com.hspedu.properties_;

import java.io.FileOutputStream; import java.io.IOException; import java.util.Properties;

/**

  • @author HarborGao
  • @version 1.0 */ public class Properties03 { public static void main(String[] args) throws IOException {

    1. //使用 Properties 类添加 key-value 到新文件 mysql2.properties 中
    2. Properties properties = new Properties();
    3. //创建
    4. properties.setProperty("ip","192.168.100.100");
    5. properties.setProperty("user","汤姆");
    6. properties.setProperty("pwd","666666");
    7. properties.setProperty("charset","utf8");
    8. //修改 k-v
    9. properties.setProperty("charset","gbk");
    10. //将 k-v 存储到文件中
    11. properties.store(new FileOutputStream("src\\mysql2.properties"),null);
    12. System.out.println("保存配置文件成功");

    } } ```

八、本章作业

  1. 编程题
    1. 在判断e盘下是否有文件夹mytemp,如果没有就创建mytemp
    2. 在e:\mytemp目录下,创建文件hello.txt
    3. 如果hello.txt已经存在,提示该文件已经存在,就不要再重复创建了
    4. 并且在hello.txt文件中,写入hello,world~ ```java package com.hspedu.homework.homework01;

import java.io.File; import java.io.FileOutputStream; import java.io.IOException;

/**

  • @author HarborGao
  • @version 1.0 */ public class Homework01 { public static void main(String[] args) throws IOException {

    1. String filePath = "e:\\mytemp";
    2. File file = new File(filePath);
    3. if (file.exists()) {
    4. System.out.println("目录已经存在");
    5. } else {
    6. if (file.mkdir()) {
    7. System.out.println("目录创建成功");
    8. } else {
    9. System.out.println("目录创建失败");
    10. }
    11. }
    12. File file1 = new File(file, "hello.txt");
    13. if (file1.exists()) {
    14. System.out.println("该文件已存在");
    15. } else {
    16. if (file1.createNewFile()) {
    17. System.out.println("文件创建成功");
    18. } else {
    19. System.out.println("文件创建失败");
    20. }
    21. }
    22. FileOutputStream fos = new FileOutputStream(file1);
    23. fos.write("hello,world~".getBytes());
    24. fos.close();

    } } ```

  1. 编程题

要求:使用BufferedReader读取一 个文本文件,为每行加上行号,再连同内容一并输出到屏幕上。

//如果把文件的编码改成了gbk,出现中文乱码,大家思考如何解决
//1.默认是按照utf-8处理开始没有乱码
//2.提示:使用我们的转换流,将FileInputStream -> InputStreamReader[可以指定编码] -> BufferedReader ..

  1. package com.hspedu.homework_.homework02_;
  2. import java.io.*;
  3. /**
  4. * @author HarborGao
  5. * @version 1.0
  6. */
  7. public class Homework02 {
  8. public static void main(String[] args) throws IOException {
  9. BufferedReader reader = new BufferedReader(new InputStreamReader(
  10. new FileInputStream("e:\\code.txt"), "gbk"));
  11. //BufferedReader reader = new BufferedReader(new FileReader("e:\\code.txt"));
  12. int i = 0;
  13. String s;
  14. while ((s = reader.readLine()) != null) {
  15. System.out.println(++i + " " + s);
  16. }
  17. reader.close();
  18. }
  19. }
  1. 编程题
    1. 要编写一个dog.properties

name=tom
age=5
color=red

  1. 编写Dog类(name,age,color)创建一个dog对象,读取dog.properties 用相应的内容完成属性初始化,并输出
  2. 将创建的Dog对象,序列化到文件dog.dat文件 ```java package com.hspedu.homework.homework03;

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

/**

  • @author HarborGao
  • @version 1.0 */ public class Homework03 { public static void main(String[] args) throws IOException {

    1. //编写一个dog.properties
    2. String filePath = "src\\dog.properties";
    3. Properties properties = new Properties();
    4. properties.setProperty("name","tom");
    5. properties.setProperty("age","5");
    6. properties.setProperty("color","red");
    7. properties.store(new FileOutputStream(filePath),"Class Dog");
    8. //编写Dog类(name,age,color)创建一个dog对象,读取dog.properties 用相应的内容完成属性初始化,并输出
    9. Properties properties1 = new Properties();
    10. properties1.load(new FileInputStream(filePath));
    11. String age = properties1.getProperty("age");
    12. Dog dog = new Dog(properties1.getProperty("name"),Integer.parseInt(age),properties1.getProperty("color"));
    13. System.out.println(dog);
    14. //将创建的Dog对象,序列化到文件dog.dat文件
    15. ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("e:\\dog.dat"));
    16. objectOutputStream.writeObject(dog);
    17. objectOutputStream.close();

    } }

  1. ```java
  2. package com.hspedu.homework_.homework03_;
  3. import java.io.Serializable;
  4. /**
  5. * @author HarborGao
  6. * @version 1.0
  7. */
  8. public class Dog implements Serializable {
  9. private String name;
  10. private int age;
  11. private String color;
  12. public Dog(String name, int age, String color) {
  13. this.name = name;
  14. this.age = age;
  15. this.color = color;
  16. }
  17. public String getName() {return name;} public void setName(String name) {this.name = name;}
  18. public int getAge() {return age;} public void setAge(int age) {this.age = age;}
  19. public String getColor() {return color;} public void setColor(String color) {this.color = color;}
  20. @Override
  21. public String toString() {
  22. return "Dog{" +
  23. "name='" + name + '\'' +
  24. ", age=" + age +
  25. ", color='" + color + '\'' +
  26. '}';
  27. }
  28. }

学习参考(致谢):

  1. B站 @程序员鱼皮 Java学习一条龙
  2. B站 @韩顺平 零基础30天学会Java