Java——内部类(概念理解+应用举例)

简介: Java——内部类(概念理解+应用举例)

1.开篇 


Java 这个面向对象的编程语言中,我们几乎离不开这个概念,在博主之前发布的文章中,全部都是在某一个类中,定义一些成员属性或者是成员方法,实例属性(方法)也好、静态属性(方法)也好,最终都是可以通过规范的代码形式去实现的。那么,我们能否在一个类中创建另一个类呢?答案是:肯定的,这就是这篇博文要讲的内容:内部类!!!


2.内部类的介绍 


Java 语言中,内部类是指在一个外部类的内部再定义一个类。而对于这个内部类来说,它可以是静态 static 的,也可以用(访问修饰符)publicprotecteddefault private 来修饰。(而包含这个内部类的外部类只能使用 public default来进行修饰)。而在字节码语言中,只有类的概念,没有外部类和内部类的概念,类只能使用 public default 进行访问控制。 

注意:内部类是一个编译器现象,JVM 虚拟机并不知道内部类与常规类有什么不同。对于源文件中一个名为 Outer 的外部类和其内部定义的名为 Inner 的内部类,编译成功后会出现 Outer.class Outer$Inner.class 两个字节码文件以及文件中的 Outer Outer$Inner 两个类,编译器会把内部类翻译成用 $ (美元符号)分隔外部类名与内部类名的独立的常规类名,而虚拟机则对此一无所知,虚拟机在运行的时候,会把 Outer$Inner 作为一种常规类来处理的。所以内部类的成员变量/方法名可以和外部类的相同。


3.内部类的分类


上图展示了在 Java 语言中,内部类的具体分类,下面,我主要为大家介绍一下对这四种内部类的理解以及应用,即:成员内部类、静态内部类、局部内部类和匿名内部类。


3.1 成员内部类 


3.1.1 概念理解 


在外部类的内部,定义的非静态的内部类,叫成员内部类。



外部类的所有实例成员对成员内部类可见。成员内部类的实例对象,有外部类的实例对象的引用,所以可以访问外部类实例的所有成员(包括 private 私有的)。
外部类的静态成员对成员内部类可见。成员内部类可以访问外部类的所有静态成员(包括 private 私有的)。
外部类对成员内部类可见。在内部类中可以使用 new 生成外部类的实例对象。 

成员内部类对外部类可见。因为编译后的内部类至少是包内,其构造器至少是包内,所以在外部类中可以使用 new 生成内部类的实例对象。外部类按常规的类访问方式使用内部类,唯一的差别是外部类可以访问成员内部类的所有方法与属性,包括私有方法与私有属性。

成员内部类可以使用 publicprotecteddefaultprivate 访问修饰符进行访问控制,内部类能够隐藏起来,不为同一包的其它类访问。成员内部类一般当做成员变量设置为 private
成员内部类是非静态的。所以在成员内部类中,不能定义静态字段、静态方法和静态内部类,因为成员内部类需要先创建外部类的实例对象,才能创建自己的对象;但是可以定义非静态字段、非静态方法和非静态内部类。


3.1.2 成员内部类的特殊语法规则 


假设外部类为 Cow,其成员内部类为 CowLeg

在内部类实例对象的方法中引用外部类实例对象的私有数据域:Cow. this.weight

在外部类实例对象的方法中创建部类实例对象:CowLeg cl = this.new CowLeg(1.12, "黑白相间");

如果内部类在外部类作用域之外是可见的,则可以这样引用内部类:Cow.CowLeg

3.1.3 应用举例 

