JavaSE基础知识(个人总结)

    声明: 1. 本文为我的个人复习总结, 并那种从零基础开始普及知识 内容详细全面, 言辞官方的文章
              2. 由于是个人总结, 所以用最精简的话语来写文章
              3. 若有错误不当之处, 请指出

基础:

杂记:

  • JRE=JVM+常用类库, JDK=JRE+诸多工具(如javac, jstack, jmap, jconsole)
  • 静态(类的)/非静态(对象的,实例的), 成员属性, 局部变量
  • 说法是静态属性不属于任何一个实例所拥有, 但其实应该理解成它对于所有对象而言是公有的, 任何一个对象改变了它, 那可以看到别的对象的此属性也被改变了
  • 静态的东西, 在javac编译后都放在<cinit>中执行初始化操作; 非静态的东西被放到<init>中执行初始化操作
  • 一个类的生命周期中,静态的东西只能加载时被初始化一次,即静态代码块只能第一次执行
  • 静态的属性生命周期很长, 因为在类卸载时, 它们才会跟着被销毁
  • +=后面的部分实质是被括号括起来的, 而且自带强转效果, 强转为前面部分的类型
  • boolean存储占1个字节, 因为它的底层是byte
  • 浮点数的计算, 都不精确, 1-0.1并不等于0.9
  • switch的类型不可以是 float, double, long, 可以是其他基本类型 & String类型 & 枚举类型
  • 代码块执行顺序:父类静态—>子类静态块—>父类代码—>父类构造器—>子类代码块—>子类构造器
  • 接口继承接口, 用的是extends
  • finalize,main,run如果手动调用的话就是普通方法

基本类型和包装类型:

区别:

  • 包装类型可以有null, 而基本类型不可以

  • 包装类型可以用于泛型, 而基本类型不可以

  • 包装类型占用更多内存, 而基本类型占用内存少

包装类型的cache[], 得在你不主动new的时候才能派上用场

Float和Double没有cache[], 因为小数太多没法缓存

值传递和引用传递:

值传递: 传值

引用传递: 传地址

Java里只有值传递, 基本类型作为参数被传递时肯定是值传递(字面量的拷贝);引用类型作为参数被传递时也是值传递,只不过"值"为 堆中的地址值

方法间传参手段:

  1. 不跨类

    • 直接使用方法形参
    • 设置成本类的成员属性
  2. 跨类

    • 直接进行方法传参
    • 设置成第三方类的一个成员属性
    • 如果不跨线程可以考虑ThreadLocal进行存取

JavaBean:

  1. 有无参构造器: 无参构造器最好显示写上, 因为一旦有了其他构造器, javac就不会自动生成它,而子类构造器默认调super( ), 那就找不到相应构造器了

  2. 有Getter/Setter方法: 在json解析或spring装在bean或springmvc或jsp解析时, 并不是直接调用属性, 而是调用其Getter/Setter方法, 如果没有这些可能报错

  3. 属性不能使用基本数据类型;

    因为如int score时,score为0可能实际数据为0,也可能是因为赋的默认值才为0;

    而如果是Integer时,默认值为null, 就可以和实际数据0区分开了

常见的Object方法:

  1. Object clone( ) 浅拷贝

  2. boolean equals(Object)

  3. int hashCode( )

  4. void finalize( )

  5. Class getClass( )

  6. void wait( )

  7. void notify( ) 唤醒等待在该对象的Monitor上的一个线程

  8. void notifyAll( ) 唤醒等待在该对象的Monitor上的全部线程

  9. String toString( )

浅拷贝&深拷贝:

浅拷贝:

​ 对基本数据类型进行值复制,对引用数据类型复制一个引用指向原始引用的对象,

​ 结果是复制的引用和原始引用指向同一个对象(即只拷贝一层)

实现: 目标类先实现实现Cloneable空接口, 调用方再调用目标类的clone方法(继承自Object类)进行浅拷贝

深拷贝:

​ 对基本数据类型进行值复制,对引用数据类型,创建一个新的对象, 并复制其内容,

​ 结果是复制的引用和原始引用指向不同对象(即递归拷贝多层, 直到最后一层为止)

实现: 使用IO对象流(ObjectInputStream, ObjectOutputStream)进行拷贝

final:

  • 修饰变量表示使之为常量, 修饰方法表示此方法不能被重写, 修饰类说明此类不能被继承
  • 若final修饰引用, 那么引用不可变, 但引用指向的对象是可变的

