高级Java工程师必备 ----- 深入分析 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,关于Java NIO请参考我的另两篇文章:

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

高级Java工程师必备 ----- 深入分析 Java IO (二)NIO

Java IO类库的框架

首先看个图:

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与目标媒介相关联。

Java IO的基本用法

Java IO :字节流

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

例1,用字节流写文件

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

例2,用字节流读文件

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

CopyFileDemo

package com.chenhao.io.byteIO;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * @author ChenHao
 *
 */
public class CopyFileDemo {

    /**
     * @param args
     * @throws FileNotFoundException
     */
    public static void main(String[] args) {
        String src ="E:/xp/test";
        String dest="e:/xp/test/4.jpg";
        try {
            copyFile(src,dest);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            System.out.println("文件不存在");
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("拷贝文件失败|关闭流失败");
        }
    }
    /**
     * 文件的拷贝
     * @param  源文件路径
     * @param  目录文件路径
     * @throws FileNotFoundException,IOException
     * @return
     */
    public static void copyFile(String srcPath,String destPath) throws FileNotFoundException,IOException {
        //1、建立联系 源(存在且为文件) +目的地(文件可以不存在)
        File src =new File(srcPath);
        File dest =new File(destPath);
        if(! src.isFile()){ //不是文件或者为null
            System.out.println("只能拷贝文件");
            throw new IOException("只能拷贝文件");
        }
        //2、选择流
        InputStream is =new FileInputStream(src);
        OutputStream os =new FileOutputStream(dest);
        //3、文件拷贝   循环+读取+写出
        byte[] flush =new byte[1024];
        int len =0;
        //读取
        while(-1!=(len=is.read(flush))){
            //写出
            os.write(flush, 0, len);
        }
        os.flush(); //强制刷出

        //关闭流
        os.close();
        is.close();
    }

}

Java IO :字符流

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

例3,用字符流读文件

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

例4,用字符流写文件

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

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

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

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

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

Java IO:文件媒介操作

例6 ,File操作

public class FileDemo {
  public static void main(String[] args) {
         //检查文件是否存在
        File file = new File( "d:/test.txt");
         boolean fileExists = file.exists();
        System. out.println( fileExists);
         //创建文件目录,若父目录不存在则返回false
        File file2 = new File( "d:/fatherDir/subDir");
         boolean dirCreated = file2.mkdir();
        System. out.println( dirCreated);
         //创建文件目录,若父目录不存则连同父目录一起创建
        File file3 = new File( "d:/fatherDir/subDir2");
         boolean dirCreated2 = file3.mkdirs();
        System. out.println( dirCreated2);
        File file4= new File( "d:/test.txt");
         //判断长度
         long length = file4.length();
         //重命名文件
         boolean isRenamed = file4.renameTo( new File("d:/test2.txt"));
         //删除文件
         boolean isDeleted = file4.delete();
        File file5= new File( "d:/fatherDir/subDir");
         //是否是目录
         boolean isDirectory = file5.isDirectory();
         //列出文件名
        String[] fileNames = file5.list();
         //列出目录
        File[]   files = file4.listFiles();
  }
}

随机读取File文件

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

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

例7,随机读取文件

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

例8,随机写入文件

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

Java IO:BufferedInputStream和BufferedOutputStream

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

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

例10 ,用缓冲流读文件

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

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

copyFile

package com.chenhao.io.buffered;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
 * 字节流文件拷贝+缓冲流 ,提高性能
 * 缓冲流(节点流)
 * @author ChenHao
 *
 */
public class BufferedByteDemo {

