Java之I/O流(最详细的I/O流总结)

I/O流

  • I/O流
    • 1、定义
    • 2、分类
      • 2.1 字节流和字符流
      • 2.2 输入流和输出流
      • 2.3 节点流和处理流
    • 3、结构
    • 4、字节流
      • 4.1 定义
      • 4.2 说明
      • 4.3 字节流概述
      • 4.4 InputStream的常用方法
      • 4.5 OutputStream的常用方法
    • 5、字节流读写文件
      • 5.1 说明
      • 5.2 注意
      • 5.3 字节输入流——相关代码
      • 5.4 字节输出流——相关代码
    • 6、文件的拷贝
      • 6.1 需求
      • 6.2 分析
      • 6.3 注意
      • 6.4 快捷键
      • 6.5 相关代码
    • 7、字节流的缓冲区
      • 7. 相关代码
    • 8、字节缓冲流
      • 8.1 说明
      • 8.2 特点
      • 8.3 相关代码
      • 8.4 过程
    • 9、字符流概述
      • 9.1 说明
      • 9.2 相关代码
    • 10、字符流操作文件
      • 10.1 字符流的作用
      • 10.2 解决中文乱码的方式
      • 10.3 判断标准
      • 10.4 FileWriter ——> 字符输出流
        • 10.4.1 注意
        • 10.4.2 flush和close方法的区别
        • 10.4.3 相关代码
      • 10.5 字符缓冲流
    • 11、转换流
      • 11.1 作用
      • 11.2 说明
        • 11.2.1 InputStreamReader
        • 11.2.2 相关代码一
        • 11.2.3 OutputStreamWriter
        • 11.2.4 相关代码
    • 12、File类
      • 12.1 说明
      • 12.2 File相关代码
      • 12.3 File类常用构造方法
      • 12.4 构造方法的相关代码
      • 12.5 File类的普通方法
    • 13、遍历目录下的文件
      • 13.1 说明
    • 14、删除文件及目录
    • 15、RandomAccesseFile类
      • 15.1 原理
      • 15.2 常用方法
      • 15.3 相关代码
    • 16、对象序列化
      • 16.1 定义
      • 16.2 目的
      • 16.3 说明
      • 16.1 相关代码
    • 17、NIO
      • 17.1 NIO概述
        • 17.1.1 定义
        • 17.1.2 说明
        • 17.1.3 三大核心
      • 17.2 Buffer(缓冲器)
        • 17.2.1 作用
        • 17.2.2 说明
        • 17.2.3 使用
        • 17.2.4 相关代码
      • 17.3 Channel(通道)
        • 17.3.1 说明
        • 17.3.2 Channel接口的实现类
        • 17.3.3 使用
        • 17.3.4 相关代码一
        • 17.3.5 相关代码二
      • 17.4 Path接口
        • 17.4.1 作用
        • 17.4.2 说明
        • 17.4.3 相关代码
      • 17.5 File工具类

I/O流

1、定义

I/O流(Input/Output)流,即输入/输出流,是java中实现输入/输出的基础,他可以方便实现数据的输入/输出操作。(数据的传输)

2、分类

2.1 字节流和字符流

根据流操作的数据单位的不同,可以分为字节流和字符流

2.2 输入流和输出流

根据流传输方向的不同,又可以分为输入流和输出流

2.3 节点流和处理流

根据流的功能不同,可以分为节点流和处理流

3、结构

java中的I/O流主要定义在java.io包中,该包下定义了很多类,其中有四个类为流的顶级类,分为InputStream和OutputStream, Reader和Writer
(1)InputStream和OutPutStream是字节流,而Reader和Writer是字符流
(2)InputStream和Reader是输入流,而OutPutStream和Writer是输出流
(3)这四个顶级类都是抽象类,并且是所有流类型的父亲。

4、字节流

4.1 定义

在计算机中,无论是文本、图片、音频还是视频,所有文件都是以二进制(字节)形式存在的,I/O流中针对字节的输入/输出提供了一系列的流,统称为字节流。

4.2 说明

字节流是程序中最常用的流
在JDK中,所有的字节输入流都继承自InputStream,所有的字节输出流都继承自OutputStream。

4.3 字节流概述

InputStream被看成一个输入管道,OutputStream被看成一个输出管道,数据通过InputStream从源设备输入到程序,通过OutputStream从程序输出到目标设备,从而实现数据的传输。

4.4 InputStream的常用方法

