基础IO流必须掌握

简介: 《基础系列》

Java 中有几种类型的流?

答:两种流分别是字节流,字符流。

字节流继承于InputStream、OutputStream,字符流继承于Reader、Writer。在java.io 包中还有许多其他的流,主要是为了提高性能和使用方便。

补充:关于Java的IO需要注意的有两点:一是两种对称性(输入和输出的对称性,字节和字符的对称性);二是两种设计模式(适配器模式和装潢模式)。另外Java中的流不同于C#的是它只有一个维度一个方向。

补充:下面用IO和NIO两种方式实现文件拷贝,这个题目在面试的时候是经常被问到的。


packagecom.bjsxt;

importjava.io.FileInputStream;

importjava.io.FileOutputStream;

importjava.io.IOException;

importjava.io.InputStream;

importjava.io.OutputStream;

importjava.nio.ByteBuffer;

importjava.nio.channels.FileChannel;

 

publicclassMyUtil {

  privateMyUtil() {

        thrownewAssertionError();

    }

 

    publicstaticvoidfileCopy(String source, String target) throwsIOException {

        try(InputStream in = newFileInputStream(source)) {

            try(OutputStream out = newFileOutputStream(target)) {

                byte[] buffer = newbyte[4096];

                intbytesToRead;

                while((bytesToRead = in.read(buffer)) != -1) {

                    out.write(buffer, 0, bytesToRead);

                }

            }

        }

    }

 

    publicstaticvoidfileCopyNIO(String source, String target) throwsIOException {

        try(FileInputStream in = newFileInputStream(source)) {

            try(FileOutputStream out = newFileOutputStream(target)) {

                FileChannel inChannel = in.getChannel();

                FileChannel outChannel = out.getChannel();

                ByteBuffer buffer = ByteBuffer.allocate(4096);

                while(inChannel.read(buffer) != -1) {

                    buffer.flip();

                    outChannel.write(buffer);

                    buffer.clear();

                 }

            }

        }

    }

}

注意:上面用到Java 7的TWR,使用TWR后可以不用在finally中释放外部资源 ,从而让代码更加优雅。


.写一个方法,输入一个文件名和一个字符串,统计这个字符串在这个文件中出现的次数。

答:代码如下:


packagecom.bjsxt;

importjava.io.BufferedReader;

importjava.io.FileReader;

 

publicclassAccount {

    // 工具类中的方法都是静态方式访问的因此将构造器私有不允许创建对象(绝对好习惯)

    privateAccount() {

        thrownewAssertionError();

    }

    /**

     * 统计给定文件中给定字符串的出现次数

     * @param filename  文件名

     * @param word 字符串

     * @return 字符串在文件中出现的次数

     */

    publicstaticintcountWordInFile(String filename, String word) {

        intcounter = 0;

        try(FileReader fr = newFileReader(filename)) {

            try(BufferedReader br = newBufferedReader(fr)) {

                String line = null;

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

                    intindex = -1;

                    while(line.length() >= word.length() && (index = line.indexOf(word)) >= 0) {

                        counter++;

                        line = line.substring(index + word.length());

                    }

                }

            }

        catch(Exception ex) {

            ex.printStackTrace();

        }

        returncounter;

    }

}


输入流和输出流联系和区别,节点流和处理流联系和区别

首先,你要明白什么是“流”。直观地讲,流就像管道一样,在程序和文件之间,输入输出的方向是针对程序而言,向程序中读入东西,就是输入流,从程序中向外读东西,就是输出流。

输入流是得到数据,输出流是输出数据,而节点流,处理流是流的另一种划分,按照功能不同进行的划分。节点流,可以从或向一个特定的地方(节点)读写数据。处理流是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如BufferedReader。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。

268.字符流字节流联系区别;什么时候使用字节流和字符流?

