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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始


[石家庄校区]就业班,Lambda,File,IO,转化,缓冲,序列化,打印



等待唤醒机制:
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个方法   
         一哥栽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省去面向对象的条条框框,格式由3个部分组成
  • 一些参数
  • 一个箭头
  • 一段代码
Lambda表达式的标准格式为:
(参数类型 参数名称) -> { 代码语句 }
格式说明:
  • 小括号内的语法与传统方法参数列表一致:无参数则留空;多个参数则用逗号分隔。
  • ->是新引入的语法格式,代表指向动作。
  • 大括号内的语法与传统方法体要求基本一致。
函数式接口:
        函数式接口: "有且仅有一个抽象方法的接口"
        但函数式接口对于 哪些方法算作抽象方法 有特殊规定:
                1. 有方法体的方法"不算作"抽象方法, 如默认方法, 静态方法, 私有方法
                2. 如果一个抽象方法 与 java.lang.Object类中的方法 定义相同的, 也"不算作"抽象方法
                        因为任何实现本接口的实现类, 都会直接或间接继承java.lang.Object类的public的方法, 所以在创建实现类时其实不用重写该抽象方法, 也就不算作抽象方法
省略原则:
        "可推导的都可省略" (凡是能根据前后代码能猜测出来的代码, 都可以省略不写)

    可以省略的部分:
        1. (参数列表): 参数"类型"可以省略 (a, b) -> {}
        2. (参数列表): 如果参数只有1个, 则"类型"和"小括号"都可以省略  a -> sout(a)
        3. {一些代码}: 如果只有一条代码, 则"大括号", "return", "分号"都可以"一起省略"

*函数式接口:
        函数式接口: "有且仅有一个抽象方法的接口"
        但函数式接口对于 哪些方法算作抽象方法 有特殊规定:
                1. 有方法体的方法"不算作"抽象方法, 如默认方法, 静态方法, 私有方法
                2. 如果一个抽象方法 与 java.lang.Object类中的方法 定义相同的, 也"不算作"抽象方法
                        因为任何实现本接口的实现类, 都会直接或间接继承java.lang.Object类的public的方法, 所以在创建实现类时其实不用重写该抽象方法, 也就不算作抽象方法


Lambda表达式的使用前提:
    1. Lambda只能用于接口, 且"接口中有且仅有一个抽象方法"(也称为"函数式接口")
        普通类, 抽象类不能用
    2. 使用Lambda必须具有上下文推断
        接口中只能有一个抽象方法, 才能推断出来重写的是这个抽象方法
    简而言之: 参数类型必须是函数式接口


        比如以下构造方法
        Thread(Runnable r): 该构造方法的参数类型是Runnable

        // Runnable接口中, 有且仅有一个抽象方法, 该接口就是一个函数式接口, 可以使用Lambda表达式
        public interface Runnable {
            public abstract void run();
        }

                // Comparator接口中, 有且仅有一个抽象方法
        public interface Comparator<T> {
            // 这是该接口中有且仅有的一个抽象方法
            int compare(T o1, T o2);

            // 该方法与Object类中equals定义相同, 所以不算抽象方法
            boolean equals(Object obj);

            // 一些default方法, 有方法体, 不算抽象方法
            // 一些静态方法, 有方法体, 不算抽象方法
        }

                // 我们今天自己定义的接口, 也满足函数式接口的要求
                public interface Cook {
                        void makeFood();
                }
java.io.File类: 文件和目录的路径名的抽象表现形式, 主要用于文件和目录的创建, 查找和删除等操作
        // 静态成员变量
    static String pathSeparator: 路径分隔符的字符串形式
    static char pathSeparatorChar: 路径分隔符的char形式
            Windows系统是 分号;  
        Linux系统是 冒号:
    static String separator: 文件名称分隔符的字符串形式
    static char separatorChar: 文件名称分隔符的char形式
            Window系统是 反斜杠\
        Linux系统是 正斜杠/
绝对路径:以盘符开始的路径例c:\\user\\
相对路径:简化的路径 省略根路径(项目以后的可以称为相对路径)(不写盘符)

