总结一下Java中的IO相关内容


Java基础部分比较重要的,大概Java集合、面向对象、IO、并发、反射、异常算是高频了,这篇文章主要总结一下Java中的IO流
IO流首先要理解"流"的概念,或者说要有"流"的思想!!流只是一种抽象的数据传输形式,也可以说是一种形式,总之要有自己的认识。
所谓IO是简称,全称是in/out.是相对程序而言的读取in和写出out动作.


总述

Java 的 I/O 大概可以分成以下几类:

  • 磁盘操作:File
  • 字节操作:InputStream 和 OutputStream
  • 字符操作:Reader 和 Writer
  • 对象操作:Serializable
  • 网络操作:Socket(单独会有一篇文章,写个小案例)
  • 新的输入/输出:NIO

File类

FIle类主要是操作文件数据,可以进行创建和删除文件等操作。在使用一个File类时,则必须向File类的构造方法中传递一个文件路径。
下面代码中测试了File类中的常用方法以及小案例:

package cn.*.io;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Scanner;

//测试File
public class Test01_File {
    public static void main(String[] args) throws IOException {
        //创建对象 -- 封装文件或者文件夹
        File f = new File("E:\\string\\1.txt");
        //调用方法
        System.out.println(f.length());//获取目标文件的字节量
        System.out.println(f.exists());//判断文件是否存在
        System.out.println(f.isFile());//判断是否是文件
        System.out.println(f.isDirectory());//判断是否是文件夹
        System.out.println(f.getName());//获取文件名
        System.out.println(f.getParent());//获取父文件夹的路径
        System.out.println(f.getAbsolutePath());//获取文件的完整路径

        f = new File("E:\\string\\2.txt");
        System.out.println(f.createNewFile());//新建文件,文件夹不存在会异常,文件已存在返回false
        f = new File("E:\\string\\ioykx");
        System.out.println(f.mkdir());//新建文件夹
        f = new File("E:\\string\\ioykx\\x\\y\\z");
        System.out.println(f.mkdirs());//新建多层文件夹
        f = new File("E:\\string\\ioykx\\x\\y\\z");
        System.out.println(f.delete());//删除文件或(空的)文件夹
        f = new File("E:\\Photo");
        String [] s = f.list();//获取文件名并存入String数组中
        System.out.println(Arrays.toString(s));
//        f = new File("E:\\Photo");
        File [] file = f.listFiles();//获取文件 并存入File[]中,比较常用
        System.out.println(Arrays.toString(file));
        // 接受用户输入的一串路径
        //判断如果是文件,求文件的字节量
        //判断如果是文件夹,新建文件夹
        String str = new Scanner(System.in).nextLine();
        File fi = new File(str);
        if(fi.isFile()){
            System.out.println(fi.length());
        }else if (fi.isDirectory()){
            File [] fs = fi.listFiles();
            System.out.println(Arrays.toString(fs));
            }
        }
    }

还有一个比较常用的使用场景,也就是递归的使用
需求:递归(求目录总大小) 和 (删除文件夹)

  • 1、列出文件夹的所有资源
  • 2、判断当前资源是文件还是文件夹,
  • 如果是文件,直接求字节大小(length),如果是文件夹,重复过程 1

代码实现:

package cn.*.io;

import java.io.File;
import java.util.Scanner;
//递归 : 就是在方法内部自己调用自己
public class Test02_Digui {
    public static void main(String[] args) {
        //文件夹路径
        String path = new Scanner(System.in).nextLine() ;
        File f = new File(path);

        long total = size(f);
        System.out.println("一共"+total+"字节");
        //递归删除文件夹
        del(f);
    }

    private static void del(File f) {
        File [] fi = f.listFiles();
        for (int i = 0; i < fi.length; i++) {
            if (fi[i].isFile()){
                fi[i].delete();
            }else if (fi[i].isDirectory()){
                del(fi[i]);//递归调用
            }
        }
        f.delete();//删除空文件夹
        System.out.println("文件夹删除成功!");
    }

