转载

浅谈Java中的四种内部类

如果你看过一些JDK和框架源码的话,就经常会发现一般在类的定义中,都会再定义一些其他的类,这些类也同样会被编译成字节码文件,这样的类就被叫做 内部类 ,按照一般的分法,大致可以分为以下四类:

  • 成员内部类
  • 局部内部类
  • 匿名内部类
  • 静态内部类

接下来会针对这四种内部类进行详细讲解,旨在解释这些类的特点和应用场景,如果你懒得看的话,可以直接翻到最底下看总结

我们预先定义好一个类,接来下的所有操作都会在这个类中执行:

class Demo {
    
    public int pubVal = 1;
    
    private int priVal = 1;
    
    public static void staticMethod() {}
    
    public void publicMethod() {}
    
    private void privateMethod() {}
    
}
复制代码

成员内部类

概念

从名字就可以看出,这种内部类是作为类的成员而存在的,其定义位于类的内部,可以类比为成员变量来理解

实现

class Demo {

	// ...	
	
    class InternalClass {
        
    }
    
    // ...
    
}
复制代码

这样就是一个成员内部类了

约束

首先是类定义的约束,实际上,成员内部类的类定义没有任何约束(不涉及static,因为这属于静态内部类的范畴),不仅可以将内部类声明为public、private的,甚至可以将其声明为一个abstract抽象类和interface接口,以及继承外部类也是允许的,其定义是十分宽松的

接着是内部属性和方法,这里就有一些约束了:

  • 不能使用static来修饰任何成员内部类中的属性和方法,但可以使用private、final等其余任意修饰符
  • 可以用static final来修饰成员
  • 可以允许与外部类字段和方法重名

也就是说,成员内部类不能存在静态属性和方法,这么做也是符合成员变量的含义

最后是访问上的约束,这里十分重要:

  • 成员内部类也存在this指针,但是这个指针指向的是自己的引用,如果想访问外部类,需要使用 外部类名.this 这个指针
  • 可以通过 外部类名.静态字段 来访问外部类的静态属性或方法字段

这里的限制就和成员方法是类似的,在理解时可以进行类比

使用

先来说声明,以本文中程序为例,我们可以通过下面的方式来声明一个内部类:

Demo.InternalClass demo
复制代码

注意,如果你将这个内部类声明为private的,外部类依然可以访问,但是外部类之外的其他类是无法访问到的

接着就是通过new来创建,我们在外部类的任何 非静态方法 中,都可以通过new来进行创建,具体格式如下:

Demo.InternalClass demo = new Demo.InternalClass();
复制代码

同样地,我们可以在外部类中,通过内部类的实例来访问其内部的私有变量和方法

局部内部类

我们刚花了大力气来讲解成员内部类,而局部内部类和成员内部类十分类似,可能一些相同的地方我就一笔带过了

概念

局部内部类位于外部类成员方法的内部,可以类比局部变量

实现

public void publicMethod() {
        class InternalClass {
            
        }
    }
复制代码

约束

这里的约束基本和成员内部类类似,我就单独说一些不同的地方:

  • 其类上不允许有任何修饰符,但是可以使用abstract将类声明为抽象类
  • 不允许将局部内部类声明为接口
  • 不允许使用static来声明成员变量和方法
  • 可以将局部内部类声明在静态方法中
  • 任意两个方法中的局部内部类可以重名

其余的基本和成员内部类类似,把局部内部类当作成员内部类的局部变量版本就好理解了,比如也拥有外部类的指针,使用方法和成员内部类一致

使用

既然是局部内部类,就只能在声明类的方法处来使用,声明和使用方式如下:

public void publicMethod() {
        class InternalClass {

        }

        InternalClass test = new InternalClass();
    }
复制代码

同样地,我们依然可以无条件访问内部类中定义的私有属性

匿名内部类

这种内部类应该是我们使用的最多的一种,有时候甚至我们已经使用过了却没有发现

概念

匿名内部类没有类的声明,会隐式地继承一个类或实现一个接口

实现

概念比较抽象,我们直接看是如何定义的,这里我们有一个接受一个对象参数的方法:

private void privateMethod() {
        new Demo() {
            public int newVal = 20;

            @Override
            public void publicMethod() {
                super.publicMethod();
            }
        };
    }
复制代码

这里的Demo可以换成任意一个类或者接口,你会发现这个类没有名字,所以被叫做匿名内部类

如果Demo是一个普通类,则匿名内部类相当于这个类的子类;如果Demo是一个接口或者抽象类,则这个匿名内部类相当于接口或抽象类的实现

约束

想要理解匿名内部类的约束,就需要将整个匿名内部类不要当成一块程序逻辑来看,而应该当成一个对象来处理,整块匿名内部类完全可以当成一个对象,可以调用对象的方法、属性等等

其主要的约束有以下这些:

  • 不能使用static来修饰方法和属性,但是可以有static final的属性
  • 可以使用this指针来访问本身定义的变量和继承得到的变量,也可以使用 外部类名.this 指针来访问外部类中的所有属性
  • 无法在类上进行任何修饰,因为没有class定义符和类名
  • 其中定义的私有字段对外是完全可见的

使用

这里就不进行过多讲解了,完全和普通对象的用法一致,这里就举两个简单的例子:

int val = new Demo() {
            private int newVal = 20;

            @Override
            public void publicMethod() {
                super.publicMethod();
            }
        }.newVal;
        
        Demo demo = new Demo() {
            private int newVal = 20;

            @Override
            public void publicMethod() {
                super.publicMethod();
            }
        };
复制代码

静态内部类

概念

静态内部类相当于static修饰的成员内部类,可以当作静态变量来理解

实现

class Demo {

	// ...	
	
    static class InternalClass {
        
    }
    
    // ...
    
}
复制代码

约束

这里的约束就和之前的有很大不同了,如下:

  • 可以使用任意的修饰符来修饰类、方法和属性
  • 可以访问外部类的静态方法和属性

没错,广义上,静态内部类根本没有约束

使用

使用有以下两种情况:

在外部类的方法中使用时,通过如下语句来创建内部类对象:

InternalClass test = new InternalClass();
复制代码

在外部类之外的其他类可以通过下面的语句来创建内部类对象:

Demo.InternalClass test = new Demo.InternalClass();
复制代码

总结

最后,为大家总结了一张表,基本上内部类的知识点都在这里了:

成员内部类 局部内部类 匿名内部类 静态内部类
类比 成员变量 局部变量 子类或接口的实现类 静态变量
类定义声明位置 类的内部 作用域内部 任意对象可能出现的位置 与成员内部类一致
类修饰符 除static外任意 只允许用abstract修饰 无法修饰 任意的修饰符均可
变量和方法修饰符 除static外任意(但是可以用static final修饰变量) 与成员内部类一致 与成员内部类一致 任意修饰符均可
外部类的访问权限 均可 均可 均可 均可
对象声明位置 只能在外部类的成员方法中,和外部类之外的其余类中声明(如果在外部类之外,要看类访问控制是否允许) 只能在作用域中声明 无需声明 任意位置均可 (如果在外部类之外,要看类访问控制是否允许)
对象创建位置 只能在外部类的成员方法中创建 只能在作用域中创建 立即创建 任意位置均可 (如果在外部类之外,要看类访问控制是否允许)
是否允许有内部类的内部类 可以,但是不允许有静态内部类 与成员内部类一致 与成员内部类一致 可以
原文  https://juejin.im/post/5cef627c51882505202d0ae9
正文到此结束
Loading...