IO流

IO流

IO流概述

什么是IO流

​ io流就是内存和硬盘上完成对数据的读和写

I:Input

O:Output

通过IO可以完成硬盘文件的读和写

往内存中去,叫做输入(Input),或者叫做(Read)

从内存中出来,叫做输出(Output),或者叫做(Write)

流的分类

字节和字符

​ 字节(Byte)是一种计量单位,表示数据量多少,它是计算机信息技术用于计量存储容量的一种计量单位

​ 字符就是键盘上所有的符号,包括英文,汉字等

​ 针对于每一个字符,都有固定的Ascll码与之对应,!占了一个字节,然后根据字节流读取数据的时候,读取的是!这个字符所对应的AscII码

​ 像特殊符号、英文在java中占了一个字节,汉字在java中占了两个字节

有的流时按照字节的方式读取数据,一次读取1个字节byte,等同于一次读取8个二进制位

按照字符的方式读取数据的,一次读取一个字符,这种流是为了方便读取普通文本文件而存在的

  • 字节流

​ 字节流在读取数据的时候,会遍历文件中的字符,然后读取的是每个字符所对应的asII码

​ 字节流可以读取任意类型的文件(普通文本,视频,录音,图片…)

  • 字符流

​ 在读取数据的时候,就是一个字符一个字符的进行读取,字符流的效率比较高,但是只能读取普通文本

流的分类

java.io.InputStream 字节输入流

读取硬盘数据到内存

java.io.OutputStream 字节输出流

存储内存数据到硬盘

java.io.Reader 字符输入流

java.io.Writer 字符输出流

流的关闭

所有的都实现了java.io.Closeable接口,都是可关闭的,都有close()方法

所有的输出流都实现了java.io.Flushable接口,都是可刷新的,都有flush()方法

文件专属流
  • java.io.FileInputStream
  • java.io.FileOutputStream
  • java.io.FileReader
  • java.io.FileWriter
//创建一个字节流对象
FileInputStream fileInputStream = new FileInputStream("D:\\pythonschool\\PY_lab\\File\\html.txt");

//调用读取数据的方法,一次读取一个字符
//fileInputStream.read() 这个方法的返回值是指定字符的ascii码,在文件中数据都读取完的情况下会返回-1
int read = fileInputStream.read();

//输出一次读取的字符的Ascii码
System.out.println(read);

//读取数据
int read = fileInputStream.read();

//通过循环的方式来读取数据
do {

    System.out.println(read);

    read = fileInputStream.read();
}while (read !=-1);

fileInputStream.read() 如果传了字符数组,那么返回的是读取的字符个数

//通过字节数组的方式可以一次性读取多个字节
//1、创建一个字节数组
byte[] bytes = new byte[10];

int read = fileInputStream.read(bytes);

//read是读取字符的个数
System.out.println(read);

FileInputStream这个字节流如何将ascII转成对应的字符

可以根据String这个类来完成

//创建一个String类
//参数:读取数组,读取起始位置,读取字符数量
//默认从0开始读,读取长度为bytes.length
String s = new String(bytes);

System.out.println(s);

:第二次读取会对之前数组中已经存储的数据进行覆盖

读取数据

