【学习笔记】Java基础知识点——第9章·IO流

第9章  IO流 

9.1  Java流概述

9.1.1 InputStream和OutStream继承结构图:

 9.1.2 Reader和Writer继承结构图:

9.1.3 IO流的分类

有多种分类方式:

        一种方式是按照流的方向进行分类:以内存作为参照物,

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

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

        另一种方式是按照读取数据方式不同进行分类:

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

                                        这种流是万能的,什么类型的文件都可以读取。包括:文本文件,图片,声音文件,视频文件等....

                                        假设文件file1.txt,采用字节流的话是这样读的:a中国bc张三fe

                                                        第一次读:一个字节,正好读到'a'。

                                                        第二次读:一个字节,正好读到'中'字符的一半。

                                                        第三次读:一个字节,正好读到'中'字符的另外一半。

                        ②有的流是按照字符的方式读取数据的,一次读取一个字符,

                                        这种流是为了方便读取普通文本文件而存在的,这种流只能读取纯文本(word文档不属于纯文本)。

                                        假设文件file1.txt,采用字符流的话是这样读的: a中国bc张三fe

                                                第一次读:'a'字符('a'字符在windows系统中占用1个字节。)

                                                第二次读:'中'字符('中'字符在windows系统中占用2个字节。)

9.1.4 IO流的四大家族:

在java中只要类名以“Stream”结尾的都是字节流。以“Reader/Writer”结尾的都是字符流。

  1. java.io.InputStream  字节输入流
  2. java.io.OutputStream 字节输出流
  3. java.io.Reader 字符输入流
  4. java.io.Writer 字符输出流

所有的流都实现了:java.io.Closeable接口,都是可关闭的,都有close()方法。流毕竟是一个管道,这个是内存和硬盘之间的通道,用完之后一定要关闭,不然会耗费(占用)很多资源。养成好习惯,用完流一定要关闭。

所有的输出流都实现了:java.io.Flushable接口,都是可刷新的,都有flush()方法。养成一个好习惯,输出流在最终输出之后,一定要记得flush()刷新一下。这个刷新表示将通道/管道当中剩余未输出的数据强行输出完(清空管道!),刷新的作用就是清空管道。注意:如果没有flush()可能会导致丢失数据。

9.1.5 java.io包下需要掌握的16个流:

文件专属:

  • java.io.FileInputStream
  • java.io.FileOutputStream
  • java.io.FileReader
  • java.io.FileWriter

转换流:(将字节流转换成字符流)

  • java.io.InputStreamReader
  • java.io.OutputStreamWriter

缓冲流专属:

  • java.io.BufferedReader
  • java.io.BufferedWriter
  • java.io.BufferedInputStream
  • java.io.BufferedOutputStream

数据流专属:

  • java.io.DataInputStream
  • java.io.DataOutputStream

标准输出流:

  • java.io.PrintWriter
  • java.io.PrintStream

对象专属流:

  • java.io.ObjectInputStream
  • java.io.ObjectOutputStream

9.1.6 InputStream(字节输入流)

  1. abstract int read() 从输入流读取下一个数据字节。
  2. int read(byte[] b) 从输入流中读取一定数量的字节并将其存储在缓冲区数组 b 中。
  3. int read(byte[] b, int off, int len) 将输入流中最多 len 个数据字节读入字节数组。

9.1.7 OutputStream(字节输出流)

  1. void write(byte[] b) 将 b.length 个字节从指定的字节数组写入此输出流。
  2. void write(byte[] b, int off, int len) 将指定字节数组中从偏移量 off 开始的 len 个字节写入此输出流。
  3. abstract void write(int b) 将指定的字节写入此输出流。

9.1.8 Reader(字符输入流)

  1. int read() 读取单个字符。
  2. int read(char[] cbuf) 将字符读入数组。
  3. abstract int read(char[] cbuf, int off, int len) 将字符读入数组的某一部分。

