IO流

IO流

IO流概述

IO(Input/Output)流,即输入输出流
1.字节流和字符流
根据流的传输数据单位的不同,可以分为字节流和字符流
2.输入流和输出流
输入流只能从流中读取数据,输出流只能向流中输出数据
3.节点流和处理流
节点流被称为低级流,是指可以从一个特定的IO设备(如磁盘)读写数据的流,它只能直接连接数据源,进行数据的读写操作;处理流也被称为高级流,它是对一个已存在的节点流进行连接和封装,通过封装后的流实现流的读写能力。在这里插入图片描述
在这里插入图片描述

字节流

根据数据传输的方向可以将其分为字节输入流和字节输出流,在JDK中,提供了两个抽象类InputStream和OutputStream,它们是字节流的顶级父类,所有的字节输入流都继承自InputStream,所有的字节输出流都继承自OutputStream。
在这里插入图片描述
InputStream被看成一个输入管道,OutputStream被看成一个输出管道。
下面是InputStream的方法。
int read()
从输入流读取了一个八位的字节,把它转换为0-255之间的整数,并返回这一整数。当没有可用字节时,将返回-1
int read(byte[]b)
从输入流读取若干字节,把它们保存到参数b指定的字节数组中,返回的整数表示读取字节的数目
int read (byte [] b, int off , int len)
从输入流读取若干字节,把它们保存到参数b指定的字节数组中,off指定字节数组开始保存数据的其实下标,len表示读取的字节数目。
void close()
关闭此输入流,并释放与该流关联的所有系统资源。
第一个方法从输入流中读入字节,而第二个第三个啧将若干字节一字节数组的形式一次性读入,从而提高读取数据的效率。

与InputStream对应的是OutputStream,OutputStream是用来写数据的,下面是一些方法
void write(int b)
向输出流写入一个字节
void write(byte [] b)
把参数b指定的字节数组的所有字节流写到输出流
void write (byte [] b,int off,int len)
将指定byte数组中从偏移量off开始的len个字节写入输出流
void finsh()
刷新次树池流并强制写出所有缓冲的输出字节
void close()
关闭此输出流并释放与此流相关的所有系统资源

第一个方法诸葛写入字节,后两个方法是讲若干个字节以字节数组的形式一次性写入,从而提高写数据的效率。flush方法用来将当前输出流缓冲区中的数据强制写入目标设备,此过程被称为刷新。

字节流读取文件

针对文件的读写操作,JDK提供了两个类,分别是FileInputStream和FileOutputStream。
FileInputStream是操作文件的字节输入流,专门用于读取文件的数据源,由于从文件读取数据是重复的操作,因此需要用过训话御景园来实现数据的持续读取。
下面通过一个案例来再试下字节流对文件数据的读取,首先创建一个文件“test.txt",在文件中输入hello并保存,然后创建一个读取文本文件的类。

package Demo01;

import java.io.File;
import java.io.FileInputStream;

public class Test1 {
    public static void main(String[] args) throws Exception{
//        创建一个文件字节输入流来读取文件
        File file;
        FileInputStream inputStream = new FileInputStream("test.txt");
//        定义一个int类型的变量b
        int b = 0 ;
//        通过循环来读取文件,当前返回值为-1结束循环
        while ((b=inputStream.read())!=-1){
            System.out.println(b);
        }
        inputStream.close();
    }
}
//输出结果为
97
98
99
100
101
102
49
50

FileInputStream对应的是FileOutputStream,它是操作文件的字节输出流,专门用于把数据写入文件。

package Demo01;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class Test1 {
    public static void main(String[] args) throws Exception{
        File file;
//        创建文件输出流,并制定输出文件名称,文件名后跟的true作用是,不重新清空文件再向内输入内容,若没有true,则会
//清空文件
        FileOutputStream fileOutputStream = new FileOutputStream("fileOut.txt",true);
//        定义一个字符串
        String string = " name " ;
//        将字符串转换为字节数组进行写入操作
        fileOutputStream.write(string.getBytes());
        fileOutputStream.close();
    }
}