方法声明 功能描述
int read() 从输入流读取一个8位的字节,把它转换为0~255之间的整数,并返回这一整数。当没有可用字节时,将返回-1

int read(byte[] b)从输入流读取若干字节,把它们保存到参数b指定的字节数组中,返回的整数表示读取字节的数目

int read(byte[] b,int off,int len) 从输入流读取若干字节,把它们保存到参数b指定的字节数组中,off指定字节数组开始保存数据的起始下标,len表示读取的字节数目

void close() 关闭此输入流并释放与该流关联的所有系统资源

前三个read()方法都是用来读数据的,分按字节读取和按字节数组读取。
进行I/O流操作时,应该调用close()方法关闭流,从而释放当前I/O流所占的系统资源。

4.5 OutputStream的常用方法

void write(int b) 向输出流写入一个字节
void write(byte[] b) 把参数b指定的字节数组的所有字节写道输出流
void write(byte[] b, int off, int len) 将指定byte数组中从偏移量off开始的len个字节写入输出流
void flush() 刷新此输出流并强制写出所有缓冲的输出字节
void close() 关闭此输出流并释放与此流相关的所有系统资源

前三个write()方法都是用来写数据的,分按字节读取和按字节数组写入。
flush()方法用来将当前输出流缓冲区(通常是字节数组)中的数据强制写入目标设备,此过程称为刷新
close()方法是用来关闭流并释放与当前IO流相关的系统资源。

5、字节流读写文件

5.1 说明

(1)针对文件的读写操作,JDK专门提供了两个类,分别是FileInputStream和FileOutputStream
FileInputStream是InputStream的子集,它是操作文件的字节输入流,专门用于读取文件中的数据。
(2)从文件读取数据是重复的操作,因此需要通过循环语句来实现数据的持续读取

5.2 注意

在读取文件数据时,必须保证文件在相应目录存在并且是可读的,否则会抛出FileNotFoundException

5.3 字节输入流——相关代码

//假如在D盘已经有个a.txt文件,里面的内容是a,b,c
//read(): 一次读取一个字节,如果读取到了文件末尾,将会返回-1的标识
public class Demo1_FileStream{
    public static void main(String[] args) throws FileNotFoundException{
        //1.创建字节输入流对象
        FileInputStream fis = new FileInputStream("D:\\a.txt");

        //2.读取文件中的数据
        int i = fis.read();
        System.out.println(i);    //97

        //2.读取文件中的数据
        int i2 = fis.read();
        System.out.println((char)i2);    //b
        
    }
}


//假如在D盘已经有个a.txt文件,里面的内容是a,b,c
//read(): 一次读取一个字节,如果读取到了文件末尾,将会返回-1的标识
public class Demo2_FileStream{
    public static void main(String[] args) throws FileNotFoundException{
        //1.创建字节输入流对象
        FileInputStream fis = new FileInputStream("D:\\a.txt");

        //2.循环读取文件中的数据
        int i;
        //2.1 在循环中不断地调用read方法,并把读取到的数据给i赋值,只要没有读到-1,说明没有达到文件末尾
        while((i = fis.read())!=-1){
             System.out.println((char)i);
        }
        
        //3.关闭流释放资源
        fis.close();

    }
}

5.4 字节输出流——相关代码