public class Cow {
  private double weight;//外部类的私有成员变量
  public Cow(double weight) {//外部类的构造方法
    this.weight=weight;
  }
  private class CowLeg {//定义一个成员内部类
    private double length;//成员内部类的两个实例变量
    private String color;
    //private static int age;由于成员内部类的实例的创建依赖于外部类的实例的创建,所以成员内部类不可以包含静态成员
    public CowLeg(double length,String color) {//成员内部类的构造方法
      this.length=length;
      this.color=color;
    }
    //length、color的set和get方法
    public void setLength(double length) {
      this.length=length;
    }
    public double getLength() {
      return this.length;
    }
    public void setColor(String color) {
      this.color=color;
    }
    public String getColor() {
      return this.color;
    }
    //成员内部类的方法
    public void info() {
      System.out.println("当前牛腿颜色是:"+this.color+",高:"+this.length);
      System.out.println("本牛腿所在奶牛重:"+Cow.this.weight);//如果不写Cow.this.,编译器将自动添加
      //直接访问外部类中private修饰的成员变量weight
      //weight引用了创建内部类实例对象的外部类的实例对象的私有数据域。
    }
  }
  public void test() {
    //CowLeg类的实例对象是在Cow类的实例方法中创建的。
    //所以,在创建CowLeg内部类的实例对象之前,必先创建Cow外部类的实例对象。
    CowLeg b=new CowLeg(1.12,"黑白相间");
    //如果不写this.编译器将自动添加。
    //CowLeg b=this.new CowLeg(1.12,"黑白相间");
    b.info();
  }
  public static void main(String[] args) {
    Cow a=new Cow(378.9);//创建外部类Cow的实例对象
    a.test();
    /*Cow.CowLeg d=new Cow(378.9).new CowLeg(1.12,"黑白相间");//创建内部类CowLeg的实例对象
    d.info();*/
  }
}

image.png 

3.2 静态内部类 


3.2.1 概念理解 


将成员内部类的使用再深入限制一步,假如内部类的实例对象不需要引用外部类的实例对象,只是将一个类隐藏在另外一个类的内部,那么就可以将该内部类静态化。在外部类的内部,定义的静态的内部类,叫静态内部类(或叫嵌套类)。提示:静态内部类在实际工作中用的并不是很多。 

外部类的静态成员对静态内部类可见。静态内部类可以访问外部类的所有静态成员(包括 private 私有的)。

外部类对静态内部类可见。在内部类中可以使用 new 生成外部类的实例对象。

外部类的实例成员对静态内部类不可见。静态内部类的实例对象,没有外部类的实例对象的引用,所以不能访问外部类的实例。 


静态内部类对外部类可见。因为编译后的内部类至少是包内,其构造器至少是包内,所以在外部类中可以使用 new 生成内部类的实例对象。

静态内部类可以使用 publicprotecteddefaultprivate 访问修饰符进行访问控制,内部类能够隐藏起来,不为同一包的其它类访问。一般当做成员变量设置为 private
静态内部类是静态的。所以在静态内部类中,可以定义静态字段、静态方法和静态内部类;也可以定义非静态字段、非静态方法和非静态内部类。

与成员内部类相比,静态内部类没有编译器自动添加的实例字段和构造器参数。即静态内部类的实例是不可以访问外部类的实例成员。


3.2.2 应用举例 


public class StaticInnerClassDemo {
  private int a=5;//实例变量
  private static int b=9;//静态变量
  private static class StaticInnerClass {
    private static int number=28;//静态内部类里可以包含静态成员
    public int find1() {
      //System.out.println(a);报错!!!因为静态内部类无法直接访问外部类的实例变量
      return b;
    }
    public static int find2() {
      return number;
    }
  }
  public static void main(String[] args) {
    StaticInnerClassDemo m=new StaticInnerClassDemo();//创建外部类的实例对象
    //外部类中,a为实例字段,由对象调用;b为静态字段,由类调用
    System.out.println("外部类的成员变量的值分别为:"+m.a+","+StaticInnerClassDemo.b);
    StaticInnerClass d=new StaticInnerClass();//创建静态内部类的实例对象
    //静态内部类的实例对象调用实例方法,静态内部类直接调用静态方法
    System.out.println("静态内部类的成员变量的值分别为:"+d.find1()+","+StaticInnerClass.find2());
  }
}

image.png

3.3 局部内部类(概念理解)


在外部类的方法中定义的非静态的内部类,叫局部内部类。(因为内部类可以访问外部类方法的形参和局部变量而得此名)可以分为:在外部类的实例方法内部的局部内部类;在外部类的静态方法内部的局部内部类。
提示:在实际开发中很少使用局部内部类,只是因为局部内部类的作用域很小,只能在当前方法中使用。 


