参考视频https://www.bilibili.com/video/av80585971


写本篇博客的目的旨在个人复习巩固知识,方便自己在线阅览。

个人差缺补漏用博客


1.内部类


1.1.概述


内部类:就是在一个类中定义一个类。

举例:在一个类 A 的内部定义一个类 B,类 B 就被称为内部类。


内部类的访问特点

  • 内部类可以直接访问外部类的成员,包括私有成员
  • 外部类要访问内部类的成员,必须要创建对象

格式

public class 类名 {修饰符 class 类名{}
}

范例

public class Outer {private int num = 10;public class Inner {public void show() {//内部类可以直接访问外部类的成员,包括私有成员System.out.println(num);}}public void method() {//外部类要访问内部类的成员,必须要创建对象Inner inner = new Inner();inner.show();}
}

1.2.成员内部类


按照内部类在类中定义的位置不同,可以分为如下两种形式

  • 在类的成员位置:成员内部类
  • 在类的局部为止:局部内部类

成员内部类,外界如何创建对象使用?

  • 格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
  • 范例:Outer.Inner i = new Outer().new Inner();

但是使用 private 修饰内部类的话,上面的方法也访问不了了。

此时只能通过该类内部的方法来创建对象,进而让外界调用。


public class Outer {private int num = 10;//一般在成员内定义类,是不希望别人看到的,所以其实不会用到 public 修饰符/*public class Inner{public void show(){System.out.println(num);}}*/private class Inner {public void show() {System.out.println(num);}}public void method() {Inner i = new Inner();i.show();}
}
public class InnerDemo {public static void main(String[] args) {//创建内部类对象,并且调用方法//Inner i = new Inner();//报错//使用 private 修饰 Inner 类时(外界访问不了了),下面的代码也无法创建新的 Inner 对象了。//Outer.Inner i = new Outer().new Inner();Outer o = new Outer();o.method();}
}

1.3.局部内部类


局部内部类是在方法中定义的类,所以外界是无法直接调用的,需要在方法内部创建对象并使用。

该类可以直接访问外部类的成员,也可以访问方法内的局部变量。


public class Outer {private int num_1 = 10;public void method() {int num_2 = 20;class Inner {public void show() {System.out.println(num_1);System.out.println(num_2);}}Inner i = new Inner();i.show();}
}
/*** 测试类*/
public class OuterDemo {public static void main(String[] args) {Outer o = new Outer();o.method();}
}

1.4.匿名内部类


匿名内部类是一种特殊的局部内部类。

前提:存在一个类或者接口,这里的类可以是具体类,可以是抽象类。

格式

new 类名或者接口名(){重写方法;
};

其本质是一个继承了该类或者实现了该接口的子类匿名对象。


public interface Inter {void show();
}
public class Outer {public void method_1(){new Inter(){@Overridepublic void show() {System.out.println("匿名内部类-1");}}.show();//此处即为 Inter 对象调用了 show() 方法}public void method_2(){//既然 Inter 是一个对象,这里我们也可以将其看做 Inter 的一个实现类对象//那么便可以按照多态的形式来赋值给 Inter 接口Inter i = new Inter() {@Overridepublic void show() {System.out.println("匿名内部类-2");}};i.show();}
}
// 测试类
public class OuterDemo {public static void main(String[] args) {Outer o = new Outer();o.method();}
}

1.5.匿名内部类在开发中的使用


这里用一个案例来说明匿名内部类的用途

/*** 跳高接口*/
public interface Jumping {void jump();
}
/*** 接口操作类,里面有一个方法,方法的参数是接口名*/
public class JumpingOperator {public void method(Jumping j){j.jump();}
}
/*** 测试类*/
public class JumpingDemo {public static void main(String[] args) {//需求:创建接口操作类的对象,调用 method 方法}
}

实现该案例,需要在 JumpingDemo 类编写代码

JumpingOperator jo = new JumpingOperator();//建接口操作类的对象jo.method(XXX);//调用 method 方法

此处需要填写的就是 Jumping 接口。

但是接口类是没有办法直接创建实例的。


按照之前多态的方式则是参照多态的方式,通过实现类对象实例化。

/*** 接口实现类*/
public class Cat implements Jumping {@Overridepublic void jump() {System.out.println("猫跳高");}
}
/*** 测试类*/
public class JumpingDemo_1 {//需求:创建接口操作类的对象,调用 method 方法public static void main(String[] args) {JumpingOperator jo = new JumpingOperator();Jumping j_cat = new Cat();jo.method(j_cat);}
}

当我们想让猫跳的同时,还想让狗跳。

就需要再创建一个接口实现类来重写 jump() 方法。

而使用匿名内部类的方式来重写 jump() 方法的话,就无需在外部创建接口实现类。


这里我们使用匿名内部类的方式来调用 method 方法

/*** 测试类*/
public class JumpingDemo_2 {//需求:创建接口操作类的对象,调用 method 方法public static void main(String[] args) {JumpingOperator jo = new JumpingOperator();jo.method(new Jumping() {@Overridepublic void jump() {System.out.println("猫可以跳高了");}});jo.method(new Jumping() {@Overridepublic void jump() {System.out.println("狗可以跳高了");}});}
}

2.Lambda 表达式


2.1.函数式编程思想概述


在数学中,函数就是有输入量,输出量的一套计算方案,也就是 “拿数据做操作”

面向对象思想强调“必须通过对象的形式来做事情”

函数式思想尽量忽略面向对象的复杂语法:“强调做什么,而不是以什么形式去做”

而我们要学习的 Lambda 表达式就是函数式思想的体现


2.2.体验 Lambda 表达式


需求:启动一个线程,在控制台输出一句话:多线程启动了。


方式1

  • 定义一个类 MyRunnable 实现 Runnable 接口,重写 run() 方法
  • 创建 MyRunnable 类的对象
  • 创建 Thread 类的对象
  • 启动线程
public class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("多线程程序启动_1");}
}
public class Demo_1 {public static void main(String[] args) {//实现类的方式实现需求MyRunnable my = new MyRunnable();Thread t = new Thread(my);t.start();}
}

方式2

  • 匿名内部类的方式改进
public class Demo_2 {public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {System.out.println("多线程程序启动_2");}}).start();}
}

方式3

  • Lambda 表达式的方式改进
public class Demo_3 {public static void main(String[] args) {new Thread(()->{System.out.println("多线程启动_3");}).start();}
}

2.3.Lambda 表达式的标准格式


匿名内部类中重写 run() 方法的代码分析

  • 方法形式参数是空,说明调用方法时不需要传递参数
  • 方法返回值类型是 void,说明方法执行没有结果返回
  • 方法体中的内容,是我们具体要做的事情
new Thread(new Runnable() {@Overridepublic void run() {System.out.println("多线程程序启动_2");}
}).start();

Lambda 表达式的代码分析

