JavaSE进阶

详解Java基础知识

Posted by Will Wang on April 12, 2018

1. Object类、常用API

1.1 Object类

  • java.lang.Object
    • 类 Object 是类层次结构的根(父)类。
    • 每个类(Person,Student…)都使用 Object 作为超(父)类。
    • 所有对象(包括数组)都实现这个类的方法。
1.1.1 Object类equals方法
  • Person类默认继承了Object类,所以可以使用Object类的equals方法
    • boolean equals(Object obj) 指示其他某个对象是否与此对象“相等”。
    • equals方法源码:
        public boolean equals(Object obj) {
            return (this == obj);
        }
      
    • 参数:
      • Object obj:可以传递任意的对象
      • == 比较运算符,返回的是一个布尔值 true false
      • 基本数据类型:比较的是值
      • 引用数据类型:比价的是两个对象的地址值
      • this是谁?那个对象调用的方法,方法中的this就是那个对象;p1调用的equals方法所以this就是p1
      • obj是谁?传递过来的参数p2
      • this == obj –> p1 == p2
1.1.2 Object类equals方法防止空指针异常
  • Objects类的equals方法:对两个对象进行比较,防止空指针异常
      public static boolean equals(Object a, Object b) {
          return (a == b) || (a != null && a.equals(b));
      }
    

    1.2 Date类

  • long getTime() 把日期转换为毫秒值(相当于System.currentTimeMillis()方法)
    • 返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。
  • Date类的带参数构造方法
    • Date(long date) :传递毫秒值,把毫秒值转换为Date日期
  • Date类的空参数构造方法
    • Date() 获取当前系统的日期和时间
1.2.1 DateFormat类成员方法
  • java.text.DateFormat:是日期/时间格式化子类的抽象类
    • 作用:
      格式化(也就是日期 -> 文本)、解析(文本-> 日期)
    • 成员方法:
      String format(Date date) 按照指定的模式,把Date日期,格式化为符合模式的字符串
      Date parse(String source) 把符合模式的字符串,解析为Date日期
    • DateFormat类是一个抽象类,无法直接创建对象使用,可以使用DateFormat类的子类
  • java.text.SimpleDateFormat extends DateFormat
    • 构造方法:
      SimpleDateFormat(String pattern)
      用给定的模式和默认语言环境的日期格式符号构造 SimpleDateFormat。
    • 参数:
      String pattern:传递指定的模式
      模式:区分大小写的
      字符|中文 —|— y | 年 M | 月 d | 日 H | 时 m | 分 s | 秒
    • 写对应的模式,会把模式替换为对应的日期和时间 “yyyy-MM-dd HH:mm:ss”
    • 注意:
      • 模式中的字母不能更改,连接模式的符号可以改变
      • “yyyy年MM月dd日 HH时mm分ss秒”
1.2.2 DateFormat类parse方法
  • 使用DateFormat类中的方法parse,把文本解析为日期
  • 使用步骤:
    1. 创建SimpleDateFormat对象,构造方法中传递指定的模式
    2. 调用SimpleDateFormat对象中的方法parse,把符合构造方法中模式的字符串,解析为Date日期
  • 注意:
    • public Date parse(String source) throws ParseException
    • parse方法声明了一个异常叫ParseException
    • 如果字符串和构造方法的模式不一样,那么程序就会抛出此异常
    • 调用一个抛出了异常的方法,就必须的处理这个异常,要么throws继续抛出这个异常,要么try catch自己处理
1.2.3 DateFormat类format方法
  • 使用DateFormat类中的方法format,把日期格式化为文本
  • 使用步骤:
    1. 创建SimpleDateFormat对象,构造方法中传递指定的模式
    2. 调用SimpleDateFormat对象中的方法format,按照构造方法中指定的模式,把Date日期格式化为符合模式的字符串(文本)

1.3 Calendar类

  • java.util.Calendar类:日历类
    • Calendar类是一个抽象类,里边提供了很多操作日历字段的方法(YEAR、MONTH、DAY_OF_MONTH、HOUR )
    • Calendar类无法直接创建对象使用,里边有一个静态方法叫getInstance(),该方法返回了Calendar类的子类对象
    • static Calendar getInstance() 使用默认时区和语言环境获得一个日历。
1.3.1 Calendar类成员方法
  • Calendar类的常用成员方法:
    • public int get(int field):返回给定日历字段的值。
    • public void set(int field, int value):将给定的日历字段设置为给定值。
    • public abstract void add(int field, int amount):根据日历的规则,为给定的日历字段添加或减去指定的时间量。
    • public Date getTime():返回一个表示此Calendar时间值(从历元到现在的毫秒偏移量)的Date对象。
  • 成员方法的参数:
    • int field:日历类的字段,可以使用Calendar类的静态成员变量获取
      • public static final int YEAR = 1; 年
      • public static final int MONTH = 2; 月
      • public static final int DATE = 5; 月中的某一天
      • public static final int DAY_OF_MONTH = 5;月中的某一天
      • public static final int HOUR = 10; 时
      • public static final int MINUTE = 12; 分
      • public static final int SECOND = 13; 秒

1.4 数组

  • public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length):
    将数组中指定的数据拷贝到另一个数组中。
    • 参数:
      • src - 源数组。
      • srcPos - 源数组中的起始位置(起始索引)。
      • dest - 目标数组。
      • destPos - 目标数据中的起始位置。
      • length - 要复制的数组元素的数量。
  • 练习:
    • 将src数组中前3个元素,复制到dest数组的前3个位置上
      • 复制元素前: src数组元素[1,2,3,4,5],dest数组元素[6,7,8,9,10]
      • 复制元素后: src数组元素[1,2,3,4,5],dest数组元素[1,2,3,9,10]

1.5 StringBuilder类

  • java.lang.StringBuilder类:字符串缓冲区,可以提高字符串的效率
  • 构造方法:
    • StringBuilder()
      构造一个不带任何字符的字符串生成器,其初始容量为 16 个字符。
    • StringBuilder(String str)
      构造一个字符串生成器,并初始化为指定的字符串内容。
1.5.1 StringBuilder常用方法
  • public StringBuilder append(…):
    添加任意类型数据的字符串形式,并返回当前对象自身。
1.5.2 StringBuilder和String相互转换
  • String->StringBuilder:可以使用StringBuilder的构造方法
    • StringBuilder(String str) 构造一个字符串生成器,并初始化为指定的字符串内容。
  • StringBuilder->String:可以使用StringBuilder中的toString方法
    • public String toString():将当前StringBuilder对象转换为String对象。

1.6 装箱与拆箱

  • 装箱:把基本类型的数据,包装到包装类中(基本类型的数据->包装类)
    • 构造方法:
      • Integer(int value) 构造一个新分配的 Integer 对象,它表示指定的 int 值。
      • Integer(String s) 构造一个新分配的 Integer 对象,它表示 String 参数所指示的 int 值。
      • 传递的字符串,必须是基本类型的字符串,否则会抛出异常 “100” 正确 “a” 抛异常
    • 静态方法:
      • static Integer valueOf(int i) 返回一个表示指定的 int 值的 Integer 实例。
      • static Integer valueOf(String s) 返回保存指定的 String 的值的 Integer 对象。
  • 拆箱:在包装类中取出基本类型的数据(包装类->基本类型的数据)
    • 成员方法:
      • int intValue() 以 int 类型返回该 Integer 的值。
1.6.1 自动装箱与自动拆箱
  • 基本类型的数据和包装类之间可以自动的相互转换
  • JDK1.5之后出现的新特性
1.6.2 基本类型与字符串类型相互转换
  • 基本类型->字符串(String)
    1. 基本类型的值+”” 最简单的方法(工作中常用)
    2. 包装类的静态方法toString(参数),不是Object类的toString() 重载
      static String toString(int i) 返回一个表示指定整数的 String 对象。
    3. String类的静态方法valueOf(参数)
      static String valueOf(int i) 返回 int 参数的字符串表示形式。
  • 字符串(String)->基本类型
    • 使用包装类的静态方法parseXXX(“字符串”);
      • Integer类: static int parseInt(String s)
      • Double类: static double parseDouble(String s)

2. Collection、泛型

2.1 Collection

  • java.util.Collection接口
    • 所有单列集合的最顶层的接口,里边定义了所有单列集合共性的方法
    • 任意的单列集合都可以使用Collection接口中的方法
  • 共性的方法:
    • public boolean add(E e) : 把给定的对象添加到当前集合中 。
    • public void clear() : 清空集合中所有的元素。
    • public boolean remove(E e) : 把给定的对象在当前集合中删除。
    • public boolean contains(E e) : 判断当前集合中是否包含给定的对象。
    • public boolean isEmpty() : 判断当前集合是否为空。
    • public int size() : 返回集合中元素的个数。
    • public Object[] toArray() : 把集合中的元素,存储到数组中。

2.2 迭代器

  • java.util.Iterator接口:迭代器(对集合进行遍历)
    • 有两个常用的方法
      • boolean hasNext() 如果仍有元素可以迭代,则返回 true。
        判断集合中还有没有下一个元素,有就返回true,没有就返回false
      • E next() 返回迭代的下一个元素。
        取出集合中的下一个元素
    • Iterator迭代器,是一个接口,我们无法直接使用,需要使用Iterator接口的实现类对象,获取实现类的方式比较特殊
    • Collection接口中有一个方法,叫iterator(),这个方法返回的就是迭代器的实现类对象 Iterator iterator() 返回在此 collection 的元素上进行迭代的迭代器。
  • 迭代器的使用步骤(重点):
    1. 使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态)
    2. 使用Iterator接口中的方法hasNext判断还有没有下一个元素
    3. 使用Iterator接口中的方法next取出集合中的下一个元素

