> 文章列表 > Java ---内部类

Java ---内部类

Java ---内部类

(一)定义

 
   将一个类 A 定义在另一个类 B 里面,里面的那个类 A 就称为 部类 InnerClass ,类 B 则称为 外部类( OuterClass 广泛意义上的内部类一般来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类。

内部类的分类
根据内部类声明的位置(如同变量的分类),我们可以分为:

 

(二)为什么声明内部类呢

      具体来说,当一个事物 A 的内部,还有一个部分需要一个完整的结构 B 进行描 述,而这个内部的完整的结构 B 又只为外部事物 A 提供服务,不在其他地方单 独使用,那么整个内部的完整结构 B 最好使用内部类。 总的来说,遵循高内聚、低耦合 的面向对象开发原则。

     现实的例子就是某个公司内部卷只能在本公司使用,其他地方不认,如果声明为外部类,其他地方能使用,但不符合逻辑,所以引出来内部类。

 

(三)成员内部类

如果成员内部类中不使用外部类的非静态成员,那么通常将内部类声明为静态内部类,否则声明为非静态内部类。

语法格式:

【修饰符】 class 外部类{【其他修饰符】 【static】 class 内部类{}
}

1、静态内部类

有static修饰的成员内部类叫做静态内部类。它的特点:

  • 和其他类一样,它只是定义在外部类中的另一个完整的类结构

    • 可以继承自己的想要继承的父类,实现自己想要实现的父接口们,和外部类的父类和父接口无关

    • 可以在静态内部类中声明属性、方法、构造器等结构,包括静态成员

    • 可以使用abstract修饰,因此它也可以被其他类继承

    • 可以使用final修饰,表示不能被继承

    • 编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名和$符号。

  • 和外部类不同的是,它可以允许四种权限修饰符:public,protected,缺省,private

    • 外部类只允许public或缺省的

  • 可以在静态内部类中使用外部类的静态成员

    • 在静态内部类中不能使用外部类的非静态成员哦

    • 如果在内部类中有变量与外部类的静态成员变量同名,可以使用“外部类名."进行区别

  • 在外部类的外面不需要通过外部类的对象就可以创建静态内部类的对象(通常应该避免这样使用)

其实严格的讲(在James Gosling等人编著的《The Java Language Specification》)静态内部类不是内部类,而是类似于C++的嵌套类的概念,外部类仅仅是静态内部类的一种命名空间的限定名形式而已。所以接口中的内部类通常都不叫内部类,因为接口中的内部成员都是隐式是静态的(即public static)。例如:Map.Entry。

2、非静态成员内部类

没有static修饰的成员内部类叫做非静态内部类。非静态内部类的特点:

  • 和其他类一样,它只是定义在外部类中的另一个完整的类结构

    • 可以继承自己的想要继承的父类,实现自己想要实现的父接口们,和外部类的父类和父接口无关

    • 可以在非静态内部类中声明属性、方法、构造器等结构,但是不允许声明静态成员,但是可以继承父类的静态成员,而且可以声明静态常量

    • 可以使用abstract修饰,因此它也可以被其他类继承

    • 可以使用final修饰,表示不能被继承

    • 编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名和$符号。

  • 和外部类不同的是,它可以允许四种权限修饰符:public,protected,缺省,private

    • 外部类只允许public或缺省的

  • 还可以在非静态内部类中使用外部类的所有成员,哪怕是私有的

  • 在外部类的静态成员中不可以使用非静态内部类哦

    • 就如同静态方法中不能访问本类的非静态成员变量和非静态方法一样

  • 在外部类的外面必须通过外部类的对象才能创建非静态内部类的对象(通常应该避免这样使用)

    • 如果要在外部类的外面使用非静态内部类的对象,通常在外部类中提供一个方法来返回这个非静态内部类的对象比较合适

    • 因此在非静态内部类的方法中有两个this对象,一个是外部类的this对象,一个是内部类的this对象

静态内部类与非静态内部类的区别

静态内部类 非静态内部类 类角色    字节码文件 外部类名$内部类名 外部类名$内部类名 修饰符 public,缺省,abstract,final public,缺省,abstract,final 父类或父接口 可以 可以 成员角色 可以包含的成员 所有成员 不允许有静态成员 修饰符

public、protected、缺省、private,final,static
没有static

依赖于外部类 依赖 依赖 依赖于外部类的对象 不依赖 依赖 使用 在外部类中使用内部类 没有限制 在外部类的静态方法等中不能使用非静态内部类 在内部类中使用外部类 静态内部类中不能使用外部类的非静态成员 没有限制

在外部类的外面使用内部类的静态成员

外部类名.静态内部类名.静态成员 没有 在外部类的外面使用内部类的非静态成员 外部类名.静态内部类名 变量 = 外部类名.静态内部类名();

变量.非静态成员(); 外部类名 变量1 = new 外部类();

外部类名.非静态内部类名 变量 = 变量1.new 非静态内部类名();

变量.非静态成员(); 重名 外部类名.重名的成员名 外部类名.this.重名的成员

 具体代码:

public class TestMemberInnerClass {public static void main(String[] args) {Outer.outMethod();System.out.println("-----------------------");Outer out = new Outer();out.outFun();System.out.println("####################################");Outer.Inner.inMethod();System.out.println("------------------------");Outer.Inner inner = new Outer.Inner();inner.inFun();System.out.println("####################################");Outer outer = new Outer();
//        Outer.Nei nei = outer.new Nei();Outer.Nei nei = out.getNei();//跟上句等效nei.inFun();}
}
class Outer{private static String a = "外部类的静态a";private static String b  = "外部类的静态b";private String c = "外部类对象的非静态c";private String d = "外部类对象的非静态d";static class Inner{private static String a ="静态内部类的静态a";private String c = "静态内部类对象的非静态c";public static void inMethod(){System.out.println("Inner.inMethod");System.out.println("Outer.a = " + Outer.a);System.out.println("Inner.a = " + a);System.out.println("b = " + b);
//            System.out.println("c = " + c);//不能访问外部类和自己的非静态成员
//            System.out.println("d = " + d);//不能访问外部类的非静态成员}public void inFun(){System.out.println("Inner.inFun");System.out.println("Outer.a = " + Outer.a);System.out.println("Inner.a = " + a);System.out.println("b = " + b);System.out.println("c = " + c);
//            System.out.println("d = " + d);//不能访问外部类的非静态成员}}class Nei{private String a = "非静态内部类对象的非静态a";private String c = "非静态内部类对象的非静态c";public void inFun(){System.out.println("Nei.inFun");System.out.println("Outer.a = " + Outer.a);System.out.println("a = " + a);System.out.println("b = " + b);System.out.println("Outer.c = " + Outer.this.c);System.out.println("c = " + c);System.out.println("d = " + d);}}public static void outMethod(){System.out.println("Outer.outMethod");System.out.println("a = " + a);System.out.println("Inner.a = " + Inner.a);System.out.println("b = " + b);
//        System.out.println("c = " + c);
//        System.out.println("d = " + d);Inner in = new Inner();System.out.println("in.c = " + in.c);}public void outFun(){System.out.println("Outer.outFun");System.out.println("a = " + a);System.out.println("Inner.a = " + Inner.a);System.out.println("b = " + b);System.out.println("c = " + c);System.out.println("d = " + d);Inner in = new Inner();System.out.println("in.c = " + in.c);}public Nei getNei(){return new Nei();}
}

 

(四)局部内部类

1.局部内部类

语法格式:

【修饰符】 class 外部类{【修饰符】 返回值类型  方法名(【形参列表】){【final/abstract】 class 内部类{}}    
}

2.局部内部类的特点:

  • 和外部类一样,它只是定义在外部类的某个方法中的另一个完整的类结构

    • 可以继承自己的想要继承的父类,实现自己想要实现的父接口们,和外部类的父类和父接口无关

    • 可以在局部内部类中声明属性、方法、构造器等结构,但不包括静态成员,除非是从父类继承的或静态常量

    • 可以使用abstract修饰,因此它也可以被同一个方法的在它后面的其他内部类继承

    • 可以使用final修饰,表示不能被继承

    • 编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名、$符号、编号。

      • 这里有编号是因为同一个外部类中,不同的方法中存在相同名称的局部内部类

  • 和成员内部类不同的是,它前面不能有权限修饰符等

  • 局部内部类如同局部变量一样,有作用域

  • 局部内部类中是否能访问外部类的非静态的成员,取决于所在的方法

  • 局部内部类中还可以使用所在方法的局部常量,即用final声明的局部变量

    • JDK1.8之后,如果某个局部变量在局部内部类中被使用了,自动加final

    • 为什么在局部内部类中使用外部类方法的局部变量要加final呢?考虑生命周期问题。

3.匿名内部类

        当我们在开发过程中,需要用到一个抽象类的子类的对象或一个接口的实现类的对象,而且只创建一个对象,而且逻辑代码也不复杂。那么我们原先怎么做的呢?

(1)编写类,继承这个父类或实现这个接口

(2)重写父类或父接口的方法

(3)创建这个子类或实现类的对象

这里,因为考虑到这个子类或实现类是一次性的,那么我们“费尽心机”的给它取名字,就显得多余。那么我们完全可以使用匿名内部类的方式来实现,避免给类命名的问题。

new 父类(【实参列表】){重写方法...
}
//()中是否需要【实参列表】,看你想要让这个匿名内部类调用父类的哪个构造器,如果调用父类的无参构造,那么()中就不用写参数,如果调用父类的有参构造,那么()中需要传入实参
new 父接口(){重写方法...
}
//()中没有参数,因为此时匿名内部类的父类是Object类,它只有一个无参构造

匿名内部类是没有名字的类,因此在声明类的同时就创建好了唯一的对象。

注意:

匿名内部类是一种特殊的局部内部类,只不过没有名称而已。所有局部内部类的限制都适用于匿名内部类。例如:

  • 在匿名内部类中是否可以使用外部类的非静态成员变量,看所在方法是否静态

  • 在匿名内部类中如果需要访问当前方法的局部变量,该局部变量需要加final

思考:这个对象能做什么呢?

(1)使用匿名内部类的对象直接调用方法

(2)通过父类或父接口的变量多态引用匿名内部类的对象  

(3)匿名内部类的对象作为实参