  • ():里面没有内容,可以看成是方法形式参数为空
  • ->:用箭头指向后面要做的事情
  • {}:包含一段代码,我们称之为代码块,可以看成是方法体中的内容
new Thread(()->{System.out.println("多线程启动_3");
}).start();
  • 组成 Lambda 表达式 的三要素:形式参数箭头代码块

Lambda 表达式的格式

  • 格式:(形式参数)->{代码块}
  • 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
  • ->:由英文中画线和大于符号组成,固定写法。代表指向动作
  • 代码块:是我们要做的事情,也就是我们之前写的方法体的内容

2.4.Lambda 表达式的使用前提


  • 有一个接口
  • 接口中有且仅有一个抽象方法

2.5.抽象方法无参无返回值


案例1

  • 定义一个接口(Eatable),里面定义一个抽象方法:void eat()
  • 定义一个测试类(EatableDemo),在测试类中提供两个方法
    • 一个方法是:useEatable(Eatable e)
    • 另一个方法是主方法,在主方法中调用 useEatable
public interface Eatable {void eat();
}
public class EatableDemo {public static void main(String[] args) {//在主方法中调用 useEatable 方法//... ...}private static void useEatable(Eatable e) {e.eat();}
}

方式一:创建接口实现类

public class EatableImpl implements Eatable{@Overridepublic void eat() {System.out.println("一天一苹果,医生远离我");}
}
public class EatableDemo_1 {public static void main(String[] args) {//在主方法中调用 useEatable 方法Eatable e = new EatableImpl();useEatable(e);}private static void useEatable(Eatable e) {e.eat();}
}

方式二:匿名内部类

public class EatableDemo_2 {public static void main(String[] args) {//在主方法中调用 useEatable 方法useEatable(new Eatable() {@Overridepublic void eat() {System.out.println("一天一苹果,医生远离我");}});}private static void useEatable(Eatable e) {e.eat();}
}

方式三:Lambda 表达式

public class EatableDemo_3 {public static void main(String[] args) {//在主方法中调用 useEatable 方法useEatable(()->{System.out.println("一天一苹果,医生远离我");});}private static void useEatable(Eatable e){e.eat();}
}

2.6.抽象方法带参无返回值


案例2

  • 定义一个接口(Flyable),里面定义一个抽象方法:void fly(String s);
  • 定义一个测试类(FlyableDemo),在测试类中提供两个方法
    • useFlyable(Flyable f)
    • 主方法,且在主方法中调用 useFlyable 方法

public interface Flyable {void fly(String s);
}
public class FlyableDemo {public static void main(String[] args) {//方式一:匿名内部类useFlyable(new Flyable() {@Overridepublic void fly(String s) {System.out.println("匿名内部类");System.out.println(s);}});//方式二:Lambda 表达式useFlyable((String s)->{System.out.println("Lambda 表达式");System.out.println(s);});}private static void useFlyable(Flyable f) {f.fly("风和日丽,晴空万里");}
}

2.7.抽象方法带参带返回值


案例3

  • 定义一个接口(Addable),里面定义一个抽象方法:int add(int x,int y);
  • 定义一个测试类(AddableDemo),在测试类中提供两个方法
    • useAddable(Addable a)
    • 主方法,且在主方法中调用 useAddable() 方法

public interface Addable {int add(int x, int y);
}
public class AddableDemo {public static void main(String[] args) {//在主方法中调用 useAddable 方法useAddable((int x, int y) -> {return x + y;});}private static void useAddable(Addable a) {int sum = a.add(10, 20);System.out.println(sum);}
}

2.8.Lambda 表达式的省略模式


省略规则

  • 参数类型可以省略,但是有多个参数的情况下,不能只省略一个
  • 如果参数有且仅有一个,那么小括号可以省略
  • 如果代码块的语句只有一条,可以省略大括号和分号,有 return 的情况下还要省略掉 return

案例4

  • 准备
public interface Flyable {void fly(String s);
}
public interface Addable {int add(int x, int y);
}
public class Demo {public static void main(String[] args) {// ... ...}private static void useFlyable(Flyable f) {f.fly("风和日丽,晴空万里");}private static void useAddable(Addable a) {int sum = a.add(10, 20);System.out.println(sum);}
}
  • 示例1:参数类型可以省略(在多个参数的情况下,不能只省略一个)
useAddable((x, y) -> {return x + y;
});
  • 示例2:当参数仅有一个时,小括号也可以省略
useFlyable(s -> {System.out.println(s);
});
  • 示例3:若代码块语句只有一条,可以省略大括号和分号
useFlyable(s -> System.out.println(s));
  • 示例4:

    • 若代码块的语句仅有一条,可以省略大括号和分号。
      在省略调大括号和分号后,若有 return,那么 return 也需要省略掉
useAddable((x, y) -> x + y);

2.9.Lambda 表达式的注意事项


  • 使用 Lambda 必须要有接口,并且要求 接口中有且仅有一个抽象方法
  • 必须有上下文环境,才能推导出 Lambda 对应的接口
    • 根据 局部变量的赋值 得知 Lambda 对应的接口:
      unnable r = () -> System.out.println("Lambda表达式一");
    • 根据 调用方法的参数 得知 Lambda 对应的接口:
      new Thread(() -> System.out.println("Lambda表达式二")).start();

示例:准备工作

public interface Inter {void show();
}
public class LambdaDemo {public static void main(String[] args) {//......}public static void userInter(Inter i) {i.show();}
}

  • 使用 Lambda 必须要有接口,并且要求接口中有且仅有一个抽象方法

正常模式

userInter(() -> {System.out.println("好好学习,天天向上");
});

省略模式

userInter(() -> System.out.println("笑傲江湖"));

  • 使用 lambda 时必须要有上下文环境,才能推导出 Lambda 的接口

匿名内部类

new Thread(new Runnable() {@Overridepublic void run() {System.out.println("匿名内部类");}
}).start();

第一种方式

Runnable r = () -> System.out.println("Lambda表达式一");new Thread(r).start();

第二种方式

new Thread(() -> System.out.println("Lambda表达式二")).start();

2.10.Lambda 表达式和匿名内部类的区别


  • 所需类型不同:

    • 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
    • lambda 表达式:只能是接口

  • 使用限制不同:

    • 如果接口中有且仅有一个抽象方法,可以使用 Lambda 表达式,也可以使用匿名内部类
    • 如果接口中多一个抽象方法,只能使用匿名内部类

  • 实现原理不同:

    • 匿名内部类:编译之后,产生一个单独的 .class 字节码文件
    • Lambda 表达式:编译之后,没有一个单独的 .class 字节码文件
      对应的字节码会在运行的时候动态生成

3.接口组成更新


3.1.接口组成更新概述


接口的组成

