Java-内部类

知识点1:内部类的作用

  • 内部类是Java独有的一种语法结构,即在一个类的内部定义另一个类,此时,内部类就成为外部类中的成员,访问权限遵循类成员的访问权限机制,可以是public、protected、缺省和private。

  • 内部类可以很方便地访问外部类中的其它成员。

  • 完善多重继承。

  • 继承耦合度太高。每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响

知识点2- 内部类的分类

  • 内部类的分类:

    • 内部类定义在成员位置上:成员内部类

    • 内部类定义在局部位置上:局部内部类

    • 没有名字的内部类: 匿名内部类

    • 被static修饰的内部类:静态内部类

知识点3:成员内部类

    • 成员内部类是最普通的内部类,它是外围类的一个成员,所以他是可以无限制的访问外围类的所有成员属性和方法,即便是private的,但是外围类要访问内部类的成员属性和方法则需要通过内部类实例来访问。

    •  在成员内部类中要注意两点:

      • 第一:成员内部类中不能存在任何static的变量和方法

      • 第二:成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类    

    • 复制代码
      public class OuterClass {
          private String name;
          private int age;
          //定义成员内部类
          public class InnerClass { 
              //无参的构造方法
              public InnerClass() {
                  name = "jack"; //name,age是外部类的成员变量 ,内部类可以直接用
                  age = 30;      
              }
              public void display() {
                  System.out.println("name=" + name + ",age=" + age);
              }
              //成员内部不能定义静态方法
      //        public static void display1() {
      //            System.out.println("name=" + name + ",age=" + age);
      //        }
          }
          public static void main(String[] args) {
              //1、创建外部类对象
              OuterClass outer = new OuterClass();
              //2、定义内部类类型的变量
              OuterClass.InnerClass inner = null;  //外部类名称.内部类名称  
              //3、内部类类型的变量初始化,创建内部类对象
              inner = outer.new InnerClass();  //每一个外部类对象才有内部类     以外部类对象.new 内部类构造方法()的方式构建对象
              //4、调用display方法
              inner.display();
          }
      }

      输出name=jack,age=30
      复制代码
  • 内部类是个编译时的概念,一旦编译成功后,它就与外围类属于两个完全不同的类(当然他们之间还是有联系的)

  • 对于一个名为OuterClass的外围类和一个名为InnerClass的内部类,在编译成功后,会出现这样两个class文件:OuterClass.class和OuterClass$InnerClass.class

知识点4:局部内部类

  • 嵌套在方法和作用域内的,对于这个类的使用主要是应用与解决比较复杂的问题,局部内部类和成员内部类一样被编译,它只能在该方法和作用域中被使用。

  • 局部内部类示例

    复制代码
    //外部类
    public class OuterClass1 {
        //外部类的成员变量
        private String name;
        private int age;
        //成员方法
        public void display(int age1) {
            //定义局部内部类
            class InnerClass {
                public InnerClass() {
                    name = "jack";  
                    age = age1;   //使用了外部类的成员age,并把方法参数变量age1赋值给了 age
                }
                public void display() {
                    System.out.println("name=" + name + ",age=" + age);
                }
                //局部内部不能定义静态方法
    //            public static void display1() {
    //                System.out.println("name=" + name + ",age=" + age);
    //            }
            }
            //创建局部内部类对象
            InnerClass inner = new InnerClass();
            //调用display,局部内部类的方法
            inner.display();
        }
        public static void main(String[] args) {
            //创建外部类对象
            OuterClass1 outer = new OuterClass1();
            //调用外部类对象的display方法
            outer.display(60);
        }
    }
    输出name=jack,age=60
    复制代码

