重温《JAVA编程思想》----2017.1.13 内部类

本笔记中有部分选自:http://blog.csdn.net/chenssy/article/details/13170015  (很好的博客,尤其是它的java提高篇,对我的帮助很大)

 

1.1非普通内部类:

 

(最普通的内部类叫成员内部类,所以一共有4中内部类:

1.成员内部类。2.局部内部类。3.匿名内部类。4.static内部类(又名嵌套类)。

 

a.)一个定义在方法中的类,或者是方法作用域的类。---------“局部内部类”   

这个定义在方法中的类是方法的一部分,而不是方法所属类的一部分。所以方法之外不能使用它,所以他们的作用域仅仅是相关作用域,理解“局部”的意思

 

使用原因:可能是你的方法想生成1个基类或者接口的的对象,可以通过自己在方法中创建个实现接口的局部内部类完成这种操作,这可能是因为你不想别人也生成这个类,但是我仍然需要一个类来解决我的问题。

 

interface A{

}

 

class Inner{

public A getA(){

class AA implements A{

}

return new AA();

}

}

 

 

有的时候选用局部内部类代替匿名内部类的原因:

局部内部类可以访问当前代码块的常量,和外围类的成员,但是它不是外围类的一部分。

第一个原因是:有的时候我们需要一个已经命名的构造器,或者需要重载构造器,而匿名内部类仅仅用于实例初始化,

 

第二个原因是:我们需要不止一个该内部类对象,而匿名内部类只能生成1个内部类对象,局部内部类可能通过方法调用,每调用1次就生成1个局部内部类对象。

 

 

 

1.2匿名内部类:

a.) ruturn new A(){

Int i = 0;

            Public int value(){};

}

 

 它的实际意义是创建1个继承自A类的匿名类对象”。

         我们这个例子是用默认的构造器来生成的,如果我们要生成的匿名内部类的构造器有参数,我们生成匿名内部类的时候,只要传合适的参数就好。

 

 

b.) 如果定义了1个匿名内部类,并且希望它使用(注意:必须是要使用的参数,如果不使,就可以不是final.)1个在其外部定义的1个对象,那么编译器会要求其参数引用是final的。注意:不是这个参数的类或者参数的对象是final的,而是参数引用是final的。

 

c.)通过实例初始化,就可以做一些类似构造器的行为。

Return new A(i){

{

实例初始化(即代码块);

}

}

d.)对于匿名内部类而言,实例初始化的效果和构造器一样,但是你不能重载实例初始化方法。

 

 

e.)匿名内部类的限制:

它只能继承1个类或者实现1个接口,不能两者兼备,接口也只能实现1个。

 

 

 

 

 

1.3嵌套类:

a.)static声明的内部类就是嵌套类。

b.)普通的内部类对象隐式的保存了1个指向外部类对象的引用,所以生成普通内部类对象的时候需要借助外部类对象,无外部类对象生成不了普通内部类对象,然而1)static内部类对象的生成不再需要外部类对象,(2)并且不能从static嵌套类的对象访问非静态的外围类对象。

 

 

 

c.)与普通内部类的区别:

c.1)生成对象不需要外部类对象。

c.2)不能从static内部类的对象中访问外部类的非静态对象。

c.3)普通的内部类不能有static数据和static字段,也不能包含static内部类。

c.4)static内部类可以有static数据、static字段、static内部类。

c.5)普通内部类可以通过.this指向外部类对象的引用,然后static内部类就没有.this的static引用。

 

 

 

 

 

 

d.)接口内部的static嵌套类:

d.1)在接口中你的所有类都自动是public和staitc的,所以接口中的内部类都是static内部类。

d.2)我们甚至可以直接使接口中的static内部类实现我们的外部接口。例子如下:

 

interface Service{

void test();

class StaticTest implements Service{//static内部类实现外部类接口

 

@Override

public void test() {

System.out.println("我是实现了外部接口的static内部类 ");

}

}

}

 

 

 

 

public class Test {

 

public static void main(String[] args) {

Service.StaticTest test = new Service.StaticTest();

test.test();

}

}

输出:我是实现了外部接口的static内部类

 

注意1点:Service.StaticTest test = new Service.StaticTest();

生成静态内部类,我们再也不需要外部类对象了。

 

 

 

 

 

在接口中使用static内部类的目的:

如果我们想要创建1部分的公共代码,使得他们可以被某个接口的所有不同实现所共用,那么使用static内部类会很方便。

   

e.)嵌套类常常指的是static内部类,但是也可以包括普通的内部类,普通内部类也可以多层级的嵌套,并且它享有它所嵌入的类中的所有成员的访问权限。

 

----------------------------------------------------------------------------

class A{

private void f(){};

class B {

private void g(){};

class C{

public void  h(){

f();

g();

};

}

}

}