finally:

  • finally里最好要别写return, 如果非写return, 那么当是基本数据类型或是不可变的String类型时, 是不生效的; 当它是引用类型时, 是可以生效的

finalize:

  • 不重写finalize时, GC时会直接调用finalize( )方法来释放内存

  • 重写finalize时, 此类的实例会被包装为Finalizer类的实例, 它们被unfinalized链表串起来;

    当第一次GC时, 只是把它迁移到引用队列里, 并不进行释放内存;

    得等到第二次GC时,才真正调用finalize方法

    如果重写代码里并不是释放内存得逻辑, 比如将这个垃圾对象再赋值给别的引用, 相当于救了这个对象一命使之不再成为垃圾

不要手动调用System.gc( ), GC太伤性能; 另外GC具体什么时候执行 是JVM决定的, 我们决定不了, 只是"建议"一下

循环:

  • foreach循环, 如果是数组则会被javac编译成fori循环; 如果是集合则被编译成迭代器遍历
  • for( xx : 集合/数组), 这个xx实际上是对集合/数组元素的浅拷贝; 即当它是基本数据类型或是不可变的String类型时, 对xx的修改是不影响原数据的; 当是普通的引用类型时, 是可以影响的
  • 循环嵌套时, 在for外循环上方mark: 一个标记, 然后循环里break mark; 跳出指定的外循环, 否则只能跳出一层循环

Comparable VS Comparator:

Comparable 是实体类本身具有可比较性, compareTo方法写在实体类内部

Comparator 是第三方比较器, 单独是一个类, 用compare方法比较两个元素

若想升序排序, 则应该返回a-b<0即负数, 证明a<b; 返回值>0代表a>b; 返回值==0代表a==b

接口:

  • 方法均必须被public abstract修饰, 可以不显示写出来, 但是不能修改它

  • 属性均必须被public final修饰, 可以不显示写出来, 但是不能修改它

  • 接口中不能写构造器, 因为构造器就是用来初始化的, 而它的属性都是常量了得提前赋值, 没必要再要构造器初始化了

  • java8中, 接口里也可以有static方法和default方法, 这种是非抽象的方法; 目的是让你万不得已才这样做, 而不是建议你直接这样做;

    static方法不可被重写, 但可重名

    default方法可被重写, 重写时不可带default关键字

  • 接口是规范

抽象类:

  • 抽象类也有构造方法, 是给子类用的
  • 抽象类可以不写抽象方法, 但抽象方法只能出现在抽象类或接口中
  • 抽象类不能被final修饰, 因为抽象类必须要有子类才有意义
  • 抽象类用了模板方法设计模式, 给子类提供个模板, 剩下的功能由子类自己实现

继承抽象类用extends, 实现接口用implement;

继承只有单继承, 实现有多实现; extends写前面, implement写后面

重写:

想要重写父类方法, 必须遵循3个规则:

  1. 抛出的异常范围≤父类
  2. 返回值≤父类
  3. 方法权限修饰符≥父类

例题: 这个会报错的, 因为"方法权限修饰符≥父类"这个规则, 子类的默认权限<父接口隐藏的public权限

public interface Person{
    void eat( );
}

public class Student extends Person{
    void eat( ){
        System.out.println( );
    }
}

证明有些方法不能被重写:

  1. static方法不能被重写, 可以发现@Override检查后报错, 说明只是同名字

  2. private方法不能被重写, 可以发现@Override检查后报错, 说明只是同名字

  3. final方法不能被重写, 而且不能同名字

重载:

重载方法是编译型多态, 重写方法是运行时多态

方法返回值不作为判断重载的依据, 重载是方法名一样, 但参数不一样(参数个数, 参数类型, 参数顺序)

内部类:

有 静态内部类, 非静态内部类, 匿名内部类, 局部(方法)内部类

  • 非静态内部类里不能使用static关键字

  • 方法内部类使用的外部方法里的变量必须为final常量(可以不显示写出)。[方法内的lambad表达式也一样]

    这是为了改变它的生命周期, 万一这个方法执行完毕后该变量又没有逃逸出去被别人使用, 它就被回收了, 而方法内部类可能依旧需要使用它

    final怎样改变它(外部方法里的变量)的生命周期的?

     final修饰的常量引用在方法区, 指向的对象在堆中; 
    
     方法区的常量引用不会被因此方法调用结束而被回收, 且final常量是可以作为GcRoots对象的, 那么所指的堆中对象就不会被回收, 
    
     从而改变了生命周期
    

    如果要使用user对象, 常用套路:

    • final User finalUser = user, 然后使用finalUser

    如果非要修改user对象, 常用套路:

    • final User[] userArr=new user[]{user}, 然后使用userArr[0]即可; userArr是final的表示此引用不能指向别的对象, 但此对象中的数组元素是可变的
    • 如果同时涉及lambda多线程, 那么就使用原子类, 既保证安全,又保证了final, 原子类引用不能指向别的对象, 但对象内的数据是可变的

