Java I/O 总结

1 I/O流的概念,分类
2 I/O所有类的结构图及详解
3 何为NIO,和传统I/O有何区别
4 在开发中正确使用I/O流
5 IO流面试题

1 I/O流的概念,分类

Java中I/O操作主要是指使用Java进行输入,输出操作。Java所有的I/O机制都是基于数据流进行输入输出,这些数据流表示了字符或者字节数据的流动序列。

当程序需要读取数据的时候,就会建立一个通向数据源的连接,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会建立一个通向目的地的连接。

1.1 分类

(1)按数据来源(去向)分类

流序列中的数据既可以是未经加工的原始二进制数据,也可以是经一定编码处理后符合某种格式规定的特定数据。因此Java中的流分为两种:

  • 1 字节流:数据流中最小的数据单元是字节;
  • 2 字符流:数据流中最小的数据单元是字符, Java中的字符是Unicode编码,一个字符占用两个字节。

文件类: FileInputStream, FileOutputStream, FileReader, FileWriter。
byte[]类:ByteArrayInputStream, ByteArrayOutputStream。
Char[]类: CharArrayReader, CharArrayWriter。
String类: StringBufferInputStream, StringReader, StringWriter。
网络数据类:InputStream, OutputStream, Reader, Writer。
缓冲类:BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter。
格式化类:PrintStream, PrintWriter
二进制格式(只要不能确定是纯文本的): InputStream, OutputStream及其所有带Stream结束的子类。
纯文本格式(含纯英文与汉字或其他编码方式);Reader, Writer及其所有带Reader, Writer的子类。

2 I/O所有类的结构图及详解

image.png

(1)InputStream

public abstract class InputStream extends Object implements Closeable

InputStream:所有字节输入流的父类,所有的字节输入流都要继承该类。

常用的字节输入流

(a)FileInputStream(文件输入流):

从文件系统中的某个文件中获得输入字节。哪些文件可用取决于主机环境。FileInputStream 用于读取诸如图像数据之类的原始字节流。

构造方法:
//通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。
public FileInputStream(File file);

//通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。
public FileInputStream(String name);
常用的方法:
//从此输入流中读取一个数据字节。
public int read();

//从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。
public int read(byte[] b);

//从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。off:目标数组 b 中的起始偏移量。
public int read(byte[] b,int off,int len);

从文件中读取数据:

import java.io.FileInputStream;

/**
 * FileInputStream:节点流(低级流),从文件中读入数据
 * @author Administrator
 *
 */