    /**
     * 创建size()求字节大小
     * @param f
     * @return
     */
    private static long size(File f) {
        long lon = 0;
        File [] file = f.listFiles();

        for (int i = 0; i < file.length; i++) {
            if (file[i].isFile()){

                lon = lon + file[i].length();

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

                lon = lon + size(file[i]);
            }
        }
        return lon;
    }
}

字节操作

字节操作顾名思义就是针对字节单位的操作,不管是磁盘还是网络传输,最小的存储单元都是字节,而不是字符。

字节流

字节流:针对二进制文件
InputStream

  • FileInputStream
  • BufferedInputStream
  • ObjectInputStream

OutputStream

  • FileOutputStream
  • BufferedOutputStream
  • ObjectOutputStream

字节读取流

InputStream – FileInputStream – BufferedInputStream
InputStream:是字节读取流的父类,而且被设计为了抽象类。

常用方法:
abstract int read()
从输入流中读取数据的下一个字节。
int read(byte[ ] b)
从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
int read(byte[ ] b, int off, int len)
将输入流中最多 len 个数据字节读入 byte 数组。
void close()
关闭此输入流并释放与该流关联的所有系统资源。

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

FileInputStream(File file)
FileInputStream(String name)

BufferedInputStream:为另一个输入流添加一些功能,底层会创建一个内部缓冲区数组。

BufferedInputStream(InputStream in)

它们的常用方法在以下代码中有所体现

package cn.*.io;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
//测试字节读取流
public class Test03_InputStream {
    public static void main(String[] args) throws IOException {
    //    FIS();//FileInputStream字节读取流
        BIS();//BufferedInputStream字节读取流,又称高级流(缓冲流)
    }

    private static void BIS() throws IOException {
        //创建对象
        InputStream file = new BufferedInputStream(new FileInputStream("E:\\string\\2.txt"));//方式一
//        InputStream file2 = new BufferedInputStream(new FileInputStream(new File("")));//方式二
        //开始读取
        int b = 0;
        while ( (b = file.read()) != -1) {//用循环读取
            System.out.println(b);
        }
        //释放资源
        file.close();
    }

    private static void FIS() throws IOException {
        //创建对象
        InputStream file = new FileInputStream("E:\\string\\2.txt");//方式一
//        InputStream file2 = new FileInputStream(new File("D:\\iotest\\1.txt"));//方式二
        //开始读取
        int b = 0;
        while ((b = file.read()) != -1) {//用循环读取
            System.out.println(b);
        }
        //释放资源
        file.close();
    }
}

小结:

FileInputStream BufferedInputStream都可以用来读取
效率上来讲: BufferedInputStream > FileInputStream,原因是:FileInputStream 是一个字节一个字节的读取,BufferedInputstream 是一个数组一个数组的读取,本质上,底层就是维护了一个byte[]数组叫buf,用来缓冲数据,默认大小是8192字节,把数组里的数据一次性的给程序读取进来.减少了程序冲流里获取数据的次数,提高了效率

字节写出流

字节流写出:OutputStream – FileOutputStream – BufferedOutputStream
OutputStream:此抽象类是表示输出字节流的所有类的超类,被修饰成了抽象类,不能new,只能学习共性方法

常用方法:
void close()
关闭此输出流并释放与此流有关的所有系统资源。
void flush()
刷新此输出流并强制写出所有缓冲的输出字节。
void write(byte[ ] b)
将 b.length 个字节从指定的 byte 数组写入此输出流。
void write(byte[ ] b, int off, int len)
将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
abstract void write(int b)
将指定的字节写入此输出流。

FileOutputStream:文件输出流

创建对象:
FileOutputStream(String name)
FileOutputStream(File file)
FileOutputStream(String name, boolean append)
FileOutputStream(File file, boolean append)

BufferedOutputStream:该类实现缓冲的输出流

创建对象:
BufferedOutputStream(OutputStream out)

package cn.*.io;

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