2.3 增强for

  • 增强for循环:底层使用的也是迭代器,使用for循环的格式,简化了迭代器的书写
    • 是JDK1.5之后出现的新特性
    • Collectionextends Iterable : 所有的单列集合都可以使用增强for
    • public interface Iterable实现这个接口允许对象成为 "foreach" 语句的目标。
  • 增强for循环:用来遍历集合和数组
    • 格式:
        for(集合/数组的数据类型 变量名: 集合名/数组名){
            sout(变量名);
        }
      

2.4 泛型

  • 创建集合对象,使用泛型
    • 好处:
      1. 避免了类型转换的麻烦,存储的是什么类型,取出的就是什么类型
      2. 把运行期异常(代码运行之后会抛出的异常),提升到了编译期(写代码的时候会报错)
    • 弊端:
      • 泛型是什么类型,只能存储什么类型的数据
2.4.1 泛型利弊
  • 创建集合对象,不使用泛型
    • 好处:
      • 集合不使用泛型,默认的类型就是Object类型,可以存储任意类型的数据
    • 弊端:
      • 不安全,会引发异常
2.4.2 泛型的通配符
  • ?:代表任意的数据类型
  • 使用方式:
    • 不能创建对象使用
    • 只能作为方法的参数使用
  • 定义一个方法,能遍历所有类型的ArrayList集合
    • 这时候我们不知道ArrayList集合使用什么数据类型,可以泛型的通配符?来接收数据类型
    • 注意 : 泛型没有继承概念的
2.4.3 泛型的上下限定
  • 泛型的上限限定: ? extends E 代表使用的泛型只能是E类型的子类/本身
  • 泛型的下限限定: ? super E 代表使用的泛型只能是E类型的父类/本身
2.4.4 含有泛型的类
  • 定义一个含有泛型的类,模拟ArrayList集合
    • 泛型是一个未知的数据类型,当我们不确定什么什么数据类型的时候,可以使用泛型
    • 泛型可以接收任意的数据类型,可以使用Integer,String,Student…
    • 创建对象的时候确定泛型的数据类型
2.4.5 含有泛型的接口1
  • 含有泛型的接口,第一种使用方式:定义接口的实现类,实现接口,指定接口的泛型
      public interface Iterator<E> {
          E next();
      }
    
  • Scanner类实现了Iterator接口,并指定接口的泛型为String,所以重写的next方法泛型默认就是String
      public final class Scanner implements Iterator<String>{
          public String next() {}
      }
    
2.4.6 含有泛型的接口2
  • 含有泛型的接口第二种使用方式:接口使用什么泛型,实现类就使用什么泛型,类跟着接口走
    • 就相当于定义了一个含有泛型的类,创建对象的时候确定泛型的类型
        public interface List<E>{
        boolean add(E e);
        E get(int index);
        }
      
        public class ArrayList<E> implements List<E>{
        public boolean add(E e) {}
        public E get(int index) {}
        }
      
2.4.7 含有泛型的方法
  • 定义含有泛型的方法:泛型定义在方法的修饰符和返回值类型之间
  • 格式:
      修饰符 <泛型> 返回值类型 方法名(参数列表(使用泛型)){
          方法体;
      }
    
    • 含有泛型的方法,在调用方法的时候确定泛型的数据类型
    • 传递什么类型的参数,泛型就是什么类型

3. List、Set

3.1 List接口

  • java.util.List接口 extends Collection接口
    • List接口的特点:
      1. 有序的集合,存储元素和取出元素的顺序是一致的(存储123 取出123)
      2. 有索引,包含了一些带索引的方法
      3. 允许存储重复的元素
    • List接口中带索引的方法(特有)
      • public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。
      • public E get(int index):返回集合中指定位置的元素。
      • public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。
      • public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
    • 注意:
      • 操作索引的时候,一定要防止索引越界异常
      • IndexOutOfBoundsException:索引越界异常,集合会报
      • ArrayIndexOutOfBoundsException:数组索引越界异常
      • StringIndexOutOfBoundsException:字符串索引越界异常
3.1.1 LinkedList集合
  • java.util.LinkedList集合 implements List接口
    • LinkedList集合的特点:
      1. 底层是一个链表结构:查询慢,增删快
      2. 里边包含了大量操作首尾元素的方法

      注意:使用LinkedList集合特有的方法,不能使用多态 ```java public void addFirst(E e):将指定元素插入此列表的开头。 public void addLast(E e):将指定元素添加到此列表的结尾。 public void push(E e):将元素推入此列表所表示的堆栈。

    public E getFirst():返回此列表的第一个元素。 public E getLast():返回此列表的最后一个元素。

    public E removeFirst():移除并返回此列表的第一个元素。 public E removeLast():移除并返回此列表的最后一个元素。 public E pop():从此列表所表示的堆栈处弹出一个元素。

    public boolean isEmpty():如果列表不包含元素,则返回true。 ```

3.2 Set接口

  • java.util.Set接口 extends Collection接口
    • Set接口的特点:
      1. 不允许存储重复的元素
      2. 没有索引,没有带索引的方法,也不能使用普通的for循环遍历
  • java.util.HashSet集合 implements Set接口
    • HashSet特点:
      1. 不允许存储重复的元素
      2. 没有索引,没有带索引的方法,也不能使用普通的for循环遍历
      3. 是一个无序的集合,存储元素和取出元素的顺序有可能不一致
      4. 底层是一个哈希表结构(查询的速度非常的快)
3.2.1 LinkedHashSet集合
  • java.util.LinkedHashSet集合 extends HashSet集合
    • LinkedHashSet集合特点:
      底层是一个哈希表(数组+链表/红黑树)+链表:多了一条链表(记录元素的存储顺序),保证元素有序
  • 哈希值
    • 是一个十进制的整数,由系统随机给出(就是对象的地址值,是一个逻辑地址,是模拟出来得到地址,不是数据实际存储的物理地址)
    • 在Object类有一个方法,可以获取对象的哈希值
      • int hashCode() 返回该对象的哈希码值。
      • hashCode方法的源码:
        • public native int hashCode();
        • native:代表该方法调用的是本地操作系统的方法
  • 可变参数:
    • 是JDK1.5之后出现的新特性
    • 使用前提:
      • 当方法的参数列表数据类型已经确定,但是参数的个数不确定,就可以使用可变参数.
    • 使用格式:定义方法时使用
      • 修饰符 返回值类型 方法名(数据类型…变量名){}
    • 可变参数的原理:
      • 可变参数底层就是一个数组,根据传递参数个数不同,会创建不同长度的数组,来存储这些参数
      • 传递的参数个数,可以是0个(不传递),1,2…多个

3.3 Collections工具类

  • java.utils.Collections是集合工具类,用来对集合进行操作。部分方法如下:
    • public static boolean addAll(Collection c, T... elements):往集合中添加一些元素。
    • public static void shuffle(List<?> list) 打乱顺序:打乱集合顺序。
3.3.1 Collections排序
  • java.utils.Collections是集合工具类,用来对集合进行操作。部分方法如下:
    • public static void sort(List list):将集合中元素按照默认规则排序。
    • 注意:
      • sort(List list)使用前提
        被排序的集合里边存储的元素,必须实现Comparable,重写接口中的方法compareTo定义排序的规则
    • Comparable接口的排序规则:
      自己(this)-参数:升序
3.3.2 Collections排序比较
  • java.utils.Collections是集合工具类,用来对集合进行操作。部分方法如下:
    • public static void sort(List list,Comparator<? super T> ):将集合中元素按照指定规则排序。
    • Comparator和Comparable的区别
      • Comparable:自己(this)和别人(参数)比较,自己需要实现Comparable接口,重写比较的规则compareTo方法
      • Comparator:相当于找一个第三方的裁判,比较两个
    • Comparator的排序规则:
      • o1-o2:升序

4. Map

4.1 Map

  • java.util.Map<k,v>集合
    • Map集合的特点:
      1. Map集合是一个双列集合,一个元素包含两个值(一个key,一个value)
      2. Map集合中的元素,key和value的数据类型可以相同,也可以不同
      3. Map集合中的元素,key是不允许重复的,value是可以重复的
      4. Map集合中的元素,key和value是一一对应
  • java.util.HashMap<k,v>集合 implements Map<k,v>接口
    • HashMap集合的特点:
      1. HashMap集合底层是哈希表:查询的速度特别的快
        • JDK1.8之前:数组+单向链表
        • JDK1.8之后:数组+单向链表 红黑树(链表的长度超过8):提高查询的速度
      2. hashMap集合是一个无序的集合,存储元素和取出元素的顺序有可能不一致
  • java.util.LinkedHashMap<k,v>集合 extends HashMap<k,v>集合
    • LinkedHashMap的特点:
      1. LinkedHashMap集合底层是哈希表+链表(保证迭代的顺序)
      2. LinkedHashMap集合是一个有序的集合,存储元素和取出元素的顺序是一致的
4.1.1 keySet()
  • Map集合的第一种遍历方式:通过键找值的方式
    • Map集合中的方法:
      Set keySet() 返回此映射中包含的键的 Set 视图。
    • 实现步骤:
      1. 使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个Set集合中
      2. 遍历set集合,获取Map集合中的每一个key
      3. 通过Map集合中的方法get(key),通过key找到value
