一、java io 概述

1.1 相关概念

Java IO

Java IO即Java 输入输出系统。不管我们编写何种应用,都难免和各种输入输出相关的媒介打交道,其实和媒介进行IO的过程是十分复杂的,这要考虑的因素特别多,比如我们要考虑和哪种媒介进行IO(文件、控制台、网络),我们还要考虑具体和它们的通信方式(顺序、随机、二进制、按字符、按字、按行等等)。Java类库的设计者通过设计大量的类来攻克这些难题,这些类就位于java.io包中。

在JDK1.4之后,为了提高Java IO的效率,Java又提供了一套新的IO,Java New IO简称Java NIO。它在标准java代码中提供了高速的面向块的IO操作。本篇文章重点介绍Java IO 之 BIO,关于Java NIO请参考我的另两篇文章: 
Java NIO详解(一) 
Java NIO详解(二)

在Java IO中,流是一个核心的概念。流从概念上来说是一个连续的数据流。你既可以从流中读取数据,也可以往流中写数据。流与数据源或者数据流向的媒介相关联。在Java IO中流既可以是字节流(以字节为单位进行读写),也可以是字符流(以字符为单位进行读写)。

IO相关的媒介

Java的IO包主要关注的是从原始数据源的读取以及输出原始数据到目标媒介。以下是最典型的数据源和目标媒介:

  • 文件
  • 管道
  • 网络连接
  • 内存缓存
  • System.in, System.out, System.error(注:Java标准输入、输出、错误输出)

二、Java IO类库的框架

2.1 Java IO的类型

虽然java IO类库庞大,但总体来说其框架还是很清楚的。从是读媒介还是写媒介的维度看,Java IO可以分为:

  1. 输入流:InputStream和Reader
  2. 输出流:OutputStream和Writer

而从其处理流的类型的维度上看,Java IO又可以分为:

  1. 字节流:InputStream和OutputStream
  2. 字符流:Reader和Writer

下面这幅图就清晰的描述了JavaIO的分类:

- 字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer

我们的程序需要通过InputStream或Reader从数据源读取数据,然后用OutputStream或者Writer将数据写入到目标媒介中。其中,InputStream和Reader与数据源相关联,OutputStream和writer与目标媒介相关联。 以下的图说明了这一点:

2.2 IO 类库

上面我们介绍了Java IO中的四各类:InputStream、OutputStream、Reader、Writer,其实在我们的实际应用中,我们用到的一般是它们的子类,之所以设计这么多子类,目的就是让每一个类都负责不同的功能,以方便我们开发各种应用。各类用途汇总如下:

  • 文件访问
  • 网络访问
  • 内存缓存访问
  • 线程内部通信(管道)
  • 缓冲
  • 过滤
  • 解析
  • 读写文本 (Readers / Writers)
  • 读写基本类型数据 (long, int etc.)
  • 读写对象

下面我们就通过两张图来大体了解一下这些类的继承关系及其作用

图1:java io 类的集成关系

图2:java io中各个类所负责的媒介

三、Java IO的基本用法

3.1 Java IO :字节流

通过上面的介绍我们已经知道,字节流对应的类应该是InputStreamOutputStream,而在我们实际开发中,我们应该根据不同的媒介类型选用相应的子类来处理。下面我们就用字节流来操作文件媒介:

例1,用字节流写文件

  1. public static void writeByteToFile() throws IOException{
  2. String hello= new String( "hello word!");
  3. byte[] byteArray= hello.getBytes();
  4. File file= new File( "d:/test.txt");
  5. //因为是用字节流来写媒介,所以对应的是OutputStream
  6. //又因为媒介对象是文件,所以用到子类是FileOutputStream
  7. OutputStream os= new FileOutputStream( file);
  8. os.write( byteArray);
  9. os.close();
  10. }

例2,用字节流读文件

  1. public static void readByteFromFile() throws IOException{
  2. File file= new File( "d:/test.txt");
  3. byte[] byteArray= new byte[( int) file.length()];
  4. //因为是用字节流来读媒介,所以对应的是InputStream
  5. //又因为媒介对象是文件,所以用到子类是FileInputStream
  6. InputStream is= new FileInputStream( file);
  7. int size= is.read( byteArray);
  8. System. out.println( "大小:"+size +";内容:" +new String(byteArray));
  9. is.close();
  10. }

