文章目录
- JAVA第六周学习笔记
- 方法引用
- 引用静态方法
- 引用成员方法
- 引用构造方法
- 其他调用方式
- 使用类名引用成员方法
- 引用数组的构造方法
 
 
- 异常
- 作用
- 处理方式
- 常见成员方法
- 抛出异常
- finally
- 自定义异常
 
- File
- 路径
- 构造方法
- **判断、获取**
- **创建 、删除**
- **获取并遍历**
- 练习
 
- IO流
- 体系
- 字节流
- FileOutputStream
- FileInputStream
- try-catch处理
 
- 编码
- 标准ASCII字符集
- GBK
- Unicode字符集
- 总结
- 乱码
- 编码与解码
 
- 字符流
- FileReader
- FIleWriter
- 原理分析
- 原理分析
 
 
JAVA第六周学习笔记
方法引用
把已经有的方法拿过来用,当做函数式接口中抽象方法的方法体
::方法引用符
- 被引用的方法必须已经存在
- 被引用方法的形参和返回值需要跟抽象方法保持一致
- 被引用方法的功能要满足当前需求
- 被引用方法的功能需要满是当前的要求
引用静态方法
格式:类名::静态方法
 范例:Integer::parseInt
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Function;
public class Test {
    public static void main(String[] args) {
        ArrayList<String> list1 = new ArrayList<>();
        Collections.addAll(list1, "1", "2", "3", "4");
        //使用匿名内部类来实现Function接口,将String转换为Integer
//        list1.stream().map(new Function<String, Integer>() {
//            @Override
//            public Integer apply(String s) {
//                int i = Integer.parseInt(s);
//                return i;
//            }
//        }).forEach(s-> System.out.println(s));
        
        // 使用流的map方法,将list1中的每个String元素转换为Integer
        // Integer::parseInt是一个方法引用,等同于上面注释掉的代码段
        list1.stream()
            .map(Integer::parseInt) // 将每个String元素转换为Integer
            .forEach(s -> System.out.println(s));
    }
}
引用成员方法
其他类的成员方法
本类的成员方法
父类的成员方法
格式:对象::成员方法
其他类:其他类对象::方法名
 本类:this::方法名
 父类:super::方法名
在静态方法中无法使用 this 关键字,因此只能创建类的对象引用方法。
引用构造方法
格式:类名::new
 范例:Student::new
其他调用方式
使用类名引用成员方法
格式:类名::成员方法
 范例:String::substring
第一个参数:表示被引用方法的调用者,决定了可以引用哪些类中的方法,在stream流当中,第一个参数一般都表示流里面的每一个数据。假设流里面的数据是字符串,那么使用这种方式进行方法引用,只能引用string这个类中的方法
 第二个参数到最后一个参数:跟被引用方法的形参保持一致,如果没有第二个参数,说明被引用的方法需要是无参的成员方法
引用数组的构造方法
格式:数据类型[]::new
 范例:int[]::new
技巧:
- 现在有没有一个方法符合我当前的需求
- 如果有这样的方法,这个方法是否满足引用的规则
- 静态: 类名::方法名
 成员方法: 三种
 构造方法:类名::new
异常
对于异常情况,Java使用了一种称为异常处理(exception handing)的错误捕获机制。

Error:代表的系统级别错误(属于严重问题)
 系统一旦出现问题,sun公司会把这些错误封装成Error对象:
 Error是给sun公司自己用的,不是给我们程序员用的。
 因此我们开发人员不用管它,
-  Exception:叫做异常,代表程序可能出现的问题 我们通常会用Exception以及他的子类来封装程序出现的问题 RuntimeException及其子类,编译阶段不会出现异常提醒。 
-  编译时异常: 
 没有继承RuntimeExcpetion的异常,直接继承于Excpetion。编译阶段就会出现异常提醒的。(如:日期解析异常)
-  运行时异常: RuntimeException本身和子类。 
 编译阶段没有错误提示,运行时出现的异常(如:数组索引越界异常)