在IO流中进行数据读写操作会出现异常,那么关闭的close方法则无法执行,流对象所占用的内存将无法及时释放,
为了保证close方法一定执行,会将代码写在finally中

  finally {
            try{
                if(fileInputStream !=null){
                    fileInputStream.close();
                }
            }catch (Exception e ){
                e.printStackTrace();
            }
            try{
                if(fileOutputStream != null){
                    fileOutputStream.close();
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }

文件的拷贝

在应用程序中,输入流和输出流通常一起使用,例如文件的拷贝就需要通过输入输出流来读取原文件中的数据,并通过输出流将数据写入新文件。

package Demo01;

import java.io.*;

public class Test1 {
    public static void main(String[] args) throws Exception{
//      创建文件输入流对象读取制定目录下的文件
        FileInputStream fileInputStream = new FileInputStream("F/picture/picture01.jpg");
//      创建文件输出流对象将读取到的文件内容写入到指定目录中
        FileOutputStream fileOutputStream = new FileOutputStream("F/recive/test01");
//      定义一个int类型的变量len
        int len = 0 ;
//      获取拷贝文件前的系统时间
        long beginTime = System.currentTimeMillis();
//      通过循环将读取到的文件字节信息写入到新文件
        while ((len = fileInputStream.read()) != -1){
            fileOutputStream.write(len);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("花费的时间为"+(endTime - beginTime));
//      关闭流
        fileInputStream.close();
        fileOutputStream.close();
    }
}
//输出结果为3705

程序运行结束后,发现文件中的picture01 被复制了,且在另一个文件夹中被重命名了。

字节流的缓冲区

实现文件的拷贝,一个字节一个字节的读写,需要频繁操作文件,这种效率较低。就好像发快递,一件一件的发送效率很低,为了减少运输次数,可以把它们装在一个车箱,这样就可以成批的运送快递,这时车厢就相当于一个缓冲区。同样当通过流的方式拷贝文件时,可以定义一个数组作为缓冲区。这样读取文件时,就可以一次性读取多个文件,将数据先保存在字节数组中,然后将字节数组中的数据一次性写入到新文件中。
接下来演示如何使用缓冲区来拷贝文件

package Demo01;

import java.io.*;

public class Test1 {
    public static void main(String[] args) throws Exception{
//      创建文件输入流对象读取制定目录下的文件
        FileInputStream fileInputStream = new FileInputStream("picture/picture01.jpg");
//      创建文件输出流对象将读取到的文件内容写入到指定目录中
        FileOutputStream fileOutputStream = new FileOutputStream("recive/test01.jpg");
//      定义一个int类型的变量len
        int len = 0 ;
//      定义一个长度为1024的数组
        byte [] buff = new byte[1024];
//      获取拷贝文件前的系统时间
        long beginTime = System.currentTimeMillis();
//      通过循环将读取到的文件字节信息写入到新文件
        while ((len = fileInputStream.read(buff)) != -1){
//      每循环读取一次字节数组,就将所读取到的内容写入到新文件
            fileOutputStream.write(buff,0,len);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("花费的时间为"+(endTime - beginTime));
//      关闭流
        fileInputStream.close();
        fileOutputStream.close();
    }
}
//输出结果为7

拷贝过程中,使用while循环语句逐渐实现字节文件的拷贝,每循环一次,就从文件读取若干字节填充到字节数组,并通过变量len记住读入数组的字节数,然后从数组的第一个字开始,将len个字节一次写入到新文件,循环往复,当len的值为-1时,说明已经到了文件末尾,循环会结束,整个拷贝过程也就结束了,花费的时间明显减少,效率增加了很多。

字节缓冲流

在io包中华提供了两个带缓冲的字节流,分别是BufferedInputStream和BufferedOutputStream类型的参数,他们的构造方法中分别接受InputStream和OutputStream类型的参数作为对象,在读写数据时提供缓冲功能。应用程序,缓冲流和底层字节流之间的关系图如图所示。
源设备 ->字节流->字节缓冲流->应用程序->字节缓冲流->字节流->目标设备
应用程序是通过缓冲流来完成数据读写的,而缓冲流又是通过底层的字节流设备进行关联的,接下来展示两个方法的使用。

package Demo01;

import java.io.*;

public class Test1 {
    public static void main(String[] args) throws Exception{
//        创建用于输入和输出的字节缓冲流对象
        BufferedInputStream bis = new BufferedInputStream(
                new FileInputStream("picture/picture01.JPG"));
        BufferedOutputStream bos =  new BufferedOutputStream(
                new FileOutputStream("recive/test01.jpg"));
//        定义一个int类型的变量len
        int len = 0 ;
//        获取拷贝文件的系统时间
        long beginTime = System.currentTimeMillis();
//        通过循环读取输入字节缓冲流的数据,并通过输出字节缓冲流写入到新文件
        while ((len = bis.read()) != -1){
            bos.write(len);
        }
//        获取拷贝后的系统时间
        long endTime = System.currentTimeMillis();
//        输出拷贝所花费的时间
        System.out.println("花费时间为"+(endTime - beginTime) + "毫秒");
//        关闭流
        bis.close();
        bos.close();
    }
}

BufferedInputStream和BufferedOutputStream两个流内部都内置了一个大小为8192的数组,和字节流缓冲区类似,都对数据进行了缓冲,有效提高数据读写效率。

字符流

字符流概述

字符流也有两个抽象的顶级父类Reader和Writer,其中Reader是字符输入流,用于从某个源设备读取字符。Writer是字符输出流,用于想某个目标设备写入字符。

字符流操作文件

想从文件中读取字符就可以使用字符输入流FileReader,通过呲溜可以从文件中读取一个或一组字符。
在项目目录下兴建文本文件“reader.txt”并在其中输入字符“好好学习,天天向上!”然后创建一个使用字符输入流FileReader读取文件中字符的类。

package Demo01;
//该方法是keySet()方法

import java.io.FileReader;
import java.util.*;

/**
 * @author Dell
 */
public class Test02 {
    public static void main(String[] args) throws Exception {
//        创建FileReader对象,并指定需要读取的文件
        FileReader fileReader = new FileReader("reader.txt");
//        定义一个int类型的变量len,其初始化值为0
        int len = 0;
//        通过循环来判断是否读取到了文件末尾
        while ((len = fileReader.read())!=-1){
//        输出读取到的字符
            System.out.println((char) len);            
        }
//        关闭流
        fileReader.close();
    }
}

由于read()方法返回的是int类型的值,如果想获得字符就需要进行强制类型转换。
如果要向文件中写入字符就需要使用FileWriter类。

package Demo01;

import java.io.*;

/**
 * @author Dell
 */
public class Test02 {
    public static void main(String[] args) throws Exception {
        FileWriter fileWriter = new FileWriter("write.txt");
        fileWriter.write("轻轻地我走了,\r\n");
        fileWriter.write("正如我轻轻地来;\r\n");
        fileWriter.write("我轻轻地招手,\r\n");
        fileWriter.write("作别西天的云彩。\r\n");
        fileWriter.close();
    }
}

如果指定文件不存在,就会先创建文件,再写入数据,如果文件存在,则会先清空文件的内容。如果想在文末添加内容,则在文件名后添加true。
下面在演示如何用缓冲区实现对文字的拷贝。

package Demo01;

import java.io.*;

/**
 * @author Dell
 */
public class Test02 {
    public static void main(String[] args) throws Exception {
//        创建FileReader对象
        FileReader fileReader = new FileReader("reader.txt");
//        创建FileWrite对象,并指定写入数据的目标文件
        FileWriter fileWriter = new FileWriter("write.txt");
        int len = 0;
//        定义一个长度为1024的字符数组
        char [] buff =new char [1024];
//        通过循环来判断是否到了文件末尾
        while ((len = fileReader.read(buff))!=-1){
//          输出读取到的字符
            fileWriter.write(len);
        }
        fileReader.close();
        fileWriter.close();
    }
}

字符流也提供了带缓冲区的字符缓冲流BufferedReader,BufferedWriter,在BufferedRead中有一个重要方法是readLine(),该方法用于一次读取一整行文本。

package Demo01;

import java.io.*;

/**
 * @author Dell
 */
public class Test02 {
    public static void main(String[] args) throws Exception {
//        创建一个字符输入流缓冲对象
        BufferedReader br = new BufferedReader(new FileReader("reader.txt"));
//        创建一个字符输出流缓冲对象
        BufferedWriter bw = new BufferedWriter(new FileWriter("writer.txt"));
//        申明一个字符串变量
        String str = null;
//        循环时每次读取一行文本,如果不为null(即到了文本末尾),则继续循环
        while((str = br.readLine()) !=null){
            bw.write(str);
//		  写入一个换行符,该方法会根据不同的操作系统生成对应的换行符  
            bw.newLine();
        }
        br.close();
        bw.close();
    }
}

在拷贝过程中,每次循环都用readLine()进行读取,然后通过write写入目标文件,同时使用newLine()进行换行写入,否则读取源文件所有行内容都会追加写入目标文件一行中()。其中readLine方法会逐个读取字符,当读到回车符"\r",或者换行符"\n"时会将读到的字符作为一行的内容返回。

转换流

前面提到的io流可分为字符流和字节流,有时字符流和字节流也需要转换。在JDK中提供了两个方法将字节流转换为字符流,他们是InputStreamReader和OutputStreamWriter。它们可以先将字节输入流转换为字符输入流,方便直接读取字符。

package Demo01;

import java.io.*;

/**
 * @author Dell
 */
public class Test02 {
    public static void main(String[] args) throws Exception {
//        1.创建字节输入流对象,获取源文件
        FileInputStream in = new FileInputStream("reader.txt");
//        将字节输入刘对象转换成字符输入流对象
        InputStreamReader isr = new InputStreamReader(in);
//        创建字符输入缓冲流对象
        BufferedReader br = new BufferedReader(isr);
//        2.创建字符输出流对象,指定目标文件
        FileOutputStream out = new FileOutputStream("writer.txt");
//        将字节输出流对象转换为字符输出流对象
        OutputStreamWriter osw = new OutputStreamWriter(out);
//        创建字符输出流缓冲对象
        BufferedWriter bw = new BufferedWriter(osw);
//        定义一个字符串变量
        String line = null;
//        通过循环判断是否到文件末尾
        while ((line = br.readLine()) != null){
//        输出到读取文件
            bw.write(line);
            bw.newLine();
        }
        br.close();
        bw.close();
    }
}

需要注意的是,使用转换流只能针对文本操作的文件,如果操作的是图片视频等,此时转换则会造成数据丢失。

File类

File类的常用构造方法
public File(String pathname):根据指定的路径(可以是绝对路径或相对路径)创建File对象。
public File(String parent, String child):根据指定的父文件夹和子文件或者子文件夹创建File对象
public File(File parent, String child):根据指定的父文件夹对象和子文件或者子文件夹创建File对象
File类的常用方法
public String getName(): 返回File构造方法中传入路径表示的文件名或目录名(如果是目录名则是最后一级子目录名)
public String getPath(): 返回File构造方法中传入的路径名
public String getParent():返回File构造方法中传入文件或目录路径的父目录,如果传入路径没有父目录,则返回null
public String getAbsolutePath(): 返回File对象表示的文件或目录的绝对路径名
public File getAbsoluteFile(): 返回一个通过此File对象表示的文件或目录的绝对路径名重新new出来的一个File对象

public boolean exists(): 判断此File对象表示的文件或目录是否存在
public boolean canWrite():判断此File对象表示的文件或目录是否可写
public boolean canRead():判断此File对象表示的文件或目录是否可读
public boolean isFile():判断此File对象是否是表示文件,而非目录
public boolean isDirectory():判断此File对象是否是表示目录,而非文件
public boolean isAbsolute():判断此File对象的构造方法传入的路径是否是绝对路径

public long lastModified():返回文件的最后修改时间
public long length():返回文件内容的长度
public boolean createNewFile():创建一个此File对象所表示的文件,返回true则创建成功,false则失败。

public File[] listFiles(FilenameFilter filter):返回此File对象下通过FilenameFilter过滤器过滤后的所有文件和一级子目录
public File[] listFiles(FileFilter filter):返回此File对象下通过FileFilter过滤器过滤后的所有文件和一级子目录

FilenameFilter和FileFilter过滤器的区别就是accept抽象方法接收的形参不同,FilenameFilter的accept方法接收文件的父目录dir和文件名name,一般是在accept方法重写时通过new File(dir, name)得到文件的File对象。而FileFilter的accept方法直接接收文件的File对象,较FilenameFilter省略了new一个File对象的步骤。

遍历目录下的文件

File类中有一个list()方法,该方法用于遍历某个指定目录下所有文件的名称,接下来演示如何使用

import java.io.*;
import java.lang.reflect.Array;
import java.util.Arrays;

public class test01 {
    public static void main(String[] args) throws Exception {
//        创建File对象,并指定文件路径
        File file = new File("D:\\IntelliJ IDEA Community Edition 2021.1");
//        判断是否目录
        if(file.isDirectory()){
//            获取目录中所有文件的名称
            String [] fileNames = file.list();
//            对指定路径下的文件或目录进行遍历
            Arrays.stream(fileNames).forEach(f-> System.out.println(f));
        }
    }
}

创建了一个File对象,然后指定路径,通过调用方法,检测是否为目录,是就调用list方法,获得一个String类型的数组fileNames,接着通过数组类工具Arrays的stream方法讲数组先转化为stream流,并进行遍历,依次打印出每个文件的文件名。
虽然使用list方法可以遍历得到所有文件,但是有时只需要获取指定类型的文件,如只需获取txt文件,这时就需要用到public File[] listFiles(FilenameFilter filter)方法:返回此File对象下通过FilenameFilter过滤器过滤后的所有文件和一级子目录。FilenameFilter是一个函数时接口,该接口中定义了一个抽象方法accept(File dir,String name)用于一次对指定File所有子目录或文件进行迭代。

import java.io.*;
import java.lang.reflect.Array;
import java.util.Arrays;

public class test01 {
    public static void main(String[] args) throws Exception {
//        创建File对象,并指定文件路径
        File file = new File("D:\\IntelliJ IDEA Community Edition 2021.1");
//        判断是否目录
        if(file.isDirectory()){
//      使用Lambda表达式过滤目录中所有以.txt结尾的文件的名称
            String [] fileNames = file.list(
                    ((dir, name) -> name.endsWith(".txt"))
            );
//            对指定路径下的文件或目录进行遍历
            Arrays.stream(fileNames).forEach(f-> System.out.println(f));
        }
    }
}

有时在一个目录下还有子目录,如果想得到所有子目录下的文件类型,则需要使用另一个方法listFiles(),该方法返回一个File对象数组,当对数组中元素进行遍历时,如果元素中还有子目录需要遍历,则可以使用递归再次遍历子目录。

import java.io.*;
import java.lang.reflect.Array;
import java.util.Arrays;

public class test01 {
    public static void main(String[] args) throws Exception {
//        创建File对象,并指定文件路径
        File file = new File("D:\\IntelliJ IDEA Community Edition 2021.1");
//        调用fileDir()方法,遍历目录
        fileDir(file);
    }
//遍历目录及子目录
    public static void fileDir(File file){
//        获得目录下所有文件,并赋值给数组
        File [] listFiles = file.listFiles();
//        遍历循环数组
        for(File files : listFiles){
//            如果遍历的是目录,则调用forDir方法
            if(files.isDirectory()){
                fileDir(files);
            }
            String [] fileNames = file.list(
                    ((dir, name) -> name.endsWith(".txt"))
            );
            Arrays.stream(fileNames).forEach(f-> System.out.println(f));
        }
    }
}

删除文件及其目录

操作文件时,常常需要删除一个目录下的某个文件或整个文件夹,这时可以用File类的delet()方法来实现,使用该方法时需要先判断当前目录下是否存在文件,如果存在,则需要先删除内部文件,然后在删除空的文件夹。

import java.io.*;
import java.lang.reflect.Array;
import java.util.Arrays;

public class test01 {
    public static void main(String[] args) throws Exception {
//        创建File对象,并指定文件路径
        File files = new File("C:\\IntelliJ IDEA Community Edition 2021.1");
//        调用删除方法
        deleteDir(files);
    }
//遍历目录及子目录
    public static void deleteDir(File files){
//        获得目录下所有文件,并赋值给数组
        File [] listFiles = files.listFiles();
//        遍历循环数组
        for(File file : listFiles) {
//            如果遍历的是目录,则调用delete方法
            if (file.isDirectory()) {
//             调用删除方法
                deleteDir(file);
            }
//            如果是文件就删除
            file.delete();
        }
//        删除文件加中所有文件后,再删除文件夹
        files.delete();
    }
}

RandomAccessFile

前面介绍的io流都有一个特点,就是只能按照数据的先后顺序读取源文件中的数据,或者按照数据的先后顺序向目标设备写入数据,但是从任意位置写入数据,则需要使用RandomAccessFile类,它不属于流泪,但是具有读写文件的功能,可以从随机从文件的任何位置开始执行读写数据的操作。
RandomAccessFile可以将文件以指定的操作权限(如只读,可读写等)的方式打开,具体使用了哪种权限取决去他采用的构造方法。
RandomAccessFile(File file,String mode) 使用参数file指定被访问的文件,并使用mode来指定访问模式
RandomAccessFile(String name,String mode) 使用参数name指定被访问文件的路径,并使用mode来指定访问模式
参数mode有四个值
r 表示以只读的方式打开文件,如果试图对文件对象进行改写,则会抛出异常。
rw 表示以读写的方式打开文件,如果文件不存在,则会自动创建该文件。
rsw 表示以读写方式打开文件,与rw相比,他对文件的内容或者元数据的每个更新都同步写入到底层的存储设备
rwd 表示以读写的方式来打开文件,与rw相比,他对文件的内容每个更新都同步西而入底层的存储设备。
RandomAccessFile中包含一个记录指针来标识当前读写处的位置,当程序新建RandomAccessFile对象s时,该对象的文件记录指正会在文件开始处,当读写了n个字节后,文件记录指针会自动往后移动n个字节。当然RandomAccessFile指针是可以自由移动的,可以往前,也可以往后。
long getFilePointer() 返回当前读写指针所处的位置
void seek(long pos) 设定读写指针的位置,与文件开头相隔pos个字节数
**int skipByte(int n)**使读写指针从当前位置开始,跳过n个字节
void write(byte [] b) 将指定的字节数组写入到这文件,并从当前文件指针开始
void setLength(long newLength) 设置文件的长度
final String readLine() 从指定文件当前指针读取下一行内容

RandomAccessFile在实际开发中也有用处,大家知道在一些软件使用时是需要付费的,但会有几次免费使用机会,接下来使用RandomAccessFile来模拟实现记录软件使用次数的过程。

package Demo01;

import java.io.*;

/**
 * @author Dell
 */
public class Test02 {
    public static void main(String[] args) throws Exception {
//        创建RandomAccessFile对象,并以只读模式打开time.txt文件
        RandomAccessFile raf = new RandomAccessFile("time.txt", "rw");
//        读取还可以使用的次数,第一次读取为5
        int times = Integer.parseInt(raf.readLine())-1;
//        判断剩余次数
        if(times>0){
//            每执行一次代表使用一次
            System.out.println("还剩"+times+"次");
//            将记录指针指向文件开头
            raf.seek(0);
//            将剩余次数再次写入文件中
            raf.write((times+"").getBytes());
        }else {
            System.out.println("使用次数已用完");
        }
//        关闭随机流
        raf.close();
    }
}

运行时time.txt文档中,次数也会变化

对象序列化

在运行过程中可能需要将一些数据永久的保存到硬盘上,这时候就要用到Java的对象序列化。
对象序列化值得是将一个Java对象转换成一个io流中字节的序列的过程,其目的是为了讲对象保存到磁盘中,对象序列化机制可以使内存中Java对象转换成于平台无关的二进制流,既可以将这种二进制流持久的保存在磁盘上,又可以通过网络将这种二进制流传输到另一个网络节点,其他程序获取了二进制流后还可以将它恢复为原来的Java对象,这种将io流恢复成Java对象的过程被称为反序列化。
如果想让某个对象支持序列化机制,那么之恶个对象所在的类必须是可序列化的,在Java中可序列化的类必须实现Serializable或Externalizable两个接口之一。大部分时候都使用Serializable接口发方式来实现序列化的。
Serializable接口
系统自动存储必要信息
Java内部支持,易于实现,只需要实现该接口即可,不需要其他代码支持
性能较差
Externalizable接口
由程序员决定所存储的信息
接口中只提供了两个空方法,实现该接口必须为两个空方法提供实现
性能较好

NIO

NIO概述

NIO是为替代传统io而出现的,与标准io相比,NIO提供了一种与io不同的工作方式。NIO采用内存映射文件的方式来处理输入输出,他将文件或文件的一段区域映射到内存中,这样就可以想访问内存一样访问文件了。
在io流中使用的字节流和字符流,而在NIO中使用的是通道和缓冲区,数据总是从通道读入缓冲区,或从缓冲区写入通道。

Buffer(缓冲器)

Java NIO中的Buffer用于写入和NIO中的Channel进行交互,交互时数据会从Channel中读取到Buffer中,或从Buffer写入到Channel中。
从结构上来说Buffer类似于一个数组,他可以保存多个类型相同的数据。从类型上来说,Buffer是一个抽象类,有很多子类。最常用的是ByteBuffer和CharBuffer。
Buffer类的子类中并没有提供构造方法,因此想要创建对象,通常会通过子类中的static xxxBuffer allocate(int capacity)来实现例如

CharBuffer buffer = CharBuffer.allocate(6);

1.capacity(容量):缓冲区的容量表示该Buffer的最大数据容量,即最多可以存储多少数据,缓冲区的容量值不能为负数,也不能够改变。
2.limit(界限):表示Buffer容器中不可被读取的区域的第一个索引,即位于Buffer容器中索引为0到limit之间的区域都可以精选读取操作。limit的容量值不能为负数,也不大于其容量。
3.position(位置):用于指定下一个可以被读写的缓冲区位置索引,新创建的Buffer对象,position的默认值为0,每一次进行读取或写入操作,position的值都会自动向后移一步。

Channel通道

channel对象是接口,它类似于传统的流对象,具体表现如下
channel可以异步的执行io流读写操作
channel的读写操作是双向的,既可以从channel中读取数据,又可以写数据到channel,而流的读写操作往往是单向的
channel可以直接将指定文件的部分或者全部直接映射成Buffer
channel只能与Buffer进行交互,程序不能直接读写channel的数据。
要使用Channel,就需要使用它的实现类。channel对象并不是通过构造方法创建的,而是通过传统的io的getChannel方法来获取对应的Channel,不同的流获取的Channel是不同的。
下面通过一个例子来演示FileChannel的使用

package Demo01;

import java.io.*;
import java.nio.channels.FileChannel;
import java.util.Arrays;

/**
 * @author Dell
 */
public class Test02 {
    public static void main(String[] args) throws Exception {
//        创建RandomAccessFile对象,指定源文件
        RandomAccessFile inFile = new RandomAccessFile(
                "reader,txt", "rw");
//        获取读取源文件FileChannel通道
        FileChannel inChannel = inFile.getChannel();
//        创建RandomAccessFile对象,指定目标文件
        RandomAccessFile outFile = new RandomAccessFile("writer.txt", "rw");
//        获取复制目标文件FileChannel通道
        FileChannel outChannel = outFile.getChannel();
//        使用transferTo()方法进行整体复制
        long transferTo = inChannel.transferTo(0,inChannel.size(),outChannel);
        if (transferTo>0){
            System.out.println("复制成功");
        }
        inFile.close();
        inChannel.close();
        outChannel.close();
        outFile.close();
    }
}

先使用两个RandomAccessFile类的构造方法生成两个对象,然后通过getChannel方法获取读写通道,再通过transferTo方法实现了整个文件的拷贝。

Path接口

File虽然可以访问文件系统,但是File类所提供的方法性能较低,大多数方法在出错时仅返回失败,而不提供异常信息,不仅如此,File类还不能利用特定的文件系统的特性。为了弥补NIO提供了一个Path接口,该接口是一共用在文件系统中定位文件的对象,通常表示一个依赖于系统的文件路径。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值