黑马程序员技术交流社区

标题: 笔记 [打印本页]

作者: 农家豆腐    时间: 2018-11-26 16:17
标题: 笔记
本帖最后由 农家豆腐 于 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(), 可以输出任意类型的值, 原样输出





























欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2