广泛意义上的内部类一般来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类
成员内部类
在成员内部类中要注意两点,第一:成员内部类中不能存在任何static的变量和方法;第二:成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类。
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() {
}
}
}
当成员内部类拥有和外部类同名的成员变量或者方法时,默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问:
外部类.this.成员变量
外部类.this.成员方法
内部类可以拥有private访问权限、protected访问权限、public访问权限及包访问权限。
- 如果成员内部类Inner用private修饰,则只能在外部类的内部访问,
- 如果用public修饰,则任何地方都能访问;
- 如果用protected修饰,则只能在同一个包下或者继承外部类的情况下访问;
- 如果是默认访问权限,则只能在同一个包下访问。
这一点和外部类有一点不一样,外部类只能被public和包访问两种权限修饰。
推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时 。
为什么成员内部类可以访问外部类所有成员?
编译器会默认为成员内部类添加了一个指向外部类对象的引用, 虽然我们在定义的内部类的构造器是无参构造器,编译器还是会默认添加一个参数,该参数的类型为指向外部类对象的一个引用,因此可以在成员内部类中随意访问外部类的成员。从这里也间接说明了成员内部类是依赖于外部类的,如果没有创建外部类的对象,也就无法创建成员内部类的对象了。
局部内部类
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
它嵌套在方法和作用域内,对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类,局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法和属性中被使用,出了该方法和属性就会失效。
class People
{
public People() {}
}
class Man
{
public Man(){}
public People getWoman()
{
class Woman extends People
{ //局部内部类
int age =0;
}
return new Woman();
}
}
局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。
匿名内部类
匿名内部类也是不能有访问修饰符和static修饰符的。
匿名内部类是唯一一种没有构造器的类 ( 因为它连名字都没有何来构造方法 ) 。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。匿名内部类在编译的时候由系统自动起名为Outter$1.class。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。
匿名内部类由于没有名字,所以它的创建方式有点儿奇怪。创建格式如下:
new 父类构造器(参数列表)||实现接口()
{
//匿名内部类的类体部分
}
一个实现抽象类的例子
abstract class Person {
public abstract void eat();
}
public class Demo {
public static void main(String[] args) {
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}
注意:
- 匿名内部类不能有构造方法。
- 匿名内部类不能定义任何静态成员、方法和类。
- 匿名内部类不能是public,protected,private,static。
- 只能创建匿名内部类的一个实例。
- 一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
- 因匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效。
- 匿名类和内部类中的中的this:有时候,我们会用到一些内部类和匿名类。当在匿名类中用this时,这个this则指的是匿名类或内部类本身。这时如果我们要使用外部类的方法和变量的话,则应该加上外部类的类名。
静态内部类
静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。
- 静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似
- 它不能使用外部类的非static成员变量或者方法
public class Test {
public static void main(String[] args) {
Outter.Inner inner = new Outter.Inner();
}
}
class Outter {
public Outter() {
}
static class Inner {
public Inner() {
}
}
}
上面说过:
非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有, 因而静态内部类不依赖于外部类
为什么在Java中需要内部类?
- 每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整,
- 方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。
- 方便编写事件驱动程序
- 方便编写线程代码
- 防止类名重复的冲突
在《Think in java》中有这样一句话:使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。
内部类还能够为我们带来如下特性(摘自《Think in java》):
- 内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立。
- 在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类。
- 创建内部类对象的时刻并不依赖于外围类对象的创建。
- 内部类并没有令人迷惑的“is-a”关系,他就是一个独立的实体。
- 内部类提供了更好的封装,除了该外围类,其他类都不能访问。
创建静态内部类对象的一般形式为: 外部类类名.内部类类名 xxx = new 外部类类名.内部类类名()
创建成员内部类对象的一般形式为: 外部类类名.内部类类名 xxx = 外部类对象名.new 内部类类名()
更深层次的原因请参考以下博主(本文的参考源)