4.1.2 entrySet()
  • Map集合遍历的第二种方式:使用Entry对象遍历
    • Map集合中的方法:
      Set<Map.Entry<K,V» entrySet() 返回此映射中包含的映射关系的 Set 视图。
    • 实现步骤:
      1. 使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中
      2. 遍历Set集合,获取每一个Entry对象
      3. 使用Entry对象中的方法getKey()和getValue()获取键与值
4.1.3 自定义类型键值
  • HashMap存储自定义类型键值
    • Map集合保证key是唯一的:
      • 作为key的元素,必须重写hashCode方法和equals方法,以保证key唯一

4.2 LinkedHashMap

  • java.util.LinkedHashMap<K,V> entends HashMap<K,V>
    • Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。
    • 底层原理: 哈希表+链表(记录元素的顺序)

4.3 Hashtable

  • java.util.Hashtable<K,V>集合 implements Map<K,V>接口
    • Hashtable:底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢
    • HashMap:底层是一个哈希表,是一个线程不安全的集合,是多线程的集合,速度快
    • HashMap集合(之前学的所有的集合):可以存储null值,null键
    • Hashtable集合,不能存储null值,null键
    • Hashtable和Vector集合一样,在jdk1.2版本之后被更先进的集合(HashMap,ArrayList)取代了
    • Hashtable的子类Properties依然活跃在历史舞台
    • Properties集合是一个唯一和IO流相结合的集合

JDK9的新特性—集合接口添加多元素

  • List接口,Set接口,Map接口:里边增加了一个静态的方法of,可以给集合一次性添加多个元素
    • static List of​(E... elements)
    • 使用前提:
      • 当集合中存储的元素的个数已经确定了,不在改变时使用
  • 注意:
    1. of方法只适用于List接口,Set接口,Map接口,不适用于接接口的实现类
    2. of方法的返回值是一个不能改变的集合,集合不能再使用add,put方法添加元素,会抛出异常
    3. Set接口和Map接口在调用of方法的时候,不能有重复的元素,否则会抛出异常

Debug调试程序

  • 可以让代码逐行执行,查看代码执行的过程,调试程序中出现的bug
  • 使用方式:
    • 在行号的右边,鼠标左键单击,添加断点(每个方法的第一行,哪里有bug添加到哪里)
    • 右键,选择Debug执行程序
    • 程序就会停留在添加的第一个断点处
  • 执行程序:
    • f8:逐行执行程序
    • f7:进入到方法中
    • shift+f8:跳出方法
    • f9:跳到下一个断点,如果没有下一个断点,那么就结束程序
    • ctrl+f2:退出debug模式,停止程序
    • Console:切换到控制台

5. 异常

5.1 Throwable类

  • java.lang.Throwable:类是 Java 语言中所有错误或异常的超类。
    • Exception:编译期异常,进行编译(写代码)java程序出现的问题
      • RuntimeException:运行期异常,java程序运行过程中出现的问题
      • 异常就相当于程序得了一个小毛病(感冒,发烧),把异常处理掉,程序可以继续执行(吃点药,继续革命工作)
    • Error:错误
      • 错误就相当于程序得了一个无法治愈的毛病(非典,艾滋).必须修改源代码,程序才能继续执行

5.2 throws关键字

  • throws关键字:异常处理的第一种方式,交给别人处理
  • 作用:
    • 当方法内部抛出异常对象的时候,那么我们就必须处理这个异常对象
    • 可以使用throws关键字处理异常对象,会把异常对象声明抛出给方法的调用者处理(自己不处理,给别人处理),最终交给JVM处理–>中断处理
  • 使用格式:在方法声明时使用
      修饰符 返回值类型 方法名(参数列表) throws AAAExcepiton,BBBExcepiton...{
          throw new AAAExcepiton("产生原因");
          throw new BBBExcepiton("产生原因");
          ...
      }
    
  • 注意:
    1. throws关键字必须写在方法声明处
    2. throws关键字后边声明的异常必须是Exception或者是Exception的子类
    3. 方法内部如果抛出了多个异常对象,那么throws后边必须也声明多个异常 如果抛出的多个异常对象有子父类关系,那么直接声明父类异常即可
    4. 调用了一个声明抛出异常的方法,我们就必须的处理声明的异常
      要么继续使用throws声明抛出,交给方法的调用者处理,最终交给JVM
      要么try…catch自己处理异常

5.3 try…catch

  • try…catch:异常处理的第二种方式,自己处理异常
  • 格式:
      try{
          可能产生异常的代码
      }catch(定义一个异常的变量,用来接收try中抛出的异常对象){
          异常的处理逻辑,异常异常对象之后,怎么处理异常对象
          一般在工作中,会把异常的信息记录到一个日志中
      }
      ...
      catch(异常类名 变量名){
    
      }
    
  • 注意:
    1. try中可能会抛出多个异常对象,那么就可以使用多个catch来处理这些异常对象
    2. 如果try中产生了异常,那么就会执行catch中的异常处理逻辑,执行完毕catch中的处理逻辑,继续执行try…catch之后的代码
    3. 如果try中没有产生异常,那么就不会执行catch中异常的处理逻辑,执行完try中的代码,继续执行try…catch之后的代码

5.4 finally代码块

  • 格式:
      try{
          可能产生异常的代码
      }catch(定义一个异常的变量,用来接收try中抛出的异常对象){
          异常的处理逻辑,异常异常对象之后,怎么处理异常对象
          一般在工作中,会把异常的信息记录到一个日志中
      }
      ...
      catch(异常类名 变量名){
        
      }finally{
          无论是否出现异常都会执行
      }
    
  • 注意:
    1. finally不能单独使用,必须和try一起使用
    2. finally一般用于资源释放(资源回收),无论程序是否出现异常,最后都要资源释放(IO)

5.5 子父类的异常

  • 如果父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常。
  • 父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出
  • 注意 : 父类异常时什么样,子类异常就什么样

5.6 自定义异常类

  • java提供的异常类,不够我们使用,需要自己定义一些异常类
  • 格式:
      public class XXXExcepiton extends Exception | RuntimeException{
          添加一个空参数的构造方法
          添加一个带异常信息的构造方法
      }
    
  • 注意:
    1. 自定义异常类一般都是以Exception结尾,说明该类是一个异常类
    2. 自定义异常类,必须的继承Exception或者RuntimeException 继承Exception:那么自定义的异常类就是一个编译期异常,如果方法内部抛出了编译期异常,就必须处理这个异常,要么throws,要么try…catch
      • 继承RuntimeException:那么自定义的异常类就是一个运行期异常,无需处理,交给虚拟机处理(中断处理)

6. 线程、同步

  • 主线程 : 执行主(main)方法的线程
    • 单线程程序:java程序中只有一个线程
    • 执行从main方法开始,从上到下依次执行
  • JVM执行main方法,main方法会进入到栈内存
  • JVM会找操作系统开辟一条main方法通向cpu的执行路径
  • cpu就可以通过这个路径来执行main方法
  • 而这个路径有一个名字,叫main(主)线程

6.1 多线程创建方式一

  • 创建多线程程序的第一种方式:创建Thread类的子类
    • java.lang.Thread类:是描述线程的类,我们想要实现多线程程序,就必须继承Thread类
  • 实现步骤:
    1. 创建一个Thread类的子类
    2. 在Thread类的子类中重写Thread类中的run方法,设置线程任务(开启线程要做什么?)
    3. 创建Thread类的子类对象
    4. 调用Thread类中的方法start方法,开启新的线程,执行run方法
      void start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
      结果是两个线程并发地运行;当前线程(main线程)和另一个线程(创建的新线程,执行其 run 方法)。
      多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。
  • java程序属于抢占式调度,那个线程的优先级高,那个线程优先执行;同一个优先级,随机选择一个执行
6.1.1 线程的名称
  • 主线程: main
  • 新线程: Thread-0,Thread-1,Thread-2
6.1.2 获取线程的名称
  1. 使用Thread类中的方法getName()
    • String getName() 返回该线程的名称。
  2. 可以先获取到当前正在执行的线程,使用线程中的方法getName()获取线程的名称
    • static Thread currentThread() 返回对当前正在执行的线程对象的引用。
6.1.3 设置线程的名称
  1. 使用Thread类中的方法setName(名字)
    • void setName(String name) 改变线程名称,使之与参数 name 相同。
  2. 创建一个带参数的构造方法,参数传递线程的名称;调用父类的带参构造方法,把线程名称传递给父类,让父类(Thread)给子线程起一个名字
    • Thread(String name) 分配新的 Thread 对象。
6.1.4 线程睡眠
  • public static void sleep(long millis) :
    使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
    毫秒数结束之后,线程继续执行

6.2 多线程创建方式二

  • 创建多线程程序的第二种方式:实现Runnable接口
    • java.lang.Runnable
      • Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为 run 的无参数方法。
    • java.lang.Thread类的构造方法
      • Thread(Runnable target) 分配新的 Thread 对象。
      • Thread(Runnable target, String name) 分配新的 Thread 对象。
  • 实现步骤:
    1. 创建一个Runnable接口的实现类
    2. 在实现类中重写Runnable接口的run方法,设置线程任务
    3. 创建一个Runnable接口的实现类对象
    4. 创建Thread类对象,构造方法中传递Runnable接口的实现类对象
    5. 调用Thread类中的start方法,开启新的线程执行run方法
  • 实现Runnable接口创建多线程程序的好处:
    1. 避免了单继承的局限性
      • 一个类只能继承一个类(一个人只能有一个亲爹),类继承了Thread类就不能继承其他的类
      • 实现了Runnable接口,还可以继承其他的类,实现其他的接口
    2. 增强了程序的扩展性,降低了程序的耦合性(解耦)
      • 实现Runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦)
      • 实现类中,重写了run方法:用来设置线程任务
      • 创建Thread类对象,调用start方法:用来开启新线程