3.3.1 外部类实例方法中的局部内部类(概念理解)


实例方法的形参对局部内部类可见。局部内部类的实例对象,可以有外部类的实例方法的形参的字段,可以访问外部类实例方法的形参。这些形参在 JDK8 之前必须被声明为 final,但在 JDK8 中就不需要了。

实例方法的局部变量对局部内部类可见。局部内部类的实例对象,可以有外部类的实例方法的局部变量的字段,可以访问外部类实例方法的局部变量。这些局部变量在 JDK8 之前必须被声明为 final,但在 JDK8 中就不需要了。 


外部类的实例成员对局部内部类可见。局部内部类的实例对象,有外部类的实例对象的引用,所以可以访问外部类实例的所有成员(包括 private 私有的)。

外部类的静态成员对局部内部类可见。局部内部类可以访问外部类的所有静态成员(包括 private 私有的)。

外部类对局部内部类可见。在内部类中可以使用 new 生成外部类的实例对象。 


局部内部类对外部类的实例方法可见。因为编译后的内部类至少是包内,其构造器至少是包内,所以在外部类的实例方法中可以使用 new 生成局部内部类的实例对象。 

局部内部类不能使用 publicprotectedprivate 访问修饰符进行访问控制,它的作用域被限定在声明该局部内部类的方法块中,所以在外部类的方法中不可以使用 new 生成局部内部类的实例对象。除了局部内部类所在的外部类方法,没有任何方法知道内部类的存在。

局部内部类是非静态的。所以在局部内部类中,不能定义静态字段、静态方法和静态内部类;但是可以定义非静态字段、非静态方法和非静态内部类。

3.3.2 外部类实例方法中的局部内部类(应用举例)

class InstanceLocalOut {
  private int age=12;
  public void Print(int x) {//如下是实例方法中的局部内部类
    int m=8;
    class InstanceLocalIn {//在实例方法中定义一个局部内部类
      public void inPrint() {//局部内部类的实例方法
                                //直接访问外部类的private修饰的成员变量age
        System.out.println("局部内部类获取外部类的实例成员:"+age);
                                //直接访问外部类实例方法的形参x
        System.out.println("局部内部类获取外部类实例方法的形参:"+x);
                                //直接访问外部类实例方法的局部变量m
        System.out.println("局部内部类获取外部类实例方法的局部变量:"+m);
      }
    }
    InstanceLocalIn b = new InstanceLocalIn();//创建一个局部内部类的实例对象
    b.inPrint();//调用该局部内部类中的实例方法
  }
}
public class InstanceLocalInnerClass {
  public static void main(String[] args) {
    InstanceLocalOut a=new InstanceLocalOut();//创建一个外部类的实例对象
    a.Print(3);//通过实例对象调用外部类中的实例方法
  }
}

image.png


3.3.3 外部类静态方法中的局部内部类(概念理解)


外部类静态方法中的局部内部类除了不能访问外部类的实例外(因为它没有外部类的实例对象的引用),与实例方法中的局部内部类相同。

静态方法的形参对局部内部类可见。局部内部类的实例对象,可以有外部类的静态方法的形参的字段,可以访问外部类静态方法的形参。这些形参在 JDK8 之前必须被声明为final,但在 JDK8 中就不需要了。

静态方法的局部变量对局部内部类可见。局部内部类的实例对象,可以有外部类的静态方法的局部变量的字段,可以访问外部类静态方法的局部变量。这些局部变量在 JDK8 之前必须被声明为 final,但在 JDK8 中就不需要了。 

外部类的静态成员对局部内部类可见。局部内部类可以访问外部类的所有静态成员(包括 private 私有的)。

外部类对局部内部类可见。在局部内部类中可以使用 new 生成外部类的实例对象。

外部类的实例成员对局部内部类不可见。局部内部类的实例对象,没有外部类的实例对象的引用,所以不可以访问外部类实例的所有成员。

局部内部类对外部类的静态方法可见。因为编译后的内部类至少是包内,其构造器至少是包内,所以在外部类的静态方法中可以使用 new 生成内部类的实例对象。

局部内部类不能使用publicprotectedprivate访问修饰符进行访问控制,它的作用域被限定在声明该局部内部类的方法块中。