动态绑定:

调用方法是动态绑定的(看实例的真正类型是啥, 如果是多态,则按重写调用, 对于同名字的那种视而不见),
而调用属性是静态绑定的(看类的类型是啥)

例题:

  1.  public class Demo01 {
         public static void main(String[] args) {
             A a = new B( );
             // 输出结果是: 20
             System.out.println(a.getResult( ));
         }
     }
     class A {
         private int i = 10;
         public int getResult( ) {
             return i + 10;
         }
     }
     class B extends A {
         private int i = 20;
     }
    
  2.  public class Demo01 {
         public static void main(String[] args) {
             A a = new B( );
             // 输出结果是: 20
             System.out.println(a.getResult( ));
         }
     }
     class A {
         private int i = 10;
         public int getResult( ) {
             return i + 10;
         }
     }
     class B extends A {
         private int i = 20;
         public int getResult( ) {
             return i + 20;
         }
     }
    
  3.  public class Demo01 {
         public static void main(String[] args) {
             A a = new B( );
             // 输出结果是: 20
             // 不是权限不足才被迫调用父类中该方法, 
             // 而是对于那种非重写但同名的方法, 在多态形式调用时,不会理会它,只会理父类中的该方法
             System.out.println(a.getResult( ));
         }
     }
     class A {
         private int i = 10;
         // private权限的getI
         private int getI( ) {
             return i;
         }
         public int getResult( ) {
             return getI( ) + 10;
         }
     }
     class B extends A {
         private int i = 20;
         // private权限的getI, 并非重写, private方法不能被重写, 只是同名字
         public int getI( ) {
             return i;
         }
     }
    
  4.  public class Demo01 {
         public static void main(String[] args) {
             A a = new B( );
             // 输出结果是: 1
             // 对于那种非重写但同名的方法, 在多态形式调用时,不会理会它,只会理父类中的该方法
             System.out.println(a.getI( ));
         }
     }
     
     class A {
         // private权限的getI
         public static int getI( ) {
             return 1;
         }
     }
     
     class B extends A {
         // private权限的getI, 并非重写, private方法不能被重写, 只是同名字
         public static int getI( ) {
             return 2;
         }
     }
    

封装, 继承, 多态

  1. 封装

    即包装, 把客观事物封装成类, 有利于解耦

    权限封装, get/set 可以写一些数据校验逻辑

    参数封装成map, 既美观又拓展性更强, 加参数只需要执行map.put, 而不用修改方法签名

    对功能的封装,封装成一个方法,或对方法进一步封装以加强其功能

    框架的封装

  2. 继承

    避免写重复性的代码, 继承父类后可以拥有父类的一部分东西

    构造器不能被继承

    private可以继承, 但子类无权使用

  3. 多态

    不同的实现类有不同的功能, 有利于功能的拓展

    多态发生的条件: 继承+重写+向上转型

equals相关:

==:

  • 如果==比较的是基本数据类型,那么比较的是两个值是否相等

  • 如果==是比较的两个对象,那么比较的是对象的物理地址是否相等, 即两个对象是否指向了同一块内存

equals:

如果不重写, 则等效于==

很多时候我们想比较的是两个对象的 属性内容是否相等

equals方法要具有以下特性:

  • 自反性。对于任意不为null的引用值x,x.equals(x)一定是true。
  • 对称性。对于任意不为null的引用值x和y,当且仅当x.equals(y)是true时,y.equals(x)也是true。
  • 传递性。对于任意不为null的引用值x、y和z,如果x.equals(y)是true,同时y.equals(z)是true,那么x.equals(z)一定是true。
  • 一致性。对于任意不为null的引用值x和y,如果用于equals比较的对象信息没有被修改的话,多次调用时x.equals(y)要么一致地返回true要么一致地返回false。
  • 对于任意不为null的引用值x,x.equals(null)返回false。

hashcode( ):