public class Demo3_FileStream{
    //字节输出流: FileOutputStream
    //输出流写出数据,关联文件的时候,如果文件不存在,则自动创建
    /**
    *输出流写出数据,如果关联的文件已经存在,会将原本内容清空掉之后,再次写入,
    *如果不想清空原有内容,就可以在构造方法的位置,加入true的开关即可
    */
    public static void main(String[] args) throws IOException{
        //1.创建字节输出流对象
        FileOutputStream fos = new FileOutputStream("D:\\1.txt", true);
     
        //2.写出数据
        fos.write(98);

        //或者
        byte[] bytes = "你好,世界".getBytes();
        System.out.println(Arrays.toString(bytes));
        fos.write(bytes);

        //等价于
        fos.write("你好,世界2".getBytes());

        //3.关闭流释放资源
        fos.close();
}


6、文件的拷贝

6.1 需求

使用JavaIO技术完成文件的拷贝

6.2 分析

(1)创建输入流和输出流对象
输入流对象关联要拷贝的数据源
输出流对象关联要拷贝的数据目的

(2)在读取过程中,将数据写出到目标文件当中

(3)关闭流释放资源

6.3 注意

建议IO流的异常使用try…catch进行处理,并加入finally代码块将关闭流释放资源的代码,放在finally当中。

6.4 快捷键

alt + shift + z --> try…catch

6.5 相关代码

public class Test1_Copy{
    public static void main(String[] args) {
        FileInputStream fis = null;
         FileOutputStream fos = null;

        try{
            //1. 创建输入流和输出流对象
            FileInputStream fis = new FileInputStream("D:\\abc.txt");
            FileOutputStream fos = new FileOutputStream("E:\\copy.txt");

            //2. 在读取的过程中,将数据写出到目标文件当中
            int i;
            while((i = fis.read())! = -1){
                fos.write(i);
            }
            
            //3.关闭流释放资源
            fis.close();
            fos.close();
        } catch (IOException e){
            e.printStackTrace();
        } finally{
            //3.关闭流释放资源
            if(fis!=null){
                try{
                    fis.close();
                }catch(IOException e){
                    e.printStackTrace();
                }
            }

            if(fos!=null){
               try{
                    fos.close();
                }catch(IOException e){
                    e.printStackTrace();
                }
            }
        }
    }

}

7、字节流的缓冲区

在文件拷贝过程中,通过以字节形式逐个拷贝,效率很低,因此可以定义一个字节数组缓冲区,在拷贝文件时,就可以
一次性读取多个字节的数据。

7. 相关代码

//假如:已经在D盘创建了a,b两个文件
//read方法如果传入了数组,那么会根据数组容器大小,决定一次读取多少个字节
//read方法的返回值是: 读取到的(有效)字节个数

public class Test2_CopyFile{
    public static void main(String[] args) throws IOException{
        long start = System.currentTimeMillis();

        //1. 创建输入输出流,关联数据源和数据目的
        FileInputStream fis = new FileInputStream("D:\\a.flv");
        FileOutputStream fos = new FileOutputStream("D:\\b.flv");

        //创建字节缓冲区
        byte[] bys = new byte[1024];

        //2.在读取的过程中,将数据写出到数据目的
        int len;
        while((len = fis.read(bys))!=-1){
            //3.写出的时候,从数组容器中取出,0-len(有效字节个数)
            fos.write(bys,0,len);
        }
        
        fis.close();
        fos.close();

        long end = System.currentTimeMillis();
        System.out.println("花费时间为:"+ (end-start) + "毫秒");
    }
}

8、字节缓冲流

8.1 说明

除了定义字节缓冲区来提高文件拷贝效率外,IO中还提供了两个字节缓冲流来提高文件拷贝效率:
BufferedInputStream 和 BufferedOutputStream。
他们的构造方法中分别接收InputStream和OutputStream类型的参数作为对象,在读写数据时提供缓冲功能。

8.2 特点

由于两个流对象中,内置了字节(数组) ,从而提高IO流的读写效率。

8.3 相关代码

public class Demo_BufferedStream{
    /*
    * BufferedInputStream    字节缓冲输入流
    * BufferedOutputStream   字节缓冲输出流
    *
    */
    public static void main(String[] args) throws IOException{
         long start = System.currentTimeMillis();

        //1.创建字节缓冲,输入输出流
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\a.flv"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileInputStream("D:\\b.flv"));

        //2.读写操作
        int i;
        while((i=bis.read())!=-1){
            bos.write(i);
        }

        //3.关闭流释放资源
        bis.close();
        bos.close();

        long end = System.currentTimeMillis();
        System.out.println("花费时间为:"+ (end-start) + "毫秒");
    }
}

8.4 过程

数据源(源设备)——>FileInputStream(字节流)——>BufferedInputStream(字节缓冲流)——>JAVA(应用程序)——>BufferedOutputStream(字节缓冲流)——>FileOutputStream(字节流)——>数据目的(目标设备)

9、字符流概述

9.1 说明

除了字符流,JDK还提供了用于实现字符操作的字符流,同字节流一样,字符流也有两个抽象的顶级父类,分别是Reader 和 Writer。
(用字节流输出中文,可能会出现中文乱码问题,所以这时候便要利用到字符流)

9.2 相关代码

public class Demo1{
    public static void main(String[] args) throws IOException{

        FileInputStream fis = new FileInputStream("b.txt");

        byte[] bys = new byte[2];

        int i;

        while((i=fis.read(bys))!=-1){
            System.out.println(new String(bys,0,i));
        }

        fis.close();
    }
}


10、字符流操作文件

10.1 字符流的作用

操作纯文本文件的时候,可以解决中文乱码问题。
(注意:字符流只能操作纯文本文件)

10.2 解决中文乱码的方式

字符流底层实际上是通过(字节流+编码表)的形式进行读取的,在读取之后,会根据平台默认码表决定一次读取多少个字节,并在内存中转换为字符。

GBK ——> 一个中文占2个字节
UTF-8 ——> 一个中文占3个字节

10.3 判断标准

字符流底层有一个判断,
如果读取到的是中文字符,一次读取2个字节(GBK),
如果读取到的是非中文字符,一次读取1个字节,
其中中文字符一般都是负数的字节,部分是第一个字节为负数,后面的字节有可能为正数,
非中文字符都是正数。

10.4 FileWriter ——> 字符输出流

10.4.1 注意

如果字符输出流没有调用close方法、flush方法的话,数据将不会写出到文件中

10.4.2 flush和close方法的区别

(1)flush方法是将数据刷出到文件中去,刷出后可以继续调用write方法写出
(2)close方法的主要功能是关闭流释放资源,同时也具有刷出数据的效果
(3)close方法调用结束后,不能再调用write方法写出数据

10.4.3 相关代码
public static void main(String[] args) throws IOException{
    FileWrite fw = new FileWriter("c.txt");
    //1.写出一个字符
    fw.write('a');
    //2.写出一个字符数组
    char[] cbs = {'1','2','3','4'};
    //3.写出一个字符数组的一部分
    fw.write(cbs,0,2);
    //4.写出一个字符串
    fw.write("你好你好");
    //5.写出字符串的一部分
    fw.write("你好你好",0,1);

    fw.close();

}

10.5 字符缓冲流

BufferedInputStream
BufferedOutputStream

BufferedReader
readLine() -> 一次读取一整行字符串,同时结束标记是null,不会读取到回车换行符
BufferedWriter
newLine() -> 写出回车换行,但是此方法具有跨平台效果

public static void main(String[] args) throws IOException{
    BufferedReader br = new BufferedReader(new FileReader("a.txt"));
    BufferedWriter br = new BufferedWriter(new FileWriter("bufferCopy.txt"));

    String line;

    while((line = br.readLine())!=null){
        bw.write(line);

    }
    br.close();
    bw.close();

}

11、转换流

11.1 作用

转换流可以按照指定的编码表读写数据

11.2 说明

在JDK中,提供了两个类用于实现将字节流转换为字符流,它们分别是InputStreamReader和OutputStreamWriter
(1)InputStreamReader是Reader的子类,它可以将一个字节输入流转换成字符输入流,方便直接读取字符。
(2)OutputStreamWriter是Writer的子类,它可以将一个字节输出流转换成字符输出流,方便直接写入字符。

11.2.1 InputStreamReader

字节流通向字符流的桥梁
InputStreamReader(InputStream in)
InputStreamReader(InputStream in, String charsetName)

11.2.2 相关代码一
public static void main(String[] args) throws IOException{
    InputStreamReader isr = new InputStreamReader(new FileInputStream("b.txt"),"utf-8");

    int i;

    while((i=isr.read())!=-1){
        System.out.println((char)i);
    }

    isr.close();
}

或者

public static void main(String[] args) throws IOException{
    BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("b.txt"),"utf-8"));
 
    String line = br.readLine();
    System.out.println(line);

    br.close();
}