局部内部类是非静态的。所以在局部内部类中,不能定义静态字段、静态方法和静态内部类;但是可以定义非静态字段、非静态方法和非静态内部类。

3.3.4 外部类静态方法中的局部内部类(应用举例)

class StaticLocalOut {
  private int age=12;
  public static void print(int x) {
    int m=8;
    class StaticLocalIn {
      public void inPrint() {
        //System.out.println(age);静态方法中的局部内部类不能访问外部类的实例
        //直接访问外部类静态方法的形参x
        System.out.println("局部内部类获取外部类静态方法的形参:"+x);
        //直接访问外部类静态方法的局部变量m
        System.out.println("局部内部类获取外部类静态方法的局部变量:"+m);
      }
    }
    StaticLocalIn a=new StaticLocalIn();//创建一个局部内部类的实例对象
    a.inPrint();//调用该局部内部类中的实例方法
  }
}
public class StaticLocalInnerClass {
  public static void main(String[] args) {
    StaticLocalOut.print(3);//该外部类自身去调用其中的静态方法
  }
}


3.4 匿名内部类(概念理解) 


1.将局部内部类的使用再深入一步,假如只创建这个类的一个对象,就不必命名了。从使用上讲,匿名内部类和局部内部类的区别是:一个是匿名的,另一个是命名的,其它均相同。(匿名的含义是由编译器自动给内部类起一个内部名称)
2.
在外部类的方法中,定义的非静态的没有类名的内部类,叫匿名内部类。
3.
匿名内部类适合只需要使用一次的类,当创建一个匿名内部类时会立即创建该类的一个实例对象,匿名类不能重复使用。可以分为:在外部类的实例方法内部的匿名内部类,在外部类的静态方法内部的匿名内部类。 


3.4.1 外部类实例方法中的匿名内部类(概念理解)

实例方法的形参对匿名内部类可见。匿名内部类的实例对象,可以有外部类的实例方法的形参的字段,可以访问外部类实例方法的形参。这些形参在 JDK8 之前必须被声明为 final,但在 JDK8 中就不需要了。
实例方法的局部变量对匿名内部类可见。匿名内部类的实例对象,可以有外部类的实例方法的局部变量的字段,可以访问外部类实例方法的局部变量。这些局部变量在 JDK8 之前必须被声明为 final,但在 JDK8 中就不需要了。 

外部类的实例成员对匿名内部类可见。匿名内部类的实例对象,有外部类的实例对象的引用,所以可以访问外部类实例的所有成员(包括 private 私有的)。

外部类的静态成员对匿名内部类可见。匿名内部类可以访问外部类的所有静态成员(包括 private 私有的)。

外部类对局部内部类可见。在匿名内部类中可以使用 new 生成外部类的实例对象。 


匿名内部类对外部类该实例方法可见。因为编译后的内部类至少是包内,其构造器至少是包内,所以在外部类中实例方法可以使用 new 生成内部类的实例对象。


匿名内部类不能使用 publicprotectedprivate 访问修饰符进行访问控制,它的作用域被限定在声明该匿名内部类的方法块中。

由于构造器的名字必须与类名相同,而匿名类无类名,所以匿名内部类不能有构造器。取而代之的是将构造器参数传递给父类构造器。
匿名内部类是非静态的。所以在匿名内部类中,不能定义静态字段、静态方法和静态内部类;但是可以定义非静态字段、非静态方法和非静态内部类。


3.4.2 外部类实例方法中的匿名内部类(应用举例)


class InstanceAnonOut {
  private int age=12;
  public void Print(int x) {//如下是实例方法中的匿名内部类
    int m=8;
    (new InstanceAnonOut() {//创建匿名内部类
      public void inPrint() {//匿名内部类的实例方法
        //直接访问外部类的private修饰的成员变量age
        System.out.println("匿名内部类获取外部类的实例成员:"+age);
        //直接访问外部类实例方法的形参x
        System.out.println("匿名内部类获取外部类实例方法的形参:"+x);
        //直接访问外部类实例方法的局部变量m
        System.out.println("匿名内部类获取外部类实例方法的局部变量:"+m);
      }
    }).inPrint();
  }
}
public class InstanceAnonInnerClass {
  public static void main(String[] args) {
    InstanceAnonOut a=new InstanceAnonOut();//创建一个外部类的实例对象
    a.Print(3);//通过实例对象调用外部类中的实例方法
  }
}

