(Thinking in Java)内部类的简单使用

1. 成员内部类

1. 最基本使用

 1 public class Demo {
 2     class Contents{
 3         private int i=11;
 4         public int value(){
 5             return i;
 6         }
 7     }
 8 
 9     class Destination{
10         private String label;
11         Destination(String whereTo){
12             label=whereTo;
13         }
14         String readLabel(){
15             return label;
16         }
17     }
18 
19     public void ship(String dest){
20         Contents c=new Contents();
21         Destination d=new Destination(dest);
22         System.out.println(d.readLabel());
23     }
24 
25     public static void main(String[] args) {
26         Demo d=new Demo();
27         d.ship("Tasmania");
28     }
29 }

2.内部类可以访问外部类的成员

内部类可以访问外部类的成员变量。如下:

 1 public class Demo {
 2     private Object[] items;
 3     private int next = 0;
 4 
 5     public Demo(int size) {
 6         items = new Object[size];
 7     }
 8 
 9     public void add(Object x) {
10         if (next < items.length) {
11             items[next++] = x;
12         }
13     }
14 
15     private class SequenceSelector implements Selector {
16         private int i = 0;
17 
18         public boolean end() {
19             return i == items.length;
20         }
21 
22         public Object current() {
23             return items[i];
24         }
25 
26         public void next() {
27             if (i < items.length) {
28                 i++;
29             }
30         }
31     }
32 
33     public Selector selector() {
34         return new SequenceSelector();
35     }
36 
37     public static void main(String[] args) {
38         Demo d = new Demo(10);
39         for (int i = 0; i < 10; i++) {
40             d.add(Integer.toString(i));
41         }
42         Selector selector = d.selector();
43         while (!selector.end()) {
44             System.out.print(selector.current() + " ");
45             selector.next();
46         }
47     }
48 }
49 
50 interface Selector {
51     boolean end();
52 
53     Object current();
54 
55     void next();
56 }

因为在创建内部类对象的时候,内部类对象会捕获一个指向外部类对象的引用。访问外部类成员的时候,就是用这个引用来获取外部类成员的。内部类中也可以取得这个外部类对象引用。举例如下:

 1 public class DotThis{
 2     void f(){
 3         System.out.println("DotThis.f()");
 4     }
 5     class Inner{
 6         public DotThis outer(){
 7             return DotThis.this;
 8             //A plain "this" would be Inner's this
 9         }
10     }
11 
12     public Inner inner(){
13         return new Inner();
14     }
15 
16     public static void main(String[] args) {
17         DotThis dt=new DotThis();
18         DotThis.Inner dti=dt.inner();
19         dti.outer().f();
20     }
21 }

当要在其他类中创建一个内部类对象的时候,可以使用.new语法。

public class DotNew{
    public class Inner{
    }
    public static void main(String[] args){
        Inner dni=new DotNew().new Inner();
    }
}

当创造内部类对象的时候,如果这个内部类不是嵌套类(静态内部类),那么就必须要通过外部类对象,才能创建这个内部类对象,为什么呢,之前说过,因为这个内部类对象要获取外部类的引用啊。 
并且在存在多个内部类的时候,多个内部类之间可以互相创建对象。例子就不举了。

小结:现在所说的都是成员内部类,其实内部类没那么复杂,既然叫做成员内部类了,他就只是类的成员罢了。他也可以带修饰符,他的修饰符和其他普通的成员变量的修饰符的意义也没有什么不同。

3. 内部类权限修饰符

当内部类被private修饰的时候,该类只能被外部类内的方法使用,其他类不能获取该内部类的引用,因为是private的,所以其他类根本不知道存在一个这样的类。当然也可以作为一个成员变量使用,但是如果作为成员变量,则其他类并不能直接创建内部类的引用,需要用其他手段获取该引用,如下:

class Outer{
    Inner in;
    private class Inner implements a_interface{
        void show(){
            System.out.println("123");
        }
    }
}
interface a_interface{
    void show();
}

class test{
    //Inner in=new Outer().in;这是错误的,因为test并不知道Outer类有一个Inner内部类,因为是私有的
    a_interface in=new Outer().in;//可以运用向上转型的方法获取private修饰的内部类。
}

小结:其实这也很好记,无论是public还是private,修饰到内部类上的时候,和他们修饰普通的成员变量(如string,int之类)的时候没什么不同,规则都一样,public就都能使用,private就类内可以用。所以规则就记住三条就好:1.先考虑外部类的权限,是否可以获取一个外部类对象。2.创建成员内部类对象的时候需要外部类对象。3.考虑内部类的权限,是否可以获取这样的一个内部类对象(或者说,在外部知不知道有这样一个内部类)。

2. 方法和作用域内的内部类

当我们需要解决一个复杂的问题,想创建一个类来辅助解决问题,但是不希望这个类是公共可用的,甚至不希望在外部类之内的其他地方可以访问到这个辅助类。我们可以运用方法内的内部类

 1 public class Outer {
 2     public InterfaceDemo get_InterfaceDemo(String s) {
 3          class InterfaceDemoTool implements InterfaceDemo {
 4             private String label;
 5 
 6             private InterfaceDemoTool(String label) {
 7                 this.label = label;
 8             }
 9 
10             public String readLabel() {
11                 return label;
12             }
13         }
14 
15         return new InterfaceDemoTool(s);
16     }
17 
18     public static void main(String[] args) {
19         Outer o = new Outer();
20         InterfaceDemo i = o.get_InterfaceDemo("123");
21     }
22 }
23 
24 interface InterfaceDemo {
25     String readLabel();
26 }

