java基础(8)---内部类

可以将一个类的定义放在另一个类的定义内部,这就是内部类。

1 创建内部类

 
 
class Circle {
    double radius = 0;
    public Circle(double radius) {
        this.radius = radius;
    }
    class Draw {     //内部类
      public void drawSahpe() {
            System.out.println("drawshape");
        }
    }
}

2 连接到外部类

到目前为止,内部类似乎还只是一种名字隐藏和组织代码的模式。这些是很有用,但还不是最引人注目的,它还有其他的用途。当生成一个内部类的对象时,此对象与制造它的外围对象之间就有了一种联系,所以他能访问其外围对象的所有成员,而不需要任何特殊条件。

3 使用 .this与.new

当你需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟圆点和this。

public class Outer {  
    class Inner{  
        Outer getOuter(){  
            return Outer.this;  
        }  
    }  
}  

有时你可能想要告知某些其他对象,去创建其某个内部类的对象。要实现此目的,你必须在new表达式中提供对其他外部类对象的引用,这需要使用.new语法。

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

4 内部类与向上转型

当将内部类向上转型为其基类,尤其是转型为一个接口的时候,内部类就有了用武之地。这是因为此内部类某个接口的实现--能够完全不可见。所得到的只是指向基类或接口的引用,所以能够很方便地隐藏实现细节。

5 在方法和作用域内的内部类

内部类语法覆盖了大量其他更加难以理解的技术。例如,可以在一个方法里面或者在任意的作用域内定义内部类。

这么做有两个理由:

1: 如前所示,你实现了某类型的接口,于是可以创建并返回对其的引用。

2:你要解决一个复杂的问题,想创建一个类来辅助你的解决方案,但是又不希望这个类是公共可用的。

局部内部类

public class Parcel5{
 public Destination destination(String s){
 class PDestination implements Destination{
 private String label;
 private PDestination(String whereTo){
   label=whereTo;
  }
  public String readLabel(){return label;}
return new PDestinnation(s);
}
public static void main(String[] args){
Parcel5 p=new Parcel5();
Destination d =p.destination("Tasmania");
}
}

6 匿名内部类

匿名内部类中使用一个在外部定义的对象,那么编译器会要求其参数引用是final的。

《Java编程思想》这块,作者写的是内部类使用外部类对象必须要求是final类型的(作者使用的是JAVA8之前的版本),然而我使用的是JAVA8,JAVA8中,匿名内部类使用外部变量不再被强制要求用final修饰但是要求初始化后的值不能被修改,这是为何呢?

对于final类型来说:编译器编译后,final类型是常量,被存储到了常量池中,在匿名内部类中使用该变量的地方都被替换成了具体的常量值,关于外部类的变量的信息,内部类是不知道的。
对于非final类型来说:传入内部类的仅仅只是传值操作,所以在匿名内部类中改变这个值是无效的。如果在外部类中修改这个值,那么匿名内部类得到的参数值可能已经不是期望中的那个值。所以,在内部类使用外部类的变量时,不允许做任何修改才会避免所以问题。
interface Contents{  
     int value();  
}  
  
public class Parcel7 {  
    public Contents contents(){  
      return new Contents() {//匿名内部类,类的使用与定义结合到了一起  
           private int i=1;  
            public int value(){  
                return i;  
            }  
        };  
    }  
    public static void main(String[] args){  
        Parcel7 p=new Parcel7();  
        Contents c=p.contents();  
    }  
}


JAVA8版本对于非final类型会进行检查,要求不允许修改。final变量自然不会被修改,也不会检查,JAVA8以前的版本要求必须是final变量才能给匿名内部类使用。


7 嵌套类

如果不想使内部类对象与外部类对象相互联系,那么可以将内部类声明为static,这就是嵌套类。
我们知道,非static内部类必须通过外部类对象创建并且获得一个该外部类对象的引用。然而,对于static类型的内部类:
1、创建静态内部类对象,不需要外部类的对象
2、静态内部类中不能使用外部类的非静态成员(因为没有外部类对象的引用)
3、静态内部类中可以创建staic类中的数据和字段(普通内部类不可以,因为它通过外部类对象创建,不属于类成员)

public class Parcel11 {  
    public static class ParcelContents implements Contents{//静态内部类  
        private static int i=11;//静态内部类中的静态变量  
        public int value(){  
            return i;  
        }  
    }  
    public static ParcelContents contents(){  
        return new ParcelContents();  
    }  
    public static void main(String[] args){  
        Contents c=contents();  
    
}

7.1 接口中的内部类

public interface  ClassInInterface {  
    void howdy();  
    static class Test implements ClassInInterface{  
        public void howdy(){  
            System.out.println("Howdy");  
        }  
    }  
    public static void main(String[] args){  
        new Test().howdy();  
    }  
}

正常情况下,接口内部内不能放置任何代码,但是嵌套类可以作为接口的一部分。

接口中类自动地是public和static的。类是static了,仅仅只是将该类置于接口的命名空间里,不违反接口的规则。同时,该嵌套类可以实现它命名空间下的接口。

8 为什么需要内部类

内部类使得多重继承的解决方案变得完整,一个外部类可以实现多个接口。然而,只有继承一个抽象的类或具体的类,此时如果想继承多个抽象的类或具体的类,那么内部类可以解决多重继承中的问题。

9 内部类的继承

如何继承一个内部类?根据前面的内容,内部类中会有一个捕获自创建它外部类对象的引用,如果继承该内部类,这个外部类的引用也要被连接初始化,内部类中也不存在可连接的默认对象。



class WithInner{  
    class Inner{}  
}  
  
public class InheritInner extends WithInner.Inner{  
    //public InheritInner(){} 不能使用该构造器,因为没有获取父类外部类的引用  
    public InheritInner(WithInner wi) {  
        wi.super();//通过父类外部类的引用初始化继承的内部类  
    }  
    public static void main(String[] args){  
        WithInner wi=new WithInner();  
        InheritInner i1=new InheritInner(wi);  
    }  
}

10 内部类可以被覆盖吗

如果有一个类,并且该类有一个内部类,那么创建一个新类继承该外部类,然后在新类中重新创建此内部类会覆盖父类中的内部类吗?

class Egg{  
    private Yolk y;  
    protected class Yolk{  
        public Yolk(){  
            System.out.println("Egg.Yolk()");  
        }  
    }  
    public Egg(){  
        System.out.println("New Egg()");  
        y=new Yolk();  
    }  
}  
  
public class BigEgg extends Egg{//继承了一个外部类  
    public class Yolk{//重写继承外部类中的内部类  
        public Yolk(){  
            System.out.println("BigEgg.Yolk()");  
        }  
    }  
    public static void main(String[] args){  
        new BigEgg();  
    }  
}/*Output: 
New Egg() 
Egg.Yolk()*/

创建自身对象时,会调用父类构造器,然后父类构造器中顶一起了内部类,然后输出的结果并不是子类中的内部类,也就是说父类并没有获取子类内部类的对象,也就不存在转型。两个内部类没有什么关系,它们各自在自己的命名空间内。

class Egg2{  
    private Yolk y=new Yolk();//先与构造器前初始化,调用内部类构造器  
    protected class Yolk{  
        public Yolk(){//内部类构造函数  
            System.out.println("Egg2.Yolk()");  
        }  
        public void f() {//内部类f()方法  
            System.out.println("Egg2.Yolk().f()");  
        }  
    }  
    public Egg2(){//初始化之前,先初始化字段  
        System.out.println("New Egg2()");  
    }  
    public void insertYolk(Yolk yy){//获取一个Yolk类或子类的引用  
        y=yy;  
    }  
    public void g(){  
        y.f();//调用y引用指向的类的f()方法  
    }  
}  
  
public 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());//首先初始化父类BigEgg,然后调用本类中的Yolk类的构造函数,然后调用inserYolk()方法  
    }  
    public static void main(String[] args){  
        Egg2 e2=new BigEgg2();//调用BigEgg2()构造器,向上转型为Egg2类  
        e2.g();  
    }  
}/*Output: 
Egg2.Yolk() 
New Egg2() 
Egg2.Yolk() 
BigEgg2.Yolk() 
BigEgg2.Yolk().f() 
*/