  • 常量:public static final
  • 抽象方法:public abstract
  • 默认方法(Java 8)
  • 静态方法(Java 8)
  • 私有方法(Java 9)

3.2.接口中的默认方法


接口中默认方法的定义格式

  • 格式:public default 返回值类型方法名(参数列表){ }
  • 范例:public default void show3(){ }

接口中默认方法的注意事项

  • 默认方法不是抽象方法,私有不强制被重写。但是可以被重写,重写时需去掉 default 关键字
  • public 可以省略,default 不能省略

3.3.接口中的静态方法


接口中静态方法的定义格式

  • 格式:public static 返回值类型方法名(参数列表){ }
  • 范例:public static void show(){ }

接口中静态方法的注意事项

  • 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
  • public 可以省略,static 不能省略

3.4.接口中的私有方法


Java 中新增了带方法的私有方法,这其实在 Java 8 中就埋下了伏笔:

  • Java 8 中允许在接口中定义带方法体的默认方法和静态方法。

这样可能会导致一个问题:

  • 当两个默认方法或者静态方法中包含一段相同的代码实现时,
  • 程序必然考虑将这段实现代码抽取成一个共性方法,
  • 而这个共性方法是不需要别人使用的,因此用私有给隐藏起来,
  • 这就是 Java 9 增加私有方法的必然性

接口中的私有方法的定义格式:

  • 格式1:private 返回值类型方法名(参数列表){ }

  • 范例1:private void show(){ }

  • 格式2:private static 返回值类型方法名(参数列表){ }

  • 范例2:private static void method(){ }


接口中私有方法的注意事项

  • 默认方法可以调用私有的静态方法和非静态方法
  • 静态方法只可以调用私有的静态方法

4.方法引用


4.1.方法引用体验


在使用 Lambda 表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作

那么考虑一种情况:如果我们在 Lambda 中所指定的操作方案,已经有地方存在方案,

那是否有还有必要再写重复逻辑呢?答案是没有必要的。

我们通过方法引用来使用已经存在的方案的


案例需求

  1. 定义一个接口(Printable),里面定义一个抽象方法:void printString(String s);
  2. 定义一个测试类(PrintableDemo),在测试类中提供两个方法:
    一个是 usePrintable(Printable p)
    另一个是主方法,且在主方法中调用 usePrintable 方法
  • 接口
public interface Printable {void printString(String s);
}
  • 测试类
public class PrintableDemo {public static void main(String[] args) {//... ...}static void usePrintable(Printable p) {p.printString("剪辑视频");}
}
  • 在主方法中调用 usePrintable 方法(Lambda 表达式)
usePrintable(s -> System.out.println(s));
  • 在主方法中调用 usePrintable 方法(方法引用)
usePrintable(System.out::println);

4.2.方法引用符


方法引用符:

  • :: 该符号为引用运算符,而它所在的表达式被称为方法引用

  • Lambda 表达式:usePrintable(s -> System.out.println(s));
    分析:拿到参数 s 之后,通过 Lambda 表达式 ,传递给 System.out.println 方法去处理

  • 方法引用:usePrintable(System.out::println);
    分析:直接使用 System.out 中 println 方法来取代 Lambda ,代码写的更加简洁


推导与省略:

  • 如果使用 Lambda表达式,那么根据 “可推导就是省略” 的原则,
    无需指定参数类型,也无需指定的重载形式,它们都将被自动推导
  • 如果使用方法引用,也可以同样根据上下文进行推导
  • 可以使用 Lambda 表达式 的情况下,也可以使用方法引用符

4.3.Lambda 表达式支持的方法引用


常见的引用方式

  • 引用类方法
  • 引用对象的实例方法
  • 引用类的实例方法
  • 引用构造器

4.3.1.引用类方法


引用类方法,其实就是引用类的静态方法

  • 格式: 类名::静态方法
  • 范例: Integer::parseInt
    • Integer类 的方法:public static int parseInt(String s) 将此 String 转换为 int 类型数据

案例需求

  1. 定义一个接口(Converter),里面定义一个抽象方法:int convert(String s);
  2. 定义一个测试类(ConverterDemo),在测试类中提供两个方法:
    1. useConverter(Converter c)
    2. 主方法,且在主方法中调用 useConverter 方法
public interface Converter {int convert(String s);
}
public class ConverterDemo {public static void main(String[] args) {//... ...}private static void useConverter(Converter c) {int number = c.convert("666");System.out.println(number);}
}

此处作一下对比

  • Lambda 表达式
useConverter(s -> Integer.parseInt(s));
  • 引用类方法
useConverter(Integer::parseInt);

Lambda 表达式被类方法替代的时候,它的形式参数全部传递给静态方法


4.3.2.引用对象的实例方法


引用对象的实例方法,其实就是引用类的成员方法

  • 格式:对象::成员方法
  • 范例:"HelloWorld"::toUpperCase
    • String 类中的方法:public String toUpperCase() 将此 String 所有字符转换为大写

案例

  • 定义一个类(PrintString),里面定义一个方法:public void printUpper(String s);
    把字符串参数变成大写的数据,然后在控制台输出
  • 定义一个接口(Printer),里面定义一个抽象方法:void printUpperCase(String s);
  • 定义一个测试类(PrintDemo),在测试类中提供两个方法:
    • usePrinter(Printer p)
    • 主方法,在主方法中调用 usePrinter 方法
public interface Printer {void printUpperCase(String s);
}
public class PrintString {public void printUpper(String s) {String result = s.toUpperCase();System.out.println(result);}
}
public class PrinterDemo {public static void main(String[] args) {//在主方法中调用 usePrinter 方法//Lambda 表达式usePrinter((String s) -> {System.out.println(s.toUpperCase());});//Lambda 表达式(省略模式)usePrinter(s -> System.out.println(s.toUpperCase()));//方法引用(对象的实例方法)PrintString ps = new PrintString();usePrinter(ps::printUpper);//Lambda 表达式被对象的实例方法替代时,它的形式参数全部传递给了该方法作为参数}static void usePrinter(Printer p) {p.printUpperCase("HelloWorld");}
}

Lambda 表达式被对象的实例方法替代时,它的形式参数全部传递给了该方法作为参数


4.3.3.引用类的实例方法


引用类的实例方法,即是引用类中的成员方法

  • 格式:类名::成员方法
  • 范例:String::substring
    String类 中的方法:public String substring(int beginIndex,int endIndex)
    从 beginIndex 开始到 endIndex 结束,截取字符串,
    返回一个字符串,子串的长度是 endIndex-beignIndex

案例