Error 类: ⼀般是指与虚拟机相关的问题,如:系统崩溃、虚拟机错误、内存空间不⾜、⽅法调⽤栈溢出等。这类
错误将会导致应⽤程序中断,仅靠程序本身⽆法恢复和预防;
Exception 类:分为运⾏时异常和受检查的异常
运⾏时异常:如:空指针异常、指定的类找不到、数组越界、⽅法传递参数错误、数据类型转换错误。可以编译通过,但是⼀运⾏就停⽌了,程序不会⾃⼰处理;
受检查异常:要么⽤ try … catch… 捕获,要么⽤ throws 声明抛出,交给⽗类处理。
| 特征 | 编译时异常 | 运行时异常 | 
|---|---|---|
| 检测时机 | 在编译阶段检测到 | 在运行时检测到 | 
| 处理方式 | 必须显式处理或声明抛出 | 通常不需要显式处理或声明抛出 | 
| 继承关系 | 继承自 Exception | 继承自 RuntimeException | 
| 典型示例 | IOException、ParseException 等 | NullPointerException、ArrayIndexOutOfBoundsException 等 | 
作用
- 异常是用来查询bug的关键参考信息
- 异常可以作为方法内部的一种特殊返回值,以便通知调用者底层的执行情况
处理方式
- JVM默认的处理方式
- 自己处理
- 抛出异常
格式:
try {
    可能出现异常的代码;
} catch(异常类名 变量名) {
    异常的处理代码;
}
public class test02 {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5, 6};
        try {
            System.out.println(arr[10]);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("数组越界");
        }
        System.out.println("这里是执行的");
    }
}
目的:当代码出现异常时,可以让程序继续往下执行。
-  如果try中没有遇到问题,怎么执行? catch 块将不会被执行。 
-  如果try中可能会遇到多个问题,怎么执行? 添加多个 catch 块来捕获不同类型的异常。 父类异常写在下面 
-  如果try中遇到的问题没有被捕获,怎么执行? 异常将会被抛到 JVM,导致程序终止执行,并输出异常信息。 
-  如果try中遇到了问题,那么try下面的其他代码还会执行吗? try 块中异常发生点后面的代码将不会执行。程序会立即跳转到与异常匹配的 catch 块中执行相应的异常处理逻 
常见成员方法
| 方法名称 | 说明 | 
|---|---|
| public String getMessage() | 返回此 Throwable 的详细消息字符串 | 
| public String toString() | 返回此可抛出对象的简短描述 | 
| public void printStackTrace() | 将异常的错误信息输出到控制台 | 
printStackTrace仅仅打印信息,不会结束程序
抛出异常
throws:
 写在方法定义处,表示声明一个异常,告诉调用者,使用本方法可能会有哪些异常
public void 方法()throws 异常类名1,异常类名2...{
}
throw:
 写在方法内,结束方法,手动抛出异常对象,交给调用者,方法中下面的代码不再执行了
public void 方法() {
throw new NullPointerException();
}
编译时异常:必须要写
 运行时异常:可以不写。
finally
finally关键字用来创建在 try 代码块后面执行的代码块。
无论是否发生异常,finally 代码块中的代码总会被执行。
在 finally 代码块中,可以运行清理类型等收尾善后性质的语句。
finally 代码块出现在 catch 代码块最后,语法如下:
try{
  // 程序代码
}catch(异常类型1 异常的变量名1){
  // 程序代码
}catch(异常类型2 异常的变量名2){
  // 程序代码
}finally{
  // 程序代码
}
问题一,finally是不是⼀定会被执行到?
不⼀定
- 在执行 finally 块之前 JVM 强制退出
- 执行线程被杀死
- 无限循环
- 调用了System.exit()。
问题二、try-catch-finally中,如果catch中return了,finally还会执⾏吗?
如果在 catch 块中执行了 return 语句,finally 块仍然会在返回之前执行。
对于基本数据类型,因为返回的是数值本身而不是引用,finally 块中对返回值的修改不会影响到实际的返回值。
但是对于引用类型,finally 块中对引用对象的修改会影响到返回值。这是因为引用类型的返回值是对象的地址,而不是对象本身。因此,如果在 finally 块中修改了引用对象的状态,这些修改会在返回之后仍然生效。
问题三、try-catch-finally 中那个部分可以省略?
catch 可以省略。try 只适合处理运⾏时异常,try+catch 适合处理运⾏时异常+普通异常。也就是说,如果你只⽤
try 去处理普通异常却不加以 catch 处理,编译是通不过的,因为编译器硬性规定,普通异常如果选择捕获,则必
须⽤ catch 显示声明以便进⼀步处理。⽽运⾏时异常在编译时没有如此规定,所以 catch 可以省略,你加上 catch
编译器也觉得⽆可厚⾮
自定义异常
- 定义异常类
- 写继承关系
- 空参构造
- 带参构造
- 所有异常都必须是 Throwable 的子类。
- 如果希望写一个检查性异常类,则需要继承 Exception 类。
- 如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
public class CustomException extends Exception {
    // 自定义异常类的代码
}

