Java内部类 - 局部/匿名/成员/静态内部类

简介: Java内部类 - 局部/匿名/成员/静态内部类

【1】内部类定义


我们所说的内部类,官方的叫法是嵌套类(Nested Classes)。嵌套类包括静态内部类(Static Nested Classes)和内部类(Inner Classes)。而内部类分为成员内部类,局部内部类(Local Classes)和匿名内部类(Anonymous Classes)。


内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两个类,分别为outer.class和outer$inner.class类。所以内部类的成员变量/方法名可以和外部类的相同。


当你访问外部类的私有数据成员时,JDK会在外部类中创建包级访问权限的(package-acess)成员函数以便内部类来访问外部类的私有成员。这种结果会导致一个安全漏洞。通常情况下,我们应当尽量避免使用内部类。


只有当内部类仅仅只和外部类的上下文相关或者内部类需要被设置为私有的且只能被外部类访问的这些情况下,我们才使用内部类。内部类主要被用来实现类似Iterators、Comparators这些辅助类。它们被使用在外部类的上下文中。

【2】内部类特点


内部类是指在一个外部类的内部再定义一个类,内部类作为外部类的一个成员并且依附于外部类而存在的。

注意这里明确说明了是在类的内部作为其成员,而不仅仅只是和外部类在同一个class文件中!

如下所示:枚举类Operator和OO均为SearchFilter的内部类


20181124151358727.png


如下所示,class BB 和class SearchFilter在同一个class 文件中,但是二者并无关系。唯一需要注意的是,一个class文件只能有一个public类型的外部类!


编译后的class文件显示如下:

可以在一个外部类中定义多个内部类,这些内部类在逻辑上是一个整体,与外部类之间是从属关系。在内部类中可以访问外部类定义的私有成员。


其特点如下:


非静态内部类能够访问其外部类的所有属性和方法;


静态内部类能够访问其外部类的static属性和static方法


内部类能够隐藏起来,不被同一个包中的其他类访问。如果一个类只对某个类提供使用,那么可以将其定义为内部类;


匿名内部类可以方便的用在回调方法中,典型应用是图形编程中的事件处理;


外部类不能直接访问内部类的成员, 但是可以通过内部类的实例访问内部类的私有成员;


局部类不仅可以访问外部类的所有成员,还可以访问方法体的局部变量,但必须是final修饰的局部变量;


非静态内部类不能够定义静态字段和方法,但是静态内部类则可以定义静态字段和方法也可以定义非静态字段和方法。


内部类可以声明为抽象类,因此可以被其他的内部类继承,也可以声明为final;


和外部类不同,内部类可以声明为private或protected,外部类只能用public和default;


内部类可以声明为static,但此时就不能再使用外层封装类的非static成员变量;


非static的内部类的成员不能声明为static的,只有在顶层类或static的内部类才可以声明static成员。


内部类有效实现了“多重继承”,优化 java 单继承的缺陷。


匿名内部类可以很方便的定义回调。

内部类示例代码如下:

public class Outer{
  private int size;
  //此处为成员内部类-非静态内部类
  public class Inner{
    public void duStuff(){
    //内部类可以访问外部类的私有属性
    size++;
    }
  }
  //方法定义
  public void testInner(){
    //调用内部类中的方法
    Inner i = new Inner();
    i.duStuff();
  }
}

在上述代码中,在Outer类中定义了属性size、内部类Inner和方法testInner(),在方法中调用内部类,直接通过"对象.方法"的形式即可调用。

如果在其他类中需要调用Inner类中的方法,则可以使用以下格式:

//方法一
Outer.Inner in = new Outer().new Inner();
in.doStuff();
//方法二
Outer o = new Outer();
Outer.Inner in = o.new Inner();
in.doStuff();

内部类可以通过static修饰,称为静态内部类,在静态内部类中调用外部类成员,外部类成员也要求用static修饰。

如果内部类用static修饰,则可以使用以下格式调用其方法。

Outer.Inner in = new Outer.Inner();
in.doStuff();

当外部类和内部类成员变量相同时,在内部类中需要使用“外部类.this.attr”来调用外部类的变量。

示例如下:

public class A{
  private int i = 100;
  class B {
    private int i = 10;
    public void show(){
      //若不显示指明,默认为内部类成员
      System.out.println(i);
      //该处打印外部类的成员变量
      System.out.println(A.this.i);
    }
  }
}
输出结果:10   100

