本帖最后由 农家豆腐 于 2018-11-26 16:47 编辑
day07 线程间通信 线程池 Lambda
线程间通信
基于java并发机制 cpu的高速切换 为有效利用资源
原因 为完成一个目标的共同合作
目的 有限资源 有效利用
如何实现合作 " 等待唤醒机制 "
等待唤醒机制
调用wait: 1. 释放锁 2.进入WAITING状态
离开WAITING: 1. 获取锁 2.等待CPU调用执行
wait会释放锁,notify仅仅只是通知,不释放锁
调用obj.wait()会立即释放锁,,以便其他线程可以执行obj.notify(),但是notify()不会立刻立刻释放sycronized(obj)中的obj锁,必须要等notify()所在线程执行完synchronized(obj)块中的所有代码才会释放这把锁.
而sleep()不会释放锁。
线程池
普通创建线程方式的缺点:
"创建"线程和"销毁"线程都是比较占用内存和CPU的操作.
对于一些数量多, 执行时间短的任务, 频繁的创建和销毁线程来执行, 会降低程序运行效率.
线程池:
一个容纳多个线程的容器(集合)
线程池可以解决的问题:
其中的线程可以反复使用, 省去了频繁创建线程对象的操作, 无需反复创建线程而消耗过多资源
线程池的工作原理:
提前创建好多个线程对象, 放在集合中. 多个任务来了反复使用这些线程对象来执行
线程池的创建和使用步骤:
1. 使用Executors的静态方法 newFixedThreadPool(int nThreads) 创建线程池ExecutorService
2. 创建一个任务类, 实现Runnable接口, 重写run()方法
3. 调用ExecutorService对象的 submit(Runnable task) 方法, 传递任务给线程池, 执行任务
4. 调用ExecutorService对象的 shutdown() 方法, 销毁线程池 (不建议执行)
函数式编程思想: Lambda表达式
Lambda表达式的3个部分:
1. 一些参数 ()
接口中抽象方法的参数列表. 没参数就空着; 有参数就写, 多个参数用逗号分隔
2. 一个箭头 ->
将参数传递给方法体
3. 一段代码 {}
重写接口抽象方法的方法体
函数式接口: "有且仅有一个抽象方法的接口"
但函数式接口对于 哪些方法算作抽象方法 有特殊规定:
1. 有方法体的方法"不算作"抽象方法, 如默认方法, 静态方法, 私有方法
2. 如果一个抽象方法 与 java.lang.Object类中的方法 定义相同的, 也"不算作"抽象方法
因为任何实现本接口的实现类, 都会直接或间接继承java.lang.Object类的public的方法, 所以
在创建实现类时其实不用重写该抽象方法, 也就不算作抽象方法
Lambda省略格式和使用前提
省略原则:
"可推导的都可省略" (凡是能根据前后代码能猜测出来的代码, 都可以省略不写)
可以省略的部分:
1. (参数列表): 参数"类型"可以省略 (a, b) -> {}
2. (参数列表): 如果参数只有1个, 则"类型"和"小括号"都可以省略 a -> sout(a)
3. {一些代码}: 如果只有一条代码, 则"大括号", "return", "分号"都可以"一起省略"
*函数式接口:
函数式接口: "有且仅有一个抽象方法的接口"
但函数式接口对于 哪些方法算作抽象方法 有特殊规定:
1. 有方法体的方法"不算作"抽象方法, 如默认方法, 静态方法, 私有方法
2. 如果一个抽象方法 与 java.lang.Object类中的方法 定义相同的, 也"不算作"抽象方法
因为任何实现本接口的实现类, 都会直接或间接继承java.lang.Object类的public的方法, 所以
在创建实现类时其实不用重写该抽象方法, 也就不算作抽象方法
Lambda表达式的使用前提:
1. Lambda只能用于接口, 且"接口中有且仅有一个抽象方法"(也称为"函数式接口")
普通类, 抽象类不能用
2. 使用Lambda必须具有上下文推断
接口中只能有一个抽象方法, 才能推断出来重写的是这个抽象方法
day08 File类 递归
File类
路径分隔符可用" / "
"文件"和"目录"的路径名的抽象表现形式, 主要用于文件和目录的创建, 查找和删除等操作
File的构造方法创建File对象, 并将其指向该路径. 不会真正在磁盘上创建这个文件
三个构造方法
File File(String pathname): 根据 路径字符串 封装一个File对象
File File(String parent, String child): 根据 父路径字符串 和 子路径字符串 封装File对象
File File(File parent, String child): 根据 父路径的File对象 和 子路径 封装File对象
获取方法
getAbsolutePath(): 绝对路径
getPath(): File对象的封装路径 (创建对象时传入的路径)
String getName(): File对象的文件名或目录名
long length(): 获取File表示的"文件"大小的字节byte数 (不能获取目录的大小)
判断方法
boolean exists(): File对象代表的文件或目录是否实际存在
boolean isDirectory(): File表示的是否为目录
boolean isFile(): File表示的是否为文件
创建删除方法
boolean createNewFile(): 当文件不存在时, 创建一个新的空文件
false: 路径已经存在(无论文件还是目录)
抛IO异常: 写的路径不符合逻辑 (Y:\\a.txt\dsfsd)
boolean delete(): 删除由此File表示的文件或目录.
删除目录时: 必须是空目录
boolean mkdir(): 创建File表示的目录
false: 1. 路径已经存在(无论文件还是目录) 2. 写的路径不符合逻辑 (Y:\\a.txt\dsfsd)
boolean mkdirs(): 创建File表示的多级目录
false: 1. 路径已经存在(无论文件还是目录) 2. 写的路径不符合逻辑 (Y:\\a.txt\ds)
遍历目录方法
String[] list(): 获取当前File目录下的所有子文件或目录的名字数组
File[] listFiles(): 获取当前File目录中的所有子文件或目录的File对象数组
递归
递归时的注意事项:
1. 递归要有限定条件(出口), 保证递归能够停止(就是在某种情况下方法不再调用自己), 否则会栈内存溢出
2. 递归次数不能太多, 否则会栈内存溢出
3. 构造方法不能递归
过滤器
调File类中 成员方法 listFiles, 返回File对象数组,参数为
接口 : FilenameFilter 或 FileFilter.
需要重写接口内 accept() 方法
java.io.FileFilter接口
FileFilter 接口中 抽象方法 accept( File pathname )
抽象路径名是否包含在某个路径名列表内
用于File对象的过滤器
java.io.FilenameFilter接口
FilenameFilter 接口 抽象方法 accept(File dir ,String name)
指定文件是否包含在某一文件列表内
将File对象拆分为父路径和子路径来判断的过滤器
day09 字节流 字符流
|
输入流
| 输出流
| 字节流
| 字节输入流 InputStream
| 字节输出流 OutputStream
| 字符流
| 字符输入流 Reader
| 字符输出流 Writer
|
FileOutputStream类
构造方法的作用:( 指定读取的数据源)
写出字节数据
1.写进去一个字节数组 write( int i )
十进制的数字会被转换为"二进制"的数字写入文件 ,打开文本文件时,根据编码表,二进制数字转换为对应字符
2.写出字节数组: write(byte[ ] b)
读到的字节存放在参数中的字节数组中, 返回int值是本次读到的字节的个数. 读到文件末尾返回-1
3.写出指定长度字节数组: write(byte[] b, int off, int len)
看到的内容是经过编辑器,真实内容依然为byte字节
数据追加续写
每次程序运行,创建输出流对象,都会清空目标文件中的数据。
(File file / String name , boolean append) true 表示追加数据
FileInputStream类
读取字节数据 ( byte 转换为 int )
1.读取字节: read 方法,每次可以读取一个字节的数据,提升为int类型,读取到文件末尾,返回-1
2.字节数组读取: byte[ ] 作为装字节数据的容器
构造方法的作用:( 指定写入的目的地 )
1. 创建FileInputStream对象
2. 将FileInputStream对象指向磁盘上的文件
字符流 (字符流只能读写"文本文件")
字符输入流: Reader
FileReader
构造方法的作用:
1. 创建FileReader对象
2. 将FileReader对象指向磁盘上的文件
(UTF-8中文占3个, GBK占2个)
FileInputStream
直接打印为 字节
1. 单字节字符
可在前方加 (char) 强转为char类型接收,即可正常显示
2. 多字节字符
用byte数组接收
需要用byte数组,一旦单字符多字节使用顺序错误,就会出现乱码
不建议使用,后面有专门的方法
read() 方法 读取一个字节时,会自动提升为int类型,需要用int变量接收
FileOutputStream
直接输入为字节
1.单字节
默认编译器编译,直接显示内容
2.byte数组
一种直接新建byte数组,
一种是用 String 类的 getBytes( )方法
write(int b): 一次写一个字节 (参数int便于传递byte的整数,不用强转)虽然参数为int类型四个字节,但是只会保留一个字节的信息写出
FileOutputStream 创建的文件路径 ,如果没有这个文件,会创建该文件。如果有这个文件,会清空这个文件的数据。
个文件路径。该路径下,如果没有该文件,会抛出FileInputStream 传入的文件路径下,如果没有该文件,会抛异常
FileReader
1. 单字符 char类型
会自动提升为 int 类型, 所以需要用 int 接收
2. 字符数组 char[ ] 类型
返回为 字符个数, 所以 也需要用 int 接收
可将数组转换为 字符串 ( String类的 String(char[] value) , 将转换后存放在数组内容,转换为 String 字符串 )
FileWriter
1. 单字符
用 int 类型作为参数写一个字符 (为了代码编写方便)
2.字符数组
3.字符串
构造方法中的创建文件 , 如果文件已存在, 则清空文件内容
注意: write()方法只是将数据写到内存缓冲区, 最后必须调用flush()或close()才能将数据真正写入磁盘
Properties集合
双列集合 键值 都是 String类型
1. 添加键值对 setProperty
2. 键找值 getProperty
3. 获取键集合 set<String> stringPropertyNames
4. 写入字节/字符流,添加注释
store ( OutputStream out / Writer writer,String comments)
5.写入字节/字符流,添加注释
store ( OutputStream out / Writer writer,String comments)
6.从配置文件中通过字节/字符流加载数据到Properties集合
load (InputStream inStream / Reader reader)
day10 缓冲流 转换流 序列化流 打印流
缓冲流
底层也是使用基本流(FileXxx)来读写
缓冲字节输出流: BufferedOutputStream
缓冲字节输入流: BufferedInputStream
缓冲流 + 一次读写一个字节数组 效率最高
缓冲字符输出流: BufferedWriter
//特有方法
newLine(): 写入一个换行符, 换行符自动根据当前系统确定
缓冲字符输入流: BufferedReader
// 特有方法
String readLine(): 一次读一行字符串, "不包含换行符". 读到文件末尾返回null
转换流
编码: 字符 -> 字节 'a' -> 97
解码: 字节 -> 字符 97 -> 'a'
编码表: 字符和二进制数字的对应规则
(UTF-8中文占3个字节, GBK占2个字节)
乱码原因: 读写编码不一致
GBK文件中存储的是"你好"在GBK中对应的byte
而IDEA中使用FileReader读取文件时, 是将byte按照UTF-8编码表查找字符, 结果找不到, 就显示了问号
转换流原理: 字符流和转换流的关系
查指定编码表
OutputStreamWriter类
字符流 ----> 字节流
InputStreamReader类
字节流 ----> 字符流
字符流 = 编码表 + 字节流
转换流主要用于解决Web开发中的乱码问题
序列化流(对象流)
序列化: 对象 -> 字节
对象序列化流: ObjectOutputStream
反序列化: 字节 -> 对象
对象反序列化流: ObjectInputStream
transient瞬态关键字: 避免属性序列化
static 修饰的成员变量属于类不属于对象, 所以不能序列化
transient 修饰的成员变量, 不能被序列化
transient 应用场景:
如果对象的某个属性不希望被序列化, 可以使用 transient 修饰, 这样就不会被对象流写到文件中
InvalidClassException异常: 原因和解决方案
序列化操作时, 会根据类生成一个默认的 serialVersionUID 属性, 写入到文件中.
该版本号是根据类中成员计算出来的一个数值. 当类的成员发生改变时, 序列版本号也会发生变化
当代码中的类和读取的对象序列版本号不一致时, 就会抛出InvalidClassException
为了避免这种问题, 我们可以手动写死这个序列号, 这样无论成员如何变化, 序列版本号都是一样的
打印流
打印流PrintStream
1. 只有输出流, 没有输入流
2. PrintStream不会抛出IOException
3. 有特殊方法 print(), println(), 可以输出任意类型的值, 原样输出
|
|