意义:
自定义异常的意义在于提高程序的可读性和可维护性,使得控制台输出的错误信息更加具有意义和可理解性。通过自定义异常,为不同的错误场景定义具有描述性的异常类,使异常信息能够清晰地传达出问题的类型和原因,有助于开发人员快速定位和解决问题。
File
路径
File对象就表示一个路径,可以是文件的路径、也可以是文件夹的路径
 这个路径可以是存在的,也允许是不存在的
相对路径
相对路径是相对于当前工作目录或者某个基准目录的路径描述。换句话说,相对路径描述的是一个文件或目录相对于另一个文件或目录的位置关系。
绝对路径
绝对路径是从文件系统的根目录开始的完整路径描述。无论当前工作目录在哪里,绝对路径都能够准确地描述一个文件或目录的位置。
构造方法
| 方法名称 | 说明 | 
|---|---|
| public File(String pathname) | 根据文件路径创建文件对象 | 
| public File(String parent, String child) | 根据父路径名字符串和子路径名字符串创建文件对象 | 
| public File(File parent, String child) | 根据父路径对应文件对象和子路径名字符串创建文件对象 | 
import java.io.File;
public class Test {
    public static void main(String[] args) {
        String str = "C:\\Users\\HP\\Desktop\\ggg.txt";
        File f1 = new File(str);
        System.out.println(f1);
        String parent = "C:\\Users\\HP\\Desktop\\";
        String child = "ggg.txt";
        File f2 = new File(parent, child);
        System.out.println(f2);
        File f3 = new File(parent);
        File f4 = new File(f3, child);
        System.out.println(f3);
        System.out.println(f4);
    }
}
判断、获取
| 方法名称 | 说明 | 
|---|---|
| public boolean isDirectory() | 判断此路径名表示的File是否为文件夹 | 
| public boolean isFile() | 判断此路径名表示的File是否为文件 | 
| public boolean exists() | 判断此路径名表示的File是否存在 | 
| public long length() | 返回文件的大小(字节数量) | 
| public String getAbsolutePath() | 返回文件的绝对路径 | 
| public String getPath() | 返回定义文件时使用的路径 | 
| public String getName() | 返回文件的名称,带后缀 | 
| public long lastModified() | 返回文件的最后修改时间(时间毫秒值) | 
-  length- 返回的是字节
- 无法获取文件夹大小
 
-  length
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test {
    public static void main(String[] args) {
        String str = "C:\\Users\\HP\\Desktop\\ggg.txt";
        File f1 = new File(str);
        System.out.println(f1);
        long d = f1.lastModified();
        Date date = new Date(d);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
        System.out.println(sdf.format(date));
    }
}
创建 、删除
| 方法名称 | 说明 | 
|---|---|
| public boolean createNewFile() | 创建一个新的空的文件 | 
| public boolean mkdir() | 创建单级文件夹 | 
| public boolean mkdirs() | 创建多级文件夹 | 
| public boolean delete() | 删除文件、空文件夹 | 
-  createNewFile创建一个新的空的文件-  细节1:如果当前路径表示的文件是不存在的,则创建成功,方法返回true  如果当前路径表示的文件是存在的,则创建失败,方法返回false 
-  细节2:如果父级路径是不存在的,那么方法会有异常 IOException
-  细节3: createNewFile方法创建的一定是文件,如果路径中不包含后缀名,则创建一个没有后缀的文件
 
-  
-  mkdir(make Directory)- 细节1:windows当中路径是唯一的,如果当前路径已经存在,则创建失败,返回false
- 细节2:mkdir方法只能创建单级文件夹,无法创建多级文件夹
 
-  mkdirs(make Directory)创建多级文件夹细节:既可以创建单级的,又可以创建多级的文件夹 
-  delete - 如果删除的是文件:直接删除,不走回收站
- 如果删除的是文件夹: 
    - 空文件夹,直接删除,不走回收站
