美文网首页java
java 内部类

java 内部类

作者: n油炸小朋友 | 来源:发表于2018-05-10 02:52 被阅读2次

    内部类分为以下四种:

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

    一、静态内部类

    • 静态内部类: 在类中用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 内部类

        本文链接:https://www.haomeiwen.com/subject/zlworftx.html