6.3 匿名内部类方式创建线程

  • 匿名:没有名字
  • 内部类:写在其他类内部的类
  • 匿名内部类作用:简化代码
    • 把子类继承父类,重写父类的方法,创建子类对象合一步完成
    • 把实现类实现类接口,重写接口中的方法,创建实现类对象合成一步完成
  • 匿名内部类的最终产物:子类/实现类对象,而这个类没有名字
    • 格式:
        new 父类/接口(){
            重复父类/接口中的方法
        };
      

6.4 线程安全问题-同步代码块

  • 解决线程安全问题的一种方案:使用同步代码块
  • 格式:
      synchronized(锁对象){
          可能会出现线程安全问题的代码(访问了共享数据的代码)
      }
    
  • 注意:
    1. 通过代码块中的锁对象,可以使用任意的对象
    2. 但是必须保证多个线程使用的锁对象是同一个
    3. 锁对象作用: 把同步代码块锁住,只让一个线程在同步代码块中执行

6.5 线程安全问题-同步方法

  • 解决线程安全问题的二种方案:使用同步方法
  • 使用步骤:
    1. 把访问了共享数据的代码抽取出来,放到一个方法中
    2. 在方法上添加synchronized修饰符
  • 格式:定义方法的格式
      修饰符 synchronized 返回值类型 方法名(参数列表){
          可能会出现线程安全问题的代码(访问了共享数据的代码)
      }
    

6.6 线程安全问题-Lock锁

  • 解决线程安全问题的三种方案:使用Lock锁
  • java.util.concurrent.locks.Lock接口
    • Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
    • Lock接口中的方法:
      • void lock()获取锁。
      • void unlock() 释放锁。
  • java.util.concurrent.locks.ReentrantLock implements Lock接口
  • 使用步骤:
    1. 在成员位置创建一个ReentrantLock对象
    2. 在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁
    3. 在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁

6.7 线程间通信

  • 等待唤醒案例:线程之间的通信
    • 创建一个顾客线程(消费者):告知老板要的包子的种类和数量,调用wait方法,放弃cpu的执行,进入到WAITING状态(无限等待)
    • 创建一个老板线程(生产者):花了5秒做包子,做好包子之后,调用notify方法,唤醒顾客吃包子
  • 注意:
    • 顾客和老板线程必须使用同步代码块包裹起来,保证等待和唤醒只能有一个在执行
    • 同步使用的锁对象必须保证唯一
    • 只有锁对象才能调用wait和notify方法
  • Obejct类中的方法
    • void wait()
      在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
    • void notify()
      唤醒在此对象监视器上等待的单个线程。
      会继续执行wait方法之后的代码
6.7.1 计时等待的两种方式
  • 进入到TimeWaiting(计时等待)有两种方式
    1. 使用sleep(long m)方法,在毫秒值结束之后,线程睡醒进入到Runnable/Blocked状态
    2. 使用wait(long m)方法,wait方法如果在毫秒值结束之后,还没有被notify唤醒,就会自动醒来,线程睡醒进入到Runnable/Blocked状态
  • 唤醒的方法:
    • void notify() 唤醒在此对象监视器上等待的单个线程。
    • void notifyAll() 唤醒在此对象监视器上等待的所有线程。
6.7.2 资源类
  • 资源类:包子类
    • 设置包子的属性
      • 包子的状态: 有 true,没有 false
6.7.3 生产者类
  • 生产者(包子铺)类 : 是一个线程类,可以继承Thread
    • 设置线程任务(run):生产包子
    • 对包子的状态进行判断 - true:有包子 - 包子铺调用wait方法进入等待状态 - false:没有包子 - 包子铺生产包子 - 增加一些趣味性:交替生产两种包子 - 有两种状态(i%2==0) - 包子铺生产好了包子 - 修改包子的状态为true有 - 唤醒吃货线程,让吃货线程吃包子
  • 注意:
    • 包子铺线程和包子线程关系–>通信(互斥)
    • 必须同时同步技术保证两个线程只能有一个在执行
    • 锁对象必须保证唯一,可以使用包子对象作为锁对象
    • 包子铺类和吃货的类就需要把包子对象作为参数传递进来
      1. 需要在成员位置创建一个包子变量
      2. 使用带参数构造方法,为这个包子变量赋值
6.7.4 消费者类
  • 消费者(吃货)类:是一个线程类,可以继承Thread
    • 设置线程任务(run):吃包子
    • 对包子的状态进行判断
      • false:没有包子 - 吃货调用wait方法进入等待状态
      • true:有包子 - 吃货吃包子 - 吃货吃完包子 - 修改包子的状态为false没有 - 吃货唤醒包子铺线程,生产包子
6.7.5 测试类
  • 测试类:
    • 包含main方法,程序执行的入口,启动程序
    • 创建包子对象;
    • 创建包子铺线程,开启,生产包子;
    • 创建吃货线程,开启,吃包子;

7. 线程池、Lambda表达式

7.1 线程池

  • 线程池:JDK1.5之后提供的
  • java.util.concurrent.Executors:线程池的工厂类,用来生成线程池
  • Executors类中的静态方法:
    • static ExecutorService newFixedThreadPool(int nThreads)
      创建一个可重用固定线程数的线程池
    • 参数:
      • int nThreads:创建线程池中包含的线程数量
    • 返回值:
      • ExecutorService接口,返回的是ExecutorService接口的实现类对象,我们可以使用ExecutorService接口接收(面向接口编程)
  • java.util.concurrent.ExecutorService:线程池接口
    • 用来从线程池中获取线程,调用start方法,执行线程任务
      submit(Runnable task) 提交一个 Runnable 任务用于执行
    • 关闭/销毁线程池的方法
      void shutdown()
  • 线程池的使用步骤:
    1. 使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
    2. 创建一个类,实现Runnable接口,重写run方法,设置线程任务
    3. 调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
    4. 调用ExecutorService中的方法shutdown销毁线程池(不建议执行)

7.2 Lambda表达式

  • Lambda表达式的标准格式:
    • 由三部分组成:
      • 一些参数
      • 一个箭头
      • 一段代码
  • 格式:
      (参数列表) -> {一些重写方法的代码};
    
  • 解释说明格式:
    • ():接口中抽象方法的参数列表,没有参数,就空着;有参数就写出参数,多个参数使用逗号分隔
    • ->:传递的意思,把参数传递给方法体{}
    • {}:重写接口的抽象方法的方法体
7.2.1 Lambda表达式条件
  • Lambda表达式:是可推导,可以省略
  • 凡是根据上下文推导出来的内容,都可以省略书写
  • 可以省略的内容:
    1. (参数列表):括号中参数列表的数据类型,可以省略不写
    2. (参数列表):括号中的参数如果只有一个,那么类型和()都可以省略
    3. {一些代码}:如果{}中的代码只有一行,无论是否有返回值,都可以省略({},return,分号)
      • 注意 : 要省略{},return,分号必须一起省略

8. File类、递归

8.1 File类

  • java.io.File类
    • 文件和目录路径名的抽象表示形式。
    • java把电脑中的文件和文件夹(目录)封装为了一个File类,我们可以使用File类对文件和文件夹进行操作
    • 我们可以使用File类的方法
      • 创建一个文件/文件夹
      • 删除文件/文件夹
      • 获取文件/文件夹
      • 判断文件/文件夹是否存在
      • 对文件夹进行遍历
      • 获取文件的大小
    • File类是一个与系统无关的类,任何的操作系统都可以使用这个类中的方法
  • 重点:记住这三个单词
    • file : 文件
    • directory : 文件夹/目录
    • path : 路径
8.1.1 父路径和子路径
  • File(String parent, String child)
    根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。
    • 参数:把路径分成了两部分
      • String parent:父路径
      • String child:子路径
    • 好处:
      • 父路径和子路径,可以单独书写,使用起来非常灵活;
      • 父路径和子路径都可以变化
8.1.2 File类获取功能的方法
public String getAbsolutePath() 返回此File的绝对路径名字符串

public String getPath() 将此File转换为路径名字符串

public String getName()  返回由此File表示的文件或目录的名称

public long length()  返回由此File表示的文件的长度
8.1.3 File类判断功能的方法
public boolean exists() 此File表示的文件或目录是否实际存在

public boolean isDirectory() 此File表示的是否为目录

public boolean isFile() 此File表示的是否为文件
8.1.4 File类创建删除功能的方法
public boolean createNewFile() 当且仅当具有该名称的文件尚不存在时创建一个新的空文件

public boolean delete() 删除由此File表示的文件或目录

public boolean mkdir() 创建由此File表示的目录

public boolean mkdirs() 创建由此File表示的目录包括任何必需但不存在的父目录
8.1.5 File类遍历(文件夹)目录功能
public String[] list() 返回一个String数组表示该File目录中的所有子文件或目录