image.png

3.4.3 外部类静态方法中的匿名内部类(概念理解)


静态方法中的匿名内部类除了不能访问外部类的实例外(因为它没有外部类的实例对象的引用),与实例方法中的匿名内部类相同。 

静态方法的形参对匿名内部类可见。匿名内部类的实例对象,可以有外部类的静态方法的形参的字段,可以访问外部类静态方法的形参。这些形参在 JDK8 之前必须被声明为 final,但在 JDK8 中就不需要了。

静态方法的局部变量对匿名内部类可见。匿名内部类的实例对象,可以有外部类的静态方法的局部变量的字段,可以访问外部类静态方法的局部变量。这些局部变量在 JDK8 之前必须被声明为 final,但在 JDK8 中就不需要了。

外部类的静态成员对匿名内部类可见。匿名内部类可以访问外部类的所有静态成员(包括 private 私有的)。
外部类对匿名局部内部类可见。在局部内部类中可以使用 new 生成外部类的实例对象。
外部类的实例成员对匿名内部类不可见。匿名内部类的实例对象,没有外部类的实例对象的引用,所以不可以访问外部类实例的所有成员(包括 private 私有的)。

内部类对外部类的静态方法可见。因为编译后的内部类至少是包内,其构造器至少是包内,所以在外部类的静态方法中可以使用 new 生成内部类的实例对象。

匿名内部类不能使用 publicprotectedprivate 访问修饰符进行访问控制,它的作用域被限定在声明该匿名内部类的方法块中。

匿名内部类不能有构造方法。因为匿名类无类名,所以无法写构造方法。取而代之的是将构造器参数传递给超类构造器。
匿名内部类是非静态的。所以在匿名内部类中,不能定义静态字段、静态方法和静态内部类;但是可以定义非静态字段、非静态方法和非静态内部类。


3.4.4 外部类静态方法中的匿名内部类(应用举例)


class StaticAnonOut {
  private int age=12;//外部类的实例成员
  public static void print(int x) {//如下是静态方法中的匿名内部类
    int m=8;
    (new StaticAnonOut() {
      public void inPrint() {
        //System.out.println(age);静态方法中的匿名内部类不能访问外部类的实例
        //直接访问外部类静态方法的形参x
        System.out.println("匿名内部类获取外部类静态方法的形参:"+x);
        //直接访问外部类静态方法的局部变量m
        System.out.println("匿名内部类获取外部类静态方法的局部变量:"+m);
      }
    }).inPrint();
  }
}
public class StaticAnonInnerClass {
  public static void main(String[] args) {
    StaticAnonOut.print(3);//该外部类自身去调用其中的静态方法
  }
}
1. }


以上,就是博主在学习过程中,总结的 Java 语言中,四种内部类(成员内部类、静态内部类、局部内部类和匿名内部类)的相关内容,希望对大家的学习和理解有所帮助!!!😁😁😁 

