• 内部类及Lambda表达式


    内部类

    内部类概念

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

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

    • 在类的成员位置:成员内部类

    • 在类的局部位置:局部内部类

    内部类的访问特点

    • 内部类可以直接访问外部类的成员,包括私有

    • 外部类要访问内部类的成员,必须创建对象

    示例代码:

    1. public class Outer {
    2. private int num = 10;
    3. private class Inner {
    4. public void show() {
    5. System.out.println(num); //内部类可以直接访问外部类的成员,包括私有
    6. }
    7. }
    8. public void method() {
    9. Inner i = new Inner();
    10. i.show(); //外部类要访问内部类的成员,必须创建对象
    11. }
    12. }

    成员内部类

    • 成员内部类的定义位置

      • 在类中方法,跟成员变量是一个位置

      • 被 private 修饰则是 私有成员内部类,被 static 修饰则是 静态成员内部类

    • 外界创建成员内部类格式 (私有成员内部类和静态成员内部类不能用这种方式

      • 格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;

      • Outer.Inner oi = new Outer().new Inner();  

    1. class Outer {
    2. // 当为 public 时,外界创建内部类对象也是像下面这样
    3. class Inner {
    4. }
    5. }
    6. class Test {
    7. public static void main(String[] args) {
    8. Outer.Inner oi = new Outer().new Inner();
    9. }
    10. }

    私有成员内部类

    • 将一个类,设计为内部类的目的,大多数都是不想让外界去访问,所以内部类的定义应该私有化,私有化之后,再提供一个可以让外界调用的方法,方法内部创建内部类对象并调用。

    • 示例代码:

    1. class Outer {
    2. private int num = 10;
    3. private class Inner {
    4. public void show() {
    5. System.out.println(num);
    6. }
    7. }
    8. public void method() {
    9. Inner i = new Inner();
    10. i.show();
    11. }
    12. }
    13. public class InnerDemo {
    14. public static void main(String[] args) {
    15. // 这样会报错
    16. //Outer.Inner oi = new Outer().new Inner();
    17. Outer o = new Outer();
    18. o.method();
    19. }
    20. }

    静态成员内部类

    • 创建静态成员内部类对象格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();

    • 外界调用静态成员内部类中的静态方法:外部类名.内部类名.方法名();

    • 示例代码

    1. class Outer {
    2. static class Inner {
    3. public void show(){
    4. System.out.println("inner..show");
    5. }
    6. public static void method(){
    7. System.out.println("inner..method");
    8. }
    9. }
    10. }
    11. public class Test3Innerclass {
    12. public static void main(String[] args) {
    13. // 这样会报错
    14. //Outer.Inner oi = new Outer().new Inner();
    15. // 外部类名.内部类名 对象名 = new 外部类名.内部类名();
    16. Outer.Inner oi = new Outer.Inner();
    17. oi.show();
    18. Outer.Inner.method(); // 静态成员内部类中静态方法调用方式
    19. }
    20. }

    局部内部类

    • 局部内部类定义位置

      • 局部内部类是在方法中定义的类

    • 局部内部类方式方式

      • 局部内部类,外界是无法直接使用,需要在方法内部创建对象并使用

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

    • 示例代码(局部内部类在自己平常写和源码中基本不会出现

    1. class Outer {
    2. private int num = 10;
    3. public void method() {
    4. int num2 = 20;
    5. class Inner {
    6. public void show() {
    7. System.out.println(num);
    8. System.out.println(num2);
    9. }
    10. }
    11. Inner i = new Inner();
    12. i.show();
    13. }
    14. }
    15. public class OuterDemo {
    16. public static void main(String[] args) {
    17. Outer o = new Outer();
    18. o.method();
    19. }
    20. }

    匿名内部类

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

    • 匿名内部类的前提

      • 存在一个类或者接口,这里的类可以是具体类也可以是抽象类

    • 匿名内部类的格式

      • 格式:new 类名 ( ) { 重写方法 } new 接口名 ( ) { 重写方法 }

      • 举例:

        1. new Inner(){
        2. @Override
        3. public void method(){}
        4. }
    • 匿名内部类的本质

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

    • 匿名内部类的细节

    匿名内部类可以通过多态的形式接收

    1. interface Inner {
    2. void method1();
    3. void method2();
    4. }
    5. class Test {
    6. public static void main(String[] args) {
    7. Inner i = new Inner() {
    8. @Override
    9. public void method1() {
    10. System.out.println("method1……");
    11. }
    12. @Override
    13. public void method2() {
    14. System.out.println("method2……");
    15. }
    16. };
    17. i.method1();
    18. i.method2();
    19. // 如果不以多态的形式接收,那么它后面调用其中一个方法了
    20. new Inner() {
    21. @Override
    22. public void method1() {
    23. System.out.println("method1……");
    24. }
    25. @Override
    26. public void method2() {
    27. System.out.println("method2……");
    28. }
    29. }.method1();
    30. }
    31. }

    匿名内部类在开发中的应用场景

    当发现某个方法需要,接口或抽象类的子类对象,我们就可以传递一个匿名内部类过去,来简化传统的代码。就比如我们都用到过的:Arrays.sort() 方法,我们就可以传入比较器的匿名对象来让数组按我们想要的方式排序

    例子:

    1. class Test {
    2. public static void main(String[] args) {
    3. Person zs = new Person("张三", 28);
    4. Person ls = new Person("李四", 19);
    5. Person ww = new Person("王五", 99);
    6. Person[] persons = new Person[]{zs, ls, ww};
    7. Arrays.sort(persons, new Comparator() {
    8. @Override
    9. public int compare(Person o1, Person o2) {
    10. return o1.getAge() - o2.getAge();
    11. }
    12. });
    13. for (Person person : persons) {
    14. System.out.println(person);
    15. }
    16. }
    17. }
    18. class Person {
    19. private String name;
    20. private int age;
    21. public Person(String name, int age) {
    22. this.name = name;
    23. this.age = age;
    24. }
    25. public String getName() {
    26. return name;
    27. }
    28. public void setName(String name) {
    29. this.name = name;
    30. }
    31. public int getAge() {
    32. return age;
    33. }
    34. public void setAge(int age) {
    35. this.age = age;
    36. }
    37. @Override
    38. public String toString() {
    39. return "Person{" +
    40. "name='" + name + '\'' +
    41. ", age=" + age +
    42. '}';
    43. }
    44. }

    输出结果: 

     

    Lambda表达式

    理解: 对于Lambda表达式, 相当于对匿名内部类进行了优化。但是本质上是有区别的

    • 初步体验:

      1. /*
      2. 游泳接口
      3. */
      4. interface Swimming {
      5. void swim();
      6. }
      7. public class TestSwimming {
      8. public static void main(String[] args) {
      9. // 通过匿名内部类实现
      10. goSwimming(new Swimming() {
      11. @Override
      12. public void swim() {
      13. System.out.println("铁汁, 我们去游泳吧");
      14. }
      15. });
      16. /* 通过Lambda表达式实现
      17. 理解: 对于Lambda表达式, 对匿名内部类进行了优化
      18. */
      19. goSwimming(() -> System.out.println("铁汁, 我们去游泳吧"));
      20. }
      21. /**
      22. * 使用接口的方法
      23. */
      24. public static void goSwimming(Swimming swimming) {
      25. swimming.swim();
      26. }
      27. }
    • 函数式编程思想概述

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

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

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

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

    Lambda表达式的标准格式

    • 格式:

      (形式参数) -> {代码块}

      • 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可

      • ->:由英文中画线和大于符号组成,固定写法。代表指向动作

      • 代码块:是我们具体要做的事情,也就是以前我们写的方法体内容

    • 组成Lambda表达式的三要素:

      • 形式参数,箭头,代码块

    演示

    Lambda表达式演示1

    • Lambda表达式的使用前提

      • 有一个接口

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

    • 演示内容:

      无参无返回值抽象方法

    • 代码

      1. //接口
      2. public interface Eatable {
      3. void eat();
      4. }
      5. //实现类
      6. public class EatableImpl implements Eatable {
      7. @Override
      8. public void eat() {
      9. System.out.println("一天一苹果,医生远离我");
      10. }
      11. }
      12. //测试类
      13. public class EatableDemo {
      14. public static void main(String[] args) {
      15. //在主方法中调用useEatable方法
      16. Eatable e = new EatableImpl();
      17. useEatable(e);
      18. //匿名内部类
      19. useEatable(new Eatable() {
      20. @Override
      21. public void eat() {
      22. System.out.println("一天一苹果,医生远离我");
      23. }
      24. });
      25. //Lambda表达式
      26. useEatable(() -> {
      27. System.out.println("一天一苹果,医生远离我");
      28. });
      29. }
      30. private static void useEatable(Eatable e) {
      31. e.eat();
      32. }
      33. }

    Lambda表达式演示2

    • 演示内容:

      有参无返回值抽象方法

    • 代码

      1. public interface Flyable {
      2. void fly(String s);
      3. }
      4. public class FlyableDemo {
      5. public static void main(String[] args) {
      6. //在主方法中调用useFlyable方法
      7. //匿名内部类
      8. useFlyable(new Flyable() {
      9. @Override
      10. public void fly(String s) {
      11. System.out.println(s);
      12. System.out.println("飞机自驾游");
      13. }
      14. });
      15. System.out.println("--------");
      16. //Lambda
      17. useFlyable((String s) -> {
      18. System.out.println(s);
      19. System.out.println("飞机自驾游");
      20. });
      21. }
      22. private static void useFlyable(Flyable f) {
      23. f.fly("风和日丽,晴空万里");
      24. }
      25. }

    Lambda表达式演示3

    • 演示内容:

      有参有返回值抽象方法

    • 代码(这里就用上面匿名内部类用到的Arrays.sort()来演示)

    1. class Test {
    2. public static void main(String[] args) {
    3. Person zs = new Person("张三", 28);
    4. Person ls = new Person("李四", 19);
    5. Person ww = new Person("王五", 99);
    6. Person[] persons = new Person[]{zs, ls, ww};
    7. /*Arrays.sort(persons, new Comparator() {
    8. @Override
    9. public int compare(Person o1, Person o2) {
    10. return o1.getAge() - o2.getAge();
    11. }
    12. });
    13. */
    14. Arrays.sort(persons, (Person o1, Person o2) -> o1.getAge() - o2.getAge());
    15. for (Person person : persons) {
    16. System.out.println(person);
    17. }
    18. }
    19. }

    Lambda表达式的省略模式

    • 省略的规则

      • 参数类型可以省略。但是有多个参数的情况下,不能只省略一个参数的类型

      • 如果参数有且仅有一个,那么小括号可以省略

      • 如果代码块的语句只有一条,可以省略大括号和分号及return关键字(三个要一起省,不能省一部分)

    • 知道了这几点后,自己试着去省略上面的那些代码吧,你肯定会的,而且会发现它用着真的很爽。

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

    • 所需类型不同

      • 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类

      • Lambda表达式:只能是接口

    • 使用限制不同

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

      • 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式

    • 实现原理不同

      • 匿名内部类:编译之后,产生一个单独的.class字节码文件(xxx$xxx.class,内部类类名都是:外部类类名$内部类类名)

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

    • 思想不一样

      • 匿名内部类是面向对象的思想

      • Lambda表达式是函数式思想

    本文到此就结束了,如果有什么错误或建议欢迎到评论区留言哦~

    如果本文对你有所帮助,也不要吝啬给个三连呀~

  • 相关阅读:
    Spring Boot 定义接口的方法是否可以声明为 Private?
    一文带你吃透阻塞队列
    力扣每日一题 - 【单词搜索】
    Blazor HyBrid在香橙派(Ubuntu Arm)运行的效果
    8.基于SpringBoot3+Security6+JWT实现鉴权机制(二)
    【STL】vector
    开源遇上华为云——DataX for HuaweiCloud OBS
    计算机网络
    100天精通Python(数据分析篇)——第65天:Pandas聚合操作与案例
    服务器为什么要一直开机?
  • 原文地址:https://blog.csdn.net/qq_61557294/article/details/126668666