9.1.9 Writer(字符输出流)

  1. Writer append(char c) 将指定字符追加到此 writer。
  2. void write(char[] cbuf) 写入字符数组。
  3. abstract void write(char[] cbuf, int off, int len) 写入字符数组的某一部分。
  4. void write(int c) 写入单个字符。

9.2  文件流

9.2.1 FileInputStream(文件字节输入流)

FileInputStream类的其他常用方法:

  • int available():返回流当中剩余没有读到的字节数量。
  • long skip(long n):跳过几个字节不读。
FileInputStream fis = new FileInputStream("绝对/相对路径");
byte[] bytes = new byte[4];
int readCount = 0;
while((readCount = fis.read(bytes)) != -1) {
    System.out.print(new String(bytes, 0, readCount));	//读了几个字节输出几个字节
}

 9.2.2 FileOutputStream(文件字节输出流)

输出的文件不存在的时候会自动创建,并且构造方法后的boolean值决定是追加写入(true)还是覆盖写入(false),默认为false(覆盖写入)。 

FileOutputStream fos = fos =  new FileOutputStream("myfile", true);
byte[] bytes = {97, 98, 99, 100};
// 将byte数组全部写出!
fos.write(bytes); // abcd
// 将byte数组的一部分写出!
fos.write(bytes, 0, 2); // 再写出ab
String s = "我是一个中国人,我骄傲!!!";
// 将字符串转换成byte数组。
byte[] bs = s.getBytes();
fos.write(bs);
// 写完之后,最后一定要刷新
fos.flush();

9.2.3 FileReader(文件字符输入流)

FileReader reader = new FileReader("第10章_IO流/src/temp/example05");
char[] chars = new char[4]; // 一次读取4个字符
int readCount = 0;
while((readCount = reader.read(chars)) != -1) {
    System.out.print(new String(chars,0,readCount));
}

9.2.4 FileWriter(文件字符输出流)

FileWriter out = new FileWriter("file", true);
char[] chars = {'我','是','中','国','人'};
out.write(chars);
out.write(chars, 2, 3);
out.write("我是一名java软件工程师!");
out.flush();

9.3  缓冲流

9.3.1 BufferedReader

带有缓冲区的字符输入流,使用这个流的时候不需要自定义char数组,或者说不需要自定义byte数组,自带缓冲。

构造方法:BufferedReader(Reader in) 创建使用默认大小的输入缓冲区的缓冲字符输入流。

当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做:节点流。

外部负责包装的这个流,叫做:包装流,还有一个名字叫做:处理流。

例如(String readLine() 读一行文字 ):

FileReader reader = new FileReader("documentURL");
BufferedReader br = new BufferedReader(reader);
String s = null;
while((s = br.readLine()) != null){
    System.out.println(s);
}
br.close();

关闭流的时候只需要关闭“包装流”(关闭最外层),里面的“节点流”会自动关闭。

9.3.2 BufferedWriter

void newLine() 写一行行分隔符。

BufferedWriter bw = new BufferedWriter(new FileWriter("第10章_IO流/src/temp/example06", true));
bw.write("打工人打工魂");
bw.flush();
bw.close();

9.4  转换流(将字节流转换成字符流)

9.4.1 InputStreamReader

public class BufferedReaderTest02 {
    public static void main(String[] args) throws Exception{
        /*// 字节流
        FileInputStream in = new FileInputStream("Copy02.java");
        // 通过转换流转换(InputStreamReader将字节流转换成字符流。)
        InputStreamReader reader = new InputStreamReader(in);
        // 这个构造方法只能传一个字符流。不能传字节流。
        BufferedReader br = new BufferedReader(reader);*/
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("第10章_IO流/src/缓冲流/BufferedReaderTest01.java")));
        String line = null;
        while((line = br.readLine()) != null){
            System.out.println(line);
        }
        // 关闭最外层
        br.close();
    }
}

9.4.2 OutputStreamWriter

BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("f", true)));

9.5  数据流

9.5.1 DataOutputStream

java.io.DataOutputStream:数据专属的流。这个流可以将数据连同数据的类型一并写入文件。注意:这个文件不是普通文本文档(这个文件使用记事本打不开。)