hashcode是获取散列哈希码, 默认情况是追求不同对象的哈希码不一样, 有极少情况会两个不同对象的哈希码相等(比如Tom(12岁,猫)可能恰好和Jerry(10岁, 鼠))的哈希值是相等的, 但equals不相等

在HashMap集合中重写equals时, 也需要重写hashcode, 如果只重写了equals方法而不重写hashcode的方法,会造成hashcode的值不同,而equals( )方法判断出来的结果为true。造成相同的对象散列到不同的位置而造成对象的不能覆盖的问题。

hashcode的作用:

  1. 功能上:散列时找到对象所属的坑位, 应该落到哪个数组的位置
  2. 性能上:找到坑位后, 需要对该数组下标位置里已有key进行比较是否相等。直接进行equals代价太大, 因为equals需要比较每个属性, 而hashcode比较起来比较快捷, 可以过滤掉一些一定不相等的key

hashcode相等后,还需要再equals判断:

因为hashcode不相等时那么equals也一定不相等;而hashcode相等时,那equals未必相等

String相关:

String是不可变对象, 底层是private final char value[]; 若要修改, 实质上是返回一个新的对象

String经常被使用, 所以为了复用性, 用字符串常量池进行了池化

不可变&字符串常量池 的好处:

  1. 对象的复用

  2. String的对象不可变, 则hashcode不可变, 可以进行缓存, 不需要重新计算; 所以推荐Map的key为String类型, 寻找值更快

  3. 使多线程安全

    既然是不可变, 那就自然没有多线程安全问题了

  4. 保证其他安全问题

    保护 网络连接地址URL,文件路径path 不被黑客修改

StringBuilder是线程不安全的, StringBuffer是线程安全的

但一般StringBuilder作为局部变量且不暴漏给其他线程, 所以常用些

StringBuilder和StringBuffer都是修改this对象, 实现了链式调用

常量池中的字符串仅是符号,第一次用到时才变为实际的对象

字符串常量池里寸存的是字面量对象

字符串变量拼接的原理是StringBuilder, 即最后toString的时候创建了新字符串对象;

newString(“xxx”)产生了几个对象?

1个或2个

  • 当常量池里已经有了"xxx"时, 就只在堆里创建对象就行了

  • 当常量池里没有"xxx"时, 就现在常量池里创建, 然后再在堆里创建一个

    注意这个不是意为着手动调用.intern就没意义了:

    • 当new String(“xxx”)后, 常量池里一定会有"xxx"了, .intern确实没意义了
    • 但对于字符串拼接 new String(“xxx”)+ new String(“yyy”), 那么"xxxyyy"并不会自动放到常量池, 这时可以手动调用.intern

对于直接字面量相加的字符串, 会被优化为没有拼接这一步

调用 intern( )方法,主动将串池中还没有的字符串对象放入串池(运行时常量池)

  • 1.8 会将这个字符串对象尝试放入串池, 如果有则并不会放入, 如果没有则放入串池, 最终会返回串池中的对象
  • 1.6 会将这个字符串对象尝试放入串池, 如果有则并不会放入, 如果没有会把此对象复制一份放入串池, 最终会返回串池中的对象

所以在s1.intern( ) 没有成功把s1放入串池时, 1.8和1.6做法无差异, 所以s1==s1.intern( );

​ 在s1.intern( ) 成功把s1放入串池时, 1.8和1.6做法有差异(1.8是移动, 1.6是复制), 所以s1!=s1.intern( )

习题:

// jdk1.8时

String s1 = "a"; 
String s2 = "b"; 
String s3 = "a" + "b"; 
String s4 = s1 + s2; 
String s5 = "ab"; 
String s6 = s4.intern( ); 

System.out.println(s3 == s4); 	// false
System.out.println(s3 == s5); 	// true
System.out.println(s3 == s6); 	// true

String x1 = "cd"; 
String x2 = new String("c") + new String("d"); 
String x3=x2.intern( ); 
System.out.println(x1 == x2);	// false
System.out.println(x2 == x3);	// false


String y2 = new String("e") + new String("f"); 
String y3=y2.intern( ); 
String y1 = "ef"; 
System.out.println(y1 == y2);	// true
System.out.println(y2 == y3);	// true
// jdk1.6时

String s1 = "a"; 
String s2 = "b"; 
String s3 = "a" + "b"; 
String s4 = s1 + s2; 
String s5 = "ab"; 
String s6 = s4.intern( ); 

