Java基础-I/O流

目录

一 I/O流

1 I/O流概述

2 I/O流分类

3 字节流

(1)字节流概述

(2)字节流的继承体系

(3)InputStream常用方法

(4)FileInputStream

(5)OutputStream常用方法

(6)FileOutputStream

(7)拷贝图片

(8)字节流缓冲区

(9)字节缓冲流

4 字符流

(1)字符流继承体系

(2)FileReader

(3)FileWriter

(4)拷贝文档

(5)字符流缓冲区

(6)字符缓冲流

5 转换流

二 File类

1File类常用构造方法

2File类的常用方法

三 对象的序列化

1 什么是对象的序列化

2 ObjectOutputStream对象输出流

3 ObjectInputStream对象输入流

四 NIO

1 什么是NIO

2 NIO相关包

3 NIO三大核心


一 I/O流

1 I/O流概述

| 定义: | I/O(Input/Output)流,即输入/输出流,是Java中实现输入/输出的基础,它可以方便地实现数据的输入/输出操作 | | ---------- | ------------------------------------------------------------ |

| Java中的I/O流主要定义在java.io包中,该包下定义了很多类,其中有4个类为流的顶级类,分别为InputStream和OutputStream,Reader和Writer。 | | ------------------------------------------------------------ |

2 I/O流分类

根据操作数据单位:字节流和字符流
根据数据流向:输入流和输出流

注意:

InputStream和OutPutStream是字节流,而Reader和Writer是字符流;
InputStream和Reader是输入流,而OutputStream和Writer是输出流;
4个顶级类都是抽象类,并且是所有流类型的父类

3 字节流

(1)字节流概述

定义在计算机中,无论是文本、图片、音频还是视频,所有文件都是以二进制(字节)形式存在的,I/O流中针对字节的输入/输出提供了一系列的流,统称为字节流。
说明字节流是程序中最常用的流 在JDK中,所有的字节输入流都继承自InputStream,所有的字节输出流都继承自OutputStream。

注意:

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

(2)字节流的继承体系

字节输入流:

字节输出流:

(3)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)FileInputStream

FileInputStream是InputStream的子类,它是操作文件的字节输入流,专门用于读取文件中的数据。
从文件读取数据是重复的操作,因此需要通过循环语句来实现数据的持续读取。

代码:

    //创建字节输入流通道
    public static void main(String[] args) throws IOException {
        FileInputStream in = new FileInputStream("D:\\a.txt");
        int content = 0;//定义一个变量,保存读取内容
        while((content=in.read())!=-1){//判断是否读取完
            System.out.println((char)content);//打印读取内容
        }
        in.close();//释放资源
    }

结果:

注意:

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