public File[] listFiles() 返回一个File数组表示该File目录中的所有的子文件或目录
  • 注意:
    • list方法和listFiles方法遍历的是构造方法中给出的目录
    • 如果构造方法中给出的目录的路径不存在,会抛出空指针异常
    • 如果构造方法中给出的路径不是一个目录,也会抛出空指针异常

8.2 递归

  • 递归:方法自己调用自己
    • 递归的分类:
      • 递归分为两种,直接递归和间接递归。
      • 直接递归称为方法自身调用自己。
      • 间接递归可以A方法调用B方法,B方法调用C方法,C方法调用A方法。
  • 注意事项:
    • 递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出。
    • 在递归中虽然有限定条件,但是递归次数不能太多。否则也会发生栈内存溢出。
    • 构造方法,禁止递归
  • 递归的使用前提:
    • 当调用方法的时候,方法的主体不变,每次调用方法的参数不同,可以使用递归
8.2.1 递归应用实例
  • 需求:
    • 遍历c:\abc文件夹,及abc文件夹的子文件夹
    • 只要.java结尾的文件
    • c:\abc
      c:\abc\abc.txt
      c:\abc\abc.java
      c:\abc\a
      c:\abc\a\a.jpg
      c:\abc\a\a.java
      c:\abc\b
      c:\abc\b\b.java
      c:\abc\b\b.txt
  • 我们可以使用过滤器来实现
  • 在File类中有两个和ListFiles重载的方法,方法的参数传递的就是过滤器
  • File[] listFiles(FileFilter filter)
  • java.io.FileFilter接口:用于抽象路径名(File对象)的过滤器。
    • 作用:用来过滤文件(File对象)
    • 抽象方法:用来过滤文件的方法
      • boolean accept(File pathname)
        测试指定抽象路径名是否应该包含在某个路径名列表中。
      • 参数:
        • File pathname:使用ListFiles方法遍历目录,得到的每一个文件对象
  • File[] listFiles(FilenameFilter filter)
  • java.io.FilenameFilter接口:实现此接口的类实例可用于过滤器文件名。
    • 作用:用于过滤文件名称
    • 抽象方法:用来过滤文件的方法
      • boolean accept(File dir, String name)
        测试指定文件是否应该包含在某一文件列表中。
      • 参数:
        • File dir:构造方法中传递的被遍历的目录
        • String name:使用ListFiles方法遍历目录,获取的每一个文件/文件夹的名称
  • 注意:
    • 两个过滤器接口是没有实现类的,需要我们自己写实现类,重写过滤的方法accept,在方法中自己定义过滤的规则

9. 字节流、字符流

9.1 字节输出流

  • java.io.OutputStream:字节输出流
    • 此抽象类是表示输出字节流的所有类的超类。
  • 定义了一些子类共性的成员方法:
      public void close() 关闭此输出流并释放与此流相关联的任何系统资源
        
      public void flush() 刷新此输出流并强制任何缓冲的输出字节被写出
        
      public void write(byte[] b) b.length字节从指定的字节数组写入此输出流
        
      public void write(byte[] b, int off, int len) 从指定的字节数组写入 len字节从偏移量 off开始输出到此输出流
        
      public abstract void write(int b) 将指定的字节输出流
    
  • java.io.FileOutputStream extends OutputStream
    • FileOutputStream:文件字节输出流
    • 作用:把内存中的数据写入到硬盘的文件中
  • 构造方法:
    • FileOutputStream(String name)创建一个向具有指定名称的文件中写入数据的输出文件流。
    • FileOutputStream(File file) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
    • 参数:写入数据的目的
      • String name:目的地是一个文件的路径
      • File file:目的地是一个文件
    • 构造方法的作用:
      1. 创建一个FileOutputStream对象
      2. 会根据构造方法中传递的文件/文件路径,创建一个空的文件
      3. 会把FileOutputStream对象指向创建好的文件
  • 写入数据的原理(内存–>硬盘)
    • java程序–>JVM(java虚拟机)–>OS(操作系统)–>OS调用写数据的方法–>把数据写入到文件中
  • 字节输出流的使用步骤(重点):
    1. 创建一个FileOutputStream对象,构造方法中传递写入数据的目的地
    2. 调用FileOutputStream对象中的方法write,把数据写入到文件中
    3. 释放资源(流使用会占用一定的内存,使用完毕要把内存清空,提供程序的效率)
9.1.1 一次写多个字节的方法
  • 一次写多个字节的方法:
      public void write(byte[] b)  b.length字节从指定的字节数组写入此输出流
        
      public void write(byte[] b, int off, int len) 从指定的字节数组写入 len字节从偏移量 off开始输出到此输出流
    
    9.1.2 续写
  • 追加写/续写:使用两个参数的构造方法
      FileOutputStream(String name, boolean append) : 创建一个向具有指定 name 的文件中写入数据的输出文件流
        
      FileOutputStream(File file, boolean append) : 创建一个向指定 File 对象表示的文件中写入数据的文件输出流
    
    • 参数:
      • String name,File file:写入数据的目的地
      • boolean append:追加写开关
        • true:创建对象不会覆盖源文件,继续在文件的末尾追加写数据
        • false:创建一个新文件,覆盖源文件
    • 写换行:写换行符号
      • windows:\r\n
      • linux:/n
      • mac:/r

9.2 字节输入流

  • java.io.InputStream:字节输入流
    • 此抽象类是表示字节输入流的所有类的超类。
  • 定义了所有子类共性的方法:
      int read() : 从输入流中读取数据的下一个字节
        
      int read(byte[] b) : 从输入流中读取一定数量的字节并将其存储在缓冲区数组 b 
        
      void close() : 关闭此输入流并释放与该流关联的所有系统资源
    
  • java.io.FileInputStream extends InputStream
    • FileInputStream:文件字节输入流
    • 作用:把硬盘文件中的数据,读取到内存中使用
  • 构造方法:
    • FileInputStream(String name)
    • FileInputStream(File file)
    • 参数:读取文件的数据源
      • String name:文件的路径
      • File file:文件
    • 构造方法的作用:
      1. 会创建一个FileInputStream对象
      2. 会把FileInputStream对象指定构造方法中要读取的文件
  • 读取数据的原理(硬盘–>内存)
    • java程序–>JVM–>OS–>OS读取数据的方法–>读取文件
  • 字节输入流的使用步骤(重点):
    1. 创建FileInputStream对象,构造方法中绑定要读取的数据源
    2. 使用FileInputStream对象中的方法read,读取文件
    3. 释放资源
9.2.1 一次读取多个字节的方法
  • 字节输入流一次读取多个字节的方法:
    • int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
    • 明确两件事情:
      1. 方法的参数byte[]的作用?
        • 起到缓冲作用,存储每次读取到的多个字节
        • 数组的长度一把定义为1024(1kb)或者1024的整数倍
      2. 方法的返回值int是什么?
        • 每次读取的有效字节个数
  • String类的构造方法
      String(byte[] bytes) : 把字节数组转换为字符串
        
      String(byte[] bytes, int offset, int length) : 把字节数组的一部分转换为字符串 offset:数组的开始索引 length:转换的字节个数
    

    9.3 文件复制

  • 文件复制练习:一读一写
  • 明确:
    • 数据源: c:\\1.jpg
    • 数据的目的地: d:\\1.jpg
  • 文件复制的步骤:
    1. 创建一个字节输入流对象,构造方法中绑定要读取的数据源
    2. 创建一个字节输出流对象,构造方法中绑定要写入的目的地
    3. 使用字节输入流对象中的方法read读取文件
    4. 使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
    5. 释放资源

9.4 字符输入流

  • java.io.Reader:字符输入流,是字符输入流的最顶层的父类,定义了一些共性的成员方法,是一个抽象类
  • 共性的成员方法:
      int read() 读取单个字符并返回
        
      int read(char[] cbuf)一次读取多个字符,将字符读入数组
        
      void close() 关闭该流并释放与之关联的所有资源
    
  • java.io.FileReader extends InputStreamReader extends Reader
    • FileReader:文件字符输入流
    • 作用:把硬盘文件中的数据以字符的方式读取到内存中
  • 构造方法:
    • FileReader(String fileName)
    • FileReader(File file)
    • 参数:读取文件的数据源
      • String fileName:文件的路径
      • File file:一个文件
    • FileReader构造方法的作用:
      1. 创建一个FileReader对象
      2. 会把FileReader对象指向要读取的文件
  • 字符输入流的使用步骤:
    1. 创建FileReader对象,构造方法中绑定要读取的数据源
    2. 使用FileReader对象中的方法read读取文件
    3. 释放资源

9.5 字符输出流

  • java.io.Writer:字符输出流,是所有字符输出流的最顶层的父类,是一个抽象类
  • 共性的成员方法:
      void write(int c) 写入单个字符
      void write(char[] cbuf)写入字符数组
      abstract void write(char[] cbuf, int off, int len)写入字符数组的某一部分,off数组的开始索引,len写的字符个数
      void write(String str)写入字符串
      void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数
      void flush()刷新该流的缓冲
      void close() 关闭此流但要先刷新它
    
  • java.io.FileWriter extends OutputStreamWriter extends Writer
    • FileWriter:文件字符输出流
    • 作用:把内存中字符数据写入到文件中
  • 构造方法:
    • FileWriter(File file)根据给定的 File 对象构造一个 FileWriter 对象。
    • FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象。
    • 参数:写入数据的目的地
      • String fileName:文件的路径
      • File file:是一个文件
    • 构造方法的作用:
      1. 会创建一个FileWriter对象
      2. 会根据构造方法中传递的文件/文件的路径,创建文件
      3. 会把FileWriter对象指向创建好的文件
  • 字符输出流的使用步骤(重点):
    1. 创建FileWriter对象,构造方法中绑定要写入数据的目的地
    2. 使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
    3. 使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中
    4. 释放资源(会先把内存缓冲区中的数据刷新到文件中)