package com.os467.io;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class IoTest03 {

    public static void main(String[] args) {

        //创建输入流对象
        FileInputStream fileInputStream = null;

        try {

            fileInputStream = new FileInputStream("D:\\pythonschool\\PY_lab\\File\\html.txt");

            //创建字节数组 1kb=1024b
            byte[] bytes = new byte[1024];

            int read = 0;

            //遍历字节数组,读取数据
            while ((read = fileInputStream.read(bytes)) != -1){

                //根据字符串将读取到的字节转换成字符串,读取到多少个就转换多少个
                System.out.print(new String(bytes,0,read));

            }


        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {

            try {
                //关闭流对象
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

    }




}

方法2:只适合小文件

fileInputStream = new FileInputStream("D:\\pythonschool\\PY_lab\\File\\html.txt");

//获取指定文件的字节总个数
int available = fileInputStream.available();

byte[] bytes = new byte[available];

int read = fileInputStream.read(bytes);

System.out.println(new String(bytes,0,read));

写入数据

//创建字节输出流
fileOutputStream = new FileOutputStream("C:\\Users\\tly20\\Desktop\\test.txt");

//写入数据到硬盘上
fileOutputStream.write(97);

//创建一个字节数组
byte[] bytes = {98,99,100,101};

fileOutputStream.write(bytes);

将字符串数据写入

//创建一个字符串
String str = "你好,Tom";

//将字符串转成对应的字节数组
byte[] bytes1 = str.getBytes();

//写入数据
fileOutputStream.write(bytes1);

//刷新
fileOutputStream.flush();

开启写入追加(默认是false,覆盖写入)

fileOutputStream = new FileOutputStream("C:\\Users\\tly20\\Desktop\\test.txt",true);

字符输入输出流

package com.os467.io;

import java.io.*;

/**
 * 通过字符输入流和字符输出流来完成一个普通文本文件的拷贝
 */
public class IoTest06 {
    public static void main(String[] args){

        //文件字符输入流
        FileReader fileReader = null;

        //文件字符输出流
        FileWriter fileWriter = null;


        //创建文件字符输入流
        try {

            //创建文件字符输入流
            fileReader = new FileReader("C:\\Users\\tly20\\Desktop\\test.txt");

            fileWriter = new FileWriter("C:\\Users\\tly20\\Desktop\\test_copy.txt",true);

            //创建字符数组 一次读取1024字符
            char[] chars = new char[100];

            //读数据
            int readCount = 0;

            //一边读,一边写
            while ((readCount = fileReader.read(chars) )!= -1){

                //数据的转换
                String str = new String(chars,0,readCount);

                //数据的写入,可以直接写入字符串
                fileWriter.write(str);

            }

            //刷新输出流
            fileWriter.flush();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {

            //关闭资源
            if (fileReader != null){

                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }

            if (fileWriter != null){

                try {
                    fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }

        }


    }




}

注意点

  • 字节流要创建字节数组bytes[]接收读取的数据,字符流要创建字符数组chars[]接收读取的数据
  • 输入流和输出流在最后都需要通过close()方法关闭资源
  • 输出流需要通过flush()方法进行刷新
缓冲流
  • java.io.BufferedReader
  • java.io.BufferedWriter
  • java.io.BufferedInputStream
  • java.io.BufferedOutputStream

缓冲流会将字节先写到一个缓冲区中,然后通过flush()或者关闭这个缓冲流来实现将缓冲流中的数据回显

特点:不需要创建数组

package com.os467.io;

import java.io.*;

/**
 * 缓冲流:
 *      自带缓冲区的流,在一次读取多个字符的时候,可以不用创建数组
 */
public class IoTest08 {
    public static void main(String[] args) throws Exception{

        String readLine = null;

        //创建缓冲流对象
        BufferedReader bufferedReader = new BufferedReader(new FileReader("C:\\Users\\tly20\\Desktop\\test.txt"));

        //调用读取数据的方法
        //读取一行数据

        while ((readLine = bufferedReader.readLine()) != null){

            System.out.println(readLine);

        }


        bufferedReader.close();
    }

}
转换流

可以将字符流转换成字节流,也可以将字节流转换成字符流 InputStreamReader

包装流和节点流: new 流(new 流()) 外层是包装流,里面的是节点流

在关闭流对象的时候,只需要关闭最外层的包装流即可

//创建转换流 将字节流转换成字符流
        InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("C:\\Users\\tly20\\Desktop\\test.txt"));

//关闭流的时候只需要关闭最外层的包装流即可
inputStreamReader.close();
数据流
  • java.io.DataInputStream
  • java.io.DataOutputStream

数据流在写入数据到文件中的时候,要根据数据类型来写入,而且写入之后的文件是加密的,是我们看不懂的文件,如果想读取这些数据的话,只能通过数据输入流来读取

数据输入流在读取数据的时候,是要按照写入的顺序来读取,而且需要按照指定的数据类型来读取

存入数据流数据

public static void main(String[] args) throws  Exception {

    //创建一个数据输出流
    DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream("C:\\Users\\tly20\\Desktop\\test_copy.txt"));

    //指定数据
    boolean b = false;
    float f = 10f;
    int i = 20;
    double d = 3.14;
    
    //调用写入的方法
    dataOutputStream.writeBoolean(b);
    dataOutputStream.writeFloat(f);
    dataOutputStream.writeInt(i);
    dataOutputStream.writeDouble(d);
    
    //刷新输出流
    dataOutputStream.flush();
    
    //关闭流
    dataOutputStream.close();
    
}

按照存储的顺序读取加密数据

public static void main(String[] args) throws  Exception {

    //创建一个数据输出流
    DataInputStream dataInputStream = new DataInputStream(new FileInputStream("C:\\Users\\tly20\\Desktop\\test_copy.txt"));

    //读取数据要根据写入的顺序来读取
    boolean b = dataInputStream.readBoolean();

    float v = dataInputStream.readFloat();

    int i = dataInputStream.readInt();

    double v1 = dataInputStream.readDouble();

    System.out.println(b);
    System.out.println(v);
    System.out.println(i);
    System.out.println(v1);
    
    dataInputStream.close();


}
标准输出流
  • java.io.PrintWriter
  • java.io.PrintStream

默认情况是我们经常使用的控制台打印,我们可以去改变输出流的输出方向

标准输出流的默认输出方向就是控制台

标准输出流以后适用的场景

可以去收集日志

比如说程序在某个时间点发生了某某异常,然后可以根据这个流来收集异常信息

或者去收集模块域模块之间的调用次数

//创建输出流对象,使用相对路径
PrintStream printStream = new PrintStream(new FileOutputStream("data.txt"));

//打印hello world在data.txt文件中
printStream.print("hello world");
//创建输出流对象,不设置路径会默认打印在控制台
PrintStream printStream = System.out;

printStream.print("Hello world");
//设置输出的方向
PrintStream printStream = new PrintStream(new FileOutputStream("data.txt"));

System.setOut(printStream);

//打印在data.txt中
System.out.println("hello world");

//刷新输出流
printStream.flush();

//关闭资源
printStream.close();
异常日志收集工具类
package com.os467.utils;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.PrintStream;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 日志收集工具类
 */
public class LoggerUtils {

    private LoggerUtils(){

    }

    /**
     * 收集日志的方法
     * @param message
     */
    public static void logger(String message){

        PrintStream printStream = null;

        try {

            //创建标准输出流对象
            printStream = new PrintStream(new FileOutputStream("logger",true));

            //设置输出路径
            System.setOut(printStream);

            //创建日期对象
            Date date = new Date();

            //创建日期格式化对象
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

            //将日期转成字符串
            String time = simpleDateFormat.format(date);

            //记录日志
            System.out.println(time +" : "+message );

            //刷新输出流对象
            printStream.flush();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }finally {

            if (printStream != null){

                printStream.close();

            }

        }


    }

}

对象专属流

java.io.ObjectInputStream

java.io.ObjectOutput

可序列化接口

写入的对象必须实现序列化的接口Serializable

  • 对象专属流可以完成在一个内存和硬盘上对java对象进行序列化反序列化

序列化与反序列化

序列化

​ 序列化就是将java对象写入到硬盘上,写入之后的文件也是加密的格式

反序列化

​ 将文件中我们看不懂的加密数据读取出来,读取的就是java对象

  • 以后我们搭建的分布式项目,模块与模块间如果想完成数据的传输,需要通过序列化与反序列化来完成

  • 序列化java对象的时候,该对象需要去实现一个序列化的接口

  • Serializable序列化接口,这个接口没有任何的方法,只是一个标识性接口

    因为我们在执行加载某一个类的时候会生成该类对应的字节码文件,这个时候jvm虚拟机会检查该类是否有Serializable这个接口

    如果有,则赋予该类序列化的权限,如果没有则不能进行序列化

对User类的对象进行序列化操作

public static void main(String[] args) throws  Exception {

    //创建输出流对象(序列化对象)
    ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("users.txt"));

    //创建user对象
    User user = new User();

    //将java对象写到文件中
    objectOutputStream.writeObject(user);

    //刷新输出流
    objectOutputStream.flush();

    //关闭流
    objectOutputStream.close();

}

对文件中存储的user数据进行反序列化操作

public static void main(String[] args) throws  Exception {

    //创建输入流对象
    ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("users.txt"));

    //读取数据
    Object object = objectInputStream.readObject();

    //判断数据类型
    if (object instanceof User){

        User user = (User)object;

        System.out.println(user);

    }

    //关闭流
    objectInputStream.close();


}

对多个对象进行序列化与反序列化(通过ArrayList来完成)

序列化多个对象
-----------------------------------------------------------------------------------------
//创建输出流对象(序列化对象)
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("users.txt"));

