Java 基础 – IO知识体系

往期推荐

IO流是什么

  • Java的IO是实现输入和输出的基础,可以方便的实现数据的输入和输出操作。
  • 在Java中把不同的输入/输出源(键盘,文件,网络连接等)抽象表述为“流”(stream)
  • 通过流的形式允许Java程序使用相同的方式来访问不同的输入/输出源。stram是从起源(source)到接收的(sink)的有序数据。

IO流的分类

按照流的流向划分

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

??注意:输入流和输出流是相对于程序而言的。

按照处理数据单位划分

  • 字节流(InputStream、OutputStream):数据流中最小的数据单元是字节。
  • 字符流(Reader、Writer):数据流中最小的数据单元是字符。

按照流的角色划分

  • 节点流:可以从或向一个特定的地方(节点)读写数据,直接连接数据源
  • 处理流 不直接连接数据源,是对一个已存在的流的连接和封装,是一种典型的装饰器设计模式,使用处理流主要是为了更方便的执行输入输出工作。

IO流的体系分类

按操作对象划分

  1. 文件操作:FileInputStream、FileOutputStream、FileReader、FileWriter

  2. 管道操作:PipedInputStream

  3. 数组操作:

    • 字符数组:CharArrayReader、CharArrayWriter
    • 字节数组:ByteArrayInputStream、ByteArrayOutputStream
  4. 缓冲操作:BufferedWriter、BufferedInputStream

  5. 基本数据类型操作:DataOutputStream、DataInputStream

  6. 对象序列化操作:ObjectOutputStream、ObjectInputStream

  7. 转化操作:InputStreamReader、OutputStreamWriter

  8. 打印控制:PrintStream、PrintWriter

2.png

按操作方式划分

0004.png

字节输入/输出流

 InputStream类 和 OutputStream类

InputStream类:是一个抽象类 ,是所有字节输入流类的父类。

OutputStream类:是一个抽象类,是所有字节输出流的父类。

InputStream中定义的一些子类共性的成员方法

  • public abstract int read() 从输入流中读取一个数据,返回的是数据的ASCII值。

  • public int read(byte[] b) 从输入流中读取一定数量的字节,默认从第0个位置读取b.length长度的数据,参数byte[]数组表示起缓冲作用,存储每次读取的多个字节。

  • int read(byte b[], int off, int len)从第 off 位置读取 len 长度字节的数据放到byte数组中。

  • int available() 返回可读的字节数量。

  • long skip(long n) 跳过指定个数的字节不读取。

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

OutputStream中定义的一些子类共性的成员方法

  • void close() 关闭此输出流并释放与此流有关的所有系统资源。

  • void flush() 刷新此输出流,将缓冲区中的数据写出。

  • abstract void write(int b) 将指定的字节写入此输出流。

  • void write(byte[] b) 将 b.length 个字节从指定的 byte 数组写出。

  • void write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。

 FileInputStream类 和 FileOutPutStream类

FileInputStream:是文件字节输入流,表示从本地文件中读取数据到内存中。

FileOutPutStream:是文件字节输出流,表示将内存中的数据写入到本地文件中。
)

FileInputStream的使用

//1、创建一个FileInputStream对象,构造方法中传递写入数据的目的地
FileInputStream fis = new  FileInputStream("C:\\Java\\test.txt");

