字节流和字符流(阿里云开发者社区学习笔记)

流的基本概念

java.io包里面File类是唯一一个与文件本身有关的程序处理类,但是File只能够操作文件本身而不能够操作文件的内容,或者说在实际的开发之中IO操作的核心意义在于:输入与输出操作。而对于程序而言,输入与输出可能来自于不同的环境,例如:当通过电脑连接服务器上进行浏览的时候,实际上此时客户端发出了一个信息,而后服务器接收到此信息后进行回应处理
对于服务器或者客户端而言实质上传递的就是一种数据流的处理形式,而所谓的数据流指的就是字节数据。而对于流的处理形式在java.io包里面提供有两类支持:
‒ 字节处理流:OutputStream(输出字节流),InputStream(输入字节流)
‒ 字符处理流:Writer(输出字符流),Reader(输入字符流)

所有的流操作都应该采用如下统一的步骤进行,下面以文件处理的流程为例:
‒ 如果现在要进行的是文件的读写操作,则一定要通过File类找到一个文件路径
‒ 通过字节流或字符流的子类为父类对象实例化
‒ 利用字节流或字符流中的方法是西安数据的输入与输出操作
‒ 流的操作属于资源操作,资源操作必须进行关闭处理

OutputStream字节输出流

字节的数据是以byte类型为主实现的操作,在进行字节内容输出的时候可以使用OutputStream类完成,这个类的基本定义如下:

public abstract class OutputStream extends Object implements Closeable, Flushable

首先可以发现这个类实现了两个接口,于是基本的对应关系如下:

closeable:
public interface Closeable extends AutoCloseable{
public void close() throws IOException;
}
Flushable:
public interface Flushable{
public void flush​() throws IOException
}

这个结构要背一下

OutputStream类定义的是一个公共的输出操作标准,在这个操作标准里面一共定义有三个内容输出的方法:

NO方法名称类型描述
1public abstract void write​(int b)throws IOException普通输出单个字节数据
2(用的最多)public void write​(byte[] b)throws IOException普通输出一组字节数据
3public void write​(byte[] b,int off,int len)throws IOException普通输出部分字节数据

但是需要注意的一个核心问题在于:OutputStream类毕竟是一个抽象类,而这个抽象类如果想要获得实例化对象,按照传统的认识,应该通过子类实例的向上转型完成,如果说现在要进行的是文件处理操作,则可以使用FileOutputStream子类

因为最终都需要发生向上转型的处理关系,所以对于此时的FileOutputStream子类核心的关注点就可以放在
构造方法上了
‒ 【覆盖】构造方法:public FileOutputStream​(File file)throws FileNotFoundException
‒ 【追加】构造方法:public FileOutputStream​(File file,boolean append)throws FileNotFoundException
范例:使用OutputStream类实现内容输出

public class JavaAPIDemo{
    public static void main(String args[]) throws Exception{
        File file = new File("D:"+File.separator+"hello"+File.separator+"mldn.txt"); // 1.指定要操作文件路径
        if(!file.getParentFile().exists()){ // 文件不存在
            file.getParentFile().mkdirs();//创建父目录
        }
        OutputStream output = new FileOutputStream(file);// 2.通过子类实例化  
        String str = "www.mldn.cn"; // 要输出的文件内容
        output.write(str.getBytes());//3.将字符串变为字节数组并输出
        output.close();//4.关闭资源

    }
}

