视频出处 提取码: 24s1
IO流感觉都是大同小异 会了最基本的流操作 其他的流就差不多了
直观感觉多线程的学习还是比较简单的 但是对多线程的思想要求较高 以后在多做项目 领会多线程吧
随看随记
io流
File类
- 静态成员变量
- String类型 File.pathSeparator 与系统相关的路径分割符 win ; unix :
- String类型 File.separator 与系统相关的目录名称分割符 win \ unix /
- 构造函数
- 输入字符串构造 File(“D:\idea”)
- 输入父字符串路径和子字符串路径 File(“D:”,“idea”)
- 输入File类型父串路径和子字符串路径File(file , “idea”)
- 注意: 在输入单个字符串时要注意\ 要写成 \ \ 将其进行转义
- 当带有分隔符时 File myFile = new File(“C:” + File.separator + “tmp” + File.separator+ “test.txt”);
- FIle类的功能 默认已经进行了构造:File file = new File(“filepath”);
- idea中的相对路径默认是在被idea接管的文件夹内创建
- 创建功能
- 创建文件: 返回Boolean file.creatNewFile(); 根据构造函数中的路径和文件名创建文件 只能创建文件如果没有文件类型 默认记事本类型 创建成功返回true 如果文件已经存在则不再创建 返回false
- windows默认不区分大小写 而Linux和MACos时区分大小写的 windowsDOS 创建文件命令为echo LInux终端中创建文件命令为touch
- 创建文件夹: 返回Boolean file.mkdirs(); 根据构造函数中的路径和文件名创建多层文件夹 创建成功返回true 如果文件夹已经存在则不再创建 返回false
- windowsDOS 创建文件夹命令为md LInux终端中创建文件夹命令为mkdir
- 创建文件: 返回Boolean file.creatNewFile(); 根据构造函数中的路径和文件名创建文件 只能创建文件如果没有文件类型 默认记事本类型 创建成功返回true 如果文件已经存在则不再创建 返回false
- 删除功能
- 返回Boolean file.delete(); 根据构造函数中的路径和名字删除文件或文件夹 删除成功返回true 删除失败返回false
- 注意:
- 删除失败包括两种:1.没有该路径 2. 该路径的文件正在打开
- 删除方法直接从硬盘中删除,不经过回收站。
- windowsDOS 删除文件命令为del 删除文件夹为rd
- LInux终端中 删除文件命令为rm 删除文件夹为rm -d
- 获取功能
- 获取文件名 返回String file.getName(); 返回构造函数中的路径最后部分的名字
- 获取文件的字节数 返回long file.length(); 返回构造函数中的路径的文件的字节数
- 注意: length() 方法只能获得文件的字节数 对于文件夹的字节数是一个不确定数值,目前只知道文件夹不行,后面再看看原因什么的吧
- 获取该绝对路径的文件对象 返回File file.getAbsoluteFile();返回该构造函数的绝对路径的文件 file.getAbsolutePath();是返回String类型 可以通过第一个的toString方法获得
- 获取该父路径的文件对象返回File file.getParentFile();返回该构造函数的父路径的文件 file.getParen();是返回String类型 可以通过第一个的toString方法获得
- 获取File构造方法中封装的路径中的文件和文件名()遍历一个目录 返回File[] file.listFiles()返回的是目录或文件的全路径 file.list()返回的是String类型 只返回文件或文件夹的名称 可以通过第一个方法遍历时进行getName()来获得
- windowsDOS 查看文件夹的命令为dir Linux终端查看文件夹的命令为ls 一般使用ll
- 判断功能
- 判断路径是否存在 返回Boolean file.exists(); 存在该路径则返回True ,不存在则返回False
- 判断文件夹是否存在 返回Boolean file.isDirectory(); 该路径是个文件夹则返回True ,不不是则返回False
- 文件过滤器
文件名传递参数 在FileFilter中调用方法accept(); 在accept()中进行判断如果返回true则放到数组中 false则过滤掉
文件过滤器的简单lambda表达式写法 ->后面是过滤器的过滤条件
File[] listFiles = file.listFiles(pathname -> pathname.getName().endsWith(".java"));
//等同于
File[] listFiles = file.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.getName().endsWith(".java");
}
});
- 递归遍历所有的目录包括子目录中的java
public void getAllDir(File pathname) {
File[] files = pathname.listFiles(pathname1 -> pathname1.isDirectory()
|| pathname1.getName().toLowerCase().endsWith(".java"));
//lambda表达式完全展开
File[] files1 = pathname.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
if (pathname.isDirectory()) {
return true;
} else {
return pathname.getName().toLowerCase().endsWith("java");
}
}
});
for (File file : files) {
if (file.isDirectory()) {
getAllDir(file);
} else {
System.out.println(file);
}
}
}
字节流
字节输出流
- OutPutStream是所有字节输出流的超类 抽象类 每次只操作一个字节
- 常用方法 均是无返回值
- 写入内容
- outPutStream.write(int b) 写入一个字节
- outPutStream.write(byte[] b); 写入一个字节数组
- outPutStream.write(byte[] b ,int index ,int length); 写入一个字节数组,从index开始写入length长度个字节
- 关闭流对象
- outputStream.close(); 关闭该输出流对象
- 写入内容
FileOutputStream
- 构造方法参数可以是 FIle:已经封装好的文件 或 String:字符串的文件名
- OutputStream outputStream = new FileOutputStream(“pathName” 或 file); 注意需要抛出异常FileNotFoundException 继承于IOException
- 流对象的使用流程
- 创建流的子类对象,绑定数据目的(路径文件或封装好的文件对象)
- 调用流对象的write方法
- close释放资源
- 注意
- 流对象的构造方法可以创建一个文件,如果该路径已经存在文件,则直接覆盖。
- 对路径进行续写需要使用重载构造函数,将append参数赋值为true
- 流对象是通过字节来进行写入的 会将int转成字节 经过编码表输出到记事本中 因此 outputStream.write(100);会写入一个d (d的ASCII码是100)
- 输入的最简便办法是出入字符串,并将字符串转化成字节数组 outputStream.write(“hello\r\nworld”.getBytes());
- 流对象的异常处理
OutputStream outputStream = null;
try {
outputStream=new FileOutputStream("pathnamet",true);
outputStream.write("hello\r\nworld".getBytes());
} catch (IOException e) {
System.out.println(e.getMessage());
throw new RuntimeException("输出文件失败");
}finally {
try {
if (outputStream != null) {
outputStream.close();
}
} catch (IOException e) {
throw new RuntimeException("输出文件失败");
}
- 注意:
- 保证流对象的作用域足够大 流文件的定义放在所有异常作用域的最外面
- catch里要写出怎么处理异常 要输出异常信息,寻找并解决问题
- 如果流对象创建失败不需要关闭资源
字节输入流
- 构造方法和输入流读取文件的步骤与输出流相似 这里就不做记录了
- 常用方法
- 返回int read(); 执行一次就会自动读取下一个字节 ,返回读取到的字节
- 使用该方法读取文件所有内容
InputStream inputStream = new FileInputStream("pathname");
int length = 0;
while ((length = inputStream.read()) != -1) {
System.out.print((char) length);
}
- 返回int read(byte[] b); 执行一次就会自动装满整个字节数组,返回读取到的字节的个数 数组主要起到缓冲的作用
- 使用该方法读取文件所有内容
InputStream inputStream = new FileInputStream("pathname");
byte[] bytes = new byte[10];
int length=0;
while ((length=inputStream.read(bytes))!=-1){
System.out.print(new String(bytes,0,length));
/*当字节数组没有被装满时,会输出打印上一次数组未被覆盖的内容
用length来限定打印的数组长度,正好打印到接受的字节数*/
}
字节流复制文件
其本质就是在源文件出读取,在目标文件写入
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = new FileInputStream("inputPathName");
outputStream = new FileOutputStream("outputPathName");
byte[] bytes = new byte[1024*50];
int length = 0;
while ((length=inputStream.read(bytes))!=-1){
outputStream.write(bytes,0,length);
}
} catch (IOException e) {
new RuntimeException("文件复制异常");
} finally {
try {
if (outputStream != null) {
outputStream.close();
}
} catch (IOException e) {
new RuntimeException("输出流释放异常");
} finally {
try {
if (inputStream != null) {
outputStream.close();
}
} catch (IOException e) {
new RuntimeException("输入流释放异常");
}
}
}
- 注意:
- 输入输出流对象一定要指向数据源文件和目标源文件,不要仅仅指向该文件的文件夹
- 缓冲字节数组大小建议为1024byte即1kb的整数倍
- 文件流的异常处理比较复杂 需要理清
字符流
字符流只能用于文本文件 尤其对于有中文汉字的文本文件更为适合
也是抽象类 需要使用其子类FileWriter 和 FileReader
字符输出流 Writer
- 常用方法
- 写内容
- writer.write(int c) 写一个字符
- writer.write(char[] c); 写一个字符数组
- writer.write(char[] c ,int index ,int length); 写一个字符数组,从index开始写length长度个字节
- writer.write(String s); 写一个字符串
- 关闭流对象
- writer.close(); 关闭该输出流对象
- 输入流刷新 注意 每进行一次输出操作都必须对输出流进行刷新
- writer.flush(); 刷新输出流
- 写内容
字符输入流 Reader
- 常用方法
- 写入内容
- reader.read() 读取一个字符
- reader.read(char[] c); 读取一个字符数组
- 注 不能读取字符串
- 关闭流对象
- reader.close(); 关闭该输出流对象
注意 输入流不需要每一次输出操作进行刷新
其操作和字符流操作类似 这里就不多记录了
- reader.close(); 关闭该输出流对象
- 写入内容
转换流
两个转换流的类 都是继承于字符流 Writer 和 Reader类
- 接收字符输出字节 OutputStreamWriter
- 接收字节输出字符 inputStreamReader
- 其流程如图所示
inputStream = new FileInputStream("inputPathName");
outputStream = new FileOutputStream("outputPathName");
InputStreamReader ipsw = new InputStreamReader(inputStream,"GBK");
OutputStreamWriter opsw = new OutputStreamWriter(outputStream,"UTF-8");
char[] cbff= new char[2];
int length = 0;
while ((length = ipsw.read(cbff))!=-1){
opsw.write(cbff,0,length);
opsw.flush();
}
ipsw.close();
opsw.close();
注意: 当创建一个txt是GBK格式的时候 inputStreamReader的编码是GBK 但是要输出一个UTF-8格式时 需要在输出加“UTF-8” 其他和字符流 字节流差不多。
缓冲流
缓冲流的目的就是提高文件读写的顺序
字节缓冲流
除了构造方法不一样外 其余和不同字节流是一样的 主要是通过缓冲流对字节流进行缓冲 高效读写
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("outputPathname"));
BufferedInputStream bis= new BufferedInputStream(new FileInputStream("inputPathname"));
字符缓冲流
构造方法如下所示
BufferedWriter bw= new BufferedWriter( new FileWriter("writerPathNane"));
BufferedReader br= new BufferedReader( new FileReader("readerPathNane"));
- 字符输出缓冲流特有方法
- 无返回值 bw.newLine(); 输出一个换行 win为\r\n linux为\n
- 字符输入缓冲流
- 返回值String br.readLine(); 读取一行文本 以换行为结束 如果读取结束则返回-1
- 它只识别换行 但是不会输出读取换行 如果要输出还需要 bw.newLine();
- 带有编码表转换的字符缓冲流
BufferedWriter bw= new BufferedWriter( new OutputStreamWriter(new FileOutputStream(
"E:\\......\\b.txt"),"UTF-8"));
BufferedReader br= new BufferedReader(new InputStreamReader(new FileInputStream(
"E:\\......\\a.txt"),"GBK"));
- 实现文本复制的流程
BufferedWriter bw= new BufferedWriter( new OutputStreamWriter(new FileOutputStream(
"E:\\......\\b.txt"),"UTF-8"));
BufferedReader br= new BufferedReader(new InputStreamReader(new FileInputStream(
"E:\\......\\a.txt"),"GBK"));
String str=null;
while ((str= br.readLine())!=null){
bw.write(str);
bw.newLine();
bw.flush();
}
bw.close();
br.close();
小结
注
- 实线表示继承 虚线表示调用
- 虽然调用的是超类 但是都是创建其子类的对象 或者通过多态进行创建超类
Properties
继承自Hashtable ,实现了Map接口
在.properties文件中 用#表示注释 # 1=a
- 特有方法
- setProperty(String key , String Value) 等同于map的put方法
- getProperty(String key) 等同意map的get方法
- 加载 无返回值 load 读取字节或字符输入流的键值对,保存到集合中
- pro.load(InputStream in)
- pro.load(Reader read)
- 存储 无返回值 store 接受字节或字符输出流的键值对,写到文件中保存
- pro.store(outputStream, “” ) ; 后面是一个字符串参数,必须要加,一般只加一个空字符串
- pro.store( Writer write)
序列化
序列化是对象的序列化 对于静态的成员变量无法将其序列化
- 要序列化的类必须要实现serializable接口
对象流
- ObjectOutputStream
InputStream inputStream = new FileInputStream("FileInputPathName");
OutputStream outputStream = new FileOutputStream("FileOutputPathName");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(new Person("zhangsan", 23));
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
Object obj = objectInputStream.readObject();
System.out.println(obj);
objectInputStream.close();
objectOutputStream.close();
- ObjectOutputStream 构造方法如上
- 特有方法 无返回值 objectOutputStream.writeObject(new Person(“zhangsan”, 23)); 写入特有对象进行序列化
- ObjectInputStream 构造方法如上 其输入流必须是序列化的文件
- 特有方法 返回Object objectInputStream.readObject(); 对序列化文件进行反序列化读取
readObject的返回值是Object类型 如果需要低权限类需要强转 - 关键字 transient 阻止类中的成员变量序列化
- 当对象流写入后 但是在读取前修改了类 这时类的序列号会被改变 会产生序列号冲突异常
- 因此需要对序列化类设置一个特定的序列号 号是随意的
private static final long serialVersionUID = -1499574590977726397L;
打印流
- 打印流只能做输出的动作 不做输入 因此只需要关注数据目的即可
- 为其他流添加功能
- 永远不会抛出IOExeption 但是会抛出其他异常
- PrintStream
- 可以接收File String OutputStream三个类型
- PrintWriter
- 可以接收File String OutputStream Writer三个类型
- 注
- 他们的方法都是一样的 Print(cont) Println(cont) Print 的输出是原样输出 而write()输出是需要走编码表的。
- Print() 和 Println() 中针对char[] 重写了方法 因此对字符数组进行Print操作时会输出字符数组中的内容 而其他类型的数组只输出其数组的地址
- 打印流的自动刷新 其需要满足的条件
- 输出的数据目的必须是流对象
- 必须调用Println(), Printf(),format() 方法三个中的一个
- 构造方法的第二个参数autoFlush 置为true
- 打印流的文本复制
- 读取数据源用BufferedInputStream/BufferedReader
- 写数据用PringWriter 和 Println() 方法、
多线程
线程类的创建与使用
继承Thread 重写了run()方法
public class Subthread01 extends Thread{
@Override
public void run() {
运行的方法内容
}
}
- 直接调用类中的run()时 与普通方法没有任何区别 通过start()调用的run方法才会开启线程
- 线程开启的步骤
- 创建和启动一个线程
- 创建Thread子类对象
- 子类对象调用方法start()
- 让线程程序执行, JVM调用线程中的run
Subthread01 subthread01 = new Subthread01();
subthread01.setName("SbuThread01");
subthread01.start();
for (int i = 0; i < 100; i++) {
Thread td=Thread.currentThread();
System.out.println(td.getName());
}
- 当直接打印Thread.currentThread()时 内容为
Thread[main,5,main] Thread[Thread-0,5,main]
分别为 [当前线程 线程优先级 程序的主线程] - 获取线程名字方法
- 静态方法 返回值Thread Thread.currentThread(); 获取当前线程
- 返回值String td.getName(); 获取线程的线程名字 继承自Thread而且无法被重写
- 设置线程的名字
- 无返回值 subthread01.setName(“SbuThread01”);
- 线程休眠
- Thread.sleep(millis) 根据millis毫秒值进行休眠 时间过后苏醒 要处理异常 在线程类中无法抛出异常 因为父类中没有该异常 因此只能try catch finally 进行处理
实现Runable接口
创建方法与继承Thread类似
public class SubThread02 implements Runnable{
@Override
public void run() {
运行的方法内容
}
}
主要实现了线程类与对象的解耦
Thread thread = new Thread(new SubThread02());
thread.start();
for (int i = 0; i < 100; i++) {
Thread td=Thread.currentThread();
System.out.println(td.getName());
}
通过匿名内部类的方式
Thread thread = new Thread(new Runnable(){
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("Thread");
}
}
});
Thread thread = new Thread(() -> {
for (int i = 0; i < 100; i++) {
System.out.println("Thread");
}
});
- 线程的六个状态 (线程的一生)
- 线程池
- 使用工厂类Executors中的静态方法创建线程对象,指定线程的个数
- static ExecutorService newFixedThreadPool(int个数)返回线程池对象 返回的是ExecutorService接口的实现类(线程池对象)
- 接口实现类对象,调用方法submit (Ruunable r)提交线程执行任务
ExecutorService executorService = Executors.newFixedThreadPool(3);
executorService.submit(() -> {for (int i = 0; i < 100; i++)
System.out.println(Thread.currentThread().getName()+"...."+i);});
executorService.submit(() -> {for (int i = 0; i < 100; i++)
System.out.println(Thread.currentThread().getName()+"...."+i);});
executorService.submit(() -> {for (int i = 0; i < 100; i++)
System.out.println(Thread.currentThread().getName()+"...."+i);});
线程安全问题
同步代码块
private Object object = new Object();
synchronized (object){
if (ticketsTotal>0){
System.out.println(
Thread.currentThread().getName()+"还有"+ticketsTotal--+"张票");
}
}
()中object被称为同步锁 只有得到同步锁的线程才能去执行同步代码块的程序 即在同代码块中的线程是单线程的 无法多线程同时执行
同步方法
- 将同步代码块的内容封装成一个方法 并将该方法设置 synchronized 关键字 这个方法就是同步方法 其同步锁是 this 对于静态方法的同步锁是本类类名.class
public synchronized void sailTickets() {
if (ticketsTotal > 0) {
System.out.println(
Thread.currentThread().getName() + "还有" + ticketsTotal-- + "张票");
}
}
Lock接口
通过Lock() 接口的方法 代替同步代码块 进一步提高其安全性 主要防止当线程出现异常的时候 锁不会被释放的情况
在不管是否出现异常 都会在finally中释放锁
private int ticketsTotal = 1000;
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();
if (ticketsTotal > 0) {
try {
Thread.sleep(20);System.out.println(
Thread.currentThread().getName() +
"还有" + ticketsTotal-- + "张票");
} catch (InterruptedException e) {
}finally {
lock.unlock();
}
}
}
}
死锁现象
(感觉这个需要在后面真的做项目才遇到 看了视频的案例感觉有点空洞)
线程之间的通信
- 判断同步代码块是否成功主要看两个方面
- 同步代码块是否覆盖所有执行内容
- 同步代码块的同步锁是不是同一个内容
- 线程之间的交替通信
- input 线程执行完后唤醒notify() output线程
- output 线程执行完后唤醒notify() input线程
其思想
其代码如下所示
public class Resource {
private String name;
private String sax;
private boolean flag = true;
private Resource() {}
public static final Resource RESOURCE = new Resource();
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public String getSax() {
return sax;
}
public void setSax(String sax) {
this.sax = sax;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Input implements Runnable {
public Resource resource;
public Input(Resource resource) {
this.resource = resource;
}
@Override
public void run() {
int i = 0;
while (true) {
synchronized (resource) {
if (!resource.isFlag()) {
try {
resource.wait();
} catch (InterruptedException e) {
}
}
if ((i % 2) == 0) {
resource.setName("张三");
resource.setSax("男");
} else {
resource.setName("lisi");
resource.setSax("nv");
}
resource.setFlag(!resource.isFlag());
resource.notify();
}
i++;
}
}
}
public class Output implements Runnable {
public Resource resource;
public Output(Resource resource) {
this.resource = resource;
}
@Override
public void run() {
while (true) {
synchronized (resource) {
if (resource.isFlag()) {
try {
resource.wait();
} catch (InterruptedException e) {}
}
System.out.println( resource.getName() + "......" + resource.getSax());
resource.setFlag(!resource.isFlag());
resource.notify();
}
}
}
}
public static void main(String[] args) {
Resource resource=Resource.RESOURCE;
Thread threadInput = new Thread(new Input(resource));
Thread threadOutput = new Thread(new Output(resource));
threadInput.start();
threadOutput.start();
}
注
- 锁是否唯一 如果要使得锁唯一 则以定不能去new新对象 本案例是通过构造函数去接受main中的Resource对象 实现同步锁的唯一
- 线程的唤醒问题 在使用wait() 将线程等待之前 除了要将标志置反以外 还要对等待的线程进行唤醒操作 即notify()
- wait() 和 nofify() 的 是同步锁的对象方法 应该使用resource.wait() 和resource.nofify()
- 只有在同步代码块内的resource才能被称之为同步锁
- 一定是要输出先等待 输入后等待 输入反了的话 第一个输出就是null了