当然在方法中还可以定义多个内部类,并且这些内部类之间的关系和普通一个Java文件中多个类之间的关系好像没什么不同。也可以相互继承和创建对象。另外在方法中的内部类不能加private等权限修饰符,只能加abstract和final修饰符。 
另外也可以在某个作用域内创建内部类对象

1 if(a==b){
2     class inner{
3     }
4     new inner();
5 }

上面的例子中,在if外就不能知道有这么个inner类了,他的作用域只在{…}之中,同理,在方法内定义的内部类,在方法外也不能知道存在这么个类,因为这个内部类的作用域只在这个方法内。

3.匿名内部类

下面这块代码中get_inner()的意思是,创建一个继承自InnerFather的匿名类对象,并且自动向上转型为InnerFather后返回。

 1 public class Outer {
 2     public InnerFather get_inner() {
 3         return new InnerFather() {
 4             void print(){
 5                 System.out.println("Inner_Override");
 6             }
 7         };
 8     }
 9 
10     class InnerFather {
11         InnerFather() {
12 
13         }
14         void print(){
15             System.out.println("InnerFather");
16         }
17     }
18 
19     public static void main(String[] args) {
20         Outer o = new Outer();
21         InnerFather i = o.get_inner();
22         i.print();
23     }
24 }

当然这只是有无参构造函数,当父类只有一个含参构造函数的时候,我们可以这样向匿名内部类传入一个构造函数参数。

 1 public class Outer {
 2     public InnerFather get_inner(int i) {
 3         return new InnerFather(i) {
 4             void print(){
 5                 System.out.println("Inner_Override");
 6             }
 7         };
 8     }
 9 
10     class InnerFather {
11         InnerFather(int i) {
12 
13         }
14         void print(){
15             System.out.println("InnerFather");
16         }
17     }
18 
19     public static void main(String[] args) {
20         Outer o = new Outer();
21         InnerFather i = o.get_inner(10);
22         i.print();
23     }
24 }

可以通过构造代码块来实现匿名内部类的自定义的构造函数

abstract class Base {
    public Base(int i) {
        System.out.println("Base constructor");
    }

    public abstract void f();
}

public class AnonymousConstructor {
    public static Base getBase(int i){
        return new Base(i){
            {System.out.println("AnonymousConstructor constructor");}
            public void f(){
            }
        };
    }

    public static void main(String[] args) {
        Base base = getBase(47);
    }
}

(书上说,如果传入了新的对象,就比如下面例子中的s_in,这个s_in就必须是final的,但是我实验了一下发现并不用啊,我也没太搞懂。)

 1 abstract class Base {
 2     public Base(int i) {
 3         System.out.println("Base constructor");
 4     }
 5 
 6     public abstract void f();
 7 }
 8 
 9 public class AnonymousConstructor {
10     public static Base getBase(int i,String s_in){
11         return new Base(i){
12             {System.out.println("AnonymousConstructor constructor");}
13             String s=s_in;
14             public void f(){
15             }
16         };
17     }
18 
19     public static void main(String[] args) {
20         Base base = getBase(47,"hello");
21     }
22 }

4.嵌套类

嵌套类指的是被static修饰的内部类。这意味着:1.创建嵌套类对象不需要外部类对象。2.不能再嵌套类对象之中访问非静态的外围类对象。普通内部类的成员和方法只能放在类的外部层次上(这句话我没搞懂= =),所以普通内部类不能有static的成员和方法。但是嵌套类可以有。

public class Outer {
    static class Inner{
        static int i=5;
    }
    public static void main(String[] args) {
        Inner i=new Outer.Inner();
    }
}

可以从上面的例子看到,在创建这个嵌套类对象的时候,并没有像最开始那样,用一个外部类对象来创建这个内部类对象。其实这和静态方法差不多。

可以在接口内部定义内部类,而且他们即使没有static修饰,也会自动变成public static的。

 1 public interface ClassInInterface {
 2     void howdy();
 3     class Test implements ClassInInterface{
 4         public void howdy(){
 5             System.out.println("howdy!");
 6         }
 7         public static void main(String[] args) {
 8             new Test().howdy();
 9         }
10     }
11 }

书上有句话说的很好,在开发的时候建议在每个类中都写一个main方法测试,但是这又必须带着那些已经编译过的额外代码,所以我们可以用嵌套类放置测试代码。

 1 public class Outer {
 2     public void f(){
 3         System.out.println("I need to be tested");
 4     }
 5 
 6     public static class Tester{
 7         public static void main(String[] args) {
 8             Outer o=new Outer();
 9             o.f();
10         }
11     }
12 }

当然以上两段代码如果是在eclipse上运行的话,需要设置一下运行的main函数在哪,否则会报错

5.多层内部类嵌套

纸老虎,爱有几层有几层,反正只要是外部类的东西,不管哪层外部类,都能访问到。

 1 public class Outer {
 2     void f(){
 3         System.out.println("hello");
 4     }
 5     class Inner1{
 6         void g(){
 7             System.out.println("java");
 8         }
 9         class Inner2{
10             void h(){
11                 f();
12                 g();
13             }
14         }
15     }
16     public static void main(String[] args) {
17         Outer.Inner1.Inner2 in2=new Outer().new Inner1().new Inner2();
18         in2.h();
19     }
20 }

下面是个好玩的

public class Outer {
    void f(){
        System.out.println("hello");
    }
    class Inner1{
        void f(){
            System.out.println("java");
        }
        class Inner2{
            void h(){
                f();
            }
        }
    }
    public static void main(String[] args) {
        Outer.Inner1.Inner2 in2=new Outer().new Inner1().new Inner2();
        in2.h();
    }
}

最后打印结果是java。

大概就这么多吧,以后如果还有新东西再补。

posted @ 2017-04-28 09:58  z_dominic  阅读(367)  评论(0)    收藏  举报