    public static void main(String[] args) {
        String src ="E:/xp/test";
        String dest="e:/xp/test/4.jpg";
        try {
            copyFile(src,dest);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            System.out.println("文件不存在");
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("拷贝文件失败|关闭流失败");
        }
    }
    /**
     * 文件的拷贝
     * @param  源文件路径
     * @param  目录文件路径
     * @throws FileNotFoundException,IOException
     * @return
     */
    public static void copyFile(String srcPath,String destPath) throws FileNotFoundException,IOException {
        //1、建立联系 源(存在且为文件) +目的地(文件可以不存在)
        File src =new File(srcPath);
        File dest =new File(destPath);
        if(! src.isFile()){ //不是文件或者为null
            System.out.println("只能拷贝文件");
            throw new IOException("只能拷贝文件");
        }
        //2、选择流
        InputStream is =new BufferedInputStream(new FileInputStream(src));
        OutputStream os =new BufferedOutputStream( new FileOutputStream(dest));
        //3、文件拷贝   循环+读取+写出
        byte[] flush =new byte[1024];
        int len =0;
        //读取
        while(-1!=(len=is.read(flush))){
            //写出
            os.write(flush, 0, len);
        }
        os.flush(); //强制刷出

        //关闭流
        os.close();
        is.close();
    }

}

Java IO:BufferedReader和BufferedWriter

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

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

另外,BufferedReader提供一个readLine()可以方便地读取一行,而FileInputStream和FileReader只能读取一个字节或者一个字符,因此BufferedReader也被称为行读取器.

public static void keyIn() throws IOException {
 try (//InputStreamReader是从byte转成char的桥梁
      InputStreamReader reader = new InputStreamReader(System.in);
      //BufferedReader(Reader in)是char类型输入的包装类
      BufferedReader br = new BufferedReader(reader);) {

         String line = null;
         while ((line = br.readLine()) != null) {
             if (line.equals("exit")) {
                 //System.exit(1);
                 break;
             }
             System.out.println(line);
         }
     } catch (IOException e) {
         e.printStackTrace();
     }
}

Java IO: 序列化与ObjectInputStream、ObjectOutputStream

Serializable

如果你希望类能够序列化和反序列化,必须实现Serializable接口,就像所展示的ObjectInputStream和ObjectOutputStream例子一样。

ObjectInputStream

ObjectInputStream能够让你从输入流中读取Java对象,而不需要每次读取一个字节。你可以把InputStream包装到ObjectInputStream中,然后就可以从中读取对象了。代码如下:

ObjectInputStream input = new ObjectInputStream(new FileInputStream("object.data"));
MyClass object = (MyClass) input.readObject(); //etc.
input.close();

在这个例子中,你读取的对象必须是MyClass的一个实例,并且必须事先通过ObjectOutputStream序列化到“object.data”文件中。

在你序列化和反序列化一个对象之前,该对象的类必须实现了java.io.Serializable接口。

ObjectOutputStream

ObjectOutputStream能够让你把对象写入到输出流中,而不需要每次写入一个字节。你可以把OutputStream包装到ObjectOutputStream中,然后就可以把对象写入到该输出流中了。代码如下:

ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("object.data"));
MyClass object = new MyClass();  output.writeObject(object); //etc.
output.close();

例子中序列化的对象object现在可以从ObjectInputStream中读取了。

同样,在你序列化和反序列化一个对象之前,该对象的类必须实现了java.io.Serializable接口。

原文地址:https://www.cnblogs.com/java-chen-hao/p/11083740.html

时间: 2025-01-14 12:51:34

高级Java工程师必备 ----- 深入分析 Java IO (三)的相关文章

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

BIO编程 最原始BIO 网络编程的基本模型是C/S模型,即两个进程间的通信. 服务端提供IP和监听端口,客户端通过连接操作想服务端监听的地址发起连接请求,通过三次握手连接,如果连接成功建立,双方就可以通过套接字进行通信. 传统的同步阻塞模型开发中,ServerSocket负责绑定IP地址,启动监听端口:Socket负责发起连接操作.连接成功后,双方通过输入和输出流进行同步阻塞式通信.最原始BIO通信模型图: 存在的问题: 同一时间,服务器只能接受来自于客户端A的请求信息:虽然客户端A和客户端B

Java 工程师面试题整理(三)