// 构造方法
     (创建了File对象, 并将其指向该路径. 不会真正在磁盘上创建这个文件)
        File File(String pathname): 根据 路径字符串 封装一个File对象(字符串转为File对象)

        File File(String parent, String child): 根据 父路径字符串 和 子路径字符串 封装File对象
        File File(File parent, String child): 根据 父路径的File对象 和 子路径 封装为File对象
// 常用获取方法
    String    getAbsolutePath(): 返回此File的绝对路径名字符串(不管传入的路径是绝对还是相对返回都是绝对路径)
    String    getPath(): 获取File对象的封装路径 (创建对象时传入的路径一致)(与toString相同)
    String    getName(): 获取File对象的文件名目录名(最后的文件或者文件夹名称)(与toString相同)
    long      length(): 获取File表示的文件大小字节数 (不能获取文件夹的大小)
// 常用判断方法
        boolean   exists(): 判断File对象代表的文件或目录是否实际存在
        boolean    isDirectory(): 判断File表示的是否为目录
        boolean    isFile(): 判断File表示的是否为文件
// 常用创建删除方法
        boolean  createNewFile(): 当文件不存在时, 创建一个新的空文件
        boolean  delete(): 删除由此File表示的文件或目录. (删除目录时必须是空目录)
        boolean   mkdir(): 创建File表示的目录
        boolean   mkdirs(): 创建File表示的多级目录
// 常用获取目录中内容的方法

        String[]    list(): 获取当前File目录下的所有子文件或目录的名字数组(最后一级的子文件或者子目录名字)
        File[]   listFiles(): 获取当前File目录中的所有子文件或目录的File对象数组(得到(每一个子文件或者子文件夹的)一长串的路径,)
      String     toLowerCase() 将获得的String类型的路径或者名称变为小写
      String     endsWith(".java")比较字符串结尾或者后缀与括号中是否相同

        File[]   listFiles(FileFilter    filter): 通过File对象过滤, 返回文件过滤器过滤后的File对象数组
        File[]    listFiles(FilenameFilter   filter): 通过File对象的文件名过滤, 返回文件过滤器过滤后的File对象数组

java.io.FileFilter接口: 用于File对象的过滤器
        boolean accept(File pathName): true则会将参数的File对象加入返回的File[], false则不加入
java.io.FilenameFilter接口:
listFiles内方法将File分为路径与子路径,子路径为最后的文件
        boolean accept(File dir, String name): true则会将参数的File对象加入返回的File[], false
则不加入
递归
递归思想:
        遇到一个问题时, 将该问题拆解成可以解决的小问题, 如果解决不了, 继续拆解为更小的问题. 如果小问题解决了, 大问题也就能够解决

Java中实现递归的方式:
        方法内部调用方法自己 (所以必须定义方法)

递归的分类:
        直接递归: 方法自己调用方法
        间接递归: A方法调用B方法, B方法调用C方法, C方法调用A方法

递归时的注意事项:
    1. 递归要有限定条件(出口), 保证递归能够停止(就是在某种情况下方法不再调用自己), 否则会栈内存溢出
    2. 递归次数不能太多, 否则会栈内存溢出
    3. 构造方法不能递归

递归的使用前提:
        调用方法时, 方法的主体不变, 但每次传递的参数值不同, 可以使用递归
IO流:
java.io.OutputStream抽象类: 字节输出流 (顶层类)
java程序---->JVM(java虚拟机)---OS(操作系统)--->os调用写数据方法---->数据写到文件夹
写数据的原理:
        Java程序 -> JVM虚拟机 -> OS操作系统 -> OS调用写数据的方法 -> 将数据写入文件

使用字节输出流写数据到文件的步骤:
        1. 创建对象: 创建FileOutputStream对象, 构造方法中传递文件路径
        2. 写入数据: 使用FileOutputStream对象调用 write(int b) 方法, 将字节写入文件
        3. 释放资源: 使用FileOutputStream对象调用 close() 方法, 关闭流对象释放资源

        FileOutputStream fos = new FileOutputStream("模块名\\文件名.txt");
        fos.write(97);
        fos.close();
        // 成员方法
        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可以续写,false)
        FileOutputStream(File?file, boolean append): 通过File对象创建文件字节输出流, true可以续写