//或通过File文件对象来创建FileInputStream
File file = new File(C:\\Java\\test.txt");
FileInputStream fis2 = new FileInputStream(file);


//2.1调用FileInputStream对象中的方法read,从该输入流中读取一个字节的数据
int s = fis.read();
System.out.println((char)s);

//2.2或循环读取整个文件数据
int data=0;
//返回数据的下一个字节,如果到达文件末尾则返回-1
while((data=fis.read())!=-1){
    System.out.println((char)data);
}

//2.3或从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中
byte[] b = new byte[6];
//存储到数组b中
fis.read(b);
//将字节数组转换成字符串输出
String s1 = new String(b);
System.out.println(s1);

//3.关闭流
fis.close();
复制代码

FileOutputStream的使用

public static void main(String[] args) throws IOException {
    FileOutputStream fos = new FileOutputStream("C:\Study\aaa.tx",true);
    byte[] b ={99,98,97};
    //将字节数组b写入到输出流中
    fos.write(b);
    fos.close();
}
复制代码

ByteArrayInputStream类 和 ByteArrayOutputStream类

ByteArrayInputStream(字节数组输入流):是字节数组输入流,它里面包含一个内部的缓冲区(就是一个字节数组 ),该缓冲区含有从流中读取的字节。

ByteArrayOutputStream(字节数组输出流):是字节数组输出流,会将数据写入到定义的缓冲区中,默认缓冲区大小为32,会自动扩容。

ByteArrayInputStream的使用

public static void main(String[] args) {
    byte[] bytes = {97,98,99,100};
    //从字节数组的索引下标为1的位置开始读取2个元素
    ByteArrayInputStream bais = new ByteArrayInputStream(bytes,1,2);
    int data;
    while((data =bais.read())!=-1){
        System.out.print(data);
    }
    输出:9899
}
复制代码

ByteArrayOutputStream的使用

public static void main(String[] args) throws IOException {
    //创建一个文件输入流
    FileInputStream in = new FileInputStream(new File("C:\Study\aaa.txt"));
    //创建一个字节数组输出流
    ByteArrayOutputStream out = new ByteArrayOutputStream();

    int data = 0;
    //循环遍历文件输入流中的数据
    while((data = in.read()) != -1){
        //将数据写出到字节数组的缓冲区中
        out.write(data);
    }
    //获取内存缓冲区中的数据
    byte[] bytes = out.toByteArray();
    for (int i = 0; i < bytes.length; i++) {
        System.out.println((char)bytes[i]);
    }

    in.close();
    out.close();

}
复制代码

ObjectInputStream类 和 ObjectOutpuStream类

ObjectInputStream:是反序列化流,一般和ObjectOutputStream配合使用。

ObjectOutputStream:将java对象序列化然后存入文件中,然后用ObjectInputStream读取出来。

 class Data implements Serializable {
    private int n;
    public Data(int n){
        this.n=n;
    }
    @Override
    public String toString(){
        return Integer.toString(n);
    }
}
public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Data w=new Data(2);
        ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("worm.out"));
        //序列化对象,把对象写到worm.out里面
        out.writeObject("Worm storage\n");
         //序列化对象,把对象写到worm.out里面
        out.writeObject(w);
        out.close();
        //从worm.out里面读取对象 
        ObjectInputStream in=new ObjectInputStream(new FileInputStream("worm.out"));
        //读取String对象
        String s=(String)in.readObject();
        //读取Data对象
        Data d=(Data)in.readObject();
        System.out.println(s+"Data = "+d);
    }
}
复制代码

字符读取/写出流

Reader类 和 Wirter类

  • Reader是用于读取字符的抽象类
  • Writer是用于写出字符的抽象类

Reader类常见子类有:

  • FileReader:文件输入流
  • BufferedReader: 带缓冲区的字符输入流
  • InputStreamReader:实现字节流和字符流之间的转换

Writer类常见子类有:

  • FileWriter:文件输出流
  • BufferedWriter:带缓冲区的字符输出流
  • OutputStreamWriter:实现字符流转换为字节

Reader一些子类共性的方法

  • int read(CharBuffer target) 读取字节到字符缓存中。

  • int read() 读取单个字符。

  • int read(char cbuf[]) 读取字符到指定的 char 数组中。

  • int read(char cbuf[], int off, int len) 从 off 位置读取 len 长度的字符到 char 数组中。

  • long skip(long n) 跳过指定长度的字符数量不读取。

Writer一些子类共性的方法

  • void write(int c) 写入一个字符。

  • void write(char cbuf[]) 写入一个字符数组。

  • void write(char cbuf[], int off, int len) 从字符数组的 off 位置写入 len 数量的字符。

  • void write(String str) 写入一个字符串。

  • void write(String str, int off, int len) 从字符串的 off 位置写入 len 数量的字符。

  • Writer append(CharSequence csq)以追加的方式写入一个字符序列。

 FileReader类 和 FileWriter类