11.2.3 OutputStreamWriter

字符流通向字节流的桥梁

11.2.4 相关代码
public static void main(String[] args) throws IOException{
    OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("e.txt"),"utf-8");

    osw.write("你好你好");

    osw.close();
}


12、File类

12.1 说明

File类用于封装一个路径,这个路径可以是从系统盘符开始的绝对路径,也可以是相对于当前目录而言的相对路径。封装的路径可以指向一个文件,也可以指向一个目录,在File类中提供了针对这些文件或目录的一些常规操作。

12.2 File相关代码

/**
*File: Java中的File类可以将文件或文件夹的路径,封装成对象。并通过代码,对这些文件或文件夹对象,进行操作。
*相对路径:相对于当前项目下的文件或文件夹。
*绝对路径:从盘符的根目录开始,一直指向到某个具体的文件。
*/
public static void main(String[] args){
    File file = new File("D:\\c.flv");
    System.out.println(file.exists());

    File file2 = new File("bbb");
    System.out.println(file2.exists());
}

12.3 File类常用构造方法

方法声明 功能描述
File(String pathname) 通过指定的一个字符串类型的文件路径来创建一个新的File对象
File(String parent,String child) 根据指定的一个字符串类型的父路径和一个字符串类型的子路径(包括文件名称)创建一个File对象
File(File parent,String child) 根据指定的File类的父路径和字符串类型的子路径(包括文件名称)创建一个File对象