9.5.1 flush方法和close方法
  • flush :刷新缓冲区,流对象可以继续使用。
  • close: 先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。
9.5.2 字符输出流写数据的其他方法
void write(char[] cbuf) : 写入字符数组
abstract  void write(char[] cbuf, int off, int len) : 写入字符数组的某一部分,off数组的开始索引,len写的字符个数
void write(String str) : 写入字符串
void write(String str, int off, int len)  : 写入字符串的某一部分,off字符串的开始索引,len写的字符个数
9.5.3 续写和换行
  • 续写,追加写:使用两个参数的构造方法
    • FileWriter(String fileName, boolean append)
    • FileWriter(File file, boolean append)
    • 参数:
      • String fileName,File file:写入数据的目的地
      • boolean append:续写开关
        • true:不会创建新的文件覆盖源文件,可以续写;
        • false:创建新的文件覆盖源文件
  • 换行:换行符号
    • windows:\r\n
    • linux:/n
    • mac:/r

9.6 流中异常处理

  • 在jdk1.7之前使用try catch finally 处理流中的异常
  • 格式:
      try{
          可能会产出异常的代码
      }catch(异常类变量 变量名){
          异常的处理逻辑
      }finally{
          一定会指定的代码
          资源释放
      }
    
9.6.1 JDK7的新特性
  • 在try的后边可以增加一个(),在括号中可以定义流对象
  • 那么这个流对象的作用域就在try中有效
  • try中的代码执行完毕,会自动把流对象释放,不用写finally
  • 格式:
      try(定义流对象;定义流对象....){
          可能会产出异常的代码
      }catch(异常类变量 变量名){
          异常的处理逻辑
      }
    
9.6.2 JDK9新特性
  • try的前边可以定义流对象
  • 在try后边的()中可以直接引入流对象的名称(变量名)
  • 在try代码执行完毕之后,流对象也可以释放掉,不用写finally
  • 格式:
      A a = new A();
      B b = new B();
      try(a,b){
          可能会产出异常的代码
      }catch(异常类变量 变量名){
          异常的处理逻辑
      }
    
9.7 Properties集合
  • java.util.Properties集合 extends Hashtable<k,v> implements Map<k,v>
    • Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。
    • Properties 集合是一个唯一和IO流相结合的集合
      • 可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
      • 可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用
    • 属性列表中每个键及其对应值都是一个字符串。
      • Properties集合是一个双列集合,key和value默认都是字符串
9.7.1 load方法
  • 可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用
      void load(InputStream inStream)
        
      void load(Reader reader)
    
    • 参数:
      • InputStream inStream:字节输入流,不能读取含有中文的键值对
      • Reader reader:字符输入流,能读取含有中文的键值对
  • 使用步骤:
    1. 创建Properties集合对象
    2. 使用Properties集合对象中的方法load读取保存键值对的文件
    3. 遍历Properties集合
  • 注意:
    1. 存储键值对的文件中,键与值默认的连接符号可以使用=,空格(其他符号)
    2. 存储键值对的文件中,可以使用#进行注释,被注释的键值对不会再被读取
    3. 存储键值对的文件中,键与值默认都是字符串,不用再加引号
9.7.2 store方法
  • 可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
      void store(OutputStream out, String comments)
        
      void store(Writer writer, String comments)
    
    • 参数:
      • OutputStream out : 字节输出流,不能写入中文
      • Writer writer : 字符输出流,可以写中文
      • String comments : 注释,用来解释说明保存的文件是做什么用的
        • 不能使用中文,会产生乱码,默认是Unicode编码
        • 一般使用”“空字符串
  • 使用步骤:
    1. 创建Properties集合对象,添加数据
    2. 创建字节输出流/字符输出流对象,构造方法中绑定要输出的目的地
    3. 使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
    4. 释放资源
9.7.3 遍历properties集合
  • 使用Properties集合存储数据,遍历取出Properties集合中的数据
    • Properties集合是一个双列集合,key和value默认都是字符串
    • Properties集合有一些操作字符串的特有方法
        Object setProperty(String key, String value) : 调用 Hashtable 的方法 put
              
        String getProperty(String key)  : 通过key找到value值,此方法相当于Map集合中的get(key)方法
              
        Set<String> stringPropertyNames() :  返回此属性列表中的键集其中该键及其对应值是字符串,此方法相当于Map集合中的keySet方法
      

10. 缓冲流、转换流、序列化流、打印流

10.1 字节缓冲输出流

  • java.io.BufferedOutputStream extends OutputStream
    • BufferedOutputStream:字节缓冲输出流
    • 继承自父类的共性成员方法:
        public void close() :关闭此输出流并释放与此流相关联的任何系统资源。
              
        public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。
              
        public void write(byte[] b) :将 b.length字节从指定的字节数组写入此输出流。
              
        public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
              
        public abstract void write(int b) :将指定的字节输出流。
      
    • 构造方法:
        BufferedOutputStream(OutputStream out)  创建一个新的缓冲输出流以将数据写入指定的底层输出流
              
        BufferedOutputStream(OutputStream out, int size)  创建一个新的缓冲输出流以将具有指定缓冲区大小的数据写入指定的底层输出流
      
      • 参数:
        • OutputStream out : 字节输出流
          • 我们可以传递FileOutputStream,缓冲流会给FileOutputStream增加一个缓冲区,提高FileOutputStream的写入效率
        • int size:指定缓冲流内部缓冲区的大小,不指定默认
  • 使用步骤(重点)
    1. 创建FileOutputStream对象,构造方法中绑定要输出的目的地
    2. 创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象对象,提高FileOutputStream对象效率
    3. 使用BufferedOutputStream对象中的方法write,把数据写入到内部缓冲区中
    4. 使用BufferedOutputStream对象中的方法flush,把内部缓冲区中的数据,刷新到文件中
    5. 释放资源(会先调用flush方法刷新数据,第4部可以省略)

10.2 字节缓冲输入流

  • java.io.BufferedInputStream extends InputStream
    • BufferedInputStream:字节缓冲输入流
    • 继承自父类的成员方法:
        int read()从输入流中读取数据的下一个字节
              
        int read(byte[] b) 从输入流中读取一定数量的字节并将其存储在缓冲区数组 b 
              
        void close() 关闭此输入流并释放与该流关联的所有系统资源
      
    • 构造方法:
        BufferedInputStream(InputStream in) 创建一个 BufferedInputStream 并保存其参数即输入流 in以便将来使用
              
        BufferedInputStream(InputStream in, int size) 创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数即输入流 in以便将来使用
      
      • 参数:
        • InputStream in:字节输入流
          • 我们可以传递FileInputStream,缓冲流会给FileInputStream增加一个缓冲区,提高FileInputStream的读取效率
        • int size:指定缓冲流内部缓冲区的大小,不指定默认
  • 使用步骤(重点):
    1. 创建FileInputStream对象,构造方法中绑定要读取的数据源
    2. 创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率
    3. 使用BufferedInputStream对象中的方法read,读取文件
    4. 释放资源

10.3 字符缓冲输出流

  • java.io.BufferedWriter extends Writer
    • BufferedWriter:字符缓冲输出流
    • 继承自父类的共性成员方法:
        void write(int c) : 写入单个字符
              
        void write(char[] cbuf) : 写入字符数组
              
        abstract  void write(char[] cbuf, int off, int len) : 写入字符数组的某一部分,off数组的开始索引,len写的字符个数
              
        void write(String str) : 写入字符串
              
        void write(String str, int off, int len)  : 写入字符串的某一部分,off字符串的开始索引,len写的字符个数
              
        void flush() : 刷新该流的缓冲
              
        void close() : 关闭此流但要先刷新它
      
    • 构造方法:
        BufferedWriter(Writer out) : 创建一个使用默认大小输出缓冲区的缓冲字符输出流
              
        BufferedWriter(Writer out, int sz) : 创建一个使用给定大小输出缓冲区的新缓冲字符输出流
      
      • 参数:
        • Writer out:字符输出流 我们可以传递FileWriter,缓冲流会给FileWriter增加一个缓冲区,提高FileWriter的写入效率
        • int sz:指定缓冲区的大小,不写默认大小
    • 特有的成员方法:
        void newLine() 写入一个行分隔符会根据不同的操作系统,获取不同的行分隔符
      
      • 换行:换行符号
      • windows:\r\n
      • linux:/n
      • mac:/r
  • 使用步骤:
    1. 创建字符缓冲输出流对象,构造方法中传递字符输出流
    2. 调用字符缓冲输出流中的方法write,把数据写入到内存缓冲区中
    3. 调用字符缓冲输出流中的方法flush,把内存缓冲区中的数据,刷新到文件中
    4. 释放资源