public class DataOutputStreamTest {
    public static void main(String[] args) throws Exception{
        // 创建数据专属的字节输出流
        DataOutputStream dos = new DataOutputStream(new FileOutputStream("data"));
        byte b = 100;
        short s = 200;       
        dos.writeByte(b); // 把数据以及数据的类型一并写入到文件当中。
        dos.writeShort(s);
        dos.flush();
        dos.close();
    }
}

9.5.2 DataInputStream

DataInputStream:数据字节输入流。DataOutputStream写的文件,只能使用DataInputStream去读。并且读的时候你需要提前知道写入的顺序。读的顺序需要和写的顺序一致。才可以正常取出数据。

public class DataInputStreamTest01 {
    public static void main(String[] args) throws Exception{
        DataInputStream dis = new DataInputStream(new FileInputStream("data"));
        // 开始读
        byte b = dis.readByte();
        short s = dis.readShort();
        System.out.println(b);
        dis.close();
    }
}

9.6  标准输出流

9.6.1 PrintStream

java.io.PrintStream:标准的字节输出流。默认输出到控制台。标准输出流不需要手动close()关闭,并且可以改变标准输出流方向。

public class PrintStreamTest {
    public static void main(String[] args) throws Exception{
        // 联合起来写
        System.out.println("hello world!");
        // 分开写
        PrintStream ps = System.out;
        ps.println("hello 这是输出在控制台上");
        // 标准输出流不再指向控制台,指向“log”文件。
        PrintStream printStream = new PrintStream(new FileOutputStream("第10章_IO流/src/temp/log"));
        // 修改输出方向,将输出方向修改到"log"文件。
        System.setOut(printStream);
        // 再输出
        System.out.println("此语句输出至\"第10章_IO流/src/temp/log\"中");
    }
}

9.6.2 PrintWriter

打印流主要包含两个:PrintStream 和 PrintWriter,分别对应字节流和字符流。

