[学习笔记]Java IO之其他流及总结

1. 概述

  • SequenceInputStream是字节流的包装类,能够提供多个流序列输入功能。
  • 序列流只有输入流,适合完成多个源一个目的的需求。
  • SequenceInputStream支持枚举输入,若源的数量大于2个,那么需要先建立枚举再通过构造器创建序列输入流。
  • SequenceInputStream的使用和其他流基本类似。
  • SequenceInputStream本质属于字节流。

2. 构造器

  • SequenceInputStream(Enumeration<? extends InputStream> e)
  • SequenceInputStream(InputStream s1, InputStream s2)

3. 常用方法

  • int  available()
  • void  close()
  • int  read()
  • int  read(byte[] b, int off, int len)

4. 示例

源文件1:"喜欢",源文件2:"Ja",源文件3:"va"。


public static void SequenceDemo() throws IOException {

  List<FileInputStream> list = new ArrayList<FileInputStream>();

  list.add(new FileInputStream( new File("temp\\partfiles\\char1.txt" )));

  list.add(new FileInputStream( new File("temp\\partfiles\\char2.txt" )));

  list.add(new FileInputStream( new File("temp\\partfiles\\char3.txt" )));

  SequenceInputStream sis = new SequenceInputStream(Collections.enumeration( list));

  BufferedReader br = new BufferedReader( new InputStreamReader(sis));

  String str = null;

  while (( str = br. readLine()) != null) {

    System.out.println( str);

  }

  br.close();

}

运行结果

喜欢Java

打印输出流(PrintStream/PrintWriter)

1. 概述

  • PrintStream/PrintWriter能够输出多种数据类型的表现形式,多用于基本数据的打印格式化字符串输出。
  • PrintStream/PrintWriter的目标可以是文件和所有的输出流。
  • PrintStream/PrintWriter永远不会抛出IOException,只能通过调用checkError()检查是否出现错误。
  • PrintStream可以实现换行符自动刷新,PrintWriter仅在调用println、printf或format方法时才可以实现自动刷新。
  • PrintStream保留了原始字节流的输出方式,而PrintWriter仅限于带编码的字符数据。
  • PrintStream打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类。
  • System.out类型即为PrintStream。

2. 构造器

  • PrintStream:

    PrintStream(File file)

    PrintStream(File file, String csn)

    PrintStream(OutputStream out)

    PrintStream(OutputStream out, boolean autoFlush)

    PrintStream(OutputStream out, boolean autoFlush, String encoding)

    PrintStream(String fileName)

    PrintStream(String fileName, String csn)

  • PrintWriter:

    PrintWriter(File file)

    PrintWriter(File file, String csn)

    PrintWriter(OutputStream out)

    PrintWriter(OutputStream out, boolean autoFlush)

    PrintWriter(String fileName)

    PrintWriter(String fileName, String csn)

    PrintWriter(Writer out)

    PrintWriter(Writer out, boolean autoFlush)

3. 常用方法

PrintStream和PrintWriter流的方法高度类似。

  • PrintStream  append(...)
  • boolean  checkError()
  • protected void  clearError()
  • void  close()
  • void  flush()
  • PrintStream  format(String format, Object... args)
  • void  print(...)
  • PrintStream  printf(String format, Object... args)
  • void  println(...)
  • protected void  setError()
  • void  write(...)

4. 示例


public static void printWrite() throws IOException {

  BufferedReader br = new BufferedReader( new FileReader("temp\\char.txt" ));

  PrintStream ps = new PrintStream(System.out, true );

  String line = null;

  while (( line = br.readLine()) != null) {

    ps.println(line.toUpperCase());

  }

  br.close();

}

标准输入输出流(System.in/System.out)

1. 概述

  • Java中内置了标准输入输出流,以静态域的形式提供在System类中。
  • 标准输入流System.in默认为键盘,标准输出流System.out默认为控制台。
  • 标准输入流为字节流InputStream类型,标准输出流为打印流PrintStream类型。
  • 标准输入输出流可以被其他流装饰类包装,以实现更多功能。
  • 标准输入输出流不需要关流。

2. 示例


package io;

 

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStreamReader;

import java.io.OutputStreamWriter;

 

public class KeyPrintDemo {

 

  public static void main(String[] args) throws IOException {

    // 需求:读取键盘上录入的字符,显示并以 utf-8编码保存成文件

    BufferedReader br = new BufferedReader( new InputStreamReader(System.in ));

    BufferedWriter bw =

        new BufferedWriter( new OutputStreamWriter( new FileOutputStream("temp\\key.txt" ), "utf-8" ));

    String str = null;

    while (( str = br.readLine()) != null) {

      if ("exit".equals( str)) {

        break;

      }

      System.out.println( str);

      bw.write(str);

      bw.newLine();

      bw.flush();

    }

    bw.close();

  }

}

对象输入输出流(ObjectInputStream/ObjectOutputStream)

1. 概述

  • 目标是字节流。
  • 将基本数据或对象转化为数据流的过程被称之为序列化,而反序列化就是相反的过程。
  • ObjectInputStream/ObjectOutputStream的作用就是基本数据类型或对象的序列化和反序列化。
  • ObjectInputStream确保从流创建的图形中所有对象的类型与Java虚拟机中显示的类相匹配。
  • 只有实现了java.io.Serializable或java.io.Externalizable接口的对象才能被序列化或反序列化。
  • 在Java中,字符串和数组都是对象,所以在序列化期间将其视为对象,读取时,需要将其强制转换为期望的类型。
  • 序列化和反序列化进程将忽略声明为瞬态或静态的字段。
  • readObject()及其他非父类读取方法的读取是无法判断文件末尾的,所以需要确定读取次数,以免出现EOFException。

2. 序列化接口Serializable

  • Serializable没有方法,不需要覆盖,是一个标记接口。
  • Serializable启动一个序列化功能,给每一个需要序列化的类都分配一个序列版本号。
  • 实现了Serializable的类需要定义序列版本号serialVersionUID,该号和该类相关联。
  • serialVersionUID用于序列化过程中的验证。在序列化时,会将这个序列号也一同保存到文件中。

    在反序列化会读取这个序列化和本类的序列化进行匹配,如果不匹配会抛出异常:java.io.InvalidClassException

  • 强烈建议:显式声明serialVersionUID,因为默认serialVersionUID的计算是对类的细节高度敏感的,所以可能会有不同平台

    计算默认的的serialVersionUID值有所不同的情况出现,可能就会发生不可预期的InvalidClassExceptions异常。

  • 强烈建议:使用private关键字修饰声明serialVersionUID,因为serialVersionUID只对当前类有效,对子类是毫无意义的。

3. 构造器

  • ObjectInputStream()
  • ObjectInputStream(InputStream in)
  • ObjectOutputStream()
  • ObjectOutputStream(OutputStream out)