System.out.println(s3 == s4); 	// false
System.out.println(s3 == s5); 	// true
System.out.println(s3 == s6); 	// true

String x1 = "cd"; 
String x2 = new String("c") + new String("d"); 
String x3=x2.intern( ); 
System.out.println(x1 == x2);	// false
System.out.println(x2 == x3);	// false


String y2 = new String("e") + new String("f"); 
String y3=y2.intern( ); 
String y1 = "ef"; 
System.out.println(y1 == y2);	// false
System.out.println(y2 == y3);	// false

String长度的最大值:

  • 在编译期: .class文件的Constant_Utf8_info表 用一个无符号短整型变量记录字符串占用字节大小,

    ​ 所以最大字节为2^16-1=65535, 而最大长度是2^16-2=65534

  • 在运行期: 字符串长度最大值就变成了数组长度的最大值, 数组的对象头里有4个字节记录数组长度, 故最大长度为2^31-1

异常 & 错误

都是Throwable的子类

错误是虚拟机自身造成的, 是程序自身不可以处理的, 比如StackOverFlowError, OutOfMemoryError

异常是程序逻辑不对造成的, 是程序自身可以处理的

  • 异常=编译时异常(大部分是IOException)+运行时异常

  • 见过的异常:

    1. ConcurrentModificationException 并发修改异常,

      比如遍历ArrayList集合期间删除了元素 导致modCount != expectedModCount

      这里的并发并非是线程并发, 而是指一边遍历一遍修改

    2. IllegalAccessException 非法访问权限异常

      如调用别类中的private方法

    3. InstantiationException 实例化异常

      Class的newInstance( )方法创建某个类的实例, 而该类却没有相应构造器

    4. ClassCastException 类型转换异常

    5. NumberFormatException 数据格式异常

try with source 即省略了finally, 把待释放的资源放在try后的( )里, 这里的变量实质上是final的(可以不显示写出来)

try catch finally中, catch 不是必要的

日期类:

SimpleDateFormat是非线程安全的, 因为它内部的calendar属性被暴漏出来, 临界区发生了竞态条件

应该使用JDK8新出来的LocalDateTime类

枚举:

应用场景:

  1. 星期: Monday, Tuesday…
  2. 性别: Man、Woman
  3. 季节: Spring、Summer、Autumn、Winter

枚举的本质是有一个final static 的引用属性, 指向了自己的对象

什么时候用枚举?

  1. 类的对象是确定的有限个数
  2. 当需要定义一组常量时
  3. 如果枚举类中只有一个对象,则可以实现单例模式

特点:

  1. 不能被继承
  2. 不能在外部被new 创建对象
  3. 多个枚举成员用,隔开, 后面什么都没有时, 可以省略;结束符

枚举类的常用方法:

返回值方法描述
Stringname( )获取枚举成员的名称
static TvalueOf(Class enumType, String name)获取指定枚举成员名称和类型的枚举成员
String[]values( )获取枚举成员的所有值
intcompareTo(E o)比较此枚举与指定对象的 顺序
intordinal( )获取枚举成员的序数(从0开始)
Class<E>getDeclaringClass( )获取枚举成员的类对象

泛型:

泛型就是将类型参数化

泛型的优点:

  1. 编译时检查类型是否合法
  2. 省去了强制类型转换的麻烦
  3. 使代码可读性更高

泛型在运行期会被擦除, 都变成Object类型, 这些方法调用时会有checkcast指令去强制类型转换

泛型方法:

泛型 T 是方法独有的, 与所在类上定义的泛型无关

public <T> void fun(T t){

}

静态方法 不可以访问 类上定义的泛型(因为得new 才能传泛型, 而static类初始化阶段 还没有new对象初始化发生),

但可以使用 泛型方法上定义的泛型

通配符 ? 类型:

  1. List<? extends T> 泛型上限是T

  2. List <? super T> 泛型下限是T

  3. List <?> 不确定类型, 可以持有任意类型

错误使用:

  1. ArrayList<Object> list=new ArrayList<Integer>( );
  2. List<Object> list=new ArrayList<Integer>( );

而List<Object> list = new ArrayList<Object>( ); 是正确的, <>里面的类型前后必须得一致才行

IO流:

流的种类:

节点流有: 字节流(读写任何数据格式), 字符流(读写文本格式)

包装流(处理流)有: 缓冲流, 转换流, 数据流, 对象流