  • 定义一个接口(MyString),里面定义一个抽象方法:
    String mySubString(String s,int x,int y)
  • 定义一个测试类(MyStringDemo),在测试类中提供两个方法
    一个方法是:useMyString(MyString my)
    一个方法是:主方法,且在主方法中调用 useMyString 方法
public interface MyString {String mySubString(String s, int x, int y);
}
public class MyStringDemo {public static void main(String[] args) {useMyString((String s, int x, int y) -> {return s.substring(x, y);});useMyString((s, x, y) -> s.substring(x, y));//引用类的实例方法useMyString(String::substring);/*Lambda 表达式被类的实例方法替代时第一个参数作为调用者后面的参数全部传递给该方法作为参数*/}private static void useMyString(MyString my) {String s = my.mySubString("HelloWorld", 2, 5);System.out.println(s);}
}

Lambda 表达式被类的实例方法替代时,第一个参数作为调用者,后面的参数全部传递给该方法作为参数


4.3.4.引用构造器


引用构造器,其实就是在引用构造方法

  • 格式:类名::new
  • 范例:Student::new

案例

  • 定义一个类(Student):里面有两成员变量(name,age)
    并提供 无参构造方法 和 带参构造方法,以及成员变量对应的 get 方法 和 set 方法
  • 定义一个接口(StudentBuilder),里面定义一个抽象方法
    Student builder(String name,int age);
  • 定义一个测试类(StudentDemo),在测试类中提供两个方法
    一个方法是:useStudentBuilder(StudentBuilder s)
    另一个方法是主方法,在主方法中调用 useStudentBuilder 方法

Student 类(略)

StudentBuilder 接口类

public interface StudentBuilder {Student build(String name, int age);
}

StudentDemo 类

public class StudentDemo {public static void main(String[] args) {//在主方法中调用 useStudentBuilder 方法//Lambda 表达式useStudentBuilder((String name, int age) -> {return new Student(name, age);});//Lambda 表达式(省略模式)useStudentBuilder(((name, age) -> new Student(name, age)));//引用构造器useStudentBuilder(Student::new);}private static void useStudentBuilder(StudentBuilder studentBuilder) {Student s = studentBuilder.build("张三丰", 108);System.out.println(s.getName() + ":" + s.getAge());}
}

Lambda 表达式被构造器替代时,它的形式参数全部传递给构造器作为参数


5.函数式接口


5.1.函数式接口概述


函数式接口:有且仅有一个抽象方法的接口

Java 中的函数式编程体现就是 lambda 表达式,所以函数式接口就是可以适用于 lambda 适用的接口

只有确保接口中有且仅有一个抽象方法,Java 中的 lambda 才能顺利地进行推导


如何检测一个接口是不是函数式接口?

  • @FunctionalInterface
  • 放在接口定义的上方:如果接口是函数式接口,编译通过;否,编译失败

注意

  • 我们自己定义函数接口的时候,@FunctionalInterface可选 的,
  • 只要满足函数式定义的条件,写不写注解,都是函数式接口
  • 一般建议加上该注解

5.2.函数式接口作为方法的参数


需求

  • 定义一个类(RunnableDemo),在类中提供两个方法
    一个方法是:startThread(Runnable r),方法参数 Runnable 是一个函数式接口
    一个方法是主方法,在主方法中调用 satrtThread 方法

如果方法的参数是一个函数式接口,我们可以用 Lambda 表达式作为参数传递

  • satrtThread(()->System.out.println(Thread.currentThread().getName()+":线程启动了"));
public class RunnableDemo {public static void main(String[] args) {//匿名内部类的方式startThread(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + "线程启动了");}});//Lambda 表达式的方式startThread(() -> System.out.println(Thread.currentThread().getName() + "线程启动了"));}private static void startThread(Runnable r) {new Thread(r).start();}
}

5.3.函数式接口作为方法的返回值


需求

定义一个类(Comparator),在类中提供两个方法