4. 常用方法

  • ObjectInputStream:

    boolean  readBoolean()

    byte  readByte()

    char  readChar()

    double  readDouble()

    float  readFloat()

    void  readFully(byte[] buf)

    void  readFully(byte[] buf, int off, int len)

    int  readInt()

    long  readLong()

    Object  readObject()

    short  readShort()

  • ObjectOutputStream:

    void  write(byte[] buf)

    void  write(byte[] buf, int off, int len)

    void  writeBoolean(boolean val)

    void  writeByte(int val)

    void  writeBytes(String str)

    void  writeChar(int val)

    void  writeChars(String str)

    void  writeDouble(double val)

    void  writeFields()

    void  writeFloat(float val)

    void  writeInt(int val)

    void  writeLong(long val)

    void  writeObject(Object obj)

    void  writeShort(int val)

5. 示例

Employee.java


package bean;

 

public class Employee extends Person {

 

  private static final long serialVersionUID = 201411261L;

 

  /*

   * transient: 瞬态,被该修饰符修饰的变量将不被序列化。同样地,静态变量也不被序列化。

   */

  private transient int salary ;

 

  public Employee(String name, int age, int salary) {

    super(name, age);

    this.salary = salary;

  }

 

  public Employee() {

    super();

  }

 

  public int getSalary() {

    return salary;

  }

 

  public void setSalary( int salary) {

    this.salary = salary;

  }

 

  @Override

  public int hashCode() {

    final int prime = 31;

    int result = super.hashCode();

    result = prime * result + salary;

    return result;

  }

 

  @Override

  public boolean equals(Object obj ) {

    if (this == obj) return true ;

    if (!super.equals( obj)) return false ;

    if (getClass() != obj.getClass()) return false ;

    Employee other = (Employee) obj;

    if (salary != other.salary) return false;

    return true;

  }

 

  @Override

  public String toString() {

 

    return "Employee [Name=" + getName() + ", Age=" + getAge() + ", Salary=" + salary + "]" ;

  }

 

  public static void staticPrint() {

    System.out.println( "Static print run!");

  }

}

ObjectReader/Writer


public static Object objectRead() throws IOException, ClassNotFoundException {

  ObjectInputStream ois = new ObjectInputStream( new FileInputStream("temp\\os.txt" ));

  Object obj = ois.readObject();

  ois.close();

  return obj;

}

 

public static void objectWrite() throws IOException {

  Employee employee = new Employee( "Java", 25, 25000);

  ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("temp\\os.txt" ));

  oos.writeObject( employee);

  oos.close();

}

数据输入输出流(DataInputStream/DataOutputStream)

1. 概述

  • 目标为字节流。
  • 用于基本数据的内存原始数据的输入输出。
  • 该类特有的数据读取方法无法检测流的末尾,所以需要确定读取次数,以免出现EOFException异常。

2. 示例


public static int dataRead() throws IOException {

  DataInputStream dis = new DataInputStream( new FileInputStream("temp\\data.txt" ));

  int num = dis.readInt();

  dis.close();

  return num;

}

 

public static void dataWrite() throws IOException {

  DataOutputStream dos = new DataOutputStream( new FileOutputStream("temp\\data.txt" ));

  dos.writeInt(97);   // 写入4字节:00000000 00000000 00000000 01100001

  dos.write(97);  // 写入1字节

  dos.close();

}

数组输入输出流(ByteArrayInputStream/ByteArrayOutputStream)

1. 概述

  • 目标为内存数组,本质为字节流。
  • 该流是为了按流的方式处理数据,某些情况下将会更加方便。
  • 由于没有调用底层资源,所以该流不需要关闭。
  • 创建ByteArrayInputStream流对象时需要将字节数组作为数据源参数传入,而ByteArrayOutputStream流对象中维护了一个大数组用于存储数据。
  • 该流的方法与其他流类似,ByteArrayOutputStream流使用toByteArray()方法返回保存数据的数组。

2. 特有方法

ByteArrayOutputStream:

  • 将内置数组数据输出到一个流中

    void  writeTo(OutputStream out)

  • 将内置数组数据输出到一个数组中

    byte[]  toByteArray()

  • 缓冲区大小

    int  size()

  • String  toString()

    String  toString(String charsetName)

3. 示例


private static byte[] byteArray(byte[] b) {

  ByteArrayInputStream bis = new ByteArrayInputStream(b);

  ByteArrayOutputStream bos = new ByteArrayOutputStream(); // 内部有一个可自动增长的数组。

 

  int ch = 0;

  while(( ch= bis.read())!=-1){

      bos.write(ch);

  }

  //因为没有调用底层资源,所以不要关闭,即使调用了close,也没有任何效果,不会抛出IOException。

  return bos.toByteArray();

}

文件随机访问类(RandomAccessFile)

1. 概述

  • 目标为文件,可以实现字节流字符流的读写。
  • 提供了文件指针,可以进行随机读写,就类似存储在文件系统中的一个大型 byte 数组。
  • 创建对象时可选模式:"r"只读, "rw"读写, "rws"读写并将更新的内容和元数据同步到设备, 或者"rwd"读写并将更新的内容同步到设备。

2. "rw", "rws", "rwd" 的区别

  • 当操作的文件是存储在本地的基础存储设备上时(如硬盘, NandFlash等),"rws" 或 "rwd", "rw" 才有区别。
  • 当模式是 "rws" 并且操作的是基础存储设备上的文件;那么,每次“更改文件内容[如write()写入数据]” 或 “修改文件元数据(如文件的mtime)”时,都会将这些改变同步到基础存储设备上。
  • 当模式是 "rwd" 并且操作的是基础存储设备上的文件;那么,每次“更改文件内容[如write()写入数据]”时,都会将这些改变同步到基础存储设备上。
  • 当模式是 "rw" 并且操作的是基础存储设备上的文件;那么,关闭文件时,会将“文件内容的修改”同步到基础存储设备上。至于,“更改文件内容”时,是否会立即同步,取决于系统底层实现。

3. 构造器

  • RandomAccessFile(File file, String mode)
  • RandomAccessFile(String name, String mode)

4. 常用方法

  • void  close()
  • 获取文件指针

    long  getFilePointer()

  • 获取文件长度

    long  length()

  • int  read()

    int  read(byte[] b)

    int  read(byte[] b, int off, int len)

    boolean  readBoolean()

    byte  readByte()

    char  readChar()

    double  readDouble()

    float  readFloat()

    void  readFully(byte[] b)

    void  readFully(byte[] b, int off, int len)

    int  readInt()

    String  readLine()

    long  readLong()

    short  readShort()

    int  readUnsignedByte()

    int  readUnsignedShort()

    String  readUTF()

  • 设置文件指针

    void  seek(long pos)

  • 设置文件长度

    void  setLength(long newLength)

  • 跳过字节数

    int  skipBytes(int n)

  • void  write(byte[] b)

    void  write(byte[] b, int off, int len)

    void  write(int b)

    void  writeBoolean(boolean v)

    void  writeByte(int v)

    void  writeBytes(String s)

    void  writeChar(int v)

    void  writeChars(String s)

    void  writeDouble(double v)

    void  writeFloat(float v)

    void  writeInt(int v)

    void  writeLong(long v)

    void  writeShort(int v)

    void  writeUTF(String str)