形式有: 字节数组形式/文件形式

包装流的反复包装, 使用了装饰者设计模式

转换流(字节转字符):

InputStreamReader 字节输入流 转 字符输入流

OutputStreamWrite 字节输出流 转 字符输出流

序列化:

序列化是把内存中的数据转换成字节流等形式, 使数据能落盘存储网络传输,

分布式节点中要传输数据, 数据得先序列化才能传输

序列化可以打破单例, 所以需要处理一下:

// 待序列化的类写上此方法, 反序列化时依旧保持单实例
protected Object readResolve( ) {
     return instance;
}

Serializable:

  • 需要继承Serializable空接口, 最好要加上public static final long serialVersionUID属性(相当于版本号)

  • 如果没有这个属性, 那么一旦类被修改了, 反序列化时就会报错(相当于每次反序列化会生成新的版本号)

    serialVersionUID 用来表明类的不同版本间的兼容性, 值是多少无所谓, 只要不变就行

  • transient修饰的属性 和 静态属性 是不会被序列化的

serialVersionUID也被static修饰, 为什么serialVersionUID会被序列化?

其实原先的serialVersionUID属性并没有被序列化, JVM在序列化对象时会自动生成一个serialVersionUID; 然后将原先的serialVersionUID属性值 赋值 给自动生成的serialVersionUID

Externalizable:

是Serializable的子接口

重写writeExternal 和 readExternal两个方法, 自己指明要序列化哪些属性

   public void writeExternal(ObjectOutput out) throws IOException {       out.writeObject(name);   }   public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {       name = (String) in.readObject( );   }

注解:

是一种可以被程序读取的标记, 可以用来作配置

用@Interface来声明, 属性长得像方法, 使用时, 只有一个value属性的话, value可以省略

注解的传递: 注解A修饰了注解B, 那么注解B去修饰注解C时, 注解C也会被A修饰

元注解:

  1. @Target 声明注解的使用范围

    public enum ElementType {    TYPE, // 类、接口、枚举类    FIELD, // 成员变量(包括:枚举常量)    METHOD, // 成员方法    PARAMETER, // 方法参数    CONSTRUCTOR, // 构造方法    LOCAL_VARIABLE, // 局部变量    ANNOTATION_TYPE, // 注解类    PACKAGE, // 可用于修饰:包    TYPE_PARAMETER, // 类型参数,JDK 1.8 新增    TYPE_USE // 使用类型的任何地方,JDK 1.8 新增}
    
  2. @Retention 声明注解可以活到什么时候

    public enum RetentionPolicy {    SOURCE,    // 源文件保留    CLASS,       // 编译期保留,默认值    RUNTIME   // 运行期保留,可通过反射去获取注解信息}
    
  3. @Inherited 表明 被注解修饰的父类,其子类会自动继承该注解

  4. @Documnent 表明注解可以被javadoc提取生成说明文档

JDK8中注解的新特性:

  1. 新增可用于类型的注解

    ElementType.TYPE_PARAMETER

    ElementType.TYPE_USE

  2. 可重复的注解

    相当于在原先注解上加个重复注解 表示数组容器, 原先注解被多次使用时 就会被放到此数组中

    示例:

    image-20211108124256351

反射:

  • 对于任意一个类, 能获得到它相关的类信息(属性, 方法, 被修饰的注解)
  • 对于任意一个对象,都能够调用它的任意一个属性和方法
  • 以前是对象调方法, 反射是方法调对象, 但这个方法比较特殊, 是java.lang.Method类

反射可以用来突破权限, 但不建议直接这样, 除非你万不得已

函数式编程:

lambad表达式, 即函数也进行匿名, 把函数看作对象; 当接口里只有一个方法时, 可以这样做

方法引用(类调用 反而要比对象调用 支持的更多些), 传入getter方法时常常这样做, 比如Student::getScore

函数式接口:

被@FunctionInterface修饰

  1. Function——函数型接口**(有输入、有返回)**

  2. Predicate——断定型接口**(有输入,返回boolean)**

  3. Consumer——消费型接口**(有输入,无返回)**

  4. Supplier——供给型接口**(无输入,有返回值)**

BiConsumer是有两个参数输入

StreamAPI:

StreamAPI是将集合变为Stream流, 对于Stream流的操作并不会影响原集合, 有丰富的API

如map, flatMap, filter, reduce, sorted

最后通过 .collect(Collectors.toXXX( )) 转为集合

  • 2
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值