【3】局部内部类与静态内部类

二者区别


创建静态内部类的对象可以直接通过外部类调用静态内部类的构造器;创建非静态的内部类的对象必须先创建外部类的对象,通过外部类的对象调用内部类的构造器。


在静态内部类中调用外部类成员,成员也要求用static修饰。


非静态内部类能访问外部类的一切成员, 包括私有成员。外部类虽然不能直接访问内部类的成员, 但是可以通过内部类的实例访问内部类的私有成员。


局部内部类定义在方法体内,只能在该方法或条件的作用域内才能使用,退出这些作用域就无法引用。


局部类不仅可以访问外部类的所有成员,还可以访问方法体的局部变量,但必须是final修饰的局部变量。示例如下:

public class TestInnerClass {
  public static void main(String[] args) {
    //创建静态内部类的对象:可以直接通过外部类调用静态内部类的构造器
    Person.Dog d = new Person.Dog();
    //创建非静态的内部类的对象:必须先创建外部类的对象,
    //通过外部类的对象调用内部类的构造器
    Person p = new Person();
    Person.Bird b = p.new Bird();//new p.Bird();
    b.info();
    b.setName("杜鹃");
  }
}
class Person{
  String name = "韩梅梅";
  int age;
  //成员内部类(非static的)
  class Bird{
    String name = "黄鹂";
    int id;
    public Bird(){
  }
    public void setName(String name){
      System.out.println(name);//杜鹃
      System.out.println(this.name);//黄鹂
      System.out.println(Person.this.name);//韩梅梅
    }
    public void info(){
    //调用外部类的成员方法
      show();
    }
  }
  //成员内部类(静态内部类)
  static class Dog{
  }
  /*成员方法*/
  public void show(){
    System.out.println("我是show()方法");
  }
  public void method1(){
    /*局部内部类*/
    class A{
    }
  }
}


运行结果:

我是show()方法
杜鹃
黄鹂
韩梅梅

【4】局部内部类与匿名内部类的使用

示例如下:

public class TestInnerClass1 {
}
class OuterClass{
  //局部内部类
  //如下的使用方式较少
  public void method1(){
    class InnnerClass{  
    }
  }
  //常常使用一个方法,使其返回值为某个类或接口的对象。
  //而这个类或接口在方法内部创建
  //使用方式一
  public Comparable getComparable(){
    //1.创建一个实现Comparable接口的类:局部内部类
    class MyComparable implements Comparable{
      public int compareTo(java.lang.Object o) {
        return 0;
      }
    }
  }
  //使用方式二
  public Comparable getComparable1(){
    //返回一个实现Comparable接口的匿名内部类的对象
    return new Comparable(){
      public int compareTo(java.lang.Object o) {
        return 0;
      } 
    };
    /*
    new Thread(new Runnable() {
      public void run() {
        for(int i = 1;i <= 100;i++){
          if(i % 2 == 0){     
  System.out.println(Thread.currentThread().getName() + ":" + i);
                }
              }
      }
    });
    */
  }
}


为什么匿名内部类和局部内部类只能访问final变量?

参考博文:浅谈匿名内部类和局部内部类只能访问final变量


【5】静态内部类和非静态内部类


非静态嵌套类(即内部类)对它嵌套的外部类的成员拥有全部的访问权限。


而静态嵌套类并不包含对外部类的实例引用,这就使得静态嵌套类不能够调用外部类实例的非静态方法或者访问外部类的非静态字段。


在声明成员字段和方法上,非静态嵌套类不能够定义静态字段和方法。但是,静态内部类则可以定义静态字段和方法也可以定义非静态字段和方法。


非静态内部类的实例是通过使用外部类的对象引用来创建的,也就在说在内部类中已经定义了外部类实例。但是静态嵌套类实例的创建过程中并不涉及到外部类的引用,这就是说它并不拥有外部类的实例。

示例如下:

public class OuterClass {
    class InnerClass {
        // static int x; not allowed here
    }
    static class StaticInnerClass {
        static int x; // allowed here
    }
}
class Test {
    public static void main(String... str) {
        OuterClass oc = new OuterClass();
        OuterClass.InnerClass obj1 = oc.new InnerClass();// need of inclosing
                                                            // instance
        OuterClass.StaticInnerClass obj2 = new OuterClass.SIC();
        // no need of reference of object of outer class
    }
}