3.2 Java IO :字符流

同样,字符流对应的类应该是ReaderWriter。下面我们就用字符流来操作文件媒介:

例3,用字符流写文件

  1. public static void writeCharToFile() throws IOException{
  2. String hello= new String( "hello word!");
  3. File file= new File( "d:/test.txt");
  4. //因为是用字符流来读媒介,所以对应的是Writer,又因为媒介对象是文件,所以用到子类是FileWriter
  5. Writer os= new FileWriter( file);
  6. os.write( hello);
  7. os.close();
  8. }

例4,用字符流读文件

  1. public static void readCharFromFile() throws IOException{
  2. File file= new File( "d:/test.txt");
  3. //因为是用字符流来读媒介,所以对应的是Reader
  4. //又因为媒介对象是文件,所以用到子类是FileReader
  5. Reader reader= new FileReader( file);
  6. char [] byteArray= new char[( int) file.length()];
  7. int size= reader.read( byteArray);
  8. System. out.println( "大小:"+size +";内容:" +new String(byteArray));
  9. reader.close();
  10. }

3.3 Java IO :字节流转换为字符流

字节流可以转换成字符流,java.io包中提供的InputStreamReader类就可以实现,当然从其命名上就可以看出它的作用。其实这涉及到另一个概念,IO流的组合,后面我们详细介绍。下面看一个简单的例子:

例5 ,字节流转换为字符流

  1. public static void convertByteToChar() throws IOException{
  2. File file= new File( "d:/test.txt");
  3. //获得一个字节流
  4. InputStream is= new FileInputStream( file);
  5. //把字节流转换为字符流,其实就是把字符流和字节流组合的结果。
  6. Reader reader= new InputStreamReader( is);
  7. char [] byteArray= new char[( int) file.length()];
  8. int size= reader.read( byteArray);
  9. System. out.println( "大小:"+size +";内容:" +new String(byteArray));
  10. is.close();
  11. reader.close();
  12. }

3.4 Java IO :IO类的组合

从上面字节流转换成字符流的例子中我们知道了IO流之间可以组合(或称嵌套),其实组合的目的很简单,就是把多种类的特性融合在一起以实现更多的功能。组合使用的方式很简单,通过把一个流放入另一个流的构造器中即可实现,两个流之间可以组合,三个或者更多流之间也可组合到一起。当然,并不是任意流之间都可以组合。关于组合就不过多介绍了,后面的例子中有很多都用到了组合,大家好好体会即可。

3.5 Java IO:文件媒介操作

File是Java IO中最常用的读写媒介,那么我们在这里就对文件再做进一步介绍。

3.5.1 File媒介

例6 ,File操作

  1. public class FileDemo {
  2. public static void main(String[] args) {
  3. //检查文件是否存在
  4. File file = new File( "d:/test.txt");
  5. boolean fileExists = file.exists();
  6. System. out.println( fileExists);
  7. //创建文件目录,若父目录不存在则返回false
  8. File file2 = new File( "d:/fatherDir/subDir");
  9. boolean dirCreated = file2.mkdir();
  10. System. out.println( dirCreated);
  11. //创建文件目录,若父目录不存则连同父目录一起创建
  12. File file3 = new File( "d:/fatherDir/subDir2");
  13. boolean dirCreated2 = file3.mkdirs();
  14. System. out.println( dirCreated2);
  15. File file4= new File( "d:/test.txt");
  16. //判断长度
  17. long length = file4.length();
  18. //重命名文件
  19. boolean isRenamed = file4.renameTo( new File("d:/test2.txt"));
  20. //删除文件
  21. boolean isDeleted = file4.delete();
  22. File file5= new File( "d:/fatherDir/subDir");
  23. //是否是目录
  24. boolean isDirectory = file5.isDirectory();
  25. //列出文件名
  26. String[] fileNames = file5.list();
  27. //列出目录
  28. File[] files = file4.listFiles();
  29. }

}

