【JavaSE总结】IO流

一、字节流

一般处理二进制文件

1、输入流

InputStream(抽象类)

包含子类
FileInputStream
FilterInputStream(实用子类BufferedInputStream)
ObjectInputStream
PipedInputStream
ByteArrayInputStream
FilterInputStream
DataInputStream
BufferedInputStream

2、输出流

子类几乎和字节输入流一样
OutputStream(抽象类)

包含子类(InputStream没有的)
PrintStream

二、字符流

一般处理文本文件和中文。

1、输入流

Reader(抽象类)

包含子类
FileReader
BufferedReader
PipedReader
StringReader
CharArrayReader
FilterReader
InputStreamReader(输出流没有)
BufferedReader

2、输出流

Writer(抽象类)
子类几乎和字符输入流一样

包含子类(Reader没有的)
PrintWriter
OutputStreamWriter

三、节点流

(针对数据源操作)

1、文件流

字节流

FileInputStream FileOutputStream
读写文件的常用方法:

  • 输入流read()方法:读取流中字节,返回值为-1表示到达文件末尾。参数可以为字节数组,表示读取到指定字节数组中,返回读取字节数。
  • 输出流write()方法:按字节写入流,无返回值。
  • close()方法:关闭流,释放资源。

构造器
有参构造,参数为文件路径,如:

FileInputStream fis = new FileInputStream("D://test1.txt");
FileOutputStream fos = new FileOutputStream("D://test2.txt");

第二个参数默认为false(覆盖模式),可手动改为true(追加模式),在追加模式下,流的指针在文件末尾。
调用构造器会产生FileNotFoundException的异常,read()、write()、close()会有IOException的异常,但是后者包含前者,所以仅抛出后者即可。
读写示例:

//D盘下有个叫test1.txt的文件,读取并输出
FileInputStream fis = new FileInputStream("D://test1.txt");
byte[] fileContents = new byte[1024];
fis.read(fileContents);
String str = new String(fileContents);
System.out.println(str);
fis.close();

//修改text2.txt的内容
FileOutputStream fos = new FileOutputStream("D://test2.txt");
String source = "FileOutputStream";
byte[] writeContents = source.getBytes();
fos.write(writeContents);
fos.close();
字符流

FileReader FileWriter
和字节流类似,读取的是字符,常用于读取中文。参数为byte[]字节数组的变为char[]字符数组。

  • FileWriter使用后必须调用close()或者flush()方法,否则写入不到指定文件。
    读写示例:
//读取文件并输出
FileReader fr = new FileReader("D://test1.txt");
char[] chars = new char[1024];
fr.read(chars);
System.out.println(chars);
fr.close();

//写入文件
FileWriter fw = new FileWriter("D://test2.txt");
String str = "FileWriter";
char[] ch = str.toCharArray();
fw.write(ch);
//fw.flush();
fw.close();

2、数组流

包括字节数组流和字符数组流,这两个里面还分输入和输出流。
示例:

//字符流
    //输入流
    String text1 = "123";
    byte[] bytes = text1.getBytes();//字符串文本转字节数组
    byte[] b = new byte[1024];//存放读取的字节数组
    ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
    bais.read(b);
    String str = new String(b);//字节数组转字符串,方便打印
    System.out.println(str);
    bais.close();
    //输出流
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    String text2 = "abc";
    bytes = text2.getBytes();//bytes中的内容重新赋值为
    baos.write(bytes);//写入baos到对象
    System.out.println(baos);
    baos.close();
//字节流
    //输入流
    char[] chars = text1.toCharArray();//字符串文本转字符数组
    CharArrayReader car = new CharArrayReader(chars);
    char[] c = new char[1024];//存放读取的字符数组
    car.read(c);
    str = new String(c);//字符数组转字符串,方便打印
    System.out.println(str);
    car.close();
    //输出流
    CharArrayWriter caw = new CharArrayWriter();
    chars = text2.toCharArray();//字符数组重新赋值
    caw.write(chars);//写入到caw对象
    System.out.println(caw);
    caw.close();

3、管道流

  • 用于多线程,单线程可能会出现死锁
  • 每一个管道输入流都对应一个输出流

以字节流为例,字符流类似:
定义发送数据的类

class SenderThread extends Thread{
    private PipedOutputStream pos;

    public SenderThread(PipedOutputStream pos) {
        super();
        this.pos = pos;
    }