  • 一个方法是:Comparator<String>getComparator()
    方法返回值 Comparator 是一个函数式接口
  • 一个方法是主方法,在主方法中调用 getComparator 方法

如果方法的返回值是一个函数式接口,我们可以使用 lambda 表达式作为结果返回

private static Comparator<String> getComparator() {return (s1, s2) -> s1.length() - s2.length();
}

代码

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;public class ComparatorDemo {public static void main(String[] args) {//构造使用场景//定义集合,存储字符串元素ArrayList<String> array = new ArrayList<String>();array.add("cccc");array.add("aa");array.add("b");array.add("ddd");System.out.println("排序前:" + array);//排序前:[cccc, aa, b, ddd]Collections.sort(array, getComparator());System.out.println("排序后:" + array);//排序后:[b, aa, ddd, cccc]}private static Comparator<String> getComparator() {//匿名内部类的方式实现/*return new Comparator<String>() {@Overridepublic int compare(String s1, String s2) {return s1.length() - s2.length();}};*///Lambda 表达式的方式实现/*return (String s1, String s2) -> {return s1.length() - s2.length();};*///Lambda 表达式的方式实现(省略模式)return (s1, s2) -> s1.length() - s2.length();}}

5.4.常用的函数式接口


Java8 在 java.util.function 包下预定义了大量的函数式接口供我们使用

这里重点学习四个接口

  • Supplier 接口
  • Consumer 接口
  • Predicate 接口
  • Function 接口

5.4.1.Supplier 接口


Supplier<T>:包含一个无参的方法

  • T get():获得结果

    • 该方法不需要参数,它会按照某种实现逻辑(由 Lambda 表达式实现)返回一个数据

Supplier<T> 接口 也被称为 生产型接口

  • 如果我们指定了接口的泛型是什么类型
    那么接口中的 get 方法就会产什么样的类型供我们使用

案例1-代码

import java.util.function.Supplier;public class SupplierDemo {public static void main(String[] args) {String s1 = getString(() -> {return "周慧敏";});System.out.println(s1);String s2 = getString(() -> "王祖贤");System.out.println(s2);Integer i = getInteger(() -> 22);System.out.println(i);}//定义一个方法,返回一个整数private static Integer getInteger(Supplier<Integer> sup) {return sup.get();}//定义一个方法,返回一个字符串数据private static String getString(Supplier<String> sup) {return sup.get();}
}

案例2-代码

定义一个类(SupplierTest),在类中提供两个方法

  • 一个方法是:int getMax(Supplier<Integer> sup)
    用于返回一个 int 数组中的最大值
  • 一个方法是主方法,在主方法中调用 getMax 方法
import java.util.function.Supplier;/*** 获取数组中的最大值*/
public class SupplierTest {public static void main(String[] args) {int[] arr = {19, 50, 28, 37, 46};int maxValue = getMax(() -> {int max = arr[0];for (int i = 0; i < arr.length; i++) {if (arr[i] > max) {max = arr[i];}}return max;});System.out.println(maxValue);}private static int getMax(Supplier<Integer> sup) {return sup.get();}
}

5.4.2.Consumer 接口


Consumer<T>:包含两个方法:

  • void accept(T t) :对给定的参数执行此操作。
  • default Consumer<T> andThen(Consumer<? super T> after)
    返回一个组合的 Consumer ,按顺序执行该操作,然后执行 after 操作。

Conusumer<T> 接口 也被称为 消费型接口,它消费的数据的数据类型由泛型指定


案例1-代码

import java.util.function.Consumer;public class ConsumerDemo {public static void main(String[] args) {//Lambda 表达式operatorString("岸边露伴", (String s) -> {System.out.println(s);});//Lambda 表达式(省略版)operatorString("广濑康一", s -> System.out.println(s));//方法引用operatorString("东方仗助", System.out::println);operatorString("吉良吉影",s -> System.out.println(s),//吉良吉影s -> System.out.println(new StringBuilder(s).reverse().toString())//影吉良吉);operatorString_2("吉良吉影",s -> System.out.println(s),//吉良吉影s -> System.out.println(new StringBuilder(s).reverse().toString())//影吉良吉);}//定义一个方法,消费一个字符串数据private static void operatorString(String name, Consumer<String> consumer) {consumer.accept(name);}//定义一个方法,消费一个字符串数据private static void operatorString(String name, Consumer<String> con_1, Consumer<String> con_2) {con_1.accept(name);con_2.accept(name);}//default Consumer<T> andThen(Consumer<? super T> after)private static void operatorString_2(String name, Consumer<String> con_1, Consumer<String> con_2) {con_1.andThen(con_2).accept(name);}
}

案例2-代码

  • String[] strArray = {"笑傲江湖,33", "天龙八部,55", "神雕侠侣,44"};
  • 字符串数组中有多条信息,请按照格式:"姓名:XX,年龄:XX。" 的格式将信息打印出来
  • 要求
    • 把打印姓名的动作作为第一个 Consumer 接口 的 Lambda 实例
    • 把打印年龄的动作作为第二个 Consumer 接口 的 Lambda 实例
    • 将两个 Consumer 接口按照顺序组合到一起使用
import java.util.function.Consumer;public class ConsumerTest {public static void main(String[] args) {String[] strArray = {"笑傲江湖,33", "天龙八部,55", "神雕侠侣,44"};//Lambda 表达式printInfo(strArray,(String str) -> {String name = str.split(",")[0];System.out.print("姓名:" + name);}, (String str) -> {int age = Integer.parseInt(str.split(",")[1]);System.out.println(",年龄:" + age + "。");});//Lambda 表达式优化printInfo(strArray,str -> System.out.print("姓名:" + str.split(",")[0]),str -> System.out.println(",年龄:" + Integer.parseInt(str.split(",")[1]) + "。"));}private static void printInfo(String[] strArray, Consumer<String> con1, Consumer<String> con2) {for (String str : strArray) {con1.andThen(con2).accept(str);}}
}

5.4.3.Predicate 接口


Predicate<T> 常用的四个方法

  • default Predicate<T> and(Predicate<? super T> other)
    返回一个组合的谓词,表示该谓词与另一个谓词的短路逻辑与。

  • default Predicate<T> negate()
    返回表示此谓词的逻辑否定的谓词。

  • default Predicate<T> or(Predicate<? super T> other)
    返回一个组合的谓词,表示该谓词与另一个谓词的短路逻辑或。

  • boolean test(T t)
    在给定的参数上评估这个谓词。


Predicate<T> 接口 通常用于判断参数是否满足指定的条件


案例1

  • default Predicate<T> negate()
  • boolean test(T t)
import java.util.function.Predicate;public class PredicateDemo01 {public static void main(String[] args) {//Lambda 表达式boolean b1 = checkString_1("HelloWorld", (String s) -> {return s.length() > 8;});System.out.println(b1);//true//Lambda 表达式(省略模式)boolean b2 = checkString_1("Hello", s -> s.length() > 8);System.out.println(b2);//falseboolean b3 = checkString_2("Hello", s -> s.length() > 8);System.out.println(b3);//true}//判断给定的字符串是否满足要求private static boolean checkString_1(String s, Predicate<String> predicate) {return predicate.test(s);}private static boolean checkString_2(String s, Predicate<String> predicate) {//return !predicate.test(s);return predicate.negate().test(s);//正式写法}
}

案例2

  • default Predicate<T> and(Predicate<? super T> other)
  • default Predicate<T> or(Predicate<? super T> other)
import java.util.function.Predicate;public class PredicateDemo02 {public static void main(String[] args) {boolean b_1 = checkString_1("Hello", s -> s.length() > 8, s -> s.length() < 15);System.out.println(b_1);//falseboolean b_2s = checkString_2("Hello", s -> s.length() > 8, s -> s.length() < 15);System.out.println(b_2s);//trueboolean b_2 = checkString_1("HelloWorld", s -> s.length() > 8, s -> s.length() < 15);System.out.println(b_2);//true}//同一个字符串给出俩不同判断条件,最后把这俩判断的结果做逻辑与运算的结果作为最终的结果private static boolean checkString_1(String s, Predicate<String> pre1, Predicate<String> pre2) {/*boolean b1 = pre1.test(s);boolean b2 = pre2.test(s);boolean b = b1 && b2;return b;*/return pre1.and(pre2).test(s);}private static boolean checkString_2(String s, Predicate<String> pre1, Predicate<String> pre2) {/*boolean b1 = pre1.test(s);boolean b2 = pre2.test(s);boolean b = b1 || b2;return b;*/return pre1.or(pre2).test(s);}
}

案例3

  • String[] strArray ={"楚留香,22","西门吹雪,33","花满楼,66","陆小凤,44","叶孤城,55"};
  • 字符串数组中有多条信息
    请通过 Predicate 接口的拼装符合要求的字符串筛选到 ArrayList 中
    并且遍历 ArrayList 集合
  • 同时满足如下要求:姓名长度大于3,年龄大于33
  • 分析:
    有两个判断条件,所以需要使用两个 Predicate 接口,对条件进行判断
    必须同时满足两个条件,所以可以使用 and 方法连接两个判断条件
import java.util.ArrayList;
import java.util.function.Predicate;public class PredicateTest {public static void main(String[] args) {String[] strArray = {"楚留香,22", "西门吹雪,33", "花满楼,66", "陆小凤,44", "叶孤城,55"};ArrayList<String> array = myFilter(strArray,s -> s.split(",")[0].length() > 2,s -> Integer.parseInt(s.split(",")[1]) > 33);for (String str : array) {System.out.println(str);}}//通过 Predicate 接口的拼装将符合要求的字符串筛选到集合 ArrayList 中private static ArrayList<String> myFilter(String[] strArray, Predicate<String> pre1, Predicate<String> pre2) {//定义一个集合ArrayList<String> array = new ArrayList<String>();//遍历数组for (String str : strArray) {if (pre1.and(pre2).test(str)) {array.add(str);}}return array;}
}

5.4.4.Function 接口


Function<T,R>

T:函数输入的类型R:函数结果的类型。
  • R apply(T t)
    将此函数应用于给定的参数。

  • default<V> Function andThen(Function after)
    返回一个组合函数,首先将该函数应用于输入,然后将 after 函数应用于结果


Function<T,R> 接口通常用于对参数进行处理,转换(处理逻辑由 lambda 表达式 实现),然后返回一个新的值


案例1

import java.util.function.Function;public class FunctionDemo {public static void main(String[] args) {/* ************************************************************************ *///Lambda 表达式convert("100", (String s) -> {return Integer.parseInt(s);});//Lambda 表达式(省略模式)convert("100", s -> Integer.parseInt(s));//方法引用convert("100", Integer::parseInt);/* ************************************************************************ */convert(100, i -> String.valueOf(i + 566));/* ************************************************************************ */convert("100", f1 -> Integer.parseInt(f1), f2 -> String.valueOf(f2 + 566));/* ************************************************************************ */}//定义一个方法:把字符转换为 int 类型,在控制台输出private static void convert(String s, Function<String, Integer> fun) {//Integer i = fun.apply(s);int i = fun.apply(s);System.out.println(i);}//定义一个方法:把一个 int 类型的数据加上一个整数之后,转为字符串在控制台输出private static void convert(int i, Function<Integer, String> fun) {String s = fun.apply(i);System.out.println(s);}//定义一个方法:把一个字符串转换为 int 类型,把 int 类型的数据加上一个整数之后,转为字符串在控制台输出private static void convert(String s, Function<String, Integer> fun1, Function<Integer, String> fun2) {/*Integer i = fun1.apply(s);String ss = fun2.apply(i);System.out.println(ss);*/String ss = fun1.andThen(fun2).apply(s);System.out.println(ss);}}

案例2

  • String s = "林书豪,22";
  • 按照要求进行操作
    • 将字符串截取得到数字年龄部分
    • 将上一步的年龄字符串转换为 int 类型数据
    • 将上一步的 int 数据加 70,得到一个 int 结果,在控制台输出
  • 请通过 Function 接口来实现函数拼接
import java.util.function.Function;public class FunctionTest {public static void main(String[] args) {String s = "林书豪,30";//Lambda 表达式convert(s,(String ss) -> {return s.split(",")[1];},(String ss) -> {return Integer.parseInt(ss);},(Integer i) -> {return i + 70;});//Lambda 表达式(省略模式)convert(s, ss -> ss.split(",")[1], ss -> Integer.parseInt(ss), i -> i + 70);//方法引用convert(s, ss -> ss.split(",")[1], Integer::parseInt, i -> i + 70);}private static void convert(String s, Function<String, String> f1, Function<String, Integer> f2, Function<Integer, Integer> f3) {int i = f1.andThen(f2).andThen(f3).apply(s);System.out.println(i);}
}

6.Stream 流


6.1.体验 Stream 流


需求

  • 创建一个集合,存储多个字符串元素
  • 把集合中所有以 “张” 开头的元素存储到一个新的集合中
  • “张” 字开头的集合中的长度为 3 的元素存储到一个新的集合
  • 遍历上一步得到的集合

  • 使用 Stream 流的方式完成过滤操作

    • list.stream().fiflter(s->s.startWith("张")).fiflter(s->s.length()==5).forEach(System.out::println);
  • 直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:生成流、过滤姓张、过滤长度为 3、逐一打印
  • Stream 流把真正的函数式编程风格引入到 Java 中

import java.util.ArrayList;public class Demo {public static void main(String[] args) {//创建一个结合,存储多个字符串元素ArrayList<String> list = new ArrayList<String>();list.add("林青霞");list.add("张曼玉");list.add("王祖贤");list.add("柳岩");list.add("张敏");list.add("张柏芝");//把集合中所有以 “张” 开头的元素存储到一个新的集合ArrayList<String> zhangList = new ArrayList<String>();for (String s : list) {if (s.startsWith("张")) {zhangList.add(s);}}//把 “张” 开头的集合中长度为 3 的元素 存储到一个新的集合ArrayList<String> threeList = new ArrayList<String>();for (String s : zhangList) {if (s.length() == 3) {threeList.add(s);}}//遍历上一步得到的集合for (String s : threeList) {System.out.println(s);}}
}

显然,只是为了实现上面的要求,这里的代码就有这么多了。

所以我们有必要精简代码。


使用 Stream 流 的方式来实现上面的要求,可以精简上面的代码。

import java.util.ArrayList;public class StreamDemo01 {public static void main(String[] args) {//创建一个结合,存储多个字符串元素ArrayList<String> list = new ArrayList<String>();list.add("林青霞");list.add("张曼玉");list.add("王祖贤");list.add("柳岩");list.add("张敏");list.add("张柏芝");//Stream 流改进代码list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(s -> System.out.println(s));}}

当然,使用方法引用的话,可以进一步精简代码。

list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(System.out::println);

在以上的代码中 Stream 流的使用

  • 生成流
    通过数据源(集合、数组等)生成流
    list.stream()

  • 中间流
    一个流后面可以跟随零个或者多个中间操作,其主要目的是打开流,
    做出某种程度的数据过滤/映射,然后返回一个新的流
    fiflter()

  • 终结操作
    一个流只能有一个终结操作,当这个操作执行后,
    流就被使用完毕,无法再被操作。
    所以这必然是流的最后一个操作
    forEach()


6.2.Stream 流的创建生成方式


Stream 流的常见生成方式

  • Collection 体系 的集合可以使用默认方法 stream() 生成流
    default Stream<E>stream()

  • Map 体系 的集合 间接的生成流

  • 数组 可以通过 Stream 接口 的静态方法 of(T...values) 生成流


6.3.Stream 流的中间操作


  • Stream<T> fiflter(Predicate predicate):用于对流中的数据进行过滤

    • Predicate 接口中的方法:boolean test(T t):对给定的参数进行判断,返回一个布尔值

  • Stream<T> limit(long maxSize):返回此流中的元素组成的流,截取前指定参数个数的数据

  • Stream<T> skip(long n):跳过指定参数个数的数据,返回由该流的剩余元素组成的流

  • static <T> Stream<T> concat(Stream a,Stream b):合并 a 和 b 两个流为一个流

  • Stream<T> distinct():返回由该流的不同元素(根据 Object.equals(Object))组成的流

  • Stream<T> sorted():返回由此流的元素组成的流,根据自然顺序排序

  • Stream<T> sorted(Comparadtor comparator):返回由此流的元素组成的流

    • 根据提供的 Comparator 进行排序

  • <R> Stream<R> map(Function mapper):返回由给定函数应用于此流的元素的结果组成的流

    • Function 接口中的方法:R apply(T t)

  • IntStream mapToInt(ToIntFunction mapper):返回一个 IntStream 其中包含给定函数应用于此流的元素的结果

    • intStream:表示原始 int
    • ToIntFunction 接口中的方法: int applyAsInt(T value)

6.3.1.Stream 流中间操作之 filter


创建如下的集合,存储多个元素

ArrayList<String> list = new ArrayList<String>();list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");

需求1:把 list 集合中以 “张” 开头的元素在控制台输出

//Lambda 表达式
list.stream().filter((String s) -> {return s.startsWith("张");}).forEach(System.out::println);
//Lambda 表达式优化
list.stream().filter(s -> s.startsWith("张")).forEach(System.out::println);

需求2:把 list 集合中长度为 3 的元素在控制台输出

list.stream().filter(s -> s.length() == 3).forEach(System.out::println);

需求3:把 list 集合中以张开头的、长度为 3 的元素在控制台输出

list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(System.out::println);

6.3.2.Stream 流中间操作之 limit&skip


创建如下的集合,存储多个元素

ArrayList<String> list = new ArrayList<String>();list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");

需求1:取前三个数据在控制台输出

list2.stream().limit(3).forEach(System.out::println);
System.out.println("----------------------");

需求2:跳过前三个元素,把剩余的元素在控制台输出

list2.stream().skip(3).forEach(System.out::println);
System.out.println("----------------------");

需求3:跳过两个元素,把剩下的元素中的前两个在控制台输出

list2.stream().skip(2).limit(2).forEach(System.out::println);
System.out.println("----------------------");

6.3.3.Stream 流中间操作之 concat&distinct


创建如下的集合,存储多个元素

ArrayList<String> list = new ArrayList<String>();list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");

需求1:取前 4 个数据组成一个流

Stream<String> s1 = list3.stream().limit(4);

需求2:跳过 2 个数据组成一个流

Stream<String> s2 = list3.stream().skip(2);

需求3:合并 需求1 和 需求2 得到的流,并且把结果在控制台输出

Stream.concat(s1, s2).forEach(System.out::println);

需求4:合并 需求1 和 需求2 得到的流,并且把结果在控制台输出,要求字符串元素不能重复

Stream.concat(s1, s2).distinct().forEach(System.out::println);

6.3.4.Stream 流中间操作之 sorted


创建如下的集合,存储多个元素

ArrayList<String> list4 = new ArrayList<String>();
list4.add("linqingxia");
list4.add("zhangmanyu");
list4.add("wangzuxian");
list4.add("liuyan");
list4.add("zhangmin");
list4.add("zhangwuji");

需求1:取前 4 个数据组成一个流

list4.stream().sorted().forEach(System.out::println);

需求2:按照字符串长度把数据在控制台输出

list4.stream().sorted((s1, s2) -> {int num1 = s1.length() - s2.length();int num2 = (num1 == 0) ? s1.compareTo(s2) : num1;return num2;
}).forEach(System.out::println);

6.3.5.Stream 流中间操作之 map&mapToInt


创建如下的集合,存储多个元素

ArrayList<String> list5 = new ArrayList<String>();list5.add("10");
list5.add("20");
list5.add("30");
list5.add("40");
list5.add("50");

需求:将集合中的字符串数据转换为整数之后在控制台输出

//Lambda 表达式
list5.stream().map(s -> Integer.parseInt(s)).forEach(System.out::println);//使用 方法引用 优化 Lambda 表达式
list5.stream().map(Integer::parseInt).forEach(System.out::println);

也可以使用 IntStream mapToInt(ToIntFunction mapper)

list5.stream().mapToInt(Integer::parseInt).forEach(System.out::println);

int sum() 返回此流中的元素的总和

int result = list5.stream().mapToInt(Integer::parseInt).sum();System.out.println(result);

6.4.Stream 流的常见终结操作方法


  • void forEach(Consumer action):对此流的每个元素执行操作

    • Consumer 接口中的方法:void accept(T t):对给定的参数执行此操作

  • long count():返回此流中的元素数

import java.util.ArrayList;public class StreamDemo08 {public static void main(String[] args) {//创建一个集合,存储多个字符串元素ArrayList<String> list = new ArrayList<String>();list.add("林青霞");list.add("张曼玉");list.add("王祖贤");list.add("柳岩");list.add("张敏");list.add("张无忌");//需求1:把统计的元素输出在控制台上list.stream().forEach(System.out::println);//需求2:统计集合中有几个以张开头的元素,并把统计结果在控制台输出long count = list.stream().filter(s -> s.startsWith("张")).count();System.out.println(count);}
}

6.5.Stream 流的综合练习


案例需求

现有两个 ArrayList 集合,分别存储 6 名男演员和 6 名女演员,要求完成如下操作

  • 男演员只要名字为 3 个字的前三人
  • 女演员只要姓 “林” 的人,并不要第一个
  • 把过滤后的男演员姓名和女演员姓名合并到一起
  • 把上一步操作后的元素作为构造方法的参数创建演员对象,遍历数据
  • 补充:演员类 Actor:里面有一个成员变量,一个带参构造方法,以及成员变量对应的 get/set 方法

public class Actor {private String name;public Actor(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}
import java.util.ArrayList;
import java.util.stream.Stream;public class StreamTest {public static void main(String[] args) {ArrayList<String> manList = new ArrayList<String>();manList.add("周润发");manList.add("成龙");manList.add("刘德华");manList.add("吴京");manList.add("周星驰");manList.add("李连杰");ArrayList<String> womanList = new ArrayList<String>();womanList.add("林心如");womanList.add("张曼玉");womanList.add("林青霞");womanList.add("林志玲");womanList.add("王祖贤");//男演员只要名字为3个字的前三人Stream<String> manStream = manList.stream().filter(s -> s.length() == 3).limit(3);//女演员只要姓林的人,并不要第一个Stream<String> womanStream = womanList.stream().filter(s -> s.startsWith("林")).skip(1);//把过滤后的男演员姓名和女演员姓名合并到一起Stream<String> stream = Stream.concat(manStream, womanStream);//把上一步操作后的元素作为构造方法的参数创建演员对象,遍历数据stream.map(Actor::new).forEach(p -> System.out.println(p.getName()));}
}

最后的 Stream 流处理那块儿,我们可以简化一下。

Stream.concat(manList.stream().filter(s -> s.length() == 3).limit(3),womanList.stream().filter(s -> s.startsWith("林")).skip(1)).map(Actor::new).forEach(p -> System.out.println(p.getName())
);

6.6.Stream 流的收集操作


问:对数据使用 Stream 的方式进行操作完毕后,想把流中的数据收集到集合中,应该如何操作?


Stream 流的收集方法

  • R collect(Collector collector)
  • 但是这个收集方法的参数是一个 Collector 接口

工具类 Collectors 提供了具体的收集方式

  • public static<T>Collectors toList():把元素收集到 List 集合中
  • public static<T>Collectors toSet():把元素收集到 Set 集合中
  • public static Collectors toMap(Function keyMapper,Function valueMapper):把元素收集到 Map 集合中

创建 List 集合,存储多个字符串元素

List<String> list = new ArrayList<String>();list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");

需求:得到名字为 3 个字的流

Stream<String> listStream = list.stream().filter(s -> s.length() == 3);

需求:把使用 Stream 流操作完毕的数据收集到 list 集合中并且遍历

List<String> names = listStream.collect(Collectors.toList());for (String name : names) {System.out.println(name);
}

创建 Set 集合存储多个 Integer 类型的元素

Set<Integer> set = new HashSet<Integer>();set.add(10);
set.add(20);
set.add(30);
set.add(33);
set.add(35);

需求:得到年龄大于 25 的流

Stream<Integer> setStream = set.stream().filter(age -> age > 25);

需求:把使用 Stream 流操作完毕的数据收集到 Set 集合中并遍历

Set<Integer> ages = setStream.collect(Collectors.toSet());for (Integer age : ages) {System.out.println(age);
}

定义一个字符串数组,每一个字符串数组由姓名数据和年龄组合而成

String[] strArray = {"林青霞,30", "张曼玉,35", "王祖贤,33", "柳岩,25"};

需求:得到字符串中年龄数据大于 28 的流

Stream<String> arrayStream = Stream.of(strArray).filter(s -> Integer.parseInt(s.split(",")[1]) > 28);

需求:把使用 Stream 流操作完毕的数据收集到 Map 集合中并遍历,字符串中的姓名作键,年龄作值

Map<String, Integer> map = arrayStream.collect(Collectors.toMap(s -> s.split(",")[0],s -> Integer.parseInt(s.split(",")[1]))
);Set<String> keySet = map.keySet();for (String key : keySet) {Integer value = map.get(key);System.out.println(key + ":" + value);
}

【查缺补漏 | JAVA 基础知识 | 粗略复习②】相关推荐

  1. java基础知识粗略整理

    Java基础 java的三大特性: 封装:隐藏对象的属性和实现细节,对外仅提供公共访问方式,提高复用性和安全性.在java当中有3中修饰符 public.private.protected赋予不同的访 ...

  2. 查缺补漏 - Java 反射全面解析

    若有收获,请记得分享和转发哦 1.动态语言 是一类在运行时可以改变其结构的语言:例如新的函数.对象.甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化.通俗点说就是在运行时代码可以根据某些条 ...

  3. java基础的查缺补漏

    打好基础,加油加油加油! 文章目录 前言 day1(6.16test,7.2) day2(7.3) java运行机制 java关键字 变量定义 常量定义 7.4 7.6 总结 前言 为了方便自己回顾知 ...

  4. Java 基础开发技术查缺补漏笔记

    暑假期间,个人对一些未来研究生阶段可能会常用的编程技术进行重新一轮的系统复习和学习,及希望能够查缺补漏,有所提升.本文也是作为复习和学习过程中的笔记,用于长久的记录.不排除其中可能含有部分疏漏和错误, ...

  5. JAVA核心,200例,查缺补漏

    适用于想要查缺补漏的人:本已经掌握的技术,但是不知道如何表达的人:不断完善技自己,顺带梳理下答案. 主要包括以下模块:Java基础.容器.多线程.反射.对象拷贝.Java Web模块,异常.网络.设计 ...

  6. java基础知识复习(上半)

    java基础知识复习 java为什么被发明? Green项目的确立,应用于像电视盒一样的消费类电子产品,语言本身本身中立. java的三大版本? javaSE的定位在于客户端,只要用于桌面应用软件的编 ...

  7. 2020年最新最全的Java面试经历整理(一次性查缺补漏个够)

    前言 现在已经是2020年了,相信很多人都在准备面试,,如何拿到心仪的 Offer,进入梦寐以求的大厂,实现自己的职业理想,达到程序员的小康水平. 这篇文章主要介绍了刘哥一月份的几次面经,(一次性查缺 ...

  8. 就业前夕——Java查缺补漏(从头学)

    Java查缺补漏 变量 局部变量 方法或语句块内部定义的变量. 生命周期从声明位置开始到方法或语句块结束. 使用前必须先初始化. 成员变量(实例变量) 方法外部.类内部定义的变量(未被static修饰 ...

  9. Java基础知识复习(一)

    Java基础知识复习(一) 目录 Java简介 命名规则 八种基本的数据类型 字面量 类型转换 变量的形态 逻辑运算符 位运算 移位运算 习题知识点 目录 Java简介 Java是由Sun公司在199 ...

  10. 自用的快速复习Java基础知识,不适用于每一个人

    自用的快速复习java基础知识,不适用于每一个人 问题背景 1. 类定义出来的变量称为**对象** [IDEA安装](https://www.jb51.net/article/193853.htm) ...

最新文章

  1. retinaface人脸姿态
  2. 数据链路层、交换机内容整合
  3. PCA人脸识别的python实现
  4. java不带package和带package的编译方式
  5. Nginx安装手冊以及图片server部署
  6. iOS 设备的网页调试工具-Firebug
  7. javascript基础之判断变量类型
  8. Outlook 2013 电子邮件账户设置备份与恢复
  9. 4999元起!华为Mate 30 5G系列今日预售:支持SA及NSA 5G双模
  10. 一行Python代码计算两点间曼哈顿距离
  11. thrift编写服务端 客户端
  12. 迅捷PDF虚拟打印机怎么保存文件
  13. java读取pdf的文字、图片、线条和对应坐标
  14. c语言模拟鼠标键盘控制电脑
  15. Java学习手册:华为2020届校园招聘——软件题
  16. iPhone和iPad等苹果设备自主更新Beta系统
  17. P3324 [SDOI2015]星际战争
  18. 如何更改自己电脑上的公网IP?
  19. Excel函数的使用
  20. 上交公开课张志华--- 统计机器学习的播放顺序

热门文章

  1. python输出一棵树_如何用Python画一棵树
  2. android viewpager中每个view,ViewPager系列之 打造一个通用的ViewPager
  3. 英语学习软件——《经典双语广告语大全》(图)
  4. 关于无法完全停止windowsUpdate的解决方法
  5. html钢笔特效,JS仿Photoshop钢笔工具(贝塞尔曲线可视化操作)效果
  6. ICE for Linux
  7. .NET Standard详解
  8. 迪恩素材教程资源图片下载站源码 dz社区论坛discuz 模板
  9. [毕业设计]威客网站可行性研究报告书
  10. 教程丨一文了解如何在OpenSea上创建自己的NFT商店