3.5.3 随机读取File文件

通过上面的例子我们已经知道,我们可以用FileInputStream(文件字符流)或FileReader(文件字节流)来读文件,这两个类可以让我们分别以字符和字节的方式来读取文件内容,但是它们都有一个不足之处,就是只能从文件头开始读,然后读到文件结束。

但是有时候我们只希望读取文件的一部分,或者是说随机的读取文件,那么我们就可以利用RandomAccessFile。RandomAccessFile提供了seek()方法,用来定位将要读写文件的指针位置,我们也可以通过调用getFilePointer()方法来获取当前指针的位置,具体看下面的例子:

例7,随机读取文件

  1. public static void randomAccessFileRead() throws IOException {
  2. // 创建一个RandomAccessFile对象
  3. RandomAccessFile file = new RandomAccessFile( "d:/test.txt", "rw");
  4. // 通过seek方法来移动读写位置的指针
  5. file.seek(10);
  6. // 获取当前指针
  7. long pointerBegin = file.getFilePointer();
  8. // 从当前指针开始读
  9. byte[] contents = new byte[1024];
  10. file.read( contents);
  11. long pointerEnd = file.getFilePointer();
  12. System. out.println( "pointerBegin:" + pointerBegin + "\n" + "pointerEnd:" + pointerEnd + "\n" + new String(contents));
  13. file.close();
  14. }

例8,随机写入文件

  1. public static void randomAccessFileWrite() throws IOException {
  2. // 创建一个RandomAccessFile对象
  3. RandomAccessFile file = new RandomAccessFile( "d:/test.txt", "rw");
  4. // 通过seek方法来移动读写位置的指针
  5. file.seek(10);
  6. // 获取当前指针
  7. long pointerBegin = file.getFilePointer();
  8. // 从当前指针位置开始写
  9. file.write( "HELLO WORD".getBytes());
  10. long pointerEnd = file.getFilePointer();
  11. System. out.println( "pointerBegin:" + pointerBegin + "\n" + "pointerEnd:" + pointerEnd + "\n" );
  12. file.close();
  13. }

3.6 Java IO:管道媒介

管道主要用来实现同一个虚拟机中的两个线程进行交流。因此,一个管道既可以作为数据源媒介也可作为目标媒介。

需要注意的是java中的管道和Unix/Linux中的管道含义并不一样,在Unix/Linux中管道可以作为两个位于不同空间进程通信的媒介,而在java中,管道只能为同一个JVM进程中的不同线程进行通信。和管道相关的IO类为:PipedInputStreamPipedOutputStream,下面我们来看一个例子:

例9,读写管道

  1. public class PipeExample {
  2. public static void main(String[] args) throws IOException {
  3. final PipedOutputStream output = new PipedOutputStream();
  4. final PipedInputStream input = new PipedInputStream(output);
  5. Thread thread1 = new Thread( new Runnable() {
  6. @Override
  7. public void run() {
  8. try {
  9. output.write( "Hello world, pipe!".getBytes());
  10. } catch (IOException e) {
  11. }
  12. }
  13. });
  14. Thread thread2 = new Thread( new Runnable() {
  15. @Override
  16. public void run() {
  17. try {
  18. int data = input.read();
  19. while( data != -1){
  20. System. out.print(( char) data);
  21. data = input.read();
  22. }
  23. } catch (IOException e) {
  24. } finally{
  25. try {
  26. input.close();
  27. } catch (IOException e) {
  28. e.printStackTrace();
  29. }
  30. }
  31. }
  32. });
  33. thread1.start();
  34. thread2.start();
  35. }

}

3.7 Java IO:网络媒介

关于Java IO面向网络媒介的操作即Java 网络编程,其核心是Socket,同磁盘操作一样,java网络编程对应着两套API,即Java IO和Java NIO,关于这部分我会准备专门的文章进行介绍。

3.8 Java IO:BufferedInputStream和BufferedOutputStream