- 有内容的文件夹,删除失败
 
 
获取并遍历
| 方法名称 | 说明 | 
|---|---|
| public File[] listFiles() | 获取当前该路径下所有内容 | 
- 当调用者File表示的路径不存在时,返回null
- 当调用者File表示的路径是文件时,返回null
- 当调用者File表示的路径是一个空文件夹时,返回一个长度为0的数组
- 当调用者File表示的路径是一个有内容的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回
- 当调用者File表示的路径是一个有隐藏文件的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏文件
- 当调用者File表示的路径是需要权限才能访问的文件夹时,返回null
| 方法名称 | 说明 | 
|---|---|
| public static File[] listRoots() | 列出可用的文件系统根(获取系统中所有的盘符) | 
| public String[] list() | 获取当前该路径下所有内容(仅仅能获取名字 ) | 
| public String[] list(FilenameFilter filter) | 利用文件名过滤器获取当前该路径下所有内容 | 
| public File[] listFiles() | 获取当前该路径下所有内容 | 
| public File[] listFiles(FileFilter filter) | 利用文件名过滤器获取当前该路径下所有内容 | 
| public File[] listFiles(FilenameFilter filter) | 利用文件名过滤器获取当前该路径下所有内容 | 
import java.io.File;
import java.io.FilenameFilter;
import java.util.Arrays;
public class Test {
    public static void main(String[] args) {
        File f2 = new File("p:\\aaa");
        String[] arr3 = f2.list(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                File src = new File(dir, name);
                return src.isFile() && name.endsWith(".txt");
            }
        });
        System.out.println(Arrays.toString(arr3));
    }
}
练习
找文件夹下某后缀文件,包括文件夹中的
递归
public static void findmd() {
    // 获取所有根目录
    File arr[] = File.listRoots();
    // 遍历所有根目录
    for (File f : arr) {
        // 调用findmd(File src)方法,从根目录开始递归查找.md文件
        findmd(f);
    }
}
public static void findmd(File src) {
    // 获取目录下的所有文件和子目录
    File[] files = src.listFiles();
    // 如果目录不为空
    if (files != null) {
        // 遍历目录下的所有文件和子目录
        for (File file : files) {
            // 如果是文件
            if (file.isFile()) {
                // 检查文件是否以".md"结尾,如果是则打印文件名
                if (file.getName().endsWith(".md"))
                    System.out.println(file.getName());
            } else {
                // 如果是目录,递归调用findmd(File src)方法,继续查找该子目录下的.md文件
                findmd(file);
            }
        }
    }
}
计算当前目录大小
public static long getCapticpy(File src) {
    // 获取目录下的所有文件和子目录
    File[] files = src.listFiles();
    // 初始化总字节数
    long sum = 0;
    // 如果目录不为空
    if (files != null) {
        // 遍历目录下的所有文件和子目录
        for (File f : files) {
            // 如果是文件,累加文件长度到总字节数中
            if (f.isFile()) {
                sum += f.length();
            } else {
                // 如果是目录,递归调用getCapticpy方法,将返回的字节数累加到总字节数中
                sum += getCapticpy(f);
            }
        }
    }
    // 返回总字节数
    return sum;
}
IO流
存储和读取数据的解决方案
用于读写文件中的数据(可以读写文件,或网络中的数据)
按流向分:
- 输出流:程序
- 输入流:文件
按操作文件的类型分:
- 字节流:可以操作所有类型的文件
- 字符流:只能操作纯文本文件
纯文本文件:用windows系统自带的记事本打开并且能读懂的文件。
 例如:txt文件,md文件,xml文件,lrc文件等
体系

