JavaSE高级【吐血整理汇总】

版权声明:本博客为本人原创,本人拥有其版权。如有遗漏之处,欢迎指出,本人将于24小时内做出回复并进行确认。 https://blog.csdn.net/flyingwzb/article/details/85130517

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集合特有的方法,不能使用多态

    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)

猜你喜欢

转载自blog.csdn.net/flyingwzb/article/details/85130517
今日推荐