根据自己收集的还有一部分自己面试的整理出来,希望能帮到面试的兄弟(2019年). 用友网络 1.你们用微服务有什么好处呢? 2.微服务之间的事务是怎么处理的?怎么回滚? 3.MQ用的什么?MQ的事务是怎么做的? 4.Object里面有什么方法?这些方法什么时候需要用到?equals方法和hashCode需要一起重写吗? 5.怎么删除一个list里面的奇数.list里面有十个整数. 6.Java的IO有没有了解?包里都有些什么内容? 7.Java的类加载过程是什么样的? 8.怎么在Object类中

Java工程师必备

JAVA基础扎实,熟悉JVM,熟悉网络.多线程.分布式编程及性能调优 精通Java EE相关技术 熟练运用Spring/SpringBoot/MyBatis等基础框架 熟悉分布式系统相关技术 熟悉MySQL及SQL优化 高并发.高可用.微服务.容器化等开发经验 熟悉JVM原理,精通io.多线程,精通分布式.缓存.消息等机制 熟练使用和配置Tomcat应用服务器 掌握Eclispse,Maven,SVN,GIT等软件开发工具的环境搭建和使用 熟悉H5.CSS.JS等前端技术,熟悉BootStrap

JAVA面试必备的知识宝典(三)

数据类型相关 java中int char,long各占多少字节? |类型|位数|字节数| |-|-|-| |short|2|16| |int|4|32| |long|8|64| |float|4|32 |double|8|64| |char|2|16| 64位的JVM当中,int的长度是多少? Java 中,int 类型变量的长度是一个固定值,与平台无关,都是 32 位.意思就是说,在 32 位 和 64 位 的Java 虚拟机中,int 类型的长度是相同的. java int和Integer的

《Java Web开发实战》——Java工程师必备干货教材

一年一度毕业季,又到了简历.offer漫天飞,失望与希望并存的时节.在IT行业,高校毕业生求职时,面临的第一道门槛就是技能与经验的考验,但学校往往更注重学生的理论知识,忽略了对学生实践能力的培养,因而导致很多求职者在面试中败下阵来.在毕业生所学知识普遍脱离职场实战的大趋势下,<Java Web开发实战>应运而生,此教材由千锋教研院高教产品研发部编著.清华大学出版社出版,将理论应用于实践,切实提高学生的实战能力. 整合多年实战案例 助力Java人才快速成长 <Java Web开发实战>

从零开始搭建Java开发环境第一篇:Java工程师必备软件大合集

1.JDK https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 目前主流的JDK版本还是JAVA8,我在阿里用的也是Java8. JDK里已经包含了JRE也就是Java虚拟机和运行环境,无需另外下载安装. 2.MySQL 和 Navicat for MySQL 数据库一般还是用免费的MySQL,这里推荐8.0以上的版本,具体怎么安装可以看这篇文章:https://mp.csdn.

高级.net工程师必备

<div><p style="margin:0.7em">Microsoft SQL Server 数据库<br/>一.创建和维护数据库<br/>1.数据库<br/> SQL Server 数据库的组成部分?(参见联机丛书)<br/> 如何保证数据库的完整性.安全性.并发性?<br/> 数据库设计创建步骤?(参见联机丛书 - 创建数据库计划)<br/> 数据库设计考虑的事项?(参见联机丛书)

React 源码深度解析 高级前端工程师必备技能完整教程百度云

原文配套资源获取链接:点击获取 第1章 课程导学 对课程整体进行讲解. 1-1 课程导学 第2章 基础知识 React API 一览 React主要API介绍,在这里你能了解它的用法,为下一章源码分析打基础. 2-1 准备工作 2-2 JSX到JavaScript的转换 2-3 react-element 2-4 react-component 2-5 react-ref 2-6 forward-ref 2-7 context 2-8 concurrent-mode 2-9 suspense-a

React源码深度解析 高级前端工程师必备技能

String 课程地址 = " http://icourse8.com/reactyuanma.html "; 章节信息第1章 课程导学 第2章 基础知识 React API 一览 第3章 React中的更新 第4章 Fiber Scheduler 第5章 各类组件的Update 第6章 完成节点任务 第7章 commitRoot 第8章 功能详解:基础 第9章 suspense and priority 第10章 功能详解:Hooks 第11章 课程总结 class Solution