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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始


        从上次自习算起至现在已经过去四天,这四天我们分别学习了多线程的等待唤醒机制线程池Lambda——Day07,IO流File类、递归遍历多级目录思想——Day08、InputStream、OutputSteam普通流——Day09,还有缓冲流、转换流、对象流、打印流——Day10。知识点总结如下:
       Day07

        一、线程间通信

        多个线程并发执行时, 在默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成一件任务,并且我们希望他们有规律的执行, 那么多线程之间需要一些协调通信,以此来帮我们达到多线程共同操作一份数据。

概念多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。

        多个线程在处理同一个资源,并且任务不同时,需要线程通信来帮助解决线程之间对同一个变量的使用或操作。 就是多个线程在操作同一份数据时, 避免对同一共享变量的争夺。也就是我们需要通过一定的手段使各个线程能有效的利用资源。而这种手段即—— 等待唤醒机制

        例如线程A用来生成包子的,线程B用来吃包子的,包子可以理解为同一资源,线程A与线程B处理的动作,一个是生产,一个是消费,那么线程A与线程B之间就存在线程通信问题

        1、等待唤醒机制

简单来说就是在一个线程进行了规定操作后,就进入等待状态(wait()), 等待其他线程执行完他们的指定代码过后 再将其唤醒(notify());在有多个线程进行等待时, 如果需要,可以使用 notifyAll()来唤醒所有的等待线程。wait/notify 就是线程间的一种(等待唤醒)协作机制。详细分析如下图:

        2、等待唤醒中的方法

java.lang.Object类:

        // 成员方法
        void wait(): 使用锁对象调用, 当前线程进入WAITING无限等待状态, 直到被其他线程唤醒
        void notify(): 使用锁对象调用, 随机唤醒一个处于等待状态的线程
        void notifyAll(): 使用锁对象调用, 唤醒所有处于等待状态的线程
        二、线程池

        1、线程池思想概述

         我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。所以这时候我们就需要一个线程池。线程池就是一个容纳多个线程的容器。线程池其中的线程可以反复使用, 省去了频繁创建线程对象的操作, 无需反复创建线程而消耗过多资源。大幅提升了效率

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

         详细分析

         2、线程池的使用

        java.util.concurrent.Executors类: 线程池工厂类, 用于管理线程池

        // 静态方法:
        static ExecutorService newFixedThreadPool(int nThreads): 创建固定数量线程的线程池(常用)
        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表达式
     1、函数式编程思想概述:强调做事 (不关心用什么对象, 重写什么方法)。
        2、Lambda表达式
        Lambda使用前提:
  1) Lambda只能用于接口, 且"接口中有且仅有一个抽象方法"(也称为"函数式接口")普通类, 抽象类不能用        2) 使用Lambda必须具有上下文推断方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例(简而言之: 作为参数类型的接口, 必须是函数式接口)
   Lambda表达式的3个部分:        1) 一些参数 ()                接口中抽象方法的参数列表. 没参数就空着; 有参数就写, 多个参数用逗号分隔        2) 一个箭头 ->                将参数传递给方法体        3) 一段代码 {}                重写接口抽象方法的方法体        格式:        // 写成一行        (参数列表) -> {一些重写方法的代码}        // 写成多行        (参数列表) -> {        一些重写方法的代码         }
       3、Lambda省略格式
        省略原则:        可推导的都可省略 (凡是能根据前后代码能猜测出来的代码, 都可以省略不写)        可以省略的部分:        1. (参数列表): 参数"类型"可以省略        2. (参数列表): 如果参数只有1个, 则"类型"和"小括号"都可以省略 ,如:a -> sout(a)        3. {一些代码}: 如果只有一条代码, 则"大括号", "return", "分号"都可以"一起省略"。
Day08

       一、File类

        java.io.File类: 文件和目录的路径名的抽象表现形式, 主要用于文件和目录的创建, 查找和删除等操作        我们可以对用File类进行的操作:        创建文件/目录        删除文件/目录        获取文件/目录        判断文件/目录是否存在        对目录进行遍历        获取文件的大小        重要英文单词的含义: (起变量名时会用到)        file: 文件        directory: 目录        path: 路径

1、File类的静态成员变量

        // 静态成员变量

        static String pathSeparator: 路径分隔符的字符串形式
        static char pathSeparatorChar: 路径分隔符的char形式
        Windows系统是 分号;
        Linux系统是 冒号:
        static String separator: 文件名称分隔符的字符串形式
        static char separatorChar: 文件名称分隔符的char形式
        Window系统是 反斜杠\
        Linux系统是 正斜杠/
        绝对路径和相对路径 2、绝对路径:以盘符开始的路径          如: “D:\\a\hi.txt”        相对路径:不以盘符开始的简化路径,相当于项目的根目录        如:”a\\1.mp3”, “123.txt”        注意事项:        1) 路径不区分大小写在(windows系统中部分大小写,Linux,Mac区分)        2)路径一般写成字符串,而字符串中一个\是转义,所以要写两个\\
