Java内部类

来源:互联网 发布:java并发实战 epub 编辑:程序博客网 时间:2024/06/01 21:14

Java内部类相比较其它类而言其实也算是一种比较奇特的类,一般的类都是各是各的,分开定义,分别鲜明,但内部类偏偏是在其它类内部。我们看内部类的定义很简单,直接在一个类内部定义就可以了,定义方式也和其它类差不多,然后创建它的对象就可以使用了。但你有没有想过为什么要出现“内部类”这个概念呢?仔细想一下为什么呢,会发现这样定义有一般类达不到的效果,比如:要想让一个类A只能被另一类B使用,那你怎么定义这个类A呢?类的修饰符只有public、protected,看来一般方法是定义不了了(利用子类继承的想法肯定也是不行的)。所以就出现了内部类的概念,在一个类里面定义另一个类即内部类,内部类可以作为这个外部类的成员,并用private修饰,则这个内部类就只可以被该外部类访问了,它既是一个类,同时也可作为外部类的成员,就也可以访问外部类的私有方法和属性。当然这只是内部类的一种用途之一。


内部类既可以作为外部类的一个成员(即成员内部类,像上面说的),也可以作为方法内部的一个类,另外也可以作为一个匿名内部类。内部类又可以分为非静态内部类和静态内部类。


先说非静态内部类。

第一种,成员内部类。则它当然可以使用修饰普通类的修饰符,public、protected、private,当使用private修饰符时,只能被其直接外部类访问,但注意它的外部类(即直接外部类)对象必须通过内部类的实例对象来访问其属性和方法,包括访问内部类的私有属性和方法(重点疑点:这一点跟其它类不一样,其它类的私有属性别的类无论如何是不能访问的,但内部类的私有属性和公有方法外部类通过该内部类的实例就可以访问)(内部类实例对象中有一个指向其外部类对象的指针)。由于是非静态的,所以在静态的main方法中不能创建非静态内部类的对象!!同时外部类的静态方法也不能访问非静态内部类(静态的不能访问非静态的,非静态的也不能访问静态的,这条规则永远有用)。

另一条重要的规则:Java中不能在非静态内部类中定义静态成员。

关于这个问题我自己仔细思考了一下,找不到Java这样定义的原因(李刚老师书上也没有详细解释这一块,可能李刚老师觉得初学者还不太适合深究这一块的吧)。然后我在网上查了一下,其中在csdn中有个帖子http://bbs.csdn.net/topics/90510249对这个原因做了详细的解释,但我看的还不太懂,也不太认同shine333网友的分析(我自己现在水平有限,高手再解释一下?)。我觉得Java这样规定的原因可能设计到更深层的原因机制了吧,我搜索一些资料以后,整理出来的大致原因有以下两点:

第一呢就是:非静态内部类中如果有静态成员,则这个静态成员不必依靠具体对象就能产生,但是内部类又必须是先产

生其外部类对象,然后它才能去初始化它的各种(包括静态或非静态的)变量的。即内部类的初始化必须依靠外部

类对象,但如果内部类中有静态成员的话,则该成员就不必依靠任何对象了。

第二点:静态变量的作用就是让该类的所有对象共享一个状态,这个类的所有对象都可以获取和修改这个状态。假如

仅仅是这个目的,就可以推出这个状态也是所有外部对象所共享的状态,因此这个定义就可以提升至外围类中定义,

没有必要在内部类中定义,因此在JAVA中不答应在内部类中声明静态变量,但是可以答应其继续父类的静态变量,

由于父类可能有很多子类,这些子类不一定是用作内部类。


静态内部类:静态内部类就是和外部类相关的了,而不属于具体外部类对象。静态内部类可以包含静态的和非静态的属性和方法。静态内部类仍然不可以访问外部类的非静态成员。而外部类仍然不可以直接访问内部类的成员,外部类要么用静态内部类的“静态内部类类名.静态成员”的方式,或者“静态内部类对象.非静态成员”。


另外Java还允许在接口里定义内部类,接口里的内部类默认使用public static修饰符,如:

