A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

线程间通信                                      day-08笔记线程间通信介绍
线程间通信: 多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同(需要协作)
为什么要进行线程间通信:        通常是竞争关系: 多个线程并发执行时, 在默认情况下CPU是随机切换线程的.        有时也需要合作: 当我们需要多个线程来共同完成一件任务, 并且我们希望他们有规律的执行, 那么多线程之间需要一些协调通信, 以此来帮我们达到多线程共同操作一份数据
如何保证线程间通信有效利用资源:        等待唤醒机制
等待唤醒机制介绍等待唤醒机制:
​        一个线程进行了规定操作后, 就进入等待状态( wait() ), 等待其他线程执行完他们的指定代码过后, 再将其唤醒( notify() )    在有多个线程进行等待时, 如果需要, 可以使用 notifyAll() 来唤醒所有的等待线程        *`<!--wait/notify 就是线程间的一种协作机制, 用于解决线程间通信的问题-->
等待唤醒中的方法
java.lang.Object类:        // 成员方法 (只能通过"锁对象"调用)        void notify(): 随机唤醒在同一个锁对象上的某一个处于等待状态的线程        void notifyAll(): 唤醒所有在同一个锁对象上处于等待状态的线程        void wait(): 让当前线程处于无限等待状态, 同时释放锁
wait和notify/notifyAll的执行原理:
​        wait:                线程不再活动, 不再参与调度, 进入 wait set 中, 因此不会浪费 CPU 资源, 也不会去竞争锁, 这时的线程状态即是"WAITING". 它还要等着别的线程执行"通知(notify)", 让在锁对象上等待的线程从 wait set 中释放出来, 重新进入到调度队列(ready queue)中        notify/notifyAll:                哪怕只通知了一个等待的线程, 被通知线程也不能立即恢复执行, 因为它当初中断的地方是在同步块内, 而此刻它已经不持有锁, 所以它需要再次尝试去获取锁(很可能面临其它线程的竞争), 成功后才能在当初调用 wait() 之后的地方恢复执行                总结如下:                        如果能获取锁, 线程就从"WAITING"状态变成"RUNNABLE"状态                        否则, 从 wait set 出来, 又进入 entry set, 线程就从"WAITING"状态又变成"BLOCKED"状态
调用 wait() 和 notify() 需要注意的细节:    1. wait() 与 notify() 必须要由"同一个锁对象"调用
          应的锁对象可以通过 notify() 唤醒使用同一个锁对象调用的 wait() 后的线程

    2. wait() 与 notify() 是属于Object类的方法
          对象可以是任意对象, 而任意对象的所属类都是继承了Object类的

    3. wait() 与 notify() 必须要在"同步代码块"或者是"同步方法"中使用
          须要通过锁对象调用这2个方法

          ```

          ```

          ### **线程池****线程池概念和原理**

          ##### 普通创建线程方式的缺点:

                  "创建"线程和"销毁"线程都是比较占用内存和CPU的操作.
                  对于一些数 量多, 执行时间短的任务, 频繁的创建和销毁线程来执行, 会降低程序运行效率.
                  
          线程池:
                  一个容纳多个线程的容器
                  
          线程池可以解决的问题:
                  其中的线程可以反复使用, 省去了频繁创建线程对象的操作, 无需反复创建线程而消耗过多资源
                  

          ##### 线程池的工作原理:

                  提前创建好多个线程对象, 放在集合中. 多个任务来了反复使用这些线程对象来执行

          ##### **线程池的代码实现**

                               一哥载Q特儿斯
          java.util.concurrent.Executors类: 线程池工厂类, 用于管理线程池
                  // 静态方法:
                  static ExecutorService newFixedThreadPool(int nThreads): 创建固定数量线程的线程池(常用)
                  
                               一哥载Q特儿 色儿微斯
          java.util.concurrent.ExecutorService接口: 真正执行任务的线程池服务
                  // 成员方法:
                  Future submit(Runnable task): 提交一个Runnable任务
                  void shutdown(): 通知线程执行完任务后关闭. 如不调此方法, 则线程执行 完任务后仍在运行以便重复使用
                  

          ##### 线程池的创建和使用步骤:

                  1. 使用Executors的静态方法 newFixedThreadPool(int nThreads) 创建线程池ExecutorService

                  2. 创建一个任务类, 实现Runnable接口, 重写run()方法

                  3. 调用ExecutorService对象的 submit(Runnable task) 方法, 传递任务给线程池, 执行任务

                  4. 调用ExecutorService对象的 shutdown() 方法, 销毁线程池 (不建议执行)
函数式编程思想函数式编程思想Lambda表达式函数式编程思想概述
函数式:         在数学中, 函数就是有输入量, 输出量的一套计算方案, 也就是"拿什么东西做什么事情"
面向对象: 强调"用哪个对象的哪个方法"来做事 (注重语法形式)函数式: 强调做事 (不关心用什么对象, 重写什么方法)
函数式编程的好处: 简化代码编写
冗余的Runnable代码

// 我们要通过Lambda表达式简化以下的代码
new Thread(new Runnable() {
    @Override
    public void run() {
        // 要执行的代码才是重要的
    }
}).start();
关键代码是: run()方法中要执行的任务而其他代码都只是形式
编程思想的转换, 体验Lambda更优写法
JDK 8 中加入的Lambda表达式, 是函数式编程思想中的重点
对比:
// 面向对象方式的代码
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + "新线程创建了");
        }
    }).start();


    // 函数式编程的代码
    new Thread(()-> {
            System.out.println(Thread.currentThread().getName() + "新线程创建了");
        }
    ).start();
格式:
    (参数列表)->{一些重要的方法代码};
解析说明格式:
():接口中抽象方法的参数列表,没有参数,就空着;有参数就写出参数,多个参数使用逗号分隔
->: 传递的意思,把参数传递给方法体{}
{}:重写接口的抽象方法的方法体
Lambda标准格式Lambda表达式的3个部分:

1. 一些参数 ()
            接口中抽象方法的参数列表. 没参数就空着; 有参数就写, 多个参数用逗号分隔
2. 一个箭头 ->
            将参数传递给方法体
3. 一段代码 {}
            重写接口抽象方法的方法体格式:
​        // 写成一行        (参数列表) -> {一些重写方法的代码}        // 写成多行        (参数列表) -> {        一些重写方法的代码    }
Lambda省略格式和使用前提省略原则:
​        可推导的都可省略 (凡是能根据前后代码能猜测出来的代码, 都可以省略不写)
可以省略的部分:
    1. (参数列表): 参数"类型"可以省略
    2. (参数列表): 如果参数只有1个, 则"类型"和"小括号"都可以省略  a -> sout(a)
    3. {一些代码}: 如果只有一条代码, 则"大括号", "return", "分号"都可以"一起省略"*函数式接口:*函数式接口:
​        函数式接口"有且仅有一个抽象方法"        但函数式接口对于哪些方法算作抽象方法有特殊规定:

            1. 有方法体的方法"不算作"抽象方法, 如默认方法, 静态方法, 私有方法
            2. 抽象方法与java.lang.Object类中的方法定义相同的, 也"不算作"抽象方法
                    因为任何实现本接口的实现类, 都会直接或间接继承java.lang.Object类的public的方法, 所以在创建实现类时其实不用重写该抽象方法, 也就不算作抽象方法Lambda表达式的使用前提:Lambda表达式的使用前提:
1. Lambda只能用于接口, 且"接口中有且仅有一个抽象方法"(也称为"函数式接口")
    普通类, 抽象类不能用
2. 使用Lambda必须具有上下文推断
    方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例
    (简而言之: 作为参数类型的接口, 必须是函数式接口)
​        比如以下构造方法​        比如以下构造方法        Thread(Runnable r): 该构造方法的参数类型是Runnable
Day-09-10笔记:

I

java.io.OutputStream抽象类: 字节输出流 (顶层类)
    // 成员方法
    void close() :释放资源
    void flush() :刷新缓冲区(对于字节流来说没有作用)
    // 写字节的成员方法
    abstract void write(int b): 一次写一个字节 (参数int便于传递byte的整数不用强转)
    void write(byte[] b): 一次写一个字节数组
    void write(byte[] b, int offset, int len): 一次写一个字节数组的一部分
   
java.io.FileOutputStream类: 文件字节输出流 (向文件写数据)
    // 构造方法
    FileOutputStream(String name): 通过文件路径创建文件字节输出流
    FileOutputStream(File file): 通过File对象创建文件字节输出流
    构造方法的作用:
        1. 创建一个FileOutputStream对象
        2. 根据构造方法传递的路径, 在磁盘上创建一个空文件 ("如果文件存在则会清空数据")
        3. 将创建的FileOutputStream对象指向这个磁盘上的文件
补充:

字节输出流: 一次写一个字节到文件
知识点:

了解Java程序将数据写入文件的原理
FileOutputStream写数据到文件需要哪3步
总结:

写数据的原理:
    Java程序 -> JVM虚拟机 -> OS操作系统 -> OS调用写数据的方法 -> 将数据写入文件
   
使用字节输出流写数据到文件的步骤:
    1. 创建对象: 创建FileOutputStream对象, 构造方法中传递文件路径
    2. 写入数据: 使用FileOutputStream对象调用 write(int b) 方法, 将字节写入文件
    3. 释放资源: 使用FileOutputStream对象调用 close() 方法, 关闭流对象释放资源
   
    FileOutputStream fos = new FileOutputStream("模块名\\文件名.txt");
    fos.write(97);
    fos.close();



java.io.FileOutputStream类: 文件字节输出流 (向文件写数据)
    void write(byte[] b): 一次写一个字节数组
    void write(byte[] b, int off, int len): 一次写一个字节数组的一部分
   
   
   
Java中, byte的范围是 -128 ~ 127 共256个数值
编码表中字符范围是       0 ~ 255 共256个数值
   
写多个字节时:
    如果第一个字节是正数中的 0~127, 则记事本会查询 ASCII码表 显示字符
    如果第一个字节是负数中的:-128~-1, 则记事本会查询 GBK码表 显示字符.(将两个连续的byte组合为一个中文)
补充:

FileOutputStream fos = new FileOutputStream(new File("09_IOAndProperties\\b.txt"));
        
byte[] bytes2 = "你好".getBytes();
System.out.println(Arrays.toString(bytes2));//[-28, -67, -96, -27, -91, -67]
fos.write(bytes2);

//释放资源
fos.close();
字节输出流: 续写, 换行
知识点:

如何在文件已有内容之后, 接着写入内容 (续写)
如何在文件中写入换行, Windows系统的换行符号是什么
总结:

java.io.FileOutputStream类: 文件字节输出流
    // 带有 续写 功能的构造方法, 不会清空文件
    FileOutputStream(String name, boolean append): 通过文件路径创建流, true可以续写
    FileOutputStream(File file, boolean append): 通过File对象创建流, true可以续写
   
换行符:
    Windows系统: "\r\n"
    Linux系统: "\n"
    MacOS系统: "\r"
补充:

5分钟练习: 续写和换行
需求:
创建FileOutputStream对象, 指向当前模块下的testOutputStream2.txt文件, 并开启续写
for循环5次, 每次循环写入"你好"的byte数组, 并写入windows的换行符的byte数组
循环结束后释放资源
运行程序查看效果, 再次运行再次查看效果
代码:

public class Test {
    public static void main(String[] args) throws IOException {
        // 创建文件字节输出流对象
        FileOutputStream fos = new FileOutputStream("day09\\testOutputStream2.txt", true);  // 追加写

        // 循环写入5行数据
        for (int i = 0; i < 5; i++) {
            // 写入文字
            fos.write("你好".getBytes());
            // 在文字后写入换行符
            fos.write("\r\n".getBytes());
        }

        // 释放资源
        fos.close();
    }
}
字节输入流: InputStream和FileInputStream
知识点:

InputStream抽象类 有哪些共性的方法
FileInputStream普通类 可以做什么
总结:

java.io.InputStream抽象类: 字节输入流 (顶层类)
    // 常用成员方法
    void close(): 释放资源
    // 读数据的方法
    int read(): 一次读一个字节
        读到文件末尾返回-1 (返回int也是为了代码编写方便)
    int read(byte[] b): 一次读一个字节数组
        读到的字节存放在参数中的字节数组中, 返回int值是本次读到的字节的个数. 读到文件末尾返回-1
   
java.io.FileInputStream类: 文件字节输入流
    // 构造方法
    FileInputStream(String name): 使用文件路径创建文件字节输入流
    FileInputStream(File file): 使用File对象创建文件字节输入流
    构造方法的作用:
        1. 创建FileInputStream对象
        2. 将FileInputStream对象指向磁盘上的文件
补充:

字节输入流: 一次读取一个字节
知识点:

了解Java程序从文件读取数据的原理
FileInputStream读数据需要哪3步
总结:

Java程序从文件读取数据的原理:
    Java程序 -> JVM虚拟机 -> OS(操作系统) -> OS调用读取的方法 -> 读取磁盘文件数据
   
java.io.InputStream抽象类: 字节输入流 (顶层类)
    int read(): 一次读一个字节
        读到文件末尾返回-1 (返回int也是为了代码编写方便)
   
   
FileInputStream读数据步骤:
    1. 创建FileInputStream对象
    2. 使用FileInputStream对象, 调用 int read() 方法, 一次读取一个byte
    3. 释放资源
   
    FileInputStream fis = new FileInputStream("模块名\\文件名");
    // 一次读一个字节:
    int by;   // int变量用来存储每次读到的数据
    while (   (  by = fis.read()  ) != -1    ) {
        System.out.print((char)by);  // 不要换行, 文件中自带换行的byte
    }

    fis.close();
补充:

5分钟练习: 一次读取一个字节
需求:
在当前模块中, 手动创建一个文件testInputStream1.txt, 双击打开直接在里面写入abc三个字母.
使用FileInputStream读取该文件, 一次读取一个byte, 每次将读到的byte强转为char后, 打印到控制台
代码:

public class Test {
    public static void main(String[] args) throws IOException {
        // 1. 创建文件字节输入流对象
        FileInputStream fis = new FileInputStream("day09\\testInputStream1.txt");

        // 2. 读取文件: 一次读取一个字节
        int by;                              // 定义一个变量, 用于存储每次读到的字节
        while ((by = fis.read()) != -1) {    // 循环读取, 如果不是-1, 则说明读到了数据, 在循环体中操作; 如果是-1, 则循环结束
            // 将读取到的字节从int类型强转为char, 便于我们看懂字符, 打印出来
            System.out.println((char)by);
        }

        // 3. 释放资源
        fis.close();
    }
}
字节输入流: 一次读取一个字节原理
知识点:
字节输入流一次读取一个字节是如何实现的

总结:

补充:

字节输入流: 一次一个字节数组
知识点:

如何一次读取多个byte
一次读取多个byte是如何实现的, 相比于一次读取一个byte效率上哪个更高

补充:
java.lang.String类:
    String(byte[] bytes): 使用平台默认字符集解码将byte数组转换为String
    String(byte[] bytes, int offset, int length): 使用平台默认字符集将数组一部分转换为String
总结:

java.io.InputStream抽象类: 字节输入流 (顶层类)
    int read(byte[] b): 一次读一个字节数组
        参数 byte[] b 作用: 存储每次读到的字节, 起到缓冲作用
        返回值作用: 每次读到的字节个数, 读到文件末尾返回-1


   
// 示例代码
FileInputStream fis = new FileInputStream("模块名\\文件名");

byte[] bytes = new byte[1024];  // 定义字节数组, 用于保存每次读取到的数据
int len;                        // 定义int变量, 用于保存每次读取到的长度
while ((len = fis.read(bytes)) != -1) {
    String s = new String(bytes, 0, len); // 将读取到的字节转换为字符串, 读到多少个就转换多少个
    System.out.print(s);
}

fis.close(); // 释放资源
补充:

5分钟练习: 一次读取一个字节数组
需求:
在当前模块中, 手动创建一个文件testInputStream2.txt, 双击打开直接在里面写入ABCDE五个字母.
使用FileInputStream读取该文件, 使用一次读写一个字节数组的方式, 每次读到的数组转换为String, 打印到控制台
代码:

public class Test {
    public static void main(String[] args) throws IOException {
        // 创建文件字节输入流对象
        FileInputStream fis = new FileInputStream("day09\\testInputStream2.txt");

        // 读取文件: 一次读取一个字节数组
        byte[] bytes = new byte[1024];  // 创建数组用于保存读到的字节数据
        int len;                        // 定义int变量用于保存每次读到的字节个数
        while ((len = fis.read(bytes)) != -1) {
            // 读到的数据都在bytes数组中, 读到多少个, 转换多少个, 不要全转
            String s = new String(bytes, 0, len); // 从0索引转换len个字节为String
            System.out.println(s);
        }
        
        // 释放资源
        fis.close();
    }
}
字节流复制图片文件
知识点:
如何通过 FileInputStream 和 FileOutputStream 实现文件复制

总结:

文件复制的步骤:
    1.创建一个字节输入流对象,构造方法中绑定要读取的数据源
    2.创建一个字节输出流对象,构造方法中绑定要写入的目的地
    3.使用字节输入流对象中的方法read读取文件
    4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
    5.释放资源
   
    // 创建输入流和输出流
    FileInputStream fis = new FileInputStream("c:\\1.jpg");   //输入流指向要读的数据源文件
    FileOutputStream fos = new FileOutputStream("d:\\1.jpg"); //输出流指向要写的目的地文件
    // 一次读写一个字节数组
    byte[] bytes = new byte[1024];
    int len = 0;
    while((len = fis.read(bytes))!=-1){
        fos.write(bytes,0,len);
    }
    // 释放资源
    fos.close();
    fis.close();
补充:

5分钟练习: 字节流复制图片需求:将D盘中的"小岳岳.jpg"图片, 使用一次读写一个字节数组的方式, 复制到当前模块目录下, 名为"小岳岳复制.jpg"

代码:

public class Test {
    public static void main(String[] args) throws IOException {
        long start = System.currentTimeMillis();

        // 创建文件字节输入流对象 (读取 数据源)
        FileInputStream fis = new FileInputStream("d:\\小岳岳.jpg");
        // 创建文件字节输出流对象 (写入 目的地)
        FileOutputStream fos = new FileOutputStream("day09\\小岳岳复制.jpg");

        // 复制文件方式1: 一次读写一个字节
        /*
        int by;
        while ((by = fis.read()) != -1) {
            // 读到一个写一个
            fos.write(by);
        }
        */

        // 复制文件方式2: 一次读写一个字节数组
        int len;
        byte[] bytes = new byte[1024];
        while ((len = fis.read(bytes)) != -1) {
            // 读到多少写多少, 不要全写
            fos.write(bytes, 0, len);
        }

        // 释放资源
        fos.close();
        fis.close();

        long end = System.currentTimeMillis();
        System.out.println("复制耗时:" + (end - start) + "毫秒");
        // 一次读写一个字节     复制耗时:1309毫秒
        // 一次读写一个字节数组 复制耗时:35毫秒
    }
}
字节流读取中文问题
知识点:
字节流读取中文是否方便?GBK编码中, 一个汉字占用几个byteUTF-8编码中, 一个汉字占用几个byte

总结:

GBK编码中, 一个汉字占用2个byte
UTF-8编码中, 一个汉字占用3个byte
补充:

字符流字符输入流: Reader和FileReader
知识点:
Reader类有哪些共性方法FileReader类可以做什么

总结:

注意: 字符流只能读写"文本文件"

java.io.Reader抽象类: 字符输入流 (顶层)
    // 常用成员方法
    void close() :关闭此流并释放与此流相关联的任何系统资源
    // 读
    int read(): 一次读一个字符char, 返回读到的字符.
        读到文件末尾返回-1  (返回int为了代码编写方便)
    int read(char[] cbuf): 一次读取一个字符数组char[]
        返回读取的字符个数. 读到文件末尾返回-1
   
java.io.FileReader类: 文件字符输入流
    // 构造方法
    FileReader(File file): 根据File对象创建文件字符输入流
    FileReader(String fileName): 根据File对象创建文件字符输入流
    构造方法的作用:
        1. 创建FileReader对象
        2. 将FileReader对象指向磁盘上的文件
补充:

字符输入流: 2种方式读取文本文件
知识点:
如何使用 FileReader 的 一次读一个字符 的方式, 实现读取文件如何使用 FileReader 的 一次读一个字符数组 的方式, 实现读取文件

总结:

java.io.Reader抽象类: 字符输入流 (顶层)
    int read(): 一次读一个字符char, 返回读到的字符.
        读到文件末尾返回-1  (返回int为了代码编写方便)
    int read(char[] cbuf): 一次读取一个字符数组char[]
        返回读取的字符个数. 读到文件末尾返回-1
   
java.lang.String类:
    String(char[] value): 将char数组转换为String
    String(char[] value, int offset, int count): 将char数组的一部分转换为String


FileReader fr = new FileReader("a.txt");

// 一次读一个字符
int ch;  // 定义变量保存每次读到的字符
while ((ch = fr.read()) != -1) {
    System.out.print((char)ch);
}

// 一次读一个字符数组
char[] cs = new char[1024];
int len;
while ((len = fr.read(cs)) != -1) {
    String s = new String(cs, 0, len);
    System.out.print(s);
}

// 释放资源
fr.close();
5分钟练习: 字符流读取文件需求:使用FileReader的 一次读写一个字符, 和 一次读写一个字符数组 2种方式, 读取当前模块下的随便某一个文本文件, 打印到控制台

代码:

public class Test {
    public static void main(String[] args) throws IOException {
        // 创建文件字符输入流对象
        FileReader fr = new FileReader("day09\\Test.java");

        // 读取文件方式1: 一次读取一个字符
        /*
        int ch;  // 定义一个变量, 保存每次读到的字符
        while ((ch = fr.read()) != -1) {
            // 将读取到的int值强转为char类型
            System.out.print((char)ch);  // 打印不换行, 因为读取到的字符中有换行符
        }
        */

        // 读取文件方式2: 一次读取一个字符数组
        int len;
        char[] chs = new char[1024];
        while ((len = fr.read(chs)) != -1) {
            // 先将读到的字符数组转换为String
            String s = new String(chs, 0, len);
            System.out.print(s);         // 打印不换行, 因为读取到的字符中有换行符
        }

        // 释放资源
        fr.close();
    }
}
字符输出流: Writer和FileWriter
知识点:
Writer类有哪些共性方法FileWriter类可以做什么创建FileWriter对象时有哪3个作用

总结:

java.io.Writer抽象类: 字符输出流 (顶层类)   'a''b'  缓冲区    97 98
    // 常用成员方法
    abstract void close(): 刷新缓冲区并释放资源
    abstract void flush() :刷新缓冲区
    // 写
    void write(int c): 写一个字符 (int类型为了代码编写方便)
    void write(char[] cbuf): 写一个字符数组
    abstract void write(char[] b, int off, int len): 写一个字符数组的一部分
    void write(String str): 写一个字符串
    void write(String str, int off, int len): 写一个字符串的一部分
    注意: write()方法只是将数据写到内存缓冲区, 最后必须调用flush()或close()才能将数据真正写入磁盘

java.io.FileWriter类: 文件字符输出流
    // 构造方法
    FileWriter(File file): 通过File对象创建文件字符输出流
    FileWriter(String fileName): 通过文件路径创建文件字符输出流
    作用:
        1. 创建FileWriter对象
        2. 根据构造方法中的路径, 在磁盘上创建文件 ("如果文件已存在, 则清空文件内容")
        3. 将FileWriter对象指向磁盘上的文件
字符输出流: 一次写一个字符
知识点:
如何使用 FileWriter 一次写一个字符 到文件

总结:

void write(int c): 写一个字符 (int类型为了代码编写方便)

FileWriter使用步骤:
    1.创建FileWriter对象, 构造方法中绑定要写入数据的目的地
    2.使用FileWriter中的方法 write(), 把数据写入到"内存缓冲区"中(字符转换为字节的过程)
    3.使用FileWriter中的方法 flush(), 把内存缓冲区中的数据,"刷新到文件中"
    4.释放资源 close() (会先把内存缓冲区中的数据刷新到文件中)
        
    FileWriter fw = new FileWriter("09_IOAndProperties\\d.txt");
    fw.write(97);  // int ch 用int值代表char
    //fw.flush();
    fw.close();
补充:

字符输出流: flush与close区别
知识点:

flush() 和 close() 方法有什么区别
close() 关流后, 能否继续读写数据
flush() 和 close() 方法哪个可以省略
总结:

区别:
    flush(): 刷新缓冲区 (将数据从内存中写入到磁盘)
    close(): 刷新缓冲区, 并释放资源. 关闭流后不能再用同一个流对象操作

flush() 可以省略, 只用 close()来 刷新并释放资源
补充:

字符输出流: 其他方法
知识点:
其他write()方法可以写入什么样的数据

总结:

写的其他方法:
    void write(char[] cbuf): 写一个字符数组
    abstract void write(char[] b, int off, int len): 写一个字符数组的一部分
    void write(String str): 写一个字符串
    void write(String str, int off, int len): 写一个字符串的一部分
补充:

5分钟练习: FileWriter其他方法
需求:
向当前模块中的testFileWriter.txt文件中, 分别使用以下方式写入数据:
    写一个字符方式, 写入 '0'
    写一个字符数组方式, 写入 {'a', 'b', 'c', 'd', 'e'}
    写一个字符数组的一部分方式, 写入上面数组的前3个字符
    写一个字符串的方式, 写入"传智播客"
    写一个字符串的一部分的方式, 写入"黑马程序员"的后3个字符串
代码:

public class Test {
    public static void main(String[] args) throws IOException {
        // 创建字符文件输出流对象
        FileWriter fw = new FileWriter("day09\\testFileWriter.txt");

        // 写一个字符方式, 写入 '0'
        fw.write('0');  // 参数是int类型, 将char类型值赋值给int类型, 自动类型转换, 是可以的
        // 写一个字符数组方式, 写入 {'a', 'b', 'c', 'd', 'e'}
        char[] chs = {'a', 'b', 'c', 'd', 'e'};
        fw.write(chs);
        // 写一个字符数组的一部分方式, 写入上面数组的前3个字符
        fw.write(chs, 0, 3);
        // 写一个字符串的方式, 写入"传智播客"
        fw.write("传智播客");
        // 写一个字符串的一部分的方式, 写入"黑马程序员"的后3个字符串
        fw.write("黑马程序员", 2, 3);

        // 释放资源
        fw.close();
    }
}
字符输出流: 续写, 换行
知识点:
FileWriter如何续写和换行

总结:

FileWriter中带有续写功能的构造:
    FileWriter(File file, boolean append): 通过File对象创建流. 第二个参数为true可以续写
    FileWriter(String fileName, boolean append): 通过文件路径创建流. 第二个参数为true可以续写
   
换行:
    windows: "\r\n"  fw.write("\r\n");
补充:

字节流 和 字符流 如何选择:
    1. 如果不确定读写的是什么类型的数据, 用字节流最通用
    2. 如果确定读写的就是文本, 用字符流最方便
IO异常处理IO流操作中的异常处理
知识点:
IO流使用try catch的标准格式是什么

总结:

IO流的标准格式:

    FileWriter fw = null;
    try {
        //IO流对象的创建, 操作等代码
        fw = new FileWriter("d:\\09_IOAndProperties\\g.txt", true);
        for (int i = 0; i <10 ; i++) {
            fw.write("HelloWorld"+i+"\r\n");
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 释放资源
        if(fw != null){
            try {
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
补充:

JDK7和JDK9中IO异常处理的不同方式
知识点:
JDK 7 和 JDK 9 对于IO流的异常处理, 分别做了哪些简化, 格式分别是什么

总结:

JDK 7 增加的 "try-with-resource"
    省略了 finally, 可自动释放资源
   
    // 格式
    try (创建流对象语句,如果多个,使用';'隔开) {
        // 读写数据   
    } catch (IOException e) {
        e.printStackTrace();   
    }
   
    // 示例
    try (FileWriter fw = new FileWriter("fw.txt");FileReader fr = new FileReader("fr.txt")) {
        // IO操作
        int ch = fr.read();
        fw.write(ch);
    } catch (IOException e) {
        e.printStackTrace();
    }


JDK 9 对于"try-with-resource"的改进:
    流对象的声明和创建可以放在括号外面
    流对象要求是有效final的.(即不要修改流对象的值)

    FileWriter fw = new FileWriter("fw.txt");
    FileReader fr = new FileReader("fr.txt");
    try (fw; fr) {
        // IO操作
        int ch = fr.read();
        fw.write(ch);
    } catch (IOException e) {
        e.printStackTrace();
    }
补充:

// JDK 9 的格式适用于定义方法时, 通过参数传递流对象
public static void method(FileWriter fw, FileReader fr) {
    try (fw; fr) {
        // IO操作
        int ch = fr.read();
        fw.write(ch);
    } catch (IOException e) {
        e.printStackTrace();
    }
}
Properties集合Properties存储数据和遍历
知识点:
Properties双列集合存储的键和值是什么数据类型Properties双列集合有哪些特有方法

总结:

Properties双列集合:
    键和值都是 String 类型

java.util.Properties类: 属性集, 属于Map的双列集合, 继承自Hashtable
    // 构造方法
    Properties(): 创建一个Properties集合
    // 可以使用Map接口中的方法
    // 特有成员方法
    Object setProperty(String key, String value): 保存/替换键值对
    String getProperty(String key): 通过键获取值. 键不存在返回null
    Set<String> stringPropertyNames(): 返回键的集合
    void store(OutputStream out, String comments): 将集合用字节流写入文件(不能中文),并写入注释
    void store(Writer writer, String comments): 将集合用字符流写入文件(可以中文),并写入注释
    void load(InputStream inStream): 从配置文件中通过字节流加载数据到Properties集合(不能中文)
    void load(Reader reader): 从配置文件中通过字符流加载数据到Properties集合(可以中文)
补充:

Properties: store()存储数据到文件
知识点:
如何将Properties中存储的键值对, 写入到文件

总结:

Properties将数据写入到文件的方法:
    void store(OutputStream out, String comments): 将集合用字节流写入文件(不能中文),并写入注释
    void store(Writer writer, String comments): 将集合用字符流写入文件(可以中文), 并写入注释
   
使用步骤:
    1.创建Properties集合对象,添加数据
    2.创建字节输出流/字符输出流对象,构造方法中绑定要输出的目的地
    3.使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
    4.释放资源
补充:

属性集(配置文件), 标准的后缀是 .properties
配置文件格式要求:
    一行一个键值对
    键和值中间用=分隔 (标准格式, 但空格分开也行)
    #表示单行注释
示例: db.properties

# This is a comment!
className=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=123456
5分钟练习: 使用Properties添加数据并写入文件
需求:
使用Properties集合, 添加以下键值对:
    "赵丽颖", "168"
    "迪丽热巴", "165"
    "古力娜扎", "160"
使用 store(Writer writer, String comments) 方法将数据写入到当前模块下的prop.properties文件中
代码:

public class Test {
    public static void main(String[] args) throws IOException {
        // 先创建Properties集合对象
        Properties properties = new Properties();

        // 向集合中添加键值对数据
        properties.setProperty("赵丽颖", "168");
        properties.setProperty("迪丽热巴", "165");
        properties.setProperty("古力娜扎", "160");

        // 数据目前在集合中, 也就是在内存中
        // 要想持久化存储, 则需要调用store方法来存储
        FileWriter fw = new FileWriter("day09\\prop.properties");
        properties.store(fw, "save data");

        // 释放流的资源
        fw.close();
    }
}
Properties: load()从文件加载数据到集合
知识点:
如何将磁盘上的配置文件加载到Properties集合

总结:

Properties将数据从文件加载到集合中的方法:
    void load(InputStream inStream): 从配置文件中通过字节流加载 数据到Properties集合(不能读中文)
    void load(Reader reader): 从配置文件中通过字符流加载数据到Properties集合(可以读中文)
        
使用步骤:
    1.创建Properties集合对象
    2.使用Properties集合对象中的方法load读取保存键值对的文件
    3.遍历Properties集合
补充:

5分钟练习: 加载文件数据到Properties
需求:
从刚才的prop.properties文件中, 使用 load(Reader reader) 方法加载数据到Properties集合, 使用Set<String> stringPropertyNames() 方法遍历打印到控制台
代码:

public class Test {
    public static void main(String[] args) throws IOException {
        // 创建Properties集合对象
        Properties properties = new Properties();
        
        // 目前新创建的集合对象中什么数据都没有
        // 需要调用load方法从磁盘上加载数据
        properties.load(new FileReader("day09\\prop.properties"));

        // 调用完方法后, 集合中就已经有键值对了, 先获取所有键的集合, 然后遍历
        Set<String> set = properties.stringPropertyNames();
        for (String key : set) {
            // 通过键获取值
            String value = properties.getProperty(key);
            System.out.println(key + "=" + value);
        }
    }
}


IO流:
java.io.OutputStream抽象类: 字节输出流 (顶层类)
    // 成员方法
    void close() :释放资源
    void flush() :刷新缓冲区(对于字节流来说没有作用)
    // 写字节的成员方法
    abstract void write(int b): 一次写一个字节 (参数是int是为了代码中写表示byte的整数方便不用强转)
    void write(byte[] b): 一次写一个字节数组
    void write(byte[] b, int off, int len): 一次写一个字节数组的一部分
   
java.io.FileOutputStream类: 文件字节输出流 (向文件写数据)
    // 构造方法
    FileOutputStream(String name): 通过文件路径创建文件字节输出流
    FileOutputStream(File file): 通过File对象创建文件字节输出流
    FileOutputStream(String name, boolean append): 通过文件路径创建文件字节输出流, true可以续写
    FileOutputStream(File file, boolean append): 通过File对象创建文件字节输出流, true可以续写
   
java.io.InputStream抽象类: 字节输入流 (顶层类)
    // 常用成员方法
    void close(): 释放资源
    // 读数据的方法
    int read(): 一次读一个字节. 读到文件末尾返回-1 (返回int也是为了代码编写方便)
    int read(byte[] b): 一次读一个字节数组. 读到的字节存放在参数中的字节数组中, 返回int值是本次读到的字节的个数. 读到文件末尾返回-1
   
java.io.FileInputStream类: 文件字节输入流
    // 构造方法
    FileInputStream(String name): 使用文件路径创建文件字节输入流
    FileInputStream(File file): 使用File对象创建文件字节输入流
   
java.io.Reader抽象类: 字符输入流 (顶层)
    // 常用成员方法
    void close() :关闭此流并释放与此流相关联的任何系统资源
    // 读数据的方法
    int read(): 一次读一个字符char, 返回读到的字符. 读到文件末尾返回-1  (返回int为了代码编写方便)
    int read(char[] cbuf): 一次读取一个字符数组char[]. 返回读取的字符个数. 读到文件末尾返回-1
   
java.io.FileReader类: 文件字符输入流
    // 构造方法
    FileReader(File file): 根据File对象创建文件字符输入流
    FileReader(String fileName): 根据File对象创建文件字符输入流
   
java.io.Writer抽象类: 字符输出流 (顶层类)
    // 常用成员方法
    abstract void close(): 刷新缓冲区并释放资源
    abstract void flush() :刷新缓冲区
    // 写
    void write(int c): 写一个字符 (int类型为了代码编写方便)
    void write(char[] cbuf): 写一个字符数组
    abstract void write(char[] b, int off, int len): 写一个字符数组的一部分
    void write(String str): 写一个字符串
    void write(String str, int off, int len): 写一个字符串的一部分
   
java.io.FileWriter类: 文件字符输出流
    // 构造方法
    FileWriter(File file): 通过File对象创建文件字符输出流
    FileWriter(String fileName): 通过文件路径创建文件字符输出流
    FileWriter(File file, boolean append): 通过File对象创建文件字符输出流. 第二个参数为true可以续写
    FileWriter(String fileName, boolean append): 通过文件路径创建文件字符输出流. 第二个参数为true可以续写
   
集合:
java.util.Properties类: 属性集, 属于Map的双列集合, 继承自Hashtable
    // 构造方法
    Properties(): 创建一个Properties集合
    // 可以使用Map接口中的方法
    // 特有成员方法
    Object setProperty(String key, String value): 保存/替换键值对
    String getProperty(String key): 通过键获取值. 键不存在返回null
    Set<String> stringPropertyNames(): 返回键的集合
    void store(OutputStream out, String comments): 将集合数据用字节流写入文件(不能中文), 并写入注释
    void store(Writer writer, String comments): 将集合数据用字符流写入文件(可以中文), 并写入注释
    void load(InputStream inStream): 从配置文件中通过字节流加载数据到Properties集合(不能读中文)
    void load(Reader reader): 从配置文件中通过字符流加载数据到Properties集合(可以读中文)
IO流
截止今日的IO流体系结构
[size=0.9]字节流

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马