3、File类方法
java.io.File类: 文件和目录的路径名的抽象表现形式, 主要用于文件和目录的创建, 查找和删除等操作
    // 构造方法(创建了File对象, 并将其指向该路径. 不会真正在磁盘上创建这个文件)
    File File(String pathname): 根据 路径字符串 封装一个File对象
    File File(String parent, String child): 根据 父路径字符串 和 子路径字符串 封装File对象
    File File(File parent, String child): 根据 父路径的File对象 和 子路径 封装File对象
java.io.File
    // 常用获取方法
    String getAbsolutePath(): 返回此File的绝对路径名字符串
    String getPath(): 获取File对象的封装路径 (创建对象时传入的路径)
    String getName(): 获取File对象的文件名或目录名
    long length(): 获取File表示的文件大小的字节数 (不能获取目录的大小)
获取目录文件夹的大小做不到 会得到结果0/4096 都是错的 4096是1024*4也是不对的
getAbsolutePath()getPath()getName()、这三个获取方法和有没有这个文件没有关系 他不会是核实你的文件是不是真实存在的
java.io.File类    // 常用判断方法    boolean exists(): 判断File对象代表的文件或目录是否实际存在    boolean isDirectory(): 判断File表示的是否为目录    boolean isFile(): 判断File表示的是否为文件
全都是boolean类型的返回值。电脑的硬盘中只有文件/文件夹,两个方法是互斥的。这两个方法使用前提,路径必须是存在的,否则返回false
// 常用创建删除方法
    boolean createNewFile(): 当文件不存在时, 创建一个新的空文件
    boolean delete(): 删除由此File表示的文件或目录. (删除目录时必须是空目录)
    boolean mkdir(): 创建File表示的目录
    boolean mkdirs(): 创建File表示的多级目录
delete方法是直接在硬盘上删除文件/文件夹,不走回收站 一定要慎重
    // 常用获取目录中内容的方法    String[] list(): 获取当前File目录下的所有子文件或目录的名字数组    File[] listFiles(): 获取当前File目录中的所有子文件或目录的File对象数组
只能用表示目录的File对象调用用文件File对象, 或者路径不存在, 调用会报错
        二、递归
       递归思想:遇到一个问题时, 将该问题拆解成可以解决的小问题, 如果解决不了, 继续拆解为更小的问题. 如果小问题解决了, 大问题也就能够解决                Java中实现递归的方式:方法内部调用方法自己 (所以必须定义方法)。
        递归的分类:
        直接递归: 方法自己调用方法。
        间接递归: A方法调用B方法, B方法调用C方法, C方法调用A方法
        递归时的注意事项:
1. 递归要有限定条件(出口), 保证递归能够停止(就是在某种情况下方法不再调用自己), 否则会栈内存溢出
2. 递归次数不能太多, 否则会栈内存溢出
3. 构造方法不能递归
        递归的使用前提:调用方法时, 方法的主体不变, 但每次传递的参数值不同, 可以使用递归 。
        构造方法禁止递归:构造方法是创建对象使用的,一直递归会导致内存中有无数多个对象,直接编译报错递归求和的思路       用递归完成N以内求和       把解决不了的问题定义成方法(求5-1的和)       如果小问题能解决 直接写代码实现。
        三、FileFilter文件过滤器的原理和使用java.io.File类: Filter过滤器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接口:boolean accept(File dir, String name): true则会将参数的File对象加入返回的File[], false则不加入
Day09

        一、IO流

   i就是input  输入(读取)        o就是output  输出(写入)​        流:数据(字符,字节) 1个字符 = 2个字节  1个字节 = 8个二进制位​        输入:从硬盘读取到内存        输出:从内存写入到硬盘 (入和出都是相对于内存来说的)

        1、分类
字节流 (读写字节: byte, 可以读写所有类型的文件, 包括视频, 图片, 音频, 文本等)​                        字节输入流:InputStream        FileInputStream​                        字节输出流:OutputStream            FileOutputStream​       
         ②字符流 (读写字符: char, String, 只能读写文本文件)​                        字符输入流:Reader        FileReader​                        字符输出流:Writer                FileWriter
       2.字节输出流:       
​        // 成员方法
        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可以续写
​        换行符:          windows:  \r\n             linux:   /n                mac:  /r



        3.字节输入流:​        java.io.InputStream抽象类: 字节输入流 (顶层类)        // 常用成员方法        void close(): 释放资源        // 读数据的方法        int read(): 一次读一个字节. 读到文件末尾返回-1 (返回int也是为了代码编写方便)