public interface Test{
class A{
public int a=1;

使用内部类的情况大致分类:

1,在外部类内部使用内部类。这是最常见的一种使用方式,就不用再说了。

2,在外部类以外使用非静态内部类。则内部类就不能使用private修饰符修饰了。在外部类以外定义(是定义)内部类的格式(包括使用静态和非静态内部类):outclass.innerclass name,如果有包名还要加上包名。因为内部类依赖于其外部类对象,所以创建内部类之前必须先创建其外部类对象:new outobject().new innerobject(),如:

class Out
{ class In
   { public In(String msg)
      { System.out.println(msg);
       }
    }
}
public class CreateInnerInstance
{  public static void main(String args[])
   { Out.In in=new Out().new In("就是这种方式");
     }
}
另外一点是在外部类之外创建内部类的子类,则非静态内部类的构造器必须经过其外部类的对象来调用,如:

public class SubClass extends Out.In
{ public SubClass(Out out)//在此处非静态内部类的子类的构造器的参数,传入的是非静态内部类的外部类的引用
   { out.super("hello");     //因为它的构造器中要用super调用它父类(即非静态内部类)的构造器,而它父类(非静态
    }                              //内部类)的构造器必须经过外部类对象来调用,所以才在此处传来了外部类的引用!!

}                                  //当然这个构造器也可以完全自己实现,不使用父类的构造器。。(是不是?)
需要注意:非静态内部类In对象和和其子类SubClass对象都保留有一个指向外部类Out对象的引用,但有一点区别,非静态内部类对象的创建必须借助外部类对象,当创建非静态内部类的子类SubClass时,Out对象只是用来调用SubClass的父类In的构造器。

PS:非静态内部类的子类不一定是内部类,也就是说它独立了,可以是任何类,如顶层类,但它一定保留有一个指向外部类的引用,也就是说当一个非静态内部类存在时,一定也有一个与之对应的外部类存在。


3,在外部类以外使用静态内部类。

静态内部类就当然是跟外部类关联的了,所以创建静态内部类就不需要外部类对象。

格式:new Outclass(外部类名).InnerConstructor内部类构造器()

如下面的例子(李刚老师书上的,很精简的说明了问题):

class staticOut
{ static class staticIn
    { public staticIn()
       { System.out.println("静态内部类构造器");
        }
      }
}
public class CreateStaticInnerInstance
{ public static void main(String args[])
   { staticOut.staticIn in=new staticOut.staticIn();//注意这一行跟静态内部类创建时的区别
    }
}

局部内部类

局部内部类就是在方法中定义的类,作为方法的一个局部成员,因为方法的成员只在该方法里有效,所以那很显然,方法中的局部内部类不能使用任何修饰符,而且也不能使用static。(因为不同方法中可以有同名局部内部类,所以编译后内部类class文件会带个数字(1、2、3……)用以区分)


下面又是一个重点难点:匿名内部类。如果说内部类相对于一般类来说是比较奇特的,那么匿名内部类在内部类里面又是比较奇特的一种类了。匿名内部类没有类名,所以匿名内部类里不能显式定义构造器(但它有个隐式的),创建它的语法也比较奇怪,匿名内部类适合那种只使用一次的地方,比如作为某个方法的参数,匿名内部类不能重复使用。匿名内部类不能是抽象类。

匿名内部类的创建格式1:new 父类构造器(实参列表){ 匿名内部类类体 }//必须是实参列表,怎会能是形参呢?

