可将一个类定义置入另一个类定义中。这就叫作“内部类”。内部类对我们非常有用,因为利用它可对那些逻辑上相互联系的类进行分组,并可控制一个类在另一个类里的“可见性”。
为何要使用内部类,有以下比较基本的原因:
(1) 我们准备实现某种形式的接口,使自己能创建和返回一个句柄。
(2) 要解决一个复杂的问题,并希望创建一个类,用来辅助自己的程序方案。同时不愿意把它公开。(通过内部类隐藏实施细节)
基本注意点
1.普通类(指的是非内部类)不可设为private或 protected——只允许 public或者“友好的”,而内部类可以,这么做可将具体的实施细节完全隐藏起来。
2.对于局部内部类和匿名类(尤其是if语句内的局部内部类),并不意味着内部类是有条件创建的——它会随同其他所有东西得到编译。然而,在定义它的那个作用域之外,它是不可使用的。
3.对于局部内部类和匿名类,和局部变量一样,不能被public,protected,private和static修饰。局部内部类(含匿名类)只能访问局部方法中定义的final类型的局部变量。(注意:这里说的是所处方法作用域内的局部变量,如果是外部类中的局部变量,可直接使用)
4.非static内部类不能定义static变量,除非是final类型。
5.内部类拥有对封装类所有元素的访问权限。(当然,如果内部类是static的,必须变量也是static的。究其原因,静态内部类没有指向外部的引用)
6.一般来说,接口中不能有具体实现。但内部类可以作为接口的一部分。(通常是static内部类,否则可能没有实际意义。)
Java中的内部类共分为四种:
成员内部类member inner class
局部内部类local inner class
匿名内部类anonymous inner class
静态内部类static inner class (also called nested class)
成员内部类
成员内部类基本用法
abstract class Contents {
abstract public int value();
}
interface Destination {
String readLabel();
}
class Parcel3 {
private class PContents extends Contents {
private int i = 11;
public int value() {
return i;
}
}
protected class PDestination implements Destination {
private String label;
private PDestination(String whereTo) {
label = whereTo;
}
public String readLabel() {
return label;
}
}
public Destination dest(String s) {
return new PDestination(s);
}
public Contents cont() {
return new PContents();
}
}
public static void Test2(){
Parcel3 p = new Parcel3();
Contents c = p.cont();
Destination d = p.dest("Tanzania");
// Illegal -- can't access private class:
//! Parcel3.PContents c = p.new PContents();
}
局部内部类
在一个方法内定义内部类
class Parcel4 {
public Destination dest(String s) {
class PDestination implements Destination {
private String label;
private PDestination(String whereTo) {
label = whereTo;
}
public String readLabel() {
return label;
}
}
return new PDestination(s);
}
public static void main(String[] args) {
Parcel4 p = new Parcel4();
Destination d = p.dest("Tanzania");
}
}
在任意作用域内定义内部类
class Parcel5 {
private void internalTracking(boolean b) {
if(b) {
class TrackingSlip {
private String id;
TrackingSlip(String s) {
id = s;
}
String getSlip() {
return id;
}
}
TrackingSlip ts = new TrackingSlip("slip");
String s = ts.getSlip();
}
// TrackingSlip ts = new TrackingSlip("x");//不可访问,超出作用域
}
public void track() {
internalTracking(true);
}
public static void main(String[] args) {
Parcel5 p = new Parcel5();
p.track();
}
}
匿名内部类
匿名内部类就是没有名字的局部内部类,不使用关键字class, extends, implements, 没有构造方法。匿名内部类隐式地继承了一个父类或者实现了一个接口。也就是说,匿名类可以继承一个基础类,也可以实现一个接口,但不需要使用关键字,而是直接覆盖父类。但匿名内部类不能同时实现一个接口和继承一个类,也不能实现多个接口。如果匿名类实现了一个接口,该类是Object类的直接子类。
匿名内部类基本用法
class Wrapping {
private int i;
public Wrapping(int x) {
i = x;
}
public int value() {
return i;
}
}
interface Wrapping2 {
public int value();
}
//匿名类
class Parcel7 {
public Wrapping wrap(int x) {
// Base constructor call:
return new Wrapping(x){
public int value() {
return super.value() * 47;
}
}; // Semicolon required
}
public Wrapping2 wrap2() {//既可以是一个基础类,也可以是一个接口
return new Wrapping2(){
public int value() {
return 0;
}
};
}
public static void main(String[] args) {
Parcel7 p = new Parcel7();
Wrapping w = p.wrap(10);
Wrapping2 w2=p.wrap2();
}
}
匿名内部类不可以有构造方法,但还是可以对变量进行初始化的。
匿名类变量初始化
class Parcel8 {
// Argument must be final to use inside
// anonymous inner class:
public Destination dest(final String dest) {
return new Destination() {
private String label = dest;
public String readLabel() {
return label;
}
};
}
public static void main(String[] args) {
Parcel8 p = new Parcel8();
Destination d = p.dest("Tanzania");
System.out.println(d.readLabel());
}
}
利用构造代码块初始化
class Parcel9 {
public Destination dest(final String dest) {
return new Destination() {
{
System.out.println("代码块初始化!");
}
private String label = dest;
public String readLabel() {
return label;
}
};
}
public static void main(String[] args) {
Parcel9 p = new Parcel9();
Destination d = p.dest("Tanzania");
}
}
但构造代码块毕竟不是构造器,不能进行过载。
静态内部类
static内部类并不是说一个内部类是static 的。static内部类意味着:
(1) 创建一个 static内部类的对象,我们不需要一个外部类对象。
(2) 不能从 static内部类的一个对象中访问一个外部类对象。(除非该对象是static的)
静态内部类基本用法
class Parcel10 {
//public int a=0;
public static int a=0;
private static class PContents extends Contents {
private int i = 11;
public int value() { return i; }
}
protected static class PDestination implements Destination {
private String label;
private PDestination(String whereTo) {
label = whereTo;
}
public String readLabel() {
return label;
}
}
public static Destination dest(String s) {
return new PDestination(s);
}
public static Contents cont() {
return new PContents();
}
public static Parcel101 getP(){
return new Parcel101();
}
public static int getA(){
return a; //a也必须是static
}
public static void main(String[] args) {
Contents c = cont();
Destination d = dest("Tanzania");//这里没有显示调用static内部类,但方法是static,所以内部类必须是static的
Parcel101 p= getP();//外部类没有static一说,可直接使用
int b=getA();//变量同理,由于方法是static,因此变量必须是static
}
}
接口中的静态内部类
以下为接口中内部类的一种用法,利用内部类编写公用测试方法。
class QuestionMain implements ITest{
public static void main(String[] args) {
ITest.TestInternal.test(new QuestionMain());
}
public void print() {
System.out.println("QuestionMain.print");
}
}
//接口中的内部类
interface ITest {
void print();
public static class TestInternal {
public static void test(ITest test) {
test.print();
}
}
}
除此之外,内部类还有一些其它特性:
链接到外部类
interface Selector {
boolean end();
Object current();
void next();
}
class Sequence {
private Object[] o;
private int next = 0;
public Sequence(int size) {
o = new Object[size];
}
public void add(Object x) {
if(next < o.length) {
o[next] = x;
next++;
}
}
private class SSelector implements Selector {
int i = 0;
public boolean end() {
return i == o.length;
}
public Object current() {
return o[i];
}
public void next() {
if(i < o.length) i++;
}
}
public Selector getSelector() {
return new SSelector();
}
public static void main(String[] args) {
Sequence s = new Sequence(10);
for(int i = 0; i < 10; i++)
s.add(Integer.toString(i));
Selector sl = s.getSelector();
while(!sl.end()) {
System.out.println((String)sl.current());
sl.next();
}
}
}
引用外部类对象
class Parcel11 {
int a=1;
int b=1;
class Contents {
private int i = 11;
public int value() {
return i;
}
}
class Destination {
int a=2;
private String label;
Destination(String whereTo) {
label = whereTo;
}
String readLabel() {
return label;
}
void getValue(){
System.out.println(this.a);//this指向的是内部类
//System.out.println(this.b); //内部类中无此值
}
}
public static void main(String[] args) {
Parcel11 p = new Parcel11();
Parcel11.Contents c = p.new Contents();//非static的内部类必须通过外部类引用
Parcel11.Destination d =p.new Destination("Tanzania");//不能绕过外部类直接为非static的开辟空间,所以采用这种写法
d.getValue();
//Parcel11.Contents c2 = new Parcel11.Contents(); //报错
}
}
内部类继承
内部类继承比较特殊,子类必须有一个构造方法,且把外部类的一个对象作为参数。只有这样,才能保证内部类正常创建。
class WithInner {
class Inner {}
}
class InheritInner extends WithInner.Inner {
//! InheritInner() {} // Won't compile
InheritInner(WithInner wi) {
wi.super();
}
public static void main(String[] args) {
WithInner wi = new WithInner();
InheritInner ii = new InheritInner(wi);
}
<strong>}</strong>
外部类被继承,内部类是否被覆盖
例子如下,被继承的只是外部类,子类中有一个和内部类同名的内部类,但这两个内部类并不存在继承关系
class Egg {
protected class Yolk {
public Yolk() {
System.out.println("Egg.Yolk()");
}
}
private Yolk y;
public Egg() {
System.out.println("New Egg()");
y = new Yolk();
}
}
class BigEgg extends Egg {
public class Yolk {
public Yolk() {
System.out.println("BigEgg.Yolk()");
}
}
public static void main(String[] args) {
new BigEgg();
}
}
输出如下,Yolk并没有被继承
New Egg()
Egg.Yolk()
如果希望内部类也被继承,需要改动程序(这里是内部类继承内部类,同时外部类继承外部类,注意与上文的外部类继承内部类区分开来。)
class Egg2 {
protected class Yolk {
public Yolk() {
System.out.println("Egg2.Yolk()");
}
public void f() {
System.out.println("Egg2.Yolk.f()");
}
}
private Yolk y = new Yolk();
public Egg2() {
System.out.println("New Egg2()");
}
public void insertYolk(Yolk yy) {
y = yy;
}
public void g() {
y.f();
}
}
class BigEgg2 extends Egg2 {
public class Yolk extends Egg2.Yolk {
public Yolk() {
System.out.println("BigEgg2.Yolk()");
}
public void f() {
System.out.println("BigEgg2.Yolk.f()");
}
}
public BigEgg2() {
insertYolk(new Yolk());
}
public static void main(String[] args) {
Egg2 e2 = new BigEgg2();
e2.g();
}
}
输出如下,Yolk方法被覆盖
Egg2.Yolk()
New Egg2()
Egg2.Yolk()
BigEgg2.Yolk()
BigEgg2.Yolk.f()
利用内部类控制框架:
内部类可以做这些事情:
(1) 在单独一个类里表达一个控制框架应用的全部实施细节,从而完整地封装与那个实施有关的所有东西。内部类用于表达多种不同类型的action(),它们用于解决实际的问题。除此以外,后续的例子使用了private内部类,所以实施细节会完全隐藏起来,可以安全地修改。
(2) 内部类使我们具体的实施变得更加巧妙,因为能方便地访问外部类的任何成员。若不具备这种能力,代码看起来就可能没那么使人舒服,最后不得不寻找其他方法解决。
例子:
abstract class Event {
private long evtTime;
public Event(long eventTime) {
evtTime = eventTime;
}
public boolean ready() {
return System.currentTimeMillis() >= evtTime;
}
abstract public void action();
abstract public String description();
}
class EventSet {
private Event[] events = new Event[100];
private int index = 0;
private int next = 0;
public void add(Event e) {
if(index >= events.length)
return; // (In real life, throw exception)
events[index++] = e;
}
public Event getNext() {
boolean looped = false;
int start = next;
do {
next = (next + 1) % events.length;
// See if it has looped to the beginning:
if(start == next) looped = true;
// If it loops past start, the list
// is empty:
if((next == (start + 1) % events.length)&& looped)
return null;
} while(events[next] == null);
return events[next];
}
public void removeCurrent() {
events[next] = null;
}
}
class Controller {
private EventSet es = new EventSet();
public void addEvent(Event c) {
es.add(c);
}
public void run() {
Event e;
while((e = es.getNext()) != null) {
if(e.ready()) {
e.action();
System.out.println(e.description());
es.removeCurrent();
}
}
}
}
class GreenhouseControls extends Controller {
private boolean light = false;
private boolean water = false;
private String thermostat = "Day";
private class LightOn extends Event {
LightOn(long eventTime) {
super(eventTime);
}
public void action() {
// Put hardware control code here to
// physically turn on the light.
light = true;
}
public String description() {
return "Light is on";
}
}
private class LightOff extends Event {
public LightOff(long eventTime) {
super(eventTime);
}
public void action() {
// Put hardware control code here to
// physically turn off the light.
light = false;
}
public String description() {
return "Light is off";
}
}
private class WaterOn extends Event {
public WaterOn(long eventTime) {
super(eventTime);
}
public void action() {
// Put hardware control code here
water = true;
}
public String description() {
return "Greenhouse water is on";
}
}
private class WaterOff extends Event {
public WaterOff(long eventTime) {
super(eventTime);
}
public void action() {
// Put hardware control code here
water = false;
}
public String description() {
return "Greenhouse water is off";
}
}
private class ThermostatNight extends Event {
public ThermostatNight(long eventTime) {
super(eventTime);
}
public void action() {
// Put hardware control code here
thermostat = "Night";
}
public String description() {
return "Thermostat on night setting";
}
}
private class ThermostatDay extends Event {
public ThermostatDay(long eventTime) {
super(eventTime);
}
public void action() {
// Put hardware control code here
thermostat = "Day";
}
public String description() {
return "Thermostat on day setting";
}
}
// An example of an action() that inserts a
// new one of itself into the event list:
private int rings;
private class Bell extends Event {
public Bell(long eventTime) {
super(eventTime);
}
public void action() {
// Ring bell every 2 seconds, rings times:
System.out.println("Bing!");
if(--rings > 0)
addEvent(new Bell(System.currentTimeMillis() + 2000));
}
public String description() {
return "Ring bell";
}
}
private class Restart extends Event {
public Restart(long eventTime) {
super(eventTime);
}
public void action() {
long tm = System.currentTimeMillis();
// Instead of hard-wiring, you could parse
// configuration information from a text
// file here:
rings = 5;
addEvent(new ThermostatNight(tm));
addEvent(new LightOn(tm + 1000));
addEvent(new LightOff(tm + 2000));
addEvent(new WaterOn(tm + 3000));
addEvent(new WaterOff(tm + 8000));
addEvent(new Bell(tm + 9000));
addEvent(new ThermostatDay(tm + 10000));
// Can even add a Restart object!
addEvent(new Restart(tm + 20000));
}
public String description() {
return "Restarting system";
}
}
public static void main(String[] args) {
GreenhouseControls gc =new GreenhouseControls();
long tm = System.currentTimeMillis();
gc.addEvent(gc.new Restart(tm));
gc.run();
}
}