目录
7.3.1FileInputStream文件字节输入流的作用
7.3.4FileOutputStream文件字节输出流的作用
7.5.1BufferedIn(Out)putStream缓冲字节输入、输出流的作用
7.7.1InputStreamReader字符输入转换流作用
7.7.3PrintStream/PrintWriter打印流的作用
1.Stream流
1.1认识Stream流
【1】是Jdk8开始新增的一套API (java.util.stream.*),可以用于操作集合或者数组的数据。
【2】Stream流大量的结合了Lambda的语法风格来编程,功能强大,性能高效,代码简洁,可读性好。
1.2Stream流处理数据的步骤
【1】先得到集合或者数组的Stream流。
【2】然后调用Stream流的方法对数据进行处理。
【3】获取处理的结果。
1.3获取Stream流
1.3.1获取集合的Stream流
Collection提供的如下方法 | 说明 |
default Stream<E> stream() | 获取当前集合对象的Stream流 |
Collection<String> list = new ArrayList<>();
Collection<String> newList2 = list.stream()
1.3.2获取数组的Stream流
Arrays类提供的如下方法 | 说明 |
public static <T> Stream<T> stream(T[] array) | 获取当前数组的Stream流 |
String[] names = {"张三丰", "张无忌", "张翠山", "张良", "张学友"};
Stream<String> s5 = Arrays.stream(names);
Stream类提供的如下方法 | 说明 |
public static<T> Stream<T> of(T... values) | 获取当前接收数据的Stream流 |
String[] names = {"张三丰", "张无忌", "张翠山", "张良", "张学友"};
Stream<String> s6 = Stream.of(names);
Stream<String> s7 = Stream.of("张三丰", "张无忌", "张翠山", "张良", "张学友");
1.4Stream提供的常用方法
中间方法指的是调用完成后会返回新的Stream流,可以继续使用(支持链式编程)。
Stream提供的常用中间方法 | 说明 |
Stream<T> filter(Predicate<? super T> predicate) | 用于对流中的数据进行过滤。 |
Stream<T> sorted() | 对元素进行升序排序 |
Stream<T> sorted(Comparator<? super T> comparator) | 按照指定规则排序 |
Stream<T> limit(long maxSize) | 获取前几个元素 |
Stream<T> skip(long n) | 跳过前几个元素 |
Stream<T> distinct() | 去除流中重复的元素。 |
<R> Stream<R> map(Function<? super T,? extends R> mapper) | 对元素进行加工,并返回对应的新流 |
static <T> Stream<T> concat(Stream a, Stream b) | 合并a和b两个流为一个流 |
public static void main(String[] args) {
// 目标:掌握Stream提供的常用的中间方法,对流上的数据进行处理(返回新流:支持链式编程)
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.add("张翠山");
// 1、过滤方法
list.stream().filter(s -> s.startsWith("张") && s.length() == 3).forEach(System.out::println);
// 2、排序方法。
List<Double> scores = new ArrayList<>();
scores.add(88.6);
scores.add(66.6);
scores.add(66.6);
scores.add(77.6);
scores.add(77.6);
scores.add(99.6);
scores.stream().sorted().forEach(System.out::println); // 默认是升序。
System.out.println("--------------------------------------------------");
scores.stream().sorted((s1, s2) -> Double.compare(s2, s1)).forEach(System.out::println); // 降序
System.out.println("--------------------------------------------------");
scores.stream().sorted((s1, s2) -> Double.compare(s2, s1)).limit(2).forEach(System.out::println); // 只要前2名
System.out.println("--------------------------------------------------");
scores.stream().sorted((s1, s2) -> Double.compare(s2, s1)).skip(2).forEach(System.out::println); // 跳过前2名
System.out.println("--------------------------------------------------");
// 如果希望自定义对象能够去重复,重写对象的hashCode和equals方法,才可以去重复!
scores.stream().sorted((s1, s2) -> Double.compare(s2, s1)).distinct().forEach(System.out::println); // 去重复
// 映射/加工方法: 把流上原来的数据拿出来变成新数据又放到流上去。
scores.stream().map(s -> "加10分后:" + (s + 10)).forEach(System.out::println);
// 合并流:
Stream<String> s1 = Stream.of("张三丰", "张无忌", "张翠山", "张良", "张学友");
Stream<Integer> s2 = Stream.of(111, 22, 33, 44);
Stream<Object> s3 = Stream.concat(s1, s2);
System.out.println(s3.count());
}
1.5终结、收集Stream流
1.5.1终结方法
终结方法指的是调用完成后,不会返回新Stream了,没法继续使用流了。
Stream提供的常用终结方法 | 说明 |
void forEach(Consumer action) | 对此流运算后的元素执行遍历 |
long count() | 统计此流运算后的元素个数 |
Optional<T> max(Comparator<? super T> comparator) | 获取此流运算后的最大值元素 |
Optional<T> min(Comparator<? super T> comparator) | 获取此流运算后的最小值元素 |
public static void main(String[] args) {
// 目标:掌握Stream流的统计,收集操作(终结方法)
List<Teacher> teachers = new ArrayList<>();
teachers.add(new Teacher("张三", 23, 5000));
teachers.add(new Teacher("金毛狮王", 54, 16000));
teachers.add(new Teacher("李四", 24, 6000));
teachers.add(new Teacher("王五", 25, 7000));
teachers.add(new Teacher("白眉鹰王", 66, 108000));
teachers.add(new Teacher("陈昆", 42, 48000));
teachers.stream().filter(t -> t.getSalary() > 15000).forEach(System.out::println);
System.out.println("--------------------------------------------------");
long count = teachers.stream().filter(t -> t.getSalary() > 15000).count();
System.out.println(count);
System.out.println("--------------------------------------------------");
// 获取薪水最高的老师对象
Optional<Teacher> max = teachers.stream().max((t1, t2) -> Double.compare(t1.getSalary(), t2.getSalary()));
Teacher maxTeacher = max.get(); // 获取Optional对象中的元素
System.out.println(maxTeacher);
Optional<Teacher> min = teachers.stream().min((t1, t2) -> Double.compare(t1.getSalary(), t2.getSalary()));
Teacher minTeacher = min.get(); // 获取Optional对象中的元素
System.out.println(minTeacher);
}
1.5.2收集方法
就是把Stream流操作后的结果转回到集合或者数组中去返回。
Stream提供的常用终结方法 | 说明 |
R collect(Collector collector) | 把流处理后的结果收集到一个指定的集合中去 |
Object[] toArray() | 把流处理后的结果收集到一个数组中去 |
public static void main(String[] args) {
// 目标:掌握Stream流的统计,收集操作(终结方法)
List<Teacher> teachers = new ArrayList<>();
teachers.add(new Teacher("张三", 23, 5000));
teachers.add(new Teacher("金毛狮王", 54, 16000));
teachers.add(new Teacher("李四", 24, 6000));
teachers.add(new Teacher("王五", 25, 7000));
teachers.add(new Teacher("白眉鹰王", 66, 108000));
teachers.add(new Teacher("陈昆", 42, 48000));
teachers.stream().filter(t -> t.getSalary() > 15000).forEach(System.out::println);
System.out.println("--------------------------------------------------");
long count = teachers.stream().filter(t -> t.getSalary() > 15000).count();
System.out.println(count);
System.out.println("--------------------------------------------------");
// 获取薪水最高的老师对象
Optional<Teacher> max = teachers.stream().max((t1, t2) -> Double.compare(t1.getSalary(), t2.getSalary()));
Teacher maxTeacher = max.get(); // 获取Optional对象中的元素
System.out.println(maxTeacher);
Optional<Teacher> min = teachers.stream().min((t1, t2) -> Double.compare(t1.getSalary(), t2.getSalary()));
Teacher minTeacher = min.get(); // 获取Optional对象中的元素
System.out.println(minTeacher);
}
Collectors工具类提供了具体的收集方式 | 说明 |
public static <T> Collector toList() | 把元素收集到List集合中 |
public static <T> Collector toSet() | 把元素收集到Set集合中 |
public static Collector toMap(Function keyMapper , Function valueMapper) | 把元素收集到Map集合中 |
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.add("张三丰");
list.add("张翠山");
// 流只能收集一次
// 收集到集合或者数组中去。
Stream<String> s1 = list.stream().filter(s -> s.startsWith("张"));
// 收集到List集合
List<String> list1 = s1.collect(Collectors.toList());
System.out.println(list1);
// Set<String> set2 = new HashSet<>();
// set2.addAll(list1);
// 收集到Set集合
Stream<String> s2 = list.stream().filter(s -> s.startsWith("张"));
Set<String> set = s2.collect(Collectors.toSet());
System.out.println(set);
// 收集到数组中去
Stream<String> s3 = list.stream().filter(s -> s.startsWith("张"));
Object[] array = s3.toArray();
System.out.println("数组:" + Arrays.toString(array));
System.out.println("------------------收集到Map集合---------------------------");
// 收集到Map集合:键是老师名称,值是老师薪水
Map<String, Double> map = teachers.stream().collect(Collectors.toMap(Teacher::getName, Teacher::getSalary));
System.out.println(map);
}
2.可变参数
2.1可变参数是什么
就是一种特殊形参,定义在方法、构造器的形参列表里,格式是:数据类型...参数名称;
2.2可变参数的特点和好处
【1】特点:可以不传数据给它;可以传一个或者同时传多个数据给它;也可以传一个数组给它。
【2】好处:常常用来灵活的接收数据。
2.3可变参数的注意事项
【1】可变参数在方法内部就是一个数组。
【2】一个形参列表中可变参数只能有一个
【3】可变参数必须放在形参列表的最后面
public static void main(String[] args) {
// 目标:认识可变参数
sum(); // 不传参数
sum(10); // 可以传1个参数
sum(10, 20, 30, 40, 50); // 可以传多个参数
sum(new int[]{11, 22, 33, 44}); // 可以传数组
// 优势:接收参数很灵活,可以替代数组传参
}
// 注意事项:可变参数在形参列表中只能有一个 , 可变参数必须放在形参列表的最后面
public static void sum(int...nums){
// 内部怎么拿数据:
// 可变参数对内实际上就是一个数组。nums就是数组
System.out.println(nums.length);
System.out.println(Arrays.toString(nums));
System.out.println("------------------------------------------------");
}
3.Collections工具类
3.1Collections工具类是什么
是一个用来操作集合的工具类
3.2Collections提供的常用静态方法
方法名称 | 说明 |
public static <T> boolean addAll(Collection<? super T> c, T... elements) | 给集合批量添加元素 |
public static void shuffle(List<?> list) | 打乱List集合中的元素顺序 |
public static <T> void sort(List<T> list) | 对List集合中的元素进行升序排序 |
public static <T> void sort(List<T> list,Comparator<? super T> c) | 对List集合中元素,按照比较器对象指定的规则进行排序 |
public static void main(String[] args) {
// 目标:Colllections工具类
List<String> list = new ArrayList<>();
// list.add("张无忌");
// list.add("周芷若");
// list.add("赵敏");
// list.add("张强");
// list.add("张三丰");
// list.add("张翠山");
// 1、Collections的方法批量加
Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰","张翠山");
System.out.println(list);
// 2、打乱顺序
Collections.shuffle(list);
System.out.println(list);
}
4.File
4.1File可以做什么
File类的对象可以代表文件/文件夹,并可以调用其提供的方法对象文件进行操作。File对象既可以代表文件、也可以代表文件夹。File封装的对象仅仅是一个路径名,这个路径可以是存在的,也允许是不存在的。
4.2创建File类的对象
构造器 | 说明 |
public File(String pathname) | 根据文件路径创建文件对象 |
public File(String parent, String child) | 根据父路径和子路径名字创建文件对象 |
public File(File parent, String child) | 根据父路径对应文件对象和子路径名字创建文件对象 |
// 1、创建File对象,去获取某个文件的信息
//File f1 = new File("E:\\resource\\dlei.jpg");
File f1 = new File("E:\\resource\\dlei.jpg");
System.out.println(f1.length()); // 字节个数
System.out.println(f1.getName());
System.out.println(f1.isFile()); // true
System.out.println(f1.isDirectory()); // false
4.3绝对路径、相对路径
【1】绝对路径:从盘符开始。
File f1 = new File("E:\\resource\\dlei.jpg");
【2】相对路径:不带盘符,默认直接到当前工程下的目录寻找文件。
File f2 = new File("day03-file-io\\src\\dlei01.txt");
4.4判断文件类型
方法名称 | 说明 |
public boolean exists() | 判断当前文件对象,对应的文件路径是否存在,存在返回true |
public boolean isFile() | 判断当前文件对象指代的是否是文件,是文件返回true,反之。 |
public boolean isDirectory() | 判断当前文件对象指代的是否是文件夹,是文件夹返回true,反之。 |
File f1 = new File("E:\\resource\\dlei.jpg");
System.out.println(f1.isFile()); // true
System.out.println(f1.isDirectory()); // false
4.5获取文件信息功能
方法名称 | 说明 |
public String getName() | 获取文件的名称(包含后缀) |
public long length() | 获取文件的大小,返回字节个数 |
public long lastModified() | 获取文件的最后修改时间。 |
public String getPath() | 获取创建文件对象时,使用的路径 |
public String getAbsolutePath() | 获取绝对路径 |
// 2、可以使用相对路径定位文件对象
// 只要带盘符的都称之为:绝对路径 E:/resource/dlei.jpg
// 相对路径:不带盘符,默认是到你的idea工程下直接寻找文件的。一般用来找工程下的项目文件的。
File f2 = new File("day03-file-io\\src\\dlei01.txt");
System.out.println(f2.length());
System.out.println(f2.getAbsoluteFile()); // 获取绝对路径
// 3、创建对象代表不存在的文件路径。
File f3 = new File("E:\\resource\\dlei01.txt");
System.out.println(f3.exists()); // 判断是否存在
System.out.println(f3.createNewFile()); // 把这个文件创建出来
4.6创建文件方法
方法名称 | 说明 |
public boolean createNewFile() | 创建一个新的空的文件 |
public boolean mkdir() | 只能创建一级文件夹 |
public boolean mkdirs() | 可以创建多级文件夹 |
// 4、创建对象代表不存在的文件夹路径。
File f4 = new File("E:\\resource\\aaa");
System.out.println(f4.mkdir()); // mkdir只能创建一级文件夹
File f5 = new File("E:\\resource\\kkk\\jjj\\mmm");
System.out.println(f5.mkdirs()); // mkdirs可以创建多级文件夹,很重要!
4.7删除文件方法
delete方法默认只能删除文件和空文件夹,删除后的文件不会进入回收站。
方法名称 | 说明 |
public boolean delete() | 删除文件、空文件夹 |
// 5、创建File对象代表存在的文件,然后删除它
File f6 = new File("E:\\resource\\dlei01.txt");
System.out.println(f6.delete()); // 删除文件
// 6、创建File对象代表存在的文件夹,然后删除它
File f7 = new File("E:\\resource\\aaa");
System.out.println(f7.delete()); // 只能删除空文件,不能删除非空文件夹
File f8 = new File("E:\\resource");
System.out.println(f8.delete()); // 只能删除空文件,不能删除非空文件夹
4.8遍历文件夹功能
方法名称 | 说明 |
public String[] list() | 获取当前目录下所有的"一级文件名称"到一个字符串数组中去返回。 |
public File[] listFiles() | 获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点) |
public static void main(String[] args) {
// 目标:掌握File遍历一级文件对象的操作
File dir = new File("E:/resource/dlei.txt");
File dir2 = new File("E:\\resource\\eee777");
File[] files = dir2.listFiles();
System.out.println(Arrays.toString(files));
}
4.9遍历文件夹(listFiles)方法时的注意事项
【1】当主调是文件,或者路径不存在时,返回null
【2】当主调是空文件夹时,返回一个长度为0的数组
【3】当主调是一个有内容的文件夹时,将里面所有一级文件和文件夹的路径放在File数组中返回.
【4】当主调是一个文件夹,且里面有隐藏文件时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏文件
【5】当主调是一个文件夹,但是没有权限访问该文件夹时,返回null
5.方法递归
5.1什么是递归
递归是一种算法,在程序设计语言中广泛应用。从形式上说:方法调用自身的形式称为方法递归( recursion)。
5.2递归的形式
【1】直接递归:方法自己调用自己。
public static void main(String[] args) {
// 目标:认识递归的形式。
printA();
}
public static void printA() {
System.out.println("===A执行了===");
printA(); // 直接递归:自己调用自己 . 递归可能出现死循环,导致出现栈内存溢出现象。
}
【2】间接递归:方法调用其他方法,其他方法又回调方法自己。
public static void main(String[] args) {
// 目标:计算n的阶乘
//间接递归
System.out.println("5的阶乘:" + f(5));
System.out.println("1-5的和:" + f2(5));
}
public static int f(int n){
if(n == 1){
return 1;
}
return f(n - 1) * n; // 递归调用
}
public static int f2(int n){
if(n == 1){
return 1;
}
return f2(n - 1) + n; // 递归调用
}
5.3使用递归时的注意事项
递归如果没有控制好终止,会出现递归死循环,导致栈内存溢出错误。
5.4递归算法三要素
【1】递归的公式: f(n) = f(n-1) * n;
【2】递归的终结点:f(1)
【3】递归的方向必须走向终结点
5.5文件搜索
public static void main(String[] args) {
// 目标:完成文件搜索。找出D:盘下的QQ.exe的位置。
try {
File dir = new File("D:/");
searchFile(dir , "QQ.exe");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 搜索文件
* @param dir 搜索的目录
* @param fileName 搜索的文件名称
*/
public static void searchFile(File dir, String fileName) throws Exception {
// 1、判断极端情况
if(dir == null || !dir.exists() || dir.isFile()){
return; // 不搜索
}
// 2、获取目录下的所有一级文件或者文件夹对象
File[] files = dir.listFiles();
// 3、判断当前目录下是否存在一级文件对象,存在才可以遍历
if(files != null && files.length > 0){
// 4、遍历一级文件对象
for (File file : files) {
// 5、判断当前一级文件对象是否是文件
if(file.isFile()){
// 6、判断文件名称是否和目标文件名称一致
if(file.getName().contains(fileName)){
System.out.println("找到目标文件:" + file.getAbsolutePath());
Runtime r = Runtime.getRuntime();
r.exec(file.getAbsolutePath());
}
}else{
// 7、如果当前一级文件对象是文件夹,则继续递归调用
searchFile(file, fileName);
}
}
}
}
6.字符集
6.1常见的字符集及特点
【1】ASCII字符集:只有英文、数字、符号等,占1个字节。
【2】GBK字符集:汉字占2个字节,英文、数字占1个字节。
【3】UTF-8字符集:汉字占3个字节,英文、数字占1个字节。
6.2编码和解码时的要求
字符编码时使用的字符集,和解码时使用的字符集必须一致,否则会出现乱码
6.3对字符的编码
String提供了如下方法 | 说明 |
byte[] getBytes() | 使用平台的默认字符集将该 String编码为一系列字节,将结果存储到新的字节数组中 |
byte[] getBytes(String charsetName) | 使用指定的字符集将该 String编码为一系列字节,将结果存储到新的字节数组中 |
6.4对字符的解码
String提供了如下方法 | 说明 |
String(byte[] bytes) | 通过使用平台的默认字符集解码指定的字节数组来构造新的 String |
String(byte[] bytes, String charsetName) | 通过指定的字符集解码指定的字节数组来构造新的 String |
7.IO流
7.1什么是IO流
I指Input,称为输入流:负责把数据读到内存中去。O指Output,称为输出流:负责写数据出去
7.2IO流的分类
【1】字节输入流 InputStream(读字节数据的)
【2】字节输出流 OutputStream(写字节数据出去的)
【3】字符输入流 Reader(读字符数据的)
【4】字符输出流 Writer(写字符数据出去的)
7.3字节流
7.3.1FileInputStream文件字节输入流的作用
以内存为基准,可以把磁盘文件中的数据以字节的形式读入到内存中去。
7.3.2文件字节输入流的构造器、方法
构造器 | 说明 |
public FileInputStream(File file) | 创建字节输入流管道与源文件接通 |
public FileInputStream(String pathname) | 创建字节输入流管道与源文件接通 |
方法名称 | 说明 |
public int read() | 每次读取一个字节返回,如果发现没有数据可读会返回-1. |
public int read(byte[] buffer) | 每次用一个字节数组去读取数据,返回字节数组读取了多少个字节,如果发现没有数据可读会返回-1. |
public static void main(String[] args) throws Exception {
// 目标:掌握文件字节输入流读取文件中的字节数组到内存中来。
// 1、创建文件字节输入流管道于源文件接通
// InputStream is = new FileInputStream(new File("day03-file-io\\src\\dlei02.txt"));
InputStream is = new FileInputStream("day03_file_io\\src\\dlei02.txt"); // 简化写法
// 2、开始读取文件中的字节并输出: 每次读取一个字节
// 定义一个变量记住每次读取的一个字节
int b;
while ((b = is.read()) != -1) {
System.out.print((char) b);
}
// 每次读取一个字节的问题:性能较差,读取汉字输出一定会乱码!
}
public static void main(String[] args) throws Exception {
// 目标:掌握文件字节输入流读取文件中的字节数组到内存中来。
// 1、创建文件字节输入流管道于源文件接通
InputStream is = new FileInputStream("day03_file_io\\src\\dlei03.txt"); // 简化写法
// 2、开始读取文件中的字节并输出: 每次读取多个字节
// 定义一个字节数组用于每次读取字节
byte[] buffer = new byte[3];
// 定义一个变量记住每次读取了多少个字节。
int len;
while ((len = is.read(buffer)) != -1) {
// 3、把读取到的字节数组转换成字符串输出,读取多少倒出多少
String str = new String(buffer,0, len);
System.out.print(str);
}
// 拓展:每次读取多个字节,性能得到提升,因为每次读取多个字节,可以减少硬盘和内存的交互次数,从而提升性能。
// 依然无法避免读取汉字输出乱码的问题:存在截断汉字字节的可能性!
}
方法名称 | 说明 |
public byte[] readAllBytes() throws IOException | 直接将当前字节输入流对应的文件对象的字节数据装到一个字节数组返回 |
public static void main(String[] args) throws Exception {
// 目标:掌握文件字节输入流读取文件中的字节数组到内存中来。
// 1、创建文件字节输入流管道于源文件接通
InputStream is = new FileInputStream("day03_file_io\\src\\dlei04.txt"); // 简化写法
// 2、一次性读完文件的全部字节:可以避免读取汉字输出乱码的问题。
byte[] bytes = is.readAllBytes();
String rs = new String(bytes);
System.out.println(rs);
}
7.3.3使用文件字节输入流时注意事项
【1】使用FileInputStream每次读取一个字节,读取性能较差,并且读取汉字输出会乱码。
【2】使用FileInputStream每次读取多个字节,读取性能得到了提升,但读取汉字输出还是会乱码。
【3】如果文件过大,创建的字节数组也会过大,可能引起内存溢出。
【4】读取文本适合用字符流;字节流适合做数据的转移,比如:文件复制
7.3.4FileOutputStream文件字节输出流的作用
以内存为基准,把内存中的数据以字节的形式写出到文件中去。
7.3.5文件字节输出流的构造器、方法
构造器 | 说明 |
public FileOutputStream(File file) | 创建字节输出流管道与源文件对象接通 |
public FileOutputStream(String filepath) | 创建字节输出流管道与源文件路径接通 |
public FileOutputStream(File file,boolean append) | 创建字节输出流管道与源文件对象接通,可追加数据 |
public FileOutputStream(String filepath,boolean append) | 创建字节输出流管道与源文件路径接通,可追加数据 |
方法名称 | 说明 |
public void write(int a) | 写一个字节出去 |
public void write(byte[] buffer) | 写一个字节数组出去 |
public void write(byte[] buffer , int pos , int len) | 写一个字节数组的一部分出去。 |
public void close() throws IOException | 关闭流。 |
public static void main(String[] args) throws Exception {
// 目标:学会使用文件字节输出流。
// 1、创建文件字节输出流管道与目标文件接通
// OutputStream os = new FileOutputStream("day03-file-io/src/dlei05-out.txt"); // 覆盖管道
OutputStream os = new FileOutputStream("day03_file_io\\src\\csb1.txt", true); // 追加数据
// 2、写入数据
// public void write(int b)
os.write(97); // 写入一个字节数据
os.write('b'); // 写入一个字符数据
// os.write('徐'); // 写入一个字符数据 会乱码
os.write("\r\n".getBytes()); // 换行
// 3、写一个字节数组出去
// public void write(byte[] b)
byte[] bytes = "我爱你中国666".getBytes();
os.write(bytes);
os.write("\r\n".getBytes()); // 换行
// 4、写一个字节数组的一部分出去
// public void write(byte[] b, int pos, int len)
os.write(bytes, 0, 3);
os.write("\r\n".getBytes()); // 换行
os.close(); // 关闭管道 释放资源
}
7.3.6如何实现写数据的追加操作
创建FileOutpuStream的对象,在构造器第二个参数声明true.
7.3.7字节输出流如何实现写出去的数据可以换行
os.write(“\r\n”.getBytes());
7.3.6文件复制
public static void main(String[] args) {
// 目标:使用字节流完成文件的复制操作。
// 源文件:E:\resource\jt.jpg
// 目标文件:D:\jt_new.jpg (复制过去的时候必须带文件名的,无法自动生成文件名。)
try {
copyFile("E:\\resource\\jt.jpg", "D:\\jt_new.jpg");
} catch (Exception e) {
e.printStackTrace();
}
}
// 复制文件
public static void copyFile(String srcPath, String destPath) throws Exception {
// 1、创建一个文件字节输入流管道与源文件接通
InputStream fis = new FileInputStream(srcPath);
OutputStream fos = new FileOutputStream(destPath);
// 2、读取一个字节数组,写入一个字节数组 1024 + 1024 + 3
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len); // 读取多少个字节,就写入多少个字节
}
System.out.println("复制成功!");
}
7.3.7资源释放问题
JDK 7开始提供了更简单的资源释放方案:try-with-resourc
try(
定义资源1;定义资源2;…
){
可能出现异常的代码;
}catch(异常类名 变量名){
异常的处理代码;
}
7.4字符流
7.4.1FileReader文件字符输入流的作用
以内存为基准,可以把文件中的数据以字符的形式读入到内存中去。
7.4.2文件字符流输入流的构造器、方法
构造器 | 说明 |
public FileReader(File file) | 创建字符输入流管道与源文件接通 |
public FileReader(String pathname) | 创建字符输入流管道与源文件接通 |
方法名称 | 说明 |
public int read() | 每次读取一个字符返回,如果发现没有数据可读会返回-1. |
public int read(char[] buffer) | 每次用一个字符数组去读取数据,返回字符数组读取了多少个字符,如果发现没有数据可读会返回-1. |
public static void main(String[] args) {
try (
Reader reader = new FileReader("day03_file_io\\src\\dlei01.txt")
) {
char[] chars=new char[3];
int len;
while ((len=reader.read(chars))!=-1){
String str = new String(chars, 0, len);
System.out.print(str);
}
}catch (Exception e){
e.printStackTrace();
}
}
7.4.3FileWriter文件字符输出流的作用
以内存为基准,把内存中的数据以字符的形式写出到文件中去。
7.4.4文件字符流输出流的构造器、方法
构造器 | 说明 |
public FileWriter(File file) | 创建字节输出流管道与源文件对象接通 |
public FileWriter(String filepath) | 创建字节输出流管道与源文件路径接通 |
public FileWriter(File file,boolean append) | 创建字节输出流管道与源文件对象接通,可追加数据 |
public FileWriter(String filepath,boolean append) | 创建字节输出流管道与源文件路径接通,可追加数据 |
方法名称 | 说明 |
void write(int c) | 写一个字符 |
void write(String str) | 写一个字符串 |
void write(String str, int off, int len) | 写一个字符串的一部分 |
void write(char[] cbuf) | 写入一个字符数组 |
void write(char[] cbuf, int off, int len) | 写入字符数组的一部分 |
public static void main(String[] args) {
// 目标:搞清楚文件字符输出流的使用:写字符出去的流。
try (
// 1. 创建一个字符输出流对象,指定写出的目的地。
// Writer fw = new FileWriter("day03-file-io/src/dlei07-out.txt"); // 覆盖管道
Writer fw = new FileWriter("day03-file-io/src/dlei07-out.txt", true); // 追加数据
){
// 2. 写一个字符出去: public void write(int c)
fw.write('a');
fw.write(98);
fw.write('磊');
fw.write("\r\n"); // 换行
// 3、写一个字符串出去: public void write(String str)
fw.write("java");
fw.write("我爱Java,虽然Java不是最好的编程之一,但是可以挣钱");
fw.write("\r\n"); // 换行
// 4、写字符串的一部分出去: public void write(String str, int off, int len)
fw.write("java", 1, 2);
fw.write("\r\n"); // 换行
// 5、写一个字符数组出去: public void write(char[] cbuf)
char[] chars = "java".toCharArray();
fw.write(chars);
fw.write("\r\n"); // 换行
// 6、写字符数组的一部分出去: public void write(char[] cbuf, int off, int len)
fw.write(chars, 1, 2);
fw.write("\r\n"); // 换行
// fw.flush(); // 刷新缓冲区,将缓冲区中的数据全部写出去。
// 刷新后,流可以继续使用。
// fw.close(); // 关闭资源,关闭包含了刷新!关闭后流不能继续使用!
} catch (Exception e) {
e.printStackTrace();
}
}
7.4.5字符输出流的注意事项
字符输出流写出数据后,必须刷新流,或者关闭流,写出去的数据才能生效。
7.5缓冲流
7.5.1BufferedIn(Out)putStream缓冲字节输入、输出流的作用
可以提高字节输入流读取数据的性能
7.5.2缓冲字节输入、输出流的原理
缓冲字节输入流自带了8KB缓冲池;缓冲字节输出流自带了8KB缓冲池;
7.5.3缓冲字节输入、输出流的构造器
构造器 | 说明 |
public BufferedInputStream(InputStream is) | 把低级的字节输入流包装成一个高级的缓冲字节输入流,从而提高读数据的性能 |
public BufferedOutputStream(OutputStream os) | 把低级的字节输出流包装成一个高级的缓冲字节输出流,从而提高写数据的性能 |
public static void main(String[] args) {
// 目标:掌握缓冲字节流的使用。
// 源文件:E:\resource\jt.jpg
// 目标文件:D:\jt_new.jpg (复制过去的时候必须带文件名的,无法自动生成文件名。)
copyFile("E:\\resource\\jt.jpg", "D:\\jt_new2.jpg");
}
// 复制文件
public static void copyFile(String srcPath, String destPath) {
// 1、创建一个文件字节输入流管道与源文件接通
try (
// 这里只能放置资源对象,用完后,最终会自动调用其close方法关闭!!
InputStream fis = new FileInputStream(srcPath);
// 把低级的字节输入流包装成高级的缓冲字节输入流
InputStream bis = new BufferedInputStream(fis);
OutputStream fos = new FileOutputStream(destPath);
// 把低级的字节输出流包装成高级的缓冲字节输出流
OutputStream bos = new BufferedOutputStream(fos);
){
// 2、读取一个字节数组,写入一个字节数组 1024 + 1024 + 3
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len); // 读取多少个字节,就写入多少个字节
}
System.out.println("复制成功!");
} catch (Exception e) {
e.printStackTrace();
}
}
7.5.4BufferedReader缓冲字符输入流的作用
可以提高字符输入流读取字符数据的性能。
7.5.5缓冲字符输入流的原理
缓冲字符输入流自带了8KB缓冲池;
7.5.6缓冲字符输入流的构造器、方法
构造器 | 说明 |
public BufferedReader(Reader r) | 把低级的字符输入流包装成字符缓冲输入流管道,从而提高字符输入流读字符数据的性能 |
方法 | 说明 |
public String readLine() | 读取一行数据返回,如果没有数据可读了,会返回null。 |
public static void main(String[] args) {
// 目标:搞清楚缓冲字符输入流读取字符内容:性能提升了,多了按照行读取文本的能力。
try (
// 1、创建文件字符输入流与源文件接通
Reader fr = new FileReader("day03-file-io\\src\\dlei08.txt");
// 2、创建缓冲字符输入流包装低级的字符输入流
BufferedReader br = new BufferedReader(fr);
) {
// // 2、定义一个字符数组,每次读多个字符
// char[] chs = new char[1024];
// int len; // 用于记录每次读取了多少个字符
// while ((len = br.read(chs)) != -1){
// // 3、每次读取多个字符,并把字符数组转换成字符串输出
// String str = new String(chs,0,len);
// System.out.print(str);
// }
// System.out.println(br.readLine());
// System.out.println(br.readLine());
// System.out.println(br.readLine());
// System.out.println(br.readLine());
// System.out.println(br.readLine());
// System.out.println(br.readLine()); // null
// 使用循环改进,来按照行读取数据。
// 定义一个字符串变量用于记住每次读取的一行数据
String line;
while ((line = br.readLine()) != null){
System.out.println(line);
}
// 目前读取文本最优雅的方案:性能好,不乱码,可以按照行读取。
}catch (Exception e){
e.printStackTrace();
}
}
7.5.4BufferedWriter缓冲字符输出流的作用
可以提高字符输出流读取字符数据的性能。
7.5.5缓冲字符输出流的原理
缓冲字符输出流自带了8KB缓冲池;
7.5.6缓冲字符输出流的构造器、方法
构造器 | 说明 |
public BufferedWriter(Writer r) | 把低级的字符输出流包装成一个高级的缓冲字符输出流管道,从而提高字符输出流写数据的性能 |
方法 | 说明 |
public void newLine() | 换行 |
public static void main(String[] args) {
// 目标:搞清楚缓冲字符输出流的使用:提升了字符输出流的写字符的性能,多了换行功能
try (
// 1. 创建一个字符输出流对象,指定写出的目的地。
// Writer fw = new FileWriter("day03-file-io/src/dlei07-out.txt"); // 覆盖管道
Writer fw = new FileWriter("day03-file-io/src/dlei07-out.txt", true); // 追加数据
// 2. 创建一个缓冲字符输出流对象,把字符输出流对象作为构造参数传递给缓冲字符输出流对象。
BufferedWriter bw = new BufferedWriter(fw);
){
// 2. 写一个字符出去: public void write(int c)
bw.write('a');
bw.write(98);
bw.write('磊');
bw.newLine(); // 换行
// 3、写一个字符串出去: public void write(String str)
bw.write("java");
bw.write("我爱Java,虽然Java不是最好的编程之一,但是可以挣钱");
bw.newLine(); // 换行
// 4、写字符串的一部分出去: public void write(String str, int off, int len)
bw.write("java", 1, 2);
bw.newLine(); // 换行
// 5、写一个字符数组出去: public void write(char[] cbuf)
char[] chars = "java".toCharArray();
bw.write(chars);
bw.newLine(); // 换行
// 6、写字符数组的一部分出去: public void write(char[] cbuf, int off, int len)
bw.write(chars, 1, 2);
bw.newLine(); // 换行
} catch (Exception e) {
e.printStackTrace();
}
}
7.6性能分析
推荐使用哪种方式提高字节流读写数据的性能
建议使用字节缓冲输入流、字节缓冲输出流,结合字节数组的方式,目前来看是性能最优的组合。
7.7其他流
7.7.1InputStreamReader字符输入转换流作用
可以指定编码把原始字节流转换成字符流,如此字符流中的字符不乱码。
7.7.2字符输入转换流的构造器
构造器 | 说明 |
public InputStreamReader(InputStream is) | 把原始的字节输入流,按照代码默认编码转成字符输入流(与直接用FileReader的效果一样) |
public InputStreamReader(InputStream is ,String charset) | 把原始的字节输入流,按照指定字符集编码转成字符输入流(重点) |
public static void main(String[] args) {
// 目标:使用字符输入转换流InputStreamReader解决不同编码读取乱码的问题、
// 代码:UTF-8 文件 UTF-8 读取不乱码
// 代码:UTF-8 文件 GBK 读取乱码
try (
// 先提取文件的原始字节流
InputStream is = new FileInputStream("day03-file-io\\src\\dlei09.txt");
// 指定字符集把原始字节流转换成字符输入流
Reader isr = new InputStreamReader(is, "GBK");
// 2、创建缓冲字符输入流包装低级的字符输入流
BufferedReader br = new BufferedReader(isr);
) {
// 定义一个字符串变量用于记住每次读取的一行数据
String line;
while ((line = br.readLine()) != null){
System.out.println(line);
}
}catch (Exception e){
e.printStackTrace();
}
}
7.7.3PrintStream/PrintWriter打印流的作用
打印流可以实现更方便、更高效的打印数据出去,能实现打印啥出去就是啥出去。
7.7.4PrintStream打印流的构造器、方法
构造器 | 说明 |
public PrintStream(OutputStream/File/String) | 打印流直接通向字节输出流/文件/文件路径 |
public PrintStream(String fileName, Charset charset) | 可以指定写出去的字符编码 |
public PrintStream(OutputStream out, boolean autoFlush) | 可以指定实现自动刷新 |
public PrintStream(OutputStream out, boolean autoFlush, String encoding) | 可以指定实现自动刷新,并可指定字符的编码 |
方法 | 说明 |
public void println(Xxx xx) | 打印任意类型的数据出去 |
public void write(int/byte[]/byte[]一部分) | 可以支持写字节数据出去 |
7.7.5PrintWriter打印流的构造器、方法
同上,只需把Stream换成Writer。
7.7.6两个打印流的区别
【1】打印数据的功能上是一模一样的:都是使用方便,性能高效(核心优势)
【2】PrintStream继承自字节输出流OutputStream,因此支持写字节数据的方法。
【3】PrintWriter继承自字符输出流Writer,因此支持写字符数据出去。
public static void main(String[] args) {
// 目标:打印流的使用。
try (
// PrintStream ps = new PrintStream("day03-file-io/src/ps.txt");
PrintStream ps = new PrintStream(new FileOutputStream("day03-file-io/src/ps.txt", true));
// PrintWriter ps = new PrintWriter("day03-file-io/src/ps.txt");
){
ps.println(97);
ps.println('a');
ps.println("黑马");
ps.println(true);
ps.println(99.9);
}catch (Exception e){
e.printStackTrace();
}
}
7.7.7DataOutputStream数据输出流的作用
允许把数据和其类型一并写出去。
7.7.8数据输出流的构造器、方法
构造器 | 说明 |
public DataOutputStream(OutputStream out) | 创建新数据输出流包装基础的字节输出流 |
方法 | 说明 |
public final void writeByte(int v) throws IOException | 将byte类型的数据写入基础的字节输出流 |
public final void writeInt(int v) throws IOException | 将int类型的数据写入基础的字节输出流 |
public final void writeDouble(Double v) throws IOException | 将double类型的数据写入基础的字节输出流 |
public final void writeUTF(String str) throws IOException | 将字符串数据以UTF-8编码成字节写入基础的字节输出流 |
public void write(int/byte[]/byte[]一部分) | 支持写字节数据出去 |
public static void main(String[] args) {
// 目标:特殊数据流的使用。
try (
DataOutputStream dos = new DataOutputStream(new FileOutputStream("day03-file-io\\src\\data.txt"));
){
dos.writeByte(34);
dos.writeUTF("你好");
dos.writeInt(3665);
dos.writeDouble(9.9);
}catch (Exception e){
e.printStackTrace();
}
}
7.7.9DataInputStream数据输入流的作用
用于读取数据输出流写出去的数据。
7.7.10数据输入流的构造器、方法
同上,将write换成read即可
public static void main(String[] args) {
// 目标:特殊数据流的使用。
try (
DataInputStream dis = new DataInputStream(new FileInputStream("day03-file-io\\src\\data.txt"));
){
System.out.println(dis.readByte());
System.out.println(dis.readUTF());
System.out.println(dis.readInt());
System.out.println(dis.readDouble());
}catch (Exception e){
e.printStackTrace();
}
}
7.8IO框架
7.8.1什么是框架
解决某类问题,编写的一套类、接口等,可以理解成一个半成品,大多框架都是第三方研发的。
7.8.2框架的好处
在框架的基础上开发,可以得到优秀的软件架构,并能提高开发效率
7.8.3框架的形式
一般是把类、接口等编译成class形式,再压缩成一个.jar结尾的文件发行出去。
7.8.4什么是IO框架
封装了Java提供的对文件、数据进行操作的代码,对外提供了更简单的方式来对文件进行操作,对数据进行读写等。
7.8.5导入IO框架的步骤
【1】在项目中创建一个文件夹:lib
【2】将commons-io-2.6.jar文件复制到lib文件夹
【3】在jar文件上点右键,选择 Add as Library -> 点击OK
【4】在类中导包使用
7.8.6IO框架提供的部分方法
FileUtils类提供的部分方法展示 | 说明 |
public static void copyFile(File srcFile, File destFile) | 复制文件 |
public static void copyDirectory(File srcDir, File destDir) | 复制文件夹 |
public static void deleteDirectory(File directory) | 删除文件夹 |
public static String readFileToString(File file, String encoding) | 读数据 |
public static void writeStringToFile(File file, String data, String charname, boolean append) | 写数据 |
IOUtils类提供的部分方法展示 | 说明 |
public static int copy(InputStream inputStream, OutputStream outputStream) | 复制文件 |
public static int copy(Reader reader, Writer writer) | 复制文件 |
public static void write(String data, OutputStream output, String charsetName) | 写数据 |