public class PrintWriterTest01 {
    public static void main(String[] args) {
        try {
            PrintWriter pw = new PrintWriter(new FileWriter("第10章_IO流/src/temp/log",true));
            pw.println("PrintWriter是个标准字符输出流");
            pw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

9.7  File类

File类和四大家族没有关系,所以File类不能完成文件的读和写。File对象代表文件和目录路径名的抽象表示形式。一个File对象有可能对应的是目录,也可能是文件。File只是一个路径名的抽象表示形式。

构造方法:File(String pathname) 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。

需要掌握File类中常用的方法:

  1. boolean createNewFile() 当且仅当具有该名称的文件尚不存在时,创建一个由该抽象路径名命名的新的空文件。
  2. boolean delete() 删除由此抽象路径名表示的文件或目录。
  3. boolean exists() 测试此抽象路径名表示的文件或目录是否存在。
  4. File getAbsoluteFile() 返回此抽象路径名的绝对形式。
  5. String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串。
  6. String getParent() 返回此抽象路径名的父 null的路径名字符串,如果此路径名未命名为父目录,则返回null。  
  7. File getParentFile() 返回此抽象路径名的父,或抽象路径名 null如果此路径名没有指定父目录。  
  8. String getPath() 将此抽象路径名转换为路径名字符串。  
  9. boolean mkdir() 创建由此抽象路径名命名的目录。  
  10. boolean mkdirs() 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录。  
  11. boolean isDirectory() 测试此抽象路径名表示的文件是否为目录。  
  12. boolean isFile() 测试此抽象路径名表示的文件是否为普通文件。  
  13. long lastModified() 返回此抽象路径名表示的文件上次修改的时间。  
  14. long length() 返回由此抽象路径名表示的文件的长度。
  15. File[] listFiles() 返回一个抽象路径名数组,表示由该抽象路径名表示的目录中的文件。

 9.8  对象专属流

9.8.1 序列化与反序列化

参与序列化和反序列化的对象,必须实现Serializable接口:

public interface Serializable {}

注意:通过源代码发现,Serializable接口只是一个标志接口:这个接口当中什么代码都没有。起到标识的作用,标志的作用,java虚拟机看到这个类实现了这个接口,可能会对这个类进行特殊待遇。

Serializable这个标志接口是给java虚拟机参考的,java虚拟机看到这个接口之后,会为该类自动生成一个序列化版本号。

1.transient

采用 transient 关键字修饰某一属性,序列化时会忽略该属性。

2.serialVersionUID

Java语言中是采用什么机制来区分类的?

  •  首先通过类名进行比对,如果类名不一样,肯定不是同一个类。
  •  如果类名一样,再怎么进行类的区别?靠序列化版本号进行区分。

这种自动生成的序列化版本号缺点是:一旦代码确定之后,不能进行后续的修改,因为只要修改,必然会重新编译,此时会生成全新的序列化版本号,这个时候java虚拟机会认为这是一个全新的类。

最终结论:凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号。这样,以后这个类即使代码修改了,但是版本号不变,java虚拟机会认为是同一个类,便不会报错

9.8.2 ObjectInputStream与ObjectOutputStream

定义一个User类(继承了Serializable接口):

public class User implements Serializable {
    private int no;
    // transient关键字表示游离的,不参与序列化。
    private transient String name; 
    public User(int no, String name) {
        this.no = no;
        this.name = name;
    }
    @Override
    public String toString() {
        return "User{" +
                "no=" + no +
                ", name='" + name +
                '}';
    }
}

一次序列化多个对象,可以将对象放置集合中,序列化集合:

public class ObjectOutputStreamTest02 {
    public static void main(String[] args) throws Exception{
        List<User> userList = new ArrayList<>();
        userList.add(new User(1,"zhangsan"));
        userList.add(new User(2, "lisi"));
        userList.add(new User(3, "wangwu"));
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("第10章_IO流/src/对象类/序列化与反序列化/users"));
        // 序列化一个集合,这个集合对象中放了很多其他对象。
        oos.writeObject(userList);
        oos.flush();
        oos.close();
    }
}

反序列化集合:

public class ObjectInputStreamTest02 {
    public static void main(String[] args) throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("第10章_IO流/src/对象类/序列化与反序列化/users"));
        //Object obj = ois.readObject();
        //System.out.println(obj instanceof List);  //true
        List<User> userList = (List<User>)ois.readObject();
        for(User user : userList){
            System.out.println(user);
        }
        ois.close();
    }
}

自动生成的序列号一经确认再修改时,会重新编译,生成新的序列化版本号,JVM认为这是一个全新的类便会报错,因此在User类中声明一个固定的序列号版本号:

private static final long serialVersionUID = -111111111111111222L;

9.9  IO和Properties联合使用

IO+Properties的联合应用是非常好的一个设计理念: 以后经常改变的数据,可以单独写到一个文件中,使用程序动态读取。将来只需要修改这个文件的内容,java代码不需要改动,不需要重新编译,服务器也不需要重启,就可以拿到动态的信息。

类似于以上机制的这种文件被称为配置文件。并且当配置文件中的内容格式是:“key1=value”的时候,我们把这种配置文件叫做属性配置文件。

Java规范中有要求:属性配置文件建议以.properties结尾,但这不是必须的。这种以.properties结尾的文件在java中被称为:属性配置文件。其中Properties是专门存放属性配置文件内容的一个类。配置文件最好“=”两边不要有空格。

public class IoPropertiesTest01 {
    public static void main(String[] args) throws Exception{
        /*
        Properties是一个Map集合,key和value都是String类型。
        想将userinfo文件中的数据加载到Properties对象当中。
         */
        // 新建一个输入流对象
        FileReader reader = new FileReader("chapter23/userinfo.properties");

        // 新建一个Map集合
        Properties pro = new Properties();

        // 调用Properties对象的load方法将文件中的数据加载到Map集合中。
        pro.load(reader); // 文件中的数据顺着管道加载到Map集合中,其中等号=左边做key,右边做value

        String username = pro.getProperty("username");
        System.out.println(username);

        String password = pro.getProperty("password");
        System.out.println(password);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值