额外知识点
属性名和枚举对象的区别
- 属性名通常是指在一个类中定义的变量名
- 枚举类型:是一个特殊的类,用于定义一组相关的有限的常量(即枚举常量)。例如一组表示季节的枚举常量
- 枚举对象:枚举类型的一个实例,有自己的名称和值,与枚举常量的名称和值是相同的。
举例说明
- Season类
package at.guigu.meiju;
public class Season {
private String info;
private Season() {
}
private Season(String info) {
this.info = info;
}
public static final Season SPRING = new Season("春天");
public static final Season SUMMER = new Season("夏天");
public static final Season AUTUMN = new Season("秋天");
public static final Season WINTER = new Season("冬天");
}
Season类中一共有四个枚举常量,以
public static final Season SPRING = new Season("春天");
为例,该枚举常量的名称为SPRING,值为“春天”
- TestSeason类
package at.guigu.meiju;
public class TestSeason {
Season spring = Season.SPRING;
}
Season.SPRING即为枚举对象,spring为一个引用类型的变量(即引用变量的名称),用于引用 Season.SPRING
这个枚举对象(即用于指向枚举对象 Season.SPRING),这使得你可以使用 spring 来操作和引用 Season.SPRING
代表的枚举常量。
枚举对象Season.SPRING的名称为SPRING,值为“春天”
查看是否属于标准类库的类
若包名是以java开头则代表是标准类库的类,反之则不是
公共类
- 在java的一个类中同级别下只允许有一个公共类存在,若有多个则会报错
- 公共类的内部类可以是公共类
枚举
定义
- 某些类型的对象是有限的几个(主要用来解决现实生活中有限的情况)。JDK1.5之后支持enum关键字来定义枚举类型
- 一种特殊的数据类型,用于表示一个固定的值的有限集合。枚举类型在java中是一个类,它的实例是枚举常量
JDK1.5之前的枚举
注意: 为了不能进行无限创建对象从而达到枚举效果,需做如下步骤:
- 构造器私有化(目的:限制创建对象的数量。注意:私有化之后无法创建对象)
- 构造器私有化之后,在其它类中无法创建对象。所以需要提供若干本类属性作为公开静态常量
- 经过以上两个步骤之后,即可完成枚举。此时在测试类中虽无法创建对象但是可以通过引用得到已经规定好的枚举的值,如下图所示
JDK1.5之后的枚举
声明格式
实现步骤
- 右键新建enum
- 写枚举类的构造器及本类属性(注意:本类属性需要全部写在第一部分之前且用逗号分开)
构造器中添加其它普通属性
详见代码截图
注意事项
- enum关键字声明的枚举类中,构造器默认用private修饰且只能用private修饰
- enum关键字声明的枚举类必须在类体的第一部分(即枚举类中第一个分号之前)给出若干本类的属性。(注意:就算不写本类属性你也要将分号加上,否则会报错)
- enum关键字声明的枚举类默认继承自enum类,enum类中toString()方法会被重新定义,其返回的是枚举属性名。如图所示。若你不想返回枚举属性名,则重写toString()方法即可。
枚举提供的常用方法(enum类的方法)
toString()方法和name方法
以上两种方法均为实例方法
enum类替自定义枚举类重写了toString()方法,toString()方法返回该枚举对象名的字符串表示,name方法返回该枚举对象的名称。如图所示
ordinal()方法
其是一个实例方法,返回枚举对象对应的枚举常量的下标(从0开始)。如图所示
values()方法
values()方法是一个静态方法,通过枚举类型本身来调用它,它返回一个包含该枚举类型中所有枚举常量的数组,这个数组的顺序与枚举常量在类型定义中的顺序相同
valueOf(String name)
valueOf()方法是一个静态方法,可以通过枚举类型本身来调用它,它可以将一个字符串转换成对应的枚举对象,如果该字符串不是合法的枚举类型(此处以Seasonn枚举类型为例)名称,则会抛出IllegalArgumentException异常
。该方法对于大小写敏感,输入的字符串必须与枚举常量的名称完全匹配。
注意
实例方法需要创建对象来使用,而静态方法则不需要创建对象就可以被调用。
包装类(封装类)—通过JDK1.9的API学习
注意:包装类的默认值均为null
包装类中对每一个基本数据类型都对应准备了一个引用数据类型
装箱
- 定义:将基本数据类型转为包装类对象(即将基础类型封装成引用类型)
以int类型为例
由图知int的包装类为Integer------可在API搜索Integer
将int类型转换为Integer类型
方式一:通过创建对象实现(JDK1.8之后就已经过时了)
- 将int类型转换为Integer类型
- 将String类型转换为Integer类型
方式二:在方法摘要中寻找参数是int,返回类型是Integer的方法(如图二所示)
- 由以上图二可知,此时可用的方法为valueOf(String s),且此为静态方法(由类名直接调用)。使用方式如图所示
拆箱
- 定义:将包装类对象转换成基本数据类型
以int类型为例
- 将Integer转为int类型------在API中寻找。由图可知,此为普通方法,需要创建对象来调用
- 使用方式如图所示
JDK1.5之后可自动进行装箱和拆箱
注意:只能与自己对应的类型之间才能实现自动装箱与拆箱
包装类对象的特点
包装类缓存对象
由图可知缓存对象就是在方法区中的常量池,两种情况解释均以整型为例。
注意:浮点型没有缓存对象,所以不论你怎么比较都会是false。
情况解释1
创建对象后用==来比较的是创建的对象是否是一样的。一般情况下只要new了,则对象一定不一样,但是由图可看出当包装类的值为1时,a和b的对象一样;当为128时,c和d的对象不一样
原因: 如图所示,在方法区内有一个整数型常量池,当用的是自动装箱时,若值在整数型常量池内,则Integer的引用会指向常量池中的值,反之则会在堆中new对象来保存值
情况解释2
**原因:**利用自动装箱时,首先会在整数型常量池中查找是否有对应的值,若有则会指向常量池中的对象,反之则会在堆中创建对象;而利用new来创建Integer对象时,每new一次,则会在堆中创建一个对象,所以两个结果均为false
内部类
成员内部类
- 语法格式------可以由public static修饰
普通成员内部类
- 定义:一个类声明在另一个类的类体中的类
- 特点:
(1)普通成员内部类可访问外部类的所有成员
(2)普通成员内部类只能声明普通的成员(普通属性、普通方法),不能声明静态成员(如:静态属性、静态方法)
(2)的原因:普通成员内部类在外部类没有创建对象的情况下是不可能被加载的,而内部类只要不被加载,内部类中的成员(普通成员和静态成员)就没办法被使用(静态成员随着类的加载而被加载)。假设可以声明静态成员,而且你用类名将静态成员调用出来了,那创建普通成员内部类还有什么意义?不就相当于将“静态成员是随着类的加载而被加载”这句话给否定了。
普通成员内部类对象创建
- 在外部类的类体中创建------与普通类创建对象没任何区别
- 在外部类的类体外(如:测试类)------需要先创建外部类对象,然后再由外部类对象创建内部类对象
静态成员内部类
- 定义
一个类声明在另一个类的类体中的由static修饰的类
- 与外部类的相同点
(1)都可以继承父类和接口并实现接口
(2)都可以声明属性、方法、构造器等结构,包括静态成员
(3)都可以使用abstract修饰。因此也可被其他类继承
(4)都可以使用final修饰,表时不能被继承
(5)支持多态性
(6)都允许使用java中的访问修饰符和非访问修饰符
- 特点
(1)可声明普通成员(如:普通属性和方法。原因:它是一个独立的类,具有独立的成员变量和方法。它不依赖于外部类的实例,因此可以在内部类中声明任何类型的成员)和静态成员(如:静态属性和静态方法)
(2)静态成员内部类只能访问外部类的静态成员,不能访问外部类的普通成员
(3)静态成员内部类可以访问外部类的静态私有成员(原因:private是只有本类可以访问,而内部类就在本类中且是本类的内部类,所以可以访问)
(4)在外部类的类体内创建内部类对象时,内部类可当作普通类来使用
静态成员内部类创建对象
- 在外部类的类体中创建------与普通类创建对象没任何区别
- 在外部类的类体外(如:测试类)
创建对象时可以直接创建内部类对象,无需先创建外部类对象。创建静态内部类对象时格式如下:外部类类名.内部类类名 abc = new 外部类类名.内部类类名
问题
- 为什么静态成员内部类在外部类的类体外创建对象后不可以访问外部类的普通成员而普通成员内部类可以?
(1)加载顺序不同(先父类在子类,先静态后普通)。静态成员是随着类的加载而被加载,而普通成员是随着对象的创建才会被加载。
(2)静态成员内部类创建对象时并不是先创建外部类的对象,所以不能访问外部类的普通成员只能访问外部类的静态成员
(3)而普通成员内部类则可以,因为在创建普通成员内部类的对象前会先创建外部类的对象,外部类中的普通成员会随着对象的创建而被加载,所以可访问。
- 为什么普通成员内部类在创建对象之前要先创建外部类对象?
类中的静态成员随着类的加载而被加载而普通成员随着对象的创建才会被加载。普通成员内部类属于外部类的普通成员,所以如果你想要创建普通成员内部类的对象你就要先创建外部类的对象使普通成员内部类被加载,这样你才可以创建普通成员内部类的对象。
局部内部类
普通局部内部类
- 定义
一个类声明在类的方法的方法体中的类
- 特点
(1)局部内部类只能声明普通成员,不能声明静态成员。(与普通成员内部类相似)
(2)局部内部类随着方法的运行而被加载
(3)局部内部类可访问外部类的所有成员以及本类的所有成员
(4)局部内部类(Local Inner Class)不能使用访问修饰符public
、private
、protected
或static
进行修饰。局部内部类的可见性仅限于声明它的方法或代码块内部,因此它对外部的类和方法是不可见的,包括不能在其他类中访问。局部内部类通常用于封装和实现某个方法中的辅助逻辑或特定需求的类。由于它们的作用范围仅限于声明它们的方法或代码块,所以它们不需要额外的访问修饰符来限制其可见性。 - 局部内部类对象创建—不能再外部类的类体外创建
由于局部内部类随着方法的加载而被加载,所以创建对象只能在方法体内创建并使用
三种内部类总结
内部类 | 特点 |
---|---|
普通成员内部类 | 可以访问外部类的所有成员;只能声明普通成员不能声明静态成员 |
静态成员内部类 | 只能访问外部类的静态成员;可以声明普通成员和静态成员 |
局部成员内部类 | 可以访问外部类的所有成员; 只能声明普通成员不能声明静态成员 |
匿名内部类------局部内部类的特殊形式
- 特点
作为接口的实现类或者抽象父类的子类出现 - 应用场景
只使用一次,以后不再使用
匿名内部类的调用
举例说明1----采用匿名内部类有名对象的方式
- 以作为接口的实现类为例
假设有一个接口及该接口的实现类,如图一图二所示
创建接口的测试类,代码及运行结果如下(注意:new后面的是该接口的实现类)
现假设你没有这个接口的实现类或者有该接口的实现类但是忘记了该实现类的名字,此时没办法去创建对象,则按如图所示方式处理(该处理方式即为匿名内部类)
- 以抽象父类的子类为例
假设有个抽象父类及其子类,如图一图二所示
创建抽象父类的测试类,代码及运行结果如下(注意:new后面的是该抽象父类的子类)
现假设你没有这个抽象父类的子类或者有该抽象父类的子类但是忘记了该子类的名字,此时没办法去创建对象,则按如图所示方式处理(该处理方式即为匿名内部类)
举例说明2----采用匿名内部类匿名对象的方式
- 以作为接口的实现类为例—只写最后测试类中的实现代码截图
- 以抽象父类的子类为例—只写最后测试类中的实现代码截图
匿名内部类的传参
举例说明1----采用匿名内部类有名对象的方式
- 以作为接口的实现类为例
- 以抽象父类的子类为例
举例说明2----采用匿名内部类匿名对象的方式
- 以作为接口的实现类为例
- 以抽象父类的子类为例
Lambda表达式
举例说明
接口
- 传参形式的Lambda表达式写法
package at.heima;
public interface Swimming {
public void swim();
}
测试类
package at.heima;
public class TestOne {
public static void main(String[] args) {
//匿名内部类
goSwimming(new Swimming(){
@Override
public void swim() {
System.out.println("一起去游泳");
}
});
goSwimming(() -> {
System.out.println("一起去游泳");
});
goSwimming(() -> System.out.println("一起去游泳"));
}
public static void goSwimming(Swimming swimming) {
swimming.swim();
}
}
- 非传参形式的Lambda表达式写法
package at.guigu.lambda;
public interface Swimming {
public void swim();
}
package at.guigu.lambda;
public class TestSwimming {
public static void main(String[] args) {
Swimming s1 = new Swimming() {
@Override
public void swim() {
System.out.println("一起游泳");
}
};
s1.swim();
Swimming s2 = () -> System.out.println("游泳呀");
s2.swim();
new Swimming() {
@Override
public void swim() {
System.out.println("游泳");
}
}.swim();
((Swimming) () -> System.out.println("一起游泳啊")).swim();
}
}
由以上示例可知,Lambda表达式是对匿名内部类的优化,Lambda表达式代码更少,关注点更加明确
摁住Alt+回车,可将匿名内部类替换为Lambda表达式,如图及代码所示
package at.heima;
public class TestOne {
public static void main(String[] args) {
//匿名内部类转换而成的Lambda表达式
goSwimming(() -> System.out.println("一起去游泳"));
goSwimming(() -> {
System.out.println("一起去游泳");
});
}
public static void goSwimming(Swimming swimming) {
swimming.swim();
}
}
- 函数式编程思想的概述
在数学中,函数就是有输入量、输出量的一套计算方案,也就是”拿数据做操作“
面向对象思想强调”必须通过对象的形式来做事情“
函数式思想则尽量忽略面向对象的复杂语法:”强调做什么,而不是以什么形式去做“
Lambda表达式格式
函数式接口:就是只有一个抽象方法的接口
函数式接口可以被隐式转换为Lambda表达式
-
Lambda表达式三要素
(1)形式参数
(2)箭头
(3)代码块
-
Lambda表达式的标准格式
(形式参数)-> {代码块}
形式参数:是接口中抽象方法的参数。若有多个参数则参数之间用逗号隔开;若没有参数则留空即可
->:代表指向动作,即将小括号中的参数传递到大括号中进行使用
代码块:具体要做的事情,也就是以前写的方法的方法体中的内容
- Lambda表达式的使用前提
(1)必须有一个接口
(2)该接口有且只能有一个抽象方法
Lambda表达式的省略模式
- 省略规则
(1)参数类型可以省略,但是有多个参数的情况下不能只省略一个
(2)若参数只有一个,则小括号可以省略;没有参数时,小括号不能省略
(3)若代码块的语句只有一条则可以省略大括号、分号和eturn
例题
例题1:编写一个ShowHandler接口,该接口中存在一个抽象方法show(该方法无参数无返回值),在测试类(TestShowHandle)中存在一个方法(useShowHandle),该方法的参数是ShowHandle类型的,在方法内部调用了ShowHandle的show方法
ShowHandler接口
package at.guigu.lambda;
public interface ShowHandler {
void show();
}
测试类
package at.guigu.lambda;
public class TestShowHandle {
public static void main(String[] args) {
ShowHandler sh = new ShowHandler() {
@Override
public void show() {
System.out.println("show------------");
}
};
useShowHandle(sh);
ShowHandler sh1 = () -> System.out.println("---show------------");
useShowHandle(new ShowHandler() {
@Override
public void show() {
System.out.println("123");
}
});
useShowHandle(() -> System.out.println("show"));
}
public static void useShowHandle(ShowHandler show) {
show.show();
}
}
例题2:编写一个StringHandler接口,该接口中存在一个抽象方法printMessage(该方法有参数无返回值),在测试类(TestStringHandle)中存在一个方法(useStringHandler),该方法的参数是StringHandler类型的,在方法内部调用了StringHandler的printMessage方法
StringHandler接口
package at.guigu.lambda;
public interface StringHandler {
void printMessage(int a, String b);
}
测试类
package at.guigu.lambda;
public class TestStringHandler {
public static void main(String[] args) {
StringHandler string = new StringHandler() {
@Override
public void printMessage(int a, String b) {
System.out.println(a + "122");
}
};
useStringHandler(string);
StringHandler str = (a, b) -> System.out.println("123" + a);
useStringHandler(str);
useStringHandler(new StringHandler() {
@Override
public void printMessage(int a, String b) {
System.out.println("a = " + a);
}
});
useStringHandler((a, b) -> System.out.println("a==" + a));
}
public static void useStringHandler(StringHandler string) {
string.printMessage(2,"qwe");
}
}
例题3:编写一个RandomNumHandler接口,该接口中存在一个抽象方法getNumber(该方法无参数有返回值),在测试类(TestRandomNumHandler)中存在一个方法(useRandomNumHandle),该方法的参数是RandomNumHandler类型的,在方法内部调用了RandomNumHandler的getNumber方法
RandomNumHandler接口
package at.guigu.lambda;
public interface RandomNumHandler {
int getNumber();
}
/*
若Lambda所操作的接口中的方法有返回值则一定要通过return语句将结果返回,否则会出现编译错误
*/
测试类
package at.guigu.lambda;
public class TestRandomNumHandler {
public static void main(String[] args) {
RandomNumHandler random1 = new RandomNumHandler() {
@Override
public int getNumber() {
int a = 2;
return a;
}
};
useRandomNumHandler(random1);
RandomNumHandler random2 = () -> {
int a = 2;
return a;
};
useRandomNumHandler(random2);
useRandomNumHandler(new RandomNumHandler() {
@Override
public int getNumber() {
int a = 3;
return a;
}
});
useRandomNumHandler(() -> {
int a = 5;
return a;
});
}
public static void useRandomNumHandler (RandomNumHandler random) {
System.out.println(random.getNumber());
}
}
注意:若Lambda所操作的接口中的方法有返回值,一定要通过return语句将结果返回,否则会出现编译错误
例题4:编写一个Calculator接口,该接口中存在一个抽象方法calc(该方法有参数有返回值),在测试类(TestCalculator)中存在一个方法(useCalculator),该方法的参数是Calculator类型的,在方法内部调用了Calculator的calc方法
Calculator接口
package at.guigu.lambda;
public interface Calculator {
String calc(int a, String b);
}
测试类
package at.guigu.lambda;
import java.util.Calendar;
public class TestCalculator {
public static void main(String[] args) {
Calculator calc1 = new Calculator() {
@Override
public String calc(int a, String b) {
return "a:" + a + "b:" + b;
}
};
useCalculator(calc1);
Calculator calc2 = (a, b) -> {
return "a::" + a + "b::" + b;
};
useCalculator(calc2);
Calculator calc3 = (a, b) -> "a:::" + a + "b:::" + b;
useCalculator(calc3);
useCalculator(new Calculator() {
@Override
public String calc(int a, String b) {
return "a=" + a + "b=" + b;
}
});
useCalculator((a, b) -> {
return "a==" + a + "b==" + b;
});
useCalculator((a, b) -> "a==" + a + "b==" + b);
}
public static void useCalculator(Calculator calculator) {
System.out.println(calculator.calc(2, "李白"));
}
}
Lambda表达式和匿名内部类的区别
-
所属类型不同
匿名内部类:可以是接口及其实现类;也可以是抽象父类及其子类
Lambda表达式:只能是接口
-
使用限制不同
若接口中有且仅有一个抽象方法,则可以使用Lambda表达式,也可以使用匿名内部类
若接口中多于一个抽象方法,则只能使用匿名内部类
-
实现原理不同
匿名内部类:编译之后,产生一个单独的.class字节码文件
Lambda表达式:编译之后,没有一个单独的.class字节码文件,对应的字节码会在运行时动态生成
方法引用
方法引用是用来直接访问类或实例的已经存在的方法或构造方法
标志性符号两个冒号::
静态方法的引用
- 格式
类名::静态方法
类名是包含静态方法的类名
- 使用场景
若某个Lambda表达式只是调用一个静态方法且前后参数的形式一致则可以使用静态方法引用
- 代码示例
假设现在有一个共用Student类,代码如下:
package at.guigu.jihe.collection;
public class Student {
private String name;
private double height;
private int age;
public Student() {
}
public Student(String name, double height, int age) {
this.name = name;
this.height = height;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "name:" + name + ",height:" + height + ",age:" + age;
}
}
(1)传统方式
现在写一个测试类TestStudent,写出Student类的对象数组来根据年龄进行对象数组的升序排列,代码如下:
注意:利用了重写Comparator接口中的compare方法来比较年龄的大小
package at.guigu.jihe.collection;
import java.util.Arrays;
import java.util.Comparator;
public class TestStudent {
public static void main(String[] args) {
Student[] stu = new Student[4];
stu[0] = new Student("蜘蛛精", 169.5, 23);
stu[1] = new Student("紫霞", 163.8, 26);
stu[2] = new Student("紫霞", 163.8, 26);
stu[3] = new Student("至尊宝", 167.5, 24);
Arrays.sort(stu, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getAge() - o2.getAge();//按照年龄进行升序排序
}
});
//Lambda表达式形式
Arrays.sort(stu, (o1, o2) -> o1.getAge() - o2.getAge());
for (Student student : stu) {
//打印出排好序的Student对象数组
System.out.println(student.toString());
}
}
}
(2)方法引用
假设我现在不用传统的两个接口(Comparable、Comparator)来比较年龄的大小,而是自己写了一个类CompareByData类,该类中有一个静态方法来比较年龄的大小,如下代码所示:
package at.guigu.jihe.collection;
public class CompareByData {
public static int compareByAge(Student o1, Student o2) {
return o1.getAge() - o2.getAge();
}
}
此时测试代码如下:
package at.guigu.jihe.collection;
import java.util.Arrays;
import java.util.Comparator;
public class TestStudent1 {
public static void main(String[] args) {
Student[] stu = new Student[4];
stu[0] = new Student("蜘蛛精", 169.5, 23);
stu[1] = new Student("紫霞", 163.8, 26);
stu[2] = new Student("紫霞", 163.8, 26);
stu[3] = new Student("至尊宝", 167.5, 24);
Arrays.sort(stu, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getAge() - o2.getAge();//按照年龄进行升序排序
}
});
//重写Comparator接口中的compare方法
Arrays.sort(stu, (o1, o2) -> o1.getAge() - o2.getAge());
//重写Comparator接口中的compare方法---调用CompareByData类中的静态方法compareByAge来比较
Arrays.sort(stu, (o1, o2) -> CompareByData.compareByAge(o1, o2));
}
}
注意:Arrays.sort(stu, (o1, o2) -> CompareByData.compareByAge(o1, o2));
该行代码中,Lambda表达式只是调用一个静态方法,且前后参数形式一致,所以可以使用静态方法的引用
方法引用代码如下:
package at.guigu.jihe.collection;
import java.util.Arrays;
import java.util.Comparator;
public class TestStudent1 {
public static void main(String[] args) {
Student[] stu = new Student[4];
stu[0] = new Student("蜘蛛精", 169.5, 23);
stu[1] = new Student("紫霞", 163.8, 26);
stu[2] = new Student("紫霞", 163.8, 26);
stu[3] = new Student("至尊宝", 167.5, 24);
Arrays.sort(stu, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getAge() - o2.getAge();//按照年龄进行升序排序
}
});
//重写Comparator接口中的compare方法
Arrays.sort(stu, (o1, o2) -> o1.getAge() - o2.getAge());
//重写Comparator接口中的compare方法---调用CompareByData类中的静态方法compareByAge来比较
Arrays.sort(stu, (o1, o2) -> CompareByData.compareByAge(o1, o2));
//静态方法引用(类名::静态方法)
Arrays.sort(stu, CompareByData::compareByAge);
for (Student student : stu) {
System.out.println(student.toString());
}
}
}
实例方法引用
- 格式
对象名::实例方法
- 使用场景
若某个Lambda表达式里只是调用一个实例方法且前后参数的形式一致则可以使用实例方法引用
- 代码示例
仍以Student类为基础展开
CompareByData类中有一个普通方法compareByAgeDesc(降序排序),代码如下:
package at.guigu.jihe.collection;
public class CompareByData {
public static int compareByAge(Student o1, Student o2) {
return o1.getAge() - o2.getAge();//升序排序规则
}
public int compareByAgeDesc(Student o1, Student o2) {
return o2.getAge() - o1.getAge();//降序排序规则
}
}
测试类中代码如下:
package at.guigu.jihe.collection;
import java.util.Arrays;
import java.util.Comparator;
public class TestStudent2 {
public static void main(String[] args) {
Student[] stu = new Student[4];
stu[0] = new Student("蜘蛛精", 169.5, 23);
stu[1] = new Student("紫霞", 163.8, 26);
stu[2] = new Student("紫霞", 163.8, 26);
stu[3] = new Student("至尊宝", 167.5, 24);
Arrays.sort(stu, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o2.getAge() - o1.getAge();//按照年龄进行降序排序
}
});
//重写Comparator接口中的compare方法
Arrays.sort(stu, (o1, o2) -> o2.getAge() - o1.getAge());//按照年龄进行降序排序
//重写Comparator接口中的compare方法---调用CompareByData类中的实例方法compareByAgeDesc来比较
Arrays.sort(stu, (o1, o2) -> new CompareByData().compareByAgeDesc(o1, o2));//按照年龄进行降序排序
for (Student student : stu) {
System.out.println(student.toString());
}
}
}
注意:Arrays.sort(stu, (o1, o2) -> new CompareByData().compareByAgeDesc(o1, o2));
该行代码中,Lambda表达式只是调用一个实例方法,且前后参数形式一致,所以可以使用实例方法的引用
代码如下:
package at.guigu.jihe.collection;
import java.util.Arrays;
import java.util.Comparator;
public class TestStudent2 {
public static void main(String[] args) {
Student[] stu = new Student[4];
stu[0] = new Student("蜘蛛精", 169.5, 23);
stu[1] = new Student("紫霞", 163.8, 26);
stu[2] = new Student("紫霞", 163.8, 26);
stu[3] = new Student("至尊宝", 167.5, 24);
Arrays.sort(stu, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o2.getAge() - o1.getAge();//按照年龄进行降序排序
}
});
//重写Comparator接口中的compare方法
Arrays.sort(stu, (o1, o2) -> o2.getAge() - o1.getAge());//按照年龄进行降序排序
//重写Comparator接口中的compare方法---调用CompareByData类中的实例方法compareByAgeDesc来比较
Arrays.sort(stu, (o1, o2) -> new CompareByData().compareByAgeDesc(o1, o2));//按照年龄进行降序排序
//实例方法引用
Arrays.sort(stu, new CompareByData()::compareByAgeDesc);
for (Student student : stu) {
System.out.println(student.toString());
}
}
}
特殊接口中的方法引用
- 代码示例
public class TestThree {
public static void main(String[] args) {
HashMap<String, Integer> hm = new HashMap<>();
hm.put("张三", 15);
hm.put("李四", 16);
hm.put("王五", 17);
Map<String, Integer> map = hm.entrySet().stream()
.filter(s -> s.getValue() > 15)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
System.out.println(map);
}
}
代码中最后将筛选后的流保存为Map集合时利用了方法引用,其中
Entry
为Map
接口中的内置静态接口,所以可以直接用Map.Entry
来调用,而getKey
和getValue
为静态内置接口Entry
中的抽象方法,而Map接口的实现类会提供对Map.Entry
接口方法的具体实现,所以此时可以使用方法引用进行方法的使用
特定类型方法的引用
- 格式
类型::方法
- 使用场景
若某个Lambda表达式只是调用一个实例方法且前面的参数列表中的第一个参数是作为方法的主调,后面的所有参数都是作为该实例方法的入参的,则可以使用特定类型的方法引用
- 注意
1.特定类型:指代的是一个具体的类
2.特定类型的方法引用中的方法必须在特定类型中是存在的,也就是说类型指的是该方法所对应的类
- 代码示例
假设现在有个字符串数组,来进行排序,则普通排序代码如下:
package at.guigu.methodyinyong;
import java.util.Arrays;
import java.util.Comparator;
//特定类型的方法引用
public class TestOne {
public static void main(String[] args) {
String[] names = {"body", "angle", "Andy", "dlei", "caocao", "Babo", "jack", "Cici"};
Arrays.sort(names);//默认按照字符串的首字母编号进行升序排序
System.out.println(Arrays.toString(names));//将String类型的数组转换为字符串表示形式,字符串的表示形式由数组的元素列表组成
}
}
现在要求忽略首字符大小写进行排序,代码如下所示:
package at.guigu.methodyinyong;
import java.util.Arrays;
import java.util.Comparator;
//特定类型的方法引用
public class TestOne {
public static void main(String[] args) {
String[] names = {"body", "angle", "Andy", "dlei", "caocao", "Babo", "jack", "Cici"};
//要求:忽略首字符大小写进行排序
Arrays.sort(names, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareToIgnoreCase(o2);
}
});
//Lambda表达式形式
Arrays.sort(names, (o1, o2) -> o1.compareToIgnoreCase(o2));
System.out.println(Arrays.toString(names));
}
}
注意:Arrays.sort(names, (o1, o2) -> o1.compareToIgnoreCase(o2));
在该代码中第一个参数作为了方法的主调,而第二个参数作为该实例方法的入参的,所以可使用特定类型的方法引用,代码如下:
package at.guigu.methodyinyong;
import java.util.Arrays;
import java.util.Comparator;
//特定类型的方法引用
public class TestOne {
public static void main(String[] args) {
String[] names = {"body", "angle", "Andy", "dlei", "caocao", "Babo", "jack", "Cici"};
//要求:忽略首字符大小写进行排序
Arrays.sort(names, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareToIgnoreCase(o2);
}
});
//Lambda表达式形式
Arrays.sort(names, (o1, o2) -> o1.compareToIgnoreCase(o2));
//特定类型的方法引用
Arrays.sort(names, String::compareToIgnoreCase);
System.out.println(Arrays.toString(names));
}
}
注意:Arrays.sort(names, String::compareToIgnoreCase);
在该代码中类型String
一定为方法compareToIgnoreCase
所在的类
构造器引用
- 格式
类名::new
- 使用场景
若某个Lambda表达式只是在创建对象,并且前后参数情况一致,就可以使用构造器引用
- 代码示例
创建一个Car类,代码如下:
package at.guigu.methodyinyong;
public class Car {
private String name;
private int price;
public Car() {
}
public Car(String name, int price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return "name:" + name + ",price:" + price;
}
}
创建汽车对象
package at.guigu.methodyinyong;
public class TestCar {
public static void main(String[] args) {
//方式一
CreatCar cc1 = new CreatCar(){
@Override
public Car creat(String name, int price) {
return new Car(name, price);
}
};
Car car1 = cc1.creat("现代", 12500);
System.out.println(car1);
//方式二
CreatCar cc2 = (name, price) -> new Car(name, price);
Car car2 = cc2.creat("东风", 5000);
System.out.println(car2);
//方式三:想创建哪个类的对象就用哪个类的类名
CreatCar cc3 = Car::new;
Car car3 = cc3.creat("大众", 100000);
System.out.println(car3);
}
}
//接口的作用:创建Car对象
interface CreatCar{
Car creat(String name, int price);
}