本程序是采用了最为标准的形式实现了输出的操作处理,并且在整体的处理之中,只是创建了文件的父目录,但是并没有创建文件,而在执行后会发现文件可以自动帮用户创建。另外需要提醒的是,由于OutputStream的子类也属于AutoCloseable的接口子类,所以对于close()方法也可以简化使用
范例:自动关闭处理

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class JavaAPIDemo13 {
    public static void main(String[] args) {
        File file = new File("."+File.separator+"字节流和字符流"+File.separator+"message.txt");
        if (!file.getParentFile().exists()){
            file.getParentFile().mkdir();
        }try (OutputStream outputStream = new FileOutputStream(file,true)){
            String str = "www.baidu.com\r\n";
            outputStream.write(str.getBytes());
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

是否使用自动的关闭取决于你项目的整体结构,另外还需要提醒大家的是,整个的程序里面最终输出的是一组的字节数据,但是千万不要忘记了,OutputStream类之中定义的输出方法一共有三个.

InputStream字节输入流

与OutputStream类对应的一个流就是字节输入流,InputStream类主要实现的就是字节数据读取,该类定义如下:

public abstract class InputStream extends Object implements Closeable

在InputStream类里面定义有如下的几个核心方法:

NO方法名称类型描述
1public int read​()throws IOException普通读取(返回)单个字节数据,如果现在已经读取到底返回-1
2-常用方法public int read​(byte[] b)throws IOException普通读取一组字节数据,返回的是读取的个数,如果没有数据已经读取到底则返回-1
3public int read​(byte[] b,int off,int len)throws IOException普通读取一组字节数据的部分内容

InputStream类属于一个抽象类,这时应该依靠它的子类来实例化对象,如果要从文件读取一定要使用FileInputStream子类,对于子类而言只关心父类对象实例化,构造方法:public FileInputStream​(File file)throws FileNotFoundException

范例:读取数据
import java.io.*;

public class JavaAPIDemo15 {
public static void main(String[] args) throws IOException {
File file = new File(“.”+File.separator+“字节流和字符流”+File.separator+“message.txt”);
InputStream input = new FileInputStream(file);
byte data[] = new byte[1024]; // 开辟一个缓冲区读取数据
int len = input.read(data); // 读取数据,数据全部保存在字节数组之中,返回读取个数
// byte[] bytes = input.readAllBytes();
// System.out.println(“[”+ new String(bytes)+“]”);

    System.out.println("["+ new String(data,0,len)+"]");
    input.close();

}

}

对于字节输入流里面最为麻烦的问题就在于:使用read()方法读取的时候只能够以字节数组为主进行接收

特别需要注意的是从JDK1.9开始在InputSteam里面增加了一个新的方法:public byte[] readAllBytes​()throws IOException

范例:新方法

public class JavaAPIDemo{
    public static void  main(String args[]) throws  Exception{
        File file = new File("D:"+File.separator + "hello" + File.separator + "mldn.txt");
        InputStream input = new FileInputStream(file);
        byte data[] = input.readAllBytes();//读取全部数据
        System.out.println("["+ new String(data)+"]");
        input.close();
    }
}

如果你现在要读取的内容很大很大,那么这种读取会直接搞死你的程序

不要读超过10K的文件,谁用谁知道

Writer字符输出流

使用OutputStream字节输出流进行数据输出的时候使用的都是字节类型的数据,而很多的情况下字符串的输出是比较方便的,所以对于java.io包而言,在JDK1.1的时候推出了字符输出流:Writer,这个类的定义如下:

public abstract class Writer extends Object implements Appendable,Closeable,Flushable

在Writer类里面提供有许多的输出操作方法,重点来看两个:
‒ [覆盖]输出字符数组:public void write​(char[] cbuf)throws IOException
‒ [覆盖]字符串:public void write​(String str) throws IOException
范例:使用Writer输出

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class JavaAPIDemo16 {
    public static void main(String[] args) throws IOException {
        File file = new File("."+File.separator+"字节流和字符流"+File.separator+"message.txt");
        if (!file.getParentFile().exists()){
            file.getParentFile().mkdirs();
        }
        Writer out = new FileWriter(file);
        String str = "www.baidu.com\r\n";
        out.write(str);
        out.append("hello world!");
        out.close();
    }
}

运行结果

使用Writer输出的最大优势在于可以直接利用字符串完成。Writer是在字符流,字符处理的优势在于中文数据上

Reader字符输入流

Reader是实现字符输出流的一种类型,其本身属于一个抽象类,这个类的定义如下:

public abstract class Reader extends Object implements Readable, Closeable

Reader类里面并没有像Writer类一样提供有整个字符串的输入处理操作,只能够利用字符数组来实现接收
‒ 接收数据:public int read​(char[] cbuf) throws IOException
范例:实现数据读取

import java.io.*;

public class JavaAPIDemo17 {
    public static void main(String[] args) throws IOException {
        File file = new File("."+File.separator+"字节流和字符流"+File.separator+"message.txt");
        if (file.exists()){
            Reader reader = new FileReader(file);
            char[] chars = new char[1024];
            int len = reader.read(chars);
            System.out.println("读取内容:" + new String(chars,0,len));
        }
    }
}

字符流读取的时候只能够按照数组的形式来实现处理操作

字节流和字符流的区别

现在通过一系列的分析已经可以清楚字节流与字符流的基本操作了,但是对于这两类流依然是存在有区别的,重点来分析一下输出的处理操作。在使用OutputStream和Writer输出的最后发现都使用了close()方法进行了关闭处理。

public class JavaAPIDemo{
        public static void main(String args[]) throws Exception{
            File file = new File("D:"+File.separator+"hello"+File.separator+"mldn.txt"); // 1.指定要操作文件路径
            if(!file.getParentFile().exists()){ // 文件不存在
                file.getParentFile().mkdirs();//创建父目录
            }
            OutputStream output = new FileOutputStream(file);// 2.通过子类实例化 
            String str = "www.mldn.cn"; // 要输出的文件内容
            output.write(str.getBytes());//3.将字符串变为字节数组并输出
           // output.close();//4.关闭资源
        }
    }
    ```
在使用OutputStream类输出的时候,如果现在没有使用close()方法关闭输出流发现内容依然可以实现正常的输出

```java
public class JavaAPIDemo{
        public static void main(String args[]) throws Exception{
            File file = new File("D:"+File.separator + "hello"+File.separator+"mldn.txt");
            if(!file.getParentFile().exists()){
                file.getParentFile().mkdirs(); // 父目录必须存在
            }
            Writer out = new FileWriter(file);// 追加true即为追加
            String str = "www.mldn.cn\r\n";
            out.write(str);
            out.append("xxxws");  // 追加输出内容
            // out.close();
        }
    }

但是如果在使用Writer的时候没有使用close()方法关闭输出流,那么这个时候内容将无法进行输出,因为Writer使用到了缓冲区,当使用了close()方法的时候实际上会出现有强制刷新缓冲区的情况,所以这个时候会将内容进行输出,如果没有关闭,那么将无法进行输出操作,所以此时如果在不关闭的情况要想将全部的内容输出可以使用flush()方法强制性清空

范例:使用Writer并强制性清空

public class JavaAPIDemo{
        public static void main(String args[]) throws Exception{
            File file = new File("D:"+File.separator + "hello"+File.separator+"mldn.txt");
            if(!file.getParentFile().exists()){
                file.getParentFile().mkdirs(); // 父目录必须存在
            }
            Writer out = new FileWriter(file);// 追加true即为追加
            String str = "www.mldn.cn\r\n";
            out.write(str);
            out.append("xxxws");  // 追加输出内容
            // out.close();
    out.flush(); //强制性刷新
        }
    }

字节流在进行处理的时候并不会使用到缓冲区,而字符流会使用到缓冲区。另外使用缓冲区的字符流更加适合中文数据的处理,所以在日后的程序开发之中,如果要涉及到包含中文信息的输出一般都会使用字符流处理,但是从另外一方面来讲,字节流和字符流的基本处理形式是相似的,由于IO很多情况下都是进行数据的传输使用(二进制)所以本次的讲解将以字节流为主。

转化流

所谓的转换流指的是可以实现字节流与字符流操作的功能转换,例如:进行输出的时候OutputStream需要将内容变为字节数组后才可以输出,而Writer可以直接输出字符串,这一点是方便的,所以很多人就认为需要提供一种转换的机制来实现不同流类型的转换机制,为此在java.io包里面提供有两个类:InputStreamReader,OutputStreamWriter

类:InputStreamReader
定义:public class InputStreamReader extends Reader
构造方法:public InputStreamReader​(InputStream in)

类:OutputStreamWriter
定义:public class OutputStreamWriter extends Writer
构造方法:public OutputStreamWriter​(OutputStream out)

通过类的继承结构与构造方法可以发现,所谓的转换处理就是将接收到的字节流对象通过向上转型变为字符流对象

范例:观察转换

public class JavaAPIDemo{
        public static void main(String args[]) throws Exception{
                File file = new File("D:"+File.separator + "hello"+File.separator+"mldn.txt");
                if(!file.getParentFile().exists()){
                        file.getParentFile().mkdirs(); // 父目录必须存在
                }
                OutputStream output  = new FileOutputStream(file);
                Writer out = new OutputStreamWriter(output); // 字节流变为字符流
                out.write("www.mldn.cn"); // 直接输出字符串,字符流适合处理中文
                out.close();
        }
}

讲解转换流的主要目的基本上不是为了让开发者去记住它,而是知道有这样一种功能,但同时更多的是需要进行结构的分析处理。通过之前的字节流与字符流一系列的分析之后会发现OutputStram类有FileOutputStream直接子类,InputStream有FileInputStream直接子类,但是来观察一下FileWriter和FileReader类的继承关系
FileWriter:
public class FileWriter extends OutputStreamWriter

FileReader:
public class FileReader extends InputStreamReader
实际上所谓的缓存都是指的是程序中间的一道处理缓冲区

学习资料

阿里云开发者社区

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值