目录
一,内部类
1.成员内部类
1). 在类里面定义的类叫做成员内部类
2).如何定义一个内部类:
/*
格式:
class 外部类名{
修饰符 class 内部类名{
}
}
*/
//Outer是外部类
class Outer {
//Inner是内部类
public class Inner {
}
}
3).内部类的分类
a. 在类的成员位置:成员内部类
b.在类方法内位置:局部内部类(后面说)
4).外部如何使用普通成员内部类
格式:外部类名.内部类名 对象名=new 外部对象().new 内部对象()
范例: Outer.Inner oi=new Outer().new Inter();
5).成员内部类访问非静态成员时有什么特点
a.内部类可以直接访问外部类的成员,包括私有
b.外部类要访问内部类的成员,必须先创对象
package com.itheima01;
public class Outer {
//成员变量
int a = 1;
public void outerMethod(){
//内部可以访问外部, 外部不能访问内部
// System.out.println(b);
}
//成员内部类
class Inner{
int b = 2;
public void innerMethod(){
System.out.println(a);
}
}
}
//import com.itheima01.Outer.Inner;
public class Demo {
public static void main(String[] args) {
Outer outer = new Outer();
System.out.println(outer.a); // 1
Outer.Inner inner = outer.new Inner();
/*
todo
1. 语法
外部类名.内部类名 对象名=new 外部对象().new 内部对象()
2. 创建了两个对象
一个Outer对象,一个是Inner对象
3. 变量oi指向的是Inner对象
*/
Outer.Inner oi = new Outer().new Inner();
System.out.println(oi.b); //2
}
}
2.私有成员内部类
1).私有成员内部类的访问特点
只能在自己所在的外部类创建对象访问
public class Outer {
int a = 1;
//私有成员内部类: 只能本类内部使用
private class Inner{
public void method01(){
System.out.println(a);
}
}
//静态成员内部类
public static class Inner2{
public void method02(){
// System.out.println(a);
}
}
public void method(){
Inner i = new Inner();
}
}
2).如何创建静态成员内部类对象
外部类名.内部类名 对象名 = new 外部类名.内部类名();
3).如何访问静态成员内部类中的静态方法
外部类名.内部类名.方法名();
public class Demo {
public static void main(String[] args) {
Outer o = new Outer();
Outer.Inner2 oi2 = new Outer.Inner2();
}
}
3.局部内部类
1).定义:在方法中创建的类
2).局部内部类如何使用
局部内部类,外界是无法直接使用的 ,需要在方法里面创建对象使用
3).局部内部类的特点
局部内部类是方法里面定义的类,可以直接访问外部类的成员,也可以访问方法里面的 局部变量
public class Demo02 {
public static void main(String[] args) {
int a = 1;
/*
局部内部类: 在方法内定义的类
仅限当前方法使用
*/
class Inner3{
int b = 2;
public void m1(){
System.out.println(a);
}
}
Inner3 i3 = new Inner3();
System.out.println(i3.b);
}
public static void method(){
}
}
4.匿名内部类(重点) (匿名: 匿的是类名)
1).定义:特殊的局部内部类
2).匿名内部类的前提
存在一个类或者接口,这里的类可以使具体类也可以是抽象类
3).如何使用匿名内部类
格式:new 类名(){重写方法}
格式:new 接口名 (){实现方法}
例如:
new Inter(){
@Override
public void method(){}
}
4).内部类的本质
本质:是一个继承了该类或实现了该接口的子类/实现类匿名对象
理解:匿名内部类是将(继承/实现,方法重写,创建对象)三合一,一起进行
public class Demo {
public static void main(String[] args) {
//TODO 需求: 创建Animal的子类对象
abstract class Animal{
public abstract void shout();//叫
}
//1. 定义一个Animal的子类 (类名不重要,类体才重要: 方法重写)
class Dog extends Animal{
@Override
public void shout() {
System.out.println("汪汪汪..");
}
}
//2. 创建对象
Animal d = new Dog();
d.shout();
/*
TODO 匿名内部类 (匿名: 匿的是类名)
1. 语法:
父类类型/父接口类型 变量 = new 父类类型/父接口类型(){ //方法重写 }
Animal a = new Animal(){
//方法重写
}
2. 作用: 简化语法, 它是定义类+创建对象的二合一
*/
//抽象类类型 变量 = 此抽象类的子类对象
Animal a = new Animal(){//类体
@Override
public void shout() {
System.out.println("喵喵叫");
}
};
a.shout();
}
}
public class Demo02 {
public static void main(String[] args) {
// Usb u1 = new Mouse();
// u1.chongDian();
Usb u2 = new Usb(){
@Override
public void chongDian() {
System.out.println("给键盘充电...");
}
};
u2.chongDian();
}
}
interface Usb{
void chongDian();
}
//class Mouse implements Usb{
// @Override
// public void chongDian() {
// System.out.println("给鼠标充电");
// }
//}
5.匿名内部的引用场景(重点)
1). 匿名内部类在开发中的使用
当某个方法的参数类型是接口或者抽象类类型时
package com.itheima04;
/*
TODO: 匿名内部类的运用场景
当某个方法的参数类型是一个接口或抽象类 类型
*/
public class Demo {
public static void main(String[] args) {
//本来: 先定义类,后创建对象,好麻烦
/* class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("run1....");
}
}
Runnable mr = new MyRunnable();
Runnable mr2 = new MyRunnable();
method(mr);
method(mr2);*/
//匿名内部类: 将定义类+创建对象 二合一
/* Runnable r = new Runnable(){
@Override
public void run() {
System.out.println("run2....");
}
};
method(r);*/
//不用变量接收,匿名对象更简洁
method(new Runnable(){
@Override
public void run() {
System.out.println("run3....");
}
});
/* method(new Runnable(){
@Override
public void run() {
System.out.println("run3....");
}
});*/
//TODO 匿名内部类因为没有类名,只能一次性使用
}
public static void method(Runnable runnable){
runnable.run();
}
}
6.小节
1).什么是内部类? 在类中定义一个类
2).内部类的分类? 成员内部类和局部内部类
3).如何使用成员内部类? 外部类名.内部类名 对象名 = new 外部对象名().内部对象名();
4).成员内部类访问时有什么特点? 内部类可以访问外部类的成员,包括私有,外部类要 访问内部类的非静态成员,必须创建对象。
5).私有成员内部类的访问特点? 只能在自己所在外部类里创建对象访问
6).如何创建静态成员内部类对象? 外部类名.内部类名 对象名 = new 外部类名.内部类名();
7).如何访问静态成员内部类中的静态方法? 外部类名.内部类名.方法名();
8).局部内部类的使用方式? 外界无法直接使用,只能在方法里面创建对象访问使用
9).局部内部类的特点? 定义在方法里面的类,可以访问外部类的成员和方法的局部变量
10).什么是匿名内部类? 没有显示名字的内部类
11).匿名内部类的前提? 存在一个类或者接口,这里的类可以是抽象类和具体类
12).如何使用匿名内部类? 格式:new 类名(){重写方法} new 接口名(){重写方法}
13).匿名内部类的理解? 是一个继承了该类或者实现了该接口的子类匿名对象
14).匿名内部类的引用场景? 当某个方法的参数是一个接口或者是一个抽象类的时候即 当发现某个方法需要接口或者抽象类的子类对象,我们就 可以床底一个匿名内部类过去,简化代码。
二,JDK 新特性(Lambda表达式)(重点)
1.函数式编程思想
函数式编程: 关注方法
a. 入参: 形参
b. 逻辑: 方法体
c. 出参: 返回值
比匿名内部类更简洁
/*
TODO:
0. 小技巧: 反向定义
1). 正常流程: 先定义,后调用
2). 现在: 先调用,后定义(可以利用编程工具的提示工具,提高代码编写效率)
*/
public class Demo {
public static void main(String[] args) {
//匿名内部类: 面向对象编程
goSwimming(new Swimming(){
@Override
public void swim() {
System.out.println("走,去游泳...");
}
});
/*
函数式编程: 关注方法
1. 入参: 形参
2. 逻辑: 方法体
3. 出参: 返回值
比匿名内部类更简洁!!!
*/
goSwimming(() -> {
System.out.println("go, swim...");
});
}
private static void goSwimming(Swimming swimming) {
swimming.swim();
}
}
interface Swimming{
void swim();
}
2.Lambda表达式的格式说明和前提条件(重点)
1).Lambda表达式的前提条件
有一个接口,该接口有且仅有一个抽象方法
2).如何使用Lambda表达式?
组成Lambda表达式的三要素: 形式参数 箭头 代码块 即(形式参数)-> {代码块}
a.形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
b.->:由英文中画线和大于符号组成,固定写法。代表指向动作
c.代码块:是我们具体要做的事情,也就是以前我们写的方法体内容
/*
TODO:
0. 小技巧: 反向定义
1). 正常流程: 先定义,后调用
2). 现在: 先调用,后定义(可以利用编程工具的提示工具,提高代码编写效率)
*/
public class Demo {
public static void main(String[] args) {
//匿名内部类: 面向对象编程
goSwimming(new Swimming(){
@Override
public void swim() {
System.out.println("走,去游泳...");
}
});
/*
函数式编程: 关注方法
1. 入参: 形参
2. 逻辑: 方法体
3. 出参: 返回值
比匿名内部类更简洁!!!
*/
goSwimming(() -> {
System.out.println("go, swim...");
});
}
private static void goSwimming(Swimming swimming) {
swimming.swim();
}
}
interface Swimming{
void swim();
}
3.有参无返回值的Lambda表达式
package com.itheima.test3;
public class StringHandlerDemo {
/*
1.首先存在一个接口(StringHandler)
2.在该接口中存在一个抽象方法(printMessage),该方法是有参数无返回值
3.在测试类(StringHandlerDemo)中存在一个方法(useStringHandler)
方法的的参数是StringHandler类型的
在方法内部调用了StringHandler的printMessage方法
*/
public static void main(String[] args) {
useStringHandler(new StringHandler() {
@Override
public void printMessage(String msg) {
System.out.println("我是匿名内部类" + msg);
}
});
// Lambda实现
useStringHandler( msg -> System.out.println("我是Lambda表达式" + msg));
}
public static void useStringHandler(StringHandler stringHandler){
stringHandler.printMessage("itheima");
}
}
interface StringHandler {
void printMessage(String msg);
}
4.无参有返回值的Lambda表达式
import java.util.Random;
public class RandomNumHandlerDemo {
/*
1. 首先存在一个接口(RandomNumHandler)
2. 在该接口中存在一个抽象方法(getNumber),该方法是无参数但是有返回值
3. 在测试类(RandomNumHandlerDemo)中存在一个方法(useRandomNumHandler)
方法的的参数是RandomNumHandler类型的
在方法内部调用了RandomNumHandler的getNumber方法
*/
public static void main(String[] args) {
useRandomNumHandler(new RandomNumHandler() {
@Override
public int getNumber() {
Random r = new Random();
int num = r.nextInt(10) + 1;
return num;
}
});
useRandomNumHandler( () -> {
Random r = new Random();
int num = r.nextInt(10) + 1;
return num;
// 注意: 如果lambda所操作的接口中的方法, 有返回值, 一定要通过return语句, 将结果返回
// 否则会出现编译错误
} );
}
public static void useRandomNumHandler(RandomNumHandler randomNumHandler){
int result = randomNumHandler.getNumber();
System.out.println(result);
}
}
interface RandomNumHandler {
int getNumber();
}
5.带参带返回值的Lambda表达式
public class Demo02 {
public static void main(String[] args) {
//匿名内部类方式
method(new Calculator() {
@Override
public int calc(int a, int b) {
return a + b;
}
});
//lambda方式
method(
(int a, int b) -> {
return a + b;
}
);
}
private static void method(Calculator calculator) {
int result = calculator.calc(4, 2);
System.out.println(result);
}
}
interface Calculator{
int calc(int a, int b);
}
6.函数式接口
/*
TODO: lambda表达式
1. 使用前提: 有一个接口,并且此接口的抽象方法有且仅有一个(函数式接口)
1). 语法
(形参) -> {方法体}
2). 没有方法声明(修饰符 返回值 方法名)
抽象方法只有一个,lambda就是这个此抽象方法的方法重写,无需进行方法声明
3). 函数式接口注解
检查当前接口是否是函数式接口,如果不是,编译报错
@FunctionalInterface
*/
public class Demo {
public static void main(String[] args) {
keepPet(
(String sth) -> {
System.out.println("狗吃" + sth);
}
);
keepPet(new Animal() {
@Override
public void eat(String sth) {
System.out.println("猫吃" + sth);
}
});
}
private static void keepPet(Animal a) {
a.eat("白菜");
}
}
//函数式接口: 检查当前接口是否是函数式接口,如果不是,编译报错
@FunctionalInterface
interface Animal{
void eat(String sth);
}
7.Lambda表达式的省略模式
省略的规则:
参数类型可以省略。但是有多个参数的情况下,不能只省略一个
如果参数有且仅有一个,那么小括号可以省略
如果代码块的语句只有一条,可以省略大括号和分号,和return关键字
/*
TODO lambda的简略写法
1. 标准写法
(形参列表) -> {方法体}
2. 简略语法
1). 参数类型可以省略。但是有多个参数的情况下,不能只省略一个(要么都省略,要么都不省略)
2). 如果参数有且仅有一个,那么小括号可以省略
3). 如果代码块的语句只有一条,可以省略大括号和分号,和return关键字
*/
public class Demo {
public static void main(String[] args) {
//lambda的标准写法
keepPet(
(String sth) -> {
System.out.println("狼吃" + sth);
}
);
//简略写法
//1). 参数类型可以省略。但是有多个参数的情况下,不能只省略一个(要么都省略,要么都不省略)
keepPet(
(sth) -> {
System.out.println("狼吃" + sth);
}
);
//2). 如果参数有且仅有一个,那么小括号可以省略
keepPet(
sth -> {
System.out.println("狼吃" + sth);
}
);
//3). 如果代码块的语句只有一条,可以省略大括号和分号,和return关键字
keepPet(
sth -> System.out.println("狼吃" + sth)
);
}
private static void keepPet(Animal a) {
a.eat("腊肠");
}
}
@FunctionalInterface
interface Animal{
void eat(String sth);
}
public class Demo02 {
public static void main(String[] args) {
//标准写法
method(
(int a, int b) -> {
return a + b;
}
);
//简略写法
method(
(a,b) -> a+b
);
}
private static void method(Calculator c) {
int result = c.calc(4, 2);
System.out.println(result);
}
}
@FunctionalInterface
interface Calculator{
int calc(int a, int b);
}
8.Lambda表达式的应用场景
当某个方法的参数类型是一个接口类型,并且接口只有一个抽象方法
9.Lambda表达式和匿名内部类的区别
-
所需类型不同
-
匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
-
Lambda表达式:只能是接口
-
-
使用限制不同
-
如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
-
如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
-
-
实现原理不同
-
匿名内部类:编译之后,产生一个单独的.class字节码文件
-
Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成
-
/*
TODO 匿名内部类和lambda表达式区别
1. 使用场景
1). lambda只能用于函数式接口场景
2). 匿名内部类不仅可用于类,也可以用于接口(多用于抽象类和接口)
lambda表达式确实更简洁,不能完全替代匿名内部类(比如有多个抽象方法的接口)
2. 本质区别
1). 匿名内部类还是类
2). lambda表达式不是类
lambda运行效率比匿名内部类高
3). 语法注意点
匿名内部类可以独立存在
lambda不可以,必须依赖上下文
*/
public class Demo {
public static void main(String[] args) {
// Person p = new Person(){
//
// };
//编译出: Demo$1.class
Animal a = new Animal() {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
};
//不会独立编译出字节码
Animal a2 = () -> System.out.println("狗吃骨头");
//匿名对象
new Animal() {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
};
//lambda必须依赖上下文
Animal a3 = () -> System.out.println("狗吃骨头");
method(() -> System.out.println("狗吃骨头"));
}
private static void method(Animal a) {
}
}
//class Person{
// public void show(){
//
// }
//}
interface Animal{
void eat();
}
三,Arrsys工具类
1.Arrays 基本使用
Arrays是操作数组的工具类,它可以很方便的对数组中的元素进行遍历、拷贝、排序等操作。
import java.util.Arrays;
import java.util.function.IntToDoubleFunction;
/*
TODO: 数组工具类 Arrays
*/
public class Demo {
public static void main(String[] args) {
int[] array = {10,20,30};
/* for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}*/
//TODO: 1.将数组的元素拼接成字符串
String s = Arrays.toString(array);
System.out.println(s); //[10, 20, 30]
// int[] array2 = new int[array.length];
// for (int i = 0; i < array.length; i++) {
// array2[i] = array[i];
// }
// System.out.println(Arrays.toString(array2));
/*
TODO: 2. 将数组指定索引的元素拷贝到一个新数组中
int[] original, 原数组
int from, 起始索引 (包括)
int to 结束索引 (不包括)
[form,to)
*/
int[] array2 = Arrays.copyOfRange(array, 1, array.length );
System.out.println(Arrays.toString(array2)); //[20, 30]
/*
TODO: 3. 创建一个指定长度的新数组,并将原数组元素复制进去
int[] original, 原数组
int newLength 新数组的长度
*/
int[] array3 = Arrays.copyOf(array, 5);
System.out.println(Arrays.toString(array3)); //[10, 20, 30, 0, 0]
double[] array6 = {10,20,30};
/*
TODO: 4. 把数组的旧元素替换成新元素
double[] array, 要替换元素的数组
IntToDoubleFunction generator 替换规则
*/
Arrays.setAll(array6,new IntToDoubleFunction(){
/* TODO: 此方法的运行次数为array6的元素个数
1. 参数i是元素索引 i = 0,1,2...
2. 返回值是数组的新元素*/
@Override
public double applyAsDouble(int i) {
//第一次运行: 10*0.8=8
//第二次运行: 20*0.8=16
// double element = array6[i] * 0.8;
// return element;
return array6[i] * 0.8;
}
});
System.out.println(Arrays.toString(array6)); //[8.0, 16.0, 24.0]
double[] array7 = {10,20,30};
/*Arrays.setAll(array7,(int i) -> {
return array7[i] * 0.7;
});*/
Arrays.setAll(array7,i -> array7[i]*0.7);
System.out.println(Arrays.toString(array7));
int[] array8 = {30,10,20,50,40};
//对数组中的元素进行升序排序
Arrays.sort(array8);
System.out.println(Arrays.toString(array8));//[10, 20, 30, 40, 50]
}
}
import java.util.Arrays;
import java.util.function.IntToDoubleFunction;
// TODO: Arrays.setAll底层原理
public class Demo02 {
public static void main(String[] args) {
double[] array6 = {10,20,30};
/* setMyAll(array6, new IntToDoubleFunction() {
@Override
public double applyAsDouble(int i) {
return array6[i] * 0.6;
}
});*/
setMyAll(array6, i -> array6[i] *0.6);
System.out.println(Arrays.toString(array6)); //[6.0, 12.0, 18.0]
}
/*
TODO 把数组的旧元素替换成新元素
*/
public static void setMyAll(double[] array, IntToDoubleFunction itd){
for (int i = 0; i < array.length; i++) {
/*
1. applyAsDouble方法的运行次数跟元素个数一致
2. i是数组索引
3. 返回值是数组的新元素
*/
double newElement = itd.applyAsDouble(i);
array[i] = newElement;
}
}
}
import java.util.Arrays;
import java.util.Comparator;
public class Demo {
public static void main(String[] args) {
int[] array = {30,10,20};
//默认升序排序
Arrays.sort(array);
System.out.println(Arrays.toString(array));//[10, 20, 30]
//Integer是int的引用类型
Integer[] array2 = {30,10,20};
Arrays.sort(array2, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
// return o1 - o2; //升序
return o2 - o1; //降序
}
});
Arrays.sort(array2, (o1,o2) -> o2 - o1);//降序
System.out.println(Arrays.toString(array2));
}
}
2.Arrays操作对象数组
1).如果数组中存储的元素类型是自定义的对象,如何排序呢?
首先我们要准备一个Student类,代码如下:
public class Student{
private String name;
private double height;
private int age;
public Student(String name, double height, int age) {
this.name = name;
this.height = height;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", height=" + height +
", age=" + age +
'}';
}
}
public class ArraysTest2 {
public static void main(String[] args) {
// 目标:掌握如何对数组中的对象进行排序。
Student[] students = new Student[4];
students[0] = new Student("蜘蛛精", 169.5, 23);
students[1] = new Student("紫霞", 163.8, 26);
students[2] = new Student("紫霞", 163.8, 26);
students[3] = new Student("至尊宝", 167.5, 24);
// 1、public static void sort(类型[] arr):对数组进行排序。
Arrays.sort(students);
System.out.println(Arrays.toString(students));
}
}
上面的代码为什么会报错呢?因为Arrays根本就不知道按照什么规则进行排序。为了让Arrays知道按照什么规则排序,我们有如下的两种办法。
排序方式1:让Student类实现Comparable接口,同时重写compareTo方法。Arrays的sort方法底层会根据compareTo方法的返回值是正数、负数、还是0来确定谁大、谁小、谁相等。代码如下:
public class Student implements Comparable<Student>{
private String name;
private double height;
private int age;
//...get、set、空参数构造方法、有参数构造方法...自己补全
// 指定比较规则
// this o
@Override
public int compareTo(Student o) {
// 约定1:认为左边对象 大于 右边对象 请您返回正整数
// 约定2:认为左边对象 小于 右边对象 请您返回负整数
// 约定3:认为左边对象 等于 右边对象 请您一定返回0
/* if(this.age > o.age){
return 1;
}else if(this.age < o.age){
return -1;
}
return 0;*/
//上面的if语句,也可以简化为下面的一行代码
return this.age - o.age; // 按照年龄升序排列
// return o.age - this.age; // 按照年龄降序排列
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", height=" + height +
", age=" + age +
'}';
}
}
排序方式2:在调用Arrays.sort(数组,Comparator比较器);
时,除了传递数组之外,传递一个Comparator比较器对象。Arrays的sort方法底层会根据Comparator比较器对象的compare方法方法的返回值是正数、负数、还是0来确定谁大、谁小、谁相等。代码如下
public class ArraysTest2 {
public static void main(String[] args) {
// 目标:掌握如何对数组中的对象进行排序。
Student[] students = new Student[4];
students[0] = new Student("蜘蛛精", 169.5, 23);
students[1] = new Student("紫霞", 163.8, 26);
students[2] = new Student("紫霞", 163.8, 26);
students[3] = new Student("至尊宝", 167.5, 24);
// 2、public static <T> void sort(T[] arr, Comparator<? super T> c)
// 参数一:需要排序的数组
// 参数二:Comparator比较器对象(用来制定对象的比较规则)
Arrays.sort(students, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
// 制定比较规则了:左边对象 o1 右边对象 o2
// 约定1:认为左边对象 大于 右边对象 请您返回正整数
// 约定2:认为左边对象 小于 右边对象 请您返回负整数
// 约定3:认为左边对象 等于 右边对象 请您一定返回0
// if(o1.getHeight() > o2.getHeight()){
// return 1;
// }else if(o1.getHeight() < o2.getHeight()){
// return -1;
// }
// return 0; // 升序
return Double.compare(o1.getHeight(), o2.getHeight()); // 升序
// return Double.compare(o2.getHeight(), o1.getHeight()); // 降序
}
});
System.out.println(Arrays.toString(students));
}
}
import java.util.Arrays;
import java.util.Comparator;
/*
TODO: Arrays.sort(array)
1. 会对array中的元素进行排序,仅限于基本类型
2. 如果是引用类型,要做以下两件事之一:
1). Arrays.sort(array,Comparator 比较器接口的实现类)
2). 要么此引用类型实现Comparable接口
*/
public class Demo {
public static void main(String[] args) {
Student[] array = new Student[3];
array[0] = new Student("zhangsan",19);
array[1] = new Student("lisi",18);
array[2] = new Student("wangwu",20);
System.out.println(Arrays.toString(array));
//直接报错
// Arrays.sort(array);
Arrays.sort(array, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
//按年龄大小升序排序
// return o1.getAge() - o2.getAge();
//没降序排序
return o2.getAge() - o1.getAge();
}
});
Arrays.sort(array,(o1,o2) -> o2.getAge() - o1.getAge());
System.out.println(Arrays.toString(array));
Student2[] array2 = new Student2[3];
array2[0] = new Student2("张三",19);
array2[1] = new Student2("李四",18);
array2[2] = new Student2("王五",20);
Arrays.sort(array2);
System.out.println(Arrays.toString(array2));
}
}
public class Student2 implements Comparable<Student2> {
private String name;
private int age;
@Override
public int compareTo(Student2 o2) {
//this就是o1
// return this.getAge() - o2.getAge();//升序
return o2.getAge() - this.getAge();//降序
}
public Student2() {
}
public Student2(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
四,JDK新特性(方法引用)
1.方法引用
Lambda是用来简化匿名代码的书写格式的,而方法引用是用来进一步简化Lambda表达式的。
2.静态方法引用
public class Test1 {
public static void main(String[] args) {
Student[] students = new Student[4];
students[0] = new Student("蜘蛛精", 169.5, 23);
students[1] = new Student("紫霞", 163.8, 26);
students[2] = new Student("紫霞", 163.8, 26);
students[3] = new Student("至尊宝", 167.5, 24);
// 原始写法:对数组中的学生对象,按照年龄升序排序
Arrays.sort(students, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getAge() - o2.getAge(); // 按照年龄升序排序
}
});
// 使用Lambda简化后的形式
Arrays.sort(students, (o1, o2) -> o1.getAge() - o2.getAge());
}
}
现在,我想要把下图中Lambda表达式的方法体,用一个静态方法代替
// 使用Lambda简化后的形式
Arrays.sort(students, (o1, o2) -> o1.getAge() - o2.getAge());
准备另外一个类CompareByData类,用于封装Lambda表达式的方法体代码;
public class CompareByData {
public static int compareByAge(Student o1, Student o2){
return o1.getAge() - o2.getAge(); // 升序排序的规则
}
}
现在我们就可以把Lambda表达式的方法体代码,改为下面的样子
Arrays.sort(students, (o1, o2) -> CompareByData.compareByAge(o1, o2));
Java为了简化上面Lambda表达式的写法,利用方法引用可以改进为下面的样子。实际上就是用类名调用方法,但是把参数给省略了。这就是静态方法引用
//静态方法引用:类名::方法名
Arrays.sort(students, CompareByData::compareByAge);
3.实例方法引用
基于上面的案例,我们现在来学习一下实例方法的引用。现在,我想要把下图中Lambda表达式的方法体,用一个实例方法代替。
// 使用Lambda简化后的形式
Arrays.sort(students, (o1, o2) -> o1.getAge() - o2.getAge());
在CompareByData类中,再添加一个实例方法,用于封装Lambda表达式的方法体
接下来,我们把Lambda表达式的方法体,改用对象调用方法
CompareByData compare = new CompareByData();
Arrays.sort(students, (o1, o2) -> compare.compareByAgeDesc(o1, o2)); // 降序
最后,再将Lambda表达式的方法体,直接改成方法引用写法。实际上就是用类名调用方法,但是省略的参数。这就是实例方法引用
CompareByData compare = new CompareByData();
Arrays.sort(students, compare::compareByAgeDesc); // 降序
笔记:
import java.util.Arrays;
import java.util.Comparator;
/*
TODO 函数式编程 (JDK8)
0. 前提: 函数式接口
1. lambda表达式
1). 重点: 方法,实现过程
2). 语法
(形参列表) -> {方法体}
2. 方法引用 (method reference)
1). 引用已有方法替代当前表达
2). 语法
方法的归属方 :: 方法名
3). 重点
是引用谁的什么方法
*/
public class Demo {
public static void main(String[] args) {
Student[] array = new Student[3];
array[0] = new Student("zhangsan",19);
array[1] = new Student("lisi",18);
array[2] = new Student("wangwu",20);
//匿名内部类
Arrays.sort(array, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getAge() - o2.getAge();
}
});
//lambda表达式
Arrays.sort(array,(o1,o2) -> o1.getAge() - o2.getAge());
Arrays.sort(array,(o1,o2) -> o1.getAge() - o2.getAge());
//方法引用
Arrays.sort(array,CompareByData::compareByAge);
Arrays.sort(array,CompareByData::compareByAge);
System.out.println(Arrays.toString(array));
}
}
package com.itheima12;
public class CompareByData {
public static int compareByAge(Student o1, Student o2){
return o1.getAge() - o2.getAge(); // 升序的规则
}
}
package com.itheima13;
public class Demo {
public static void main(String[] args) {
//lambda在表达: 有个参数,直接打印
method("腊肠",sth -> System.out.println(sth));
/*
理解方法: System.out对象的println方法就是: 有个参数,直接打印
方法引用重点在于: 已经有一个方法,跟我们将要写的方法逻辑一致,那么可以直接引用
*/
method("白菜",System.out::println);
}
private static void method(String str, Animal a) {
a.eat(str);
}
}
interface Animal{
void eat(String sth);
}
4.特定类型的方法引用
这种特定类型的方法引用是没有什么道理的,只是语法的一种约定。
Java约定:
如果某个Lambda表达式里只是调用一个实例方法,并且前面参数列表中的第一个参数作为方法的主调,后面的所有参数都是作为该实例方法的入参时,则就可以使用特定类型的方法引用。
格式:
类型::方法名
package com.itheima14;
import java.util.Arrays;
import java.util.Comparator;
public class Demo {
public static void main(String[] args) {
String[] names =
{"boby", "angela","Aney", "Andy" ,"dlei", "caocao", "Babo", "jack", "Cici"};
//TODO: String已经实现Comparable接口,所以能够直接排序
//排序规则按照字母的ASCII码表: A65 a97 '0'48
Arrays.sort(names);
//[Andy, Aney, Babo, Cici, angela, boby, caocao, dlei, jack]
System.out.println(Arrays.toString(names));
Arrays.sort(names, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// return o2.compareTo(o1); // o2 - o1 降序
return o1.compareToIgnoreCase(o2); // 升序,并忽略大小写
}
});
//特定类型的方法引用使用前提:
// 参数列表的第一个参数o1是后面方法的主调用方, 参数列表的后续所有参数o2,作为方法的形参
Arrays.sort(names,(o1,o2) -> o1.compareToIgnoreCase(o2));
特定类型的方法引用!
Arrays.sort(names,String::compareToIgnoreCase);
//[Andy, Aney, angela, Babo, boby, caocao, Cici, dlei, jack]
System.out.println(Arrays.toString(names));
}
}
5.构造器引用
构造器引用在实际开发中应用的并不多,目前还没有构造器的应用场景。所以大家在学习的时候,也只是关注语法就可以了。
现在,我们准备一个JavaBean类,Car类
public class Car {
private String name;
private double price;
public Car() {
}
public Car(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}
因为方法引用是基于Lamdba表达式简化的,所以也要按照Lamdba表达式的使用前提来用,需要一个函数式接口,接口中代码的返回值类型是Car类型
interface CreateCar{
Car create(String name, double price);
}
最后,再准备一个测试类,在测试类中创建CreateCar接口的实现类对象,先用匿名内部类创建、再用Lambda表达式创建,最后改用方法引用创建。同学们只关注格式就可以,不要去想为什么(语法就是这么设计的)。
public class Test3 {
public static void main(String[] args) {
// 1、创建这个接口的匿名内部类对象。
CreateCar cc1 = new CreateCar(){
@Override
public Car create(String name, double price) {
return new Car(name, price);
}
};
//2、使用Lambda改进
CreateCar cc2 = (name, price) -> new Car(name, price);
//3、使用方法引用改进:构造器引用
CreateCar cc3 = Car::new;
//注意:以上是创建CreateCar接口实现类对象的几种形式而已,语法一步一步简化。
//4、对象调用方法
Car car = cc3.create("奔驰", 49.9);
System.out.println(car);
}
}