内部类基础
1 定义
将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。
2 分类
广泛意义上的内部类一般来说包括这四种:
1)成员内部类
2)局部内部类
3)匿名内部类
4)静态内部类
四种内部类简介
1 成员内部类
1)成员内部类是最普通的内部类,它的定义为位于另一个类的内部,形如下面的形式:
public class Circle {
private double radius = 0;
public static int count =1;
public Circle(double radius) {
this.radius = radius;
}
class Draw { //内部类
public void drawSahpe() {
System.out.println(radius); //外部类的private成员
System.out.println(count); //外部类的静态成员
}
}
}
2)类Draw像是类Circle的一个成员,所以Draw类称为成员内部类,Circle类称为外部类。
3)成员内部类可以无条件访问外部类的所有成员属性和方法(包括private和static修饰的)。
4)当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问:
Circle.this.radius;
5)在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问:
public class Circle {
private double radius = 0;
public Circle(double radius) {
this.radius = radius;
getDrawInstance().drawSahpe(); //必须先创建成员内部类的对象,再进行访问
}
private Draw getDrawInstance() {
return new Draw();
}
class Draw { //内部类
public void drawSahpe() {
System.out.println(radius); //外部类的private成员
}
}
}
6)另一种创建成员内部类的语法如下:
public class Circle {
private double radius = 0;
public Circle(double radius) {
this.radius = radius;
}
class Draw { //内部类
public void drawSahpe() {
System.out.println("sheep");
}
}
public static void main(String[] args) {
Circle circle = new Circle();
Draw drow = circle.new Draw(); //必须通过circle对象来创建
}
}
2 局部内部类
1)局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
public class People{
public People() {
}
}
public class Man{
public Man()
}
public People getWoman(){
class Woman extends People{ //局部内部类
int age =0;
}
return new Woman();
}
}
2)局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。
3 匿名内部类
1)匿名内部类应该是平时我们编写代码时用得最多的,在编写事件监听的代码时使用匿名内部类不但方便,而且使代码更加容易维护。
new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
}
2)匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。匿名内部类在编译的时候由系统自动起名为Outter$1.class。
3)一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。
4 静态内部类
静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。
2)静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法,这点很好理解,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。
public class Outter {
public Outter() {
}
static class Inner {
public Inner() {
}
}
public static void main(String[] args) {
Outter.Inner inner = new Outter.Inner(); // 不需要先创建外部类的实例
}
}
深入理解内部类
1 为什么成员内部类可以无条件访问外部类的成员?
1)编译器会默认为成员内部类添加了一个指向外部类对象的引用。
2)虽然我们在定义的内部类的构造器是无参构造器,编译器还是会默认添加一个参数,该参数的类型为指向外部类对象的一个引用
3)从这里也间接说明了成员内部类是依赖于外部类的,如果没有创建外部类的对象,则无法对Outter this&0引用进行初始化赋值,也就无法创建成员内部类的对象了。
2 为什么局部内部类和匿名内部类只能访问局部final变量?
public class Test {
public static void main(String[] args) {
}
public void test(final int b) {
final int a = 10;
new Thread(){
public void run() {
System.out.println(a);
System.out.println(b);
};
}.start();
}
}
1)当test方法执行完毕之后,变量a的生命周期就结束了,而此时Thread对象的生命周期很可能还没有结束,那么在Thread的run方法中继续访问变量a就变成不可能了,但是又要实现这样的效果,怎么办呢?Java采用了 复制 的手段来解决这个问题。
2)这个过程是在编译期间由编译器默认进行,如果这个变量的值在编译期间可以确定,则编译器默认会在匿名内部类(局部内部类)的常量池中添加一个内容相等的字面量或直接将相应的字节码嵌入到执行字节码中。这样一来,匿名内部类使用的变量是另一个局部变量,只不过值和方法中局部变量的值相等,因此和方法中的局部变量完全独立开。
3)既然在run方法中访问的变量a和test方法中的变量a不是同一个变量,当在run方法中改变变量a的值的话,会出现什么情况?
4)对,会造成数据不一致性,这样就达不到原本的意图和要求。为了解决这个问题,java编译器就限定必须将变量a限制为final变量,不允许对变量a进行更改(对于引用类型的变量,是不允许指向新的对象),这样数据不一致性的问题就得以解决了。到这里,想必大家应该清楚为何 方法中的局部变量和形参都必须用final进行限定了。
内部类的使用场景和好处
1 每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整,
2 方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。
3 方便编写事件驱动程序
4 方便编写线程代码