10.4 字符缓冲输入流

  • java.io.BufferedReader extends Reader
    • BufferedReader : 字符缓冲输入流
    • 继承自父类的共性成员方法:
        int read() 读取单个字符并返回
              
        int read(char[] cbuf)一次读取多个字符,将字符读入数组
              
        void close() 关闭该流并释放与之关联的所有资源
      
    • 构造方法:
        BufferedReader(Reader in)  创建一个使用默认大小输入缓冲区的缓冲字符输入流
              
        BufferedReader(Reader in, int sz)     创建一个使用指定大小输入缓冲区的缓冲字符输入流
      
      • 参数:
        • Reader in:字符输入流
          • 我们可以传递FileReader,缓冲流会给FileReader增加一个缓冲区,提高FileReader的读取效率
    • 特有的成员方法:
        String readLine() 读取一个文本行读取一行数据
      
      • 行的终止符号 : 通过下列字符之一即可认为某行已终止:换行 (‘\n’)、回车 (‘\r’) 或回车后直接跟着换行(\r\n)。
      • 返回值 : 包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
  • 使用步骤:
    1. 创建字符缓冲输入流对象,构造方法中传递字符输入流
    2. 使用字符缓冲输入流对象中的方法read/readLine读取文本
    3. 释放资源

10.5 字符流通向字节流的桥梁

  • java.io.OutputStreamWriter extends Writer
    • OutputStreamWriter : 是字符流通向字节流的桥梁 :
      • 可使用指定的 charset将要写入流中的字符编码成字节。(编码:把能看懂的变成看不懂)
    • 继续自父类的共性成员方法:
        void write(int c) 写入单个字符
              
        void write(char[] cbuf)写入字符数组
              
        abstract  void write(char[] cbuf, int off, int len)写入字符数组的某一部分,off数组的开始索引,len写的字符个数
              
        void write(String str)写入字符串
              
        void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数
              
        void flush()刷新该流的缓冲
              
        void close() 关闭此流但要先刷新它
      
    • 构造方法:
        OutputStreamWriter(OutputStream out)创建使用默认字符编码的 OutputStreamWriter
              
        OutputStreamWriter(OutputStream out, String charsetName) 创建使用指定字符集的 OutputStreamWriter
      
      • 参数:
        • OutputStream out:字节输出流,可以用来写转换之后的字节到文件中
        • String charsetName:指定的编码表名称,不区分大小写,可以是utf-8/UTF-8,gbk/GBK,…不指定默认使用UTF-8
  • 使用步骤:
    1. 创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
    2. 使用OutputStreamWriter对象中的方法write,把字符转换为字节存储缓冲区中(编码)
    3. 使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
    4. 释放资源

10.6 字节流通向字符流的桥梁

  • java.io.InputStreamReader extends Reader I- nputStreamReader:是字节流通向字符流的桥梁: - 它使用指定的 charset 读取字节并将其解码为字符。(解码:把看不懂的变成能看懂的)
    • 继承自父类的共性成员方法:
        int read() 读取单个字符并返回
              
        int read(char[] cbuf)一次读取多个字符,将字符读入数组
              
        void close() 关闭该流并释放与之关联的所有资源
      
    • 构造方法:
        InputStreamReader(InputStream in) 创建一个使用默认字符集的 InputStreamReader
              
        InputStreamReader(InputStream in, String charsetName) 创建使用指定字符集的 InputStreamReader
      
      • 参数:
        • InputStream in:字节输入流,用来读取文件中保存的字节
        • String charsetName:指定的编码表名称,不区分大小写,可以是utf-8/UTF-8,gbk/GBK,…不指定默认使用UTF-8
  • 使用步骤:
    1. 创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
    2. 使用InputStreamReader对象中的方法read读取文件
    3. 释放资源
  • 注意事项:
    • 构造方法中指定的编码表名称要和文件的编码相同,否则会发生乱码

10.7 对象的序列化流

  • java.io.ObjectOutputStream extends OutputStream
    • ObjectOutputStream:对象的序列化流
      • 作用:把对象以流的方式写入到文件中保存
    • 构造方法:
        ObjectOutputStream(OutputStream out) 创建写入指定 OutputStream  ObjectOutputStream
      
      • 参数:
        • OutputStream out:字节输出流
    • 特有的成员方法:
        void writeObject(Object obj) 将指定的对象写入 ObjectOutputStream
      
  • 使用步骤:
    1. 创建ObjectOutputStream对象,构造方法中传递字节输出流
    2. 使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中
    3. 释放资源

10.8 对象的反序列化流

  • java.io.ObjectInputStream extends InputStream
    • ObjectInputStream:对象的反序列化流
      • 作用:把文件中保存的对象,以流的方式读取出来使用
    • 构造方法:
        ObjectInputStream(InputStream in) 创建从指定 InputStream 读取的 ObjectInputStream
      
      • 参数:
        • InputStream in:字节输入流
    • 特有的成员方法:
        Object readObject()  ObjectInputStream 读取对象
      
  • 使用步骤:
    1. 创建ObjectInputStream对象,构造方法中传递字节输入流
    2. 使用ObjectInputStream对象中的方法readObject读取保存对象的文件
    3. 释放资源
    4. 使用读取出来的对象(打印)
  • readObject方法声明抛出了ClassNotFoundException(class文件找不到异常)
    • 当不存在对象的class文件时抛出此异常
  • 反序列化的前提:
    1. 类必须实现Serializable
    2. 必须存在类对应的class文件

10.9 序列化要点

  • 序列化和反序列化的时候,会抛出NotSerializableException没有序列化异常
    • 类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
    • Serializable接口也叫标记型接口
      • 要进行序列化和反序列化的类必须实现Serializable接口,就会给类添加一个标记
      • 当我们进行序列化和反序列化的时候,就会检测类上是否有这个标记
        • 有:就可以序列化和反序列化
        • 没有:就会抛出 NotSerializableException异常
  • 去市场买肉–>肉上有一个蓝色章(检测合格)–>放心购买–>买回来怎么吃随意
  • static关键字:静态关键字
    • 静态优先于非静态加载到内存中(静态优先于对象进入到内存中)
    • 被static修饰的成员变量不能被序列化的,序列化的都是对象
        private static int age;
        oos.writeObject(new Person("小美女",18));
        Object o = ois.readObject();
        Person{name='小美女', age=0}
      
  • transient关键字:瞬态关键字
    • 被transient修饰成员变量,不能被序列化
        private transient int age;
        oos.writeObject(new Person("小美女",18));
        Object o = ois.readObject();
        Person{name='小美女', age=0}
      

10.10 打印流

  • java.io.PrintStream:打印流
    • PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。
    • PrintStream特点:
      1. 只负责数据的输出,不负责数据的读取
      2. 与其他输出流不同,PrintStream 永远不会抛出 IOException
      3. 有特有的方法,print,println
         void print(任意类型的值)
                    
         void println(任意类型的值并换行)
        
    • 构造方法:
        PrintStream(File file):输出的目的地是一个文件
              
        PrintStream(OutputStream out):输出的目的地是一个字节输出流
              
        PrintStream(String fileName) :输出的目的地是一个文件路径
      
    • PrintStream extends OutputStream
    • 继承自父类的成员方法:
        public void close() 关闭此输出流并释放与此流相关联的任何系统资源
              
        public void flush() 刷新此输出流并强制任何缓冲的输出字节被写出
              
        public void write(byte[] b) b.length字节从指定的字节数组写入此输出流
              
        public void write(byte[] b, int off, int len) 从指定的字节数组写入 len字节从偏移量 off开始输出到此输出流
              
        public abstract void write(int b) 将指定的字节输出流
      
  • 注意:
    • 如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表 97->a
    • 如果使用自己特有的方法print/println方法写数据,写的数据原样输出 97->97
10.10.1 打印流的流向
  • 可以改变输出语句的目的地(打印流的流向)
    • 输出语句,默认在控制台输出
    • 使用System.setOut方法改变输出语句的目的地改为参数中传递的打印流的目的地
      • static void setOut(PrintStream out)
      • 重新分配“标准”输出流。

11. 网络编程

11.1 TCP通信的客户端

  • TCP通信的客户端:向服务器发送连接请求,给服务器发送数据,读取服务器回写的数据
    • 表示客户端的类:
      • java.net.Socket:此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
      • 套接字:包含了IP地址和端口号的网络单位
    • 构造方法:
      • Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。
      • 参数:
        • String host:服务器主机的名称/服务器的IP地址
        • int port:服务器的端口号
    • 成员方法:
        OutputStream getOutputStream() 返回此套接字的输出流
              
        InputStream getInputStream() 返回此套接字的输入流
              
        void close() 关闭此套接字
      
  • 实现步骤:
    1. 创建一个客户端对象Socket,构造方法绑定服务器的IP地址和端口号
    2. 使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
    3. 使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据
    4. 使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
    5. 使用网络字节输入流InputStream对象中的方法read,读取服务器回写的数据
    6. 释放资源(Socket)
  • 注意:
    1. 客户端和服务器端进行交互,必须使用Socket中提供的网络流,不能使用自己创建的流对象
    2. 当我们创建客户端对象Socket的时候,就会去请求服务器和服务器经过3次握手建立连接通路
      • 这时如果服务器没有启动,那么就会抛出异常ConnectException: Connection refused: connect
      • 如果服务器已经启动,那么就可以进行交互了