5. 示例


public static void readFile() throws IOException {

  RandomAccessFile raf = new RandomAccessFile("temp\\random.txt" , "r" );

  raf.seek(4);

  int i = raf.readInt();

  System.out.println( i);

  raf.close();

}

 

public static void writeFile() throws IOException {

  RandomAccessFile raf = new RandomAccessFile("temp\\random.txt" , "rw" );

  raf.seek(4);

  raf.writeInt(102);

  System.out.println( raf.getFilePointer());

  raf.close();

}

案例

文件的切割与合并

1. 使用配置文件的方案


package io;

 

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.FileReader;

import java.io.FileWriter;

import java.io.IOException;

import java.io.SequenceInputStream;

import java.util.ArrayList;

import java.util.Collections;

import java.util.List;

import java.util.Properties;

 

/**

 * 使用配置文件的方法分割合并文件

 */

public class TSplitJoinByConfig {

 

  private static final int BUFFER_SIZE = 4096;

 

  /**

   * 以配置文件的方法分割文件

   * @param srcFile  需要分割的文件

   * @param destPath  分割文件的保存目录

   * @param len  分割文件的大小

   * @throws IOException

   */

  public void split(File srcFile, File destPath, long len) throws IOException {

    // 健壮性判断,源文件目的目录是否存在

    if (!srcFile.exists() || srcFile.isDirectory()) {

      throw new RuntimeException( "源文件不存在!" );

    }

    if (len <= 1024) {

      throw new RuntimeException( "分卷大小设置太小或错误!" );

    }

    if (!destPath.exists() || destPath.isFile()) {

      destPath.mkdirs();

    }

    // 定义缓冲区

    byte[] buf = new byte[ BUFFER_SIZE];

    // 定义分割文件序号

    int num = 0;

    // 定义缓冲区数据大小

    int bufLen = 0;

    // 定义单个分割文件剩余的写入字节数

    long splitLen = len;

    // 源文件名

    String fileName = srcFile.getName();

   

    FileInputStream fis = new FileInputStream( srcFile);

    FileOutputStream fos = null;

 

    while ( bufLen != -1) {

      // 单趟外层循环完成一个分割文件的写入

      splitLen = len;

      // 创建输出流,确定分割文件名

      fos = new FileOutputStream( new File( destPath, fileName + ".part" + String.format( "%02d", ++num )));

      while ( splitLen > 0) {

        // splitLen为该单个文件剩余的写入字节数,以此为依据来从输入流读取数据到缓冲区

        if ( splitLen >= BUFFER_SIZE) {

          bufLen = fis.read( buf);

        } else {

          bufLen = fis.read( buf, 0, ( int) splitLen);

        }

        if ( bufLen == -1) {

          break;

        }

        fos.write( buf, 0, bufLen);

        splitLen -= ( long) bufLen;

      }

      fos.close();

    }

    fis.close();

   

    // 将文件名和分割文件数写入配置文件

    Properties prop = new Properties();

    prop.setProperty( "FileName", srcFile .getName());

    prop.setProperty( "PartNo.", String.valueOf( num));

    FileWriter fw = new FileWriter( new File( destPath, srcFile.getName() + ".properties" ));

    prop.store(fw, "Split File Config");

    fw.close();

  }

 

  public void join(File splitFirFile) throws IOException {

    // 健壮性判断

    if (!splitFirFile.exists() || splitFirFile.isDirectory()) {

      throw new RuntimeException( "分割文件不存在!" );

    }

    // 分割文件名(不包括扩展名)

    String splitFileName = splitFirFile.getName().substring(0, splitFirFile.getName().length() - 7);

    // 分割文件目录

    File splitFilePath = splitFirFile.getParentFile();

    // 配置文件

    File propFile = new File( splitFilePath, splitFileName + ".properties");

    if (!propFile.exists() || propFile.isDirectory()) {

      throw new RuntimeException( "配置文件不存在!" );

    }

    // 读取配置文件

    Properties prop = new Properties();

    FileReader fr = new FileReader( propFile);

    prop.load(fr);

    fr.close();

    // 合并文件名

    String fileName = prop.getProperty( "FileName");

    int num = Integer.parseInt(prop.getProperty( "PartNo."));

    // 判断分割文件是否完整

    File[] splitFiles = new File[ num];

    for (int i = 1; i <= num; i++) {

      splitFiles[i - 1] = new File( splitFilePath, splitFileName + ".part" + String.format( "%02d", i ));

      if (!splitFiles[i - 1].exists() || splitFiles[ i - 1].isDirectory()) {

        throw new RuntimeException( "缺少分割文件:" + splitFiles[i - 1].getName());

      }

    }

   

    // 将流加入集合,并使用序列输入流完成合并

    List<FileInputStream> list = new ArrayList<FileInputStream>();

    for (int i = 1; i <= num; i++) {

      list.add(new FileInputStream( splitFiles[ i - 1]));

    }

    byte[] buf = new byte[ BUFFER_SIZE];

    int len = -1;

    SequenceInputStream sis = new SequenceInputStream(Collections.enumeration( list));

    FileOutputStream fos = new FileOutputStream( new File(splitFilePath, fileName ));

    while (( len = sis.read( buf)) != -1) {

      fos.write(buf, 0, len);

    }

    fos.close();

    sis.close();

  }

}

2. 不使用配置文件的方案


package io;

 

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.SequenceInputStream;

import java.util.ArrayList;

import java.util.Collections;

import java.util.List;

 

public class TSplitJoin {

 

  private static final int BUFFER_SIZE = 4096;

  private static final long PROPERTIES_SIZE = 1024;

 

  /**

   * 分割文件,必要信息存储到第一个文件中

   * @param srcFile  需要分割的文件

   * @param destPath  分割文件的保存目录

   * @param len  分割文件的大小

   * @throws IOException

   */

  public void split(File srcFile, File destPath, long len) throws IOException {

    // 健壮性判断,源文件目的目录是否存在

    if (!srcFile.exists() || srcFile.isDirectory()) {

      throw new RuntimeException( "源文件不存在!" );

    }

    if (len <= 4096) {

      throw new RuntimeException( "分卷大小设置太小或者错误!" );

    }

    if (!destPath.exists() || destPath.isFile()) {

      destPath.mkdirs();

    }

    // 定义缓冲区

    byte[] buf = new byte[ BUFFER_SIZE];

    // 定义分割文件序号

    int num = 0;

    // 定义写入缓冲区数据大小

    int bufLen = 0;

    // 定义单个分割文件剩余的写入字节数

    long splitLen = len - PROPERTIES_SIZE;

    // 源文件名

    String fileName = srcFile.getName();

   

    FileInputStream fis = new FileInputStream( srcFile);

    FileOutputStream fos = new FileOutputStream( new File(destPath, fileName + ".part01" ));

    String content = fileName + "\n" + (int)Math.ceil(( double)srcFile .length() / (double) len) + "\n";

    System.arraycopy(content .getBytes("utf-8" ), 0, buf , 0, content.getBytes().length );

    fos.write(buf, 0, (int)PROPERTIES_SIZE);

 

    while ( bufLen != -1) {

      // 单趟外层循环完成一个分割文件的写入

      // 创建输出流,确定分割文件名

      if (num != 0) {

        fos = new FileOutputStream( new File( destPath, fileName + ".part" + String.format( "%02d", ++num )));

      } else {

        num++;

      }

      while ( splitLen > 0) {

        // splitLen为该单个文件剩余的写入字节数,以此为依据来从输入流读取数据到缓冲区

        if ( splitLen >= BUFFER_SIZE) {

          bufLen = fis.read( buf);

        } else {

          bufLen = fis.read( buf, 0, ( int) splitLen);

        }

        if ( bufLen == -1) {

          break;

        }

        fos.write( buf, 0, bufLen);

        splitLen -= ( long) bufLen;

      }

      fos.close();

      splitLen = len;

    }

    fis.close();

  }

 