11 局部内部类

可以在类中创建匿名内部类,静态内部类,普通内部类,也可以在代码块里创建内部类,典型的方式就是在一个方法体的内部里面创建。局部内部类不能有访问说明符,因为它不是外部类的一部分,但是它可以方位当前代码块内的常量(Java8中传入到内部类的值不一定是final类型的,只要初始化后不被修改即可),以及此外围类的所有成员。
创建局部内部类和创建匿名内部类比较

interface Counter{  
    int next();//用于返回序列中的下一个值  
}  
  
public class LocalInnerClass {  
    private int count=0;  
    Counter getCounter(final String name){  
        class LocalCounter implements Counter{//局部内部类,定义在方法中  
            public LocalCounter(){  
                System.out.println("LocalCounter()");  
            }  
            public int next(){  
                System.out.print(name);  
                return count++;  
            }  
        }  
        return new LocalCounter();  
    }  
    Counter getCounter2(final String name){  
        return new Counter(){//匿名内部类  
            {  
                System.out.println("Counter()");  
            }  
            public int next(){  
                System.out.print(name);  
                return count++;  
            }  
        };  
    }  
    public static void main(String[] args){  
        LocalInnerClass lic=new LocalInnerClass();//创建外部类对象  
        Counter c1=lic.getCounter("Local inner");//通过外部类对象调用方法创建局部内部类  
        Counter c2=lic.getCounter2("Anonymous inner");//通过外部类对象创建匿名内部类  
        for(int i=0;i<5;i++)  
            System.out.println(c1.next());  
        for(int i=0;i<5;i++)  
            System.out.println(c2.next());  
    }  
}/*Output: 
LocalCounter() 
Counter() 
Local inner0 
Local inner1 
Local inner2 
Local inner3 
Local inner4 
Anonymous inner5 
Anonymous inner6 
Anonymous inner7 
Anonymous inner8 
Anonymous inner9*/

分别创建了局部内部类LocalCounter类和匿名内部类实现了Counter接口,该接口用于返回序列中的下一个值,这两个内部类具有了相同行为和能力。

局部内部类和匿名内部类的区别:局部内部类的名字在方法外是不可见的,局部内部类可以重载构造器,而匿名内部类只能用于实例初始化,没有构造器。

所以当我们需要不止一个内部类的对象时,应该选择局部内部类而不是匿名内部类。




猜你喜欢

转载自blog.csdn.net/qq_40182703/article/details/80413145