知识点5:静态内部类

  • 使用static修饰的内部类我们称之为静态内部类或嵌套内部类。静态内部类与非静态内部类之间存在一个最大的区别,非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外部类,但是静态内部类却没有。没有这个引用就意味着:

    • 它的创建是不需要依赖于外部类

    • 不能使用任何外部类的非static成员变量和方法

    • 和成员内部类不同,static内部类能够声明static的成员

  • static内部类示例:

    复制代码
    //外部类
    public class OuterClass2 {
        //外部类静态成员变量,所以new的对象共享静态成员变量,外部类名.静态成员变量
        private static String name = "jin";
        //成员变量
        private int age;
    
        //外部类的成员方法
        void outerDisplay() {
            //外部类的方法可以调用静态内部类的静态成员变量
            System.out.println("外部类的方法可以调用静态内部类的静态成员变量"+InnerClass.innerStaticName);
            //外部类的方法可以调用静态内部类的静态的成员方法
            InnerClass.innerStaticDisplay();
        }
        //定义静态内部类
        static class InnerClass {
            //静态内部类的静态成员变量
            static String innerStaticName = "yi";
            //成员变量
            int innerAge;
    
            static void innerStaticDisplay() {
                //静态方法能够调用静态成员变量
                System.out.println("静态方法能够调用静态成员变量" + innerStaticName);
            }
    
            void innerDisplay() {
                //非静态方法可以调用静态和非静态的成员变量
                System.out.println("非静态方法可以调用静态和非静态的成员变量" + innerStaticName + ",innerAge=" + innerAge);
            }
        }
    
        public static void main(String[] args) {
            OuterClass2 outer = new OuterClass2();
            outer.outerDisplay();
            //创建静态内部类对象,直接new静态内部类构造方法,不依赖outer对象
            //OuterClass2.InnerClass inner = new OuterClass2.InnerClass();
            InnerClass inner = new InnerClass();
            inner.innerDisplay();
            //调用静态内部类的静态方法
            //OuterClass2.InnerClass.innerStaticDisplay();
            InnerClass.innerStaticDisplay();
        }
    }
    /**
     * 外部类的方法可以调用静态内部类的静态成员变量yi
     * 静态方法能够调用静态成员变量yi
     * 非静态方法可以调用静态和非静态的成员变量yi,innerAge=0
     * 静态方法能够调用静态成员变量yi
     */
    复制代码

知识点6:内部类中的this及类名.this的使用

  • 之前提到非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外部类,但是和以前类中直接使用this引用当前对象的情况相比要复杂一些

  • 由于内部类本质上是一个独立的类,因此在内部类中直接使用this,其指代的是内部类自身的引用,如果要想引用外部内的对象,则应该使用外部类类名.this的方式

  • 内部类中的this及类名.this的使用示例

  

复制代码
//外部类
public class OuterClass3 {
    private int i = 10; //外部类的成员变量

    //成员内部类
    public class InnerClass {
        private int i = 100; //内部类的成员变量
        void display() {
            //内部类的成员变量i  ,this指定是内部类的对象
            System.out.println("this指定是内部类的对象:"+this.i);
            //外部类的成员变量i, 外部类名.this  外部的对象的引用
            System.out.println("外部类名.this指定的是外部类的对象:"+OuterClass3.this.i);
        }
    }
    public static void main(String[] args) {
        //1、创建外部类对象
        OuterClass3 outer = new OuterClass3();
        //2、创建内部类对象
        OuterClass3.InnerClass inner = outer.new InnerClass();
        //3、调用内部类对象的display方法
        inner.display();
    }
}
/**
 * this指定是内部类的对象:100
 * 外部类名.this指定的是外部类的对象:10
 */
复制代码

知识点7:匿名内部类

  • 如果一类内部类仅需要构建一个单一的对象,那么这个类其实并不需要额外取一个特有的名字,对于不存在名字的内部类,我们称为匿名内部类

  • 在创建对象时,只通过new的动作在堆内存开辟空间,却没有把堆内存空间的地址值赋值给栈内存的某个变量用以存储
  • 匿名类不能被引用,只能在创建时用new语句来声明
  • 匿名内部类必须继承一个父类或实现一个接口

  • 匿名内部类的声明使用方法如下:

  • 匿名内部类示例:

    //接口
    interface Foo {
        void display();
    }
    复制代码
    public class OuterClass4 {
        private String name;
        private int age;
    
        //创建 实现Foo接口的匿名类的 对象,赋值给 foo 成员变量
        Foo foo = new Foo() {
            @Override
            public void display() {
                System.out.println("实现Foo接口匿名类对象, display方法被调用");
            }
        };
        //创建 继承OuterClass2的匿名类的 对象, 赋值给 outerClass2 成员变量
        OuterClass2 outerClass2 = new OuterClass2() {
            @Override
            void outerDisplay() {
                System.out.println("重写OuterClass2.outerDisplay方法");
            }
        };
        OuterClass1 outerClass1 = new OuterClass1(){
            @Override
            public void display(int age1) {
                System.out.println("重写OuterClass1.display方法");
            }
        };
        public static void main(String[] args) {
            OuterClass4 outer = new OuterClass4();
            outer.foo.display();
            outer.outerClass2.outerDisplay();
            outer.outerClass1.display(17);
        }
    }
    /**
     * 实现Foo接口匿名类对象, display方法被调用
     * 重写OuterClass2.outerDisplay方法
     * 重写OuterClass1.display方法
     */
    复制代码
  • 匿名内部类没有构造方法(匿名内部类没有显式类名)

  • 匿名内部类要想完成一些初始化工作可以交由类初始化或实例初始化代码块来完成

  • 匿名内部类对类成员、方法临时变量的访问规则和具备名字的内部类保持一致

知识点8:内部类的使用场景和好处

