8.1 字节流读写数据

  • 字节流抽象基类
    • InputStream:这个抽象类是表示字节输入流的所有类的超类
    • OutputStream:这个抽象类是表示字节输出流的所有超类
    • 子类名特点:子类名称都是以其父类名作为子类名的后缀
  • FileOutputStream:文件输出流用于将数据写入File
    • FileOutputStream(String name):创建文件输出流以指定的名称写入文件(比下面那个方便)
    • FileOutputStream(File file):创建文件输出流以写入由指定的File对象表示的文件 ```java import java.io.FileOutputStream; import java.io.IOException;

public class Demo { public static void main(String[] args) throws IOException { FileOutputStream fos = new FileOutputStream(“C:\Users\Lenovo\Desktop\fos.txt”) ; fos.write(66); //写入的是一个 B fos.close(); }

  1. - 使用字节输出流写数据的步骤
  2. - 创建字节输出流对象(调用系统功能创建了文件,创建字节输出流对象,让字节输出流对象指向文件)
  3. - 调用字节输出流对象的写数据方法
  4. - 释放资源(关闭此文件输出流并释放与此流相关联的任何系统资源)
  5. - 写数据的三种方式:
  6. - **_void write(int b)_** 将指定的字节写入此文件输出流
  7. - **_void write(byte[] b)_** b.length字节从指定的字节数组写入此文件输出流,一次写一个字节数组数据
  8. - **_void write(byte[] b,int off,int len)_** 从偏移量off开始写长度为len的数据,一次写一个字节数组的部分数据。
  9. ```java
  10. import java.io.FileOutputStream;
  11. import java.io.IOException;
  12. import java.nio.charset.StandardCharsets;
  13. public class Demo {
  14. public static void main(String[] args) throws IOException {
  15. FileOutputStream fos = new FileOutputStream("C:\\Users\\Lenovo\\Desktop\\fos.txt") ;
  16. //Byte[] getBytes() 返回字符串对应的字节数组
  17. byte[] bys = "甚家矶试试就".getBytes(StandardCharsets.UTF_8) ;
  18. fos.write(bys);
  19. fos.close();
  20. }
  21. }
  • 如需写数据时换行,在后面加上换行符。windows \r\n ; linux \n ; Mac \r
  • 追加数据:public FileOutputStream(String name,boolean append) 创建文件输出流以指定的名称写入文件,如果第二个参数为true,则字节将写入文件末尾而不是开头。 ```java import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets;

public class Demo { public static void main(String[] args) throws IOException { FileOutputStream fos = new FileOutputStream(“C:\Users\Lenovo\Desktop\fos.txt”,true) ; byte[] bys = “dsdadf”.getBytes(StandardCharsets.UTF_8) ; for(int i=0;i<10;i++){ fos.write(bys); fos.write(“\r\n”.getBytes(StandardCharsets.UTF_8)); } fos.close(); } }

  1. - 读数据:FileInputStream(String name):通过打开与实际文件的链接来创建一个FileInputStream,改文件由文件系统中的路径名name命名。
  2. ```java
  3. /* 一次读取一个字节 */
  4. import java.io.FileInputStream;
  5. import java.io.IOException;
  6. public class Demo {
  7. public static void main(String[] args) throws IOException {
  8. FileInputStream fis = new FileInputStream("C:\\Users\\Lenovo\\Desktop\\fos.txt") ;
  9. int by ;
  10. while((by=fis.read())!=-1){
  11. System.out.println((char)by);
  12. }
  13. fis.close();
  14. }
  15. }
  16. /* 一次读取一个数组 */
  17. import java.io.FileInputStream;
  18. import java.io.IOException;
  19. public class Demo {
  20. public static void main(String[] args) throws IOException {
  21. FileInputStream fis = new FileInputStream("C:\\Users\\Lenovo\\Desktop\\fos.txt") ;
  22. byte[] bys = new byte[1024] ;
  23. int len ;
  24. while((len=fis.read(bys))!=-1){
  25. System.out.println(new String(bys,0,len));
  26. }
  27. fis.close();
  28. }
  29. }

