Java内部类详解:成员/静态/局部/匿名内部类

总的来说
1.内部类通常用来解决“多重继承”的问题。
2.当你希望隐藏一个类的实现,减少工程中.java文件数量,或者这个类不想被扩展时,你可以通过匿名内部类来创建一个类的对象。
3.java虽然无法直接在语法层面上支持闭包,但是可以通过内部类来模拟一个闭包的程序结构。

1.内部类

概念:在一个类的内部再定义一个完整的类。

语法:

class Outer {
    class Inner {
    }
}

编译:Outer$Inner.class 和 Outer.class

特点:

  • 编译之后可生成独立的字节码文件

  • 内部类可直接访问外部类的私有成员,而不破坏封装

  • 可为外部类提供必要的内部功能组件

1.1 成员内部类

概念:在类的内部定义,与实例变量、实例方法同级别的类。属于外部类的一个实例部分,创建内部类对象时,必须依赖外部类对象。

语法:

// 创建内部类对象
Outer out = new Outer();
Inner in = out.new Inner(); // 特殊:不具普适性
// 成员内部类访问外部类的重名属性
Outer.this.属性名

特点:

  • 当外部类、内部类存在重名属性时,会优先访问内部类属性。

  • 成员内部类不能定义静态成员

/**

* 成员内部类

*/

public class TestMemberInnerClass {

      public static void main(String[] args) {
            Outer out = new Outer();
            //Outer.Inner in = new Outer.Inner();  //Error
            Outer.Inner in = out.new Inner(); // 特殊:不具备普适性
            out.m1();
            in.m2();
      }
}

class Outer {
      int a = 10; // 外部类:实例变量
      public Outer() {
            System.out.println("Outer()");
      }
      public void m1() {
            System.out.println("Outer class m1()");
      }
      /**
       * 成员内部类(实例层级) - 外部类被创建后,可再创建成员内部类
       */
      class Inner {
            int b = 20;
            int a = 30; // 内部类:重名实例变量
            public Inner() {
                  System.out.println("Inner()");
            }
            public void m2() {
                  System.out.println("Inner class m2()");
                  System.out.println("Outer a = " + Outer.this.a); //  10 (特殊:不具普适性)
                  System.out.println("Inner a = " + a); // 30
            }
      }
}
1.2 静态内部类

概念:不依赖外部类对象,可直接创建或通过类名访问,可声明静态成员

特点:

  • 只能直接访问外部类的静态成员(实例成员需实例化外部类对象)
/**
* 静态内部类
*/
public class TestStaticInnerClass {
      public static void main(String[] args) {
            System.out.println(Outer.Inner.b); // 20
            // 静态内部类对象创建
            Outer.Inner in = new Outer.Inner();
            in.m2();
      }
}

class Outer {
      private static int a = 10;
      String s = "hello";
      static class Inner {
            static int b = 20;
            public void m2 () {
                  System.out.println(Outer.a); // 可访问外部类私有对象,不影响封装
                  //System.out.println(s); // 静态内部类不能访问外部类的非静态成员
                  System.out.println("Inner m2()");
            }
      }
}
1.3 局部内部类

概念:定义在外部类的成员方法中,作用范围和创建对象范围仅限于当前方法

特点:

  • 不能有静态成员(属性/方法)

  • 局部内部类访问外部类当前方法中的局部变量时,因无法保障变量的生命周期与自身相同,变量必须修饰为final

  • 隐藏类的信息,限制类的使用范围



/**
* 局部内部类
*/
public class TestLocalInnerClass {
      public static void main(String[] args) {
            Outer out = new Outer();
            out.m1();
            System.out.println(out.p); 
            //  com.day.t2_localinnerclass2.Outer$1Inner@7852e922
            out.p.print();
      }
}

interface Printable {
      public void print();
}

class Outer {
      int a = 10;
      Printable p = null; // 接口引用 (可通过成员方法的 局部内部类 对接口引用赋值,保存对象)
      public void m1() {
            //Cannot refer to a non-final variable c inside an inner  class defined in a different in a different method
            /* final */int b = 20; // 局部内部类访问时会被自动加 final 修饰
            class Inner implements Printable {
                  int c = 30;
                  @Override
                  public void print() {
                        System.out.println(a); // 10, 访问外部类的实例成员,等价于 Outer.this.a
                        System.out.println(b); // 20, 一旦局部内部类访问外部类的局部变量,会被自动final修饰
                        System.out.println(c); // 30, 访问内部类的实例成员
                        System.out.println("local inner m2()");
                  }
            }

            // 创建对象
            Inner in = new Inner();
            System.out.println(in.c);
            // 接口引用指向实例对象
            p = new Inner();
      }
}

示例:限制类的使用范围

/**
* 局部内部类 - 限制类的使用范围
*/
public class TestLocalInnerClassForApply {
      public static void main(String[] args) {
            // 学校一年级开设新版(班主任)
            // 家长意见:我们需要经验丰富的老师
            //Teacher teacher = new AdvancedTeacher(); //new Error:类定义在成员方法内 - 局部内部类
            //teacher.teach();
            // 校方没有那么多高级老师(6个班,3位高级老师,3位初级老师)
            // 校方规则:班级奇偶编号,奇数班初级,偶数班高级
            Teacher t = School.getTeacher(1);
            t.teach();
      }
}

class School {
      public static Teacher getTeacher(int classNo) { // 封装思想:隐藏了类的信息,不能在外部new对象
            // 初级老师
            class BeginnerTeacher extends Teacher {
                  @Override
                  public void teach() {
                        System.out.println("初级老师在上课");
                  }
            }

            // 高级老师
            class AdvancedTeacher extends Teacher {
                  @Override
                  public void teach() {
                        System.out.println("高级老师在上课");
                  }
            }

            Teacher currentTeacher = null; // 返回值
            if (classNo % 2 != 0) {
                  currentTeacher = new BeginnerTeacher();
            } else {
                  currentTeacher = new AdvancedTeacher();
            }
            return currentTeacher;
      }
}

abstract class Teacher {
      public abstract void teach();
}
1.4 匿名内部类【实际编码中很常见】

概念:没有类名的局部内部类(一切特征都与局部内部类相同)

特点:

  • 必须继承一个父类或者实现一个接口

  • 定义类、实现类、创建对象的语法合并,只能创建一个该类的对象

  • 使用场景:① 显式继承父类时 ② 实现接口时

  • 优点:可以减少代码量,书写的思路流畅

  • 缺点:可读性较差

/**
* 匿名内部类
*/
public class TestAnonymiteInnerClass {
      public static void main(String[] args) {
            Teacher t = School.getTeacher(1);
            t.teach();
      }
}

class School {
      public static Teacher getTeacher(int classNo) { // 封装思想:隐藏了类的信息,不能在外部new对象
            Teacher currentTeacher = null; // 返回值

            if (classNo % 2 != 0) {
                  // 匿名内部类
                  currentTeacher = new Teacher() {
                        @Override
                        public void teach() {
                              System.out.println("初级老师在上课");
                        }
                  };
            } else {
                  //匿名内部类 (创建了一个父类的子类对象,实现了子类中覆盖父类的方法)
                  currentTeacher = new Teacher() {
                        @Override
                        public void teach() {
                              System.out.println("高级老师在上课");
                        }                       
                  };
            }
            return currentTeacher;
      }
}

abstract class Teacher {
      public abstract void teach();
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,718评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,683评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,207评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,755评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,862评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,050评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,136评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,882评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,330评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,651评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,789评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,477评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,135评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,864评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,099评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,598评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,697评论 2 351