12.4 构造方法的相关代码

public static void main(String[] args){
File f1 = new File(“D:”,“a.flv”);
System.out.println(f1.exists());

//File(String parent,String child)
File f2 = new File(new File("D:"),"a.flv");
System.out.println(f2.exists());

}

12.5 File类的普通方法

public class Demo2_FileMethod{
    public static void main(String[] args){
    }


    private static void method4(){
        File f = new File("D:\\a.flv");
        
        //boolean isFile()  判断当前对象是否是文件
        System.out.println(f.isFile());

        //boolean isDirectory()  判断当前对象是否是文件夹
        System.out.println(f.isDirectory());
        
        System.out.println(f.isAbsolute());

        //long lastModified()   获取最后修改时间的毫秒值
        long time = f.lastModified();
        System.out.println(new Date(time));

        //long length()   返回文件大小
        long length = f.length();
        System.out.println(length);

    }

    private static void method3(){
        //boolean canRead()
        File f = new File("D:\\a.flv");
        System.out.println(f.canRead());

        //boolean canWrite()
        System.out.println(f.canWrite());

    }

    private static void method2(){
        File f = new File("D:\\a.flv");
        //1.getName()   获取文件\文件夹名称
        System.out.println(f.getName());

        //2.getPath()   获取对象构造方法中封装的路径
        System.out.println(f.getPath());

        //3.getAbsolutePath()   获取绝对路径
        System.out.println(f.getAbsolutePath());

        //4.getParent()    获取父级路径
        System.out.println(f.getParent());
    
    }

    public static void method1() throws IOException{
        //1.delete方法,删除文件或文件夹
        File f1 = new File("D:\\b.flv");
        System.out.println(f1.delete());

        //2.createNewFile(); 创建一个文件,返回是否创建成功
        System.out.println(f1.createNewFile());

    }
}

public class demo2_FileMethod{
        /**
        *  String[] list()     返回当前文件夹下所有的文件和文件夹名称
        *
        *  String[] list(FilenameFilter filter)   返回当前文件夹下所有的文件和文件夹名称,带有筛选效果
        *
        *  File[] listFiles()  返回当前文件夹下所有的文件和文件夹对象
        */

        public static void main(String[] args) throws IOException{
            File f = new File("D");
            File[] listFiles = f.listFiles();

            for(File file : listFiles){
                if(file.isDirectory()){
                 System.out.println(file.toString());
                 }
            } 
        
            private static void method5(){

            File f = new File("D:");

            String[] sArr = f.list(new FilenameFilter(){
                @Override
                public boolean accept(File dir, String name){
                    File ff = new File(dir,name);

                    return ff.isDirectory();
                }
            });

            for(String s : sArr){
                System.out.println(string);
            } 
        }
    
}

13、遍历目录下的文件

13.1 说明

File类中提供了一个重载的list(FilenameFilter filter)方法,该方法接收一个FilenameFilter接口类型的参数,其中定义了一个抽象方法accept(File dir,String name)用于依次对指定File的所有子目录进行迭代。

/**
*  String[] list()     返回当前文件夹下所有的文件和文件夹名称
*
*  String[] list(FilenameFilter filter)   返回当前文件夹下所有的文件和文件夹名称,带有筛选效果
*
*  File[] listFiles()  返回当前文件夹下所有的文件和文件夹对象
*/
public class Demo3_FileMethod{
    public static void main(String[] args){

    }
    
    public static void printFile(File dir){
        //1.获取传入文件夹下的所有文件和文件夹对象
        File[] files = dir.listFiles();
        //2.遍历数组,获取到每一个文件和文件夹对象
        for(File file : files){
            //3.如果是文件就打印该文件的名称
            if(file.isFile())
            {
                System.out.println(file.getName());
            }else if(file.isDirectory()){
                //4.如果是文件夹,就递归调用该方法
                if(file.listFiles()!=null){
                    printFile(file);
                }
            }
        }
    }

    private static void method2(){
        //筛选遍历扩展名“.txt”文件
        File f = new File("D:");
        if(f.isDirectory()){
            String[] sArr = f.list((dir,name)->name.endsWith(".txt"));
            Arrays.stream(sArr).forEach(System.out::println);
        }
    }