如上述代码,A嵌套B,B嵌套C.B能访问A的所有成员,C能访问BA的所有成员,注意这些内部类都不是static的。

----------------------------------------------------------------------------

public class Test {

 

public static void main(String[] args) {

A a = new A();

A.B b = a.new B();

A.B.C  c = b.new C();

c.h();

}

}

嵌套类的生成方式比较有意思,他们都需要外围类的对象才能生成自己的对象,如上述代码。

------------------------------------------------------------------------------

 

 

 

 

 

 

 

 

使用内部类的原因:----------->多重继承

下面部分选自博客:http://blog.csdn.net/chenssy/article/details/13024951    

 

一、为什么要使用内部类

       为什么要使用内部类?在《Think in Java》中有这样一句话:使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响

      在我们程序设计中有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整

 

public interface Father {  

  

}  

  

public interface Mother {  

  

}  

  

public class Son implements Father, Mother {  

  

}  

  

public class Daughter implements Father{  

  

    class Mother_ implements Mother{  

          

    }  

}  

 

      其实对于这个实例我们确实是看不出来使用内部类存在何种优点,但是如果Father、Mother不是接口,而是抽象类或者具体类呢?这个时候我们就只能使用内部类才能实现多重继承了。

 

public class Father {  

    public int strong(){  

        return 9;  

    }  

}  

  

public class Mother {  

    public int kind(){  

        return 8;  

    }  

}  

 

public class Son {  

      

    /** 

     * 内部类继承Father类 

     */  

    class Father_1 extends Father{  

        public int strong(){  

            return super.strong() + 1;  

        }  

    }  

      

    class Mother_1 extends  Mother{  

        public int kind(){  

            return super.kind() - 2;  

        }  

    }  

      

    public int getStrong(){  

        return new Father_1().strong();  

    }  

      

    public int getKind(){  

        return new Mother_1().kind();  

    }  

}  

      其实使用内部类最大的优点就在于它能够非常好的解决多重继承的问题,但是如果我们不需要解决多重继承问题,那么我们自然可以使用其他的编码方式,但是使用内部类还能够为我们带来如下特性(摘自《Think in java》):

      1、内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立。

      2、在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类。

      3、创建内部类对象的时刻并不依赖于外围类对象的创建。

      4、内部类并没有令人迷惑的“is-a”关系,他就是一个独立的实体。

      5、内部类提供了更好的封装,除了该外围类,其他类都不能访问。

总结:内部类使得多重继承机制变得完整,因为接口只能解决一部分问题,然而基类若是具体类或者抽象类的时候,我们的类只能继承单一的类,但是我们可以:通过多个内部类继承多个具体类或者抽象类,达到多重继承的目的。

 

 

内部类有自己的状态信心,与外围类信心相互独立,并且除了外围类,其他类不能访问(外部类也是通过生成内部类对象再访问的内部类成员,并且内部类的private成员外部类访问不到)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  在Java提高篇-----详解内部类中对匿名内部类做了一个简单的介绍,但是内部类还存在很多其他细节问题,所以就衍生出这篇博客。在这篇博客中你可以了解到匿名内部类的使用、匿名内部类要注意的事项、如何初始化匿名内部类、匿名内部类使用的形参为何要为final。

 

       一、使用匿名内部类内部类

       匿名内部类由于没有名字,所以它的创建方式有点儿奇怪。创建格式如下:

[java] view plain copy print?

1. new 父类构造器(参数列表)|实现接口()    

2.     {    

3.      //匿名内部类的类体部分    

4.     }  

new 父类构造器(参数列表)|实现接口()  

    {  

     //匿名内部类的类体部分  

    }

       在这里我们看到使用匿名内部类我们必须要继承一个父类或者实现一个接口,当然也仅能只继承一个父类或者实现一个接口。同时它也是没有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 "大雁";  

            }  

        });  

    }  

}  

------------------  

Output:  大雁能够飞 10000米  

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 "大雁";

            }

        });

    }

}

------------------

Output:

大雁能够飞 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);  

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、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。

 

       三、使用的形参为何要为final

       参考文件:http://android.blog.51cto.com/268543/384844

       我们给匿名内部类传递参数的时候,若该形参在内部类中需要被使用,那么该形参必须要为final。也就是说:当所在的方法的形参需要被内部类里面使用时,该形参必须为final。

      为什么必须要为final呢?

      首先我们知道在内部类编译成功后,它会产生一个class文件,该class文件与外部类并不是同一class文件,仅仅只保留对外部类的引用。当外部类传入的参数需要被内部类调用时,从java程序的角度来看是直接被调用:

public class OuterClass {  

    public void display(final String name,String age){  

        class InnerClass{  

            void display(){  

                System.out.println(name);  

            }  

        }  

    }  

}  

public class OuterClass {