向文件中写入字节数据时, 十进制的数字会被转换为"二进制"的数字写入文件

文本编辑器打开文本文件时, 会先查询编码表, 将二进制数字转换为对应的字符进行显示
        0-127: 查询ASCII码表  -128~127
        其他: 查询系统默认码表
                        Windows简体中文系统的程序打开是按 GBK 码表
                        IDEA中使用的是 UTF-8 码表   

ASCII编码表: 1个byte就是一个字符  97 a
GBK编码表:   2个byte数字组成一个汉字. "你": -60, -29
UTF-8编码表: 3个byte数字组成一个汉字. "你": -28, -67, -96

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(intc): 写一个字符 (int类型为了代码编写方便)
        void   write(char[]  cbu ): 写一个字符数组
        abstract void  write(char[]  b, int off, int  len): 写一个字符数组的一部分
        void   write(String?str): 写一个字符串
        void   write(String str, int off, int len): 写一个字符串的一部分
        使用字节数组读取:read(byte[] b),每次读取b的长度个字节到数组中,返回读取到的有效字节个数,读取到末尾时,返回-1 .
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流异常处理:
        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();
            }
        }
    }
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();
    }
缓冲字节流
  缓冲流: BufferedInputStream / BufferedOutputStream, BufferedReader / BufferedWriter
        在基本流上增加缓冲区 char[] byte[], 提高读写效率
缓冲流的原理:
        底层也是使用基本流(FileXxx)来读写
        但缓冲流内部定义了一个缓冲数组, 在读的时候类似于我们一次读一个数组的方式, 减少了磁盘操作次数, 提高了程序效率
原理图
C:/Users/22354/AppData/Local/YNote/data/qqC5DC1F9163EBD624CADB70BB0C1AD6B8/51e3f3c4efc74e63b3cf0ddfa099b166/01_%E7%BC%93%E5%86%B2%E6%B5%81%E7%9A%84%E5%8E%9F%E7%90%86.bmp

java.io.BufferedOutputStream类:
        // 构造方法
        BufferedOutputStream(OutputStream  out): 使用基本流创建一个缓冲字节输出流
        BufferedOutputStream(OutputStream  out, int size): 使用基本流创建一个缓冲字节输出流, 设置缓冲区大小(类似于数组打包输入)


java.io.BufferedInputStream类: 缓冲字节输入流
        // 构造方法
        BufferedInputStream(InputStream  in): 使用基本流创建一个缓冲字节输入流
        BufferedInputStream(InputStream?in, int size): 使用基本流创建一个缓冲字节输入流, 设置缓冲区大小

java.io.BufferedWriter类:
        // 构造方法
        BufferedWriter(Writer  out): 使用基本流创建一个缓冲字符输出流
        BufferedWriter(Writer   out, int size): 使用基本流创建一个缓冲字符输出流, 设置缓冲区大小
        // 特有方法
        void     newLine(): 写入一个换行符, 换行符自动根据当前系统确定

java.io.BufferedReader类: 缓冲字符输入流

        // 构造方法
        BufferedReader(Reader?in): 使用基本流创建一个缓冲字符输入流
        BufferedReader(Reader?in, int size): 使用基本流创建一个缓冲字符输入流, 设置缓冲区大小
        // 特有方法
        String?readLine(): 一次读一行字符串, "不包含换行符". 读到文件末尾返回null
转换流
C:/Users/22354/AppData/Local/YNote/data/qqC5DC1F9163EBD624CADB70BB0C1AD6B8/742b4ad4fc4b48b9a719dd9f0be8e9c7/02_%E8%BD%AC%E6%8D%A2%E6%B5%81%E7%9A%84%E5%8E%9F%E7%90%86.bmp
java.io.OutputStreamWriter类: 输出转换流. 字符流通往字节流的桥梁
        // 构造方法
        OutputStreamWriter(OutputStream  out): 使用默认编码表创建转换流
        OutputStreamWriter(OutputStream  out,  String charsetName): 使用指定编码表创建转换流
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"), "GBK");
                osw.write("你") -> 查"GBK"码表 -> -1,-2 -> FileOutputStream -> a.txt文件
         写数据: 字符流 --------------------------> 字节流

