黑马程序员技术交流社区
标题: [石家庄校区]线程池、Lambda表达式、递归及IO流1 [打印本页]
作者: Yilin 时间: 2018-8-7 12:00
标题: [石家庄校区]线程池、Lambda表达式、递归及IO流1
本帖最后由 小石姐姐 于 2018-8-9 11:27 编辑
一、线程池
1.1 线程间通信
线程间通信: 多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同(需要协作)
为什么要进行线程间通信:
通常是竞争关系: 多个线程并发执行时, 在默认情况下CPU是随机切换线程的.
有时也需要合作: 当我们需要多个线程来共同完成一件任务, 并且我们希望他们有规律的执行, 那么多线程之间需
要一些协调通信, 以此来帮我们达到多线程共同操作一份数据
如何保证线程间通信有效利用资源:
等待唤醒机制
1.2 等待唤醒机制介绍
1.2.1等待唤醒机制
一个线程进行了规定操作后, 就进入等待状态( wait() ), 等待其他线程执行完他们的指定代码过后, 再将其唤
醒( notify() )
在有多个线程进行等待时, 如果需要, 可以使用notifyAll() 来唤醒所有的等待线程
wait/notify 就是线程间的一种协作机制, 用于解决线程间通信的问题
1.2.2等待唤醒中的方法
java.lang.Object类:
// 成员方法 (只能通过"锁对象"调用)
void notify(): 随机唤醒在同一个锁对象上的某一个处于等待状态的线程
void notifyAll(): 唤醒所有在同一个锁对象上处于等待状态的线程
void wait(): 让当前线程处于无限等待状态, 同时释放锁
wait和notify/notifyAll的执行原理:
wait:
线程不再活动, 不再参与调度, 进入 waitset 中, 因此不会浪费 CPU 资源, 也不会去竞争锁, 这时的线
程状态即是"WAITING". 它还要等着别的线程执行"通知(notify)", 让在锁对象上等待的线程从 wait set 中释放出
来, 重新进入到调度队列(ready queue)中
notify/notifyAll:
哪怕只通知了一个等待的线程, 被通知线程也不能立即恢复执行, 因为它当初中断的地方是在同步块内, 而
此刻它已经不持有锁, 所以它需要再次尝试去获取锁(很可能面临其它线程的竞争), 成功后才能在当初调用 wait() 之
后的地方恢复执行
总结如下:
如果能获取锁, 线程就从"WAITING"状态变成"RUNNABLE"状态
否则, 从 wait set 出来, 又进入 entry set, 线程就从"WAITING"状态又变成"BLOCKED"状态
1.2.3调用 wait() 和 notify() 需要注意的细节:
1. wait() 与notify() 必须要由"同一个锁对象"调用
因为对应的锁对象可以通过 notify() 唤醒使用同一个锁对象调用的 wait() 后的线程
2. wait() 与notify() 是属于Object类的方法
因为锁对象可以是任意对象, 而任意对象的所属类都是继承了Object类的
3. wait() 与notify() 必须要在"同步代码块"或者是"同步方法"中使用
因为必须要通过锁对象调用这2个方法
1.3 线程池1.3.1线程池介绍
普通创建线程方式的缺点:
"创建"线程和"销毁"线程都是比较占用内存和CPU的操作.
对于一些数量多, 执行时间短的任务, 频繁的创建和销毁线程来执行, 会降低程序运行效率.
线程池:
一个容纳多个线程的容器
线程池可以解决的问题:
其中的线程可以反复使用, 省去了频繁创建线程对象的操作, 无需反复创建线程而消耗过多资源
线程池的工作原理:
提前创建好多个线程对象, 放在集合中. 多个任务来了反复使用这些线程对象来执行
1.3.2线程池的代码实现
Executors
java.util.concurrent.Executors类: 线程池工厂类, 用于管理线程池
// 静态方法:
static ExecutorService newFixedThreadPool(int nThreads): 创建固定数量线程的线程池(常用)
一哥载Q特儿 色儿微斯
java.util.concurrent.ExecutorService接口: 真正执行任务的线程池服务
// 成员方法:
Future submit(Runnable task): 提交一个Runnable任务
void shutdown(): 通知线程执行完任务后关闭. 如不调此方法, 则线程执行完任务后仍在运行以便重复使用
线程池的创建和使用步骤:
1. 使用Executors的静态方法 newFixedThreadPool(intnThreads) 创建线程池ExecutorService
2. 创建一个任务类, 实现Runnable接口, 重写run()方法
3. 调用ExecutorService对象的 submit(Runnable task) 方法, 传递任务给线程池, 执行任务
4. 调用ExecutorService对象的 shutdown() 方法, 销毁线程池 (不建议执行)
二、Lambda表达式2.1函数式编程思想概述
2.1.1函数式:
在数学中, 函数就是有输入量, 输出量的一套计算方案, 也就是"拿什么东西做什么事情"
面向对象: 强调"用哪个对象的哪个方法"来做事 (注重语法形式)
函数式: 强调做事 (不关心用什么对象, 重写什么方法)
函数式编程的好处: 简化代码编写
2.1.2冗余的Runnable代码
// 我们要通过Lambda表达式简化以下的代码
[AppleScript] 纯文本查看 复制代码
new Thread(new Runnable() {
@Override
public void run() {
// 要执行的代码才是重要的
}
}).start();
关键代码是: run()方法中要执行的任务
而其他代码都只是形式
2.1.3编程思想的转换, 体验Lambda更优写法
JDK 8 中加入的Lambda表达式, 是函数式编程思想中的重点
对比:
// 面向对象方式的代码
[AppleScript] 纯文本查看 复制代码
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() +"新线程创建了");
}
}).start();
// 函数式编程的代码
new Thread(()-> {
System.out.println(Thread.currentThread().getName() +"新线程创建了");
}
).start();
2.2Lambda标准格式2.2.1 Lambda标准格式
Lambda表达式的3个部分:
1. 一些参数 ()
接口中抽象方法的参数列表. 没参数就空着; 有参数就写, 多个参数用逗号分隔
2. 一个箭头 ->
将参数传递给方法体
3. 一段代码 {}
重写接口抽象方法的方法体
格式:
// 写成一行
(参数列表) -> {一些重写方法的代码}
// 写成多行
(参数列表) -> {
一些重写方法的代码
}
2.2.2 Lambda省略格式和使用前提
省略原则:
可推导的都可省略 (凡是能根据前后代码能猜测出来的代码, 都可以省略不写)
可以省略的部分:
1. (参数列表): 参数"类型"可以省略
2. (参数列表): 如果参数只有1个, 则"类型"和"小括号"都可以省略 a -> sout(a)
3. {一些代码}: 如果只有一条代码, 则"大括号", "return", "分号"都可以"一起省略"
2.3 函数式接口2.3.1函数式接口介绍
函数式接口"有且仅有一个抽象方法"
但函数式接口对于哪些方法算作抽象方法有特殊规定:
1. 有方法体的方法"不算作"抽象方法,如默认方法, 静态方法, 私有方法
2. 抽象方法与java.lang.Object类中的方法定义相同的, 也"不算作"抽象方法
因为任何实现本接口的实现类, 都会直接或间接继承java.lang.Object类的public的方法, 所以在创建
实现类时其实不用重写该抽象方法, 也就不算作抽象方法
2.3.2Lambda表达式的使用前提:
1. Lambda只能用于接口, 且"接口中有且仅有一个抽象方法"(也称为"函数式接口")
普通类, 抽象类不能用
2. 使用Lambda必须具有上下文推断
方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例
(简而言之: 作为参数类型的接口, 必须是函数式接口)
比如以下构造方法
Thread(Runnable r): 该构造方法的参数类型是Runnable
// Runnable接口中, 有且仅有一个抽象方法, 该接口就是一个函数式接口, 可以使用Lambda表达式
[AppleScript] 纯文本查看 复制代码
public interface Runnable {
public abstract void run();
}
// Comparator接口中, 有且仅有一个抽象方法
public interface Comparator<T> {
// 这是该接口中有且仅有的一个抽象方法
int compare(T o1, T o2);
// 该方法与Object类中equals定义相同, 所以不算抽象方法
boolean equals(Object obj);
// 一些default方法, 有方法体, 不算抽象方法
// 一些静态方法, 有方法体, 不算抽象方法
}
// 我们今天自己定义的接口, 也满足函数式接口的要求
public interface Cook {
void makeFood();
}
2.4 使用Lambda标准格式(简化线程创建)
代码:
[AppleScript] 纯文本查看 复制代码
public class Test {
public static void main(String[] args) {
// 匿名内部类方式
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() +"新线程创建了");
}
}).start();
// Lambda表达式方式
new Thread(() -> {
System.out.println(Thread.currentThread().getName() +"新线程创建了");
}
).start();
}
}
2.5使用Lambda标准格式(重写有参有返回值方法)2.5.1 数组排序
代码:
[AppleScript] 纯文本查看 复制代码
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test {
public static void main(String[] args) {
// 创建Person数组, 静态初始化
Person[] persons = {
new Person("柳岩", 38),
new Person("迪丽热巴", 18),
new Person("古力娜扎", 19)
};
// 匿名内部类
// Arrays.sort(persons, new Comparator<Person>() {
// @Override
// public int compare(Person o1, Person o2) {
// return o1.getAge() - o2.getAge();
// }
// });
// Lambda表达式
Arrays.sort(persons, (Person o1, Person o2) -> {
return o1.getAge() - o2.getAge();
});
for (Person person : persons) {
System.out.println(person);
}
}
}
2.6使用Lambda标准格式(自定义接口, 抽象方法有参有返回值)
2.6.1计算器
需求:
给定一个计算器 Calculator 接口,内含抽象方法 calc 可以将两个int数字相加得到和值:
[AppleScript] 纯文本查看 复制代码
public interface Calculator { // 计算器
int calc(int a, int b); // 计算a和b的结果返回
}
在下面的代码中,请使用匿名内部类和Lambda的标准格式调用 invokeCalc 方法,完成120和130的相加计算:
public class Demo08InvokeCalc {
public static void main(String[] args) {
// TODO 请在此使用匿名内部类方式调用invokeCalc方法来计算120+130的结果
// TODO 请在此使用Lambda【标准格式】调用invokeCalc方法来计算120+130的结果
}
private static void invokeCalc(int a, int b, Calculatorcalculator) {
int result = calculator.calc(a, b);
System.out.println("结果是:" + result);
}
}
代码:
/*
计算器
*/
[AppleScript] 纯文本查看 复制代码
public interface Calculator {
// 计算两个数的结果
int calc(int a, int b);
}
public class Test {
public static void main(String[] args) {
// 匿名内部类方式
invokeCalc(120, 130, new Calculator() {
@Override
public int calc(int a, int b) {
return a + b;
}
});
// Lambda表达式方式
invokeCalc(120, 130, (int a, int b) -> {
return a + b;
});
}
// 调用计算器
public static void invokeCalc(int a, int b, Calculatorcalculator) {
int result = calculator.calc(a, b);
System.out.println(result);
}
}
2.7 线程池常用API
java.lang.Object类:
// 成员方法
void wait(): 使用锁对象调用, 当前线程进入WAITING无限等待状态, 直到被其他线程唤醒
void notify(): 使用锁对象调用, 随机唤醒一个处于等待状态的线程
void notifyAll(): 使用锁对象调用, 唤醒所有处于等待状态的线程
java.util.concurrent.Executors类: 线程池工厂类, 用于管理线程池
// 静态方法:
static ExecutorService newFixedThreadPool(int nThreads): 创建固定数量线程的线程池(常用)
java.util.concurrent.ExecutorService接口: 真正执行任务的线程池服务
// 成员方法:
Future submit(Runnable task): 提交一个Runnable任务
void shutdown(): 通知线程执行完任务后关闭. 如不调此方法, 则线程执行完任务后仍在运行以便重复使用
三、IO流
3.1 File类
3.1.1 File类概述
java.io.File类:文件和目录的路径名的抽象表现形式, 主要用于文件和目录的创建,查找和删除等操作
我们可以对File进行的操作:
创建文件/目录
删除文件/目录
获取文件/目录
判断文件/目录是否存在
对目录进行遍历
获取文件的大小
重要英文单词的含义: (起变量名时会用到)
file: 文件
directory: 目录
path: 路径
3.1.2File类的静态成员变量
java.io.File类:文件和目录的路径名的抽象表现形式, 主要用于文件和目录的创建,查找和删除等操作
// 静态成员变量
static String pathSeparator: 路径分隔符的字符串形式
static char pathSeparatorChar: 路径分隔符的char形式
Windows系统是 分号;
Linux系统是 冒号:
static String separator: 文件名称分隔符的字符串形式
static char separatorChar: 文件名称分隔符的char形式
Window系统是 反斜杠\
Linux系统是 正斜杠/
3.1.3绝对路径和相对路径
绝对路径:
以盘符开始的路径
如: "D:\\a\\hi.txt"
相对路径:
不以盘符开始的简化路径. 相对于项目的根目录
如: "a\\1.mp3", "123.txt"
注意事项:
1. 路径不区分大小写 (在Windows系统中不区分大小写,Linux, Mac区分)
2. 路径一般写成字符串, 而字符串中一个\是转义, 所以要写两个\\
3.14File类构造方法
java.io.File类:文件和目录的路径名的抽象表现形式, 主要用于文件和目录的创建,查找和删除等操作
// 构造方法(创建了File对象, 并将其指向该路径. 不会真正在磁盘上创建这个文件)
File File(String pathname): 根据 路径字符串 封装一个File对象
File File(String parent, String child): 根据 父路径字符串 和 子路径字符串 封装File对象
File File(File parent, String child): 根据 父路径的File对象 和 子路径 封装File对象
3.15File类获取方法
java.io.File类
// 常用获取方法
String getAbsolutePath(): 返回此File的绝对路径名字符串
String getPath(): 获取File对象的封装路径 (创建对象时传入的路径)
String getName(): 获取File对象的文件名或目录名
long length(): 获取File表示的文件大小的字节数 (不能获取目录的大小)
3.16File类判断方法
java.io.File类
// 常用判断方法
boolean exists(): 判断File对象代表的文件或目录是否实际存在
boolean isDirectory(): 判断File表示的是否为目录
boolean isFile(): 判断File表示的是否为文件
3.17File类创建删除方法
java.io.File类
// 常用创建删除方法
boolean createNewFile(): 当文件不存在时, 创建一个新的空文件
boolean delete(): 删除由此File表示的文件或目录. (删除目录时必须是空目录)
boolean mkdir(): 创建File表示的目录
boolean mkdirs(): 创建File表示的多级目录
3.18File类遍历目录方法
java.io.File类
// 常用获取目录中内容的方法
String[] list(): 获取当前File目录下的所有子文件或目录的名字数组
File[] listFiles(): 获取当前File目录中的所有子文件或目录的File对象数组
注意:
只能用表示目录的File对象调用
用文件的File对象, 或者路径不存在, 调用会报错
3.2递归
3.2.1递归的概念, 分类, 注意事项
递归思想:
遇到一个问题时, 将该问题拆解成可以解决的小问题, 如果解决不了, 继续拆解为更小的问题. 如果小问题解决
了, 大问题也就能够解决
Java中实现递归的方式:
方法内部调用方法自己 (所以必须定义方法)
递归的分类:
直接递归: 方法自己调用方法
间接递归: A方法调用B方法, B方法调用C方法, C方法调用A方法
递归时的注意事项:
1. 递归要有限定条件(出口), 保证递归能够停止(就是在某种情况下方法不再调用自己), 否则会栈内存溢出
2. 递归次数不能太多, 否则会栈内存溢出
3. 构造方法不能递归
递归的使用前提:
调用方法时, 方法的主体不变, 但每次传递的参数值不同, 可以使用递归
3.2.2递归求n的阶乘
代码:
[AppleScript] 纯文本查看 复制代码
public class Test {
public static void main(String[] args) {
int jc = jc(5);
System.out.println(jc); // 120
}
// 计算n的阶乘
public static int jc(int n) {
// 出口
if (n == 1) {
return 1; // 能解决问题, 直接写代码
}
// 如果不能解决, 则拆解问题
return n * jc(n-1);
}
}
3.2.3递归打印多级目录
代码:
[AppleScript] 纯文本查看 复制代码
public class Test {
public static void main(String[] args) {
File dir = new File("day08");
print(dir);
}
// 定义方法: 打印某个目录中所有路径
public static void print(File dir) { // 传入要打印的目录的File对象
// 先打印当前目录的路径
System.out.println(dir);
// 获取当前目录中的所有子文件和子目录
File[] files = dir.listFiles();
// 遍历数组
for (File file : files) {
// file代表子文件或者子目录, 具体是什么还需要判断
if (file.isFile()) {
// 如果是文件, 则打印文件
System.out.println(file);
} else {
// 如果是目录, 则递归调用当前方法, 打印"该子目录"中的所有路径
print(file); // 注意传入的是子目录对象!!!!!
}
}
}
}
3.2.4综合案例之多级目录搜索
代码:
[AppleScript] 纯文本查看 复制代码
public class Test {
public static void main(String[] args) {
print(new File("day08"));
}
// 定义方法: 打印某个目录中的.java文件
public static void print(File dir) {
// 先获取当前目录中的所有文件
File[] files = dir.listFiles();
// 遍历
for (File file : files) {
// 判断是文件还是目录
if (file.isFile()) {
// 是文件, 判断后缀
if(file.getName().toLowerCase().endsWith(".java")) {
// 是java文件, 打印
System.out.println(file);
}
} else if (file.isDirectory()) {
// 是目录, 递归调用方法
print(file);
}
}
}
}
3.2FileFilter文件过滤器的原理和使用
3.3.1 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则不加入
3.3.2FileFilter文件过滤器的使用和Lambda优化
代码:
[AppleScript] 纯文本查看 复制代码
public class Test {
public static void main(String[] args) {
print(new File("day08"));
}
// 打印指定目录中的.java文件
public static void print(File dir) {
// 获取当前目录中的文件, 使用过滤器
/*File[] files = dir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return new File(dir, name).isDirectory() ||
name.toLowerCase().endsWith(".java");
}
});*/
// 使用Lambda表达式简化
File[] files = dir.listFiles((d, name) -> new File(d,name).isDirectory() ||
name.toLowerCase().endsWith(".java"));
// 遍历
for (File file : files) {
if (file.isFile()) {
// 是文件, 则打印
System.out.println(file);
} else if (file.isDirectory()) {
// 是目录, 则递归
print(file);
}
}
}
}
3.4File类常用API
java.io.File类:文件和目录的路径名的抽象表现形式, 主要用于文件和目录的创建,查找和删除等操作
// 静态成员变量
static String pathSeparator: 路径分隔符的字符串形式
static char pathSeparatorChar: 路径分隔符的char形式
Windows系统是 分号;
Linux系统是 冒号:
static String separator: 文件名称分隔符的字符串形式
static char separatorChar: 文件名称分隔符的char形式
Window系统是 反斜杠\
Linux系统是 正斜杠/
// 构造方法(创建了File对象, 并将其指向该路径. 不会真正在磁盘上创建这个文件)
File File(String pathname): 根据 路径字符串 封装一个File对象
File File(String parent, String child): 根据 父路径字符串 和 子路径字符串 封装File对象
File File(File parent, String child): 根据 父路径的File对象 和 子路径 封装File对象
// 常用获取方法
String getAbsolutePath(): 返回此File的绝对路径名字符串
String getPath(): 获取File对象的封装路径 (创建对象时传入的路径)
String getName(): 获取File对象的文件名或目录名
long length(): 获取File表示的文件大小的字节数 (不能获取目录的大小)
// 常用判断方法
boolean exists(): 判断File对象代表的文件或目录是否实际存在
boolean isDirectory(): 判断File表示的是否为目录
boolean isFile(): 判断File表示的是否为文件
// 常用创建删除方法
boolean createNewFile(): 当文件不存在时, 创建一个新的空文件
boolean delete(): 删除由此File表示的文件或目录. (删除目录时必须是空目录)
boolean mkdir(): 创建File表示的目录
boolean mkdirs(): 创建File表示的多级目录
// 常用获取目录中内容的方法
String[] list(): 获取当前File目录下的所有子文件或目录的名字数组
File[] listFiles(): 获取当前File目录中的所有子文件或目录的File对象数组
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则不加入
3.5 IO流简介3.5.1 IO的概念
IO流:输入(In)输出(Out)流
流: 数据流
输入: 从硬盘读取到内存
输出: 从内存写入到硬盘
(入和出都是相对于内存来说的)
3.5.2 IO流的分类
字节流 (读写字节: byte, 可以读写所有类型的文件, 包括视频, 图片, 音频, 文本等)
字节输入流: java.io.InputStream 抽象类
字节输出流: java.io.OutputStream 抽象类
字符流 (读写字符: char, String, 只能读写文本文件)
字符输入流: java.io.Reader 抽象类
字符输出流: java.io.Writer 抽象类
3.6字节流3.6.1字节输出流: OutputStream和FileOutputStream
java.io.OutputStream抽象类: 字节输出流 (顶层类)
// 成员方法
[Java] 纯文本查看 复制代码
void close() :释放资源
void flush() :刷新缓冲区(对于字节流来说没有作用)
// 写字节的成员方法
[Java] 纯文本查看 复制代码
abstract void write(int b): 一次写一个字节 (参数是int是为了代码中写表示byte的整数方便不用强转)
void write(byte[] b): 一次写一个字节数组
void write(byte[] b, int off, int len): 一次写一个字节数组的一部分
java.io.FileOutputStream类: 文件字节输出流 (向文件写数据)
// 构造方法
[Java] 纯文本查看 复制代码
FileOutputStream(String name): 通过文件路径创建文件字节输出流
FileOutputStream(File file): 通过File对象创建文件字节输出流
构造方法的作用:
1. 创建一个FileOutputStream对象
2. 根据构造方法传递的路径, 在磁盘上创建一个空文件 ("如果文件存在则会清空数据")
3. 将创建的FileOutputStream对象指向这个磁盘上的文件
3.6.2字节输出流: 一次写一个字节到文件
写数据的原理:
Java程序 -> JVM虚拟机 -> OS操作系统 -> OS调用写数据的方法 -> 将数据写入文件
使用字节输出流写数据到文件的步骤:
1. 创建对象: 创建FileOutputStream对象, 构造方法中传递文件路径
2. 写入数据: 使用FileOutputStream对象调用 write(int b) 方法, 将字节写入文件
3. 释放资源: 使用FileOutputStream对象调用 close() 方法, 关闭流对象释放资源
FileOutputStream fos = new FileOutputStream("模块名\\文件名.txt");
fos.write(97);
fos.close();
3.6.3文件存储原理和记事本打开文件的原理
向文件中写入字节数据时, 十进制的数字会被转换为"二进制"的数字写入文件
文本编辑器打开文本文件时, 会先查询编码表, 将二进制数字转换为对应的字符进行显示
0-127: 查询ASCII码表
其他: 查询系统默认码表
Windows简体中文系统的程序打开是按 GBK 码表
IDEA中使用的是 UTF-8 码表
ASCII编码表: 1个byte就是一个字符
GBK编码表: 2个byte数字组成一个汉字."你": -60, -29
UTF-8编码表: 3个byte数字组成一个汉字."你": -28, -67, -96
3.6.4字节输出流: 一次写多个字节 [AppleScript] 纯文本查看 复制代码
java.io.FileOutputStream类: 文件字节输出流 (向文件写数据)
void write(byte[] b): 一次写一个字节数组
void write(byte[] b, int off, int len): 一次写一个字节数组的一部分
java.lang.String类:
byte[] getBytes(): 使用平台的默认字符集将此String编码为byte数组
Java中,byte的范围是 -128 ~ 127 共256个数值
编码表中字符范围是 0 ~ 255 共256个数值
写多个字节时:
如果第一个字节是正数中的 0~127, 则记事本会查询 ASCII码表 显示字符
如果第一个字节是负数中的:-128~-1, 则记事本会查询 GBK码表 显示字符.(将两个连续的byte组合为一个中文)
3.6.5字节输出流: 续写操作 [AppleScript] 纯文本查看 复制代码
java.io.FileOutputStream类: 文件字节输出流
// 带有 续写 功能的构造方法, 不会清空文件
FileOutputStream(String name, boolean append): 通过文件路径创建文件字节输出流, true可以续写
FileOutputStream(File file, boolean append): 通过File对象创建文件字节输出流, true可以续写
换行符:
Windows系统: "\r\n"
Linux系统: "\n"
MacOS系统: "\r"
3.7字节输入流: InputStream和FileInputStream
java.io.InputStream抽象类: 字节输入流 (顶层类)
// 常用成员方法
void close(): 释放资源
// 读数据的方法
int read(): 一次读一个字节
读到文件末尾返回-1 (返回int也是为了代码编写方便)
int read(byte[] b): 一次读一个字节数组
读到的字节存放在参数中的字节数组中, 返回int值是本次读到的字节的个数.读到文件末尾返回-1
java.io.FileInputStream类: 文件字节输入流
// 构造方法
FileInputStream(String name): 使用文件路径创建文件字节输入流
FileInputStream(File file): 使用File对象创建文件字节输入流
构造方法的作用:
1. 创建FileInputStream对象
2. 将FileInputStream对象指向磁盘上的文件
3.7.1字节输入流: 一次读取一个字节
程序从文件读取数据的原理:
Java程序 -> JVM虚拟机 -> OS(操作系统) -> OS调用读取的方法 -> 读取磁盘文件数据
java.io.InputStream抽象类: 字节输入流 (顶层类)
int read(): 一次读一个字节
读到文件末尾返回-1 (返回int也是为了代码编写方便)
FileInputStream读数据步骤:
1. 创建FileInputStream对象
2. 使用FileInputStream对象, 调用 int read() 方法, 一次读取一个byte
3. 释放资源
[AppleScript] 纯文本查看 复制代码
FileInputStream fis = new FileInputStream("模块名\\文件名");
// 一次读一个字节:
int by; // int变量用来存储每次读到的数据
while ((by = fis.read()) != -1) {
System.out.print((char)by); // 不要换行, 文件中自带换行的byte
}
fis.close();
3.7.2字节输入流: 一次一个字节数组 [AppleScript] 纯文本查看 复制代码
java.io.InputStream抽象类: 字节输入流 (顶层类)
int read(byte[] b): 一次读一个字节数组
参数 byte[] b 作用: 存储每次读到的字节, 起到缓冲作用
返回值作用: 每次读到的字节个数, 读到文件末尾返回-1
java.lang.String类:
[AppleScript] 纯文本查看 复制代码
String(byte[] bytes): 使用平台默认字符集解码将byte数组转换为String
String(byte[] bytes, int offset, int length): 使用平台默认字符集将byte数组的一部分转换为String
// 示例代码
[AppleScript] 纯文本查看 复制代码
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(); // 释放资源
3.7.3字节流复制图片文件
文件复制的步骤:
1.创建一个字节输入流对象,构造方法中绑定要读取的数据源
2.创建一个字节输出流对象,构造方法中绑定要写入的目的地
3.使用字节输入流对象中的方法read读取文件
4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
5.释放资源
// 创建输入流和输出流
[AppleScript] 纯文本查看 复制代码
FileInputStream fis = newFileInputStream("c:\\1.jpg"); // 输入流指向要读的数据源文件
FileOutputStream fos = newFileOutputStream("d:\\1.jpg"); // 输出流指向要写的目的地文件
// 一次读写一个字节数组
byte[] bytes = new byte[1024];
int len = 0;
while((len = fis.read(bytes))!=-1){
fos.write(bytes,0,len);
}
// 释放资源
fos.close();
fis.close();
3.7.4字节流读取中文问题
GBK编码中, 一个汉字占用2个byte
UTF-8编码中, 一个汉字占用3个byte
3.8字符流3.8.1字符输入流: Reader和FileReader 注意: 字符流只能读写文本文件
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对象创建文件字符输入流
构造方法的作用:
1. 创建FileReader对象
2. 将FileReader对象指向磁盘上的文件
3.8.2字符输入流: 2种方式读取文本文件
java.io.Reader抽象类: 字符输入流 (顶层)
int read(): 一次读一个字符char, 返回读到的字符.
读到文件末尾返回-1 (返回int为了代码编写方便)
int read(char[] cbuf): 一次读取一个字符数组char[]
返回读取的字符个数. 读到文件末尾返回-1
java.lang.String类:
[AppleScript] 纯文本查看 复制代码
String(char[] value): 将char数组转换为String
String(char[] value, int offset, int count): 将char数组的一部分转换为String
FileReader fr = new FileReader("a.txt");
// 一次读一个字符
int ch; // 定义变量保存每次读到的字符
while ((ch = fr.read()) != -1) {
System.out.print((char)ch);
}
// 一次读一个字符数组
char[] cs = new char[1024];
int len;
while ((len = fr.read(cs)) != -1) {
String s = new String(cs, 0, len);
System.out.print(s);
}
// 释放资源
fr.close();
3.8.3字符输出流: Writer和FileWriter
java.io.Writer抽象类: 字符输出流 (顶层类)
// 常用成员方法
[AppleScript] 纯文本查看 复制代码
abstract void close(): 刷新缓冲区并释放资源
abstract void flush() :刷新缓冲区
// 写
[AppleScript] 纯文本查看 复制代码
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): 写一个字符串的一部分
注意: write()方法只是将数据写到内存缓冲区, 最后必须调用flush()或close()才能将数据真正写入磁盘
java.io.FileWriter类: 文件字符输出流
// 构造方法
[AppleScript] 纯文本查看 复制代码
FileWriter(File file): 通过File对象创建文件字符输出流
FileWriter(String fileName): 通过文件路径创建文件字符输出流
作用:
1. 创建FileWriter对象
2. 根据构造方法中的路径, 在磁盘上创建文件 ("如果文件已存在, 则清空文件内容")
3. 将FileWriter对象指向磁盘上的文件
3.8.4字符输出流: 一次写一个字符
[AppleScript] 纯文本查看 复制代码
void write(int c): 写一个字符 (int类型为了代码编写方便)
FileWriter使用步骤:
1.创建FileWriter对象, 构造方法中绑定要写入数据的目的地
2.使用FileWriter中的方法 write(), 把数据写入到"内存缓冲区"中(字符转换为字节的过程)
3.使用FileWriter中的方法 flush(), 把内存缓冲区中的数据,"刷新到文件中"
4.释放资源 close() (会先把内存缓冲区中的数据刷新到文件中)
FileWriter fw = newFileWriter("09_IOAndProperties\\d.txt");
fw.write(97); // int ch 用int值代表char
//fw.flush();
fw.close();
3.8.5字符输出流: flush与close区别
区别:
[AppleScript] 纯文本查看 复制代码
flush(): 刷新缓冲区 (将数据从内存中写入到磁盘)
close(): 刷新缓冲区, 并释放资源. 关闭流后不能再用同一个流对象操作
flush()可以省略, 只用close()来刷新并释放资源
3.8.6字符输出流: 其他方法
写的其他方法:
[AppleScript] 纯文本查看 复制代码
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): 写一个字符串的一部分
3.8.7字符输出流: 续写和换行
[AppleScript] 纯文本查看 复制代码
FileWriter中带有续写功能的构造:
FileWriter(File file, boolean append): 通过File对象创建文件字符输出流. 第二个参数为true可以续写
FileWriter(String fileName, boolean append): 通过文件路径创建文件字符输出流. 第二个参数为true可
以续写
换行:
windows: "\r\n"直接写入
3.9 IO异常处理3.9.1 3.9.1IO流操作中的异常处理标准格式
IO流的标准格式:
FileWriter fw = null;
[AppleScript] 纯文本查看 复制代码
try {
//IO流对象的创建, 操作等代码
fw = newFileWriter("d:\\09_IOAndProperties\\g.txt", true);
for (int i = 0; i <10 ; i++) {
fw.write("HelloWorld"+i+"\r\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 释放资源
if(fw != null){
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.9.2JDK7和JDK9中IO异常处理的不同方式
JDK 7 增加的 "try-with-resource"
省略了 finally, 可自动释放资源
// 格式
[AppleScript] 纯文本查看 复制代码
try (创建流对象语句,如果多个,使用';'隔开) {
// 读写数据
} catch (IOException e) {
e.printStackTrace();
}
// 示例
try (FileWriter fw = newFileWriter("fw.txt");FileReader fr = newFileReader("fr.txt")) {
// IO操作
int ch = fr.read();
fw.write(ch);
} catch (IOException e) {
e.printStackTrace();
}
JDK 9 对于"try-with-resource"的改进:
流对象的声明和创建可以放在括号外面
流对象要求是有效final的.(即不要修改流对象的值)
[AppleScript] 纯文本查看 复制代码
FileWriter fw = new FileWriter("fw.txt");
FileReader fr = new FileReader("fr.txt");
try (fw; fr) {
// IO操作
int ch = fr.read();
fw.write(ch);
} catch (IOException e) {
e.printStackTrace();
}
3.10 字节&字符输入/输出常用API
[AppleScript] 纯文本查看 复制代码
java.io.OutputStream抽象类: 字节输出流 (顶层类)
// 成员方法
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可以续写
java.io.InputStream抽象类: 字节输入流 (顶层类)
// 常用成员方法
void close(): 释放资源
// 读数据的方法
int read(): 一次读一个字节. 读到文件末尾返回-1 (返回int也是为了代码编写方便)
int read(byte[] b): 一次读一个字节数组. 读到的字节存放在参数中的字节数组中, 返回int值是本次读到的
字节的个数. 读到文件末尾返回-1
java.io.FileInputStream类: 文件字节输入流
// 构造方法
FileInputStream(String name): 使用文件路径创建文件字节输入流
FileInputStream(File file): 使用File对象创建文件字节输入流
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可
以续写
集合:
java.util.Properties类: 属性集, 属于Map的双列集合, 继承自Hashtable
// 构造方法
Properties(): 创建一个Properties集合
// 可以使用Map接口中的方法
// 特有成员方法
Object setProperty(String key, String value): 保存/替换键值对
String getProperty(String key): 通过键获取值. 键不存在返回null
Set<String> stringPropertyNames(): 返回键的集合
void store(OutputStream out, String comments): 将集合数据用字节流写入文件(不能中文), 并写入注释
void store(Writer writer, String comments): 将集合数据用字符流写入文件(可以中文), 并写入注释
void load(InputStream inStream): 从配置文件中通过字节流加载数据到Properties集合(不能读中文)
void load(Reader reader): 从配置文件中通过字符流加载数据到Properties集合(可以读中文)
-
1.3.1.jpg
(54.87 KB, 下载次数: 4)
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) |
黑马程序员IT技术论坛 X3.2 |