java 内部类

内部类分为以下四种:

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

一、静态内部类

  • 静态内部类: 在类中用static关键字声明的内部类。

  • 静态内部类不依赖于外围类对象实例而独立存在

  • 静态内部类的可以访问外围类中的所有静态成员,包括private的静态成员。

  • 同时静态内部类可以说是所有内部类中独立性最高的内部类,其创建对象、继承(实现接口)、扩展子类等使用方式与外围类并没有多大的区别。

public class OuterClass {//外部类
   
    public int aa; //普通成员变量
    private static float f = 1.5f;//private的静态成员
    
    static void myPrintln() {
        System.out.println("静态方法");
    }
    
    protected static class StaticInnerClass{//protected的静态内部类
        
        float a;
        
        public StaticInnerClass() {
             a = f;// 外围类的private静态变量
             myPrintln();//外围类的静态方法
        }
    }
}

class OtherClass{

   public static void main(String[] args) {
       //创建静态内部类的对象
    OuterClass.StaticInnerClass staticInnerClass = new OuterClass.StaticInnerClass(); 
   } 
}

二、成员内部类

  • 成员内部类是最普通的内部类,它的定义为位于一个类的内部

  • 成员内部类可以访问外部类的所有成员属性和成员方法(包括静态成员,可以拥有private访问权限、protected访问权限、public访问权限及包访问权限)

  • 成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。

  • 成员内部类是不可以声明静态成员(包括静态变量、静态方法、静态成员类、嵌套接口),但有个例外:可以声明 static final的变量, 这是因为编译器对final类型的特殊处理,是直接将值写入字节码

  • 成员内部类对象都隐式地保存了一个引用,指向创建它的外部类对象;或者说,成员内部类的入口是由外围类的对象保持着(静态内部类的入口,则直接由外围类保持着)

成员内部类中的 this关键字:
  • 获取外部类对象:OuterClass.this
  • 明确指定使用外部类的成员(当内部类与外部类的名字冲突时):
    OuterClass.this.成员名
成员内部类访问外部类成员

当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,默认情况下访问的是成员内部类的成员。

如果要访问外部类的同名成员,需要以下面的形式进行访问:

  - 外部类.this.成员变量
  - 外部类.this.成员方法
外部类访问成员内部类的成员
1.先创建一个成员内部类的对象
2.再通过指向这个对象的引用来访问。
创建成员内部类对象的一般方式:

在外部类的成员方法中创建成员内部类 (内部类名 对象名=new 内部类名();)与 在其他类中或静态方法中创建成员内部的方式是不一样的

public class Test {
    public static void main(String[] args)  {
        //第一种方式:
        Outter outter = new Outter();
        Outter.Inner inner = outter.new Inner();  //必须通过Outter对象来创建
         
        //第二种方式:
        Outter.Inner inner1 = outter.getInnerInstance();
    }
}
 ///////////////////////////////////////////////////////////////////////////////
class Outter {
    private Inner inner = null;
    public Outter() {
         
    }
     
    public Inner getInnerInstance() {
        if(inner == null)
            inner = new Inner();
        return inner;
    }
      
    class Inner {
        public Inner() {
             
        }
    }
}

上面的例子,如果成员内部类Inner用private修饰,则只能在外部类的内部访问,如果用public修饰,则任何地方都能访问;如果用protected修饰,则只能在同一个包下或者继承外部类的情况下访问;如果是默认访问权限,则只能在同一个包下访问。这一点和外部类有一点不一样,外部类只能被public和包访问两种权限修饰。我个人是这么理解的,由于成员内部类看起来像是外部类的一个成员,所以可以像类的成员一样拥有多种权限修饰。

成员内部类被继承

在内部类的访问权限允许的情况下,成员内部类也是可以被继承的。由于成员内部类的对象依赖于外围类的对象。因此,继承了成员内部类的子类必须要与一个外围类对象关联起来。同时,子类的构造器是必须要调用父类的构造器方法,所以也只能通过父类的外围类对象来调用父类构造器。


class ChildClass extends OuterClass.InnerClass{
    
    //成员内部类的子类的构造器的格式
    public ChildClass(OuterClass outerClass) {
        outerClass.super();//通过外围类的对象调用父类的构造方法
    }
}