字符流和字节流是流的一种划分,按处理照流的数据单位进行的划分。两类都分为输入和输出操作。在字节流中输出数据主要是使用OutputStream完成,输入使的是InputStream,在字符流中输出主要是使用Writer类完成,输入流主要使用Reader类完成。这四个都是抽象类。

字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,而字节流处理单元为1个字节,操作字节和字节数组。字节流是最基本的,所有的InputStrem和OutputStream的子类都是,主要用在处理二进制数据,它是按字节来处理的 但实际中很多的数据是文本,又提出了字符流的概念,它是按虚拟机的编码来处理,也就是要进行字符集的转化 这两个之间通过 InputStreamReader,OutputStreamWriter来关联,实际上是通过byte[]和String来关联的。

269.列举常用字节输入流和输出流并说明其特点,至少5对。

FileInputStream 从文件系统中的某个文件中获得输入字节。

FileOutputStream 从程序当中的数据,写入到指定文件。

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

ByteArrayInputStream 包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪 read 方法要提供的下一个字节。

FilterInputStream 包含其他一些输入流,它将这些流用作其基本数据源,它可以直接传输数据或提供一些额外的功能。FilterInputStream 类本身只是简单地重写那些将所有请求传递给所包含输入流的 InputStream 的所有方法。FilterInputStream 的子类可进一步重写这些方法中的一些方法,并且还可以提供一些额外的方法和字段。

StringBufferInputStream此类允许应用程序创建输入流,在该流中读取的字节由字符串内容提供。应用程序还可以使用ByteArrayInputStream 从 byte 数组中读取字节。 只有字符串中每个字符的低八位可以由此类使用。

ByteArrayOutputStream此类实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray() 和 toString() 获取数据。

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

FilterOutputStream类是过滤输出流的所有类的超类。这些流位于已存在的输出流(基础 输出流)之上,它们将已存在的输出流作为其基本数据接收器,但可能直接传输数据或提供一些额外的功能。 FilterOutputStream 类本身只是简单地重写那些将所有请求传递给所包含输出流的 OutputStream 的所有方法。FilterOutputStream 的子类可进一步地重写这些方法中的一些方法,并且还可以提供一些额外的方法和字段。

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

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


说明缓冲流的优点和原理

不带缓冲的流的工作原理:

它读取到一个字节/字符,就向用户指定的路径写出去,读一个写一个,所以就慢了。

带缓冲的流的工作原理:

读取到一个字节/字符,先不输出,等凑足了缓冲的最大容量后一次性写出去,从而提高了工作效率

优点:减少对硬盘的读取次数,降低对硬盘的损耗。


序列化的定义、实现和注意事项

想把一个对象写在硬盘上或者网络上,对其进行序列化,把他序列化成为一个字节流。

实现和注意事项:

1)实现接口Serializable Serializable接口中没有任何的方法,实现该接口的类不需要实现额外的方法。

2)如果对象中的某个属性是对象类型,必须也实现Serializable接口才可以,序列化对静态变量无效

3)如果不希望某个属性参与序列化,不是将其static,而是transient串行化保存的只是变量的值,对于变量的任何修饰符,都不能保存序列化版本不兼容


使用IO流完成文件夹复制

(结合递归)


packagecom.bjsxt;

 

importjava.io.*;

/**

 * CopyDocJob定义了实际执行的任务,即

 * 从源目录拷贝文件到目标目录

*/