8.2 字节缓冲流

  • BufferOutputStream:该类实现缓冲输出流。通过设置这样的输出流,应用程序可以向底层输出流写一个字节,而不必为写入的每个字节导致底层系统的调用。BufferedOutputStream(OutputStream out) ;
  • BufferInputStream:将创建一个内部缓冲区数组。当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节。 BufferedInputStream(InputStream in);
  • 为什么构造方法需要的是字节流,而不是具体的文件或者路径?—->是因为字节缓冲流仅仅提供缓冲区,而真正读写数据的还得依靠基本的字节流对象进行操作。 ```java import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException;

public class Demo { public static void main(String[] args) throws IOException { BufferedInputStream bis = new BufferedInputStream( new FileInputStream(“C:\Users\Lenovo\Desktop\fos.txt”)) ; / //一次读取一个字节 int by ; while((by=bis.read())!=-1){ System.out.println((char) by); }/ //一次读取一个字节数组 byte[] bys = new byte[1024] ; int len ; while((len=bis.read(bys))!=-1){ System.out.println(new String(bys,0,len)); } bis.close(); } }

  1. ```java
  2. /* 复制视频 四种方式 */
  3. import java.io.*;
  4. public class Demo {
  5. public static void main(String[] args) throws IOException {
  6. long startTime = System.currentTimeMillis() ;
  7. //基本字节流 666.mp4 内存占用: 1,089,536 字节
  8. FileInputStream fis = new FileInputStream("C:\\Users\\Lenovo\\Desktop\\666\\666.mp4") ;
  9. FileOutputStream fos = new FileOutputStream("C:\\Users\\Lenovo\\Desktop\\666\\777\\666.mp4") ;
  10. //一次读写一个字节 共耗时7370ms
  11. /*
  12. int by ;
  13. while((by=fis.read())!=-1){
  14. fos.write(by);
  15. }
  16. */
  17. //一次读写一个字节数组 共耗时16ms
  18. /* byte[] bys = new byte[1024] ;
  19. int len ;
  20. while((len=fis.read(bys))!=-1){
  21. fos.write(bys);
  22. }*/
  23. //字节缓冲流 复制后视频打不开,不知道为什么!!
  24. BufferedInputStream bis = new BufferedInputStream(fis) ;
  25. BufferedOutputStream bos = new BufferedOutputStream(fos);
  26. //一次读写一个字节 共耗时47ms
  27. /* int by ;
  28. while((by=bis.read())!=-1){
  29. bos.write(by);
  30. }*/
  31. //一次读写一个字节数组 共耗时8ms
  32. byte[] bys = new byte[1024] ;
  33. int len ;
  34. while((len=bis.read(bys))!=-1){
  35. bos.write(bys);
  36. }
  37. fis.close();
  38. fos.close();;
  39. long endTime = System.currentTimeMillis() ;
  40. System.out.println("共耗时"+(endTime-startTime)+"ms");
  41. }
  42. }