为什么在 Java 中需要内部类?总结一下主要有以下四点:

  • 1.每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整。
  • 2.方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。
  • 3.方便编写事件驱动程序。
  • 4.方便编写线程代码。

知识点9:Lambda表达式

  • Lambda表达式主要是替换了原有匿名内部类的写法,也就是简化了匿名内部类的写法

  • lambda语法结构:

    lambda表达式的基本组成由 参数列表、->、方法体(单句时可不加大括号) 三部分组成

    (参数类型 参数名称) -> {

    业务代码块

    }

  • lambda表达式的重要特征:

    • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。

    • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。

    • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。

    • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定表达式返回了一个数值。

  • Lambda表达式语法示例

    • 不需要参数,返回值为 5 :() -> 5

      public interface A {
          int ma();
      }
      复制代码
      public class LambdaTest {
          public static void main(String[] args) {
              //创建 实现A接口的匿名类的 对象,赋值给 a 成员变量
              /*A a = new A(){
                  @Override
                  public int ma() {
                      return 5;
                  }
              };*/
              //简化为Lambda表达式
              A a = () -> 5;
              System.out.println(a.ma());
          }
      }
      /**
       * 5
       */
      复制代码
    • 接收一个参数(数字类型),返回其2倍的值: x -> 2 * x

      public interface B {
          int ma (int x);
      }
      复制代码
      public class LambdaTest {
          public static void main(String[] args) {
              //创建 实现B接口的匿名类的 对象,赋值给 b 成员变量
              /*B b = new B(){
                  @Override
                  public int ma(int x) {
                      return 2*x;
                  }
              };*/
              //简化为Lambda表达式
              B b = x -> 2*x;
              System.out.println(b.ma(10));
          }
      }
      /**
       * 20
       */
      复制代码
    • 接受2个参数(数字),并返回他们的差值: (x, y) -> x – y

      public interface C {
          int ma(int x, int y);
      }
      复制代码
      public class LambdaTest {
          public static void main(String[] args) {
              //创建 实现C接口的匿名类的 对象,赋值给 c 成员变量
              /*C c =new C() {
                  @Override
                  public int ma(int x, int y) {
                      return Math.abs((x-y));
                  }
              };*/
              //简化为Lambda表达式
              C c = (x, y) -> Math.abs((x-y));
              System.out.println(c.ma(10,27));
          }
      }
      /**
       * 17
       */
      复制代码
    • 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void): (String s) -> System.out.print(s)

      public interface D {
          void ma(String s);
      }
      复制代码
      public class LambdaTest {
          public static void main(String[] args) {
              //创建 实现D接口的匿名类的 对象,赋值给 d 成员变量
              /*D d = new D() {
                  @Override
                  public void ma(String s) {
                      System.out.println(s);
                  }
              };*/
              //简化为Lambda表达式
              D d = s -> System.out.println(s);
              /*将lambda替换为方法引用
              D d = System.out::println;*/
              System.out.println("SEVENTEEN");
          }
      }
      /**
       * SEVENTEEN
       */
      复制代码
  • 用于替代匿名内部类的Lambda表达式例子

public interface E {
    void display();
}
复制代码
public class AnonymousInnerClass {
    private final String name = "yi";
    private final int age = 21;
    //创建实现 E 接口的匿名类的对象,赋值给 e 成员变量
        /*E e = new E() {
        @Override
        public void display() {
            System.out.println("name=" + name + ",age=" + age);
        }
    };*/

    //简化为lambda表达式
    E e = () -> System.out.println("name=" + name + ",age=" + age);

    public static void main(String[] args) {
        AnonymousInnerClass innerClass = new AnonymousInnerClass();
        innerClass.e.display();
    }
}
/**
 * name=yi,age=21
 */
复制代码
  • Lambda表达式对返回的实例类型的要求

    • 可以将lambda表达式当作任意只包含一个抽象方法的接口类型,确保你的接口一定达到这个要求,你只需要给你的接口添加@FunctionalInterface 注解,编译器如果发现你标注了这个注解的接口有多于一个抽象方法的时候会报错的:
复制代码
@FunctionalInterface
public interface D {
    void ma(String s);
    default public void foo(){
        System.out.println("默认方法");
    }
    /*//需要实现的抽象方法不止一个,编译器报错
    void foo1();*/
}
复制代码
复制代码
public class LambdaTest {
    public static void main(String[] args) {
        /*D d = new D() {
            @Override
            public void ma(String s) {
                System.out.println(s);
            }
        };*/
        D d = s -> System.out.println(s);
        d.ma("SEVENTEEN");
    }
}
/**
 * SEVENTEEN
 */  