    public void display(final String name,String age){

        class InnerClass{

            void display(){

                System.out.println(name);

            }

        }

    }

}

      从上面代码中看好像name参数应该是被内部类直接调用?其实不然,在java编译之后实际的操作如下:

public class OuterClass$InnerClass {  

    public InnerClass(String name,String age){  

        this.InnerClass$name = name;  

        this.InnerClass$age = age;  

    }  

      

      

    public void display(){  

        System.out.println(this.InnerClass$name + "----" + this.InnerClass$age );  

    }  

}  

public class OuterClass$InnerClass {

    public InnerClass(String name,String age){

        this.InnerClass$name = name;

        this.InnerClass$age = age;

    }

    

    

    public void display(){

        System.out.println(this.InnerClass$name + "----" + this.InnerClass$age );

    }

}

       所以从上面代码来看,内部类并不是直接调用方法传递的参数,而是利用自身的构造器对传入的参数进行备份,自己内部方法调用的实际上自己的属性而不是外部方法传递进来的参数。

       直到这里还没有解释为什么是final?在内部类中的属性和外部方法的参数两者从外表上看是同一个东西,但实际上却不是,所以他们两者是可以任意变化的,也就是说在内部类中我对属性的改变并不会影响到外部的形参,而然这从程序员的角度来看这是不可行的,毕竟站在程序的角度来看这两个根本就是同一个,如果内部类该变了,而外部方法的形参却没有改变这是难以理解和不可接受的,所以为了保持参数的一致性,就规定使用final来避免形参的不改变。

      简单理解就是,拷贝引用,为了避免引用值发生改变,例如被外部类的方法修改等,而导致内部类得到的值不一致,于是用final来让该引用不可改变。

      故如果定义了一个匿名内部类,并且希望它使用一个其外部定义的参数,那么编译器会要求该参数引用是final的。

 

       四、匿名内部类初始化

       我们一般都是利用构造器来完成某个实例的初始化工作的,但是匿名内部类是没有构造器的!那怎么来初始化匿名内部类呢?使用构造代码块!利用构造代码块能够达到为匿名内部类创建一个构造器的效果。

public class OutClass {  

    public InnerClass getInnerClass(final int age,final String name){  

        return new InnerClass() {  

            int age_ ;  

            String name_;  

            //构造代码块完成初始化工作  

            {  

                if(0 < age && age < 200){  

                    age_ = age;  

                    name_ = name;  

                }  

            }  

            public String getName() {  

                return name_;  

            }  

              

            public int getAge() {  

                return age_;  

            }  

        };  

    }  

      

    public static void main(String[] args) {  

        OutClass out = new OutClass();  

          

        InnerClass inner_1 = out.getInnerClass(201, "chenssy");  

        System.out.println(inner_1.getName());  

          

        InnerClass inner_2 = out.getInnerClass(23, "chenssy");  

        System.out.println(inner_2.getName());  

    }  

}  

 

 

 

 

 

 

 

 

内部类的功能:

 

闭包功能:闭包是一个可调用的对象,并且包含了一些信息。可以看见内部类是一个很好的提供闭包功能的解决方案。

 

通过内部类可以形成1个控制框架:

1.抽象类Event,我们以后的内部类都继承自这个Event,代表事件,它有抽象方法action(),每个内部类都实现成不同的action()方法。

 

2.一个控制器Controller:里面有一个List<Event>addEvent(Event)、还有一个run方法,遍历List<Event>,使list中的每个Event都调用action方法。

 

以上是我们框架不变的部分,我们变得部分仅仅是我要用控制机控制什么事件。

 

如果我们要用控制器控制1绿室类,它有LightWaterOpen等等事件,则应该:

 

 

Class GreenHouse extends Controller{

 

Class Light extends Event{

Public void  action(){

/关灯。

}

 

}

 

 

 

Class Water extends Event{

Public void  action(){

浇水/不浇水。

}

 

}

 

 

Class Open extends Event{

Public void  action(){

/关大门。

}

 

}

 

 

}

 

一个框架的变化部分就是我们要添加什么事件,并做什么事(action()),其他的地方都保持不变。

 

 

 

内部类的继承:

 

因为内部类的构造器必须连接到指向其外围类对象的引用,所以在继承内部类的时候,事情变得有点复杂。

 

问题在于,哪个指向外围类对象的“秘密引用”必须被初始化(一般是在内部类构造器中完成的),而在内部类的外部子类中不再存在可连接的默认对象。 用特殊语法解决。

 

 

Class Out{

Class In{}

}

 

Class Inner extends Out.In{

Inner(Out o){

o.super; -------------------------默认构造器不可以,必须用特殊构造器声明一些关系。

}

}

 

 

 

 

 

 

内部类标识符:

 

OutClass$InnerClass.class;

OutClass$1.class;   -------------------------->这是匿名内部类的标识符。

猜你喜欢

转载自blog.csdn.net/mypromise_tfs/article/details/54430284
今日推荐