//测试字节写出流
public class Test04_OutputStream {
    public static void main(String[] args) throws IOException {
//        FOS();//FileOutputStream
        BOS();//BufferedOutputStream

    }

    private static void BOS() throws IOException {
        //创建对象 -- 追加
        OutputStream file = new BufferedOutputStream(new FileOutputStream("E:\\string\\3.txt",true));//方式一
//      OutputStream file2 = new BufferedOutputStream(new FileOutputStream(new File("")));//方式二
        //开始写出
        file.write(49);
        file.write(50);
        file.write(51);
        //释放资源
        file.close();
        System.out.println("写出成功!");
    }

    private static void FOS() throws IOException {
        //创建对象 -- 追加
        OutputStream file = new FileOutputStream("E:\\string\\3.txt",true);//方式一,true是表示追加
//      OutputStream file2 = new FileOutputStream(new File(""));//方式二
        //开始写出
        file.write(97);
        file.write(98);
        file.write(99);
        byte [] a = {97,97,97};
        file.write(a);
//        file.write
        //释放资源
        file.flush();
        System.out.println("写出成功!");
    }
}

字符操作

字符流

字符流:针对文本文件。读写容易发生乱码现象,在读写时最好指定编码集为utf-8
Writer

  • BufferedWriter
  • OutputStreamWriter

Reader

  • BufferedReader
  • InputStreamReader
  • PrintWriter/PrintStream

字符读取流

Reader – FileReader – BufferedReader
Reader:是字符读取流的抽象 父类,既然是抽象类就不能new,只能学习共性方法

常用方法:
abstract void close()
关闭该流并释放与之关联的所有资源。
int read()
读取单个字符。
int read(char[ ] cbuf)
将字符读入数组。
abstract int read(char[ ] cbuf, intoff, int len)
将字符读入数组的某一部分。
int read(CharBuffer target)
试图将字符读入指定的字符缓冲区。

FileReader:学习子类创建对象,用来读取字符文件的便捷类。

创建对象:
FileReader(File file)
FileReader(String fileName)

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

创建对象:
BufferedReader(Reader in)

package cn.*.io;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

//测试字符读取流
public class Test05_Reader {
    public static void main(String[] args) throws IOException {
        FR();//FileReader读取流
        BR();//BufferedReader读取流
    }

    private static void BR() throws IOException {
        //创建对象
        Reader file = new BufferedReader(new FileReader("E:\\string\\3.txt"));//方式一
		//Reader file2 = new BufferedReader(new FileReader(new File("")));//方式二
        //开始读取
        int b = 0 ;
        while (( b = file.read()) != -1){
            System.out.println(b);
        }
        //释放资源
        file.close();
        System.out.println("读取成功!");
    }

    private static void FR() throws IOException {
        //创建对象
        Reader file = new FileReader("E:\\string\\3.txt");//方式一
		//Reader file2 = new FileReader(new File(""));//方式二
        //开始读取
        int b = 0 ;
        while (( b = file.read()) != -1){
            System.out.println(b);
        }
        //释放资源
        file.close();
        System.out.println("读取成功!");
    }
}

字符写出流

字符流写出:Writer – FileWriter – BufferedWriter
Writer:写出字符流的抽象类,被设计成了一个抽象类,不能new。

–常用方法: abstract void close()
关闭此流,但要先刷新它。 abstract void flush()
刷新该流的缓冲。 void write(char[] cbuf)
写入字符数组。 abstract void write(char[] cbuf, int off, int len)
写入字符数组的某一部分。
void write(int c)
写入单个字符。
void write(String str)
写入字符串。
void write(String str, int off, int len)
写入字符串的某一部分。

FileWriter:用来写出字符文件的便捷类

创建对象 :
FileWriter(String fileName)
FileWriter(File file)
FileWriter(String fileName, boolean append)
FileWriter(Filefile, boolean append)

BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。

创建对象 :
BufferedWriter(Writer out)

package cn.*.io;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