FileReader的构造方法

  • FileReader(String fileName) 根据本地文件的全路径名创建一个文件字符输入流。

  • FileReader(File file) 根据一个File对象创建一个文件字符输入流。

  • FileReader(String fileName, Charset charset) 根据本地文件的全路径名创建一个文件字符输入流,并指定字符集。

  • FileReader(File file, Charset charset)根据一个File对象创建一个文件字符输入流,并指定字符集。

FileReader的使用

public static void main(String[] args) throws IOException {
    FileReader fileReader = new FileReader(new File("C:\Study\aaa.txt"));
    int data;
    while ((data =fileReader.read()) != -1) {
        System.out.println(data);
    }
}
复制代码

FileWriter的构造方法

  • FileWriter(String fileName) 根据存在的文件的全路径文件名称来创建一个文件字符写出流。

  • FileWriter(String fileName, boolean append)以上一个构造方法相比多了一个append,表示是否追加写,默认为否。

  • FileWriter(File file)根据一个文件对象File来创建一个FileWriter文件字符写出流。

  • FileWriter(String fileName, Charset charset) 根据指定的编码和文件名称创建一个文件字符写出流。

FileWriter的使用

public static void main(String[] args) throws IOException {
    FileWriter fw = new FileWriter(new File("C:\Study\aaa.txt"));
    fw.write("FileWriter");
    fw.close();
}
复制代码

缓冲流

  • 字节缓冲流:BufferedOutputStream字节缓冲输出流,BufferedInputStream字节缓冲输入流.
  • 字符缓冲流:BufferedReader字符缓冲输出流,BufferedWriter字符缓冲输入流.

BufferedInputStream类 和 BufferedOutputStream类

BufferedInputStream构造方法

  • BufferedInputStream(InputStream in) 根据InputStream输入流创建一个 BufferedInputStream。

  • BufferedInputStream(InputStream in, int size) 根据InputStream输入流和指定的缓冲区大小创建一个BufferedInputStream。

BufferedOutputStream构造和常用方法

  • BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。

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

  • void flush() 使用flush刷新,才会把缓冲区中的字节全部写入文件中。

  • void write(byte[] b, int off, int len) 从指定的字节数组写入 len个字节,从偏移 off开始到缓冲的输出流。

  • void write(int b) 将指定的字节写入缓冲的输出流。

使用 BufferInputStream 和 BufferOutputStream 实现非文本文件的复制