                        格式2:new 实现接口()//接口中不能有参数传入

由上面可以看到,匿名内部类必须继承一个父类或实现一个接口!!

先看第一种利用继承父类创建匿名内部类格式:

abstract class Device
{ public String name;//这里那么属性之前是private的,但这样则这个抽象类的setName方法就无法被其子类重写用以

访问父类私有属性了
  public abstract double getPrice();
  public abstract String getName();
  public abstract void setName(String name);
  public Device(){};//public Device{};这种写法是不对的。。
  public Device(String name)
   { this.name=name;
     }
}
public class AnonymousInner
{ public void test(Device d)//需要一个实现了Device抽象类的子类对象作为参数
    {System.out.println("购买了一个"+d.getName()+"价值"+d.getPrice());
     d.setName("我在淘宝上买了4节7号充电电池");
     System.out.println("购买了一个"+d.getName()+"价值"+d.getPrice());
      }
  public static void main(String args[])
   { AnonymousInner ta=new AnonymousInner();
     ta.test(new Device()
              { public double getPrice()
                  { return 28; }
                public String getName()
                  { return "7号充电电池"; }
                public void setName(String name) {};//抽象类中setName有个String参数,所以子类中这里参数不能为空
               });
     ta.test(new Device()
               { public double getPrice()
                   { return 28; }
                 public void setName(String name)//这里不能只有一个name,还需要一个参数类型String
                   {this.name=name;}
                 //setname("我在淘宝上买了4节7号充电电池");怎么能在类里面传递参数呢???这句话的正确位置应该

                 //放到上面test方法中!
                 public String getName()
                   { return name;  }
                 });
     }
}
/**这个程序是有一些问题的,输出如下(你可以思考为什么会这样):
购买了一个7号充电电池价值28.0
购买了一个7号充电电池价值28.0
购买了一个null价值28.0
购买了一个我在淘宝上买了4节7号充电电池价值28.0
*/

第二种:创建实现某接口的内部类:

interface Product
{ public double getPrice();
  public String getName();
}
public class TestAnonymous
{  public void test(Product p)//接口属性作为这个方法的参数,需传来一个实现了该接口的对象
  { System.out.println("购买了一个"+p.getName()+",价值"+p.getPrice());
   }
public static void main(String args[])
 { TestAnonymous ta=new TestAnonymous();
   ta.test(new Product()
           { public double getPrice()
               { return 35; }
             public String getName()
               { return new String("7号充电电池"); }
            });
 }
}

但是我感觉最”邪门“的是哪吧,就是匿名内部类在枚举类中的使用,因为枚举类不可以显式的创建其对象,所以它的匿名 

内部类看起来也稍显特别,但其实原理都是一样的。比如实现了某个接口的枚举类:

interface GenderDesc //接口
{ void info();
 }
public enum Genderinterface implements GenderDesc
{ MALE("男")                             //枚举类的每个实例可以选择自己实现接口的方式,而对于普通类的实例用的都是
  { public void info()                   //同样的接口实现方式。这一点也是枚举类比较特别的地方。
     { System.out.println("男滴。。"); //MALE("男")这就是一个构造器调用,相当于普通类的“new 构造器(参数)”,所
       }                                           //以它后面也是直接一对大括号,里面是匿名内部类类体
    },
   FEMALE("女")
   { public void info()
      { System.out.println("女滴。。");
        }
    };
 
  private final String name;
  private Genderinterface(String name)  //枚举类的私有构造器
   { this.name=name;
    }
  private String getName()
   { return this.name;
    }
  public static void main(String args[])
   { Genderinterface s=Genderinterface.MALE;
     s.info();
    }
}

Java内部类还有一个用处就是作为闭包和回调。闭包可能是其它语言提供的一种功能,而Java没提供这个功能,但利

用内部类可以实现这个功能。

这个功能其实就是说当遇到这种情况时需要:当一个类既要实现某一个接口,又继承某一个父类,而这个接口中的抽

象方法和父类中的方法同名,则如果你要在该类中实现接口中的方法,则必然会覆盖掉父类中的同名方法,那怎么办呢?

怎么办呢?怎么办呢?。。。父类中的方法名和接口中的方法名你肯定不可以再去更改了,因为这些父类和接口很多时

候都并不是只有你一个人在用的。所以这个时候就用到内部类了。

比如有一个接口Basketball,里面有一个sport方法;另外有一个父类Football,里面也有一个sport方法,而类Mysport既继承了这个父类,又需要实现这个接口,则可以这样编写代码:

interface Basketball
{ void sport();
}
class Football
{ protected String name;
  public Football(){};
  public Football(String name)
   { this.name=name;
    }
  public void sport()
    { System.out.println("我叫"+this.name+",我的业余爱好之一是:踢足球。");
     }
}
class Mysport extends Football
{  
  public Mysport() {};//这个子类继承了父类,它可以继承父类的属性和方法,但它还是要有自己的构造器的,必须的!!
  public Mysport(String name)
  { super(name);
    }
  public void basket()
    { System.out.println("我叫"+this.name+",我的业余爱好之一是:打篮球。");
     }
  class inner implements Basketball
   { public void sport()
      { basket();
        }
     }
  public Basketball backinnerclass()
   { return new inner();
    }
}
public class Sportclass
{  
  public static void main(String args[]) //不要老是忘写主函数
 {
  Mysport s=new Mysport("周兴宾");
  s.sport();
  s.backinnerclass().sport();//刚开始调用backinnerclass方法时时少写了个括号
  }
}

原创粉丝点击