Java内部类小结

内部类的概念与使用实在是有些繁杂,因为他本身涉及到java内部一些很基础的知识,包括修饰符、作用域等等,在网上很难搜索到一篇全面、准确的关于内部类的总结,所以在这里抛砖引玉一下,希望能对自己、对大家都有所帮助。

本篇大概分成三部分内容,第一部分是对内部类概念性的介绍,第二部分是对内部类语法规则和特性方面的介绍,而第三部分则是对内部类用法、用途的简单介绍,第三部分的内容主要参考自《Effective Java 2nd. Edition》中的Item 22: Favor static member classes over nonstatic

一. 概念介绍

1. 什么是内部类

简单的说,内部(inner)类指那些类定义代码被置于其它类定义中的类;而对于一般的、类定义代码不嵌套在其它类定义中的类,称为顶层(top-level)类。对于一个内部类,包含其定义代码的类称为它的外部(outer)类。

2. 内部类都有哪几种

一般来讲,都是把内部类分为四种:静态成员类(static member classes)、非静态成员类(nonstatic member classes)、局部类(local classes)、匿名类(anonymous classes)

2.1. 静态成员类:所谓静态成员类,就是用static来修饰的,定义于外部类顶层的内部类

例:


注:继承自某个类的匿名类,和实现某个接口的匿名类,在表现形式上略有差别:

2.4.1 继承自某个类的匿名类:

package  test;

public   class  OuterClass  {
    
// 可以在外部类顶层定义静态成员类
    public static class Inner1 {
        
    }

    
    
}



2.2. 非静态成员类:顾名思义,就是没有用static来修饰的,定义于外部类顶层的内部类

例:

package  test;

public   class  OuterClass  {
    
// 可以在外部类顶层定义非静态成员类
    public class Inner1 {
        
    }

}




2.3. 局部类:定义于外部类的代码块或方法内部的内部类。局部类还可以进一步细分为两种:

    2.3.1. 局部静态成员类:定义于外部类的静态方法或静态初始化代码段中的局部类
    2.3.2. 局部成员类:定义于外部类的实例方法或实例初始化代码段中的局部类

例:

package  test;

public   class  OuterClass  {
    
    
public static void method1() {
        
// 局部静态成员类
        class inner4 {
            
        }

    }

    
    
public void method2() {
        
// 局部成员类
        class inner5 {
            
        }

    }

    
}



    也就是说,局部类也有“静态”和“非静态”之分,取决于他所处的方法或代码段。

2.4. 匿名类:表面上来看,匿名类就是没有类名的局部类。但其实二者还是有区别的。局部类,是创建一个新的类;而匿名类,是针对已经定义好的接口或类,将其实现(接口)或扩展(类)并实例化。

例:

package  test;

public   class  OuterClass  {
        
    
public static void method1() {
                
        
// 继承OuterClass类,加入一个新的方法print(),然后实例化并调用print()方法
        new OuterClass() {
            
public void print() {System.out.println("hi");}
        }
.print();
    }

    
    
public static void main(String[] args) {
        
// 运行结果:hi
        OuterClass.method1();
    }

}

 

new class-name ( [ argument-list ] ) { class-body }















    















创建匿名类的示例时,argument-list(如果有的话)将作为参数被传入基类对应的构造函数







































2.4.2. 实现某接口的匿名类:

new interface-name () { class-body }










二. 语法规则

1. 静态成员类:

1.1. 可以做的:

像静态方法或静态字段一样,可以用public, private, protected来修饰,不加则为默认(package)
可以访问外部类的任一静态字段或静态方法
可以用OuterClass.InnerClass的方式来引用

1.2. 不可以做的:

像外部类的静态方法一样,不可以访问外部类的非静态方法或非静态字段

2. 非静态成员类:

2.1. 可以做的:

可以用public, private, protected来修饰,不加则为默认(package)
可以访问外部类的任一字段和函数(不管是否static),这是因为非静态成员类的实例包含外部类的引用。
当非静态内部类中所定义的某个成员变量和外部类中的变量重名时,外部类的变量将被屏蔽,此时可以用OuterClass.this.来得到被屏蔽的外部类变量
有两种方式来创建非静态成员类的实例。第一种比较常用,就是在外部类的方法内部直接创建: InnerClass inner = new InnerClass(); 另外一种则是通过表达式enclosingInstance.new MemberClass(args)来创建。具体见2.2.

2.2. 不可以做的:

不能在非静态成员类内部定义static字段、方法或内部类。这是因为完全可以将这些东西移到外部类中去;但是可以定义static final常量
在第三方类里面(指非外部类的某个其他类)不可以用OuterClass.InnerClass inner = new OuterClass.InnerClass(); 的方法来创建非静态成员类的实例。这是因为非静态成员类实例是依赖于外部类实例而存在的(包含外部类的引用),但如果我们有了一个外部类的实例outerClass,则可以用下面的方法来创建:

OuterClass.InnerClass innerClass = outerClass.new InnerClass();


3. 局部类:

3.1. 可以做的:

能且只能访问所属代码段中声明为final的变量。这是因为局部变量在其所属的代码段(譬如某个函数)执行完毕后就会被回收,而一个局部类的实例却可以在其类定义所属代码段执行完毕后依然存在,如果它可操控非final的局部变量,用户就可以通过该实例修改已不存在的局部变量,无意义。
能且只能被final, abstract修饰

3.2. 不可以做的:

不能被public, private, protected修饰,也不能被static修饰。这是因为局部类只在定义它的代码段中可见,不能在它所属代码段之外的代码中使用
同理,局部类内部不能定义static成员
不能以局部类形式定义一个接口。因为局部类只在其所属代码段中可见,定义这样的接口无意义

4.匿名类

当前仅当匿名类处于非静态代码段时,他会有外部类的引用(即可以访问外部类任意资源)。局部类也是如此,不同的是匿名类在声明处的同时被初始化。

匿名类还有一个限制,就是不能同时实现多个接口,也不能同时实现一个接口并扩展一个基类。这是因为匿名类的声明和实例化是同时发生的,它不像通常的类的声明那样,可以通过implement关键字指定多个接口,或者通过exteds关键字来指定基类。对一个匿名类而言,他只能通过指定接口或基类的名字,来告诉编译器说:现在我要实现这个接口(or扩展这个基类),因此一个匿名类能且只能实现一个接口或者扩展一个基类




三. 内部类的使用及其意义

1. 静态成员类

静态成员类的通常用法是作为一个public helper class,与他的OuterClass结合在一起使用,为OuterClass服务。
比如一个Calculator类,他有各种操作,这些操作就可以存储在他的静态内部类Operation里,客户端就可以通过Calculator.Operation.PLUS或者Calculator.Operation.MINUS等来指定各种操作

那一个private的静态成员类可以用来干什么呢?可以用来代表外部类所需要的某个组件。
比如,对于Map接口,他内部就有一个Entry类,一个Entry对象对应着一个键-值对,该对象上的方法(getKey, getValue, setValue)也不需要访问外部类的资源,将Entry定义为private的静态成员类类就是最合适的。

2. 非静态成员类

一种通常的用法是把非静态成员类作为一个Adapter,使外部类的实例看起来提供了某个不相关的类的实例的功能。
比如,collection接口的各种实现类,一般就是用非静态成员类来实现他们的iterator:

public   class  MySet < E >   extends  AbstractSet < E >   {
    
public Iterator<E> iterator() {
        
return new MyIterator();
    }

    
private class MyIterator implements Iterator<E> {
    }

}


关于静态成员类和非静态成员类,有一点需要注意的是:如果你的内部类不需要访问外部类的成员变量和函数(尤其是指非static类型的),那么尽量用静态成员类。为什么?因为每个非静态成员类的实例都将保存他的外部类的引用,这不仅造成时空上的浪费,而且可能导致当外部类实际上已经可以被垃圾回收器回收的时候,却因为某个非静态成员类实例还保留着该外部类的引用而不能回收该外部类。

3. 匿名类

匿名类用的地方应该是这四种内部类中最多的,至少我是这样。我经常用匿名类的地方就是在事件响应的代码中。
Effective Java中总结了三种匿名类的用途:
3.1. To create function objects on the fly
3.2. To create process objects, such as Runnable, Thread, TimerTask instances
3.3. To use within static factory methods


总结,
1.当内部类需要在方法外部仍然可见时,使用成员类(静态or非静态);当内部类比较长,放在方法内部会影响程序可读性时,使用成员类(静态or非静态);
2. 如果需要在内部类内部定义静态成员,只能使用静态成员类(其他三个都不支持);如果成员类的每个实例都需要外部类的引用,定义为非静态的,否则,就要定义成静态的;
3. 假设我们需要在方法内部定义一个局部类或者匿名类,如果我们只需要在这一个位置使用内部类实例,并且已经有预先定义好的基类或接口,那就使用匿名类;否则,使用局部类

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