三、局部内部类

  • 局部内部类: 就是在方法、构造器、初始化块中声明的类,在结构上类似于一个局部变量。

  • 局部内部类的访问仅限于方法内或者该作用域内,局部内部类是不能使用访问修饰符。

  • 局部内部类只能访问final的局部变量

  • 局部内部类定义在实例环境中(构造器、对象成员方法、实例初始化块),则可以访问外围类的所有成员;但如果内部类定义在静态环境中(静态初始化块、静态方法),则只能访问外围类的静态成员

public class OuterClass {
    
    private int a = 21;
    
    static {//静态域中的局部内部类
       class LocalClass1{
              //  int z = a; //错误,在静态的作用域中无法访问对象成员
       }
    }
    
    {//实例初始化块中的局部内部类
        class localClass2{          
        }
    }
    
    public OuterClass(){
        int x = 2;
        final int y = 3;
        // x = 3;//若放开此行注释,编译无法通过,因为局部变量x已经是final类型
        //构造器中的局部内部类
        class localClass3{
            int z = y; //可以访问final的局部变量
            int b = a;//可以访问类的所有成员
             //访问没有用final修饰的局部变量
            int c = x;
        }
    }
    
    public void createRunnable() {
        
        final int x = 4;
        //方法中的局部内部类
        class LocalClass4 implements Runnable {//
            @Override
            public void run() {
                System.out.println("局部final变量:"+x);
                System.out.println("对象成员变量:"+a);
            }
            
        }
    }
}

四、匿名内部类

  • 匿名内部类也是不能有访问修饰符和static修饰符的。

  • 匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。匿名内部类在编译的时候由系统自动起名为Outter$1.class、Outter$2.class。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。

  • 在创建这个匿名内部类后,便会立即用来创建并返回此内部类的一个对象引用。

作用:

匿名内部类用于隐式继承某个类(重写里面的方法或实现抽象方法)或者实现某个接口。

匿名内部类的访问限制:

在实例环境中(构造器、对象成员方法、实例初始化块),则可以访问外围类的所有成员;但如果内部类定义在静态环境中(静态初始化块、静态方法),则只能访问外围类的静态成员

匿名内部类的优缺点:
  • 优点:
    编码方便快捷;

  • 缺点:
    只能继承一个类或实现一个接口,不能再继承其他类或其他接口。
    只能用于创建一次对象实例;

一个例子:

class MyOuterClass {

    private int x = 5;

    void createThread() {
        
        final int a = 10;
        int b = 189;
        
        // 匿名内部类继承Thread类,并重写Run方法
        Thread thread = new Thread("thread-1") {

            int c = x;  //访问成员变量
            int d = a;  //final的局部变量
            int e = b; //访问没有用final修饰的局部变量
            
            @Override
            public void run() {
                System.out.println("这是线程thread-1");
            }
        };

        // 匿名内部类实现Runnable接口
        Runnable r = new Runnable() {

            @Override
            public void run() {
                System.out.println("线程运行中");
            }
        };
    }
}

内部类嵌套内部类

  • 成员内部类可以继续包含成员内部类,而且不管一个内部类被嵌套了多少层,它都能透明地访问它的所有外部类所有成员;
  • 成员内部类可以继续嵌套多层的成员内部类,但无法嵌套静态内部类;
  • 静态内部类则都可以继续嵌套这两种内部类。
    下面的例子是基于上面的例子进行改造:
class InnerClass{//成员内部类
        private double aa; //与围类的变量aa的名字重复
        
        public InnerClass(){
            this.aa = OuterClass.this.aa + f;//明确指定两个aa的所属
            initInnerClass();
        }
        
        public  class InnerInnerCalss2{//成员内部类中的成员内部类
            protected double aa = OuterClass.this.aa;//最外层的外围类的成员变量
        }//InnerInnerCalss2
    }//InnerClass

总结

类 型 访问修饰符 声明静态成员 绑定外围类
静态内部类 四种访问修饰符 可以声明 不绑定
成员内部类 四种访问修饰符 除 final static 的变量外,其余静态成员都不行 绑定
局部内部类 不可以声明 不可以声明 取决于此内部类的声明环境
局部内部类 不可以声明 不可以声明 取决于此内部类的声明环境
为什么在Java中需要内部类

1. 每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整,

2. 方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。

3. 方便编写事件驱动程序

4. 方便编写线程代码

你可能感兴趣的:(java 内部类)