第七天 线程池通信等待唤醒机制等待唤醒机制: 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的操作. 对于一些数量多, 执行时间短的任务, 频繁的创建和销毁线程来执行, 会降低程序运行效率. 线程池: 一个容纳多个线程的容器(集合) 线程池可以解决的问题: 其中的线程可以反复使用, 省去了频繁创建线程对象的操作, 无需反复创建线程而消耗过多资源 线程池的工作原理: 提前创建好多个线程对象, 放在集合中. 多个任务来了反复使用这些线程对象来执行 public class RunnableImpl implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + "号柜台为您服务"); System.out.println("顾客正在办理业务..."); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "号柜台期待您再次光临"); System.out.println("--------------------------------------------"); } } public class Test { public static void main(String[] args) { // 创建线程池 ExecutorService executorService = Executors.newFixedThreadPool(2); // 提交10个任务 for (int i = 0; i < 10; i++) { executorService.submit(new RunnableImpl()); } // 可以关闭也可以不关闭 executorService.shutdown(); } } 函数式编程思想:Lambda表达式冗余的Runnable代码// 我们要通过Lambda表达式简化以下的代码 new Thread(new Runnable() { @Override public void run() { // 要执行的代码才是重要的 } }).start(); 关键代码是: run()方法中要执行的任务 而其他代码都只是形式 Lambda更优写法 // 函数式编程的代码 new Thread( ()-> { System.out.println(Thread.currentThread().getName() + "新线程创建了"); } ).start(); Lambda标准格式Lambda表达式的3个部分: 格式: // 写成多行 (数据类型 变量名, 数据类型 变量名) -> { 一些重写方法的代 } 可以合并为一行代码; Lambda重写的有参有返回值的方法 Arrays.sort(arr, (Person o1, Person o2) -> { return o1.getAge() - o2.getAge(); }); Lambda自定义接口,抽象方法有参有返回值 invokeCalc(120, 130, (int a, int b)->{ return a + b; }); 第八天 File类构造方法 public File(String pathname) : 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。 public File(String parent, String child) : 从父路径名字符串和子路径名字符串创建新的 File实例。 public File(File parent, String child) : 从父抽象路径名和子路径名字符串创建新的 File实例。 常用方法 public String getAbsolutePath() : 返回此File的绝对路径名字符串。 public String getPath() : 将此File转换为路径名字符串。 public String getName() : 返回由此File表示的文件或目录的名称。 public long length() : 返回由此File表示的文件的长度。 绝对路径和相对路径绝对路径:从盘符开始的路径,这是一个完整的路径。 相对路径:相对于项目目录的路径,这是一个便捷的路径,开发中经常使用。 判断功能的方法public boolean exists() :此File表示的文件或目录是否实际存在。 public boolean isDirectory() :此File表示的是否为目录。 public boolean isFile() :此File表示的是否为文件。 创建删除功能的方法public boolean createNewFile() :当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。 public boolean delete() :删除由此File表示的文件或目录。 public boolean mkdir() :创建由此File表示的目录。 public boolean mkdirs() :创建由此File表示的目录,包括任何必需但不存在的父目录。 目录的遍历public String[] list() :返回一个String数组,表示该File目录中的所有子文件或目录。public File[] listFiles() :返回一个File数组,表示该File目录中的所有的子文件或目录。 递归递归:指在当前方法内调用自己的这种现象。 递归的分类: 递归分为两种,直接递归和间接递归。 直接递归称为方法自身调用自己。 间接递归可以A方法调用B方法,B方法调用C方法,C方法调用A方法。 注意事项: 递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出。 在递归中虽然有限定条件,但是递归次数不能太多。否则也会发生栈内存溢出。 构造方法,禁止递归 递归累加求和计算1到n的和 public class DiGuiDemo { public static void main(String[] args) { 使 int num = 5; int sum = getSum(num); System.out.println(sum); } public static int getSum(int num) { if(num == 1){ return 1; } return num + getSum(num‐1); } } 第九天 字节流根据数据的流向分为:输入流和输出流。输入流 :把数据从 其他设备 上读取到 内存 中的流。 输出流 :把数据从 内存 中写出到 其他设备 上的流。 格局数据的类型分为:字节流和字符流。字节流 :以字节为单位,读写数据的流。 字符流 :以字符为单位,读写数据的流。 字节输出流public void close() :关闭此输出流并释放与此流相关联的任何系统资源。 public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。 public void write(byte[] b) :将 b.length字节从指定的字节数组写入此输出流。 public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输 出到此输出流。 public abstract void write(int b) :将指定的字节输出流。 FileOutputStream类public FileOutputStream(File file) :创建文件输出流以写入由指定的 File对象表示的文件。 public FileOutputStream(String name) : 创建文件输出流以指定的名称写入文件。 当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有这个文件,会创建该文件。如果有这个文 件,会清空这个文件的数据。 1.写出字节: write(int b) 方法,每次可以写出一个字节数据 2.写出字节数组: write(byte[] b) ,每次可以写出数组中的数据 3.写出指定长度字节数组: write(byte[] b, int off, int len) ,每次写出从off索引开始,len个字节 数据追加续写ublic FileOutputStream(File file, boolean append) : 创建文件输出流以写入由指定的 File对象表示的 文件。 public FileOutputStream(String name, boolean append) : 创建文件输出流以指定的名称写入文件。 这两个构造方法,参数中都需要传入一个boolean类型的值, true 表示追加数据, false 表示清空原有数据。 这样创建的输出流对象,就可以指定是否追加续写了, 字节输入流InputStreampublic void close() :关闭此输入流并释放与此流相关联的任何系统资源。 public abstract int read() : 从输入流读取数据的下一个字节。 public int read(byte[] b) : 从输入流中读取一些字节数,并将它们存储到字节数组 b中 。 FileInputStream类FileInputStream(File file) : 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系 统中的 File对象 file命名。 FileInputStream(String name) : 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件 系统中的路径名 name命名。 读取字节数据 字符流字符输入流Readerpublic void close() :关闭此流并释放与此流相关联的任何系统资源。 public int read() : 从输入流读取一个字符。 public int read(char[] cbuf) : 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中 FileReader类FileReader(File file) : 创建一个新的 FileReader ,给定要读取的File对象。 FileReader(String fileName) : 创建一个新的 FileReader ,给定要读取的文件的名称。 当你创建一个流对象时,必须传入一个文件路径。类似于FileInputStream 。 读取字符数据 读取字符: read 方法,每次可以读取一个字符的数据,提升为int类型,读取到文件末尾,返回 -1 ,循环读 取 使用字符数组读取: read(char[] cbuf) ,每次读取b的长度个字符到数组中,返回读取到的有效字符个数, 读取到末尾时,返回 -1 字符输出流Writervoid write(int c) 写入单个字符。 void write(char[] cbuf) 写入字符数组。 abstract void write(char[] cbuf, int off, int len) 写入字符数组的某一部分,off数组的开始索引,len 写的字符个数。 void write(String str) 写入字符串。 void write(String str, int off, int len) 写入字符串的某一部分,off 字符串的开始索引,len写的字符个 数。 void flush() 刷新该流的缓冲。 void close() 关闭此流,但要先刷新它。 FileWriter类FileWriter(File file) : 创建一个新的 FileWriter,给定要读取的File对象。 FileWriter(String fileName) : 创建一个新的 FileWriter,给定要读取的文件的名称。 当你创建一个流对象时,必须传入一个文件路径,类似于FileOutputStream。 写出字符: write(int b) 方法,每次可以写出一个字符数据 关闭和刷新 flush :刷新缓冲区,流对象可以继续使用。 close :先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了 写出字符数组 : write(char[] cbuf) 和write(char[] cbuf, int off, int len) ,每次可以写出字符数 组中的数据,用法类似FileOutputStream 写出字符串: write(String str) 和write(String str, int off, int len) ,每次可以写出字符串中的 数据,更为方便 续写和换行:操作类似于FileOutputStream 属性集 Properties类构造: public Properties() :创建一个空的属性列表 基本储存方法: public Object setProperty(String key, String value) : 保存一对属性。 public String getProperty(String key) :使用此属性列表中指定的键搜索属性值。 public Set<String> stringPropertyNames() :所有键的名称的集合。 第十天 缓冲流 缓冲字节输出流: BufferedOutputStream 字节缓冲流 |_ BufferedInputStream # 缓冲字节输入流 |_ BufferedOutputStream # 缓冲字节输出流 字符缓冲流 |_ BufferedReader # 缓冲字符输入流 |_ BufferedWriter # 缓冲字符输出流 java.io.BufferedOutputStream类: 缓冲字节输出流 // 构造方法 BufferedOutputStream(OutputStream out): 使用基本流创建一个缓冲字节输出流 BufferedOutputStream(OutputStream out, int size): 使用基本流创建一个缓冲字节输出流, 设置 缓冲区大小 BufferedOutputStream使用步骤: 1.创建FileOutputStream对象, 构造方法中绑定要输出的目的地 2.创建BufferedOutputStream对象, 构造方法中传递FileOutputStream对象 3.使用BufferedOutputStream对象中的方法 write(), 把数据写入到内部缓冲区中 4.使用BufferedOutputStream对象中的方法 flush(), 把内部缓冲区中的数据,刷新到文件中 5.释放资源(会先调用flush方法刷新数据, 第4步可以省略) FileOutputStream fos = new FileOutputStream("文件路径"); BufferedOutputStream bos = new BufferedOutputStream(fos); bos.write("你好".getBytes()); // bos.flush(); // 可以省略 bos.close(); 缓冲字节输入流: BufferedInputStream java.io.BufferedInputStream类: 缓冲字节输入流 // 构造方法 BufferedInputStream(InputStream in): 使用基本流创建一个缓冲字节输入流 BufferedInputStream(InputStream in, int size): 使用基本流创建一个缓冲字节输入流, 设置缓冲 区大小 使用步骤: 1.创建FileInputStream对象, 构造方法中绑定要读取的数据源 2.创建BufferedInputStream对象, 构造方法中传递FileInputStream对象 3.使用BufferedInputStream对象中的方法 read(), 读取文件 4.释放资源 close() FileInputStream fis = new FileInputStream("文件路径"); BufferedInputStream bis = new BufferedInputStream(fis); byte[] bytes = new byte[1024]; int len; while ((len = bis.read(bytes)) != -1) { System.out.println(new String(bytes, 0, len)); } bis.close(); 缓冲流的效率测试: 复制文件 基本流 一次读写一个字节 一个字节 基本流 一次读写一个字节数组 一个字节数组 缓冲流 一次读写一个字节 一个字节数组 缓冲流 一次读写一个字节数组 两个字节数组 缓冲流 + 一次读写一个字节数组 效率最高 缓冲字符输出流: BufferedWriter // 构造方法 BufferedWriter(Writer out): 使用基本流创建一个缓冲字符输出流 BufferedWriter(Writer out, int size): 使用基本流创建一个缓冲字符输出流, 设置缓冲区大小 // 特有方法 void newLine(): 写入一个换行符, 换行符自动根据当前系统确定 转换流 字符编码和字符集编码: 字符 -> 字节 'a' -> 97 解码: 字节 -> 字符 97 -> 'a' 编码表: 字符和二进制数字的对应规则 字符集和编码表: 字符集包含编码表 ASCII字符集 ASCII编码表 ASCII扩展编码表 ISO-8859-1字符集: Tomcat Web服务器程序 Latin-1: 拉丁字符. 没有中文. 每个字符由1个byte组成 GB字符集 GB2312编码表: 每个字符由2个byte组成 GBK编码表: 每个字符由2个byte组成 GB18030编码表: 每个字符由1, 2, 4个byte组成 Unicode字符集 UTF-8: ASCII字符占1个byte, 拉丁字符占2个byte, 中文占3个byte, Unicode辅助字符占4个byte UTF-16 UTF-32 ANSI: 表示使用系统默认编码表 OutputStreamWriter类 OutputStreamWriter(OutputStream out): 使用默认编码表创建转换流 OutputStreamWriter(OutputStream out, String charsetName): 使用指定编码表创建转换流 // 使用默认UTF-8 OutputStreamWriter o = new OutputStreamWriter(new FileOutputStream("a.txt")); o.write("dsfdsfdsaf") // 使用指定的GBK OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"), "GBK"); osw.write("你") -> 查"GBK"码表 -> -1,-2 -> FileOutputStream -> a.txt文件 写数据: 字符流 --------------------------> 字节流 序列化流对象流对象序列化流: ObjectOutputStream // 构造方法 ObjectOutputStream(OutputStream out) // 特有成员方法 void writeObject(Object obj): 将对象写出 // 创建对象输出流 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student.txt")); // 写对象 Student s = new Student("小美女", 18); oos.writeObject(s); // 释放资源 oos.close(); 注意: 被读写的对象的类必须实现"java.io.Serializable"接口, 否则会抛出"NotSerializableException" 对象反序列化流: ObjectInputStream // 构造方法 ObjectInputStream(InputStream in) // 特有成员方法 Object readObject(): 读取对象 // 创建对象输入流 ObjectInputStream oos = new ObjectInputStream(new FileInputStream("student.txt")); // 读对象 Object o = oos.readObject(); Student s = (Student)o; System.out.println(s); // 释放资源 oos.close(); 今日API 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 java.io.OutputStreamWriter类: 输出转换流. 字符流通往字节流的桥梁 // 构造方法 OutputStreamWriter(OutputStream out): 使用默认编码表创建转换流 OutputStreamWriter(OutputStream out, String charsetName): 使用指定编码表创建转换流 java.io.InputStreamReader类: 输入转换流. 字节流通往字符流的桥梁 // 构造方法 InputStreamReader(InputStream in): 使用默认编码表创建转换流 InputStreamReader(InputStream in, String charsetName): 使用指定编码表创建转换流 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): 创建字节打印流, 输出到一个文件路径 java.lang.System类: // 静态方法 static void setOut(PrintStream out): 设置System.out的输出目的地为参数的打印流
|