  public void join(File splitFirFile) throws IOException {

    // 健壮性判断

    if (!splitFirFile.exists() || splitFirFile.isDirectory()) {

      throw new RuntimeException( "分割文件不存在!" );

    }

    // 分割文件名(不包括扩展名)

    String splitFileName = splitFirFile.getName().substring(0, splitFirFile.getName().length() - 7);

    // 分割文件目录

    File splitFilePath = splitFirFile.getParentFile();

 

    // 读取配置信息

    byte[] buf = new byte[ BUFFER_SIZE];

    FileInputStream fis = new FileInputStream(splitFirFile );

    fis.read(buf, 0, (int)PROPERTIES_SIZE);

    fis.close();

    String[] prop = new String( buf, "utf-8").split("\n" );

    String fileName = prop[0];

    int num = Integer.parseInt(prop [1]);

   

    // 判断分割文件是否完整

    File[] splitFiles = new File[ num];

    for (int i = 1; i <= num; i++) {

      splitFiles[i - 1] = new File( splitFilePath, splitFileName + ".part" + String.format( "%02d", i ));

      if (!splitFiles[i - 1].exists() || splitFiles[ i - 1].isDirectory()) {

        throw new RuntimeException( "缺少分割文件:" + splitFiles[i - 1].getName());

      }

    }

   

    // 将流加入集合,并使用序列输入流完成合并

    List<FileInputStream> list = new ArrayList<FileInputStream>();

    for (int i = 1; i <= num; i++) {

      list.add(new FileInputStream( splitFiles[ i - 1]));

    }

    int len = -1;

    SequenceInputStream sis = new SequenceInputStream(Collections.enumeration( list));

    FileOutputStream fos = new FileOutputStream( new File(splitFilePath, fileName ));

    sis.skip(PROPERTIES_SIZE);

    while (( len = sis.read( buf)) != -1) {

      fos.write(buf, 0, len);

    }

    fos.close();

    sis.close();

  }

}

流总结

1. IO流框架继承结构

字节流

InputStream/OutputStream

|--FileInputStream/FileOutputStream

|--FilterInputStream/FilterOutputStream

|--BufferedInputStream/BufferedOutputStream

|--DataInputStream/DataOutputStream

|--PrintStream

|--ObjectInputStream/ObjectOutputStream

|--ByteArrayInputStream/ByteArrayOutputStream

|--SequenceInputStream

字符流

Reader/Writer

|--InputStreamReader/OutputStreamWriter

|--FileReader/FileWriter

|--BufferedReader/BufferedWriter

|--CharArrayReader/CharArrayWriter

|--StringReader/StringWriter

|--PrintWriter

2. IO流要素

目标

  • 源:InputStream/Reader及其子类
  • 目的:OutputStream/Writer及其子类

内容

  • 字节流:InputStream/OutputStream及其子类
  • 字符流:Reader/Writer及其子类

设备

  • 外存:FileInputStream/FileOutputStream、FileReader/FileWriter
  • 内存:ByteArrayInputStream/ByteArrayOutputStream、CharArrayReader/CharArrayWriter、StringReader/StringWriter
  • 键盘和控制台:System.in/System.out
  • 网络:Socket

附加功能(装饰类)

  • 转换:InputStreamReader/OutputStreamWriter(字节流)
  • 缓冲区:BufferedInputStream/BufferedOutputStream(字节流)、BufferedReader/BufferedWriter(字符流)
  • 多源:SequenceInputStream(字节流)
  • 序列化:ObjectInputStream/ObjectOutputStream(字节流)
  • 保证数据的表现形式:PrintStream(字节流,文件)、PrintWriter(字节流,字符流,文件)
  • 保证数据原始字节:DataInputStream/DataOutputStream(字节流)

1. 概述

  • SequenceInputStream是字节流的包装类,能够提供多个流序列输入功能。
  • 序列流只有输入流,适合完成多个源一个目的的需求。
  • SequenceInputStream支持枚举输入,若源的数量大于2个,那么需要先建立枚举再通过构造器创建序列输入流。
  • SequenceInputStream的使用和其他流基本类似。
  • SequenceInputStream本质属于字节流。

2. 构造器

  • SequenceInputStream(Enumeration<? extends InputStream> e)
  • SequenceInputStream(InputStream s1, InputStream s2)

3. 常用方法

  • int  available()
  • void  close()
  • int  read()
  • int  read(byte[] b, int off, int len)

4. 示例

源文件1:"喜欢",源文件2:"Ja",源文件3:"va"。


public static void SequenceDemo() throws IOException {

  List<FileInputStream> list = new ArrayList<FileInputStream>();

  list.add(new FileInputStream( new File("temp\\partfiles\\char1.txt" )));

  list.add(new FileInputStream( new File("temp\\partfiles\\char2.txt" )));

  list.add(new FileInputStream( new File("temp\\partfiles\\char3.txt" )));

  SequenceInputStream sis = new SequenceInputStream(Collections.enumeration( list));

  BufferedReader br = new BufferedReader( new InputStreamReader(sis));

  String str = null;

  while (( str = br. readLine()) != null) {

    System.out.println( str);

  }

  br.close();

}

运行结果

喜欢Java

打印输出流(PrintStream/PrintWriter)

1. 概述

  • PrintStream/PrintWriter能够输出多种数据类型的表现形式,多用于基本数据的打印格式化字符串输出。
  • PrintStream/PrintWriter的目标可以是文件和所有的输出流。
  • PrintStream/PrintWriter永远不会抛出IOException,只能通过调用checkError()检查是否出现错误。
  • PrintStream可以实现换行符自动刷新,PrintWriter仅在调用println、printf或format方法时才可以实现自动刷新。
  • PrintStream保留了原始字节流的输出方式,而PrintWriter仅限于带编码的字符数据。
  • PrintStream打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类。
  • System.out类型即为PrintStream。