字节流
FileOutputStream
操作本地文件的字节输出流,可以把程序中的数据写到本地文件中。
一、构造方法
| 构造方法 | 描述 | 
|---|---|
| FileOutputStream(String name) | 使用指定的文件名创建一个 FileOutputStream对象。如果文件不存在,会创建一个新文件。 | 
| FileOutputStream(String name, boolean append) | 使用指定的文件名和 append标志创建FileOutputStream对象。如果append为true,字节将被写入文件末尾。 | 
| FileOutputStream(File file) | 使用指定的 File对象创建FileOutputStream对象。如果文件不存在,会创建一个新文件。 | 
| FileOutputStream(File file, boolean append) | 使用指定的 File对象和append标志创建FileOutputStream对象。如果append为true,字节将被写入文件末尾。 | 
| FileOutputStream(FileDescriptor fdObj) | 创建一个文件输出流以写入指定的文件描述符。 | 
二、书写步骤
- 创建字节输出流对象
- 写数据
- 释放资源
细节:
- 创建字节输出流对象
 细节1:参数是字符串表示的路径或者是File对象都是可以的
 细节2:如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的。
 细节3:如果文件已经存在,则会清空文件
- 写数据
 细节:write方法的参数是整数,但是实际上写到本地文件中的是整数在ASCII上对应的字符
- 释放资源
 细节:每次使用完流之后都要释放资源
import java.io.FileOutputStream;
import java.io.IOException;
public class Test {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("..\\5.20\\a.txt");
        fos.write(97);
        fos.close();
    }
}
三、write用法
| 方法名称 | 说明 | 
|---|---|
| void write(int b) | 一次写一个字节数据 | 
| void write(byte[] b) | 一次写一个字节数组数据 | 
| void write(byte[] b, int off, int len) | 一次写一个字节数组的部分数据 | 
public class Test {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("..\\5.20\\a.txt");
        String str = "abcd";
        byte[] bytes = str.getBytes();
        fos.write(bytes);
        fos.close();
    }
}
void write(byte[] b, int off, int len):
off:起始索引:字节数组b中的起始偏移量。表示从数组的哪个索引位置开始写入数据。(off必须是非负整数,且不能超过数组b的长度。)
len:写入字节数:这是要写入的字节数。从off位置开始,连续写入len个字节。
四、换行与续写
1.换行
 写出一个换行符即可
 windows:\r\n
 Linux:\n
 Mac:\r
在windows操作系统当中,java对回车换行进行了优化。
虽然完整的是\r\n,但是我们写其中一个\r或者\n,
java也可以实现换行,因为java在底层会补全。
2.续写
FileOutputStream(String name, boolean append)
FileOutputStream(File file, boolean append)
如果 append 为 true,字节将被写入文件末尾。
	FileOutputStream fos = new FileOutputStream("..\\5.20\\a.txt",true);
FileInputStream
操作本地文件的字节输入流,可以把本地文件中的数据读取到程序中来。
一、构造方法
| 构造方法 | 参数描述 | 
|---|---|
| FileInputStream(File file) | 要读取的文件对象。 | 
| FileInputStream(String name) | 要读取的文件的路径名。 | 
| FileInputStream(FileDescriptor fd) | 文件描述符对象,表示已打开的文件。 | 
二、书写步骤
- 创建字节输入流对象
- 读数据
- 释放资源
细节:
-  
  - 如果文件不存在,就直接报错。
 
-  
  - 一次读一个字节,读出来的是数据在ASCII上对应的数字
- 读到文件末尾了,read方法返回-1。
 
