面向对象高级day03---知识总结

目录

一,内部类

1.成员内部类

2.私有成员内部类

3.局部内部类

4.匿名内部类(重点) 

5.匿名内部的引用场景(重点)

6.小节

二,JDK 新特性(Lambda表达式)(重点)

1.函数式编程思想

2.Lambda表达式的格式说明和前提条件(重点)

3.有参无返回值的Lambda表达式

4.无参有返回值的Lambda表达式

5.带参带返回值的Lambda表达式

6.函数式接口

7.Lambda表达式的省略模式

8.Lambda表达式的应用场景

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

10.小节

三,Arrsys工具类

1.Arrays 基本使用

2.Arrays操作对象数组 

四,JDK新特性(方法引用)

1.方法引用

2.静态方法引用

3.实例方法引用

4.特定类型的方法引用

5.构造器引用


一,内部类

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);
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值