2. 构造器

  • PrintStream:

    PrintStream(File file)

    PrintStream(File file, String csn)

    PrintStream(OutputStream out)

    PrintStream(OutputStream out, boolean autoFlush)

    PrintStream(OutputStream out, boolean autoFlush, String encoding)

    PrintStream(String fileName)

    PrintStream(String fileName, String csn)

  • PrintWriter:

    PrintWriter(File file)

    PrintWriter(File file, String csn)

    PrintWriter(OutputStream out)

    PrintWriter(OutputStream out, boolean autoFlush)

    PrintWriter(String fileName)

    PrintWriter(String fileName, String csn)

    PrintWriter(Writer out)

    PrintWriter(Writer out, boolean autoFlush)

3. 常用方法

PrintStream和PrintWriter流的方法高度类似。

  • PrintStream  append(...)
  • boolean  checkError()
  • protected void  clearError()
  • void  close()
  • void  flush()
  • PrintStream  format(String format, Object... args)
  • void  print(...)
  • PrintStream  printf(String format, Object... args)
  • void  println(...)
  • protected void  setError()
  • void  write(...)

4. 示例


public static void printWrite() throws IOException {

  BufferedReader br = new BufferedReader( new FileReader("temp\\char.txt" ));

  PrintStream ps = new PrintStream(System.out, true );

  String line = null;

  while (( line = br.readLine()) != null) {

    ps.println(line.toUpperCase());

  }

  br.close();

}

标准输入输出流(System.in/System.out)

1. 概述

  • Java中内置了标准输入输出流,以静态域的形式提供在System类中。
  • 标准输入流System.in默认为键盘,标准输出流System.out默认为控制台。
  • 标准输入流为字节流InputStream类型,标准输出流为打印流PrintStream类型。
  • 标准输入输出流可以被其他流装饰类包装,以实现更多功能。
  • 标准输入输出流不需要关流。

2. 示例


package io;

 

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStreamReader;

import java.io.OutputStreamWriter;

 

public class KeyPrintDemo {

 

  public static void main(String[] args) throws IOException {

    // 需求:读取键盘上录入的字符,显示并以 utf-8编码保存成文件

    BufferedReader br = new BufferedReader( new InputStreamReader(System.in ));

    BufferedWriter bw =

        new BufferedWriter( new OutputStreamWriter( new FileOutputStream("temp\\key.txt" ), "utf-8" ));

    String str = null;

    while (( str = br.readLine()) != null) {

      if ("exit".equals( str)) {

        break;

      }

      System.out.println( str);

      bw.write(str);

      bw.newLine();

      bw.flush();

    }

    bw.close();

  }

}

对象输入输出流(ObjectInputStream/ObjectOutputStream)

1. 概述

  • 目标是字节流。
  • 将基本数据或对象转化为数据流的过程被称之为序列化,而反序列化就是相反的过程。
  • ObjectInputStream/ObjectOutputStream的作用就是基本数据类型或对象的序列化和反序列化。
  • ObjectInputStream确保从流创建的图形中所有对象的类型与Java虚拟机中显示的类相匹配。
  • 只有实现了java.io.Serializable或java.io.Externalizable接口的对象才能被序列化或反序列化。
  • 在Java中,字符串和数组都是对象,所以在序列化期间将其视为对象,读取时,需要将其强制转换为期望的类型。
  • 序列化和反序列化进程将忽略声明为瞬态或静态的字段。
  • readObject()及其他非父类读取方法的读取是无法判断文件末尾的,所以需要确定读取次数,以免出现EOFException。

2. 序列化接口Serializable

  • Serializable没有方法,不需要覆盖,是一个标记接口。
  • Serializable启动一个序列化功能,给每一个需要序列化的类都分配一个序列版本号。
  • 实现了Serializable的类需要定义序列版本号serialVersionUID,该号和该类相关联。
  • serialVersionUID用于序列化过程中的验证。在序列化时,会将这个序列号也一同保存到文件中。

    在反序列化会读取这个序列化和本类的序列化进行匹配,如果不匹配会抛出异常:java.io.InvalidClassException

  • 强烈建议:显式声明serialVersionUID,因为默认serialVersionUID的计算是对类的细节高度敏感的,所以可能会有不同平台

    计算默认的的serialVersionUID值有所不同的情况出现,可能就会发生不可预期的InvalidClassExceptions异常。

  • 强烈建议:使用private关键字修饰声明serialVersionUID,因为serialVersionUID只对当前类有效,对子类是毫无意义的。

3. 构造器

  • ObjectInputStream()
  • ObjectInputStream(InputStream in)
  • ObjectOutputStream()
  • ObjectOutputStream(OutputStream out)

4. 常用方法

  • ObjectInputStream:

    boolean  readBoolean()

    byte  readByte()

    char  readChar()

    double  readDouble()

    float  readFloat()

    void  readFully(byte[] buf)

    void  readFully(byte[] buf, int off, int len)

    int  readInt()

    long  readLong()

    Object  readObject()

    short  readShort()

  • ObjectOutputStream:

    void  write(byte[] buf)

    void  write(byte[] buf, int off, int len)

    void  writeBoolean(boolean val)

    void  writeByte(int val)

    void  writeBytes(String str)

    void  writeChar(int val)

    void  writeChars(String str)

    void  writeDouble(double val)

    void  writeFields()

    void  writeFloat(float val)

    void  writeInt(int val)

    void  writeLong(long val)

    void  writeObject(Object obj)

    void  writeShort(int val)

5. 示例

Employee.java


package bean;

 

public class Employee extends Person {

 

  private static final long serialVersionUID = 201411261L;

 

  /*

   * transient: 瞬态,被该修饰符修饰的变量将不被序列化。同样地,静态变量也不被序列化。

   */

  private transient int salary ;

 

  public Employee(String name, int age, int salary) {

    super(name, age);

    this.salary = salary;

  }

 

  public Employee() {

    super();

  }

 

  public int getSalary() {

    return salary;

  }

 

  public void setSalary( int salary) {

    this.salary = salary;

  }

 

  @Override

  public int hashCode() {

    final int prime = 31;

    int result = super.hashCode();

    result = prime * result + salary;

    return result;

  }

 

  @Override

  public boolean equals(Object obj ) {

    if (this == obj) return true ;

    if (!super.equals( obj)) return false ;

    if (getClass() != obj.getClass()) return false ;

    Employee other = (Employee) obj;

    if (salary != other.salary) return false;

    return true;

  }

 

  @Override

  public String toString() {

 

    return "Employee [Name=" + getName() + ", Age=" + getAge() + ", Salary=" + salary + "]" ;

  }

 

  public static void staticPrint() {

    System.out.println( "Static print run!");

  }

}

ObjectReader/Writer


public static Object objectRead() throws IOException, ClassNotFoundException {

  ObjectInputStream ois = new ObjectInputStream( new FileInputStream("temp\\os.txt" ));

  Object obj = ois.readObject();

  ois.close();

  return obj;

}

 

public static void objectWrite() throws IOException {

  Employee employee = new Employee( "Java", 25, 25000);

  ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("temp\\os.txt" ));

  oos.writeObject( employee);

  oos.close();

}