BufferedInputStream顾名思义,就是在对流进行写入时提供一个buffer来提高IO效率。在进行磁盘或网络IO时,原始的InputStream对数据读取的过程都是一个字节一个字节操作的,而BufferedInputStream在其内部提供了一个buffer,在读数据时,会一次读取一大块数据到buffer中,这样比单字节的操作效率要高的多,特别是进程磁盘IO和对大量数据进行读写的时候。

使用BufferedInputStream十分简单,只要把普通的输入流和BufferedInputStream组合到一起即可。我们把上面的例2改造成用BufferedInputStream进行读文件,请看下面例子:

例10 ,用缓冲流读文件

  1. public static void readByBufferedInputStream() throws IOException {
  2. File file = new File( "d:/test.txt");
  3. byte[] byteArray = new byte[( int) file.length()];
  4. //可以在构造参数中传入buffer大小
  5. InputStream is = new BufferedInputStream( new FileInputStream(file),2*1024);
  6. int size = is.read( byteArray);
  7. System. out.println( "大小:" + size + ";内容:" + new String(byteArray));
  8. is.close();
  9. }

关于如何设置buffer的大小,我们应根据我们的硬件状况来确定。对于磁盘IO来说,如果硬盘每次读取4KB大小的文件块,那么我们最好设置成这个大小的整数倍。因为磁盘对于顺序读的效率是特别高的,所以如果buffer再设置的大写可能会带来更好的效率,比如设置成4*4KB或8*4KB。

还需要注意一点的就是磁盘本身就会有缓存,在这种情况下,BufferedInputStream会一次读取磁盘缓存大小的数据,而不是分多次的去读。所以要想得到一个最优的buffer值,我们必须得知道磁盘每次读的块大小和其缓存大小,然后根据多次试验的结果来得到最佳的buffer大小。

BufferedOutputStream的情况和BufferedInputStream一致,在这里就不多做描述了。

3.9 Java IO:BufferedReader和BufferedWriter

BufferedReader、BufferedWriter 的作用基本和BufferedInputStream、BufferedOutputStream一致,具体用法和原理都差不多 ,只不过一个是面向字符流一个是面向字节流。同样,我们将改造字符流中的例4,给其加上buffer功能,看例子:

  1. public static void readByBufferedReader() throws IOException {
  2. File file = new File( "d:/test.txt");
  3. // 在字符流基础上用buffer流包装,也可以指定buffer的大小
  4. Reader reader = new BufferedReader( new FileReader(file),2*1024);
  5. char[] byteArray = new char[( int) file.length()];
  6. int size = reader.read( byteArray);
  7. System. out.println( "大小:" + size + ";内容:" + new String(byteArray));
  8. reader.close();
  9. }

转载请说明出处,原文链接: http://blog.csdn.net/suifeng3051/article/details/48344587