    private static void method1(){
        File f = new File("D:");
        if(f.isDirectory()){
            String[] sArr = f.list();
            Arrays.stream(sArr).forEach(System.out::println);
        } 
    }

}

14、删除文件及目录

/**
*
* delete : 该方法只能删除文件,或者空文件夹
*
*/
public class Demo_DelDir{
    public static void main(String[] args){
        File f = new File("F:\\测试数据");
        System.out.println(f.delete());
    }
}


//Java中的delete方法是不走回收站的,不要使用重要的数据进行测试
public class Demo_DelDir{
    public static void main(String[] args){
        del(new File("F:\\测试数据"));
    
    }

    public static void del(File dir){
        File[] listFiles = dir.listFiles();

        for(File file : listFiles){
            if(file.isFile()){
                //如果是文件,就可以直接删除
                file.delete();
            }else if(file.isDirectory()){
                //递归调用该方法
                if(file.listFiles()!=null){
                    del(file);
                }
            }

        }

        //当所有的文件都删除后,单独删除空文件夹
        dir.delete();
    }
}


15、RandomAccesseFile类

15.1 原理

RandomAccessFile对象包含了一个记录指针来标识当前读写处的位置。
(1)当新建RandomAccessFile对象时,该对象的文件记录指针会在文件开始处(即标识为0的位置)
(2)当读写了n个字节后,文件记录指针会向后移动n个字节
(3)除了按顺序读写外,RandomAccessFile对象还可以自由的移动记录指针、即可以向前移动,也可以向后移动。

15.2 常用方法

  方法声明                      功能描述

long getFilePointer() 返回当前读写指针所处的位置
void seek(long pos) 设定读写指针的位置,与文件开头相隔pos个字节数
int skipBytes(int n) 使读者指针从当前位置开始,跳过n个字节
void write(byte[] b) 将指定的字节数组写入到这个文件,并从当前文件指针开始
void setLength(long newLength) 设置此文件的长度
final String readLine() 从指定文件当前指针读取下一行内容
说明:seek(long pos)方法可以使RandomAccessFile对象中的记录指针向前、向后自由移动,通过getFilePointer()方法,便可获取文件当前记录指针的位置。

15.3 相关代码

//RandomAccessFile : 默认带有尾部追加的效果
public class Test_RandomAccessFile{
    public static void main(String[] args) throws IOException{
        RandomAccessFile raf = new RandomAccessFile("config.txt","rw");

        int num = Integer.parseInt(raf.readLine()) - 1;

        if(num>0){
            //说明还有机会
            System.out.println("您还有" + num + "次试用机会");
            
            raf.seek(0); //将记录指针重新指向文件开头

            raf.write((num + " ").getBytes());
        }else{
            System.out.println("您的试用次数已到达上限,请购买正版");
        }

        raf.close();
    }
}


16、对象序列化

程序在运行过程中,可能需要将一些数据永久的保存到磁盘上,而数据在Java中都是保存在对象当中的,那么想要保存到磁盘上,则需要使用Java中的对象序列化。

16.1 定义

对象的序列化(Serializable)是指将一个Java对象转换成一个I/O流中的字节序列的过程

16.2 目的

为了将对象保存到磁盘中,或允许在网络中直接传输对象。

16.3 说明

对象序列化可以使内存中的Java对象转换成与平台无关的二进制流
即可以将这种二进制流持久地保存在磁盘上,又可以通过网络将这种二进制流传输到另一个网络节点
其他程序在获得了这种二进制流后,还可以将它恢复成原来地Java对象
这种将I/O流中的字节序列恢复为Java对象的过程被称之为反序列化(Deserialize)

16.1 相关代码

public class Demo_ObjectStream{

private static void readObj() throws IOException, FileNotFoundException, ClassNotFoundException{
    ObjectInputStream ois = new ObjectInputStream(new FileInputStrea("obj.txt"));
    Object object = ois.readObject();
    System.out.println(object);
}
public static void main(String[] args) throws IOException{
    //1. 创建对象输出流,用于将对象写出到文件中去
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));

    Person p = new Person("张三",23);

    //2. 使用对象输出流写出对象
    oos.writeObject(p);

    //3. 关闭流释放资源
    oos.close();
}

}

17、NIO

17.1 NIO概述

17.1.1 定义

从JDK1.4开始,Java提供了一系列改进的用于处理输入/输出的新功能
这些功能被称之为NIO(New I/O)