8.3 字符流

  • 字符流=字节流+编码表
  • 用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文。
    • 如何识别是中文?—>汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数。
  • 字符流抽象基类

    • Reader 字符输入流
    • Writer 字符输出流

      8.3.1 字符串中编码解码问题

  • 编码:

    • byte[] getByte() 使用平台默认的字符集将该String编码为一系列字节,将结果村春到新的字节数组中。
    • byte[] getByte(byte[] bytes,String charsetName) 通过指定的字符集解码指定的字节数组来构造新的String。
  • 解码:
    • String(byte[] bytes) 通过使用平台的默认字符集解码指定的字节数组来构造新的String
    • String(byte[] bytes,String charsetName) 通过指定的字符集解码指定字节数组来构造新的String
  • 相关两个类:

    • InputStreamReader
      • 从字节流到字符流的桥梁
      • 它读取字节,并使用指定的编码将其解码为字符
      • 它使用的字符集可以由名称指定,也可以被明确的指定,或者可以接收平台的默认字符集
    • OutputStreamWriter
      • 从字符流到字节流的桥梁
      • 使用指定编码将写入的字符解码为字节
      • 他使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台默认字符集

        8.3.2 字符流读写数据方式

  • flush() 刷新流,还可以继续写数据

  • close() 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能在写数据。
  • 写数据五种方式:
    • void write(int c) 写一个字符
    • void write(char[] cbuf) 写入一个字符数组
    • void write(char[] cbuf,int off,int len) 写一个字符的一部分
    • void write(String str) 写一个字符串
    • void write(String str,int off,int len) 写一个字符串的一部分
  • 读数据两种方式:
    • int read() 一次读一个字符数据
    • int read(char[] cbuf) 一次读一个字符数组 ```java import java.io.*;

public class Demo { public static void main(String[] args) throws IOException { / OutputStreamWriter ops = new OutputStreamWriter(new FileOutputStream(“C:\Users\Lenovo\Desktop\fos.txt”), “GBK”) ; ops.write(33); ops.write(“中国”); char[] chr = {‘c’,’b’,’a’}; ops.write(chr); ops.write(chr,1,1); ops.write(“少时诵诗书所”,0,3); ops.close();/

  1. InputStreamReader isr = new InputStreamReader(new FileInputStream("C:\\Users\\Lenovo\\Desktop\\fos.txt"),"GBK");
  2. //一次读取一个字符

/ int by ; while((by=isr.read())!=-1){ System.out.println((char)by); }/

  1. //一次读取一个字符数组
  2. int len ;
  3. char[] ch = new char[1024] ;
  4. while((len=isr.read(ch))!=-1){
  5. System.out.println(new String(ch,0,len));
  6. }
  7. }

}

  1. <a name="XHNL8"></a>
  2. ## 8.4 字符缓冲流
  3. ```java
  4. import java.io.*;
  5. public class Demo {
  6. public static void main(String[] args) throws IOException {
  7. FileReader fr = new FileReader("C:\\Users\\Lenovo\\Desktop\\fos.txt") ;
  8. FileWriter fw = new FileWriter("C:\\Users\\Lenovo\\Desktop\\fis.txt") ;
  9. BufferedReader br = new BufferedReader(fr) ;
  10. BufferedWriter bw = new BufferedWriter(fw) ;
  11. char[] ch = new char[1024] ;
  12. int len ;
  13. while((len=br.read(ch))!=-1){
  14. bw.write(ch,0,len);
  15. }
  16. br.close();
  17. bw.close();
  18. }
  19. }
  • 特有功能:
    • BufferedWriter
      • void newLine() 写一行行分隔符,行分隔符字符串由系统属性定义
    • BufferedReader
      • public String readLine() 读一行文字。结果包含行的内容的字符串,不包括任何行终止字符,如果流的结尾已经到达,则为null。 ```java import java.io.*;

public class Demo { public static void main(String[] args) throws IOException { FileReader fr = new FileReader(“C:\Users\Lenovo\Desktop\fis.txt”) ; FileWriter fw = new FileWriter(“C:\Users\Lenovo\Desktop\fis.txt”) ; BufferedReader br = new BufferedReader(fr) ; BufferedWriter bw = new BufferedWriter(fw) ; for(int i=0;i<10;i++){ bw.write(“hello”+i); bw.newLine(); bw.flush(); }

  1. String line ;
  2. while((line= br.readLine())!=null){
  3. System.out.println(line);
  4. }
  5. }

}

  1. ```java
  2. /* 复制文件从fis-->fos */
  3. import java.io.*;
  4. public class Demo {
  5. public static void main(String[] args) throws IOException {
  6. FileReader fr = new FileReader("C:\\Users\\Lenovo\\Desktop\\fis.txt") ;
  7. FileWriter fw = new FileWriter("C:\\Users\\Lenovo\\Desktop\\fos.txt") ;
  8. BufferedReader br = new BufferedReader(fr) ;
  9. BufferedWriter bw = new BufferedWriter(fw) ;
  10. String line ;
  11. while((line=br.readLine())!=null){
  12. bw.write(line);
  13. bw.newLine();
  14. }
  15. br.close();
  16. bw.close();
  17. }
  18. }

8.5 特殊操作流

