A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 小石姐姐 于 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, 下载次数: 2)

1.3.1.jpg

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马