public class FISDemo01 {
    public static void main(String[] args){
        String content=null;
        try {
            int size=0;
            //定义一个字节缓冲区,该缓冲区的大小根据需要来定义
            byte[] buffer=new byte[1024];
            FileInputStream fis=new FileInputStream("FOSDemo.txt");
            //循环来读取该文件中的数据
            while((size=fis.read(buffer))!=-1){
                content=new String(buffer, 0, size);
                System.out.println(content);
            }
        //关闭此文件输入流并释放与此流有关的所有系统资源。 
        fis.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
(b)ObjectInputStream(对象输入流)

ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。

ObjectOutputStream 和 ObjectInputStream 分别与 FileOutputStream 和 FileInputStream 一起使用时,可以为应用程序提供对对象图形的持久存储。ObjectInputStream 用于恢复那些以前序列化的对象。其他用途包括使用套接字流在主机之间传递对象,或者用于编组和解组远程通信系统中的实参和形参。

ObjectInputStream 确保从流创建的图形中所有对象的类型与 Java 虚拟机中显示的类相匹配。使用标准机制按需加载类。只有支持 java.io.Serializable 或 java.io.Externalizable 接口的对象才能从流读取。

构造函数
//为完全重新实现 ObjectOutputStream 的子类提供一种方法,让它不必分配仅由 ObjectOutputStream 的实现使用的私有数据。
protected ObjectOutputStream();

//创建写入指定 OutputStream 的 ObjectOutputStream。此构造方法将序列化流部分写入底层流;
//调用者可以通过立即刷新流,确保在读取头部时,用于接收 ObjectInputStreams 构造方法不会阻塞。
public ObjectOutputStream(OutputStream out);

常用的方法
//将指定的对象写入 ObjectOutputStream。对象的类、类的签名,以及类及其所有超类型的非瞬态和非静态字段的值都将被写入。
public final void writeObject(Object obj);

将对象写入到文件中永久存储

import java.io.Serializable;
import java.util.ArrayList;

//Person类实现了Serializable接口,所以该类才能被序列化;反之,如果没有实现该接口的类则不能被序列化。
public class Person implements Serializable{
    /**
     * 序列化的ID,只要加了该版本号,在反序列化的时候不论你的类的属性是否改变,只要是版本号不变那么尽经可能的兼容新版本。
     * 如果版本号改变了,那么反序列化的过程中就会抛出异常。
     */
    private static final long serialVersionUID = 6871740251451383067L;
    private String name;
    private int age;
    private char sex;
    private ArrayList<String> other;

    public Person(){

    }
    public Person(String name, int age, char sex, ArrayList<String> other) {
        super();
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.other = other;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age)throws Exception {
        this.age = age;
    }
    public char getSex() {
        return sex;
    }
    public void setSex(char sex) {
        this.sex = sex;
    }
    public ArrayList<String> getOther() {
        return other;
    }
    public void setOther(ArrayList<String> other) {
        this.other = other;
    }
    public static long getSerialversionuid() {
        return serialVersionUID;
    }
}

将Person对象写入到文件中

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;

import tools.Person;

/**
 * ObjectOutputStream:高级流,对象输出流,只能将支持 java.io.Serializable 接口的对象写入流中
 *
 * 将一个特定的数据结构转换为一组字节的过程称之为序列化
 * 将一组字节转换为特定的数据结构的过程称之为反序列化
 * 将数据写入硬盘长久保存的过程称之为持久化
 *
 * @author Administrator
 *
 */
public class OOSDemo01 {
    public static void main(String[] args){
        try {
            ArrayList<String> other=new ArrayList<String>();
            other.add("清华大学");
            other.add("软件学院");
            other.add("软件工程");
            Person person=new Person("小明", 22, '男', other);
            FileOutputStream fos=new FileOutputStream("OOS.txt");
            ObjectOutputStream oos=new ObjectOutputStream(fos);
            //将一个对象经行序列化时需要实现Serializable接口
            oos.writeObject(person);
            oos.flush();
            oos.close();
            System.out.println("对象写入成功!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

(c)PipedInputStream(管道输入流)

管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节。通常,数据由某个线程从 PipedInputStream 对象读取,并由其他线程将其写入到相应的 PipedOutputStream。不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。管道输入流包含一个缓冲区,可在缓冲区限定的范围内将读操作和写操作分离开。如果向连接管道输出流提供数据字节的线程不再存在,则认为该管道已损坏。

(d)BufferedInputStream(缓冲字节输入流)

BufferedInputStream 为另一个输入流添加一些功能,即缓冲输入以及支持 mark 和 reset 方法的能力。在创建 BufferedInputStream 时,会创建一个内部缓冲区数组。在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。mark 操作记录输入流中的某个点,reset 操作使得在从包含的输入流中获取新字节之前,再次读取自最后一次 mark 操作后读取的所有字节。

构造方法
//创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
//创建一个内部缓冲区数组并将其存储在 buf 中,该buf的大小默认为8192。
public BufferedInputStream(InputStream in);

//创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
//创建一个长度为 size 的内部缓冲区数组并将其存储在 buf 中。
public BufferedInputStream(InputStream in,int size);

常用方法
//从该输入流中读取一个字节
public int read();

//从此字节输入流中给定偏移量处开始将各字节读取到指定的 byte 数组中。
public int read(byte[] b,int off,int len);

从文件中读入数据

import java.io.BufferedInputStream;
import java.io.FileInputStream;

/**
 * BufferedInputStream:处理流(高级流),缓冲输入流
 * @author Administrator
 *
 */
public class BISDemo01 {
    public static void main(String[] args){
        try {
            FileInputStream fis=new FileInputStream("BISDemo.txt");
            BufferedInputStream bis=new BufferedInputStream(fis);
            String content=null;
             //自己定义一个缓冲区
            byte[] buffer=new byte[10240];
            int flag=0;
            while((flag=bis.read(buffer))!=-1){
                content+=new String(buffer, 0, flag);
            }
            System.out.println(content);
            //关闭的时候只需要关闭最外层的流就行了
            bis.close();
        } catch (Exception e) {
                e.printStackTrace();
        }
    }
}

(2)OutputStream

public abstract class OutputStream extends Object implements Closeable, Flushable

常用的字节输出流

(1)FileOutputStream(文件输出流)

文件输出流是用于将数据写入 File 或 FileDescriptor 的输出流。文件是否可用或能否可以被创建取决于基础平台。特别是某些平台一次只允许一个 FileOutputStream(或其他文件写入对象)打开文件进行写入。在这种情况下,如果所涉及的文件已经打开,则此类中的构造方法将失败。

构造方法
//创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
public FileOutputStream(File file);

//创建一个向指定 File 对象表示的文件中写入数据的文件输出流。如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处。
public FileOutputStream(File file,boolean append);

//创建一个向具有指定名称的文件中写入数据的输出文件流。
public FileOutputStream(String name);

//创建一个向具有指定 name 的文件中写入数据的输出文件流。如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处。
public FileOutputStream(String name,boolean append);
常用方法
//向文件中写入一个字节大小的数据
public void write(int b);

//将 b.length 个字节从指定 byte 数组写入此文件输出流中。
public void write(byte[] b);

//指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。 
public void write(byte[] b,int off,int len);

向文件中写出数据:

import java.io.FileOutputStream;

/**
 * FileOutputStream:节点流(低级流),向文件中写出数据 
 * @author Administrator
 *
 */
public class FOSDemo01 {
    public static void main(String[] args){
        try {
            //向文件中写入字节数组
            String font="输出流是用来写入数据的!";
            FileOutputStream fos = new FileOutputStream("FOSDemo.txt");
            fos.write(font.getBytes());
            //关闭此文件输出流并释放与此流有关的所有系统资源。此文件输出流不能再用于写入字节。 如果此流有一个与之关联的通道,则关闭该通道。 
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

使用FileInputStream和FileOutputStream实现文件的复制:

import java.io.FileInputStream;
import java.io.FileOutputStream;

/**
 * 使用文件输入流和文件输出流实现文件的复制
 * @author Administrator
 *
 */
public class SummaryFISAndFOS {
    public static void main(String[] args){
        /**
         * 1.先将文件中的内容读入到输入流中
         * 2.将输入流中的数据通过输出流写入到目标文件中
         * 3.关闭输入流和输出流
         */
        try {
            long begin=System.currentTimeMillis();
            //从输入流中读取数据
            FileInputStream fis=new FileInputStream("FOSDemo.txt");
            //向输出流中写入数据
            FileOutputStream fos=new FileOutputStream("FISAndFOSDest.txt");
            //先定义一个字节缓冲区,减少I/O次数,提高读写效率
            byte[] buffer=new byte[10240];
            int size=0;
            while((size=fis.read(buffer))!=-1){
                fos.write(buffer, 0, size);
            }
            fis.close();
            fos.close();
            long end=System.currentTimeMillis();
            System.out.println("使用文件输入流和文件输出流实现文件的复制完毕!耗时:"+(end-begin)+"毫秒");
        } catch (Exception e) {
            e.printStackTrace();
        }
        //解决JNI问题(Java Native Interface)
        System.exit(0);
    }
}

(2)ObjectOutputStream(对象输出流)

ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。

只能将支持 java.io.Serializable 接口的对象写入流中。每个 serializable 对象的类都被编码,编码内容包括类名和类签名、对象的字段值和数组值,以及从初始对象中引用的其他所有对象的闭包。

//为完全重新实现 ObjectInputStream 的子类提供一种方式,
//让它不必分配仅由 ObjectInputStream 的实现使用的私有数据。
protected ObjectInputStream();

//创建从指定 InputStream 读取的 ObjectInputStream。
//从流读取序列化头部并予以验证。在对应的 ObjectOutputStream 写入并刷新头部之前,此构造方法将阻塞。
public ObjectInputStream(InputStream in);

常用方法
//从 ObjectInputStream 读取对象。对象的类、类的签名和类及所有其超类型的非瞬态和非静态字段的值都将被读取。
public final Object readObject();

将已经序列化的对象反序列化

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.ArrayList;

import tools.Person;

/**
 * ObjectInputStream:高级流,对象输入流
 * ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
 * 有支持 java.io.Serializable 或 java.io.Externalizable 接口的对象才能从流读取。
 * @author Administrator
 *
 */
public class OISDemo01 {
    public static void main(String[] args){
        try {
            FileInputStream fis=new FileInputStream("OOS.txt");
            ObjectInputStream ois=new ObjectInputStream(fis);
            Person person=(Person)ois.readObject();
            System.out.println(person.getName());
            System.out.println(person.getAge());
            System.out.println(person.getSex());
            ArrayList<String> other=person.getOther();
            for (String string : other) {
                System.out.println(string);
            }
            ois.close();
            System.out.println("反序列化成功!");
        } catch (Exception e) {
                e.printStackTrace();
        }
        System.exit(0);
    }
}

(3)PipedOutputStream(管道输出流)

可以将管道输出流连接到管道输入流来创建通信管道。管道输出流是管道的发送端。通常,数据由某个线程写入 PipedOutputStream 对象,并由其他线程从连接的 PipedInputStream 读取。不建议对这两个对象尝试使用单个线程,因为这样可能会造成该线程死锁。如果某个线程正从连接的管道输入流中读取数据字节,但该线程不再处于活动状态,则该管道被视为处于 毁坏 状态。

(4)BufferedOutputStream(字节缓冲输出流)

该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。

构造方法
//创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
public BufferedOutputStream(OutputStream out);

//创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流。
public BufferedOutputStream(OutputStream out,int size);

常用方法
//向输出流中输出一个字节
public void write(int b);

//将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此缓冲的输出流。
public void write(byte[] b,int off,int len);

//刷新此缓冲的输出流。这迫使所有缓冲的输出字节被写出到底层输出流中。
public void flush();

向文件中写出数据

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;

/**
 * BufferedOutputStream:处理流(高级流),缓冲输出流
 * @author Administrator
 *
 */
public class BOSDemo01 {
    public static void main(String[] args){
        try {
            FileOutputStream fos=new FileOutputStream("BOSDemo.txt");
            BufferedOutputStream bos=new BufferedOutputStream(fos);
            String content="我是缓冲输出流测试数据!";
            bos.write(content.getBytes(),0,content.getBytes().length);
            bos.flush();
            bos.close();
        } catch (Exception e) {
                e.printStackTrace();
        }
    }
}

使用缓冲输出流和缓冲输入流实现文件的复制

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;

/**
 * 使用缓冲输出流和缓冲输入流实现文件的复制
 * @author Administrator
 *
 */
public class SummaryBISAndBOS {
    public static void main(String[] args){
        /**
         * 1.先将文件中的内容读入到缓冲输入流中
         * 2.将输入流中的数据通过缓冲输出流写入到目标文件中
         * 3.关闭输入流和输出流
         */
        try {
            long begin=System.currentTimeMillis();
            FileInputStream fis=new FileInputStream("BISDemo.txt");
            BufferedInputStream bis=new BufferedInputStream(fis);

            FileOutputStream fos=new FileOutputStream("BOSDemo.txt");
            BufferedOutputStream bos=new BufferedOutputStream(fos);

            int size=0;
            byte[] buffer=new byte[10240];
            while((size=bis.read(buffer))!=-1){
                bos.write(buffer, 0, size);
            }
            //刷新此缓冲的输出流,保证数据全部都能写出
            bos.flush();
            bis.close();
            bos.close();
            long end=System.currentTimeMillis();
            System.out.println("使用缓冲输出流和缓冲输入流实现文件的复制完毕!耗时:"+(end-begin)+"毫秒");
        } catch (Exception e) {
                e.printStackTrace();
        }
    }
}

(3)Reader

(a)BufferedReader(缓冲字符输入流)

从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。

构造函数
//创建一个使用默认大小(8192)输入缓冲区的缓冲字符输入流
public BufferedReader(Reader in);

//创建一个使用指定大小输入缓冲区的缓冲字符输入流
public BufferedReader(Reader in,int sz);
常用方法
//读取单个字符
public int read();

//读入字符数组中的某一部分
public int read(char[] cbuf,int off,int len);

//读取一个文本行
public String readLine();

//跳过字符
public long skip(long n);

从文件中读取文本数据

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;

/**
 * BufferedReader:缓冲字符输入流,高级流
 * 从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
 * 可以以行为单位读取字符串
 * @author Administrator
 *
 */
public class BRDemo01 {
    public static void main(String[] args) {
        try {
            /**
             * 逐个字符的读取
             */
            FileInputStream fis=new FileInputStream("pw.txt");
            InputStreamReader isr=new InputStreamReader(fis, "UTF-8");
            BufferedReader br=new BufferedReader(isr);
            int size=0;
            while((size=br.read())!=-1){
                char content=(char)size;
                System.out.println(content);
            }

            /**
             * 整行读取
             */
            FileInputStream fis1=new FileInputStream("pw.txt");
            InputStreamReader isr1=new InputStreamReader(fis1, "UTF-8");
            BufferedReader br1=new BufferedReader(isr1);
            String isNull=null;
            /**
             * public String readLine()
             * 该方法的返回值如果为null,则说明已经读到文件的末尾了
             */
            while((isNull=br1.readLine())!=null){
                System.out.println(isNull);
            }
            br.close();
            br1.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

使用PrintWriter/BufferedReader实现文件的复制

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;

public class SummaryPWAndBR {
    public static void main(String[] args) {
        /**
         * 使用字符缓冲输入流和字符缓冲输出流实现文件的复制
         * 1.打开文件
         * 2.设置字符集
         * 3.进行读写操作
         * 4.关闭流
         */
        try {
            long begin=System.currentTimeMillis();
            //打开源文件
            FileInputStream fis=new FileInputStream("pwSrc.txt");
            //设置字符集
            InputStreamReader isr=new InputStreamReader(fis, "UTF-8");
            //从源文件读入数据
            BufferedReader br=new BufferedReader(isr);


            //打开目标文件
            FileOutputStream fos=new FileOutputStream("pwDest.txt");
            //设置字符集
            OutputStreamWriter osw=new OutputStreamWriter(fos, "UTF-8");
            //写入数据到目标文件
            PrintWriter pw=new PrintWriter(osw, true);

            int size=0;
            char[] buffer=new char[10240];
            while((size=br.read(buffer, 0, buffer.length))!=-1){
                pw.write(buffer, 0, size);
            }
            br.close();
            pw.close();
            long end=System.currentTimeMillis();
            System.out.println("使用字符缓冲输入流实现文件的复制完毕!耗时:"+(end-begin)+"毫秒");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
(b)InputStreamReader(字符输入流)

InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。

每次调用 InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader。

构造函数
//创建一个使用默认字符集的InputStreamReader
public InputStreamReader(InputStream in);

//创建使用给定字符集的InputStreamReader
public InputStreamReader(InputStream in,Charset cs);

//创建使用指定字符集的InputStreamReader
public InputStreamReader(InputStream in,String charsetName);

常用方法
//读取单个字符
public int read();

//将字符读入数组中的某一部分
public int read(char[] cbuf,int offset,int length);

从文件中读入文本数据

import java.io.FileInputStream;
import java.io.InputStreamReader;

/**
 * Reader是所有字符输入流的父类,是抽象类
 * InputStreamReader:字符输入流(高级流),用于读入文本数据
 * @author Administrator
 *
 */
public class ISRDemo01 {
    public static void main(String[] args){
        /**
         * 从文件中读取字符数据
         * 1.从文件中读取(FileInputStream)
         * 2.字符数据(InputStreamReader)
         */
        try {
            FileInputStream fis=new FileInputStream("OSW.txt");
            InputStreamReader isr=new InputStreamReader(fis, "UTF-8");
            int size=0;
            //每次读入一个字符,读"低16位"
            while((size=isr.read())!=-1){
                char content=(char)size;
                System.out.print(content);
            }
            isr.close();

            FileInputStream fis1=new FileInputStream("OSW.txt");
            InputStreamReader isr1=new InputStreamReader(fis1, "UTF-8");
            char[] buffer=new char[1024];
      //每次读入一个字符数组
            while((size=isr1.read(buffer))!=-1){
                String content=new String(buffer);
                System.out.println(content);
            }
            isr1.close();


        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

使用字符输入流和字符输出流实现文件的复制

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class SummaryISRAndOSW {

    public static void main(String[] args) {
        /**
         * 实现文本文件的复制
         * 1.从文件中读入字符数据
         * 2.输出到另外一个文件中
         */
        try {
            long begin=System.currentTimeMillis();
            FileInputStream fis=new FileInputStream("ISR.txt");
            InputStreamReader isr=new InputStreamReader(fis, "UTF-8");

            FileOutputStream fos=new FileOutputStream("OSW.txt");
            OutputStreamWriter osw=new OutputStreamWriter(fos, "UTF-8");

            char[] buffer=new char[10240];
            int size=0;
            while((size=isr.read(buffer))!=-1){
                osw.write(buffer, 0, size);
                osw.flush();
            }
            isr.close();
            osw.close();
            long end=System.currentTimeMillis();
            System.out.println("使用文件输入流和文件输出流实现文件的复制完毕!耗时:"+(end-begin)+"毫秒");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

(4)Writer

public abstract class Writer extends Object implements Appendable, Closeable, Flushable

常用的字符输出流

(a)BufferedWriter(缓冲字符输出流)

将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。

可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。

该类提供了 newLine() 方法,它使用平台自己的行分隔符概念,此概念由系统属性 line.separator 定义。并非所有平台都使用新行符 (‘\n’) 来终止各行。因此调用此方法来终止每个输出行要优于直接写入新行符。

构造方法
//创建一个使用默认大小输出缓冲区的缓冲字符输出流
public BufferedWriter(Writer out);

//创建一个使用给定大小输出缓冲区的新缓冲字符输出流
public BufferedWriter(Writer out,int sz);
//写入字符数组的某一部分
public void write(char[] cbuf,int off,int len);

//写入单个字符
public void write(int c);

//写入字符串的某一部分
public void write(String s,int off,int len);

//写入一个行分隔符
public void newLine();

将字符串写入到文件中

import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;

/**
 * BufferedWriter:字符缓冲输出流(高级流)
 * @version 1.0
 * @parameter  
 * @since  
 * @return  
 */
public class BWDemo01 {

    public static void main(String[] args) {
        try {
            //打开文件
            FileOutputStream fos=new FileOutputStream("pw.txt");
            //设置编码集
            OutputStreamWriter osw=new OutputStreamWriter(fos,"UTF-8");
            //将字符输出流包装为字符缓冲输出流
            BufferedWriter bw=new BufferedWriter(osw);
            bw.write("BW测试文字");
            bw.append("BWBWBWBWBW");
            bw.flush();
            bw.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
(b)OutputStreamWriter(缓冲字符输出流)

OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。

每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。注意,传递给 write() 方法的字符没有缓冲。

为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中,以避免频繁调用转换器。

(5)File

public class File extends Object implements Serializable, Comparable<File>
Java中File类的基本使用

(6)RandomAcessFile

Java中RandomAcessFile类基本使用详解

https://blog.csdn.net/u013087513/article/details/51911577

3 何为NIO,和传统I/O有何区别

我们使用InputStream从输入流中读取数据时,如果没有读取到有效的数据,程序将在此处阻塞该线程的执行。其实传统的输入里和输出流都是阻塞式的进行输入和输出。 不仅如此,传统的输入流、输出流都是通过字节的移动来处理的(即使我们不直接处理字节流,但底层实现还是依赖于字节处理),也就是说,面向流的输入和输出一次只能处理一个字节,因此面向流的输入和输出系统效率通常不高。

从JDk1.4开始,java提供了一系列改进的输入和输出处理的新功能,这些功能被统称为新IO(NIO)。新增了许多用于处理输入和输出的类,这些类都被放在java.nio包及其子包下,并且对原io的很多类都以NIO为基础进行了改写。新增了满足NIO的功能。

NIO采用了内存映射对象的方式来处理输入和输出,NIO将文件或者文件的一块区域映射到内存中,这样就可以像访问内存一样来访问文件了。通过这种方式来进行输入/输出比传统的输入和输出要快的多。

JDk1.4使用NIO改写了传统Io后,传统Io的读写速度和NIO差不了太多。

4 在开发中正确使用I/O流

(1)决定使用哪个类以及它的构造进程的一般准则如下(不考虑特殊需要):

  • 考虑最原始的数据格式是什么:是否为文本?
  • 是输入还是输出?
  • 是否需要转换流:InputStreamReader, OutputStreamWriter?
  • 数据来源(去向)是什么:文件?内存?网络?
  • 是否要缓冲:bufferedReader (特别注明:一定要注意的是readLine()是否有定义,有什么比read, write更特殊的输入或输出方法)。
  • 是否要格式化输出:print?

(2)2点原则

  • 如果是操作二进制文件那我们就使用字节流,如果操作的是文本文件那我们就使用字符流。
  • 尽可能的多使用处理流,这会使我们的代码更加灵活,复用性更好。

5 IO流面试题

1 什么是比特(Bit),什么是字节(Byte),什么是字符(Char),它们长度是多少,各有什么区别

Bit最小的二进制单位 ,是计算机的操作部分 取值0或者1
Byte是计算机操作数据的最小单位由8位bit组成 取值(-128-127)
Char是用户的可读写的最小单位,在java里面由16位bit组成 取值(0-65535)

Bit 是最小单位 计算机 只能认识 0或者1

8个字节 是给计算机看的
字符 是看到的东西 一个字符=二个字节

2 什么是流,按照传输的单位,分成哪两种流,并且他们的父类叫什么流是指数据的传输

字节流,字符流
字节流:InputStream OutputStream
字符流:Reader Writer

3 流按照传输的方向可以分为哪两种,分别举例说明

输入输出相对于程序
输入流InputStream
输出流OutputStream

4 按照实现功能分为哪两种,分别举例说明

节点流,处理流
节点流:OutputStream
处理流: OutputStreamWriter

5 BufferedReader属于哪种流,它主要是用来做什么的,它里面有那些经典的方法

属于处理流中的缓冲流,可以将读取的内容存在内存里面,有readLine()方法

6 什么是节点流,什么是处理流,它们各有什么用处,处理流的创建有什么特征

节点流 直接与数据源相连,用于输入或者输出
处理流:在节点流的基础上对之进行加工,进行一些功能的扩展
处理流的构造器必须要 传入节点流的子类

7 如果我要对字节流进行大量的从硬盘读取,要用那个流,为什么

BufferedInputStream 使用缓冲流能够减少对硬盘的损伤

8 如果我要打印出不同类型的数据到数据源,那么最适合的流是那个流,为什么

Printwriter 可以打印各种数据类型。

9 怎么样把我们控制台的输出改成输出到一个文件里面,这个技术叫什么

SetOut(printWriter,printStream)重定向

11 怎么样把输出字节流转换成输出字符流,说出它的步骤

使用 转换处理流OutputStreamWriter 可以将字符流转为字节流
New OutputStreamWriter(new FileOutputStream(File file));

12 把包括基本类型在内的数据和字符串按顺序输出到数据源,或者按照顺序从数据源读入,一般用哪两个流

DataInputStream DataOutputStream

13 把一个对象写入数据源或者从一个数据源读出来,用哪两个流

ObjectInputStream ObjectOutputStream

14 什么叫对象序列化,什么是反序列化,实现对象序列化需要做哪些工作

对象序列化,将对象以二进制的形式保存在硬盘上
反序列化;将二进制的文件转化为对象读取
实现serializable接口

不想让字段放在硬盘上就加transient

15 如果在对象序列化的时候不想给一个字段的数据保存在硬盘上面,采用那个关键字?

transient关键字

16 在实现序列化接口是时候一般要生成一个serialVersionUID字段,它叫做什么,一般有什么用

是版本号,要保持版本号的一致 来进行序列化
为了防止序列化出错

17 InputStream里的read()返回的是什么,read(byte[] data)是什么意思,返回的是什么值

返回的是所读取的字节的int型(范围0-255)
read(byte [ ] data)将读取的字节储存在这个数组
返回的就是传入数组参数个数
Read 字节读取字节 字符读取字符

18 OutputStream里面的write()是什么意思,write(byte b[], int off, int len)这个方法里面的三个参数分别是什么意思

write将指定字节传入数据源
Byte b[ ]是byte数组
b[off]是传入的第一个字符
b[off+len-1]是传入的最后的一个字符
len是实际长度

19 流一般需要不需要关闭,如果关闭的话在用什么方法,一般要在那个代码块里面关闭比较好,处理流是怎么关闭的,如果有多个流互相调用传入是怎么关闭的?

流一旦打开就必须关闭,使用close方法
放入finally语句块中(finally 语句一定会执行)
调用的处理流就关闭处理流
多个流互相调用只关闭最外层的流

20 Java中的所有的流可以分为几大类,它们的名字是什么,各代表什么

分为 字节输入流 InputStream
字节输出流 OutputStream
字符输入流 Reader
字符输出流 Writer
所有流都是这四个流的子类

21 说下常用的io流

InputStream,OutputStream,
FileInputStream,FileOutputStream,
BufferedInputStream,BufferedOutputStream
Reader,Writer
BufferedReader,BufferedWriter

22 io流怎样读取文件的?

使用File对象获取文件路径,通过字符流Reader加入文件,使用字符缓存流BufferedReader处理Reader,再定义一个字符串,循环遍历出文件。代码如下:

File file = new File("d:/spring.txt");
try {
Reader reader = new FileReader(file);
BufferedReader buffered = new BufferedReader(reader);
String data = null;
while((data = buffered.readLine())!=null){
System.out.println(data);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

23 说说你对io流的理解

Io流主要是用来处理输入输出问题,常用的io流有InputStream,OutputStream,Reader,Writer等

24 JAVA的IO流和readLine方法

Java的io流用来处理输入输出问题,readLine是BufferedReader里的一个方法,用来读取一行。

25 用什么把对象动态的写入磁盘中,写入要实现什么接口。

ObjectInputStream,需要实现Serializable接口

26 FileInputStream 创建详情,就是怎样的创建不报错,它列出了几种形式!

FileInputStream是InputStream的子类,通过接口定义,子类实现创建FileInputStream,

27 用io流中的技术,指定一个文件夹的目录,获取此目录下的所有子文件夹路径

28 请问你在什么情况下会在你得java代码中使用可序列化? 如何实现java序列化?

把一个对象写入数据源或者从一个数据源读出来,使用可序列化,需要实现Serializable接口

参考链接

https://blog.csdn.net/nightcurtis/article/details/51324105
https://www.cnblogs.com/zhaoyanjun/p/6292384.html
https://blog.csdn.net/lyb1832567496/article/details/52712218
http://www.importnew.com/23708.html


END

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容

  • 概述 java.io 包几乎包含了所有操作输入、输出需要的类。所有这些流类代表了输入源和输出目标。java.io ...
    Steven1997阅读 9,164评论 1 25
  • 不知道你有没有过这样的经历,你的孩子总是打碎东西,虽然不是故意的,但你还是觉得很生气;你的学生调皮捣蛋,忘记写作业...
    小马驹0921阅读 402评论 0 0
  • 阿亮说:螺蛳粉已经吃完了。我昨天连夜又下单,有一种“药不能停”的感觉。 今天收拾屋子,发现了一包螺蛳粉。拿到阿亮面...
    茉莉大大阅读 128评论 0 0