java.io.InputStreamReader类: 输入转换流. 字节流通往字符流的桥梁
        // 构造方法
        InputStreamReader(InputStream  in): 使用默认编码表创建转换流
        InputStreamReader(InputStream   in,   String charsetName): 使用指定编码表创建转换流
java.io.Reader
        |_ InputStreamReader: 转换字符输入流      byte --查指定码表--> char, char[], String
                |_ FileReader                       byte --查UTF-8码表--> char
java.io.Writer
        |_ OutputStreamWriter: 转换字符输出流      char, char[], String --查指定码表--> byte
                |_ FileWriter                        char --查UTF-8码表--> byte
转换流小结:

FileReader = InputStreamReader + FileInputStream, 按照IDEA中UTF-8将读取的字节转换为字符
FileWriter = OutputStreamWriter + FileOutputStream, 按照IDEA中UTF-8将写出的字符转换为字节
(FliwWriter与FileReader中相当于有默认的转码表将字节转化为字符)
转化流的主要作用就是定义字节转话为字符时码表的类型
转换流InputStreamReader, OutputStreamWriter在读写时, 可以指定按照哪个编码表来进行转换
所以:
字符流 = 编码表 + 字节流
转换流主要用于解决Web开发中的乱码问题
        对象流
static 修饰的成员变量属于类不属于对象, 所以不能序列化
transient 修饰的成员变量, 不能被序列化
serialVersionUID序列号的作用:
继承serialVersion
        序列化操作时, 会根据类生成一个默认的 serialVersionUID 属性, 写入到文件中.
        该版本号是根据类中成员计算出来的一个数值. 当类的成员发生改变时, 序列版本号也会发生变化
        当代码中的类和读取的对象序列版本号不一致时, 就会抛出InvalidClassException
为了避免这种问题, 我们可以手动写死这个序列号, 这样无论成员如何变化, 序列版本号都是一样的
注意:
被序列化的对象Person, 所属的类的所有属性也都必须是可序列化的. (实现 java.io.Serializable 接口)
        比如Person类中有 String name; 那么String也必须实现Serializable.
如果要将集合序列化, 那么集合中存储的对象也必须是可序列化的
        比如ArrayList<Person>存储的Person, ArrayList对象需要实现Serializable, Person也需要
java.io.ObjectOutputStream类: 对象字节输出流
        // 构造方法
        ObjectOutputStream(OutputStream  out)
        // 特有成员方法
        void   writeObject(Object  obj): 将对象写出

java.io.ObjectInputStream类: 对象字节输入流
        // 构造方法
        ObjectInputStream(InputStream  in)
        // 特有成员方法
        Object   readObject(): 读取对象
        打印流
java.io.PrintStream类: 字节打印流
        // 构造方法
        PrintStream(File file): 创建字节打印流, 输出到一个文件
        PrintStream(OutputStream out): 创建字节打印流, 输出到一个字节输出流
        PrintStream(String  fileName): 创建字节打印流, 输出到一个文件路径
        PrintStream特点:
        1. 只有输出流, 没有输入流
        2. PrintStream不会抛出IOException
        3. 有特殊方法 print(), println(), 可以输出任意类型的值, 原样输出

java.io.PrintStream类: 字节打印流
        // 构造方法
        PrintStream(File file): 创建字节打印流, 输出到一个文件
        PrintStream(OutputStream out): 创建字节打印流, 输出到一个字节输出流
        PrintStream(String fileName): 创建字节打印流, 输出到一个文件路径

注意事项:
        如果用 write(97) 方法, 会查编码表   97 -> a
        如果用 print(97), println(97), 则原样输出  97 int -> '9''7' -> byte -> 文件 97
        System.out.println();
        PrintStream out;

java.lang.System类:
        // 静态方法
        static void setOut(PrintStream out): 设置System.out的输出目的地为参数的打印流

java.lang.System类:
        // 静态方法
        static void setOut(PrintStream out): 设置System.out的输出目的地为参数的打印流





0 个回复

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