Java内部类那些事
内部类这种东西因为平时工作时很少用到,加上本人记忆力不好,所以现在造成了一种这样的状况,很多细节东西用的时候翻书找着了,看一下明白了,接着用上。不到一星期之后马上又忘了。最近做东西用到一个复杂的数据结构又需要使用内部类来解决,写到这个地方的时候很多细节又想不起来了,无奈之下把一天没动的屁股挪开去翻书~对于我这样的懒人来说这是件很痛苦的事,我决定这次我要把内部类的知识好好总结一下,不保证从这以后永远不忘,但是起码到时候我不用再麻烦的去找书,读一下这篇小文章就行了。
一、关于Java内部类
Java中的内部类是外部类的一部分,是外部类一个完整的正式的成员,内部类的实例可以访问外部类的所有成员,包括哪些标识为private的成员!(权利很大!)
二、常规内部类:
1.编写常规内部类:
所谓的常规内部类是那些以这种形式来定义的类:
public class Outer{
class Inner{
}
}
其中Inner就是一个常规的内部类,有常规的就又不常规的,不常规的像是静态的(使用static关键字修饰的)、局部方法的、匿名的,不急,稍后介绍
如果我们将上面的Outer类进行编译:
java Outer.java
那么就会得到两个类文件,其中一个叫Outer.class,另一个名字比较怪,叫Outer$Inner.class 这是内部类的Java字节码文件
虽然我们看到内部类最终会像外部类那样生成一个独立的class文件,但是它还是有限制的:内部类中不可以包含任何类型的static声明!切记这一点!如果要访问内部类我们只能通过一个外部类的活实例!
2.实例化内部类:
要实例化一个内部类分为两种情况:1.在外部类实例化内部类 2.在外部类之外实例化内部类
第一种情况:
这个很简单,没什么可讨论的,像平时完全一样:
class Outer{
class Inner{
public Inner(){
System.out.println("Inner Alive!");
}
}
public void createInner(){
Inner inner=new Inner();
}
}
OK!这个没有什么好说的!
第二种情况有点儿学问:
前面我们说过,如果要在外部类之外访问内部类,那么就需要通过一个外部类的实例,这样就会使代码看起来似乎有些怪:
public static void main(String[] args){
Outer outer=new Outer(); //创建一个外部类的对象
Outer.Inner inner=outer.new Inner(); //创建内部类的实例inner,很怪?马上解释!这也是我最最记不住的一点儿
}
首先看引用inner的类型是Outer.Inner说明这是一个Outer内部类Inner的一个实例,当然,同往常一样,你这样写也是没问题的:Object inner,只是在类型转换的还是要使用Outer.Inner,如:
Object o=outer.new Inner();
if(o instanceof Outer.Inner){
o=(Outer.Inner)o;
}
然而最怪的还是它:outer.new Inner(); 我们是通过外部类对象outer来像调用其它方法一样来调用Inner类的构造函数。如果要直接生成一个内部类实例,可以这样去写代码:
Outer.Inner inner=new Outer().new Inner();
大多数情况下第二种情况用的不是很多,稍微知道有这么回事就可以了,以后如果真的用到了,那么再回来读这篇文章吧
3.this到底指的是谁?
这个问题我们就会经常碰到了!如果我们在内部类中使用this,这个this指的是谁?
首先我们回忆一下this关键字的规则:
(1)关键字this只能在实例代码内部使用,实力代码就是除了静态代码(包括静态方法和静态初始化块)之外的地方。
(2)this引用是对当前正在执行的对象的引用
根据第二条,内部类中的this会引用该内部类的实例,这是我们所需要的,符合我们的习惯。但是有很多时候你需要去在内部类中引用外部类的当前实例,这种情况我们该怎么办?Java当然为这种情况也提供了解决方案,可以这样写:Outer.this
public class Outer {
private int x;
public Outer(int xIn){
x=xIn;
}
class Inner{
private int x;
public Inner(int xIn){
x=xIn;
}
public void showX(){
System.out.println("The x of inner:"+this.x);
System.out.println("The x of outer:"+Outer.this.x);
}
}
}
public class Test {
public static void main(String[] args) {
Outer out=new Outer(10);
Outer.Inner in=out.new Inner(7);
in.showX();
}
}
程序输出结果:
The x of inner:7
The x of outer:10
三、方法内部类:
常规的就这些值得注意的问题,下面介绍一些"非主流"的类:
当年学习JavaScript的时候看到在函数里面可以定义函数感到非常惊奇!然而在Java中我们可以在方法中定义类!作为一名Java程序员,我们有必要了解它这个奇特的性质,这是一个方法内部类的例子:
public class Outer {
private int x=7;
public void doNothing(){
//Note here!这就是传说中的在方法中声明的类!
class Inner{
private int x=100;
public void showX(){
System.out.println("The x of inner:"+this.x);
System.out.println("The x of outer:"+Outer.this.x);
}
}
Inner in=new Inner();
in.showX();
}
}
public class Test {
public static void main(String[] args){
Outer out=new Outer();
out.doNothing();
}
}
程序执行的结果:
The x of inner:100
The x of outer:7
使用方法内部类所需要注意的问题:
1.只能够在方法内部中声明这个类的实例,而且声明实例的地方必须位于内部类定义的后面!否则编译器不会发现这个内部类!就会产生编译错误!
2.方法内部类可以访问外部类的所有成员,最值得注意的是:内部类对象虽然是在方法内部,但是它坚决不可以访问它所在方法的局部变量!除非该变量类型为final
3.静态方法中也可以声明这种方法内部类!但是该内部类只能访问外部类的静态成员!
4.方法内部类也会产生类文件!文件名同常规内部类!
对于第二条原则,我在这里再做一下详细的解释:
因为所有的方法局部变量都位于栈上,随着方法调用完毕,这些局部变量也会随之丢失!而类,包括所有的内部类,其信息是位于堆上的!因此我们无法保证内部类的生存周期同方法局部变量的生存周期相同,因此便有了这条规定!而对于final值确是永不变的!因此我们可以在内部类中使用方法的final变量!
四、匿名内部类
谈到匿名内部类,我不由得想起了匿名数组,其实两者是基本上相似的,这里顺便也说一下匿名数组
匿名数组是Java中构建数组的快捷方式,其基本形式如下所示:
int[] array=new int[]{1,2,3,4,5,6,7,8,9};
这样我们就可以随时方便的构造数组,最常用的地方就是把它用作带数组参数的方法变元:
setArray(new int[]{1,2,3,4,5,6});
值得注意的有一点:构建匿名数组时我们不需要为它指定大小,例如这种写法new int[3]{1,2,3};就会造成编译错误!
好了,知道了匿名数组,我们来看匿名内部类,假设有一个抽象类:
abstract class Animal(){
public abstract void run();
}
Animal是一个抽象类,它拥有一个抽象的方法run(),假设我们要为它“制造”一个子类的对象dog,按照常规的方法,我们需要写一个Dog类来继承Animal类,然后重写具体的run()方法,但是匿名内部类却为我们提供了另一种解决方案:
public Animal createDog(){
Animal dog=new Animal(){
public void run(){
System.out.println("The dog runs very fast!");
}
}; //不要漏掉分号!
return dog;
}
这样就返回了一个对象dog,它具体的实现了方法run(),但是这样做有一个局限性,如果我们要为该对象实现一种Animal没有的行为play(),虽然能够在匿名内部类中定义该方法,但是我们永远无法去调用它!因为返回的类型永远是Animal,而Dog类型是不存在的!因此,这种情况下我们还是要采用第一种解决方案!
另外,不仅仅是抽象类,非final的类(因为这样做相当于继承,而final是不允许继承的)还有接口都可以生成相应的匿名内部类!
五、静态内部类:
其实就是标识为static的内部类:
public class Outer {
int x=10;
static int s=100;
static class SInner{
public void doNoting(){
System.out.println(s); //只能访问静态字段s,如果访问x就会出现编译错误!
}
}
}
public class Test {
public static void main(String[] args){
Outer.SInner inner=new Outer.SInner(); //创建static内部类的实例,跟创建普通外部类的方法几乎一样,对吗?
inner.doNoting();
}
}
OK!Java内部类的情况基本上就这么多吧!嗯!现在记得很扎实!写几个小例子练练~然后开始别的工作!~~哈哈