//测试字符流写出
public class Test06_Writer {
    public static void main(String[] args) throws IOException {
		FW();//FileWriter写出流
        BW();//BufferedWriter写出流

    }
    private static void BW() {
       Writer file = null;
        //创建对象
        try {
             file  = new BufferedWriter(new FileWriter("E:\\string\\4.txt",true));//方式一
            //开始写出
            file.write(49);
            file.write(50);
            file.write(51);
            file.write("嘿嘿嘿嚯嚯嚯!!!");
        } catch (IOException e) {
            e.printStackTrace();
        }
        //释放资源
        //finally语句块,就是保证无论会不会出现异常,都一定要执行的代码
        finally{
            try {
                file.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println("写出成功!");
        }

//        Writer file2 = new BufferedWriter(new FileWriter(new File("")));;//方式二

    }

    private static void FW() throws IOException {
        //创建对象
        Writer file = new FileWriter("E:\\string\\4.txt",true);//方式一
//        Writer file2 = new FileWriter(new File(""));//方式二
        //开始写出
        file.write(49);
        file.write(50);
        file.write(51);
        file.write("奥利给giaogiao");
        //释放资源
        file.close();
        System.out.println("写出成功!");
    }
}

应用案例

  1. 利用IO流实现文件的复制

代码示例如下:

package cn.*.io;

import java.io.*;
import java.util.Scanner;

//测试文件的复制
public class Test07_Copy {
    public static void main(String[] args) throws IOException {
        System.out.println("请输入源文件路径:");
        String frompath = new Scanner(System.in).nextLine();
        File from = new File(frompath);
        System.out.println("请输入目标文件路径:");
        String topath = new Scanner(System.in).nextLine();
        File to = new File(topath);
//        ZFcopy(from,to);//字符流复制
//        ZJcopy(from,to);//字节流复制
        jdk1_7copy(from,to);//jdk1.7针对释放资源的优化 -- try with resource
    }

