一、java内部类分类
1、成员内部类
成员内部类是最普通的内部类,定义位于另一个类内部。
public class Outter { private int i =0; class Inner{ public void print(){ System.out.println(i); } } public void out(){ new Inner().print(); }
public Inner getInner(){
return new Inner();
}
}
成员内部类可以无条件访问外部类的所有成员和方法(包括private成员和方法)。当内部类和外部类有同名变量或方法时,默认情况下是访问内部类的成员。若是要访问外部类的同名变量则要 类名.this.方法/变量。
而外部类若要访问内部类的成员,则需要创建内部类对象。
public class test { public static void main(String[] args) {
//第一种创建方式 Outter outter=new Outter(); Outter.Inner inner= outter.new Inner(); inner.print();
//第二种创建方式
Outter.Inner inner1=new Outter().getInner();
}
}
成员内部类是衣服外部类而存在的,若是要创建成员内部类的对象,前提是先要存在一个外部类的对象,再通过外部类对象创建内部类对象。
同时:普通外部类类只有public 和 默认的包访问权限,而成员内部类可以有 private public protected default 四种权限,对比类的成员的访问权限。
2、局部内部类
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于,它的访问权限仅限于这个方法或作用域的内部。
public Outter getOutter(){ class Inner extends Outter{ int a=0; } return new Inner(); }
局部内部李和方法里面的局部变量一样,是不能有private 、protected、static、public 修饰词的.
3、匿名内部类
匿名内部类大概是平时用到最多的内部类了。
scan_bt.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub } });
类似这种事件监听,或是启动线程等 使用匿名内部类是很方便的。减少了大量的冗余代码。
匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。匿名内部类在编译的时候由系统自动起名为Outter$1.class。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。
4、静态内部类
静态内部类也是定义在另一个类的里面,可以比照成员内部类,比成员内部类多了一个static 的修饰,与静态成员变量类似,它也不需要依赖于外部类,并且它不能使用外部类的非static变量或者方法。因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。
静态内部类的外部引用方式:
Outter.Inner inner=new Outter.Inner(); inner.print();
二、深入理解内部类
1、为什么成员内部类可以无条件访问外部类的成员?
编译器编译时会将成员类和外部类编译为两个class文件。而成员内部类中默认添加了对外部类成员的应用,而虽然我们并没有定义构造器,但是编译器会默认的为构造器中添加对引用的外部成员进行赋值。这也解释了,为什么成员内部类为什么不能单独存在,如果没有创建外部类对象的话,内部类的赋值就没有数据来源。同时也说明了为什么外部类不能直接访问内部类成员,因为外部类的class文件中并没有引用内部类的成员,而且使用外部类时,也并不必须创建内部类的对象,所以如此。
2、匿名内部类和局部内部类为什么只能访问局部final变量,而在jdk8以后又可以访问普通局部变量了?
匿名内部类或局部内部类定义在方法体中,通过调用方法来调用内部类时,当局部方法生命周期结束,而内部类的生命周期此时不一定结束,而局部变量不能脱离方法而单独存在,所以编译器会在内部类中创建一个局部变量的拷贝,所以内部类中访问的实际上是这个拷贝,而非原本的局部变量本身,但是,若在内部类中对这个变量进行重新赋值,就会导致内部类中的变量与原本的局部变量值不相符,造成数据不一致性,这样就没有达到原本的意图了,所以编译器就限定内部类访问的局部变量只能是final变量,这样就解决了这个数据的不一致问题。
然而我今天试了一下,是可以普通变量的。这是为什么呢。
当我在内部类中首次访问变量c时,是可以访问的,而当我在内部类的调用后再加上对c的重新赋值后,内部类便不能访问这个c了。所以实际上是编译器默认内部类访问的局部变量是final类型的,而当这个局部变量会改变时,便不能访问了。又试了一下在内部类中对c重新赋值,也是无法通过编译的。那么我们可以认为,实际上jdk8与之前并无分别,只是对内部类所访问的局部变量默认的加上了final 的修饰而已。
三、内部类的使用场景和好处
1、每个内部类都可以独立的继承一个借口的实现,所以无论内部类是否已经继承了某个借口的实现,对于内部类都没有影响。内部类使得多继承的方案变的完整。
2、方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。
3、方便编写事件驱动程序。
4、方便编写线程代码。