//创建user对象
User user1 = new User(1,"张三1","男");
User user2 = new User(2,"张三2","男");
User user3 = new User(3,"张三3","男");
User user4 = new User(4,"张三4","男");

//创建集合对象
ArrayList<User> users = new ArrayList<>();

users.add(user1);
users.add(user2);
users.add(user3);
users.add(user4);

//将java对象写到文件中
objectOutputStream.writeObject(users);

//刷新输出流
objectOutputStream.flush();

//关闭流
objectOutputStream.close();
------------------------------------------------------------------------------------------
反序列化多个对象
------------------------------------------------------------------------------------------
//创建输入流对象
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("users.txt"));

//用一个数组来接收
ArrayList<User> arrayList = (ArrayList<User>)objectInputStream.readObject();

for (User user : arrayList) {

    System.out.println(user);

}

//关闭流
objectInputStream.close();

关于序列化版本号的问题

如果在反序列化之前修改了类中的代码,在类加载的时候会重新生成字节码文件,那么jvm给该类生成的序列化版本号就会改变,在反序列化时,旧的序列化版本号与新生成的版本号不对应,就无法完成反序列化

解决方案

我们可以在类中写死一个序列化版本号

private static final long serialVersionUID = 8683452581122892189L;

关键字transient

transient可以使得某一属性不参与序列化的操作,在反序列化后会得到null值

JAVA I/O读取文件

读取文本文件

new BufferedReader(new InputStreamReader(new FileInputStream("a.txt"),"UTF-8"))


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以邮件至 1300452403@qq.com

文章标题:IO流

字数:3.4k

本文作者:Os467

发布时间:2022-07-10, 20:14:33

最后更新:2022-09-05, 00:08:24

原始链接:https://os467.github.io/2022/07/10/IO%E6%B5%81/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

×

喜欢就点赞,疼爱就打赏