11.2 TCP通信的服务器端

  • TCP通信的服务器端:接收客户端的请求,读取客户端发送的数据,给客户端回写数据
    • 表示服务器的类:
      • java.net.ServerSocket:此类实现服务器套接字。
    • 构造方法:
      • ServerSocket(int port) 创建绑定到特定端口的服务器套接字。
    • 服务器端必须明确一件事情,必须的知道是哪个客户端请求的服务器
    • 所以可以使用accept方法获取到请求的客户端对象Socket
    • 成员方法:
      • Socket accept() 侦听并接受到此套接字的连接。
  • 服务器的实现步骤:
    1. 创建服务器ServerSocket对象和系统要指定的端口号
    2. 使用ServerSocket对象中的方法accept,获取到请求的客户端对象Socket
    3. 使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
    4. 使用网络字节输入流InputStream对象中的方法read,读取客户端发送的数据
    5. 使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
    6. 使用网络字节输出流OutputStream对象中的方法write,给客户端回写数据
    7. 释放资源(Socket,ServerSocket)

11.3 TCP通信实例

  • 文件上传案例的客户端:读取本地文件,上传到服务器,读取服务器回写的数据
    • 明确:
      • 数据源:c:\1.jpg
      • 目的地:服务器
    • 实现步骤:
      1. 创建一个本地字节输入流FileInputStream对象,构造方法中绑定要读取的数据源
      2. 创建一个客户端Socket对象,构造方法中绑定服务器的IP地址和端口号
      3. 使用Socket中的方法getOutputStream,获取网络字节输出流OutputStream对象
      4. 使用本地字节输入流FileInputStream对象中的方法read,读取本地文件
      5. 使用网络字节输出流OutputStream对象中的方法write,把读取到的文件上传到服务器
      6. 使用Socket中的方法getInputStream,获取网络字节输入流InputStream对象
      7. 使用网络字节输入流InputStream对象中的方法read读取服务回写的数据
      8. 释放资源(FileInputStream,Socket)
  • 文件上传案例服务器端:读取客户端上传的文件,保存到服务器的硬盘,给客户端回写”上传成功”
    • 明确:
      • 数据源:客户端上传的文件
      • 目的地:服务器的硬盘 d:\upload\1.jpg
    • 实现步骤:
      1. 创建一个服务器ServerSocket对象,和系统要指定的端口号
      2. 使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象
      3. 使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
      4. 判断d:\upload文件夹是否存在,不存在则创建
      5. 创建一个本地字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
      6. 使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件
      7. 使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
      8. 使用Socket对象中的方法getOutputStream,获取到网络字节输出流OutputStream对象
      9. 使用网络字节输出流OutputStream对象中的方法write,给客户端回写”上传成功”
      10. 释放资源(FileOutputStream,Socket,ServerSocket)

12. 函数式接口

12.1 函数式接口

  • 函数式接口:有且只有一个抽象方法的接口,称之为函数式接口
    • 当然接口中可以包含其他的方法(默认,静态,私有)
    • @FunctionalInterface注解
    • 作用:可以检测接口是否是一个函数式接口
      • 是:编译成功
      • 否:编译失败(接口中没有抽象方法抽象方法的个数多余1个)
12.1.1 生产型接口
  • java.util.function.Supplier接口仅包含一个无参的方法:T get()。用来获取一个泛型参数指定类型的对象数据。
  • Supplier接口被称之为生产型接口,指定接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据
12.1.2 消费型接口
  • java.util.function.Consumer接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型决定。
    • Consumer接口中包含抽象方法void accept(T t),意为消费一个指定泛型的数据。
    • Consumer接口是一个消费型接口,泛型执行什么类型,就可以使用accept方法消费什么类型的数据
    • 至于具体怎么消费(使用),需要自定义(输出,计算….)
  • Consumer接口的默认方法andThen
    • 作用:需要两个Consumer接口,可以把两个Consumer接口组合到一起,在对数据进行消费
    • 例如:
         Consumer<String> con1
         Consumer<String> con2
         String s = "hello";
         con1.accept(s);
         con2.accept(s);
         连接两个Consumer接口  再进行消费
         con1.andThen(con2).accept(s); 谁写前边谁先消费
      
12.1.3 Predicate接口
  • java.util.function.Predicate接口
    • 作用:对某种数据类型的数据进行判断,结果返回一个boolean值
    • Predicate接口中包含一个抽象方法:
      • boolean test(T t):用来对指定数据类型数据进行判断的方法
        • 结果:
          • 符合条件,返回true
          • 不符合条件,返回false
  • 【and方法】
  • 逻辑表达式:可以连接多个判断的条件
    • &&:与运算符,有false则false
    •   :或运算符,有true则true
    • !:非(取反)运算符,非真则假,非假则真
    • 需求:判断一个字符串,有两个判断的条件
      1. 判断字符串的长度是否大于5
      2. 判断字符串中是否包含a
    • 两个条件必须同时满足,我们就可以使用&&运算符连接两个条件
    • Predicate接口中有一个方法and,表示并且关系,也可以用于连接两个判断条件
        default Predicate<T> and(Predicate<? super T> other) {
            Objects.requireNonNull(other);
            return (t) -> this.test(t) && other.test(t);
        }
      
    • 方法内部的两个判断条件,也是使用&&运算符连接起来的
  • 【or方法】
    • 需求:判断一个字符串,有两个判断的条件
      1. 判断字符串的长度是否大于5
      2. 判断字符串中是否包含a
    • 满足一个条件即可,我们就可以使用   运算符连接两个条件
    • Predicate接口中有一个方法or,表示或者关系,也可以用于连接两个判断条件
        default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
        }
      
    • 方法内部的两个判断条件,也是使用   运算符连接起来的
  • 【negate方法】
    • 需求:判断一个字符串长度是否大于5
      • 如果字符串的长度大于5,那返回false
      • 如果字符串的长度不大于5,那么返回true
    • 所以我们可以使用取反符号!对判断的结果进行取反
    • Predicate接口中有一个方法negate,也表示取反的意思
        default Predicate<T> negate() {
        return (t) -> !test(t);
        }
      
12.1.4 Function接口
  • java.util.function.Function<T,R>接口用来根据一个类型的数据得到另一个类型的数据, 前者称为前置条件,后者称为后置条件。
    • Function接口中最主要的抽象方法为:R apply(T t),根据类型T的参数获取类型R的结果。
      • 使用的场景例如:将String类型转换为Integer类型。
  • 【Function接口中的默认方法andThen】
    • 需求:
      • 把String类型的”123”,转换为Inteter类型,把转换后的结果加10
      • 把增加之后的Integer类型的数据,转换为String类型
    • 分析:
        转换了两次
        第一次是把String类型转换为了Integer类型
            所以我们可以使用Function<String,Integer> fun1
                Integer i = fun1.apply("123")+10;
        第二次是把Integer类型转换为String类型
            所以我们可以使用Function<Integer,String> fun2
                String s = fun2.apply(i);
        我们可以使用andThen方法,把两次转换组合在一起使用
            String s = fun1.andThen(fun2).apply("123");
            fun1先调用apply方法,把字符串转换为Integer
            fun2再调用apply方法,把Integer转换为字符串
      

13. Stream流、方法引用

13.1 Stream流

  • java.util.stream.Stream是Java 8新加入的最常用的流接口。(这并不是一个函数式接口。)
    • 获取一个流非常简单,有以下几种常用的方式:
      • 所有的Collection集合都可以通过stream默认方法获取流;
        • default Stream stream​()
      • Stream接口的静态方法of可以获取数组对应的流。
        • static Stream of​(T... values) 参数是一个可变参数,那么我们就可以传递一个数组
13.1.1 Stream流中的常用方法_forEach
  • void forEach(Consumer<? super T> action);
    • 该方法接收一个Consumer接口函数,会将每一个流元素交给该函数进行处理。
    • Consumer接口是一个消费型的函数式接口,可以传递Lambda表达式,消费数据
  • 简单记:
    • forEach方法,用来遍历流中的数据
    • 是一个终结方法,遍历之后就不能继续调用Stream流中的其他方法
13.1.2 Stream流中的常用方法_filter
  • Stream流中的常用方法_filter:用于对Stream流中的数据进行过滤
    • Stream filter(Predicate<? super T> predicate);
    • filter方法的参数Predicate是一个函数式接口,所以可以传递Lambda表达式,对数据进行过滤
    • Predicate中的抽象方法:
      • boolean test(T t);
13.1.3 Stream流中的常用方法_map
  • Stream流中的常用方法_map:用于类型转换
    • 如果需要将流中的元素映射到另一个流中,可以使用map方法.
    • Stream map(Function<? super T, ? extends R> mapper);
    • 该接口需要一个Function函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。
    • Function中的抽象方法:
      • R apply(T t);
13.1.4 Stream流中的常用方法_count
  • Stream流中的常用方法_count:用于统计Stream流中元素的个数
    • long count();
    • count方法是一个终结方法,返回值是一个long类型的整数
    • 所以不能再继续调用Stream流中的其他方法了
13.1.5 Stream流中的常用方法_limit
  • Stream流中的常用方法_limit:用于截取流中的元素
    • limit方法可以对流进行截取,只取用前n个。方法签名:
    • Stream limit(long maxSize);
      • 参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作
    • limit方法是一个延迟方法,只是对流中的元素进行截取,返回的是一个新的流,所以可以继续调用Stream流中的其他方法
13.1.6 Stream流中的常用方法_skip
  • Stream流中的常用方法_skip:用于跳过元素
    • 如果希望跳过前几个元素,可以使用skip方法获取一个截取之后的新流:
    • Stream skip(long n);
      • 如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。
13.1.7 Stream流中的常用方法_concat
  • Stream流中的常用方法_concat:用于把流组合到一起
    • 如果有两个流,希望合并成为一个流,那么可以使用Stream接口的静态方法concat
    • static Stream concat(Stream<? extends T> a, Stream<? extends T> b)