day22 Java IO流


I know, i know
地球另一端有你陪我




基本定义

IO(in && out)
指应用程序和外部设备之间的数据传递

(Stream)
是一个抽象的概念,是指一连串的数据(字符或字节),是以先进先出的方式发送信息的通道

按照流向分为:
    输入流 读取数据
    输出流 写出数据
    (此处是以 java 程序作为参照物)

按照传输内容分为:
    字节流
        字节输入流 InputStream
        字节输出流 OutputStream
    字符流
        字节输入流 Reader
        字节输出流 Writer
    (二者的区别是默认一次读取的字节数量)

一、字节流

1、基本字节输出流(OutputStream)

其本身为抽象类,包含具体实现子类
FileOutputStream

构造方法
FileOutputStream(File file, boolean append)

FileOutputStream(String name, boolean append)

append 为 false 时,每次输出会重置文件内容
append 为 true 时,每次输出将跟在最后的内容之后

写出方法

写入指定字符数组内容
void write(byte[] b)

扫描二维码关注公众号,回复: 13193246 查看本文章

写入字符数组 off 索引开始,len 长度的子字符数组
void write(byte[] b, int off, int len)

写入数字,默认通过 ASCII 转换
void write(int b)

    File file = new File("D:\\韭菜盒子.txt");
    FileOutputStream fos = null;
    byte[] a = {
    
    97,98,99,100,101};
    try {
    
    
        //  若文件不存在将会自动创建
        fos = new FileOutputStream(file,true);
    } catch (FileNotFoundException e) {
    
    
        e.printStackTrace();
    }

    try {
    
    
        fos.write("fgh".getBytes());
        // windows系统中的换行符
        fos.write("\r\n".getBytes());
        fos.write(a,1,3);
        fos.write("\r\n".getBytes());
        fos.write(120);

    } catch (IOException e) {
    
    
        e.printStackTrace();
    } finally {
    
    
        try {
    
    
            //	让字节输出流对象变成一个垃圾,这样就可以被回收了
            //  去通知系统关闭相关的资源
            //  关闭流之后将不能再继续输出操作
            fos.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
/*文件夹内容
		fgh
		bcd
		x
*/

2、基本字节输入流(InputStream)

其本身为抽象类,包含具体实现子类
FileInputStream

写入方法

int read()
从该输入流读取一个字节的数据
如果达到文件的末尾,则返回 -1

    File file = new File("D:\\韭菜盒子.txt");
    FileInputStream fls = null;

    try {
    
    
        fls = new FileInputStream(file);
    } catch (FileNotFoundException e) {
    
    
        e.printStackTrace();
    }

    try {
    
    
        int i = 0;
       //	此为一种文件循环输出方式
        //	达到文件的末尾,read() 返回 -1
        while((i = fls.read()) != -1){
    
    
        //  返回 ASCII 码值
        //  此处强转为字符
            System.out.print((char) i);
        }
		//	int read = fls.read();
        
    } catch (IOException e) {
    
    
        e.printStackTrace();
    } finally {
    
    
        try {
    
    
            fls.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
/*
		fgh
		bcd
		x
*/    

从该输入流读取最多 b.length个字节的数据为字节数组
int read(byte[] b)
返回的是读取字节的长度
读取到的字节存储在字节数组中

    File file = new File("D:\\韭菜盒子.txt");

    FileInputStream fis = null;
    try {
    
    
        fis = new FileInputStream(file);
    } catch (FileNotFoundException e) {
    
    
        e.printStackTrace();
    }
    //  返回的是读取出的字节数组的实际长度
    //  读取到的字节存储在字节数组中
    //  长度最好是1024的倍数
    byte[] a = new byte[1024];
    int length = 0;

    try {
    
     
        while((length = fis.read(a)) != -1){
    
    
            String s = new String(a, 0, length);
            System.out.println(s);
        }
        //  int length = fis.read(a);
        //  String s = new String(a);
        //  String s = a.toString();
        //  由于未重写 toString,会输出地址
        //  System.out.println(s);
    } catch (IOException e) {
    
    
        e.printStackTrace();
    } finally {
    
    
        try {
    
    
            fis.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
/*
		fgh
		bcd
		x
		韭菜盒子韭菜盒子小六小六
*/

3、字节缓冲输出流(BufferedOutputStream)

利用缓冲区提高传输效率

构造方法
BufferedOutputStream(OutputStream out)
注:此处形参是一个基本字节输出流对象
       往后全部应用缓冲区都需要在写入进行 flush (),进行刷新

	//	构造方法
    java.io.BufferedOutputStream bos 
    = new java.io.BufferedOutputStream(new 	FileOutputStream("韭菜盒子.txt"));

    bos.write("韭菜盒盒子".getBytes());

    //  刷新缓冲区方法,用以写入数据
    bos.flush();
    bos.close();

4、字节缓冲输入流(BufferedInputStream)

同上

构造方法
BufferedInputStream(InputStream in)
注:此处形参是一个基本字节输入流流对象

    BufferedInputStream bis 
    = new BufferedInputStream(new FileInputStream("韭菜盒子.txt"));

//        int i = 0;
//        while((i = bis.read()) != -1){
    
    
//            System.out.println((char) i);
//        }

    int length = 0;
    byte[] bytes = new byte[1024];
    while((length = bis.read(bytes)) != -1){
    
    
        System.out.println(new String(bytes,0,length));
    }
    bis.close();

二、字符流

字符流 = 字节流 + 编码表

1、基本字符输出流(OutputStreamWriter)

构造方法
OutputStreamWriter(OutputStream out)
创建一个使用默认字符集的OutputStreamWriter

OutputStreamWriter(OutputStream out, String charsetName)
根据指定的编码创建一个OutputStreamWriter

注:此处形参是一个基本字节输出流对象
       字符输出流默认使用缓冲区,因此基本字符输出也需要 flash()

写入方法
public void write(int c)
public void write(char[] cbuf)
public void write(char[] cbuf,int off,int len)
public void write(String str)
public void write(String str,int off,int len)

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

    OutputStreamWriter osw 
    = new OutputStreamWriter(new FileOutputStream("韭菜盒子2.txt"));

    osw.write(110);
    osw.flush();

    char[] c = {
    
    'a','b','c','d','e'};
    osw.write(c);
    osw.flush();

    osw.write(c,1,3);
    osw.flush();

    osw.write("韭菜盒盒子");
    osw.flush();

    osw.write("韭菜盒盒子",0,4);
    osw.flush();

    osw.close();

2、基本字符输入流(InputStreamReader)

构造方法
InputStreamReader(InputStream in)
创建一个使用默认字符集的InputStreamReader

InputStreamReader(InputStream in, String charsetName)
根据指定的编码创建一个InputStreamReader

读取方法
public int read()
一次只读一个字符

public int read(char[] cbuf)
一次读取一个字符数组

    InputStreamReader isr
            = new InputStreamReader(new FileInputStream("韭菜盒子2.txt"));

//        int i = 0;
//        while((i = isr.read()) != -1){
    
    
//            System.out.print((char) i);
//        }

    int length = 0;
    char[] c = new char[1024];
    while((length = isr.read(c)) != -1){
    
    
        System.out.print(new String(c,0,length));
    }
    isr.close();

3、基本字符输出流 简写(FileWriter)

效果完全等同于基本字符输出流 (OutputStreamWriter)

FileWriter(File file)

FileWriter(String fileName)

    FileWriter fw = new FileWriter("韭菜盒子3.txt");
    
    fw.write(123);
    fw.write("韭菜盒盒子");
    fw.flush();
    fw.close();

4、基本字符输入流 简写(FileReader)

效果完全等同于基本字符输出流 (InputStreamResder)

    FileReader fr = new FileReader("韭菜盒子3.txt");

    int length = 0;
    char[] c = new char[1024];

    while((length = fr.read(c)) != -1){
    
    
        System.out.println(new String(c,0,length));
    }

5、字符缓冲输出流(BufferedWriter)

构造方法
BufferedWriter(Writer out)
注:此处形参兑现为字符输出流对象

特殊输出方法
public void newLine()
效果同换行符相同

    BufferedWriter bw
            = new BufferedWriter(new FileWriter("韭菜盒子4.txt"));

    bw.write("韭菜盒盒子");
    //  换行
    bw.newLine();
    bw.write("fgh");

    bw.flush();
    bw.close();

6、字符缓冲输入流(BufferedReader)

构造方法
BufferedReader(Reader in)
注:此处形参兑现为字符输入流对象

特殊输入方法
public String readLine()
以换行符为结束点读一行文字,不包括换行符

    BufferedReader br
            = new BufferedReader(new FileReader("韭菜盒子4.txt"));
            
	String s = null;
	whlie((s = readline) != null)
    System.out.print(s)
输出结果:
		韭菜盒盒子fgh

三、序列化

序列化流:把对象按照流一样的方式存到文本文件或者数据库或者网络中传输等

对象 -- 流数据 :ObjectOutputStream

反序列化:把文本文件中的流对象数据或者网络中的流数据给还原成一个对象

流数据 -- 对象 :ObjectInputStream
import java.io.Serializable;

class Pokemon implements Serializable {
    
    
    private String name;
    private int level;
    public Pokemon() {
    
    
    }
    public Pokemon(String name, int level) {
    
    
        this.name = name;
        this.level = level;
    }

    get & set ...

    @Override
    public String toString() {
    
    
        return "Pokemon{" +
                "name='" + name + '\'' +
                ", level=" + level +
                '}';
    }
}

此处自定义类需要实现标记接口 Serializable
用于获得该自定义类的 serialVersionUID 序列化UID
否则抛出 java.io.NotSerializableException 未序列化异常


    Pokemon p1 = new Pokemon("piplup", 5);
    ObjectOutputStream oos
            = new ObjectOutputStream(new FileOutputStream("韭菜盒子.txt"));

    oos.writeObject(p1);
    oos.close();

    ObjectInputStream ois
            = new ObjectInputStream(new FileInputStream("韭菜盒子.txt"));

    Object o = ois.readObject();
    Pokemon p = (Pokemon) o;
    System.out.println(p);
    ois.close();
运行结果:Pokemon{name='piplup', level=5}

在输出和输入时,若自定义类内部发生改变
如成员的修饰词发生吧变化
此时会抛出 serialVersionUID 异常错误

原因是在读取时,系统会根据之前的 serialVersionUID
与当前的进行比对,倘若发生变化,就会倒置读取的失败

如何处理这种情况
1、固定该自定义类的 serialVersionUID
在这里插入图片描述
勾选后在该自定义类的类名出 alt + enter

2、利用修饰词 transient
其可以保证自定义类在序列化时,被修饰的成员不被序列化

private transient int level;

总结

flush() & close()
flush()
刷新缓冲区,利用到缓冲区的输出流需要使用
close()
先刷新一次缓冲区,接着关闭流

基本字节输出流
OutputStream                  (抽象类
FileOutputStream (…)     (具体实例

基本字节输入流
InputStream                  (抽象类
FileInputStream (…)     (具体实例

字节缓冲输出流
BufferedOutputStream( new FileOutputStream (…) )

字节缓冲输入流
BufferedInputStream( new FileInputStream (…) )

基本字符输出流
OutputStreamWriter ( new FileOutputStream (…) )
简化
FileWriter (…)

基本字符输入流
InputStreamReader ( new FileInputStream (…) )
简化
FileReader (…)

字符缓冲输出流
BufferedWriter ( FileWriter(…) )
固有技
public void newLine()             换行

字符缓冲输入流
BufferedReader( FileReader(…) )
固有技
public String readLine()          读取一行字符串

写入 Output
writer

读取 Input
read

猜你喜欢

转载自blog.csdn.net/qq_41464008/article/details/120860468