Java--内部类

来源:互联网 发布:js判断质数 编辑:程序博客网 时间:2024/06/06 11:40
内部类是指在一个外部类的内部再定义一个类。类名不需要和文件夹相同。
*内部类可以是静态static的,也可用public,default,protected和private修饰。(而外部顶级类即类名和文件名相同的只能使用public和default)。

注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。对于一个名为outer的外部类和其内部定义的名为inner的内部类。编译完成后出现outer.class和inner.class两类。所以内部类的成员变量/方法名可以和外部类的相同。
1. 成员内部类
成员内部类,就是作为外部类的成员,可以直接使用外部类的所有成员和方法,即使是private的。同时外部类要访问内部类的所有成员变量/方法,则需要通过内部类的对象来获取。
要注意的是,成员内部类不能含有static的变量和方法。因为成员内部类需要先创建了外部类,才能创建它自己的,了解这一点,就可以明白更多事情,在此省略更多的细节了。
在成员内部类要引用外部类对象时,使用outer.this来表示外部类对象;而需要创建内部类对象,可以使用outer.inner  obj = outerobj.new inner();
public class Outer {    public static void main(String[] args) {        Outer outer = new Outer();        Outer.Inner inner = outer.new Inner();        inner.print("Outer.new");        inner = outer.getInner();        inner.print("Outer.get");    }    // 个人推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时    public Inner getInner() {        return new Inner();    }    public class Inner {        public void print(String str) {            System.out.println(str);        }    }}

2. 局部内部类
局部内部类,是指内部类定义在方法和作用域内。Thinking in Java给了这么两个例子:

定义在方法内:

public class Out {    private int age = 12;    public void Print(final int x) {        class In {            public void inPrint() {                System.out.println(x);                System.out.println(age);            }        }        new In().inPrint();    }}

定义在作用域里:

public 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();        }    }    public void track() {        System.out.println("track");        internalTracking(true);    }    public static void main(String[] args) {        Parcel5 p = new Parcel5();        p.track();    }}

局部内部类也像别的类一样进行编译,但只是作用域不同而已,只在该方法或条件的作用域内才能使用,退出这些作用域后无法引用的。
3. 静态内部类
静态内部类,就是修饰为static的内部类。声明为static的内部类,不需要内部类对象和外部类对象之间的联系,就是说我们可以直接引用outer.inner,即不需要创建外部类,也不需要创建内部类。
嵌套类和普通的内部类还有一个区别:普通内部类不能有static数据和static属性,也不能包含嵌套类,但嵌套类可以。而嵌套类不能声明为private,一般声明为public,方便调用。
4. 匿名内部类
匿名内部类也就是没有名字的内部类
正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写

但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口

匿名内部类的基本实现

public abstract class Person {    public abstract void eat();}public class Demo {    public static void main(String[] args) {        Person p = new Person() {            public void eat() {                System.out.println("eat");            }        };        p.eat();    }}

不使用匿名内部类来实现抽象方法

abstract class Person {    public abstract void eat();} class Child extends Person {    public void eat() {        System.out.println("eat something");    }} public class Demo {    public static void main(String[] args) {        Person p = new Child();        p.eat();    }}
运行结果:eat something
可以看到,我们用Child继承了Person类,然后实现了Child的一个实例,将其向上转型为Person类的引用
但是,如果此处的Child类只使用一次,那么将其编写为独立的一个类岂不是很麻烦?
这个时候就引入了匿名内部类

在接口上使用匿名内部类
interface Person {    public void eat();} public class Demo {    public static void main(String[] args) {        Person p = new Person() {            public void eat() {                System.out.println("eat something");            }        };        p.eat();    }}
运行结果:eat something


由上面的例子可以看出,只要一个类是抽象的或是一个接口,那么其子类中的方法都可以使用匿名内部类来实现
最常用的情况就是在多线程的实现上,因为要实现多线程必须继承Thread类或是继承Runnable接口
 

Thread类的匿名内部类实现
public class Demo {    public static void main(String[] args) {        Thread t = new Thread() {            public void run() {                for (int i = 1; i <= 5; i++) {                    System.out.print(i + " ");                }            }        };        t.start();    }}
运行结果:1 2 3 4 5


Runnable接口的匿名内部类实现

public class Demo {    public static void main(String[] args) {        Runnable r = new Runnable() {            public void run() {                for (int i = 1; i <= 5; i++) {                    System.out.print(i + " ");                }            }        };        Thread t = new Thread(r);        t.start();    }}
运行结果:1 2 3 4 5



在这里我们看到使用匿名内部类我们必须要继承一个父类或者实现一个接口,当然也仅能只继承一个父类或者实现一个接口。同时它也是没有class关键字,这是因为匿名内部类是直接使用new来生成一个对象的引用。当然这个引用是隐式的。

public abstract class Bird {      private String name;        public String getName() {          return name;      }        public void setName(String name) {          this.name = name;      }            public abstract int fly();  }    public class Test {            public void test(Bird bird){          System.out.println(bird.getName() + "能够飞 " + bird.fly() + "米");      }            public static void main(String[] args) {          Test test = new Test();          test.test(new Bird() {                            public int fly() {                  return 10000;              }                            public String getName() {                  return "大雁";              }          });      }  }  
输出:  
大雁能够飞 10000米  
       在Test类中,test()方法接受一个Bird类型的参数,同时我们知道一个抽象类是没有办法直接new的,我们必须要先有实现类才能new出来它的实现类实例。所以在mian方法中直接使用匿名内部类来创建一个Bird实例。
       由于匿名内部类不能是抽象类,所以它必须要实现它的抽象父类或者接口里面所有的抽象方法。
       对于这段匿名内部类代码其实是可以拆分为如下形式:
public class WildGoose extends Bird{      public int fly() {          return 10000;      }            public String getName() {          return "大雁";      }  }    WildGoose wildGoose = new WildGoose();  test.test(wildGoose);  

在这里系统会创建一个继承自Bird类的匿名类的对象,该对象转型为对Bird类型的引用。
对于匿名内部类的使用它是存在一个缺陷的,就是它仅能被使用一次,创建匿名内部类时它会立即创建一个该类的实例,该类的定义会立即消失,所以匿名内部类是不能够被重复使用。对于上面的实例,如果我们需要对test()方法里面内部类进行多次使用,建议重新定义类,而不是使用匿名内部类。


注意事项
在使用匿名内部类的过程中,我们需要注意如下几点:

1、使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。

2、匿名内部类中是不能定义构造函数的。
3、匿名内部类中不能存在任何的静态成员变量和静态方法。   

4、匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。 

5、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。

5.内部类的继承
内部类的继承,是指内部类被继承,普通类 extents 内部类。而这时候代码上要有点特别处理,具体看以下例子:
public class InheritInner extends WithInner.Inner {     // InheritInner() 是不能通过编译的,一定要加上形参     InheritInner(WithInner wi) {         wi.super();     }      public static void main(String[] args) {         WithInner wi = new WithInner();         InheritInner obj = new InheritInner(wi);     } }  class WithInner {     class Inner {      } } 

可以看到子类的构造函数里面要使用父类的外部类对象.super();而这个对象需要从外面创建并传给形参。
至于内部类的重载,感觉Thinking in Java的例子很复杂,在平常应用中应该很少,因为有点难懂,不清晰。而内部类和闭包之间的事情,暂时放下,以后再看。
0 0