【6】成员变量与局部变量

成员变量:随着对象的创建而存在,随着对象的消失而消失,存储在堆内存中。

局部变量:在方法被调用,或者语句被执行的时候存在,存储在栈内存中。当方法调用完,或者语句结束后,就自动释放。

作用域

成员变量:针对整个类有效。

局部变量:只在某个范围内有效。(一般指的就是方法,语句体内)


存储位置

成员变量:随着对象的创建而存在,随着对象的消失而消失,存储在堆内存中。

局部变量:在方法被调用,或者语句被执行的时候存在,存储在栈内存中。当方法调用完,或者语句结束后,就自动释放。


生命周期

成员变量:随着对象的创建而存在,随着对象的消失而消失

局部变量:当方法调用完,或者语句结束后,就自动释放。


初始值

成员变量:有默认初始值。

局部变量:没有默认初始值,使用前必须赋值。


使用原则

在使用变量时需要遵循的原则为:就近原则 首先在局部范围找,有就使用;接着在成员位置找。

目录
相关文章
|
4月前
|
缓存 JavaScript Java
怎么在Java 16中编写C风格的局部静态变量
Java 16通过 JEP 395 放宽了内层类声明静态成员的限制, 允许声明静态成员, 如记录类成员. 这项改进使得可以在局部范围内使用类似 C 风格的静态变量, 即局部变量仅初始化一次并在多次调用间共享. 例如, 缓存正则表达式模式, 以前需要将其置于类命名空间中, 现在可以保持在方法范围内
|
1月前
|
Java 编译器
【Java】内部类
【Java】内部类
22 0
|
3月前
|
Java 调度 开发者
Java 神秘新成员 —— 虚拟线程究竟是什么?它又能解开哪些编程痛点之谜?
【8月更文挑战第23天】Java虚拟线程是一种轻量级执行线程,由Java运行时管理,相较于传统操作系统线程,其创建和管理成本更低。基于用户模式线程概念,Java应用程序无需依赖OS即可实现高度并发。虚拟线程数量可远超传统线程,有效提升多核处理器利用率和并发性能。它解决了传统Java线程模型中创建成本高、调度开销大及I/O阻塞等问题,极大提高了程序的并发性和响应速度。
37 1
|
3月前
|
Java
【Java基础面试二】、个Java文件里可以有多个类吗(不含内部类)?
这篇文章讨论了Java文件中类的定义规则,指出一个Java文件可以包含多个类(不包含内部类),但其中最多只能有一个public类,且如果有public类,它的名称必须与文件名一致。
|
3月前
|
算法 Java
12 Java常用类(一)(内部类+object类+包装类)
12 Java常用类(一)(内部类+object类+包装类)
37 5
|
4月前
|
Java
Java进阶之内部类
【7月更文挑战第13天】Java内部类增进代码组织与封装,允许直接访问外部类成员,包括私有成员。主要有四种类型:成员、静态、局部和匿名内部类。匿名内部类常用于一次性实现接口或扩展类。内部类可隐藏实现细节,减少命名冲突,并在特定上下文中定义辅助类。示例展示了静态和非静态内部类如何在Shape类中封装Circle和Rectangle。使用内部类能提升代码可读性,但可能增加复杂性。
37 6
|
4月前
|
Java 程序员
【Java探索之旅】继承概念_语法_父类的成员访问
【Java探索之旅】继承概念_语法_父类的成员访问
56 10
|
3月前
|
Java
【Java】内部类、枚举、泛型
【Java】内部类、枚举、泛型
|
5月前
|
Java
一篇文章讲明白Java中内部类详解—匿名内部类
一篇文章讲明白Java中内部类详解—匿名内部类
92 2
|
7天前
|
Java 开发者
Java多线程编程中的常见误区与最佳实践####
本文深入剖析了Java多线程编程中开发者常遇到的几个典型误区,如对`start()`与`run()`方法的混淆使用、忽视线程安全问题、错误处理未同步的共享变量等,并针对这些问题提出了具体的解决方案和最佳实践。通过实例代码对比,直观展示了正确与错误的实现方式,旨在帮助读者构建更加健壮、高效的多线程应用程序。 ####
下一篇
无影云桌面