8.5.1 标准输入输出流

  • public static final InputStream in 标准输入流,通常该流对应键盘输入或由主机环境或用户指定的另一个输入源
  • public static final OutputStream out 标准输出流,通常该流对应于显示输出或由主机环境或由用户指定的另一个输出目标 ```java import java.io.*;

public class Demo { public static void main(String[] args) throws IOException { InputStream is = System.in ; int b; / while((b=is.read())!=-1){ System.out.println(b); }/

  1. //自己实现键盘录入数据很麻烦,用Scanner就很棒
  2. BufferedReader br = new BufferedReader(new InputStreamReader(System.in)) ;
  3. System.out.println("输入一个字符串:");
  4. String line = br.readLine();
  5. System.out.println("输入一个整数:");
  6. int i = Integer.parseInt(br.readLine());
  7. }

}

  1. <a name="RXI0p"></a>
  2. ### 8.5.2 打印流
  3. - 打印流分类:
  4. - 字节打印流:_**PrintStream**_
  5. - 字符打印流:_**PrintWrite**_
  6. - 打印流特点:
  7. - 只负责输出数据,不负责读取数据
  8. - 有自己的特有方法
  9. - 字节打印流
  10. - PrintStream(String fileName) 使用指定的文件名创建新的打印流
  11. - 使用继承父类的方法写数据,查看的时候会转码;使用自己的特有方法写数据,查看数据原样输出。
  12. ```java
  13. import java.io.*;
  14. public class Demo {
  15. public static void main(String[] args) throws IOException {
  16. PrintStream ps = new PrintStream("C:\\Users\\Lenovo\\Desktop\\666.txt") ;
  17. ps.write(97); //a
  18. ps.println();
  19. ps.println(97); //97
  20. ps.close();
  21. }
  22. }
  • 字符打印流:
    • PrintWriter(String fileName) 使用指定的文件创建一个新的PrintWrite,而不需要自动执行刷新。
    • PrintWriter(Writer out,boolean autoFlush) 创建一个新的PrintWrite;out:字符输出流;autoFlush:一个布尔值,如果为真则println,printf,或format方法将刷新输出缓冲区。 ```java import java.io.*;

public class Demo { public static void main(String[] args) throws IOException { / PrintWriter pw = new PrintWriter(“C:\Users\Lenovo\Desktop\666.txt”) ; pw.write(“hello”); pw.write(“\r\n”); pw.flush(); pw.println(“world”); pw.flush();/

  1. //可以自动刷新
  2. PrintWriter pw = new PrintWriter(new FileWriter("C:\\Users\\Lenovo\\Desktop\\666.txt"),true);
  3. pw.write("world");
  4. pw.close();
  5. }

}

  1. <a name="j3FqN"></a>
  2. ### 8.5.3 对象序列化流
  3. - 对象序列化就是将对象保存到磁盘中,或者在网络中传输对象
  4. - 这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息。字节序列写到文件之后,相当于文件中持久保存了一个对象的信息。反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。
  5. - 要实现序列化和反序列化就要使用对象序列化流和对象反序列化流
  6. - **_ObjectOutputStream _** 对象序列化流
  7. - 将java对象的原始数据类型和图形写入OutputStream。可以使用ObjectInputStream读取(重构)对象。可以通过使用流的文件来实现对象的持久存储。如果流时网格套接字流,则可以在另一个主机上或另一个进程中重构对象。
  8. - 重构方法:**_ObjectOutputStream(OutputStream out_**):创建一个写入指定的OutputStream的ObjectOutputStream
  9. - 序列化对象的方法:_**void writeObject(Object obj)**_:将指定的对象写入ObjectOutputStream
  10. - 注意:一个对象想要被序列化,该对象所属的类必须实现Serializable接口,该接口是一个标记接口,不需要重写任何方法。
  11. - _**ObjectInputStream **_ 对象反序列化流
  12. - ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象。
  13. - 构造方法:**_ObjectInputStream(InputStream in)_**:创建从指定的InputStream读取的ObjectInputStream。
  14. - 反序列化对象的方法:**_Object readObject()_**:从ObjectInputStream读取一个对象
  15. ```java
  16. import java.io.*;
  17. public class Demo {
  18. public static void main(String[] args) throws IOException, ClassNotFoundException {
  19. /* ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Lenovo\\Desktop\\666.txt")) ;
  20. Student s = new Student("小黑",30) ;
  21. outputStream.writeObject(s);
  22. outputStream.close();*/
  23. ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("C:\\Users\\Lenovo\\Desktop\\666.txt")) ;
  24. Student s = (Student) inputStream.readObject() ;
  25. System.out.println(s.getName()+","+s.getAge());
  26. }
  27. }
  28. ***********************************************************************************
  29. import java.io.Serializable;
  30. public class Student implements Serializable {
  31. private String name ;
  32. private int age ;
  33. public Student(String name, int age) {
  34. this.name = name;
  35. this.age = age;
  36. }
  37. public Student() {
  38. }
  39. public String getName() {
  40. return name;
  41. }
  42. public void setName(String name) {
  43. this.name = name;
  44. }
  45. public int getAge() {
  46. return age;
  47. }
  48. public void setAge(int age) {
  49. this.age = age;
  50. }
  51. }
  • 需要注意的问题

    • 用对象 序列化流序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?
      • 会出问题,会抛出InvalidClassException异常
    • 给对象所属的类加一个serialVersionUID private static final long serialVersionUID = 42L 可以解决上面那个问题
    • 如果对象中的某个成员变量的值不想被序列化,又该如何实现呢?
      • 给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程

        8.5.4 Properties

  • 是一个Map体系的集合类

  • 可以保存到流中或者从流中加载 ```java import java.util.Properties; import java.util.Set;

public class Demo { public static void main(String[] args) { Properties prop = new Properties() ; prop.put(“001”,30); prop.put(“002”,40); prop.put(“003”,20); Set setKey = prop.keySet(); for(Object o : setKey){ System.out.println(o+”,”+prop.get(o)); } } }

  1. - Properties作为集合的特有方法
  2. - _**Object setProperty(String key,String value)**_ 设置集合的键和值,都是String类型,底层调用Hashtable方法put
  3. - **_String getProperty(String key)_** 使用此属性列表中指定的键搜索属性
  4. - _**Set<String> stringPropertyNames()**_ 从属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串
  5. ```java
  6. import java.util.Properties;
  7. import java.util.Set;
  8. public class Demo {
  9. public static void main(String[] args) {
  10. Properties prop = new Properties() ;
  11. prop.setProperty("001","30");
  12. prop.setProperty("002","40");
  13. prop.setProperty("003","20");
  14. Set<String> setKey = prop.stringPropertyNames();
  15. for(String o : setKey){
  16. System.out.println(o+","+prop.getProperty(o));
  17. }
  18. }
  19. }
  20. 运行结果:
  21. 003,20
  22. 002,40
  23. 001,30
  24. Process finished with exit code 0
  • Properties和IO流相结合的方法
    • void load(InputStream inStream) 从输入字节流读取属性列表(键和元素对)
    • void load(Reader reader) 从输入字符流读取属性列表(键和元素对)
    • void store(OutputStream out,String comments) 将此属性列表(键和元素对)写入此Properties表中,以适合于使用load(InputStreatm) 方法的格式写入输出字节流
    • void store(Writer writer,String comments) 将此属性列表(键和元素对)写入此Properties表中,以适合使用load(Reader)方法的格式写入输出字符流 ```java import java.io.*; import java.util.Properties;

public class Demo { public static void main(String[] args) throws IOException { myStore() ; myLoad(); } public static void myLoad() throws IOException { Properties prop = new Properties() ; FileReader fr = new FileReader(“C:\Users\Lenovo\Desktop\666.txt”) ; prop.load(fr) ; fr.close(); System.out.println(prop); } public static void myStore() throws IOException { Properties prop = new Properties() ; prop.setProperty(“001”,”30”) ; prop.setProperty(“002”,”40”) ; prop.setProperty(“003”,”20”) ; FileWriter fw = new FileWriter(“C:\Users\Lenovo\Desktop\666.txt”) ; prop.store(fw,null); fw.close(); } } ```