17.1.2 说明

(1)NIO采用内存映射文件的方式来处理输入/输出,它将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样来访问文件了。
(2)在NIO中,使用的是Channel(通道)和Buffer(缓冲器)
(3)数据总是从通道读入缓冲器,或从缓冲器写入通道。

17.1.3 三大核心

(1)Buffer(Buffer缓冲器)
Buffer本质是一个数组缓冲区,读入或写出到Channel中的所有对象都会先放到Buffer中。

(2)Channel(通道)
Channel是对传统的输入/输出的模拟,在NIO中,所有的数据都需要通过通道流的形式传输。

(3)Selecter用于监听多个通道的事件(例如:连续打电话、数据到达等),主要用于多线程处理。

17.2 Buffer(缓冲器)

17.2.1 作用

java NIO中的Buffer用于和NIO中的Channel进行交互,交互时数据会从Channel读取到Buffer中,或从Buffer写入到Channel中

17.2.2 说明

Buffer类似于一个数组,它可以保存多个类型相同的数据。
Buffer是一个抽象类,其子类有ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer和ShortBuffer。
Buffer子类中最常用的是ByteBuffer和CharBuffer

17.2.3 使用

Buffer类的子类中并没有提供构造方法,因此不能通过构造方法来创建对象。
创建Buffer对象,通常会通过子类中的static XxxBuffer allocate(int capacity)方法来实现(其中Xxx表示不同的数据类型,而capacity表示容量)。

例子

charBuffer buffer = CharBuffer.allocate(6);
17.2.4 相关代码
public class Demo1_Buffer{
    /**
    *Buffer(缓冲器)
    *底层就是一个数组,主要用于和Channel通道配合,完成数据的传输
    *Buffer是一个抽象类,抽象类不能直接new对象,需要使用其子类
    *
    *注意:
    *   子类不能使用new对象,需要通过allocate方法去创建
    *   capacity(): 容量
    *   limit():  可以操作到哪个索引(界限)
    *   position():当前准备操作的索引
    *   mark():  标记,用来记录当前position的值
    *   reset():  如果position的值发生变化了,那么通道随reset可以返馈mark记录的那个值。
    *   put(): 向缓冲器中添加数据
    *   get(): 获取缓冲器中的数据
    */

    public class static void main(String[] args){
        //1. 创建一个缓冲器
        ByteBuffer buffer = ByteBuffer.allocate(1024);

        System.out.println("缓冲器的容量:" + buffer.capacity());
        System.out.println("界限:" + buffer.limit());
        System.out.println("索引:" + buffer.position());

        //2. 向容器中存储数据
        buffer.put("abcde".getBytes());

        System.out.println("缓冲器的容量:" + buffer.capacity());
        System.out.println("界限:" + buffer.limit());
        System.out.println("索引:" + buffer.position());

        //3. 切换到读写模式
         buffer.flip();

        System.out.println("缓冲器的容量:" + buffer.capacity());
        System.out.println("界限:" + buffer.limit());
        System.out.println("索引:" + buffer.position());
        
        buffer.mark();  //读取之前加入标记

        //4. 读取数据
        byte[] bys = new byte[2];
        buffer.get(bys);
        System.out.println(new String(bys));
        System.out.println("缓冲器的容量:" + buffer.capacity());
        System.out.println("界限:" + buffer.limit());
        System.out.println("索引:" + buffer.position());

        buffer.reset();  //将索引归为之前的标记处

        byte[] bys = new byte[2];
        buffer.get(bys);
        System.out.println(new String(bys));
        System.out.println("缓冲器的容量:" + buffer.capacity());
        System.out.println("界限:" + buffer.limit());
        System.out.println("索引:" + buffer.position());

        byte[] bys = new byte[2];
        buffer.get(bys);
        System.out.println(new String(bys));
        System.out.println("缓冲器的容量:" + buffer.capacity());
        System.out.println("界限:" + buffer.limit());
        System.out.println("索引:" + buffer.position());

    }
}

17.3 Channel(通道)

17.3.1 说明

Channel可以异步的执行I/O的读写操作
Channel的读写操作是双向的,既可以从Channel中读取数据,又可以写数据到Channel,而流的读写操作通常都是单向的。
Channel可以直接将指定文件的部分或者全部直接映射成Buffer
Channel只能与Buffer进行交互,程序不能直接读写Channel中的数据

17.3.2 Channel接口的实现类