相关文章
|
5月前
|
人工智能 算法 Java
Java与AI驱动区块链:构建智能合约与去中心化AI应用
区块链技术和人工智能的融合正在开创去中心化智能应用的新纪元。本文深入探讨如何使用Java构建AI驱动的区块链应用,涵盖智能合约开发、去中心化AI模型训练与推理、数据隐私保护以及通证经济激励等核心主题。我们将完整展示从区块链基础集成、智能合约编写、AI模型上链到去中心化应用(DApp)开发的全流程,为构建下一代可信、透明的智能去中心化系统提供完整技术方案。
407 3
|
7月前
|
存储 数据采集 搜索推荐
Java 大视界 -- Java 大数据在智慧文旅旅游景区游客情感分析与服务改进中的应用实践(226)
本篇文章探讨了 Java 大数据在智慧文旅景区中的创新应用,重点分析了如何通过数据采集、情感分析与可视化等技术,挖掘游客情感需求,进而优化景区服务。文章结合实际案例,展示了 Java 在数据处理与智能推荐等方面的强大能力,为文旅行业的智慧化升级提供了可行路径。
Java 大视界 -- Java 大数据在智慧文旅旅游景区游客情感分析与服务改进中的应用实践(226)
|
7月前
|
存储 监控 数据可视化
Java 大视界 -- 基于 Java 的大数据可视化在企业生产运营监控与决策支持中的应用(228)
本文探讨了基于 Java 的大数据可视化技术在企业生产运营监控与决策支持中的关键应用。面对数据爆炸、信息孤岛和实时性不足等挑战,Java 通过高效数据采集、清洗与可视化引擎,助力企业构建实时监控与智能决策系统,显著提升运营效率与竞争力。
|
5月前
|
消息中间件 缓存 Java
Spring框架优化:提高Java应用的性能与适应性
以上方法均旨在综合考虑Java Spring 应该程序设计原则, 数据库交互, 编码实践和系统架构布局等多角度因素, 旨在达到高效稳定运转目标同时也易于未来扩展.
381 8
|
5月前
|
Java 编译器 Go
【Java】(5)方法的概念、方法的调用、方法重载、构造方法的创建
Java方法是语句的集合,它们在一起执行一个功能。方法是解决一类问题的步骤的有序组合方法包含于类或对象中方法在程序中被创建,在其他地方被引用方法的优点使程序变得更简短而清晰。有利于程序维护。可以提高程序开发的效率。提高了代码的重用性。方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头写,不使用连接符。例如:addPerson。这种就属于驼峰写法下划线可能出现在 JUnit 测试方法名称中用以分隔名称的逻辑组件。
276 4
|
6月前
|
人工智能 Java API
Java与大模型集成实战:构建智能Java应用的新范式
随着大型语言模型(LLM)的API化,将其强大的自然语言处理能力集成到现有Java应用中已成为提升应用智能水平的关键路径。本文旨在为Java开发者提供一份实用的集成指南。我们将深入探讨如何使用Spring Boot 3框架,通过HTTP客户端与OpenAI GPT(或兼容API)进行高效、安全的交互。内容涵盖项目依赖配置、异步非阻塞的API调用、请求与响应的结构化处理、异常管理以及一些面向生产环境的最佳实践,并附带完整的代码示例,助您快速将AI能力融入Java生态。
1026 12
|
6月前
|
安全 Java API
Java SE 与 Java EE 区别解析及应用场景对比
在Java编程世界中,Java SE(Java Standard Edition)和Java EE(Java Enterprise Edition)是两个重要的平台版本,它们各自有着独特的定位和应用场景。理解它们之间的差异,对于开发者选择合适的技术栈进行项目开发至关重要。
1112 1
|
7月前
|
设计模式 XML 安全
Java枚举(Enum)与设计模式应用
Java枚举不仅是类型安全的常量,还具备面向对象能力,可添加属性与方法,实现接口。通过枚举能优雅实现单例、策略、状态等设计模式,具备线程安全、序列化安全等特性,是编写高效、安全代码的利器。
|
7月前
|
机器学习/深度学习 人工智能 自然语言处理
Java 大视界 -- Java 大数据机器学习模型在自然语言生成中的可控性研究与应用(229)
本文深入探讨Java大数据与机器学习在自然语言生成(NLG)中的可控性研究,分析当前生成模型面临的“失控”挑战,如数据噪声、标注偏差及黑盒模型信任问题,提出Java技术在数据清洗、异构框架融合与生态工具链中的关键作用。通过条件注入、强化学习与模型融合等策略,实现文本生成的精准控制,并结合网易新闻与蚂蚁集团的实战案例,展示Java在提升生成效率与合规性方面的卓越能力,为金融、法律等强监管领域提供技术参考。
|
7月前
|
存储 人工智能 算法
Java 大视界 -- Java 大数据在智能医疗影像数据压缩与传输优化中的技术应用(227)
本文探讨 Java 大数据在智能医疗影像压缩与传输中的关键技术应用,分析其如何解决医疗影像数据存储、传输与压缩三大难题,并结合实际案例展示技术落地效果。