@Test
public void testBufferedStream(){
    BufferedInputStream bis = null;
    BufferedOutputStream bos = null;
    try {
        //1.造文件
        File srcFile = new File("test.jpg");
        File destFile = new File("test4.jpg");
        //2.造流
        //2.1造节点流
        FileInputStream fis = new FileInputStream(srcFile);
        FileOutputStream fos = new FileOutputStream(destFile);
        //2.2造缓冲流,可以合并书写
        bis = new BufferedInputStream(fis);
        bos = new BufferedOutputStream(fos);

        //3.文件读取、写出操作
        byte[] buffer = new byte[1024];
        int len;
        while ((len = bis.read(buffer)) != -1){
            bos.write(buffer,0,len);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        //4.关闭流
        if (bos != null){
            try {
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (bis != null){

            try {
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
复制代码

BufferedReader类 和 BufferedWriter类

BufferedReader的构造方法

  • BufferedReader(Reader in)创建默认大小的输入缓冲区缓冲字符输入流。

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

BufferedWriterr的构造方法

  • BufferedWriter(Writer out) 根据字符写出流创建一个字符缓冲写出流,缓冲区默认大小是8192个字符。

  • BufferedWriter(Writer out, int sz) 根据字符写出流和指定的缓冲区大小创建一个字符缓冲写出流。

使用 BufferedReader 和 BufferedWriter 实现文本文件的复制

@Test
public void testBufferedReaderBufferedWriter(){
    BufferedReader br = null;
    BufferedWriter bw = null;
    try {
        //创建文件和相应的流
        br = new BufferedReader(new FileReader(new File("dbcp.txt")));
        bw = new BufferedWriter(new FileWriter(new File("dbcp1.txt")));

        //读写操作
        //方式一:使用char[]数组
        //            char[] cbuf = new char[1024];
        //            int len;
        //            while((len = br.read(cbuf)) != -1){
        //                bw.write(cbuf,0,len);
        //    //            bw.flush();
        //            }

        //方式二:使用String
        String data;
        while((data = br.readLine()) != null){
            //方法一:
            //                bw.write(data + "\n");//data中不包含换行符
            //方法二:
            bw.write(data);//data中不包含换行符
            bw.newLine();//提供换行的操作

        }


    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        //关闭资源
        if(bw != null){

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

        }
    }

}
复制代码

转换流

  • 转换流提供了在字节流和字符流之间的转换

  • Java API提供了两个转换流:

    • InputstreamReader:将 Inputstream 转换为 Reader
    • OutputStreamWriter:将 Writer 转换为 OutputStream
  • 字节流中的数据都是字符时,转成字符流操作更高效。

  • 很多时候我们使用转换流来处理文件乱码问题。实现编码和解码的功能。

InputstreamReader类 和 OutputStreamWriter类

nputStreamReader的构造方法

  • InputStreamReader(InputStream in) 创建一个使用默认字符集的InputStreamReader流。

  • InputStreamReader(InputStream in, Charset cs) 创建一个使用给定字符集的InputStreamReader。

  • InputStreamReader(InputStream in, String charsetName) 创建一个使用命名字符集的InputStreamReader。

public static void main(String[] args) throws IOException {
    //a:创建InputStreamWriter对象,构造方法中传递字节输入流InputStream和指定的编码
    FileInputStream fis = new FileInputStream("C:\Study\a.txt");
    InputStreamReader isw = new InputStreamReader(fis,"utf-8");

    //:使用InputStreamWriter对象中的方法read读文件
    int len=0;
    while((len=isw.read())!=-1){
        System.out.print((char)len);
    }
    //c:释放资源
    isw.close();
    fis.close();

}
复制代码

OutputStreamWriter的构造方法

  • OutputStreamWriter(OutputStream out) 创建一个使用默认字符编码的OutputStreamWriter流。

  • OutputStreamWriter(OutputStream out, Charset cs) 创建一个使用给定字符集的OutputStreamWriter流。

  • OutputStreamWriter(OutputStream out, String charsetName) 创建一个使用命名字符集的OutputStreamWriter流。

public static void main(String[] args) throws IOException{
    //a:创建OutputStreamWriter对象,构造方法中传递字节输出流OutputStream和指定的编码表
    FileOutputStream os = new FileOutputStream("C:\Study\a.txt",true);
    OutputStreamWriter osw = new OutputStreamWriter(os,"utf-8");

    //b:使用OutputStreamWriter对象中的方法write,把字符转换成字节存储到缓冲区中(编码)
    osw.write("Akiang");

    //c:使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
    osw.flush();

    //释放资源
    osw.close();

}
复制代码

对象流

  • ObjectOutputStream:内存中的对象—>存储中的文件、通过网络传输出去:序列化过程
  • ObjectInputStream:存储中的文件、通过网络接收过来 —>内存中的对象:反序列化过程

对象的序列化

  • 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。//当其它程序获取了这种二进制流,就可以恢复成原来的Java对象。

  • 序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原。

  • 序列化是RMI(Remote Method Invoke-远程方法调用)过程的参数和返回值都必须实现的机制,RMI是JavaEE的基础。因此序列化机制是JavaEE平台的基础。

  • 如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一。否则,会抛出 NotserializableEXception 异常

    • Serializable
    • Externalizable
  • 凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:

    • private static final long serialVersionUID;
    • serialVersionUID 用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象进行版本控制,有关各版本反序列化时是否兼容
    • 如果类没有显示定义这个静态常量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID可能发生变化。故建议显式声明。
  • 简单来说,Java 的序列化机制是通过在运行时判断类的 serialversionUID 来验证版本一致性的。在进行反序列化时,JVM 会把传来的字节流中的 serialversionUID 与本地相应实体类的 serialversionUID 进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException)

实现序列化的对象所属的类需要满足

  1. 需要实现接口:Serializable(标识接口)
  2. 当前类提供一个全局常量:serialVersionUID(序列版本号)
  3. 除了当前 Person 类需要实现 Serializable 接口之外,还必须保证其内部所属性也必须是可序列化的。(默认情况下,基本数据类型可序列化)

补充:ObjectOutputStreamObjectInputStream 不能序列化 statictransient 修饰的成员变量

对象流的使用

序列化代码实现

序列化:将对象写入磁盘或进行网络传输

要求被序列化对象必须实现序列化

@Test
public void testObjectOutputStream(){
    ObjectOutputStream oos = null;

    try {
        //1.创建对象,创建流
        oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
        //2.操作流
        oos.writeObject(new String("我爱北京天安门"));
        oos.flush();//刷新操作

        oos.writeObject(new Person("王铭",23));
        oos.flush();

        oos.writeObject(new Person("张学良",23,1001,new Account(5000)));
        oos.flush();

    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if(oos != null){
            //3.关闭流
            try {
                oos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

}

复制代码

反序列化代码实现

反序列化:将磁盘的对象数据源读出

@Test
public void testObjectInputStream(){
    ObjectInputStream ois = null;
    try {
        ois = new ObjectInputStream(new FileInputStream("object.dat"));

        Object obj = ois.readObject();
        String str = (String) obj;

        Person p = (Person) ois.readObject();
        Person p1 = (Person) ois.readObject();

        System.out.println(str);
        System.out.println(p);
        System.out.println(p1);

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

        }
    }
}
复制代码

?以上就是对Java中IO流的简单介绍,如果有错误的地方,还请留言指正,如果觉得本文对你有帮助那就点个赞吧??

1111.gif

免责声明:务必仔细阅读

  • 本站为个人博客,博客所转载的一切破解、path、补丁、注册机和注册信息及软件等资源文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。

  • 本站为非盈利性站点,打赏作为用户喜欢本站捐赠打赏功能,本站不贩卖软件等资源,所有内容不作为商业行为。

  • 本博客的文章中涉及的任何解锁和解密分析脚本,仅用于测试和学习研究,禁止用于商业用途,不能保证其合法性,准确性,完整性和有效性,请根据情况自行判断.

  • 本博客的任何内容,未经许可禁止任何公众号、自媒体进行任何形式的转载、发布。

  • 博客对任何脚本资源教程问题概不负责,包括但不限于由任何脚本资源教程错误导致的任何损失或损害.

  • 间接使用相关资源或者参照文章的任何用户,包括但不限于建立VPS或在某些行为违反国家/地区法律或相关法规的情况下进行传播, 博客对于由此引起的任何隐私泄漏或其他后果概不负责.

  • 请勿将博客的任何内容用于商业或非法目的,否则后果自负.

  • 如果任何单位或个人认为该博客的任何内容可能涉嫌侵犯其权利,则应及时通知并提供身份证明,所有权证明至admin@proyy.com.我们将在收到认证文件后删除相关内容.

  • 任何以任何方式查看此博客的任何内容的人或直接或间接使用该博客的任何内容的使用者都应仔细阅读此声明。博客保留随时更改或补充此免责声明的权利。一旦使用并复制了博客的任何内容,则视为您已接受此免责声明.

您必须在下载后的24小时内从计算机或手机中完全删除以上内容.

您使用或者复制了本博客的任何内容,则视为已接受此声明,请仔细阅读


更多福利请关注一一网络微信公众号或者小程序

一一网络微信公众号
打个小广告,宝塔服务器面板,我用的也是,很方便,重点是免费的也能用,没钱太难了,穷鬼一个,一键全能部署及管理,送你3188元礼包,点我领取https://www.bt.cn/?invite_code=MV9kY3ZwbXo=


一一网络 » Java 基础 – IO知识体系

发表评论

发表评论

一一网络-提供最优质的文章集合

立即查看 了解详情