数据输入输出流(DataInputStream/DataOutputStream)

1. 概述

  • 目标为字节流。
  • 用于基本数据的内存原始数据的输入输出。
  • 该类特有的数据读取方法无法检测流的末尾,所以需要确定读取次数,以免出现EOFException异常。

2. 示例


public static int dataRead() throws IOException {

  DataInputStream dis = new DataInputStream( new FileInputStream("temp\\data.txt" ));

  int num = dis.readInt();

  dis.close();

  return num;

}

 

public static void dataWrite() throws IOException {

  DataOutputStream dos = new DataOutputStream( new FileOutputStream("temp\\data.txt" ));

  dos.writeInt(97);   // 写入4字节:00000000 00000000 00000000 01100001

  dos.write(97);  // 写入1字节

  dos.close();

}

数组输入输出流(ByteArrayInputStream/ByteArrayOutputStream)

1. 概述

  • 目标为内存数组,本质为字节流。
  • 该流是为了按流的方式处理数据,某些情况下将会更加方便。
  • 由于没有调用底层资源,所以该流不需要关闭。
  • 创建ByteArrayInputStream流对象时需要将字节数组作为数据源参数传入,而ByteArrayOutputStream流对象中维护了一个大数组用于存储数据。
  • 该流的方法与其他流类似,ByteArrayOutputStream流使用toByteArray()方法返回保存数据的数组。

2. 特有方法

ByteArrayOutputStream:

  • 将内置数组数据输出到一个流中

    void  writeTo(OutputStream out)

  • 将内置数组数据输出到一个数组中

    byte[]  toByteArray()

  • 缓冲区大小

    int  size()

  • String  toString()

    String  toString(String charsetName)

3. 示例


private static byte[] byteArray(byte[] b) {

  ByteArrayInputStream bis = new ByteArrayInputStream(b);

  ByteArrayOutputStream bos = new ByteArrayOutputStream(); // 内部有一个可自动增长的数组。

 

  int ch = 0;

  while(( ch= bis.read())!=-1){

      bos.write(ch);

  }

  //因为没有调用底层资源,所以不要关闭,即使调用了close,也没有任何效果,不会抛出IOException。

  return bos.toByteArray();

}

文件随机访问类(RandomAccessFile)

1. 概述

  • 目标为文件,可以实现字节流字符流的读写。
  • 提供了文件指针,可以进行随机读写,就类似存储在文件系统中的一个大型 byte 数组。
  • 创建对象时可选模式:"r"只读, "rw"读写, "rws"读写并将更新的内容和元数据同步到设备, 或者"rwd"读写并将更新的内容同步到设备。

2. "rw", "rws", "rwd" 的区别

  • 当操作的文件是存储在本地的基础存储设备上时(如硬盘, NandFlash等),"rws" 或 "rwd", "rw" 才有区别。
  • 当模式是 "rws" 并且操作的是基础存储设备上的文件;那么,每次“更改文件内容[如write()写入数据]” 或 “修改文件元数据(如文件的mtime)”时,都会将这些改变同步到基础存储设备上。
  • 当模式是 "rwd" 并且操作的是基础存储设备上的文件;那么,每次“更改文件内容[如write()写入数据]”时,都会将这些改变同步到基础存储设备上。
  • 当模式是 "rw" 并且操作的是基础存储设备上的文件;那么,关闭文件时,会将“文件内容的修改”同步到基础存储设备上。至于,“更改文件内容”时,是否会立即同步,取决于系统底层实现。

3. 构造器

  • RandomAccessFile(File file, String mode)
  • RandomAccessFile(String name, String mode)

4. 常用方法

  • void  close()
  • 获取文件指针

    long  getFilePointer()

  • 获取文件长度

    long  length()

  • int  read()

    int  read(byte[] b)

    int  read(byte[] b, int off, int len)

    boolean  readBoolean()

    byte  readByte()

    char  readChar()

    double  readDouble()

    float  readFloat()

    void  readFully(byte[] b)

    void  readFully(byte[] b, int off, int len)

    int  readInt()

    String  readLine()

    long  readLong()

    short  readShort()

    int  readUnsignedByte()

    int  readUnsignedShort()

    String  readUTF()

  • 设置文件指针

    void  seek(long pos)

  • 设置文件长度

    void  setLength(long newLength)

  • 跳过字节数

    int  skipBytes(int n)

  • void  write(byte[] b)

    void  write(byte[] b, int off, int len)

    void  write(int b)

    void  writeBoolean(boolean v)

    void  writeByte(int v)

    void  writeBytes(String s)

    void  writeChar(int v)

    void  writeChars(String s)

    void  writeDouble(double v)

    void  writeFloat(float v)

    void  writeInt(int v)

    void  writeLong(long v)

    void  writeShort(int v)

    void  writeUTF(String str)

5. 示例


public static void readFile() throws IOException {

  RandomAccessFile raf = new RandomAccessFile("temp\\random.txt" , "r" );

  raf.seek(4);

  int i = raf.readInt();

  System.out.println( i);

  raf.close();

}

 

public static void writeFile() throws IOException {

  RandomAccessFile raf = new RandomAccessFile("temp\\random.txt" , "rw" );

  raf.seek(4);

  raf.writeInt(102);

  System.out.println( raf.getFilePointer());

  raf.close();

}

案例

文件的切割与合并

1. 使用配置文件的方案


package io;

 

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.FileReader;

import java.io.FileWriter;

import java.io.IOException;

import java.io.SequenceInputStream;

import java.util.ArrayList;

import java.util.Collections;

import java.util.List;

import java.util.Properties;

 

/**

 * 使用配置文件的方法分割合并文件

 */

public class TSplitJoinByConfig {

 

  private static final int BUFFER_SIZE = 4096;

 

  /**

   * 以配置文件的方法分割文件

   * @param srcFile  需要分割的文件

   * @param destPath  分割文件的保存目录

   * @param len  分割文件的大小

   * @throws IOException

   */

  public void split(File srcFile, File destPath, long len) throws IOException {

    // 健壮性判断,源文件目的目录是否存在

    if (!srcFile.exists() || srcFile.isDirectory()) {

      throw new RuntimeException( "源文件不存在!" );

    }

    if (len <= 1024) {

      throw new RuntimeException( "分卷大小设置太小或错误!" );

    }

    if (!destPath.exists() || destPath.isFile()) {

      destPath.mkdirs();

    }

    // 定义缓冲区

    byte[] buf = new byte[ BUFFER_SIZE];

    // 定义分割文件序号

    int num = 0;

    // 定义缓冲区数据大小

    int bufLen = 0;

    // 定义单个分割文件剩余的写入字节数

    long splitLen = len;

    // 源文件名

    String fileName = srcFile.getName();

   

    FileInputStream fis = new FileInputStream( srcFile);

    FileOutputStream fos = null;

 

    while ( bufLen != -1) {

      // 单趟外层循环完成一个分割文件的写入

      splitLen = len;

      // 创建输出流,确定分割文件名

      fos = new FileOutputStream( new File( destPath, fileName + ".part" + String.format( "%02d", ++num )));

      while ( splitLen > 0) {

        // splitLen为该单个文件剩余的写入字节数,以此为依据来从输入流读取数据到缓冲区

        if ( splitLen >= BUFFER_SIZE) {

          bufLen = fis.read( buf);

        } else {

          bufLen = fis.read( buf, 0, ( int) splitLen);

        }

        if ( bufLen == -1) {

          break;

        }

        fos.write( buf, 0, bufLen);

        splitLen -= ( long) bufLen;

      }

      fos.close();

    }

    fis.close();

   

    // 将文件名和分割文件数写入配置文件

    Properties prop = new Properties();

    prop.setProperty( "FileName", srcFile .getName());

    prop.setProperty( "PartNo.", String.valueOf( num));

    FileWriter fw = new FileWriter( new File( destPath, srcFile.getName() + ".properties" ));

    prop.store(fw, "Split File Config");

    fw.close();

  }

 