(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流相关的系统资源。

(6)FileOutputStream

| 更多操作FileOutputStream是OutputStream的子类,它是操作文件的字节输出流,专门用于向文件中写入数据。 | | ------------------------------------------------------------ |

代码1:

        //创建字节输出流通道
        FileOutputStream out = new FileOutputStream("D:\\b.txt");
        //写数据
        out.write("hello world".getBytes());
        //释放资源
        out.close();

结果:

注意:

| 通过FileOutputStream向一个已经存在的文件中写入数据,该文件中的数据首先会被清空,再写入新的数据。若希望在已存在的文件内容之后追加新内容,则可使用构造函数FileOutputStream(String fileName, boolean append)来创建文件输出流对象,并把append参数的值设置为true。 | | ------------------------------------------------------------ |

代码2:

        //创建字节输出流通道
        FileOutputStream out = new FileOutputStream("D:\\b.txt",true);
        //写数据
        out.write("hello world".getBytes());
        out.write("天气不错呀!".getBytes());
        //释放资源
        out.close();

结果:

(7)拷贝图片

I/O流通常都是成对出现的,即输入流和输出流一起使用。
例如文件的拷贝就需要通过输入流来读取源文件中的数据,并通过输出流将数据写入新文件。

代码:

        FileInputStream in = null;
        FileOutputStream out = null;
        try{
            //和要拷贝的图片建立字节输入流通道
            in = new FileInputStream("C:\\Users\\Administrator\\Desktop\\a.png");
            //和拷贝后的图片建立字节输出流通道
            out = new FileOutputStream("C:\\Users\\Administrator\\Desktop\\a_copy.png");
            //定义变量,保存每次读取内容
            int content = 0;
            //边读边写
            while((content=in.read())!=-1){
                out.write(content);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //释放资源
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

结果:

注意:

| 注意: | I/O流在进行数据读写操作时会出现异常,为了保证I/O流的close()方法一定执行来释放占用的系统资源,通常会将关闭流的操作写在inally代码块中。 | | ---------- | ------------------------------------------------------------ |

(8)字节流缓冲区

| 假设从北京运送快递到上海,如果有一万件快递,一件一件的运送就必须运输一万次,这样的效率显然非常低。为了减少运输次数,可以先把一批快递装在一个车厢中,这样就可以成批的运送快递,这时的车厢就相当于一个临时缓冲区。 | | ------------------------------------------------------------ |

| 同理:在文件拷贝过程中,通过以字节形式逐个拷贝,效率也非常低。为此,可以定义一个字节数组缓冲区,在拷贝文件时,就可以一次性读取多个字节的数据。 | | ------------------------------------------------------------ |

| 程序中的缓冲区就是一块内存,该内存主要用于存放暂时输入/输出的数据,由于使用缓冲区减少了对文件的操作次数,所以可以提高读写数据的效率 | | ------------------------------------------------------------ |

代码1:

        //创建字节输入流通道
        FileInputStream in = new FileInputStream("D:\\a.txt");
        byte[] buf = new byte[1024];//定义一个变量,保存每次读取内容(缓冲数组)
        int length = 0;//定义一个变量,保存每次读取的长度
        while((length=in.read(buf))!=-1){//判断是否读取完
            String content = new String(buf,0,length);
            System.out.println(content);//打印读取内容
        }
        in.close();//释放资源

结果:

代码2:

        FileInputStream in = null;
        FileOutputStream out = null;
        try{
            //和要拷贝的图片建立字节输入流通道
            in = new FileInputStream("C:\\Users\\Administrator\\Desktop\\a.png");
            //和拷贝后的图片建立字节输出流通道
            out = new FileOutputStream("C:\\Users\\Administrator\\Desktop\\a_copy.png");
            //定义缓存数组,保存读取内容
            byte[] buf = new byte[1024];
            //定义变量,保存每次读取的长度
            int length = 0;
            //边读边写
            while((length=in.read(buf))!=-1){
                out.write(buf,0,length);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //释放资源
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

(9)字节缓冲流

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

缓冲流示意图:

代码:

        FileInputStream in = null;
        FileOutputStream out = null;
        BufferedInputStream bufIn = null;
        BufferedOutputStream bufOut = null;
        try{
            //和要拷贝的图片建立字节输入流通道
            in = new FileInputStream("C:\\Users\\Administrator\\Desktop\\a.png");
            //创建缓冲输入流
            bufIn = new BufferedInputStream(in);
            //和拷贝后的图片建立字节输出流通道
            out = new FileOutputStream("C:\\Users\\Administrator\\Desktop\\a_copy.png");
            //创建缓存输出流
            bufOut = new BufferedOutputStream(out);
            //定义缓存数组,保存读取内容
            byte[] buf = new byte[1024];
            //定义变量,保存每次读取的长度
            int length = 0;
            //边读边写
            while((length=bufIn.read(buf))!=-1){
                bufOut.write(buf,0,length);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //释放资源
            try {
                bufOut.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                bufIn.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

4 字符流

| 说明: | 除了字节流,JDK还提供了用于实现字符操作的字符流,同字节流一样,字符流也有两个抽象的顶级父类,分别是Reader和Writer。 | | ---------- | ------------------------------------------------------------ |

(1)字符流继承体系

(2)FileReader

| 想从文件中直接读取字符便可以使用字符输入流FileReader,通过此流可以从文件中读取一个或一组字符。 | | ------------------------------------------------------------ |

代码:

    //创建输入字符流对象
    FileReader fileReader = new FileReader("d:\\a.txt");
    int len = 0;//定义变量,保存读取数据
    while ((len = fileReader.read()) != -1) {//判断数据是否读完
          System.out.print((char)len);//打印数据
    }
    fileReader.close();//是否资源

结果:

(3)FileWriter

| 要向文件中写入字符就需要使用FileWriter类,该类是Writer的一个子类 | | ------------------------------------------------------------ |

代码:

    //创建字符输出流对象
    FileWriter fileWriter = new FileWriter("d:\\b.txt");
    fileWriter.write("你好!");//写数据
    fileWriter.close();//释放资源

结果:

注意:

| 使用字符流向文件追加写入数据,需要调用重载的构造方法 | | ---------------------------------------------------- |

代码:

    //创建字符输出流对象
    FileWriter fileWriter = new FileWriter("d:\\b.txt",true);
    fileWriter.write("天气不错呀");//写数据
    fileWriter.close();//释放资源

结果:

(4)拷贝文档

代码:

        //建立输入通道
        FileReader reader = new FileReader("d:\\admin.txt");
        //建立输出通道
        FileWriter writer = new FileWriter("d:\\admin_copy.txt");
        int content = 0;//定义变量保存读取内容
        while( (content= reader.read())!=-1){//读取数据
            writer.write(content);//写数据
        }
        //释放资源
        writer.close();
        reader.close();

结果:

(5)字符流缓冲区

使用字符流逐个字符的读写文件也需要频繁的操作文件,效率仍非常低。
为此,同字节流操作文件一样,也可以使用提供的字符流缓冲区(类似于字节流缓冲区)和字符缓冲流(类似于字节缓冲流)进行读写操作,来提高执行效率

| 字符流缓冲区需要定义一个字符数组作为字符缓冲区,通过操作字符缓冲区来提高文件读写效率。 | | ------------------------------------------------------------ |

代码:

        //创建字符输入流对象
        FileReader fileReader = new FileReader("d:\\admin.txt");
        //定义字符缓冲数组
        char[] buf = new char[1024];
        int len = 0;//定义变量,保存读取长度
        while ((len = fileReader.read(buf)) != -1) {//判断数据是否读完
            System.out.print(new String(buf,0,len));//打印数据
        }
        fileReader.close();//是否资源

结果:

代码2:

        //建立输入通道
        FileReader reader = new FileReader("d:\\admin.txt");
        //建立输出通道
        FileWriter writer = new FileWriter("d:\\admin_copy.txt");
        //定义缓存数组
        char[] buf = new char[1024];
        int len = 0;//定义变量保存读取长度
        while( (len= reader.read(buf))!=-1){//读取数据
            writer.write(buf,0,len);//写数据
        }
        //释放资源
        writer.close();
        reader.close();

(6)字符缓冲流

BufferedReaderString readLine() 读一行文字(不会读取换行)。
BufferedWritervoid newLine() 写一行行分隔符。

注意:

它们的构造方法中分别接收Reader和Writer类型的参数作为对象,在读写数据时提供缓冲功能。
BufferedReader(Reader in)
BufferedWriter(Writer out)

代码1:

        //创建字符输入流对象
        FileReader fileReader = new FileReader("d:\\admin.txt");
        //创建字符输入缓冲流
        BufferedReader bufferedReader = new BufferedReader(fileReader);
        String content = "";//定义变量,保存读取内容
        while ((content=bufferedReader.readLine())!=null) {//判断数据是否读完
            System.out.print(content+"\r\n");//打印数据
        }
        bufferedReader.close();//释放资源

代码2:

        //建立输入通道
        FileReader reader = new FileReader("d:\\admin.txt");
        //建立字符输入缓冲流
        BufferedReader bufferedReader = new BufferedReader(reader);
        //建立输出通道
        FileWriter writer = new FileWriter("d:\\admin_copy.txt");
        //建立字符输出缓冲流
        BufferedWriter bufferedWriter = new BufferedWriter(writer);
        String line = null;//定义变量保存读取内容
        while( (line = bufferedReader.readLine())!=null){//读取数据
            writer.write(line+"\r\n");//写数据
        }
        //释放资源
        bufferedWriter.close();
        bufferedReader.close();

5 转换流

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

转换流操作文件示意图

代码:

        //创建字节输入流
        FileInputStream in = new FileInputStream("d:\\admin.txt");
        //将字节流转换成字符流
        InputStreamReader reader = new InputStreamReader(in);
        //创建缓冲流
        BufferedReader bufferedReader = new BufferedReader(reader);
        //使用缓冲流读数据
        String line = null;
        while((line=bufferedReader.readLine())!=null){
            System.out.print(line+"\r\n");//打印读取内容
        }
        //释放资源
        bufferedReader.close();

结果:

二 File类

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

1File类常用构造方法

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

例如:

       File file1 = new File("C:\\Users\\Administrator\\Desktop\\a.txt");
       File file2 = new File("C:\\Users\\Administrator\\Desktop","a.txt");
       File file3 = new File(new File("C:\\Users\\Administrator\\Desktop"),"a.txt");

2File类的常用方法

方法声明功能描述
boolean exists()判断File对象对应的文件或目录是否存在,若存在则返回ture,否则返回false
boolean delete()删除File对象对应的文件或目录,若成功删除则返回true,否则返回false
boolean createNewFile()当File对象对应的文件不存在时,该方法将新建一个此File对象所指定的新文件,若创建成功则返回true,否则返回false
String getName()返回File对象表示的文件或文件夹的名称
String getPath()返回File对象对应的路径
String getAbsolutePath()返回File对象对应的绝对路径(在Unix/Linux等系统上,如果路径是以正斜线/开始,则这个路径是绝对路径;在Windows等系统上,如果路径是从盘符开始,则这个路径是绝对路径)
String getParent()返回File对象对应目录的父目录(即返回的目录不包含最后一级子目录)
boolean canRead()判断File对象对应的文件或目录是否可读,若可读则返回true,反之返回false
boolean canWrite()判断File对象对应的文件或目录是否可写,若可写则返回true,反之返回false
boolean isFile()判断File对象对应的是否是文件(不是目录),若是文件则返回true,反之返回false
方法声明功能描述
boolean isFile()判断File对象对应的是否是文件(不是目录),若是文件则返回true,反之返回false
boolean isDirectory()判断File对象对应的是否是目录(不是文件),若是目录则返回true,反之返回false
boolean isAbsolute()判断File对象对应的文件或目录是否是绝对路径
long lastModified()返回1970年1月1日0时0分0秒到文件最后修改时间的毫秒值
long length()返回文件内容的长度
String[] list()列出指定目录的全部内容,只是列出名称
String[] list(FilenameFilter filter)接收一个FilenameFilter参数,通过该参数可以只列出符合条件的文件
File[] listFiles()返回一个包含了File对象所有子文件和子目录的File数组

例如1:

        //判断
        File file1 = new File("C:\\Users\\Administrator\\Desktop\\a.txt");
        System.out.println("file存在吗?"+file1.exists());
        System.out.println("file是文件夹吗?"+file1.isDirectory());
        System.out.println("file是文件吗?"+file1.isFile());
        //获取
        System.out.println("file的名字:"+file1.getName());
        System.out.println("file的路径是:"+file1.getPath());
        System.out.println("file的父目录是:"+file1.getParent());
        System.out.println("file上次修改时间是:"+file1.lastModified());

结果:

例如2:

        //创建文件
        File file2 = new File("d:\\abc.txt");
        if(!file2.exists()){
            file2.createNewFile();
        }
        //删除文件(如果是文件夹必须为空才能删除)
        file2.delete();

例如3:

        //获取目录下所有内容
        File file = new File("C:\\Users\\Administrator\\Desktop\\a");
        if(file.isDirectory()){
            String[] list = file.list();//获取的是所有内容的名字
            System.out.println(Arrays.toString(list));
            File[] files = file.listFiles();//获取的是所有内容的File对象
            System.out.println(Arrays.toString(files));
        }

结果:

例如4:

软件包  java.io 
Interface FilenameFilter
boolean accept•(File dir,
               String name)测试指定文件是否应包含在文件列表中。 
参数 
dir - 找到该文件的目录。 
name - 文件的名称。 
结果 
true当且仅当名称应包含在文件列表中时; 否则为false 。
//创建过滤器
public class MyFileNameFilter implements FilenameFilter {
    @Override
    public boolean accept(File dir, String name) {
        if(name.endsWith("java"))return true;
        return false;
    }
}  
​
        File file = new File("C:\\Users\\Administrator\\Desktop\\a");
        if(file.isDirectory()){
            MyFileNameFilter filter = new MyFileNameFilter();
            String[] list = file.list(filter);//获取过滤后的内容
            File[] files = file.listFiles(filter);//获取过滤后的内容
            System.out.println(Arrays.toString(list));
            System.out.println(Arrays.toString(files));
        }

结果:

三 对象的序列化

问题:
    程序在运行过程中,可能需要将一些数据永久的保存到磁盘上,而数据在Java中都是保存在对象当中的。那么我们要怎样将对象中的数据保存到磁盘上呢?
答:
    这时就需要使用Java中的对象序列化。

1 什么是对象的序列化

定义:对象的序列化(Serializable)是指将一个Java对象转换成一个I/O流中字节序列的过程
目的:为了将对象保存到磁盘中,或允许在网络中直接传输对象。
说明:
    对象序列化可以使内存中的Java对象转换成与平台无关的二进制流;
    既可以将这种二进制流持久地保存在磁盘上,又可以通过网络将这种二进制流传输到另一个网络节点;
    其他程序在获得了这种二进制流后,还可以将它恢复成原来的Java对象;
    这种将I/O流中的字节序列恢复为Java对象的过程被称之为反序列化(Deserialize)。
注意:
    想让某个对象支持序列化,那么这个对象所在的类必须是可序列化的。
    在Java中,可序列化的类必须实现Serializable接口

2 ObjectOutputStream对象输出流

    public class Person implements Serializable {
          // 为该类指定一个serialVersionUID变量值
          private static final long serialVersionUID = 1L;
          //声明变量
          private int id;
          private String name;
          private int age;
          // 此处省略各属性的getter和setter方法
          ...
    }
        //创建字节输出流对象
        OutputStream out = new FileOutputStream("C:\\Users\\Administrator\\Desktop\\person.txt");
        //创建对象输出流
        ObjectOutputStream objOut = new ObjectOutputStream(out);
        //输出对象
        Person person = new Person();
        person.setId(1);
        person.setName("小明");
        person.setAge(11);
        objOut.writeObject(person);//对象的序列化
        //释放资源
        objOut.close();

3 ObjectInputStream对象输入流

        //创建字节输入流对象
        FileInputStream in = new FileInputStream("C:\\Users\\Administrator\\Desktop\\person.txt");
        //创建对象输入流
        ObjectInputStream objIn = new ObjectInputStream(in);
        //读对象
        Person person = (Person) objIn.readObject();//对象的反序列化
        //释放资源
        objIn.close();
        System.out.println(person);

结果:

注意:

对象的反序列化不会调用构造函数
serialVersionUID适用于Java的序列化机制。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就可以进行反序列化,否则就会出现异常。因此,为了在反序列化时确保序列化版本的兼容性,最好在每一个要序列化的类中加入private static final long serialVersionUID的变量值,具体数值可自定义(默认是1L,系统还可以根据类名、接口名、成员方法及属性等生成的一个64位的哈希字段)。这样,某个对象被序列化之后,即使它所对应的类被修改了,该对象也依然可以被正确的反序列化。

四 NIO

1 什么是NIO

定义:
	从JDK 1.4开始,Java提供了一系列改进的用于处理输入/输出的新功能,这些新功能被称之为NIO(New I/O)。
说明:
	NIO采用内存映射文件的方式来处理输入/输出,它将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样来访问文件了。
	在NIO中,使用的是Channel(通道)和Buffer(缓冲器)。
	数据总是从通道读入缓冲器,或从缓冲器写入通道。

2 NIO相关包

NIO相关包:
java.nio:主要包含各种与Buffer相关的类。
java.nio.channels:主要包含与Channel和Selector(多线程相关选择器)相关的类。
java.nio.channels.spi:主要包含与Channel相关的服务提供者编程接口。
java.nio.charset:主要包含与字符集相关的类。
java.nio.charset.spi:主要包含与字符集相关的服务提供者编程接口。

3 NIO三大核心

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值