- 每次使用完流必须要释放资源。
三、循环读取
import java.io.FileInputStream;
import java.io.IOException;
public class Demo {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("..\\5.20\\a.txt");
        int b;
        while ((b = fis.read()) != -1) {
            System.out.print((char) b);
        }
        fis.close();
    }
}
四、read
| 方法名称 | 说明 | 
|---|---|
| public int read() | 一次读取一个字节数据。 | 
| public int read(byte[] buffer) | 一次读取一个字节数组数据。 | 
一般创建1024的整数倍
五、文件拷贝
public int read():
public class Demo2 {
    public static void main(String[] args) throws IOException {
        // 创建文件输入流,用于读取原始文件
        FileInputStream fis = new FileInputStream("E:\\MyMarkdown\\链表排序.md");
        // 创建文件输出流,用于写入复制后的文件
        FileOutputStream fos = new FileOutputStream("E:\\MyMarkdown\\链表排序_copy.md");
        
        int b;
        // 循环读取原始文件的字节,写入到复制后的文件中
        while((b = fis.read()) != -1){
            fos.write(b);
        }
        
        // 关闭输入流和输出流
        fos.close();
        fis.close();
    }
}
public int read(byte[] buffer):
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Test02 {
    public static void main(String[] args) throws IOException {
        // 创建 FileInputStream 对象,用于读取源文件
        FileInputStream fis = new FileInputStream("..\\5.20\\a.txt");
        
        // 创建 FileOutputStream 对象,用于写入目标文件
        FileOutputStream fos = new FileOutputStream("..\\5.20\\acopy.txt");
        
        // 使用 try-with-resources 语句,确保在结束时自动关闭流
        try (fos; fis) {
            int len;
            byte[] bytes = new byte[1024];
            
            // 循环读取源文件,并将读取的数据写入目标文件
            while ((len = fis.read(bytes)) != -1) {
                fos.write(bytes, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
try-catch处理

AutoCloseable 接口的定义:
package java.lang;
public interface AutoCloseable {
    void close() throws Exception;
}
AutoCloseable 接口只包含一个 close() 方法,该方法没有参数,并且可能会抛出 Exception 异常。类实现了 AutoCloseable 接口的意思就是它承诺可以在不再需要时释放资源,以确保资源的正常管理和释放。
使用 AutoCloseable 接口可以让资源管理更加简单和安全,在 try-with-resources 语句中使用,无需手动编写 finally 块来关闭资源。在 try-with-resources 语句中,当 try 块结束时,自动调用 close() 方法释放资源。
编码
标准ASCII字符集
美国制定,包含美国使用的英文字符等,共128个字符
每一个字符对应一个码点,码点转化为二进制,首位是0

GBK
国标,汉字编码字符集
包含两万多个汉字等字符,
GBK中一个中文字符编码成两个字节形式存储
汉字第一个字节的第一位必须是
1,所以能代表2^15=32768个
GBK兼容ASCII字符集
英文和ASCII一致

Unicode字符集
( 统一码,万国码)
国际组织制定,容纳世界上所有文字,符号的字符集
Unicode Transfer Format
- UTF-32
四个字节一个字符
- UTF-16
2-4个字节一个字符
- UTF-8
可变长编码方案,四个长度区:1,2,3,4
英文数字,字符占一个字节,汉字字符占用三个字节

总结

- 字符编码时使用字符集和解码使用时字符集需要一致
- 英文数字一般不会乱码
乱码
原因:
- 读取数据时未读完整个汉字
- 编码和解码时的方式不统一
如何不产生乱码:
- 不要用字节流读取文本文件
- 编码解码时使用同一个码表,同一个编码方式
编码与解码
| 方法名称 | 说明 | 
|---|---|
| public byte[] getBytes() | 使用默认方式进行编码。 | 
| public byte[] getBytes(String charsetName) | 使用指定方式进行编码。 | 
| public String(byte[] bytes) | 使用默认方式进行解码。 | 
|---|---|
| public String(byte[] bytes, String charsetName) | 使用指定方式进行解码。 | 
package com.feng.test03;
import java.io.UnsupportedEncodingException;
public class Test03 {
    public static void main(String[] args) throws UnsupportedEncodingException {
        String s = "ni好";
        byte[] bytes1 = s.getBytes();
        byte[] bytes2 = s.getBytes("GBK");
        System.out.println(new String(bytes1));
        System.out.println(new String(bytes2, "GBK"));
    }
}
字符流
字符流的底层其实就是字节流
字符流 = 字节流 + 字符集
特点:
- 输入流:一次读一个字节,遇到中文时,一次读多个字节
- 输出流:底层会把数据按照指定的编码方式进行编码,变成字节再写到文件中
使用场景
对于纯文本文件进行读写操作
FileReader
一、构造方法
FileReader 构造方法
| 构造方法 | 说明 | 
|---|---|
| public FileReader(File file) | 创建字符输入流关联本地文件。 | 
| public FileReader(String pathname) | 创建字符输入流关联本地文件。参数是文件路径的字符串表示。 | 
如果文件不存在,就直接报错
二、书写步骤
- 创建字符输入流对象
 如果文件不存在,就直接报错
- 读取数据
| 方法签名 | 方法功能 | 细节 | 
|---|---|---|
| public int read() | 读取数据 | 按字节读取,遇到多字节字符会一次读取多个字节并解码为整数 | 
| public int read(char[] buffer) | 读取数据到字符数组中 | 按字节读取,遇到多字节字符会一次读取多个字节并解码为字符并填充到字符数组中 | 
如果读取到文件末尾,则返回 -1。
- 释放资源
public int read():
默认也是一个字节一个字节的读取的,如果遇到中文就会一次读取多个,在读取之后,方法的底层还会进行解码并转成十进制。
返回值就是十进制
public class Test01 {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("a.txt");
        int i;
        while ((i = fr.read()) != -1) {
            System.out.print((char)i);
        }
    }
}
public int read(char[] buffer):
读取数据,解码,强转三步合并了,把强转之后的字符放到数组当中
相当于空参的read+ 强转类型转换
public class Test01 {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("a.txt");
        int i;
        char[] chars = new char[2];
        int len;
        while ((len = fr.read(chars)) != -1) {
            System.out.print(new String(chars, 0, len));
        }
    }
}
FIleWriter
一、构造方法
| 构造方法 | 说明 | 
|---|---|
| public FileWriter(File file) | 创建字符输出流关联本地文件。 | 
| public FileWriter(String pathname) | 创建字符输出流关联本地文件。 | 
| public FileWriter(File file, boolean append) | 创建字符输出流关联本地文件,续写。 | 
| public FileWriter(String pathname, 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) | 写出字符数组的一部分 | 
三、步骤
-  创建字符输出流对象 细节1:参数是字符串表示的路径或者File对象都是可以的 
 细节2:如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的
 细节3:如果文件已经存在,则会清空文件,如果不想清空可以打开续写开关
-  写数据 如果write方法的参数是整数,但是实际上写到本地文件中的是整数在字符集上对应的字符 
-  释放资源 细节:每次使用完流之后都要释放资源 
public class Test02 {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("a.txt", true);
        
        fw.write(25105);
        fw.write("???");
        char[] chars = {'a', 'b', '你'};
        fw.write(chars);
        
        fw.close();
    }
}
原理分析
一、字符输入流
-  创建字符输入流对象 底层:关联文件,并创建缓冲区(长度为8192的字节数组) 
-  读取数据 底层:判断缓冲区中是否有数据可以读取 - 缓冲区没有数据:就从文件中获取数据,装到缓冲区中,每次尽可能装满缓冲区,
 如果文件中也没有数据了,返回-1。
- 缓冲区有数据:就从缓冲区中读取
 空参的read方法:一次读取一个字节,遇到中文一次读多个字节,把字节解码并转成十进制返回
 有参的read方法:把读取字节,解码,强转三步合并了,强转之后的字符放到数组中
 
- 缓冲区没有数据:就从文件中获取数据,装到缓冲区中,每次尽可能装满缓冲区,
二、字符输出流
| 成员方法 | 说明 | 
|---|---|
| public void flush() | 将缓冲区中的数据,刷新到本地文件中 | 
| public void close() | 释放资源/关流 | 
flush刷新:刷新之后,还可以继续往文件中写出数据
close关流:断开通道,无法再往文件中写出数据
 数在字符集上对应的字符
-  释放资源 细节:每次使用完流之后都要释放资源 
public class Test02 {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("a.txt", true);
        
        fw.write(25105);
        fw.write("???");
        char[] chars = {'a', 'b', '你'};
        fw.write(chars);
        
        fw.close();
    }
}
原理分析
一、字符输入流
-  创建字符输入流对象 底层:关联文件,并创建缓冲区(长度为8192的字节数组) 
-  读取数据 底层:判断缓冲区中是否有数据可以读取 - 缓冲区没有数据:就从文件中获取数据,装到缓冲区中,每次尽可能装满缓冲区,
 如果文件中也没有数据了,返回-1。
- 缓冲区有数据:就从缓冲区中读取
 空参的read方法:一次读取一个字节,遇到中文一次读多个字节,把字节解码并转成十进制返回
 有参的read方法:把读取字节,解码,强转三步合并了,强转之后的字符放到数组中
 
- 缓冲区没有数据:就从文件中获取数据,装到缓冲区中,每次尽可能装满缓冲区,
二、字符输出流
| 成员方法 | 说明 | 
|---|---|
| public void flush() | 将缓冲区中的数据,刷新到本地文件中 | 
| public void close() | 释放资源/关流 | 
flush刷新:刷新之后,还可以继续往文件中写出数据
close关流:断开通道,无法再往文件中写出数据
如有错误烦请指正
感谢您的阅读



