  public void join(File splitFirFile) throws IOException {

    // 健壮性判断

    if (!splitFirFile.exists() || splitFirFile.isDirectory()) {

      throw new RuntimeException( "分割文件不存在!" );

    }

    // 分割文件名(不包括扩展名)

    String splitFileName = splitFirFile.getName().substring(0, splitFirFile.getName().length() - 7);

    // 分割文件目录

    File splitFilePath = splitFirFile.getParentFile();

    // 配置文件

    File propFile = new File( splitFilePath, splitFileName + ".properties");

    if (!propFile.exists() || propFile.isDirectory()) {

      throw new RuntimeException( "配置文件不存在!" );

    }

    // 读取配置文件

    Properties prop = new Properties();

    FileReader fr = new FileReader( propFile);

    prop.load(fr);

    fr.close();

    // 合并文件名

    String fileName = prop.getProperty( "FileName");

    int num = Integer.parseInt(prop.getProperty( "PartNo."));

    // 判断分割文件是否完整

    File[] splitFiles = new File[ num];

    for (int i = 1; i <= num; i++) {

      splitFiles[i - 1] = new File( splitFilePath, splitFileName + ".part" + String.format( "%02d", i ));

      if (!splitFiles[i - 1].exists() || splitFiles[ i - 1].isDirectory()) {

        throw new RuntimeException( "缺少分割文件:" + splitFiles[i - 1].getName());

      }

    }

   

    // 将流加入集合,并使用序列输入流完成合并

    List<FileInputStream> list = new ArrayList<FileInputStream>();

    for (int i = 1; i <= num; i++) {

      list.add(new FileInputStream( splitFiles[ i - 1]));

    }

    byte[] buf = new byte[ BUFFER_SIZE];

    int len = -1;

    SequenceInputStream sis = new SequenceInputStream(Collections.enumeration( list));

    FileOutputStream fos = new FileOutputStream( new File(splitFilePath, fileName ));

    while (( len = sis.read( buf)) != -1) {

      fos.write(buf, 0, len);

    }

    fos.close();

    sis.close();

  }

}

2. 不使用配置文件的方案


package io;

 

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.SequenceInputStream;

import java.util.ArrayList;

import java.util.Collections;

import java.util.List;

 

public class TSplitJoin {

 

  private static final int BUFFER_SIZE = 4096;

  private static final long PROPERTIES_SIZE = 1024;

 

  /**

   * 分割文件,必要信息存储到第一个文件中

   * @param srcFile  需要分割的文件

   * @param destPath  分割文件的保存目录

   * @param len  分割文件的大小

   * @throws IOException

   */

  public void split(File srcFile, File destPath, long len) throws IOException {

    // 健壮性判断,源文件目的目录是否存在

    if (!srcFile.exists() || srcFile.isDirectory()) {

      throw new RuntimeException( "源文件不存在!" );

    }

    if (len <= 4096) {

      throw new RuntimeException( "分卷大小设置太小或者错误!" );

    }

    if (!destPath.exists() || destPath.isFile()) {

      destPath.mkdirs();

    }

    // 定义缓冲区

    byte[] buf = new byte[ BUFFER_SIZE];

    // 定义分割文件序号

    int num = 0;

    // 定义写入缓冲区数据大小

    int bufLen = 0;

    // 定义单个分割文件剩余的写入字节数

    long splitLen = len - PROPERTIES_SIZE;

    // 源文件名

    String fileName = srcFile.getName();

   

    FileInputStream fis = new FileInputStream( srcFile);

    FileOutputStream fos = new FileOutputStream( new File(destPath, fileName + ".part01" ));

    String content = fileName + "\n" + (int)Math.ceil(( double)srcFile .length() / (double) len) + "\n";

    System.arraycopy(content .getBytes("utf-8" ), 0, buf , 0, content.getBytes().length );

    fos.write(buf, 0, (int)PROPERTIES_SIZE);

 

    while ( bufLen != -1) {

      // 单趟外层循环完成一个分割文件的写入

      // 创建输出流,确定分割文件名

      if (num != 0) {

        fos = new FileOutputStream( new File( destPath, fileName + ".part" + String.format( "%02d", ++num )));

      } else {

        num++;

      }

      while ( splitLen > 0) {

        // splitLen为该单个文件剩余的写入字节数,以此为依据来从输入流读取数据到缓冲区

        if ( splitLen >= BUFFER_SIZE) {

          bufLen = fis.read( buf);

        } else {

          bufLen = fis.read( buf, 0, ( int) splitLen);

        }

        if ( bufLen == -1) {

          break;

        }

        fos.write( buf, 0, bufLen);

        splitLen -= ( long) bufLen;

      }

      fos.close();

      splitLen = len;

    }

    fis.close();

  }

 

  public void join(File splitFirFile) throws IOException {

    // 健壮性判断

    if (!splitFirFile.exists() || splitFirFile.isDirectory()) {

      throw new RuntimeException( "分割文件不存在!" );

    }

    // 分割文件名(不包括扩展名)

    String splitFileName = splitFirFile.getName().substring(0, splitFirFile.getName().length() - 7);

    // 分割文件目录

    File splitFilePath = splitFirFile.getParentFile();

 

    // 读取配置信息

    byte[] buf = new byte[ BUFFER_SIZE];

    FileInputStream fis = new FileInputStream(splitFirFile );

    fis.read(buf, 0, (int)PROPERTIES_SIZE);

    fis.close();

    String[] prop = new String( buf, "utf-8").split("\n" );

    String fileName = prop[0];

    int num = Integer.parseInt(prop [1]);

   

    // 判断分割文件是否完整

    File[] splitFiles = new File[ num];

    for (int i = 1; i <= num; i++) {

      splitFiles[i - 1] = new File( splitFilePath, splitFileName + ".part" + String.format( "%02d", i ));

      if (!splitFiles[i - 1].exists() || splitFiles[ i - 1].isDirectory()) {

        throw new RuntimeException( "缺少分割文件:" + splitFiles[i - 1].getName());

      }

    }

   

    // 将流加入集合,并使用序列输入流完成合并

    List<FileInputStream> list = new ArrayList<FileInputStream>();

    for (int i = 1; i <= num; i++) {

      list.add(new FileInputStream( splitFiles[ i - 1]));

    }

    int len = -1;

    SequenceInputStream sis = new SequenceInputStream(Collections.enumeration( list));

    FileOutputStream fos = new FileOutputStream( new File(splitFilePath, fileName ));

    sis.skip(PROPERTIES_SIZE);

    while (( len = sis.read( buf)) != -1) {

      fos.write(buf, 0, len);

    }

    fos.close();

    sis.close();

  }

}