    @Override
    public void run() {
        byte[] receiveBytes = "这是来自SenderThread的数据".getBytes();
        try {
            pos.write(receiveBytes);//管道输出流直接把数据写到连接的管道输入流的缓冲区
            pos.flush();
            pos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

定义接收数据的类

class ReceiveThread extends Thread{
    private PipedInputStream pis;

    public ReceiveThread(PipedInputStream pis) {
        super();
        this.pis = pis;
    }

    @Override
    public void run() {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] receiveBytes = new byte[1024];
            int len = 0;
            while ((len = pis.read(receiveBytes))!=-1){
                baos.write(receiveBytes);//把receiveBytes的数据写入到baos里
            }
            baos.flush();
            System.out.println(baos.toString());
            baos.close();
            pis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

写一下main()方法

PipedInputStream pipedI = new PipedInputStream();
PipedOutputStream pipedO = new PipedOutputStream();
pipedI.connect(pipedO);

SenderThread senderThread = new SenderThread(pipedO);
ReceiveThread receiveThread = new ReceiveThread(pipedI);

//用线程池让两个线程同步
//ExecutorService service = Executors.newFixedThreadPool(2);//线程池
//service.execute(senderThread);
//service.execute(receiveThread);
//service.shutdown();

senderThread.start();
receiveThread.start();

//pipedI.close();
//pipedO.close();
//service.shutdown();

这里是将管道流的close()方法放在了线程中,以实现两个线程的数据传输。也可以用注释掉的方法,用线程池实现两个线程的同时运行,这样close()方法就可以写在main()方法里了。

4、字符串流

只有字符流,没有字节流。
输入流把字符串读成字符数组,输出流把字符数组写入对象的内存。

//输入流
String text1 = "aaa";
StringReader sr = new StringReader(text1);
char[] buffer = new char[1024];//定义读到的字符数组
sr.read(buffer);//字符串流把字符串读成字符数组
String str = new String(buffer);//读到的数据转成字符串便于打印
System.out.println(str);
//输出流
String text2 = "bbb";
char[] chars = text2.toCharArray();
StringWriter sw = new StringWriter();
sw.write(chars);//把chars的内容写进sw对象
System.out.println(sw);

四、处理流

包装了节点流。节点流直接跟数据源相接,而处理流可以接收多种节点流类型,消除不同节点流的实现差异。

1、缓冲流

以BufferedReader/BufferedWriter为例,对应的字节流BufferedInputStream/BufferedOutputStream类似。

//读
char[] chars = "abc".toCharArray();
BufferedReader br = new BufferedReader(new CharArrayReader(chars));//参数类型为Reader,即可以在里面封装一个节点流
char[] buffer = new char[1024];
br.read(buffer);//把数据读进buffer字符数组
String str = new String(buffer);//转成字符串以便输出查看
System.out.println(str);
br.close();

//写
BufferedWriter bw = new BufferedWriter(new FileWriter("D://DemoBufferedWriter.txt"));//打开D盘文件
bw.write("Test BufferedWriter Class!".toCharArray());//将字符串写入
bw.close();

D盘文件内容如下:
文件在D盘的截图
文件内容

2、对象流

ObjectInputStream/ObjectOutputStream
需求分析:在保存一个值的时候,还希望保存它的数据类型。
举个例子,保存100在文本文档里,你不能确定它到底是int 100还是String “100”。
再举个例子,你有一个People对象,包含参数 “张三”,18 ,分别是这个对象的name和age属性对应的值,在保存后,下次要用还得还原成一个对象,而不是一堆数据。

序列化和反序列化

序列化:在保存数据时,保存数据的类型
反序列化:在恢复数据时,恢复数据的类型
要使类可序列化,必须实现接口SerializableExternalizable
Serializable是一个标记接口,没有任何方法。常用这个接口。

  • 序列化可继承
  • 序列化的对象,其属性也必须是序列化类型的
  • 序列化类中的属性默认序列化,除了statictransient修饰的属性

对象流的示例:

//对象输出流
Man man = new Man("张三",20);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\obj_os.data"));
oos.writeUnshared(man);//writeUnshared是用来写对象的,同样的write用来写Integer和Byte[]······
oos.write(100);
oos.writeBoolean(true);
oos.writeChar('a');
oos.writeDouble(3.141592653589793);
oos.writeUTF("abc");//写String
oos.close();

//对象输入流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\obj_os.data"));
//读取顺序要和写的顺序一样,不一样的话会产生格式异常
System.out.println(ois.readUnshared());//com.IOStream.Man@5fd0d5ae
System.out.println(ois.read());//100
System.out.println(ois.readBoolean());//true
System.out.println(ois.readChar());//a
System.out.println(ois.readDouble());//3.141592653589793
System.out.println(ois.readUTF());//abc
ois.close();

Man类:

class Man implements Serializable {
    private String name;
    private int age;

    public Man(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

D:\obj_os.data 文件截图:
在这里插入图片描述
用文本文件打开:
在这里插入图片描述

3、转换流

InputStreamReader/OutputStreamWriter

InputStreamReader isr = new InputStreamReader(new ByteArrayInputStream("Text".getBytes()));
OutputStreamWriter osw = new OutputStreamWriter(new ByteArrayOutputStream());
System.out.println(isr.getClass().getSuperclass());//class java.io.Reader
System.out.println(osw.getClass().getSuperclass());//class java.io.Writer

将一个字节流对象转成字符流

4、打印流

PrintStream和PrintWriter 只含输出流,不含输入流。
以PrintStream为例:

PrintStream console = System.out;//事先声明变量console为标准输出流

PrintStream ps = new PrintStream("D:\\PrintStreamTest.txt");//构造器参数可以为文件路径
ps.write("print something to file.".getBytes());
System.setOut(ps);//设置输出的属性,这里将标准输出指向了ps输出流,而不是默认的显示器
System.out.println("\nThe print path have been changed.");
ps.close();

PrintStream ps2 = console;
System.setOut(console);//将标准输出的路径改回到控制台
ps2.write("Print content using write method.\n".getBytes());//print方法的底层是write方法,所以不是按行输出
ps2.println("Print content using normal output.");
ps2.close();

输出流可以修改标准输出的路径,但是改之前得先声明变量保留默认的标准输出流,否则修改之后默认的被回收就改不回来了。(也有可能有其他改回来的方法,但是我不会)
然后看一下D盘的那个修改标准输出路径生产的文件:
在这里插入图片描述
在这里插入图片描述
PrintWriter的构造器有略微差别,有参构造需传入一个OutputStream对象,也就是说不可以直接赋值为标准输出流System.out了。用法如下:

PrintWriter pw = new PrintWriter(System.out);
pw.close();

五、标准输入输出

System.in

默认设备是键盘
它的运行类型是BufferedInputStream,是一个缓冲处理流
所以Scanner对象读取的是键盘输入的内容

System.out

默认设备是显示器
所以System.out.println()方法是将内容按行输出到显示器


上一篇 集合框架
下一篇 多线程

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

新手且笨蛋37

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值