美文网首页
狂怼面试官——JAVA IO知识点梳理

狂怼面试官——JAVA IO知识点梳理

作者: Java柱柱 | 来源:发表于2020-11-23 21:27 被阅读0次

    Java对数据的操作是通过流的方式,io是java中实现输入输出的基础,它可以很方便的完成数据的输入输出操作,Java把不同的输入输出抽象为流,通过流的方式允许Java程序使用相同的方式来访问不同的输入、输出。

    补充( File )

    构造函数

    //构造函数File(String pathname)
    File f1 =new File("c:\\abc\\1.txt");
    //File(String parent,String child)
    File f2 =new File("c:\\abc","2.txt");
    //File(File parent,String child)
    File f3 =new File("c:"+File.separator+"abc");//separator 跨平台分隔符
    File f4 =new File(f3,"3.txt");
    System.out.println(f1);//c:\abc\1.txt
    

    创建与删除方法

    //如果文件存在返回false,否则返回true并且创建文件 
    boolean createNewFile();
    //创建一个File对象所对应的目录,成功返回true,否则false。且File对象必须为路径而不是文件。只会创建最后一级目录,如果上级目录不存在就抛异常。
    boolean mkdir();
    //创建一个File对象所对应的目录,成功返回true,否则false。且File对象必须为路径而不是文件。创建多级目录,创建路径中所有不存在的目录
    boolean mkdirs()    ;
    //如果文件存在返回true并且删除文件,否则返回false
    boolean delete();
    //在虚拟机终止时,删除File对象所表示的文件或目录。
    void deleteOnExit();
    

    判断方法

    boolean canExecute()    ;//判断文件是否可执行
    boolean canRead();//判断文件是否可读
    boolean canWrite();//判断文件是否可写
    boolean exists();//判断文件是否存在
    boolean isDirectory();//判断是否是目录
    boolean isFile();//判断是否是文件
    boolean isHidden();//判断是否是隐藏文件或隐藏目录
    boolean isAbsolute();//判断是否是绝对路径 文件不存在也能判断
    

    获取方法

    String getName();//返回文件或者是目录的名称
    String getPath();//返回路径
    String getAbsolutePath();//返回绝对路径
    String getParent();//返回父目录,如果没有父目录则返回null
    long lastModified();//返回最后一次修改的时间
    long length();//返回文件的长度
    File[] listRoots();// 列出所有的根目录(Window中就是所有系统的盘符)
    String[] list() ;//返回一个字符串数组,给定路径下的文件或目录名称字符串
    String[] list(FilenameFilter filter);//返回满足过滤器要求的一个字符串数组
    File[]  listFiles();//返回一个文件对象数组,给定路径下文件或目录
    File[] listFiles(FilenameFilter filter);//返回满足过滤器要求的一个文件对象数组
    

    以上方法中包含了一个重要的接口 FileNameFilter ,该接口是个文件过滤器,包含了一个 accept(File dir,String name) 方法,该方法依次对指定File的所有子目录或者文件进行迭代,按照指定条件,进行过滤,过滤出满足条件的所有文件。

    // 文件过滤
            File[] files = file.listFiles(new FilenameFilter() {
                @Override
                public boolean accept(File file, String filename) {
                    return filename.endsWith(".mp3");
                }
            });
    

    关于IO

    流有输入和输出,输入时是流从数据源流向程序。输出时是流从程序传向数据源,而数据源可以是内存,文件,网络或程序等。

    输入流:只能从中读取数据,而不能向其写入数据。(输入程序)

    输出流:只能向其写入数据,而不能从中读取数据。(程序输出)

    同步io与异步io

    同步io:

    读写io时代码等待数据返回后才继续执行后续代码 代码编写简单,cpu执行效率低

    异步io:

    读写io时仅发出请求,然后立刻执行后续代码 代码编写复杂,cpu执行效率高

    JDK提供的java.io是同步io,java.nio是异步io

    狂怼面试官——JAVA IO知识点梳理

    字节流和字符流

    字节流和字符流和用法几乎完全一样,区别在于字节流和字符流所操作的数据单元不同。

    字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。字节流和字符流的区别:

    1. 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
    2. 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。

    注:只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。

    节点流(低级流)和处理流(高级流)

    1. 节点流:直接从数据源获取信息的流
    //节点流,直接传入的参数是IO设备
    FileInputStream fis = new FileInputStream("test.txt");
    
    1. 处理流:从别的流中获取信息,进行封装或连接的流
    //处理流,直接传入的参数是流对象
    BufferedInputStream bis = new BufferedInputStream(fis);
    

    处理流的好处

    当使用处理流进行输入/输出时,程序并不会直接连接到实际的数据源,没有和实际的输入/输出节点连接。使用处理流的一个明显好处是,只要使用相同的处理流,程序就可以采用完全相同的输入/输出代码来访问不同的数据源,随着处理流所包装节点流的变化,程序实际所访问的数据源也相应地发生变化。

    实际上,Java使用处理流来包装节点流是一种典型的 装饰器设计模式 ,通过使用处理流来包装不同的节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入/输出功能。

    四大基类

    狂怼面试官——JAVA IO知识点梳理

    InputStream , Reader , OutputStream , Writer ,这四大抽象基类,本身并不能创建实例来执行输入/输出,但它们将成为所有输入/输出流的模版,所以它们的方法是所有输入/输出流都可以使用的方法。类似于集合中的Collection接口。

    注意:在执行完流操作后,要调用 close() 方法来关系输入流, 因为程序里打开的IO资源不属于内存资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件IO资源。

    1. InputStream

    InputStream 是所有的输入字节流的父类,它是一个抽象类,主要包含三个方法:

    //读取一个字节并以整数的形式返回(0~255),如果返回-1已到输入流的末尾。 
    int read() ; 
    //读取一系列字节并存储到一个数组buffer,返回实际读取的字节数,如果读取前已到输入流的末尾返回-1。 
    int read(byte[] buffer) ; 
    //读取length个字节并存储到一个字节数组buffer,从off位置开始存,最多len, 返回实际读取的字节数,如果读取前以到输入流的末尾返回-1。 
    int read(byte[] buffer, int off, int len) ; 
    

    2. Reader

    Reader 是所有的输入字符流的父类,它是一个抽象类,主要包含三个方法:

    //读取一个字符并以整数的形式返回(0~255),如果返回-1已到输入流的末尾。 
    int read() ; 
    //读取一系列字符并存储到一个数组buffer,返回实际读取的字符数,如果读取前已到输入流的末尾返回-1。 
    int read(char[] cbuf) ; 
    //读取length个字符,并存储到一个数组buffer,从off位置开始存,最多读取len,返回实际读取的字符数,如果读取前以到输入流的末尾返回-1。 
    int read(char[] cbuf, int off, int len) 
    

    InputStream和Reader还支持如下方法来移动流中的指针位置:

    //在此输入流中标记当前的位置
    //readlimit - 在标记位置失效前可以读取字节的最大限制。
    void mark(int readlimit)
    // 测试此输入流是否支持 mark 方法
    boolean markSupported()
    // 跳过和丢弃此输入流中数据的 n 个字节/字符
    long skip(long n)
    //将此流重新定位到最后一次对此输入流调用 mark 方法时的位置
    void reset()
    

    3. OutputStream

    OutputStream 是所有的输出字节流的父类,它是一个抽象类,主要包含如下四个方法:

    //向输出流中写入一个字节数据,该字节数据为参数b的低8位。 
    void write(int b) ; 
    //将一个字节类型的数组中的数据写入输出流。 
    void write(byte[] b); 
    //将一个字节类型的数组中的从指定位置(off)开始的,len个字节写入到输出流。 
    void write(byte[] b, int off, int len); 
    //将输出流中缓冲的数据全部写出到目的地。 
    void flush();
    

    4. Writer

    Writer 是所有的输出字符流的父类,它是一个抽象类,主要包含如下六个方法:

    //向输出流中写入一个字符数据,该字节数据为参数b的低16位。 
    void write(int c); 
    //将一个字符类型的数组中的数据写入输出流, 
    void write(char[] cbuf) 
    //将一个字符类型的数组中的从指定位置(offset)开始的,length个字符写入到输出流。 
    void write(char[] cbuf, int offset, int length); 
    //将一个字符串中的字符写入到输出流。 
    void write(String string); 
    //将一个字符串从offset开始的length个字符写入到输出流。 
    void write(String string, int offset, int length); 
    //将输出流中缓冲的数据全部写出到目的地。 
    void flush() 
    

    可以看出,Writer比OutputStream多出两个方法,主要是支持写入字符和字符串类型的数据。

    使用Java的IO流执行输出时,不要忘记关闭输出流,关闭输出流除了可以保证流的物理资源被回收之外,还能将输出流缓冲区的数据flush到物理节点里(因为在执行close()方法之前,自动执行输出流的flush()方法)

    四大基类细分流

    文件字符输入流(FileReader)

    作用:把硬盘文件中的数据以字符的方式读取到内存中

    构造方法:

    FileReader(File file);//在给定从中读取数据的 File 的情况下创建一个新 FileReader。
    FileReader(FileDescriptor fd);//在给定从中读取数据的 FileDescriptor 的情况下创建一个新 FileReader。
    FileReader(String fileName);//在给定从中读取数据的文件名的情况下创建一个新 FileReader。 
    

    文件字符输出流(FileWriter)

    作用:把内存中的数据写入到文件中

    构造方法:

    FileWriter(String fileName);//根据给定的文件名构造一个 FileWriter 对象。
    FileWriter(File file);//根据给定的 File 对象构造一个 FileWriter 对象。
    

    文件字节输入流 ( FileInputStream )

    使用输入流的4个基本步骤

    1. 设定输入流的源。
    2. 创建指向源的输入流。
    3. 让输入流读取源中的数据。
    4. 关闭输入流。

    构造方法:

    // 通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的File对象file指定
     FileInputStream(File file);
     // 通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的路径name指定
     FileInputStream(String name);
    

    使用输入流读取文件

    int read(byte b[])// 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b中,返回实际读取的字节数目。到达尾部返回-1.
    int read(byte[] b, int off, int len)//将输入流中最多 len 个数据字节读入 byte 数组。返回实际读取的字节数目。到达尾部返回-1。off指定从某个位置开始存取。
    long skip()//从源中读取单个字节的数据,该方法返回字节值(0-255),未读取出字节返回-1。
    void close()//关闭流
    

    使用实例

    public static void main(String[] args) throws IOException {
            String path = FileInputStreamDemo.class.getResource("/").getPath() + "test.txt";
            System.out.println("获取文件路径:" + path);
            File file = new File(path);
            //以File为构造函数对象
            FileInputStream fileInputStream = new FileInputStream(file);
            //以字符串path为构造函数对象
            FileInputStream fileInputStream2 = new FileInputStream(path);
            String s = new String();
            byte[] b = new byte[1024];
            int length=0;
            if((length = fileInputStream.read(b))!=-1) {
                s+=new String(b,"GBK");
                System.out.println(s);
            }
            fileInputStream.close();
            fileInputStream2.close();
    

    文件字节输出流( FileOutputStream )

    1. 设定输出流的目的地。
    2. 创建指定目的地的输出流。
    3. 让输出流把数据写入到目的地。
    4. 关闭输出流。

    构造方法

    如果对文件读取需求比较简单,可以使用==文件字节输出流FileOutputStream类。==字节文件输出流是用于将数据写入到File,从程序中写入到其他位置。

    FileOutputStream(File file)// 创建一个向指定File对象表示的文件中写入数据的文件输出流
    FileOutputStream(File file, boolean append)// 创建一个向指定File对象表示的文件中写入数据的文件输出流,append为true则是在文件后面追加写入
    FileOutputStream(String name)// 创建一个向具有指定名称的文件中写入数据的输出文件流
    FileOutputStream(String name, boolean append)//创建一个向具有指定name的文件中写入数据的输出文件流,append为true则是在文件后面追加写入
    

    使用输出流写字节

    字节输出流的write方法以字节为单位向目的地写入单个数据。

    void write(int n)输出流调用该方法向目的地写入单个字节。
    void write(byte b[])输出流调用该方法向目的地写入一个字节数组。
    void write(byte b[],int off,int len)给定字节数组中起始于偏移量off处取len个字节写到目的地。
    void close()关闭输出流
    

    使用实例

    public static void main(String[] args) throws IOException {
            String path = FileInputStreamDemo.class.getResource("/").getPath() + "test.txt";
            System.out.println("获取文件路径:" + path);
            File file = new File(path);
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            FileOutputStream fileOutputStream2 = new FileOutputStream(path);
            String s = " good!";
            byte[] b;
            b=s.getBytes();
            fileOutputStream.write(b);
            fileOutputStream.close();
            fileOutputStream2.close();
        }
    

    缓冲流

    缓冲流概述

    缓冲流是对文件流处理的一种流,增强了读写文件的能力。够更高效的读写信息。因为缓冲流先将数据缓存起来,然后一起写入或读取出来。

    flush() 和 close() flush 是从缓冲区把文件写出, close 是将文件从缓冲区内写出并且关闭相应的流。

    字节缓冲流:BufferedInputStream,BufferedOutputStream

    字符缓冲流:BufferedReader,BufferedWriter

    1. BufferedOutputStream

    继承父类(OutputStream)共性成员方法

    构造方法:

    BufferedOutputStream(OutputStream out);//创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
    BufferedOutputStream(OutputStream out, int size);//创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流。
    

    注意:相比于字节输出流,使用缓冲字节输出流的时候需要flush将字节压入文件

    2.BufferedInputStream

    继承父类(InputStream)共性成员方法

    构造方法:

    BufferedInputStream(InputStream in);//创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
    BufferedInputStream(InputStream in, int size);//创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
    

    3.BufferedWriter

    继承父类(Writer)共性成员方法

    构造方法:

    BufferedWriter(Writer out);//创建一个使用默认大小输出缓冲区的缓冲字符输出流。
    BufferedWriter(Writer out, int sz);//创建一个使用给定大小输出缓冲区的新缓冲字符输出流。 
    

    特有成员方法: void newLine();//写入一个行分隔符。

    4.BufferedReader

    继承父类(Reader)共性成员方法

    构造方法:

    BufferedReader(Reader in);//创建一个使用默认大小输入缓冲区的缓冲字符输入流。
    BufferedReader(Reader in, int sz);//创建一个使用指定大小输入缓冲区的缓冲字符输入流。
    

    特有成员方法: String readLine();//读取一个文本行。(到达末尾返回null)

    随机流

    概述

    RandomAccessFile类创建的流称作随机流,RandomAccessFile类既不是InputStream类的子类,也不是OutputStream类的子类。

    当准备对一个文件进行读写操作时,创建一个指向该文件的随机流即可,这样既可以从这个流中读取文件的数据,也可以通过这个流写入数据到文件。

    构造方法

    RandomAccessFile(String name, String mode): 参数name用来确定一个文件名,给出创建的流的源,也是流的目的地。参数mode取r(只读)或rw(可读写),决定创建的流对文件的访问权力。

    数组流

    流的源和目的地除了可以是文件,还可以是计算机内存。不需要手动关闭流。只有字节流,没有字符流

    字节数组输入流(ByteArrayInputStream)

    byte[] src = "IO is easy".getBytes();
    InputStream is = new ByteArrayInputStream(src);
    byte[] flush = new byte[1024];
    int len =-1;
    while(len=is.read(flush)!=-1){
     String str = new String(flush,o,len);
     System.out.println(str);
    }
    

    字节数组输出流(ByteArrayOutputStream)

    byte[] dest = null;//其实可以不用,为了风格统一
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    String str = "IO is easy";
    byte[] datas=str.getBytes();//字符串--->字节数组(编码)
    baos.write(datas,0,datas.length);
    baos.flush();
    dest = baos.toByteArray();//获取数据
    System.out.println(dest.length + new String(dest,0,dest.length));
    

    数据流

    概述

    数据输入流DataInputStream允许应用程序以与机器无关方式从底层输入流中读取基本Java数据类型。应用程序可以使用数据输出流写入稍后由数据输入流读取的数据。

    例子

    import java.io.*;
    public class text1 {
    public static void main(String[] args) throws IOException {
     DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream("aa.txt")));
     DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("aa.txt")));
     dos.writeUTF("行大旺");
     dos.writeInt(18);
     dos.writeBoolean(false);
     dos.writeChar('a');
     dos.flush();
     String msg = dis.readUTF();
     int age = dis.readInt();
     boolean flag = dis.readBoolean();
     char ch = dis.readChar();
     System.out.println(msg);
     System.out.println(age);
     System.out.println(flag);
     System.out.println(ch);
     dos.close();
     dis.close();
    }
    }
    

    对象流(序列化与反序列化)

    对象流可以将一个对象写出,或者读取一个对象到程序中,也就是执行了序列化和反序列化的操作

    序列化将把一个Java对象变成二进制内容(byte[])存放到某种类型的永久存储器上称为保持。如果一个对象可以被存放到磁盘或磁带上,或者可以发送到另外一台机器并存放到存储器或磁盘上,那么这个对象就被称为可保持的。

    反序列化是指把一个二进制内容(byte[])变成Java对象

    使用ObjectOutputStream和ObjectInputStream实现序列化和反序列化 readObject()可能抛出的异常:

    1. ClassNotFoundException:没有找到对应的Class
    2. InvalidClassException:Class不匹配
    3. 反序列化由JVM直接构造出Java对象,不调用构造方法
    4. 可设置serialVersionUID作为版本号(非必需):

    在对象里手动添加序列号: private static final long serialVersionUID=数字 ;

    注意:

    1. 序列化与反序列化的对象需要实现 Serializable 接口( Serializable 是标记接口)

    2. 静态对象无法被序列化。

    3. 被transient修饰的成员变量不能被序列化

    4. ObjectOutputStream:对象的序列化流(方法:writeObject(p);写入对象)

    • 作用:

    把对象以流的方式写入到文件中保存,叫写对象也叫序列化

    • 构造方法:
    ObjectOutputStream(OutputStream out);//创建写入指定 OutputStream 的 ObjectOutputStream。
    
    • 特有成员方法
    void writeObject(Object obj);//将指定的对象写入 ObjectOutputStream。 
    
    1. ObejctInputStream:对象的返序列化流(方法:readObject():读对象)
    • 作用:

    把文件中保存的对象,以流的方式读取出来,叫做读对象,也叫对象的返序列化

    • 构造方法:
    ObjectInputStream(InputStream in);//创建从指定 InputStream 读取的 ObjectInputStream。
    
    • 特有成员方法
    Object readObject();//从 ObjectInputStream 读取对象。 
    

    序列化实例

    // 将Account对象保存到文件中
    FileOutputStream fos = new FileOutputStream(file);
    ObjectOutputStream oos = new ObjectOutputStream(fos);
    oos.writeObject(account);
    oos.flush();
    
    // 读取Account的内容
    FileInputStream fis = new FileInputStream(file);
    ObjectInputStream ois = new ObjectInputStream(fis);
    Account account2 = (Account)ois.readObject();
    

    由于在保存Account对象后修改了Account的结构,会导致serialVersionUID的值发生变化,在读文件(反序列化)的时候就会出错。所以为了更好的兼容性,在序列化的时候,最好将serialVersionUID的值设置为固定的。

    public class Account implements Serializable {
    
        private static final long serialVersionUID = 1L;
    
        private int age;
        private long birthday;
        private String name;
    }
    

    来源:https://www.tuicool.com/articles/vmUJNnv

    相关文章

      网友评论

          本文标题:狂怼面试官——JAVA IO知识点梳理

          本文链接:https://www.haomeiwen.com/subject/skxbiktx.html