主要包括:DatagramChannel、FileChannel、Pipe.SinkChannel、Pipe.SourceChannel、ServerSocketChannel、SocketChannel等
(1)DatagramChannel用于支持UDP网络通信
(2)FileChannel用于从文件中读写数据
(3)Pipe.SinkChannel和Pipe.SourceChannel用于支持线程之间的通信
(4)ServerSocketChannel和SocketChannel用于支持TCP网络通信

17.3.3 使用

(1)Channel对象并不是通过构造方法来创建的,而是通过传统I/O的getChannel()方法来获取对应的Channel
(2)不同的流所获取的Channel是不同的,例如FileInputStream和FileOutputStream获取的是FileChannel,同时还可以使用RandomAccessFile获取该对象。
(3)PipedInputStream和PipedOutputStream所获得的是Pipe.SinkChannel和Pipe.SourceChannel.

17.3.4 相关代码一
public class Demo2_Channel{
    //使用NIO完成文件的拷贝
    public static void main(String[] args) throws IOException{
        //1. 创建输入输出流对象
        FileInputStream fis = new FileInputStream("D:\\a.flv");
        FileOutputStream fos = new FileOutputStream("E:\\a.flv");

        //2. 获取输入和输出的通道
        FileChannel inChannel = fis.getChannel();
        FileChannel outChannel = fos.getChannel();

        //3. 创建缓冲器
        ByteBuffer buffer = ByteBuffer.allocate(1024);

        while((inChannel.read(buffer))!=-1){
            //切换读写模式
            buffer.flip();
            outChannel.write(buffer);
            buffer.clear();
        }

        inChannel.close();
        outChannel.close();

        fis.close();
        fos.close();

    }
}

17.3.5 相关代码二
public class Demo2_Channel{
    //使用NIO完成文件的拷贝
    public static void main(String[] args) throws IOException{
        
        RandomAccessFile inFile = new RandomAccessFile("D:\\a.flv","rw");
        RandomAccessFile outFile = new RandomAccessFile("E:\\a.flv","rw");
       
        //1. 获取通道
        FileChannel inChannel = fis.getChannel();
        FileChannel outChannel = fos.getChannel();
        
        long num = inChannel.transferTo(0, inChannel.size(), outChannel);

        if(num>0){
            System.out.println("复制成功!!");
        }
        
        inFile.close();
        outFile.close();
        inChannel.close();
        outChannel.close();

    }
}

17.4 Path接口

17.4.1 作用

Path接口是一个共用在文件系统中定位文件的对象,通常表示一个依赖于系统的文件路径

17.4.2 说明

NIO.2还提供了Paths和Files两个工具类,
Paths类中提供了两个返回Path的静态方法,通过这两个方法可以创建Path对象。
Files类中提供了大量的静态方法来操作文件。

17.4.3 相关代码
public class Demo1_Path{
    public static void main(String[] args){
        //1. 获取Path对象
        Path path = Paths.get("E:\\abc\\test");
        
        //2.createDirectories  创建多级文件夹
        Files.createDirectories(path);

        //3.createFile  创建文件
        Path filePath = Paths.get("E:\\abc\\test\\aaaa.txt");
        Files.createFile(filePath);

        //4. write(Path path, Iterable lines, OpenOption... options )
        //将文本写入文件,并传入指定的写入模式
        ArrayList list = new ArrayList<>();
        list.add("这是一行文本1");
        list.add("这是一行文本2");
        list.add("这是一行文本3");
        
        Files.write(filePath,list,StandardOpenOption.APPEND);

        //static List readAllLines(Path path) 从文件中读取所有行
        List list2 = Files.readAllLines(filePath);
        System.out.println(list2);

        //static long size(Path path)  返回文件的大小,以字节为单位
        long size = Files.size(filePath);
        System.out.println(size);
    }
}

17.5 File工具类

public class Demo2_File{
    public static void main(String[] args){
        //1. 获取Path对象
        Path path = Paths.get("D:\\a.flv");
        
        //2. 获取Path对象中的信息
        System.out.println(path.getRoot());  //获取根路径
        System.out.println(path.getParent());  //获取父路径
        System.out.println(path.getNameCount());  //path中的路径名称数

        for(int i=0;i 获取路径名称
            Path name = path.getName(i);
            System.out.println(name);
        }
        
        System.out.println(path.toAbsolutePath());
        System.out.println(path.toUri());
    }
}

你可能感兴趣的:(Java,java,开发语言,intellij-idea)