循环读取标准格式
[Java] 纯文本查看 复制代码
FileInputStream fis = new FileInputStream("模块名\\文件名");
					// 一次读一个字节:
					int by;   // int变量用来存储每次读到的数据
					while ((by = fis.read()) != -1) {
						System.out.print((char)by);  // 不要换行, 文件中自带换行的byte
					}

​					fis.close();


/*int read(byte[] b): 一次读一个字节数组. 读到的字节存放在参数中的字节数组中, 返回int值是本次读到的字节的个数. 读到文件末尾返回-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(); // 释放资源
​					fis.close();

        3.字符流输出:
​        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可以续写
       4、IO异常处理
       IO流的标准格式:
[AppleScript] 纯文本查看 复制代码
[/font]
[font=微软雅黑]FileWriter fw = null;
[/font][font=微软雅黑] try {[/font]
[font=微软雅黑]        //IO流对象的创建, 操作等代码[/font]
[font=微软雅黑]        fw = new FileWriter("d:\\09_IOAndProperties\\g.txt", true);[/font]
[font=微软雅黑]        for (int i = 0; i <10 ; i++) {[/font]
[font=微软雅黑]            fw.write("HelloWorld"+i+"\r\n");[/font]
[font=微软雅黑]        }[/font]
[font=微软雅黑]    } catch (IOException e) {[/font]
[font=微软雅黑]        e.printStackTrace();[/font]
[font=微软雅黑]    } finally {[/font]
[font=微软雅黑]        // 释放资源[/font]
[font=微软雅黑]        if(fw != null){[/font]
[font=微软雅黑]            try {[/font]
[font=微软雅黑]                fw.close();[/font]
[font=微软雅黑]            } catch (IOException e) {[/font]
[font=微软雅黑]                e.printStackTrace();[/font]
[font=微软雅黑]            }[/font]
[font=微软雅黑]        }[/font]
[font=微软雅黑]    }


Day10

       一、缓冲流

字节缓冲流: BufferedInputStreamBufferedOutputStream
字符缓冲流: BufferedReader BufferedWriter
缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。
1、字节缓冲流:构造方法:l public BufferedInputStream(InputStream in):创建一个 新的缓冲输入流。l public BufferedOutputStream(OutputStream out): 创建一个新的缓冲输出流。构造举例,代码如下// 创建字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"));
// 创建字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));
使用步骤:
        1.创建FileInputStream对象, 构造方法中绑定要读取的数据源
        2.创建BufferedInputStream对象, 构造方法中传递FileInputStream对象, 提高FileInputStream效率
        3.使用BufferedInputStream对象中的方法 read(), 读取文件
        4.释放资源 close()
格式如下:
[Java] 纯文本查看 复制代码
[/font][/align][align=left][font=微软雅黑]FileInputStream fis = new FileInputStream("文件路径");[/font][/align][align=left][font=微软雅黑]	BufferedInputStream bis = new BufferedInputStream(fis);[/font][/align][align=left][font=微软雅黑]	byte[] bytes = new byte[1024];[/font][/align][align=left][font=微软雅黑]	int len;[/font][/align][align=left][font=微软雅黑]	while ((len = bis.read(bytes)) != -1) {[/font][/align][align=left][font=微软雅黑]        System.out.println(new String(bytes, 0, len));[/font][/align][align=left][font=微软雅黑]    }[/font][/align][align=left][font=微软雅黑]	bis.close();[/font][/align][align=left][font=微软雅黑]
2、字符缓冲流
构造方法:public BufferedReader(Reader in) :创建一个 新的缓冲输入流。public BufferedWriter(Writer out) : 创建一个新的缓冲输出流。特有方法:BufferedReader public String readLine() : 读一行文字。BufferedWriterpublic void newLine() : 写一行行分隔符,由系统属性定义符号。二、转换流      字符编码和字符集      字符:计算机中储存的信息都是用二进制数表示,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符储存到计算机中,称为编码。反之,将储存在计算机中的二进制按照某种规则解析显示出来,称为解码
      字符编码就是一套自然语言的字符与二进制数之间的对应规则;
      字符集是一个系统支持的所有字符的合集,包括各国家文字、标点符号、图形符号、数字等。
IDEA中,使用FileReader读取项目中的文本文件。由于IDEA的设置,都是默认的 UTF-8 编码,所以没有任何问题。但是,当读取Windows系统中创建的文本文件时,由于Windows系统的默认是GBK编码,就会出现乱码。
java.io.OutputStreamWriter类: 输出转换流. 字符流通往字节流的桥梁
// 构造方法
OutputStreamWriter(OutputStream out): 使用默认编码表创建转换流
OutputStreamWriter(OutputStream out, String charsetName): 使用指定编码表创建转换流
[Java] 纯文本查看 复制代码
[/font][/align][align=left][font=微软雅黑]// 使用默认UTF-8
       OutputStreamWriter o = new OutputStreamWriter(new FileOutputStream("a.txt"));[/font][/align][align=left][font=微软雅黑]
[Java] 纯文本查看 复制代码
[/font][/align][align=left][font=微软雅黑]// 使用指定的GBK
       OutputStreamWriter o = new OutputStreamWriter(new FileOutputStream("a.txt"), "GBK");[/font][/align][align=left][font=微软雅黑]
      java.io.InputStreamReader类: 输入转换流. 字节流通往字符流的桥梁
// 构造方法
InputStreamReader(InputStream in): 使用默认编码表创建转换流
InputStreamReader(InputStream in, String charsetName): 使用指定编码表创建转换流
[Java] 纯文本查看 复制代码
[/font][/align][align=left][font=微软雅黑]// 使用默认UTF-8
InputStreamReader r = new InputStreamReader(new FileInputStream("a.txt"));[/font][/align][align=left][font=微软雅黑]
[Java] 纯文本查看 复制代码
[/font][/align][align=left][font=微软雅黑]// 使用指定的GBK
InputStreamReader r = new InputStreamReader(new FileInputStream("a.txt"), "GBK");[/font][/align][align=left][font=微软雅黑]
         三、序列化
  Java提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该 对象的数据 、 对象的类型 和 对象中存储的数据 等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据 、 对象的类型 和 对象中存储的数据 信息,都可以用来在内存中创建对象。
OutputStream
        |_ ObjectOutputStream类: 对象字节输出流
InputStream
        |_ ObjectInputStream类: 对象字节输入流
        1、ObjectOutputStream类、序列化
       java.io.ObjectOutputStream类: 对象字节输出流
        // 构造方法
        ObjectOutputStream(OutputStream out)
        // 特有成员方法
        void writeObject(Object obj): 将对象写,输出
演示:       
[Java] 纯文本查看 复制代码
[/align][align=left]// 创建对象输出流[/align]        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student.txt"));
[align=left][font=微软雅黑][size=3]// 写对象[/size][/font][/align][align=left][font=微软雅黑][size=3]        Student s = new Student("小美女", 18);[/size][/font][/align][align=left][font=微软雅黑][size=3]        oos.writeObject(s);[/size][/font][/align][align=left][font=微软雅黑][size=3]        // 释放资源[/size][/font][/align][align=left][font=微软雅黑][size=3]        oos.close();[/size][/font][/align][font=微软雅黑][size=3]
[/size][/font]

注意:
        被读写的对象的类, 必须实现 java.io.Serializable 接口, 否则会抛出 NotSerializableException
        该类的所有属性也都必须是可序列化的. (实现 java.io.Serializable 接口)
        如果某个属性不需要序列化, 则需要用 transient 修饰为瞬态的

        如果要将集合序列化, 那么集合中存储的对象也必须是可序列化的
        2、ObjectInputStream类、反序列化
        java.io.ObjectInputStream类: 对象字节输入流
        // 构造方法
        ObjectInputStream(InputStream in)
        // 特有成员方法
        Object readObject(): 读取对象
演示:
[Java] 纯文本查看 复制代码
[/align][align=left]// 创建对象输入流[/align][align=left]    ObjectInputStream oos = new ObjectInputStream(new FileInputStream("student.txt"));[/align][align=left]    // 读对象[/align][align=left]    Object o = oos.readObject();[/align][align=left]    Student s = (Student)o;[/align][align=left]    System.out.println(s);[/align][align=left]    // 释放资源[/align][align=left]    oos.close();[/align][align=left]
补充:
1、避免属性序列化
        static 修饰的成员变量属于类不属于对象, 所以不能序列化
        transient 修饰的成员变量, 不能被序列化

        如果对象的某个属性不希望被序列化, 可以使用transient修饰, 这样就不会被对象流写到文件中
2、InvalidClassException异常: 原因和解决方案
      serialVersionUID序列号的作用:        序列化操作时, 会根据类生成一个默认的 serialVersionUID 属性, 写入到文件中.         该版本号是根据类中成员计算出来的一个数值. 当类的成员发生改变时, 序列版本号也会发生变化        当代码中的类和读取的对象序列版本号不一致时, 就会抛出InvalidClassException,为了避免这种问题, 我们可以手动写死这个序列号, 这样无论成员如何变化, 序列版本号都是一样的

1 个回复

倒序浏览
顶一下,顶一下
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马