复制代码
    • 是否为接口添加@FunctionalInterface注解并不影响lambda表达式的功能,大家已经看到了,在最开始的示例中,我们并没有在接口上标注它,代码仍然运行良好,因 此@FunctionalInterface注解只是为了在代码开发阶段由编译器帮助开发人员判定 接口是否符合函数式接口的定义(是否适用于lambda表达式)
  • 在lambda表达式中访问外层作用域和老版本的匿名内部类中的方式很相似

    • 可以直接访问标记了final的外层局部变量,或者实例的成员变量以及静态变量和匿名内部类不同的是,局部变量可以不用声明为final,代码同样能够正确执 行,但变量必须不可被表达式的代码修改(即隐性的具有final的语义)
    • 有一个和接口的默认方法相关的要点需要特别留意:Lambda表达式中是无法访问到默认方法的
复制代码
@FunctionalInterface
public interface T {
    //相当于 public static final int mm=20;
    int mm = 20;
    default void ma() {
        System.out.println("TestService接口的ma方法");
    }
    default void mb() {
        System.out.println("TestService接口的mb方法");
    }
    void test();
}
复制代码
复制代码
public class TestService {
    int a = 10;
    public void ccc() {
        int c = 10;
        final int d = 20;
        T service1 = new T() {
            @Override
            public void test() {
                //匿名类对象方法是可以修改和使用成员变量
                System.out.println(a);
                a = a + 2;

                //能够使用局部变量c(隐含的final)
                System.out.println(c);
                //修改c变量值就报错,说明在匿名内部类中局部变量c是看做是隐含的final
                //c = c + 2;

                //能够使用final的局部变量d
                System.out.println(d);
                System.out.println("匿名内部类对象实现 test方法");
                //匿名内部类方法体内可以调用接口default方法(默认方法)
                ma();
                System.out.println(T.mm);
                //匿名内部类的方法体内部定义变量是可修改的
                int ff = 0;
                ff = ff + 2;
            }
        };
        Person p = new Person();
    }

    public void aaa() {
        int b = 12;
        //标准写法
        T service2 = () -> {
            //lambda是可以修改和使用成员变量
            System.out.println(a);
            a = a + 2;
            System.out.println(b);  //能使的局部变量(隐含的final)
            //b = b + 2;    //修改就报错,就说明在lambda表达式方法中局部变量b是看做是隐含的final
            System.out.println("标准写法lambda实现的test方法");
            //ma();  //lambda方法体内不能调用接口default方法(默认方法)
            System.out.println(T.mm);
            //lambda的方法体内部定义变量,是可修改的
            int ff = 0;
            ff = ff + 2;
        };
    }
}
复制代码
  • ::操作符

    ::操作符主要用作静态方法、成员方法或构造方法的绑定

    • ::绑定静态方法

public interface IntegerFactory {
    Integer createInteger(String str);
}
复制代码
public class Test1 {
    public static void main(String[] args) {
        //将函数式接口中需要实现的方法与Integer类的valueOf静态方法绑定(注意返回值和参数列表)
        //调用factory.createInteger()相当于调用Integer.valueOf方法
        IntegerFactory factory = Integer::valueOf;
        Integer integer = factory.createInteger("526");
        System.out.println(integer);
    }
}
/**
 * 526
 */
复制代码
    • 绑定实例成员方法

public interface Foo {
    void interfaceFoo();
}
复制代码
public class Test2 {
    //定义成员方法
    public void foo() {
        System.out.println("Test2的foo方法");
    }
    public static void main(String[] args) {
        //创建Test2对象
        Test2 t = new Test2();
        //把t对象的foo方法绑定Foo接口的interfaceFoo方法上,要求返回值和参数列表一致
        Foo foo = t::foo;
        //调用foo.interfaceFoo()
        foo.interfaceFoo();
    }
}
/**
 * Test2的foo方法
 */
复制代码
    • ::绑定构造方法 

public interface PersonFactory {
    Person create(String firstName, String lastName);
}
复制代码
public class Person {
    String fistName;
    String lastName;
    //Person p1 = new Person();
    public Person(String fistName, String lastName) {
        this.fistName = fistName;
        this.lastName = lastName;
    }
    public Person() {
    }
    public Person(String fistName) {
        this.fistName = fistName;
    }
    @Override
    public String toString() {
        return "Person{" +
                "fistName='" + fistName + '\'' +
                ", lastName='" + lastName + '\'' +
                '}';
    }
    public static void main(String[] args) {
        //把Person的构造方法绑定PersonFactory接口匿名类create方法上
        PersonFactory pf = Person::new;
        Person p = pf.create("索", "隆");
        System.out.println(p);
    }
}
/**
 * Person{fistName='索', lastName='隆'}
 */
复制代码
posted @   carat9588  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示