流总结

1. IO流框架继承结构

字节流

InputStream/OutputStream

|--FileInputStream/FileOutputStream

|--FilterInputStream/FilterOutputStream

|--BufferedInputStream/BufferedOutputStream

|--DataInputStream/DataOutputStream

|--PrintStream

|--ObjectInputStream/ObjectOutputStream

|--ByteArrayInputStream/ByteArrayOutputStream

|--SequenceInputStream

字符流

Reader/Writer

|--InputStreamReader/OutputStreamWriter

|--FileReader/FileWriter

|--BufferedReader/BufferedWriter

|--CharArrayReader/CharArrayWriter

|--StringReader/StringWriter

|--PrintWriter

2. IO流要素

目标

  • 源:InputStream/Reader及其子类
  • 目的:OutputStream/Writer及其子类

内容

  • 字节流:InputStream/OutputStream及其子类
  • 字符流:Reader/Writer及其子类

设备

  • 外存:FileInputStream/FileOutputStream、FileReader/FileWriter
  • 内存:ByteArrayInputStream/ByteArrayOutputStream、CharArrayReader/CharArrayWriter、StringReader/StringWriter
  • 键盘和控制台:System.in/System.out
  • 网络:Socket

附加功能(装饰类)

  • 转换:InputStreamReader/OutputStreamWriter(字节流)
  • 缓冲区:BufferedInputStream/BufferedOutputStream(字节流)、BufferedReader/BufferedWriter(字符流)
  • 多源:SequenceInputStream(字节流)
  • 序列化:ObjectInputStream/ObjectOutputStream(字节流)
  • 保证数据的表现形式:PrintStream(字节流,文件)、PrintWriter(字节流,字符流,文件)
  • 保证数据原始字节:DataInputStream/DataOutputStream(字节流)
时间: 2024-11-03 21:48:04

[学习笔记]Java IO之其他流及总结的相关文章

[学习笔记]Java IO之字符流

概述 字符流是专门用于处理文本文件的流,其中包括了文本编码相关转换工作. 字符流只可以处理文本字符数据. 每个字符流都包括一种编码方式,可以使用系统默认,也可以自行设定. 编码 1. 编码表 生活中语言文字和计算机中对应的数据表 2. 常见编码表 ASCII:包括字母和常见符号,1字节编码,首位为0. ISO8859-1:包括拉丁字母,1字节编码,首位为1. GB2312:简体中文码表,包括6~7千汉子及符号,2字节编码,2个字节首位均为1. GBK:中文码表,包括GB2312全部编码,约2万个

学习笔记-java IO流总结 转载

1.什么是IO Java中I/O操作主要是指使用Java进行输入,输出操作. Java所有的I/O机制都是基于数据流进行输入输出,这些数据流表示了字符或者字节数据的流动序列.Java的I/O流提供了读写数据的标准方法.任何Java中表示数据源的对象都会提供以数据流的方式读写它的数据的方法. Java.io是大多数面向数据流的输入/输出类的主要软件包.此外,Java也对块传输提供支持,在核心库 java.nio中采用的便是块IO. 流IO的好处是简单易用,缺点是效率较低.块IO效率很高,但编程比较

Java学习笔记之 IO包 字符流

字符流:Writer/Reader(字符流) 通过子类FileWriter和FileReader实现父类实例化 package iotest; import java.io.File; import java.io.Writer; import java.io.Reader; import java.io.FileWriter; import java.io.FileReader; public class OutputStreamDemo2 { public static void main(

Java IO之处理流(缓冲流、转换流)

一.处理流: 增强功能,提供性能,在节点流之上. 二.节点流与处理流的关系 节点流(字节流.字符流)处于IO操作的第一线,所有操作必须通过它们进行: 处理流可以对其他流进行处理(提高效率或操作灵活性). 三.缓冲流 1.字节缓冲流 BufferedInputStream BufferedOutputStream package IOBuffer; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; im

EasyARM i.mx28学习笔记——文件IO方式操作GPIO

0 前言 本文描述如果通过文件IO sysfs方式控制EasyARM GPIO端口.通过sysfs方式控制GPIO,先访问/sys/class/gpio目录,向export文件写入GPIO编号,使得该GPIO的操作接口从内核空间暴露到用户空间,GPIO的操作接口包括direction和value等,direction控制GPIO方向,而value可控制GPIO输出或获得GPIO输入. Linux学习可从应用出发,先不纠结Linux驱动编写,先把Linux给玩起来. [相关博文] [EasyARM

Java IO结构各种流详解

花了两天时间研究了一下Java IO的流,对于各种流,加深了一下理解 首先看我做的思维导图 文件流 public class FileIO { public static void main(String[] args) throws Exception { File file1 = new File(System.getProperty("user.dir") + "/c.txt"); // 找到第一个文件的File // 找到目标文件路径 File file2

数据库学习笔记3 基本的查询流 2 select lastname+&#39;,&#39;+firstname as fullname order by lastname+&#39;,&#39;+firstname len() left() stuff() percent , select top(3) with ties

order by子句对查询结果集进行排序 多列和拼接 多列的方式就很简单了 select firstname,lastname from person.person order by lastname,firstname; 这句话表示根据lastname和firstname两列进行排序,并且是先按照lastname进行排序如果有相同的值就按照firstname进行排序. 拼接很有意思,可以写成这个样子 select lastname+','+firstname as fullname from

Java IO学习笔记(五)对象流

1.Object流:直接将Object写入或读出. 2.序列化:将Object直接转化成字节流写到硬盘或网络上. 3.如果想把一个对象转化成字节流,该对象的实体类必须得实现Serializable接口,Serializable接口是标记性接口,它并没有任何方法,只是用于标识该类可以被序列化. 4.transient关键字,用于修饰成员变量,表示该成员变量是透明的,与Serializable接口同用,表示序列化的时候不考虑该成员变量.序列化时存的是该成员变量数据类型的默认值. 5.External

Java IO学习笔记(三)转换流、数据流、字节数组流

转换流 1.转换流:将字节流转换成字符流,转换之后就可以一个字符一个字符的往程序写内容了,并且可以调用字符节点流的write(String s)方法,还可以在外面套用BufferedReader()和BufferedWriter,并使用它们的readLine 和 newLine方法. 2.有两种转换流:InputStreamReader.OutputStreamWriter 练习小程序1: package test.io.transfer; import java.io.FileOutputSt