• 分布式-网络通信-IO-基础(2)


    IS 与 OS
    1. 基本 IO 操作
    1.1. InputStream 与 OutputStream
    1.1.1. 输入与输出
    我们编写的程序除了自身会定义一些数据信息外,经常还会引用外界的数据,或是将自身的数据发送到
    外界。比如,我们编写的程序想读取一个文本文件,又或者我们想将程序中的某些数据写入到一个文件
    中。这时我们就要使用输入与输出。
    什么是输入:输入是一个从外界进入到程序的方向,通常我们需要 读取 外界的数据时,使用输入。所以 “ ”
    输入是用来读取数据的。
    什么是输出:输出是一个从程序发送到外界的方向,通常我们需要 写出 数据到外界时,使用输出。所以 ” ”
    输出是用来写出数据的。
    流是一套 API(灰常多),提供了各种类型数据的读写 API!流是一套可扩展的 API,通过扩展的 API 支
    持了各种类型数据的读写。
    流是从两个抽象类扩展的:
    • InputStream 输入流
    • read()
    • read(byte[] buf)
    • OutputStream 输出流
    • wirte(int b)
    • write(byte[] buf)
    1.1.2. 节点流与处理流
    按照流是否直接与特定的地方 (如磁盘、内存、设备等) 相连,分为节点流和处理流两类。
    节点流:可以从或向一个特定的地方(节点)读写数据。(功能简单 byte 读写)
    处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。
    (必须依赖低级流 不可单独工作 功能复杂 使用方便)
    处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链
    接。
    1.1.3. InputStream 与 OutputStream 常用方法
    InputStream 是所有字节输入流的父类,其定义了基础的读取方法,常用的方法如下:
    int read()
    读取一个字节,以 int 形式返回,该 int 值的 低八位 有效,若返回值为 ” ” -1 则表示 EOF。
    int read(byte[] d)
    尝试最多读取给定数组的 length 个字节并存入该数组,返回值为实际读取到的字节量。
    总结:输入流是抽象类,所有的输入扩展流都扩展于 InputStream。
    InputStream 定义了基本的数据读取方法 read 其读取规则与 RAF 中的 read 规则一致。
    InputStream 是抽象类,不能直接使用,使用的都是其子类。
    OutputStream 是所有字节输出流的父类,其定义了基础的写出方法,常用的方法如下:
    void write(int d)
    写出一个字节,写的是给定的 int 的 低八位 ” ”
    void write(byte[] d)
    将给定的字节数组中的所有字节全部写出
    总结:
    文件输出流是 OutputStream 的子类,实现了其约定的抽象方法:write
    使用文件流可以向文件写出数据。
    案例:
    public class Demo05 {
    public static void main (String[] args) throws Exception{
    //打开被复制的文件(被读取的文件)
    FileInputStream in =new FileInputStream("demo.txt");
    //打开被写入的文件(复制目标文件)
    FileOutputStream out =new FileOutputStream(
    "newOne.txt");
    int b;//b 代表文件中的每个 byte
    while((b = in.read())!=-1){
    out.write(b);
    }
    in.close();
    out.close();
    }
    }
    1.2. 文件流
    1.2.1. 创建 FOS 对象(重写模式)
    FileOutputStream 是文件的字节输出流,我们使用该流可以以字节为单位将数据写入文件。
    1.构造方法:
    FileOutputStream(File file)
    创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
    例如:
    FIle file = new File("demo.dat");
    FileOutputStream fos = new FileOutputStream(file);
    2.构造方法:
    FileOutputStream(String filename):
    创建一个向具有指定名称的文件中写入数据的输出文 件流。
    例如:
    FileOutputStream fos = new FileOutputStream("demo.dat");
    这里需要注意,若指定的文件已经包含内容,那么当使用 FOS 对其写入数据时,会将该文件中原有数据
    全部清除。
    总结:构造器
    文件流的构造器必须给定被写出的文件名
    • FileOutputStream(Stirng filename)
    • FileOutputStream(File file)
    1.2.2. 创建 FOS 对象(追加模式)
    通过上一节的构造方法创建的 FOS 对文件进行写操作时会覆盖文件中原有数据。若想在文件的原有数据
    之后追加新数据则需要以下构造方法创建 FOS
    1.构造方法:
    FileOutputStream(File file,boolean append)
    创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
    例如:
    File file = new File("demo.dat");
    FileOutputStream fos = new FileOutputStream(file,true);
    2.构造方法:
    FileOutputStream(String filename,boolean append):
    创建一个向具有指定名称的文件中写入数据的输出文 件流。
    例如:
    FileOutputStream fos = new FileOutputStream("demo.dat",true);
    以上两个构造方法中,第二个参数若为 true,那么通过该 FOS 写出的数据都是在文件末尾追加的。
    总结:
    重载的构造器可以按照追加的方式打开文件,打开以后文件的写入位置在文件的末尾,写出数据追加到
    文件的末尾。
    • FileOutputStream(Stirng filename, boolean append )
    • FileOutputStream(File file, boolean append )
    文件输出流在打开文件时候,如果文件不存在就创建文件,如果不是追加方式时候就将文件的
    内容清空,重写写文件。如果文件名是一个目录,这时候抛出异常!
    1.2.3. 创建 FIS 对象
    FileInputStream 是文件的字节输入流,我们使用该流可以以字节为单位读取文件内容。
    FileInputStream 有两个常用的构造方法:
    1. FileInputStream(File file):
    创建用于读取给定的 File 对象所表示的文件 FIS
    例如:
    File file = new File("demo.dat");
    FileInputStream fis = new FileInputStream(file);//创建一个用于读取 demo.dat 文件的输
    入流
    2.另一个构造方法:
    FileInputStream(String name):
    创建用于读取给定的文件系统中的路径名 name 所指定的文件的 FIS
    例如
    FileInputStream fis = new FileInputStream("demo"); //创建一个用于读取 demo.dat 文件的
    输入流
    总结:
    • FileInputStream(File file)
    • FileInputStream(String filename)
    FileInputStream 构造器用于以读取的方式打开文件,如果文件不存在(或者是目录)则抛
    出文件没有找到的异常!
    1.2.4. read()和 write(int d)方法
    FileInputStream 继承自 InputStream,其提供了以字节为单位读取文件数据的方法 read。
    int read()
    从此输入流中读取一个数据字节,若返回-1 则表示 EOF(End Of File)
    FileOutputStream 继承自 OutputStream,其提供了以字节为单位向文件写数据的方法 write。
    void write(int d)
    将指定字节写入此文件输出流。,这里只写给定的 int 值的 低八位 ” ”
    例如
    FileOutputStream fos = new FileOutputStream("demo.dat");
    fos.write('A');//这里要注意,char 占用 2 个字节,但这里只写入了 1 个字节。
    1.2.5. read(byte[] d)和 write(byte[] d)方法
    FileInputStream 也支持批量读取字节数据的方法:
    int read(byte[] b)
    从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中 。
    FileOutputStream 也支持批量写出字节数据的方法:
    void write(byte[] d)
    将 b.length 个字节从指定 byte 数组写入此文件输出流中。
    例如:
    FileOutputStream fos = new FileOutputStream("demo.txt");
    byte[] data = "HelloWorld".getBytes();
    fos.write(data);//会将 HelloWorld 的所有字节写入文件。
    将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流的方法:
    void write(byte[] d,int offset,int len)
    例如:
    FileOutputStream fos = new FileOutputStream("demo.txt");
    byte[] data = "HelloWorld".getBytes();
    fos.write(data,5,5);//只会将 world 这 5 个字节写入文件。
    三.缓冲流
    缓冲流是一种扩展流,其在基本的 byte 节点流的基础上扩展了自动化缓冲区管理功能。使用缓冲流可以
    简化缓冲区的管理,提高流的 IO 性能。工作中使用的非常广泛。
    缓冲流有两个类 BufferedInputStream 和 BufferedOutputStream 分别是输入缓冲流和输出
    缓冲流。输入缓冲流为输入流扩展了缓冲区功能,输出缓冲流为输入流扩展了缓冲区功能。
    1.BufferedOutputStream
    1. BOS 基本工作原理
    与输入流相似,在向硬件设备做写出操作时,增大写出次数无疑也会降低写出效率,为此我们可以使用
    缓冲输出流来一次性批量写出若干数据减少写出次数来提高写 出效率。
    BufferedOutputStream 缓冲输出流内部也维护着一个缓冲区,每当我们向该流写数据时,都会先将数据
    存入缓冲区,当缓冲区已满时,缓冲流会将数据一次性全部写出。
    2. BOS 的 flush 方法
    使用缓冲输出流可以提高写出效率,但是这也存在着一个问题,就是写出数据缺乏即时性。有时我们需
    要在执行完某些写出操作后,就希望将这些数据确实写出,而非在缓冲区中保存直到缓冲区满后才写出。
    这时我们可以使用缓冲流的一个方法 flush。
    void flush()
    清空缓冲区,将缓冲区中的数据强制写出。
    案例:
    public class Demo07 {
    public static void main(String[] args)
    throws Exception{
    FileOutputStream fos = new FileOutputStream("test.txt");
    //创建缓冲输出流 bos,必须依赖于节点流 fos
    BufferedOutputStream bos =new BufferedOutputStream(fos);
    //bos 的写出方法先将数据写到缓冲区
    //当缓冲区满了再写到文件中。
    bos.write("HelloWorld".getBytes());
    //flush 方法作用:检查缓冲区中是否有数据,
    //如果有数据就将数据强行写文件。
    bos.flush();
    //bos 关闭方法先检查缓冲区是否有数据,如果有数据
    //将数据写到文件中,再关闭文件。
    bos.close();
    }
    }
    2. BufferedInputStream
    1. BIS 基本工作原理
    在读取数据时若以字节为单位读取数据,会导致读取次数过于频繁从而大大的降低读取效率。为此我们
    可以通过提高一次读取的字节数量减少读写次数来提高读取的效率。
    BufferedInputStream 是缓冲字节输入流。其内部维护着一个缓冲区(字节数组),使用该流在读取一个字
    节时,该流会尽 可能多的一次性读取若干字节并存入缓冲区,然后逐一的将字节返回,直到缓冲区中的
    数据被全部读取完毕,会再次读取若干字节从而反复。这样就减少了读取的次 数,从而提高了读取效率。
    BIS 是一个处理流,该流为我们提供了缓冲功能。
    2. BIS 实现输入缓冲
    使用缓冲流来实现文件复制:
    FileInputStream fis = new FileInputStream("java.zip");
    BufferedInputStream bis = new BufferedInputStream(fis);
    FileOutputStream fos = new FileOutputStream("copy_java.zip");
    BufferedOutputStream bos = new BufferedOutputStream(fos);
    int d = -1;
    while((d = bis.read())!=-1){
    bos.write(d);
    }
    bis.close();//读写完毕后要关闭流,只需要关闭最外层的流即可
    bos.close();
    四. 对象流
    ObjectOutputStream
    ObjectInpputStream
    对象流是高级流,不能单独工作,必须依赖低级流(低级流做为构造器参数),对象流提供了对象的序
    列化和反序列化功能,可以将对象序列化为 byte 数据,或将 byte 数据反序列化为对象。
    对象流要求被序列化的对象必须实现序列化接口,如果不实现序列化接口,在序列化时候会出现运
    行异常。
    对象序列化概念
    对象是存在于内存中的。有时候我们需要将对象保存到硬盘上,又有时我们需要将对象传输到另一台计
    算机上等等这样的操作。这时我们需要将 对象转换为一个字节序列,而这个过程就称为对象序列化。相
    反,我们有这样一个字节序列需要将其转换为对应的对象,这个过程就称为对象的反序列化。
    1. 使用 OOS 实现对象序列化
    ObjectOutputStream 是用来对对象进行序列化的输出流。
    其实现对象序列化的方法为:
    void writeObject(Object o)
    该方法可以将给定的对象转换为一个字节序列后写出。
    例如:
    Emp emp = new Emp("张三",12,"男");
    FileOutputStream fos = new FileOutputStream("Emp.obj");
    ObjectOutputStream oos = new ObjectOutputStream(fos);
    oos.writeObject(emp);//将 emp 对象序列化后写入文件
    oos.close();
    2. 使用 OIS 实现对象反序列化
    ObjectInputStream 是用来对对象进行反序列化的输入流。
    其实现对象反序列化的方法为:
    Object readObject()
    该方法可以从流中读取字节并转换为对应的对象。
    例如:
    FileInputStream fis = new FileInputStream("Emp.obj");
    ObjectInputStream ois = new ObjectInputStream(fis);
    Emp emp = (Emp)ois.readObject();//将 Emp 对象从文件中读取并反序列....
    ois.close();
    3. Serializable 接口
    ObjectOutputStream 在对对象进行序列化时有一个要求,就是需要序列化的对象所属的类必须实现
    Serializable 接口。
    实现该接口不需要重写任何方法。其只是作为可序列化的标志。
    通常实现该接口的类需要提供一个常量 serialVersionUID,表明该类的版本。若不显示的声明,在对象
    序列化时也会根据当前类的各个方面计算该类的默认 serialVersionUID,但不同平台编译器实现有所不
    同,所以若向跨平台,都应显示的声明版本号。
    如果声明的类序列化存到硬盘上面,之后随着需求的变化更改了类别的属性(增加或减少或改名),那么当
    反序列化时,就会出现 InvalidClassException,这样就会造成不兼容性的问题。 但当
    serialVersionUID 相同时,它就会将不一样的 field 以 type 的预设值反序列化,可避开不兼容性问题。
    例如:
    public class Emp implements Serializable{
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;
    private String gender;
    //getter and setter and other...}
    4. transient 关键字
    对象在序列化后得到的字节序列往往比较大,有时我们在对一个对象进行序列化时可以忽略某些不必要
    的属性,从而对序列化后得到的字节序列 瘦” ” 身 。
    关键字 transient 的作用:
    避免数据的暴涨,忽略其不重要的数据。
    例如:
    public class Emp implements Serializable{
    private static final long serialVersionUID = 1L;
    private String name;
    private transient int age;//该属性在序列化时会被忽略
    private String gender;//getter and setter and other...
    }
    五.字符流 Reader 和 Writer
    1. 字符流原理
    Reader 是所有字符输入流的父类,而 Writer 是所有字符输出流的父类。字符流是以字符(char)为单位读
    写数据的。一次处理一个 unicode。字符流都是高级流,其底层都是依靠字节流进行读写数据的,所以底
    层仍然是基于字节读写数据的。
    2. 常用方法
    Reader 的常用方法:
    int read()
    读取一个字符,返回的 int”值低 16”位有效。
    int read(char[] chs)
    从该流中读取一个字符数组 length 个字符并存入该数组,返回值为实际读取到的字符量。
    Writer 的常用方法:
    void write(int c)
    写出一个字符,写出给定 int 值 低” 16”位表示的字符。
    void write(char[] chs)
    将给定字符数组中所有字符写出。
    void write(String str)
    将给定的字符串写出
    void write(char[] chs,int offset,int len):
    将给定的字符数组中从 offset 处开始连续的 len 个字符写出
    六. 转换流
    1. 字符转换流原理
    InputStreamReader:字符输入流, 使用该流可以设置字符集,并按照指定的字符集从流中按照该编码
    将字节数据转换为字符并读取。
    OutputStreamWriter:字符输出流,使用该流可以设置字符集,并按照指定的字符集将字符转换为对应字
    节后通过该流写出。
    2. 指定字符编码
    InputStreamReader 的构造方法允许我们设置字符集:
    InputStreamReader(InputStream in,String charsetName)
    基于给定的字节输入流以及字符编码创建 ISR
    InputStreamReader(InputStream in)
    该构造方法会根据系统默认字符集创建 ISR
    OutputStreamWriter:的构造方法:
    OutputStreamWriter(OutputStream out,String charsetName)
    基于给定的字节输出流以及字符编码创建 OSW
    OutputStreamWriter(OutputStream out)
    该构造方法会根据系统默认字符集创建 OSW
    3. 使用 OutputStreamWriter
    public void testOutput() throws IOException{
    FileOutputStream fos = new FileOutputStream("demo.txt");
    OutputStreamWriter writer= new OutputStreamWriter(fos,"UTF-8");//这里使用的字符编码
    为 UTF-8
    String str = "大家好!";//UTF-8 中文为 3 个字节,英文符号占 1 个字节
    writer.write(str);//写出后该文件大小应该为 10 字节
    writer.close();
    }
    4. 使用 InputStreamReader
    public void testInput() throws IOException{
    FileInputStream fis = new FileInputStream("demo.txt");
    /** 这里设置了字符编码为 GBK* 之后再通过 ISR 读取 demo.txt 文件时* 就使用 GBK 编码读取
    字符了*/
    InputStreamReader reader= new InputStreamReader(fis,"GBK");
    int c = -1;
    while((c = reader.read()) != -1){
    System.out.print((char)c);
    }
    reader.close();
    }

  • 相关阅读:
    新年后的第一个学习总结
    2021/02/07周学习总结
    内网穿透
    有效的括号
    实现一个简单的模板字符串替换
    二叉树的最大深度
    前端性能和错误监控
    前端缓存
    display: none; opacity: 0; visibility: hidden;
    发布订阅模式与观察者模式
  • 原文地址:https://www.cnblogs.com/caoyingjielxq/p/9280533.html
Copyright © 2020-2023  润新知