Java IO--BIO的更多相关文章

  1. java IO(BIO)、NIO、AIO

    IO 服务端ServerSocket 客户端Socket 缺点每次客户端建立连接都会另外启一个线程处理.读取和发送数据都是阻塞式的. 如果1000个客户端建立连接将会产生1000个线程 Server端 ...

  2. Java IO(2)阻塞式输入输出(BIO)的字节流与字符流

    在上文中<Java IO(1)基础知识——字节与字符>了解到了什么是字节和字符,主要是为了对Java IO中有关字节流和字符流有一个更好的了解. 本文所述的输出输出指的是Java中传统的I ...

  3. 漫谈Java IO之普通IO流与BIO服务器

    今天来复习一下基础IO,也就是最普通的IO. 网络IO的基本知识与概念 普通IO以及BIO服务器 NIO的使用与服务器Hello world Netty的使用与服务器Hello world 输入流与输 ...

  4. Java IO(2)阻塞式输入输出(BIO)

    在上文中<Java IO(1)基础知识——字节与字符>了解到了什么是字节和字符,主要是为了对Java IO中有关字节流和字符流有一个更好的了解. 本文所述的输出输出指的是Java中传统的I ...

  5. Java IO编程全解(二)——传统的BIO编程

    前面讲到:Java IO编程全解(一)——Java的I/O演进之路 网络编程的基本模型是Client/Server模型,也就是两个进程之间进行相互通信,其中服务端提供位置信息(绑定的IP地址和监听端口 ...

  6. Java提供了哪些IO方式?IO, BIO, NIO, AIO是什么?

    IO一直是软件开发中的核心部分之一,而随着互联网技术的提高,IO的重要性也越来越重.纵观开发界,能够巧妙运用IO,不但对于公司,而且对于开发人员都非常的重要.Java的IO机制也是一直在不断的完善,以 ...

  7. Java IO 学习(四)BIO/NIO

    本文会尝试介绍Java中BIO与NIO的范例与原理 使用的模型非常简单:服务器--客户端模型,服务器会将客户端发送的字符串原样发回来.也就是所谓的echo server. BIO 也就是所谓的Sock ...

  8. 高级Java工程师必备 ----- 深入分析 Java IO (一)BIO

    BIO编程 最原始BIO 网络编程的基本模型是C/S模型,即两个进程间的通信. 服务端提供IP和监听端口,客户端通过连接操作想服务端监听的地址发起连接请求,通过三次握手连接,如果连接成功建立,双方就可 ...

  9. 【Java IO流】浅谈io,bio,nio,aio

    本文转载自:http://www.cnblogs.com/doit8791/p/4951591.html 1.同步异步.阻塞非阻塞概念        同步和异步是针对应用程序和内核的交互而言的. 阻塞 ...

  10. JAVA - IO - IO的类型(AIO, BIO, NIO)

    IO的方式通常分为几种,同步阻塞的BIO.同步非阻塞的NIO.异步非阻塞的AIO. 一.BIO 在JDK1.4出来之前,我们建立网络连接的时候采用BIO模式,需要先在服务端启动一个ServerSock ...

随机推荐

  1. JS字符串常用方法总结

    1.toLowerCase(): 把字符串转为小写,返回新的字符串. var str="Hello World"; var str1=str.toLowerCase(); cons ...

  2. Python学习笔记(三)

    组合数据类型 5种内置的序列类型:bytearray,bytes,list,str,tuple 元组 元组:固定,有序,索引从0开始,分片,步距语法支持 不能替换或者删除其中的任意数据项,使用list ...

  3. 135、JS和Android交互范例

    很简单的直接上代码 <uses-permission android:name="android.permission.INTERNET" /> assets/web. ...

  4. docker应用-5(使用overlay 网络进行容器间跨物理主机通信)

    同一个主机上的Docker容器之间通信 docker 引擎会在主机上增加一个docker0网卡,该网卡具有双重身份: 1.从容器视角,网桥(交换机)身份docker0 对于运行在同一个主机上的各个容器 ...

  5. fetch get方法的时候报错

    fetch 报错 Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body. 翻译过来就 ...

  6. 域渗透分析神器BloodHound

    一.安装过程: 1.首先安装JDK,要求是JDK8 JDK8下载地址 windows下按照提示自动安装好久可以了 2.安装neo4j neo4j图数据库下载地址 下载好后,进入对应的目录在命令行运行如 ...

  7. 修改Linux系统默认编辑器

    修改ubuntu的默认编辑器: echo export EDITOR=/usr/bin/vim >> ~/.bashrc 故障过程: 修改过程: 强制断开连接,重新连接,修改默认编辑器:e ...

  8. Centos 7内核3升级到4

    步骤 1:检查已安装的内核版本 让我们安装了一个发行版,它包含了一个特定版本的内核.为了展示当前系统中已安装的版本,我们可以: # uname -sr 下面的图片展示了在一台 CentOS 7 服务器 ...

  9. 浏览器打开exe文件

    <win-r> regedit 打开注册表,然后自定义协议 自定义协议注册表.reg 打开后导入 Windows Registry Editor Version 5.00 [HKEY_CL ...

  10. Win10, VS2017环境下OpenCV3.4.2的配置

    从官网https://opencv.org/releases.html下载OpenCV3.4.2的Win pack进行安装,安装目录便如下图所示: 要能在Visual Studio中使用上述安装的Op ...