    /**
     * jdk1.7针对释放资源的优化,不用自己写close();
     * 直接用try()包起来
     * @param from
     * @param to
     */
    private static void jdk1_7copy(File from, File to) {

        try ( //读取流
              InputStream file1 = new BufferedInputStream(new FileInputStream(from));
              //写出流
              OutputStream file2 = new BufferedOutputStream(new FileOutputStream(to))
              )
        {
            //边读边写
            int b = 0;
            while ((b = file1.read()) != -1){

                file2.write(b);//把数据写出到目标文件里去

            }
            System.out.println("文件复制成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 字节流复制:什么类型的文件数据都能复制
     * @param from
     * @param to
     */
    private static void ZJcopy(File from, File to)  {
        InputStream file1 = null;
        OutputStream file2 = null;
        try {
            //读取流
            file1 = new BufferedInputStream(new FileInputStream(from));
            //写出流
            file2 = new BufferedOutputStream(new FileOutputStream(to));
            //边读边写
            int b = 0;
            while ((b = file1.read()) != -1){

                file2.write(b);//把数据写出到目标文件里去

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
      finally{
            //释放资源
            IOUtils.close(file1);
            IOUtils.close(file2);
            System.out.println("文件复制成功!");
        }
    }

    /**字符流复制:只能复制文本文件,不能操作 图片音频视频等类型的文件
     * @param from
     * @param to
     */
    private static void ZFcopy(File from, File to)  {
        Reader file1 = null;
        Writer file2 = null;
        try {
            //读取流
            file1 = new BufferedReader(new FileReader(from));
            //写出流
            file2 = new BufferedWriter(new FileWriter(to));
            //边读边写
            int b = 0;
            while (( b = file1.read()) != -1){
                file2.write(b);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
       finally {
            IOUtils.close(file1);
            IOUtils.close(file2);
            System.out.println("文件复制成功!");
        }
    }
}

//释放资源工具类
//提供公共的close(),
//是静态的,可以通过类名直接调用
class IOUtils{
    public static void close(Closeable c){
        try {
            c.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  1. 批量读写
package cn.*.out;

		import java.io.BufferedInputStream;
		import java.io.BufferedOutputStream;
		import java.io.BufferedReader;
		import java.io.BufferedWriter;
		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.InputStream;
		import java.io.OutputStream;
		import java.io.Reader;
		import java.io.Writer;
		import java.util.Scanner;

		//测试文件复制
		public class Test3_Copy {
			public static void main(String[] args) throws IOException {
				//1,接收用户输入的 源文件 路径
				System.out.println("请输入源文件的路径:");
				String frompath = new Scanner(System.in).nextLine() ;
				File from = new File(frompath);
				//2,接收用户输入的目标文件路径
				System.out.println("请输入目标文件的路径:");
				String topath = new Scanner(System.in).nextLine() ;
				File to = new File(topath);
				//3,调用指定方法完成复制
		//		copy(from,to);//字节流
				copy2(from,to);//字符流
			}
			//字符流  --  真的只能操作 文本文件 ,不能操作 图片音频视频等类型的文件
			private static void copy2(File from, File to) throws IOException {
				//1,读取源文件from -- 字符流BufferedReader
				Reader in = new BufferedReader( new FileReader(from)) ;
				//2,写出到目标文件to中 -- 字符流BufferedWriter
				Writer out  = new BufferedWriter(new FileWriter(to)) ;
				
				//3,边读边写
				//目前是一个字符一个字符的读写,为了优化单个字符的读写效率,我们也可以批量读写,如果是字符流,维护一个字符数组
				char[] buf = new char[8*1024];
				int b = 0 ;//定义变量,记录读取到的数据
				while( ( b= in.read(buf) ) != -1 ) {//优化成一个数组一个数组的读
					//write(buf,0,b)--第一个参数是指要写出哪个数组里的数据,
					//第二参数是指从数组的哪个位置开始写出数据,第三个参数是指要写出的数据长度
					out.write(buf,0,b);//优化成一个数组一个数组的写出,必须用三个参数的,不然会多复制数据的!!
				}
				//4,释放资源
				in.close();
				out.close();
				System.out.println("恭喜您,文件复制完成!!");
			}
			//完成复制 -- 字节流  -- 什么类型的数据都可以操作
			private static void copy(File from, File to) throws IOException {
				//1,读取源文件from -- 字节流BufferedInputStream
				InputStream in = new BufferedInputStream( new FileInputStream(from)) ;
				//2,写出到目标文件to中 -- 字节流BufferedOutputStream
				OutputStream out  = new BufferedOutputStream(new FileOutputStream(to)) ;

				//3,边读边写
				byte[] buf = new byte[8*1024] ;//为了优化字节流的单字节读写,可以改成批量读写,按照一个数组的容量去读写
				int b = 0 ;//定义变量,记录读取到的数据
				while( ( b= in.read(buf) ) != -1 ) {//优化了单字节读取效率
					out.write(buf,0,b);//优化了单字节写出效率
				}
				//4,释放资源
				in.close();
				out.close();
				System.out.println("恭喜您,文件复制完成!!");
			}
			
		}

总结

InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
在这里插入图片描述
另——BIO,NIO,AIO 有什么区别?

  • BIO (Blocking I/O): 同步阻塞 I/O 模式,数据的读取写入必须阻塞在一个线程内等待其完成。在活动连接数不是特别高(小于单机 1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,我们需要一种更高效的 I/O 处理模型来应对更高的并发量。
  • NIO (Non-blocking/New I/O): NIO 是一种同步非阻塞的 I/O 模型,在 Java 1.4 中引入了 NIO 框架,对应 java.nio 包,提供了 Channel , Selector,Buffer 等抽象。NIO 中的 N 可以理解为 Non-blocking,不单纯是 New。它支持面向缓冲的,基于通道的 I/O 操作方法。 NIO 提供了与传统 BIO 模型中的 Socket 和 ServerSocket 相对应的 SocketChannel 和 ServerSocketChannel 两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞 I/O 来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发
  • AIO (Asynchronous I/O): AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改进版 NIO 2,它是异步非阻塞的 IO 模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO 是异步 IO 的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO 操作本身是同步的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值