zoukankan      html  css  js  c++  java
  • 学习笔记-Java进阶

    各类的使用方法

    object类

    类是Java语言中的根类,即所有类的父类。它中描述的所有方法子类都可以使用。在对象实例化的时候,最终找的父类就是Object。

    如果一个类没有特别指定父类, 那么默认则继承自Object类

    重要方法:

      public String toString() :返回该对象的字符串表示。
      public boolean equals(Object obj) :指示其他某个对象是否与此对象“相等”。

    public static boolean equals(Object a, Object b) :判断两个对象是否相等。

      toString

      toString方法返回该对象的字符串表示,其实该字符串内容就是对象的类型+@+内存地址值。
      由于toString方法返回的结果是内存地址,而在开发中,经常需要按照对象的属性得到相应的字符串表现形式,因此也需要重写它。
    小贴士: 在我们直接使用输出语句输出对象名的时候,其实通过该对象调用了其toString()方法。

    equals

      如果没有覆盖重写equals方法,那么Object类中默认进行== 运算符的对象地址比较,只要不是同一个对象,结果必然为false。

      如果希望进行对象的内容比较,即所有或指定的部分成员变量相同就判定两个对象相同,则可以覆盖重写equals方法。

    时间类

    date类的构造函数:

      public Date() :分配Date对象并初始化此对象,以表示分配它的时间(精确到毫秒)。
      public Date(long date) :分配Date对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即1970年1月1日00:00:00 GMT)以来的指定毫秒数。

    重要方法:

      public long getTime() 把日期对象转换成对应的时间毫秒值。

    注意:
        中国属于东八区,会把时间增加8个小时
    dateformat类:

      java.text.DateFormat:是日期/时间格式化子类的抽象类
       作用:
            格式化(也就是日期 -> 文本)、解析(文本-> 日期)
      成员方法:
          String format(Date date)  按照指定的模式,把Date日期,格式化为符合模式的字符串
          Date parse(String source)  把符合模式的字符串,解析为Date日期
    DateFormat类是一个抽象类,无法直接创建对象使用,可以使用DateFormat类的子类SimpleDateFormat

    构造方法:
        SimpleDateFormat(String pattern)
          用给定的模式和默认语言环境的日期格式符号构造 SimpleDateFormat。
        参数:
             String pattern:传递指定的模式
        模式:区分大小写的

    y

    M

    d H m s

    写好对应的模式,会把模式替换为对应的日期和时间:"yyyy-MM-dd HH:mm:ss"

        注意:
            模式中的字母不能更改,连接模式的符号可以改变:"yyyy年MM月dd日 HH时mm分ss秒"

    使用DateFormat类中的方法parse,把文本解析为日期
      使用步骤:
         1.创建SimpleDateFormat对象,构造方法中传递指定的模式
         2.调用SimpleDateFormat对象中的方法parse,把符合构造方法中模式的字符串,解析为Date日期

    使用DateFormat类中的方法format,把日期格式化为文本
      使用步骤:
          1.创建SimpleDateFormat对象,构造方法中传递指定的模式
          2.调用SimpleDateFormat对象中的方法format,按照构造方法中指定的模式,把Date日期格式化为符合模式的字符串(文本)。

    日期类

    日历类,在Date后出现,替换掉了许多Date的方法。该类将所有可能用到的时间信息封装为静态成员变量,方便获取。日历类就是方便获取各个时间属性的。

    Calendar类是一个抽象类,里边提供了很多操作日历字段的方法(YEAR、MONTH、DAY_OF_MONTH、HOUR )
    Calendar类无法直接创建对象使用,里边有一个静态方法叫getInstance(),该方法返回了Calendar类的子类对象
    static Calendar getInstance() 使用默认时区和语言环境获得一个日历。

    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类的静态成员变量获取

    字段值 含义
    YEAR  年
    MONTH  月(从0开始,可以+1使用)
    DAY_OF_MONTH  月中的天(几号)
    HOUR  时(12小时制)
    HOUR_OF_DAY  时(24小时制)
    MINUTE  分
    SECOND  秒
    DAY_OF_WEEK  周中的天(周几,周日为1,可以-1使用)
    小贴士:

      西方星期的开始为周日,中国为周一。
      在Calendar类中,月份的表示是以0-11代表1-12月。
      日期是有大小关系的,时间靠后,时间越大。

    System

    类中提供了大量的静态方法,可以获取与系统相关的信息或系统级操作,在System类的API文档中,常用的方法有:
      public static long currentTimeMillis() :返回以毫秒为单位的当前时间。
      public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) :将数组中指定的数据拷贝到另一个数组中。

    参数序号

    参数名称 参数类型 参数含义
    1 src  Object  源数组
    2  srcPos  int  源数组索引起始位置
    3 dest  Object  目标数组
    4  destPos  int  目标数组索引起始位置
    5  length   int 复制元素个数

    StringBuilder类

    StringBuilder又称为可变字符序列,它是一个类似于 String 的字符串缓冲区,通过某些方法调用可以改变该序列的长度和内容。
    原来StringBuilder是个字符串的缓冲区,即它是一个容器,容器中可以装很多字符串。并且能够对其中的字符串进行各种操作。
    它的内部拥有一个数组用来存放字符串内容,进行字符串拼接时,直接在数组中加入新内容。StringBuilder会自动维护数组的扩容。

    构造方法:
        StringBuilder() 构造一个不带任何字符的字符串生成器,其初始容量为 16 个字符。
        StringBuilder(String str) 构造一个字符串生成器,并初始化为指定的字符串内容。

    常用方法:

      public StringBuilder append(...) :添加任意类型数据的字符串形式,并返回当前对象自身。
      public String toString() :将当前StringBuilder对象转换为String对象。

    StringBuilder和String可以相互转换:
        String->StringBuilder:可以使用StringBuilder的构造方法
            StringBuilder(String str) 构造一个字符串生成器,并初始化为指定的字符串内容。
        StringBuilder->String:可以使用StringBuilder中的toString方法
            public String toString():将当前StringBuilder对象转换为String对象。

    包装类

    Java提供了两个类型系统,基本类型与引用类型,使用基本类型在于效率,然而很多情况,会创建对象使用,因为对象可以做更多的功能,如果想要我们的基本类型像对象一样操作,就可以使用基本类型对应的包装类,如下:

    基本类型

    对应的包装类(位于java.lang包中)
    byte  Byte
    short  Short
    int  Integer
    long  Long
    float  Float
    double  Double
    char  Character
    boolean  Boolean

    装箱:把基本类型的数据,包装到包装类中(基本类型的数据->包装类)
        构造方法:
            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 的值。

    自动装箱与自动拆箱:基本类型的数据和包装类之间可以自动的相互转换 JDK1.5之后出现的新特性。

    基本类型与字符串类型之间的相互转换
    基本类型->字符串(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)

    collection集合

    集合:集合是java中提供的一种容器,可以用来存储多个数据。

    数组中存储的是同一类型的元素,可以存储基本数据类型值。集合存储的都是对象。而且对象的类型可以不一致。在开发中一般当对象多的时候,使用集合进行存储。

    Collection:单列集合类的根接口,用于存储一系列符合某种规则的元素,它有两个重要的子接口,分别是java.util.List 和java.util.Set 。其中, List 的特点是元素有序、元素可重复。Set 的特点是元素无序,而且不可重复。List 接口的主要实现类有java.util.ArrayList 和java.util.LinkedList , Set 接口的主要实现类有java.util.HashSet 和java.util.TreeSet 。

    关系图:

    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(): 把集合中的元素,存储到数组中。

    迭代器

    java.util.Iterator接口:迭代器(对集合进行遍历)

    迭代:即Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。

    有两个常用的方法:
        boolean hasNext() 如果仍有元素可以迭代,则返回 true。
            判断集合中还有没有下一个元素,有就返回true,没有就返回false。
        E next() 返回迭代的下一个元素。
            取出集合中的下一个元素
    Iterator迭代器,是一个接口,我们无法直接使用,需要使用Iterator接口的实现类对象,获取实现类的方式比较特殊
    Collection接口中有一个方法,叫iterator(),这个方法返回的就是迭代器的实现类对象。

      Iterator<E> iterator() 返回在此 collection 的元素上进行迭代的迭代器。

    迭代器的使用步骤:
        1.使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态)。
        2.使用Iterator接口中的方法hasNext判断还有没有下一个元素。
        3.使用Iterator接口中的方法next取出集合中的下一个元素。

    1.使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态)。
    注意:
        Iterator<E>接口也是有泛型的,迭代器的泛型跟着集合走,集合是什么泛型,迭代器就是什么泛型。

    增强for循环:底层使用的也是迭代器,使用for循环的格式,简化了迭代器的书写,不能对集合中的元素进行增删操作。
    所有的单列集合都可以使用增强for。
    增强for循环:用来遍历集合和数组。

      for(集合/数组的数据类型 变量名: 集合名/数组名){
            sout(变量名);
    格式

    泛型

    含有泛型的方法,在调用方法的时候确定泛型的数据类型。
    传递什么类型的参数,泛型就是什么类型。

    创建集合对象,使用泛型。

    好处:
        1.避免了类型转换的麻烦,存储的是什么类型,取出的就是什么类型。
        2.把运行期异常(代码运行之后会抛出的异常),提升到了编译期(写代码的时候会报错)。
     弊端:
        泛型是什么类型,只能存储什么类型的数据。

    创建集合对象,不使用泛型。

    修饰符 class 类名<代表泛型的变量> { }
    泛型类
    修饰符 <代表泛型的变量> 返回值类型 方法名(参数){ }
    泛型方法
    修饰符 interface接口名<代表泛型的变量> { }
    泛型接口

    型泛型通配符

    泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符。

        ?:代表任意的数据类型。
    使用方式:
        不能创建对象使用。
        只能作为方法的参数使用。

    注意:
        泛型没有继承概念的。

    泛型的上限限定: ? extends E  代表使用的泛型只能是E类型的子类/本身。
    泛型的下限限定: ? super E    代表使用的泛型只能是E类型的父类/本身。

    List集合

    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:字符串索引越界异常

    List的子类:

      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。

    Set接口

    java.util.Set接口 extends Collection接口
    Set接口的特点:
        1.不允许存储重复的元素
        2.没有索引,没有带索引的方法,也不能使用普通的for循环遍历
    java.util.HashSet集合 implements Set接口
    HashSet特点:
         1.不允许存储重复的元素
         2.没有索引,没有带索引的方法,也不能使用普通的for循环遍历
         3.是一个无序的集合,存储元素和取出元素的顺序有可能不一致
         4.底层是一个哈希表结构(查询的速度非常的快)

      HashSet 是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性的方式依赖于: hashCode 与equals 方法。

      HashSet存储自定义类型元素:   

        给HashSet中存放自定义类型元素时,需要重写对象中的hashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一

    LinkedHashSet集合特点:
        底层是一个哈希表(数组+链表/红黑树)+链表:多了一条链表(记录元素的存储顺序),保证元素有序

    哈希值:是一个十进制的整数,由系统随机给出(就是对象的地址值,是一个逻辑地址,是模拟出来得到地址,不是数据实际存储的物理地址)
    在Object类有一个方法,可以获取对象的哈希值     int hashCode() 返回该对象的哈希码值。

     可变参数

    使用前提:
        当方法的参数列表数据类型已经确定,但是参数的个数不确定,就可以使用可变参数.
    使用格式:定义方法时使用
        修饰符 返回值类型 方法名(数据类型...变量名){}
    可变参数的原理:
        可变参数底层就是一个数组,根据传递参数个数不同,会创建不同长度的数组,来存储这些参数
        传递的参数个数,可以是0个(不传递),1,2...多个

     Collections

    常用功能:

      public static <T> boolean addAll(Collection<T> c, T... elements) :往集合中添加一些元素。
      public static void shuffle(List<?> list) 打乱顺序:打乱集合顺序。
      public static <T> void sort(List<T> list) :将集合中元素按照默认规则排序。
      public static <T> void sort(List<T> list,Comparator<? super T> ) :将集合中元素按照指定规则排序。

    注意:
         sort(List<T> list)使用前提
         被排序的集合里边存储的元素,必须实现Comparable,重写接口中的方法compareTo定义排序的规则
    Comparable接口的排序规则: 自己(this)-参数:升序

     Comparator和Comparable的区别:
        Comparable:自己(this)和别人(参数)比较,自己需要实现Comparable接口,重写比较的规则compareTo方法
        Comparator:相当于找一个第三方的裁判,比较两个
    Comparator的排序规则:  o1-o2:升序

     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集合是一个有序的集合,存储元素和取出元素的顺序是一致的

    Map集合的第一种遍历方式:通过键找值的方式
      Map集合中的方法:
           Set<K> keySet() 返回此映射中包含的键的 Set 视图。
      实现步骤:
          1.使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个Set集合中
          2.遍历set集合,获取Map集合中的每一个key
          3.通过Map集合中的方法get(key),通过key找到value

    Map集合遍历的第二种方式:使用Entry对象遍历
      Map集合中的方法:
          Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射关系的 Set 视图。
      实现步骤:
          1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中
          2.遍历Set集合,获取每一个Entry对象
          3.使用Entry对象中的方法getKey()和getValue()获取键与值

    HashMap存储自定义类型键值
      Map集合保证key是唯一的:
          作为key的元素,必须重写hashCode方法和equals方法,以保证key唯一

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

    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 <E> List<E> 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:切换到控制台

     异常处理

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

    throw关键字

    作用:
        可以使用throw关键字在指定的方法中抛出指定的异常

     throw new xxxException("异常产生的原因");
    使用格式

    注意:
        1.throw关键字必须写在方法的内部
        2.throw关键字后边new的对象必须是Exception或者Exception的子类对象
        3.throw关键字抛出指定的异常对象,我们就必须处理这个异常对象
            throw关键字后边创建的是RuntimeException或者是 RuntimeException的子类对象,我们可以不处理,默认交给JVM处理(打印异常对象,中断程序)
            throw关键字后边创建的是编译异常(写代码的时候报错),我们就必须处理这个异常,要么throws,要么try...catch

    Obects类中的静态方法
    public static <T> T requireNonNull(T obj):查看指定引用对象不是null。

    public static <T> T requireNonNull(T obj) {
            if (obj == null)
                throw new NullPointerException();
            return obj;
        }
    源码

    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自己处理异常

    try...catch:异常处理的第二种方式,自己处理异常

        try{
            可能产生异常的代码
        }catch(定义一个异常的变量,用来接收try中抛出的异常对象){
            异常的处理逻辑,异常异常对象之后,怎么处理异常对象
            一般在工作中,会把异常的信息记录到一个日志中
        }
        ...
        catch(异常类名 变量名){
        }
    格式

    注意:
        1.try中可能会抛出多个异常对象,那么就可以使用多个catch来处理这些异常对象
        2.如果try中产生了异常,那么就会执行catch中的异常处理逻辑,执行完毕catch中的处理逻辑,继续执行try...catch之后的代码
          如果try中没有产生异常,那么就不会执行catch中异常的处理逻辑,执行完try中的代码,继续执行try...catch之后的代码

    finally代码块

        try{
            可能产生异常的代码
        }catch(定义一个异常的变量,用来接收try中抛出的异常对象){
            异常的处理逻辑,异常异常对象之后,怎么处理异常对象
            一般在工作中,会把异常的信息记录到一个日志中
        }
        ...
        catch(异常类名 变量名){
    
        }finally{
            无论是否出现异常都会执行
        }
    格式

     注意:
        1.finally不能单独使用,必须和try一起使用
        2.finally一般用于资源释放(资源回收),无论程序是否出现异常,最后都要资源释放(IO)

     3.如果finally有return语句,永远返回finally中的结果,避免该情况.

    多个异常使用捕获又该如何处理呢?
    1. 多个异常分别处理。
    2. 多个异常一次捕获,多次处理。
    3. 多个异常一次捕获一次处理。

    子父类的异常:

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

    自定义异常类:

        java提供的异常类,不够我们使用,需要自己定义一些异常类

    public class XXXExcepiton extends Exception | RuntimeException{
            添加一个空参数的构造方法
            添加一个带异常信息的构造方法
        }
    格式

     注意:
        1.自定义异常类一般都是以Exception结尾,说明该类是一个异常类
        2.自定义异常类,必须的继承Exception或者RuntimeException
            继承Exception:那么自定义的异常类就是一个编译期异常,如果方法内部抛出了编译期异常,就必须处理这个异常,要么throws,要么try...catch
            继承RuntimeException:那么自定义的异常类就是一个运行期异常,无需处理,交给虚拟机处理(中断处理)

     进程线程

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

    创建多线程程序的第一种方式:创建Thread类的子类
    java.lang.Thread类:是描述线程的类,我们想要实现多线程程序,就必须继承Thread类
    实现步骤:
        1.创建一个Thread类的子类
        2.在Thread类的子类中重写Thread类中的run方法,设置线程任务(开启线程要做什么?)
        3.创建Thread类的子类对象
        4.调用Thread类中的方法start方法,开启新的线程,执行run方法
             void start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
             结果是两个线程并发地运行;当前线程(main线程)和另一个线程(创建的新线程,执行其 run 方法)。
             多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。
    java程序属于抢占式调度,那个线程的优先级高,那个线程优先执行;同一个优先级,随机选择一个执行

    创建多线程程序的第二种方式:实现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方法:用来开启新线程

    获取线程的名称:
        1.使用Thread类中的方法getName()
            String getName() 返回该线程的名称。
        2.可以先获取到当前正在执行的线程,使用线程中的方法getName()获取线程的名称
            static Thread currentThread() 返回对当前正在执行的线程对象的引用。

    public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。毫秒数结束之后,线程继续执行

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

       new 父类/接口(){
            重复父类/接口中的方法
        };
    格式

    解决线程安全问题的一种方案:使用同步代码块

        synchronized(锁对象){
            可能会出现线程安全问题的代码(访问了共享数据的代码)
        }
    格式

    注意:
        1.通过代码块中的锁对象,可以使用任意的对象
        2.但是必须保证多个线程使用的锁对象是同一个
        3.锁对象作用:
            把同步代码块锁住,只让一个线程在同步代码块中执行

    解决线程安全问题的二种方案:使用同步方法
    使用步骤:
        1.把访问了共享数据的代码抽取出来,放到一个方法中
        2.在方法上添加synchronized修饰符

    修饰符 synchronized 返回值类型 方法名(参数列表){
        可能会出现线程安全问题的代码(访问了共享数据的代码)
    格式(定义方法的格式)

    Obejct类中的方法

    void wait()
          在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
    void notify()
          唤醒在此对象监视器上等待的单个线程。
          会继续执行wait方法之后的代码

    进入到TimeWaiting(计时等待)有两种方式
      1.使用sleep(long m)方法,在毫秒值结束之后,线程睡醒进入到Runnable/Blocked状态
      2.使用wait(long m)方法,wait方法如果在毫秒值结束之后,还没有被notify唤醒,就会自动醒来,线程睡醒进入到Runnable/Blocked状态
    唤醒的方法:
         void notify() 唤醒在此对象监视器上等待的单个线程。
         void notifyAll() 唤醒在此对象监视器上等待的所有线程。

    设置线程的名称:
        1.使用Thread类中的方法setName(名字)
            void setName(String name) 改变线程名称,使之与参数 name 相同。
        2.创建一个带参数的构造方法,参数传递线程的名称;调用父类的带参构造方法,把线程名称传递给父类,让父类(Thread)给子线程起一个名字
            Thread(String name) 分配新的 Thread 对象。 

    线程池:

    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销毁线程池(不建议执行)

    Lambda表达式的标准格式:
        由三部分组成:
            a.一些参数
            b.一个箭头
            c.一段代码

    (参数列表) -> {一些重写方法的代码};
    格式

        解释说明格式:
            ():接口中抽象方法的参数列表,没有参数,就空着;有参数就写出参数,多个参数使用逗号分隔
            ->:传递的意思,把参数传递给方法体{}
            {}:重写接口的抽象方法的方法体

    Lambda表达式:是可推导,可以省略
    凡是根据上下文推导出来的内容,都可以省略书写
    可以省略的内容:
        1.(参数列表):括号中参数列表的数据类型,可以省略不写
        2.(参数列表):括号中的参数如果只有一个,那么类型和()都可以省略
        3.{一些代码}:如果{}中的代码只有一行,无论是否有返回值,都可以省略({},return,分号)
            注意:要省略{},return,分号必须一起省略

    递归

    递归:方法自己调用自己
    递归的分类:
        递归分为两种,直接递归和间接递归。
        直接递归称为方法自身调用自己。
        间接递归可以A方法调用B方法,B方法调用C方法,C方法调用A方法。
    注意事项:
        递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出。
        在递归中虽然有限定条件,但是递归次数不能太多。否则也会发生栈内存溢出。
        构造方法,禁止递归
    递归的使用前提:
        当调用方法的时候,方法的主体不变,每次调用方法的参数不同,可以使用递归

    文件处理

    资源回收

    java.io.File类

    java把电脑中的文件和文件夹(目录)封装为了一个File类,我们可以使用File类对文件和文件夹进行操作
    File类是一个与系统无关的类,任何的操作系统都可以使用这个类中的方法

    static String pathSeparator 与系统有关的路径分隔符,为了方便,它被表示为一个字符串。
    static char pathSeparatorChar 与系统有关的路径分隔符。
    static String separator 与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。
    static char separatorChar 与系统有关的默认名称分隔符。

    操作路径:路径不能写死了
      C:developaa.txt  windows
      C:/develop/a/a.txt  linux
      "C:"+File.separator+"develop"+File.separator+"a"+File.separator+"a.txt"

    路径分隔符 windows:分号;  linux:冒号:

    文件名称分隔符 windows:反斜杠  linux:正斜杠/

    路径:
        绝对路径:是一个完整的路径, 以盘符(c:,D:)开始的路径
        相对路径:是一个简化的路径
            相对指的是相对于当前项目的根目录
            如果使用当前项目的根目录,路径可以简化书写
        注意:
            1.路径是不区分大小写的
            2.路径中的文件名称分隔符:windows使用反斜杠,反斜杠是转义字符,两个反斜杠代表一个普通的反斜杠

    File(File parent, String child) 根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。
    参数:把路径分成了两部分
        File parent:父路径
        String child:子路径
    好处:
         父路径和子路径,可以单独书写,使用起来非常灵活;父路径和子路径都可以变化
         父路径是File类型,可以使用File的方法对路径进行一些操作,再使用路径创建对象

    File(String pathname) 通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。
    参数:
        String pathname:字符串的路径名称
        路径可以是以文件结尾,也可以是以文件夹结尾
        路径可以是相对路径,也可以是绝对路径
        路径可以是存在,也可以是不存在
        创建File对象,只是把字符串路径封装为File对象,不考虑路径的真假情况

    File类获取功能的方法
         public String getAbsolutePath() :返回此File的绝对路径名字符串。获取的构造方法中传递的路径,无论路径是绝对的还是相对的,getAbsolutePath方法返回的都是绝对路径
         public String getPath() :将此File转换为路径名字符串。获取的构造方法中传递的路径
         public String getName()  :返回由此File表示的文件或目录的名称。获取的就是构造方法传递路径的结尾部分(文件/文件夹)
         public long length()  :返回由此File表示的文件的长度。获取的是构造方法指定的文件的大小,以字节为单位

    注意:
        文件夹是没有大小概念的,不能获取文件夹的大小
        如果构造方法中给出的路径不存在,那么length方法返回0

    toString方法调用的就是getPath方法 

    File类判断功能的方法
         public boolean exists() :此File表示的文件或目录是否实际存在。用于判断构造方法中的路径是否存在
         public boolean isDirectory() :此File表示的是否为目录。用于判断构造方法中给定的路径是否以文件夹结尾
         public boolean isFile() :此File表示的是否为文件。用于判断构造方法中给定的路径是否以文件结尾

    注意:
        电脑的硬盘中只有文件/文件夹,两个方法是互斥
        这两个方法使用前提,路径必须是存在的,否则都返回false

    File类创建删除功能的方法
         public boolean createNewFile() :当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。创建文件的路径和名称在构造方法中给出(构造方法的参数)
         public boolean delete() :删除由此File表示的文件或目录。此方法,可以删除构造方法路径中给出的文件/文件夹
         public boolean mkdir() :创建由此File表示的目录。
         public boolean mkdirs() :创建由此File表示的目录,包括任何必需但不存在的父目录。创建文件夹的路径和名称在构造方法中给出(构造方法的参数)

    注意:
        delete方法是直接在硬盘删除文件/文件夹,不走回收站,删除要谨慎

     createNewFile声明抛出了IOException,我们调用这个方法,就必须的处理这个异常,要么throws,要么trycatch

    File类遍历(文件夹)目录功能
         public String[] list() :返回一个String数组,表示该File目录中的所有子文件或目录。
         public File[] listFiles() :返回一个File数组,表示该File目录中的所有的子文件或目录。遍历构造方法中给出的目录,会获取目录中所有的文件/文件夹,把文件/文件夹封装为File对象,多个File对象存储到File数组中
    注意:
        list方法和listFiles方法遍历的是构造方法中给出的目录
        如果构造方法中给出的目录的路径不存在,会抛出空指针异常
        如果构造方法中给出的路径不是一个目录,也会抛出空指针异常

     在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,在方法中自己定义过滤的规则

    public String[] list() :

    返回一个String数组,表示该File目录中的所有子文件或目录。
    遍历构造方法中给出的目录,会获取目录中所有文件/文件夹的名称,把获取到的多个名称存储到一个String类型的数组中

    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) :将指定的字节输出流。

    FileOutputStream:文件字节输出流

    java.io.FileOutputStream extends OutputStream
    作用:把内存中的数据写入到硬盘的文件中
    构造方法:
        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.释放资源(流使用会占用一定的内存,使用完毕要把内存清空,提供程序的效率)

    一次写多个字节的方法:
          public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。

            如果写的第一个字节是正数(0-127),那么显示的时候会查询ASCII表
              如果写的第一个字节是负数,那第一个字节会和第二个字节,两个字节组成一个中文显示,查询系统默认码表(GBK)
          public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。

    写入字符的方法:可以使用String类中的方法把字符串,转换为字节数组
        byte[] getBytes()  把字符串转换为字节数组

    追加写/续写:使用两个参数的构造方法
        FileOutputStream(String name, boolean append)创建一个向具有指定 name 的文件中写入数据的输出文件流。
        FileOutputStream(File file, boolean append) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
        参数:
           String name,File file:写入数据的目的地
           boolean append:追加写开关
            true:创建对象不会覆盖源文件,继续在文件的末尾追加写数据
            false:创建一个新文件,覆盖源文件

    java.io.InputStream:字节输入流

    此抽象类是表示字节输入流的所有类的超类。
    定义了所有子类共性的方法:
         int read()从输入流中读取数据的下一个字节。
         int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
         void close() 关闭此输入流并释放与该流关联的所有系统资源。

    FileInputStream:文件字节输入流

    java.io.FileInputStream extends InputStream
    作用:把硬盘文件中的数据,读取到内存中使用
    构造方法:
        FileInputStream(String name)
        FileInputStream(File file)
        参数:读取文件的数据源
            String name:文件的路径
            File file:文件
        构造方法的作用:
            1.会创建一个FileInputStream对象
            2.会把FileInputStream对象指定构造方法中要读取的文件
    读取数据的原理(硬盘-->内存)
        java程序-->JVM-->OS-->OS读取数据的方法-->读取文件
    字节输入流的使用步骤(重点):
        1.创建FileInputStream对象,构造方法中绑定要读取的数据源
        2.使用FileInputStream对象中的方法read,读取文件
        3.释放资源

    超重点:

      布尔表达式(len = fis.read())!=-1
          1.fis.read():读取一个字节
          2.len = fis.read():把读取到的字节赋值给变量len
          3.(len = fis.read())!=-1:判断变量len是否不等于-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:转换的字节个数

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

    java.io.Reader:字符输入流

    是字符输入流的最顶层的父类,定义了一些共性的成员方法,是一个抽象类
    共性的成员方法:
        int read() 读取单个字符并返回。
        int read(char[] cbuf)一次读取多个字符,将字符读入数组。
        void close() 关闭该流并释放与之关联的所有资源。

    FileReader:文件字符输入流

    java.io.FileReader extends InputStreamReader extends Reader
    作用:把硬盘文件中的数据以字符的方式读取到内存中
    构造方法:
        FileReader(String fileName)
        FileReader(File file)
        参数:读取文件的数据源
            String fileName:文件的路径
            File file:一个文件
        FileReader构造方法的作用:
            1.创建一个FileReader对象
            2.会把FileReader对象指向要读取的文件
    字符输入流的使用步骤:
        1.创建FileReader对象,构造方法中绑定要读取的数据源
        2.使用FileReader对象中的方法read读取文件
        3.释放资源

    java.io.Writer:字符输出流

    是字符输出流的最顶层的父类,定义了一些共性的成员方法,是一个抽象类
    共性的成员方法:

      void write(String str) 写入字符串。
      void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。

      void flush() 刷新该流的缓冲。
      void close() 关闭此流,但要先刷新它。

    FileWriter:文件字符输出流

    java.io.FileWriter extends OutputStreamWriter extends Writer
    作用:把内存中的数据以字符的方式写到硬盘文件中
    构造方法:

      FileWriter(File file) : 创建一个新的 FileWriter,给定要读取的File对象。
      FileWriter(String fileName) : 创建一个新的 FileWriter,给定要读取的文件的名称。

    flush :刷新缓冲区,流对象可以继续使用。
    close :先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。

    IO异常处理

    在jdk1.7之前使用try catch finally 处理流中的异常

        try{
            可能会产出异常的代码
        }catch(异常类变量 变量名){
            异常的处理逻辑
        }finally{
            一定会指定的代码
            资源释放
        }
    格式

    JDK7的新特性
    在try的后边可以增加一个(),在括号中可以定义流对象
    那么这个流对象的作用域就在try中有效
    try中的代码执行完毕,会自动把流对象释放,不用写finally

       try(定义流对象;定义流对象....){
            可能会产出异常的代码
        }catch(异常类变量 变量名){
            异常的处理逻辑
        }
    格式

    JDK9新特性
    try的前边可以定义流对象
    在try后边的()中可以直接引入流对象的名称(变量名)
    在try代码执行完毕之后,流对象也可以释放掉,不用写finally

        A a = new A();
        B b = new B();
        try(a,b){
            可能会产出异常的代码
        }catch(异常类变量 变量名){
            异常的处理逻辑
        }
    格式

    Properties 类表示了一个持久的属性集。

    java.util.Properties集合 extends Hashtable<k,v> implements Map<k,v>

    Properties 可保存在流中或从流中加载。
    Properties集合是一个唯一和IO流相结合的集合
        可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
        可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用
    属性列表中每个键及其对应值都是一个字符串。
        Properties集合是一个双列集合,key和value默认都是字符串

    可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用

    void load(InputStream inStream)
    void load(Reader reader)
    参数:
        InputStream inStream:字节输入流,不能读取含有中文的键值对
        Reader reader:字符输入流,能读取含有中文的键值对
    使用步骤:
        1.创建Properties集合对象
        2.使用Properties集合对象中的方法load读取保存键值对的文件
        3.遍历Properties集合
    注意:
        1.存储键值对的文件中,键与值默认的连接符号可以使用=,空格(其他符号)
        2.存储键值对的文件中,可以使用#进行注释,被注释的键值对不会再被读取
        3.存储键值对的文件中,键与值默认都是字符串,不用再加引号

    可以使用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.释放资源

    使用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方法

    缓冲

    BufferedOutputStream:字节缓冲输出流

    java.io.BufferedOutputStream 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) :将指定的字节输出流。
     构造方法:
        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部可以省略)

    BufferedInputStream:字节缓冲输入流

    java.io.BufferedInputStream extends InputStream
    继承自父类的成员方法:
        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.释放资源

     BufferedWriter:字符缓冲输出流

    java.io.BufferedWriter extends 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() 关闭此流,但要先刷新它。
    构造方法:
        BufferedWriter(Writer out) 创建一个使用默认大小输出缓冲区的缓冲字符输出流。
        BufferedWriter(Writer out, int sz) 创建一个使用给定大小输出缓冲区的新缓冲字符输出流。
        参数:
            Writer out:字符输出流
                我们可以传递FileWriter,缓冲流会给FileWriter增加一个缓冲区,提高FileWriter的写入效率
            int sz:指定缓冲区的大小,不写默认大小
    特有的成员方法:
        void newLine() 写入一个行分隔符。会根据不同的操作系统,获取不同的行分隔符
     使用步骤:
        1.创建字符缓冲输出流对象,构造方法中传递字符输出流
        2.调用字符缓冲输出流中的方法write,把数据写入到内存缓冲区中
        3.调用字符缓冲输出流中的方法flush,把内存缓冲区中的数据,刷新到文件中
        4.释放资源

     BufferedReader:字符缓冲输入流

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

    编码问题

    FileReader可以读取IDE默认编码格式(UTF-8)的文件
    FileReader读取系统默认编码(中文GBK)会产生乱码���

    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.释放资源

    java.io.InputStreamReader extends Reader

    InputStreamReader:是字节流通向字符流的桥梁:它使用指定的 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.释放资源
     注意事项:
        构造方法中指定的编码表名称要和文件的编码相同,否则会发生乱码

     序列化

    ObjectOutputStream:对象的序列化流

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

     ObjectInputStream:对象的反序列化流

    java.io.ObjectInputStream extends InputStream
    作用:把文件中保存的对象,以流的方式读取出来使用
    构造方法:
        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文件

    Serializable 接口

    类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
    Serializable接口也叫标记型接口
        要进行序列化和反序列化的类必须实现Serializable接口,就会给类添加一个标记
        当我们进行序列化和反序列化的时候,就会检测类上是否有这个标记
            有:就可以序列化和反序列化
            没有:就会抛出 NotSerializableException异常
    static关键字:静态关键字
        静态优先于非静态加载到内存中(静态优先于对象进入到内存中)
        被static修饰的成员变量不能被序列化的,序列化的都是对象
    transient关键字:瞬态关键字
        被transient修饰成员变量,不能被序列化

     打印流

    java.io.PrintStream:打印流
        PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。
    PrintStream特点:
        1.只负责数据的输出,不负责数据的读取
        2.与其他输出流不同,PrintStream 永远不会抛出 IOException
        3.有特有的方法,print,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

    可以改变输出语句的目的地(打印流的流向):
      输出语句,默认在控制台输出
      使用System.setOut方法改变输出语句的目的地改为参数中传递的打印流的目的地
          static void setOut(PrintStream out)  重新分配“标准”输出流。

    网络编程入门

    客户端

    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
            如果服务器已经启动,那么就可以进行交互了

     服务端

    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)

      1 ①客户端
      2 
      3 import java.io.FileInputStream;
      4 import java.io.IOException;
      5 import java.io.InputStream;
      6 import java.io.OutputStream;
      7 import java.net.Socket;
      8 
      9 /*
     10     文件上传案例的客户端:读取本地文件,上传到服务器,读取服务器回写的数据
     11 
     12     明确:
     13         数据源:c:\1.jpg
     14         目的地:服务器
     15 
     16     实现步骤:
     17         1.创建一个本地字节输入流FileInputStream对象,构造方法中绑定要读取的数据源
     18         2.创建一个客户端Socket对象,构造方法中绑定服务器的IP地址和端口号
     19         3.使用Socket中的方法getOutputStream,获取网络字节输出流OutputStream对象
     20         4.使用本地字节输入流FileInputStream对象中的方法read,读取本地文件
     21         5.使用网络字节输出流OutputStream对象中的方法write,把读取到的文件上传到服务器
     22         6.使用Socket中的方法getInputStream,获取网络字节输入流InputStream对象
     23         7.使用网络字节输入流InputStream对象中的方法read读取服务回写的数据
     24         8.释放资源(FileInputStream,Socket)
     25  */
     26 public class TCPClient {
     27     public static void main(String[] args) throws IOException {
     28         //1.创建一个本地字节输入流FileInputStream对象,构造方法中绑定要读取的数据源
     29         FileInputStream fis = new FileInputStream("c:\1.jpg");
     30         //2.创建一个客户端Socket对象,构造方法中绑定服务器的IP地址和端口号
     31         Socket socket = new Socket("127.0.0.1",8888);
     32         //3.使用Socket中的方法getOutputStream,获取网络字节输出流OutputStream对象
     33         OutputStream os = socket.getOutputStream();
     34         //4.使用本地字节输入流FileInputStream对象中的方法read,读取本地文件
     35         int len = 0;
     36         byte[] bytes = new byte[1024];
     37         while((len = fis.read(bytes))!=-1){
     38             //5.使用网络字节输出流OutputStream对象中的方法write,把读取到的文件上传到服务器
     39             os.write(bytes,0,len);
     40         }
     41 
     42         /*
     43             解决:上传完文件,给服务器写一个结束标记
     44             void shutdownOutput() 禁用此套接字的输出流。
     45             对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列。
     46          */
     47         socket.shutdownOutput();
     48 
     49         //6.使用Socket中的方法getInputStream,获取网络字节输入流InputStream对象
     50         InputStream is = socket.getInputStream();
     51 
     52 
     53 
     54         //7.使用网络字节输入流InputStream对象中的方法read读取服务回写的数据
     55         while((len = is.read(bytes))!=-1){
     56             System.out.println(new String(bytes,0,len));
     57         }
     58 
     59 
     60         //8.释放资源(FileInputStream,Socket)
     61         fis.close();
     62         socket.close();
     63     }
     64 }
     65 ②服务器端
     66 
     67 import java.io.File;
     68 import java.io.FileOutputStream;
     69 import java.io.IOException;
     70 import java.io.InputStream;
     71 import java.net.ServerSocket;
     72 import java.net.Socket;
     73 import java.util.Random;
     74 
     75 /*
     76     文件上传案例服务器端:读取客户端上传的文件,保存到服务器的硬盘,给客户端回写"上传成功"
     77 
     78     明确:
     79         数据源:客户端上传的文件
     80         目的地:服务器的硬盘 d:\upload\1.jpg
     81 
     82     实现步骤:
     83         1.创建一个服务器ServerSocket对象,和系统要指定的端口号
     84         2.使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象
     85         3.使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
     86         4.判断d:\upload文件夹是否存在,不存在则创建
     87         5.创建一个本地字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
     88         6.使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件
     89         7.使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
     90         8.使用Socket对象中的方法getOutputStream,获取到网络字节输出流OutputStream对象
     91         9.使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功"
     92         10.释放资源(FileOutputStream,Socket,ServerSocket)
     93  */
     94 public class TCPServer {
     95     public static void main(String[] args) throws IOException {
     96         //1.创建一个服务器ServerSocket对象,和系统要指定的端口号
     97         ServerSocket server = new ServerSocket(8888);
     98         //2.使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象
     99 
    100         /*
    101             让服务器一直处于监听状态(死循环accept方法)
    102             有一个客户端上传文件,就保存一个文件
    103          */
    104         while(true){
    105             Socket socket = server.accept();
    106 
    107             /*
    108                 使用多线程技术,提高程序的效率
    109                 有一个客户端上传文件,就开启一个线程,完成文件的上传
    110              */
    111             new Thread(new Runnable() {
    112                 //完成文件的上传
    113                 @Override
    114                 public void run() {
    115                    try {
    116                        //3.使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
    117                        InputStream is = socket.getInputStream();
    118                        //4.判断d:\upload文件夹是否存在,不存在则创建
    119                        File file =  new File("d:\upload");
    120                        if(!file.exists()){
    121                            file.mkdirs();
    122                        }
    123 
    124                     /*
    125                         自定义一个文件的命名规则:防止同名的文件被覆盖
    126                         规则:域名+毫秒值+随机数
    127                      */
    128                        String fileName = "itcast"+System.currentTimeMillis()+new Random().nextInt(999999)+".jpg";
    129 
    130                        //5.创建一个本地字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
    131                        //FileOutputStream fos = new FileOutputStream(file+"\1.jpg");
    132                        FileOutputStream fos = new FileOutputStream(file+"\"+fileName);
    133                        //6.使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件
    134 
    135 
    136                        int len =0;
    137                        byte[] bytes = new byte[1024];
    138                        while((len = is.read(bytes))!=-1){
    139                            //7.使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
    140                            fos.write(bytes,0,len);
    141                        }
    142 
    143 
    144                        //8.使用Socket对象中的方法getOutputStream,获取到网络字节输出流OutputStream对象
    145                        //9.使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功"
    146                        socket.getOutputStream().write("上传成功".getBytes());
    147                        //10.释放资源(FileOutputStream,Socket,ServerSocket)
    148                        fos.close();
    149                        socket.close();
    150                    }catch (IOException e){
    151                        System.out.println(e);
    152                    }
    153                 }
    154             }).start();
    155 
    156 
    157         }
    158 
    159         //服务器就不用关闭
    160         //server.close();
    161     }
    162 }
    C/S文件上 件上传案例
     1 package com.itheima.demo04.BSTCP;
     2 
     3 import java.io.*;
     4 import java.net.ServerSocket;
     5 import java.net.Socket;
     6 
     7 /*
     8     创建BS版本TCP服务器
     9  */
    10 public class TCPServerThread {
    11     public static void main(String[] args) throws IOException {
    12         //创建一个服务器ServerSocket,和系统要指定的端口号
    13         ServerSocket server = new ServerSocket(8080);
    14         /*
    15             浏览器解析服务器回写的html页面,页面中如果有图片,那么浏览器就会单独的开启一个线程,读取服务器的图片
    16             我们就的让服务器一直处于监听状态,客户端请求一次,服务器就回写一次
    17          */
    18         while(true){
    19             //使用accept方法获取到请求的客户端对象(浏览器)
    20             Socket socket = server.accept();
    21             new Thread(new Runnable() {
    22                 @Override
    23                 public void run() {
    24                     try {
    25                         //使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
    26                         InputStream is = socket.getInputStream();
    27                         //使用网络字节输入流InputStream对象中的方法read读取客户端的请求信息
    28                         /*byte[] bytes = new byte[1024];
    29                         int len = 0;
    30                         while((len = is.read(bytes))!=-1){
    31                             System.out.println(new String(bytes,0,len));
    32                         }*/
    33 
    34                         //把is网络字节输入流对象,转换为字符缓冲输入流
    35                         BufferedReader br = new BufferedReader(new InputStreamReader(is));
    36                         //把客户端请求信息的第一行读取出来 GET /11_Net/web/index.html HTTP/1.1
    37                         String line = br.readLine();
    38                         System.out.println(line);
    39                         //把读取的信息进行切割,只要中间部分 /11_Net/web/index.html
    40                         String[] arr = line.split(" ");
    41                         //把路径前边的/去掉,进行截取 11_Net/web/index.html
    42                         String htmlpath =arr[1].substring(1);
    43 
    44                         //创建一个本地字节输入流,构造方法中绑定要读取的html路径
    45                         FileInputStream fis = new FileInputStream(htmlpath);
    46                         //使用Socket中的方法getOutputStream获取网络字节输出流OutputStream对象
    47                         OutputStream os = socket.getOutputStream();
    48 
    49                         // 写入HTTP协议响应头,固定写法
    50                         os.write("HTTP/1.1 200 OK
    ".getBytes());
    51                         os.write("Content-Type:text/html
    ".getBytes());
    52                         // 必须要写入空行,否则浏览器不解析
    53                         os.write("
    ".getBytes());
    54 
    55                         //一读一写复制文件,把服务读取的html文件回写到客户端
    56                         int len = 0;
    57                         byte[] bytes = new byte[1024];
    58                         while((len = fis.read(bytes))!=-1){
    59                             os.write(bytes,0,len);
    60                         }
    61 
    62                         //释放资源
    63                         fis.close();
    64                         socket.close();
    65                     }catch (IOException e){
    66                         e.printStackTrace();
    67                     }
    68                 }
    69             }).start();
    70         }
    71         //server.close();
    72     }
    73 }
    B/S浏览网页案例

     函数式接口

    函数式接口

    函数式接口:有且只有一个抽象方法的接口,称之为函数式接口。
    当然接口中可以包含其他的方法(默认,静态,私有)
    @FunctionalInterface注解
    作用:可以检测接口是否是一个函数式接口
        是:编译成功
        否:编译失败(接口中没有抽象方法抽象方法的个数多余1个)

    Lambda的特点:延迟加载
    Lambda的使用前提,必须存在函数式接口

    如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式。

    常用的函数式接口

    java.util.function.Supplier<T>接口仅包含一个无参的方法:T get()。用来获取一个泛型参数指定类型的对象数据。
    Supplier<T>接口被称之为生产型接口,指定接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据

    import java.util.function.Supplier;
    
    /*
    
     */
    public class Demo01Supplier {
        //定义一个方法,方法的参数传递Supplier<T>接口,泛型执行String,get方法就会返回一个String
        public static String getString(Supplier<String> sup){
            return sup.get();
        }
    
        public static void main(String[] args) {
            //调用getString方法,方法的参数Supplier是一个函数式接口,所以可以传递Lambda表达式
            String s = getString(()->{
                //生产一个字符串,并返回
                return "胡歌";
            });
            System.out.println(s);
    
            //优化Lambda表达式
            String s2 = getString(()->"胡歌");
            System.out.println(s2);
        }
    }
    ②
    
    import java.util.function.Supplier;
    
    /*
        练习:求数组元素最大值
            使用Supplier接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值。
            提示:接口的泛型请使用java.lang.Integer类。
     */
    public class Demo02Test {
       //定义一个方法,用于获取int类型数组中元素的最大值,方法的参数传递Supplier接口,泛型使用Integer
       public static int getMax(Supplier<Integer> sup){
           return sup.get();
       }
    
        public static void main(String[] args) {
            //定义一个int类型的数组,并赋值
            int[] arr = {100,0,-50,880,99,33,-30};
            //调用getMax方法,方法的参数Supplier是一个函数式接口,所以可以传递Lambda表达式
            int maxValue = getMax(()->{
                //获取数组的最大值,并返回
                //定义一个变量,把数组中的第一个元素赋值给该变量,记录数组中元素的最大值
                int max = arr[0];
                //遍历数组,获取数组中的其他元素
                for (int i : arr) {
                    //使用其他的元素和最大值比较
                    if(i>max){
                        //如果i大于max,则替换max作为最大值
                        max = i;
                    }
                }
                //返回最大值
                return max;
            });
            System.out.println("数组中元素的最大值是:"+maxValue);
        }
    }
    例子

     ②

    java.util.function.Consumer<T>接口则正好与Supplier接口相反, 它不是生产一个数据,而是消费一个数据,其数据类型由泛型决定。
     Consumer接口中包含抽象方法void accept(T t),意为消费一个指定泛型的数据。
    Consumer接口是一个消费型接口,泛型执行什么类型,就可以使用accept方法消费什么类型的数据至于具体怎么消费(使用),需要自定义(输出,计算....)

    Consumer接口的默认方法andThen
    作用:需要两个Consumer接口,可以把两个Consumer接口组合到一起,在对数据进行消费

    import java.util.function.Consumer;
    
    /*
    
     */
    public class Demo01Consumer {
        /*
            定义一个方法
            方法的参数传递一个字符串的姓名
            方法的参数传递Consumer接口,泛型使用String
            可以使用Consumer接口消费字符串的姓名
         */
        public static void method(String name, Consumer<String> con){
            con.accept(name);
        }
    
        public static void main(String[] args) {
            //调用method方法,传递字符串姓名,方法的另一个参数是Consumer接口,是一个函数式接口,所以可以传递Lambda表达式
            method("赵丽颖",(String name)->{
                //对传递的字符串进行消费
                //消费方式:直接输出字符串
                //System.out.println(name);
    
                //消费方式:把字符串进行反转输出
                String reName = new StringBuffer(name).reverse().toString();
                System.out.println(reName);
            });
        }
    }
    ②
    import java.util.function.Consumer;
    
    /*
    
       例如:
        Consumer<String> con1
        Consumer<String> con2
        String s = "hello";
        con1.accept(s);
        con2.accept(s);
        连接两个Consumer接口  再进行消费
        con1.andThen(con2).accept(s); 谁写前边谁先消费
    */
    public class Demo02AndThen {
        //定义一个方法,方法的参数传递一个字符串和两个Consumer接口,Consumer接口的泛型使用字符串
        public static void method(String s, Consumer<String> con1 ,Consumer<String> con2){
            //con1.accept(s);
            //con2.accept(s);
            //使用andThen方法,把两个Consumer接口连接到一起,在消费数据
            con1.andThen(con2).accept(s);//con1连接con2,先执行con1消费数据,在执行con2消费数据
        }
    
        public static void main(String[] args) {
            //调用method方法,传递一个字符串,两个Lambda表达式
            method("Hello",
                    (t)->{
                        //消费方式:把字符串转换为大写输出
                        System.out.println(t.toUpperCase());
                    },
                    (t)->{
                        //消费方式:把字符串转换为小写输出
                        System.out.println(t.toLowerCase());
                    });
        }
    }
    例子

    java.util.function.Predicate<T>接口
    作用:对某种数据类型的数据进行判断,结果返回一个boolean值
    Predicate接口中包含一个抽象方法: boolean test(T t):用来对指定数据类型数据进行判断的方法

    Predicate接口中有一个方法and,表示并且关系,也可以用于连接两个判断条件

    Predicate接口中有一个方法or,表示或者关系,也可以用于连接两个判断条件

    Predicate接口中有一个方法negate,也表示取反的意思

    import java.util.function.Predicate;
    
    /*
    
    */
    public class Demo01Predicate {
        /*
            定义一个方法
            参数传递一个String类型的字符串
            传递一个Predicate接口,泛型使用String
            使用Predicate中的方法test对字符串进行判断,并把判断的结果返回
         */
        public static boolean checkString(String s, Predicate<String> pre){
            return  pre.test(s);
        }
    
        public static void main(String[] args) {
            //定义一个字符串
            String s = "abcdef";
    
            //调用checkString方法对字符串进行校验,参数传递字符串和Lambda表达式
            /*boolean b = checkString(s,(String str)->{
                //对参数传递的字符串进行判断,判断字符串的长度是否大于5,并把判断的结果返回
                return str.length()>5;
            });*/
    
            //优化Lambda表达式
            boolean b = checkString(s,str->str.length()>5);
            System.out.println(b);
        }
    }
    ②
    
    import java.util.function.Predicate;
    
    /*
        逻辑表达式:可以连接多个判断的条件
        &&:与运算符,有false则false
        ||:或运算符,有true则true
        !:非(取反)运算符,非真则假,非假则真
    
        需求:判断一个字符串,有两个判断的条件
            1.判断字符串的长度是否大于5
            2.判断字符串中是否包含a
        两个条件必须同时满足,我们就可以使用&&运算符连接两个条件
    
    
        default Predicate<T> and(Predicate<? super T> other) {
            Objects.requireNonNull(other);
            return (t) -> this.test(t) && other.test(t);
        }
        方法内部的两个判断条件,也是使用&&运算符连接起来的
     */
    public class Demo02Predicate_and {
        /*
            定义一个方法,方法的参数,传递一个字符串
            传递两个Predicate接口
                一个用于判断字符串的长度是否大于5
                一个用于判断字符串中是否包含a
                两个条件必须同时满足
         */
        public static boolean checkString(String s, Predicate<String> pre1,Predicate<String> pre2){
            //return pre1.test(s) && pre2.test(s);
            return pre1.and(pre2).test(s);//等价于return pre1.test(s) && pre2.test(s);
        }
    
        public static void main(String[] args) {
            //定义一个字符串
            String s = "abcdef";
            //调用checkString方法,参数传递字符串和两个Lambda表达式
            boolean b = checkString(s,(String str)->{
                //判断字符串的长度是否大于5
                return str.length()>5;
            },(String str)->{
                //判断字符串中是否包含a
                return str.contains("a");
            });
            System.out.println(b);
        }
    }
    ③
    
    import java.util.function.Predicate;
    
    /*
         需求:判断一个字符串,有两个判断的条件
            1.判断字符串的长度是否大于5
            2.判断字符串中是否包含a
        满足一个条件即可,我们就可以使用||运算符连接两个条件
    
    
        default Predicate<T> or(Predicate<? super T> other) {
            Objects.requireNonNull(other);
            return (t) -> test(t) || other.test(t);
        }
        方法内部的两个判断条件,也是使用||运算符连接起来的
     */
    public class Demo03Predicate_or {
        /*
                定义一个方法,方法的参数,传递一个字符串
                传递两个Predicate接口
                    一个用于判断字符串的长度是否大于5
                    一个用于判断字符串中是否包含a
                    满足一个条件即可
             */
        public static boolean checkString(String s, Predicate<String> pre1, Predicate<String> pre2){
            //return pre1.test(s) || pre2.test(s);
            return  pre1.or(pre2).test(s);//等价于return pre1.test(s) || pre2.test(s);
        }
    
        public static void main(String[] args) {
            //定义一个字符串
            String s = "bc";
            //调用checkString方法,参数传递字符串和两个Lambda表达式
            boolean b = checkString(s,(String str)->{
                //判断字符串的长度是否大于5
                return str.length()>5;
            },(String str)->{
                //判断字符串中是否包含a
                return str.contains("a");
            });
            System.out.println(b);
        }
    }
    ④
    
    import java.util.function.Predicate;
    
    /*
        需求:判断一个字符串长度是否大于5
            如果字符串的长度大于5,那返回false
            如果字符串的长度不大于5,那么返回true
        所以我们可以使用取反符号!对判断的结果进行取反
    
    
        default Predicate<T> negate() {
            return (t) -> !test(t);
        }
     */
    public class Demo04Predicate_negate {
        /*
               定义一个方法,方法的参数,传递一个字符串
               使用Predicate接口判断字符串的长度是否大于5
        */
        public static boolean checkString(String s, Predicate<String> pre){
            //return !pre.test(s);
            return  pre.negate().test(s);//等效于return !pre.test(s);
        }
    
        public static void main(String[] args) {
            //定义一个字符串
            String s = "abc";
            //调用checkString方法,参数传递字符串和Lambda表达式
            boolean b = checkString(s,(String str)->{
                //判断字符串的长度是否大于5,并返回结果
                return str.length()>5;
            });
            System.out.println(b);
        }
    }
    ⑤
    
    import java.util.ArrayList;
    import java.util.function.Predicate;
    
    /*
        练习:集合信息筛选
        数组当中有多条“姓名+性别”的信息如下,
        String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" };
        请通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中,
        需要同时满足两个条件:
            1. 必须为女生;
            2. 姓名为4个字。
    
        分析:
            1.有两个判断条件,所以需要使用两个Predicate接口,对条件进行判断
            2.必须同时满足两个条件,所以可以使用and方法连接两个判断条件
     */
    public class Demo05Test {
        /*
            定义一个方法
            方法的参数传递一个包含人员信息的数组
            传递两个Predicate接口,用于对数组中的信息进行过滤
            把满足条件的信息存到ArrayList集合中并返回
         */
        public static ArrayList<String> filter(String[] arr,Predicate<String> pre1,Predicate<String> pre2){
            //定义一个ArrayList集合,存储过滤之后的信息
            ArrayList<String> list = new ArrayList<>();
            //遍历数组,获取数组中的每一条信息
            for (String s : arr) {
                //使用Predicate接口中的方法test对获取到的字符串进行判断
                boolean b = pre1.and(pre2).test(s);
                //对得到的布尔值进行判断
                if(b){
                    //条件成立,两个条件都满足,把信息存储到ArrayList集合中
                    list.add(s);
                }
            }
            //把集合返回
            return list;
        }
    
        public static void main(String[] args) {
            //定义一个储存字符串的数组
            String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" };
            //调用filter方法,传递字符串数组和两个Lambda表达式
            ArrayList<String> list = filter(array,(String s)->{
                //获取字符串中的性别,判断是否为女
               return s.split(",")[1].equals("女");
            },(String s)->{
                //获取字符串中的姓名,判断长度是否为4个字符
               return s.split(",")[0].length()==4;
            });
            //遍历集合
            for (String s : list) {
                System.out.println(s);
            }
        }
    }
    例子

     ④

    java.util.function.Function<T,R>接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。
    Function接口中最主要的抽象方法为:R apply(T t),根据类型T的参数获取类型R的结果。使用的场景例如:将String类型转换为Integer类型。

    Function接口中的默认方法andThen:用来进行组合操作

    import java.util.function.Function;
    
    /*
    
     */
    public class Demo01Function {
        /*
            定义一个方法
            方法的参数传递一个字符串类型的整数
            方法的参数传递一个Function接口,泛型使用<String,Integer>
            使用Function接口中的方法apply,把字符串类型的整数,转换为Integer类型的整数
         */
        public static void change(String s, Function<String,Integer> fun){
            //Integer in = fun.apply(s);
            int in = fun.apply(s);//自动拆箱 Integer->int
            System.out.println(in);
        }
    
        public static void main(String[] args) {
            //定义一个字符串类型的整数
            String s = "1234";
            //调用change方法,传递字符串类型的整数,和Lambda表达式
            change(s,(String str)->{
                //把字符串类型的整数,转换为Integer类型的整数返回
                return Integer.parseInt(str);
            });
            //优化Lambda
            change(s,str->Integer.parseInt(str));
        }
    }
    ②
    
    import java.util.function.Function;
    
    /*
    
    
        需求:
            把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转换为字符串
     */
    public class Demo02Function_andThen {
        /*
            定义一个方法
            参数串一个字符串类型的整数
            参数再传递两个Function接口
                一个泛型使用Function<String,Integer>
                一个泛型使用Function<Integer,String>
         */
        public static void change(String s, Function<String,Integer> fun1,Function<Integer,String> fun2){
            String ss = fun1.andThen(fun2).apply(s);
            System.out.println(ss);
        }
    
        public static void main(String[] args) {
            //定义一个字符串类型的整数
            String s = "123";
            //调用change方法,传递字符串和两个Lambda表达式
            change(s,(String str)->{
                //把字符串转换为整数+10
                return Integer.parseInt(str)+10;
            },(Integer i)->{
                //把整数转换为字符串
                return i+"";
            });
    
            //优化Lambda表达式
            change(s,str->Integer.parseInt(str)+10,i->i+"");
        }
    }
    ③
    import java.util.function.Function;
    
    /*
        练习:自定义函数模型拼接
        题目
        请使用Function进行函数模型的拼接,按照顺序需要执行的多个函数操作为:
            String str = "赵丽颖,20";
    
        分析:
        1. 将字符串截取数字年龄部分,得到字符串;
            Function<String,String> "赵丽颖,20"->"20"
        2. 将上一步的字符串转换成为int类型的数字;
            Function<String,Integer> "20"->20
        3. 将上一步的int数字累加100,得到结果int数字。
            Function<Integer,Integer> 20->120
     */
    public class Demo03Test {
        /*
            定义一个方法
            参数传递包含姓名和年龄的字符串
            参数再传递3个Function接口用于类型转换
         */
        public static int change(String s, Function<String,String> fun1,
                                 Function<String,Integer> fun2,Function<Integer,Integer> fun3){
            //使用andThen方法把三个转换组合到一起
            return fun1.andThen(fun2).andThen(fun3).apply(s);
        }
    
        public static void main(String[] args) {
            //定义一个字符串
            String str = "赵丽颖,20";
            //调用change方法,参数传递字符串和3个Lambda表达式
            int num = change(str,(String s)->{
                //"赵丽颖,20"->"20"
               return s.split(",")[1];
            },(String s)->{
                //"20"->20
                return Integer.parseInt(s);
            },(Integer i)->{
                //20->120
                return i+100;
            });
            System.out.println(num);
        }
    }
    例子

    Stream流

    使用Stream流的方式,遍历集合,对集合中的数据进行过滤
    关注的是做什么,而不是怎么做

    java.util.stream.Stream<T>是Java 8新加入的最常用的流接口。(这并不是一个函数式接口。)
    获取一个流非常简单,有以下几种常用的方式:
         所有的Collection集合都可以通过stream默认方法获取流;default Stream<E> stream​()
         Stream接口的静态方法of可以获取数组对应的流。 static <T> Stream<T> of​(T... values)参数是一个可变参数

    1、Stream流中的常用方法_filter:用于对Stream流中的数据进行过滤
      Stream<T> filter(Predicate<? super T> predicate);
    filter方法的参数Predicate是一个函数式接口,所以可以传递Lambda表达式,对数据进行过滤

    2、Stream流中的常用方法_count:用于统计Stream流中元素的个数
      long count();
    count方法是一个终结方法,返回值是一个long类型的整数
    所以不能再继续调用Stream流中的其他方法了

    3、Stream流中的常用方法_limit:用于截取流中的元素
    limit方法可以对流进行截取,只取用前n个。方法签名:
      Stream<T> limit(long maxSize);
    参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作
    limit方法是一个延迟方法,只是对流中的元素进行截取,返回的是一个新的流,所以可以继续调用Stream流中的其他方法

    4、Stream流中的常用方法_skip:用于跳过元素
    如果希望跳过前几个元素,可以使用skip方法获取一个截取之后的新流:
      Stream<T> skip(long n);
     如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。

    5、Stream流中的常用方法_concat:用于把流组合到一起

    如果有两个流,希望合并成为一个流,那么可以使用Stream接口的静态方法concat
    static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)

     方法引用

    1、通过对象名引用成员方法

    /*
        通过对象名引用成员方法
        使用前提是对象名是已经存在的,成员方法也是已经存在
        就可以使用对象名来引用成员方法
     */
    public class Demo01ObjectMethodReference {
        //定义一个方法,方法的参数传递Printable接口
        public static void printString(Printable p){
            p.print("Hello");
        }
    
        public static void main(String[] args) {
            //调用printString方法,方法的参数Printable是一个函数式接口,所以可以传递Lambda表达式
            printString((s)->{
                //创建MethodRerObject对象
                MethodRerObject obj = new MethodRerObject();
                //调用MethodRerObject对象中的成员方法printUpperCaseString,把字符串按照大写输出
                obj.printUpperCaseString(s);
            });
    
            /*
                使用方法引用优化Lambda
                对象是已经存在的MethodRerObject
                成员方法也是已经存在的printUpperCaseString
                所以我们可以使用对象名引用成员方法
             */
            //创建MethodRerObject对象
            MethodRerObject obj = new MethodRerObject();
            printString(obj::printUpperCaseString);
        }
    }
    ②
    public class MethodRerObject {
        //定义一个成员方法,传递字符串,把字符串按照大写输出
        public void printUpperCaseString(String str){
            System.out.println(str.toUpperCase());
        }
    }
    ③
    /*
        定义一个打印的函数式接口
     */
    @FunctionalInterface
    public interface Printable {
        //定义字符串的抽象方法
        void print(String s);
    }
    例子

    2、通过类名称引用静态方法

    /*
        通过类名引用静态成员方法
        类已经存在,静态成员方法也已经存在
        就可以通过类名直接引用静态成员方法
     */
    public class Demo01StaticMethodReference {
        //定义一个方法,方法的参数传递要计算绝对值的整数,和函数式接口Calcable
        public static int method(int number,Calcable c){
           return c.calsAbs(number);
        }
    
        public static void main(String[] args) {
            //调用method方法,传递计算绝对值得整数,和Lambda表达式
            int number = method(-10,(n)->{
                //对参数进行绝对值得计算并返回结果
                return Math.abs(n);
            });
            System.out.println(number);
    
            /*
                使用方法引用优化Lambda表达式
                Math类是存在的
                abs计算绝对值的静态方法也是已经存在的
                所以我们可以直接通过类名引用静态方法
             */
            int number2 = method(-10,Math::abs);
            System.out.println(number2);
        }
    }
    ②
    
    @FunctionalInterface
    public interface Calcable {
        //定义一个抽象方法,传递一个整数,对整数进行绝对值计算并返回
        int calsAbs(int number);
    }
    例子

    3、通过super引用成员方法

    /*
        定义子类
     */
    public class Man extends Human{
        //子类重写父类sayHello的方法
        @Override
        public void sayHello() {
            System.out.println("Hello 我是Man!");
        }
    
        //定义一个方法参数传递Greetable接口
        public void method(Greetable g){
            g.greet();
        }
    
        public void show(){
            //调用method方法,方法的参数Greetable是一个函数式接口,所以可以传递Lambda
            /*method(()->{
                //创建父类Human对象
                Human h = new Human();
                //调用父类的sayHello方法
                h.sayHello();
            });*/
    
            //因为有子父类关系,所以存在的一个关键字super,代表父类,所以我们可以直接使用super调用父类的成员方法
           /* method(()->{
                super.sayHello();
            });*/
    
          /*
               使用super引用类的成员方法
               super是已经存在的
               父类的成员方法sayHello也是已经存在的
               所以我们可以直接使用super引用父类的成员方法
           */
          method(super::sayHello);
        }
    
        public static void main(String[] args) {
            new Man().show();
        }
    }
    ②
    /*
        定义父类
     */
    public class Human {
        //定义一个sayHello的方法
        public void sayHello(){
            System.out.println("Hello 我是Human!");
        }
    }
    ③
    /*
        定义见面的函数式接口
     */
    @FunctionalInterface
    public interface Greetable {
        //定义一个见面的方法
        void greet();
    }
    例子

    4、通过this引用成员方法

    /*
        使用this引用本类的成员方法
     */
    public class Husband {
        //定义一个买房子的方法
        public void buyHouse(){
            System.out.println("北京二环内买一套四合院!");
        }
    
        //定义一个结婚的方法,参数传递Richable接口
        public void marry(Richable r){
            r.buy();
        }
    
        //定义一个非常高兴的方法
        public void soHappy(){
            //调用结婚的方法,方法的参数Richable是一个函数式接口,传递Lambda表达式
           /* marry(()->{
                //使用this.成员方法,调用本类买房子的方法
                this.buyHouse();
            });*/
    
            /*
                使用方法引用优化Lambda表达式
                this是已经存在的
                本类的成员方法buyHouse也是已经存在的
                所以我们可以直接使用this引用本类的成员方法buyHouse
             */
            marry(this::buyHouse);
        }
    
        public static void main(String[] args) {
            new Husband().soHappy();
        }
    }
    ②
    /*
        定义一个富有的函数式接口
     */
    @FunctionalInterface
    public interface Richable {
        //定义一个想买什么就买什么的方法
        void buy();
    }
    例子

    5、类的构造器引用

    /*
        类的构造器(构造方法)引用
     */
    public class Demo {
        //定义一个方法,参数传递姓名和PersonBuilder接口,方法中通过姓名创建Person对象
        public static void printName(String name,PersonBuilder pb){
            Person person = pb.builderPerson(name);
            System.out.println(person.getName());
        }
    
        public static void main(String[] args) {
            //调用printName方法,方法的参数PersonBuilder接口是一个函数式接口,可以传递Lambda
            printName("迪丽热巴",(String name)->{
                return new Person(name);
            });
    
            /*
                使用方法引用优化Lambda表达式
                构造方法new Person(String name) 已知
                创建对象已知 new
                就可以使用Person引用new创建对象
             */
            printName("古力娜扎",Person::new);//使用Person类的带参构造方法,通过传递的姓名创建对象
        }
    }
    ②
    
    public class Person {
        private String name;
    
        public Person() {
        }
    
        public Person(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    ③
    /*
        定义一个创建Person对象的函数式接口
     */
    @FunctionalInterface
    public interface PersonBuilder {
        //定义一个方法,根据传递的姓名,创建Person对象返回
        Person builderPerson(String name);
    }
    例子

    6、数组的构造器引用

    import java.util.Arrays;
    
    /*
        数组的构造器引用
     */
    public class Demo {
        /*
            定义一个方法
            方法的参数传递创建数组的长度和ArrayBuilder接口
            方法内部根据传递的长度使用ArrayBuilder中的方法创建数组并返回
         */
        public static int[] createArray(int length, ArrayBuilder ab){
            return  ab.builderArray(length);
        }
    
        public static void main(String[] args) {
            //调用createArray方法,传递数组的长度和Lambda表达式
            int[] arr1 = createArray(10,(len)->{
                //根据数组的长度,创建数组并返回
                return new int[len];
            });
            System.out.println(arr1.length);//10
    
            /*
                使用方法引用优化Lambda表达式
                已知创建的就是int[]数组
                数组的长度也是已知的
                就可以使用方法引用
                int[]引用new,根据参数传递的长度来创建数组
             */
            int[] arr2 =createArray(10,int[]::new);
            System.out.println(Arrays.toString(arr2));
            System.out.println(arr2.length);//10
        }
    }
    ②
    /*
        定义一个创建数组的函数式接口
     */
    @FunctionalInterface
    public interface ArrayBuilder {
        //定义一个创建int类型数组的方法,参数传递数组的长度,返回创建好的int类型数组
        int[] builderArray(int length);
    }
    例子

  • 相关阅读:
    免费的asp.net空间
    利用instr()函数防止SQL注入攻击
    Dreamweaver中sql注入式攻击的防范
    编写通用的ASP防SQL注入攻击程序
    跨站式SQL注入技巧
    防范Sql注入式攻击
    PHP与SQL注入攻击
    SQL注入攻击的原理及其防范措施
    SQL注入法攻击一日通
    SQL Server应用程序中的高级SQL注入
  • 原文地址:https://www.cnblogs.com/wz46265381/p/13935694.html
Copyright © 2011-2022 走看看