publicclassCopyDir2 {

publicstaticvoidmain(String[] args) {

try{

copyDirectiory("d:/301sxt","d:/301sxt2");

catch(IOException e) {

e.printStackTrace();

}

}

/**

 * 复制单个文件

 * @param sourceFile 源文件

 * @param targetFile 目标文件

 * @throws IOException

 */

    privatestaticvoidcopyFile(File sourceFile, File targetFile) throwsIOException {

        BufferedInputStream inBuff = null;

        BufferedOutputStream outBuff = null;

        try{

            // 新建文件输入流

            inBuff = newBufferedInputStream(newFileInputStream(sourceFile));

            // 新建文件输出流

            outBuff = newBufferedOutputStream(newFileOutputStream(targetFile));

            // 缓冲数组

            byte[] b = newbyte[10245];

            intlen;

            while((len = inBuff.read(b)) != -1) {

                outBuff.write(b, 0, len);

            }

            // 刷新此缓冲的输出流

            outBuff.flush();

        finally{

            // 关闭流

            if(inBuff != null)

                inBuff.close();

            if(outBuff != null)

                outBuff.close();

        }

    }

 

    /**

     * 复制目录

     * @param sourceDir 源目录

     * @param targetDir 目标目录

     * @throws IOException

     */

    privatestaticvoidcopyDirectiory(String sourceDir, String targetDir) throwsIOException {

        // 检查源目录

     File fSourceDir = newFile(sourceDir);

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

      return;

     }

     //检查目标目录,如不存在则创建

     File fTargetDir = newFile(targetDir);

     if(!fTargetDir.exists()){

      fTargetDir.mkdirs();

     }

        // 遍历源目录下的文件或目录

        File[] file = fSourceDir.listFiles();

        for(inti = 0; i < file.length; i++) {

            if(file[i].isFile()) {

                // 源文件

                File sourceFile = file[i];

                // 目标文件

                File targetFile = newFile(fTargetDir, file[i].getName());

                copyFile(sourceFile, targetFile);

            }

            //递归复制子目录

            if(file[i].isDirectory()) {

                // 准备复制的源文件夹

                String subSourceDir = sourceDir + File.separator + file[i].getName();

                // 准备复制的目标文件夹

                String subTargetDir = targetDir + File.separator + file[i].getName();

                // 复制子目录

                copyDirectiory(subSourceDir, subTargetDir);

            }

        }

    }

}


说说BIO、NIO和AIO的区别

Java BIO: 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

Java NIO: 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。

Java AIO: 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。

NIO比BIO的改善之处是把一些无效的连接挡在了启动线程之前,减少了这部分资源的浪费(因为我们都知道每创建一个线程,就要为这个线程分配一定的内存空间)

AIO比NIO的进一步改善之处是将一些暂时可能无效的请求挡在了启动线程之前,比如在NIO的处理方式中,当一个请求来的话,开启线程进行处理,但这个请求所需要的资源还没有就绪,此时必须等待后端的应用资源,这时线程就被阻塞了。

适用场景分析:

BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解,如之前在Apache中使用。

NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持,如在 Nginx,Netty中使用。

AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持,在成长中,Netty曾经使用过,后来放弃。

相关文章
Java基础进阶IO流-文件复制
Java基础进阶IO流-文件复制
Java基础进阶IO流-文件复制
Java基础进阶IO流-FileInputStream字节输入流
Java基础进阶IO流-FileInputStream字节输入流
Java基础进阶IO流-FileInputStream字节输入流
|
安全 Linux C语言
Linux基础IO
Linux基础IO
229 1
Linux基础IO
Java基础进阶IO流-IO+Properties的联合应用
Java基础进阶IO流-IO+Properties的联合应用
Java基础进阶IO流-IO+Properties的联合应用
Java基础进阶IO流-序列化和反序列化
Java基础进阶IO流-序列化和反序列化
Java基础进阶IO流-序列化和反序列化
Java基础进阶IO流-标准输出流
Java基础进阶IO流-标准输出流
Java基础进阶IO流-标准输出流
Java基础进阶IO流-DataInputStream,DataOutputStream数据流
Java基础进阶IO流-DataInputStream,DataOutputStream数据流
Java基础进阶IO流-DataInputStream,DataOutputStream数据流
Java基础进阶IO流-BufferReader,BufferWriter字符缓冲流
Java基础进阶IO流-BufferReader,BufferWriter字符缓冲流
Java基础进阶IO流-BufferReader,BufferWriter字符缓冲流