美文网首页
第十章 内部类

第十章 内部类

作者: 浩林Leon | 来源:发表于2017-12-20 23:01 被阅读1次

内部类有重要的特性,把一些逻辑相关的类组织在一起,并控制内部的类的可见性.内部类和组合是完全不同的概念.
内部类的常见情况是: 外部类有一个方法,返回指向内部类的引用.

10.2链接到外部类

当生成一个内部类对象时,此对象有访问制造他的外围对象(enclosing Object)有了一种联系,它能访问外围对象的所有成员,不需要任何特殊条件.此外内部类也拥有外围类的所有元素的访问权限.

package tinking_in_java.innerClass;
/**
 * Created by leon on 17-12-15.
 */
public interface Selector {
    boolean end();
    void next();
    Object current();
}

package tinking_in_java.innerClass;
/**
 * Created by leon on 17-12-15.
 */
public class Sequence {
    private Object[] items;
    private int next = 0;
    public Sequence(int size) {
        items = new Object[size];
    }
    public void add(Object x) {
        if (next < items.length) {
            items[next++] = x;
        }
    }
    private class SequenceSelector implements Selector {
        private int i = 0;
        @Override
        public boolean end() {
            return i == items.length;
        }
        @Override
        public void next() {
            if (i < items.length) {
                i++;
            }
        }
        @Override
        public Object current() {
            return items[i];
        }
    }
    public Selector getSelector() {
        return new SequenceSelector();
    }
    public static void main(String[] args) {
        Sequence sequence = new Sequence(10);
        for (int i = 0; i < 10; i++) {
            sequence.add(i);
        }
        Selector selector = sequence.getSelector();
        while (!selector.end()) {
            System.out.print(selector.current()+" ");
            selector.next();
        }
    }
}
//output---
0 1 2 3 4 5 6 7 8 9 

上述代码是迭代器模式.迭代器模式,提供一个迭代接口Selector 包含 next(),current(),end() 3个协议,由其他具有容器类型的数据结构自己区根据 selector接口各自迭代.可以达到不同数据结构容器可以按照相同规则进行遍历数据.
内部类private SelectorSequence 可以自动拥有外部类items的访问.这是怎么做到的呢?编译器会自动处理所有的细节.内部类的对象只能在与其外部类对象相关联的情况下才能被创建(当内部类是非静态类时).

10.3使用.this 和.new

在内部类中返回外部类引用可以采用.this形式直接返回.例如:

package tinking_in_java.innerClass;
/**
 * Created by leon on 17-12-15.
 */
public class Dotthis {
    void f() {
        System.out.println("Out---");
    }
    public class Inner {
        public Dotthis outer() {
            return Dotthis.this;
        }
    }
    public Inner getInner() {
        return new Inner();
    }
    public static void main(String[] args) {
        Dotthis dotthis = new Dotthis();
        Dotthis.Inner inner = dotthis.getInner();
        inner.outer().f();
    }
}
//output---
Out---

如果在外部(其他类)想创建内部对象 可以通过.new,例如

public static void main(String[] args) 
   Dotthis dotthis = new Dotthis();
    Dotthis.Inner inner = dotthis.new Inner();
    Dotthis.Inner inner2 = new Dotthis().new Inner();
    inner.outer().f();
    inner2.outer().f();
}

如果内部类是 静态内部类(嵌套类)则可以直接new Inner();在其他的第三方类可以通过new 外部类.静态内部类()的方式(new Dotthis.Inner();)

package tinking_in_java.innerClass;
/**
 * Created by leon on 17-12-15.
 */
public class Dotthis {
    void f() {
        System.out.println("Out---");
    }
    public static class Inner {
        public void in() {
            System.out.println("in----");
        }
    }
    public Inner getInner() {
        return new Inner();
    }
    public static void main(String[] args) {
        Dotthis dotthis = new Dotthis();
        Dotthis.Inner inner = new Inner();
        Dotthis.Inner inner2 = new Inner();
        inner.in();
    }

inner1 和 inner2并不是指向同一个 对象.

10.4内部类和向上转型

当一个类只想对外提供具有某种接口的功能(返回基类或接口引用),但是想隐藏具体的实现细节.那么就内部类就有了用武之地(当将内部类向上转型为基类,尤其是转型为接口,内部类可以发挥作用).

package tinking_in_java.innerClass;
/**
 * Created by leon on 17-12-15.
 */
public interface Contents {
    int value();
}
package tinking_in_java.innerClass;
/**
 * Created by leon on 17-12-15.
 */
public interface Describ {
    String readLabel();
}
package tinking_in_java.innerClass;
/**
 * Created by leon on 17-12-15.
 */
public class Parcel4 {
    private class PContent implements Contents {
        @Override
        public int value() {
            return 20;
        }
    }
    private class PDescrib implements Describ {
        String label;
        public PDescrib(String label) {
            this.label = label;
        }
        @Override
        public String readLabel() {
            return label;
        }
    }
    public Describ getDecib(String label) {
        return new PDescrib(label);
    }
    public Contents getContens() {
        return new PContent();
    }
    public static void main(String[] args) {
        Parcel4 parcel4 = new Parcel4();
        Contents contents = parcel4.getContens();
        Describ describ = parcel4.getDecib("hello ");
        System.out.println(describ.readLabel() + contents.value());
    }
}
//output---
hello 20

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

通常,如果读写类中包含的内部类,那么他们都是”平凡的”内部类,简单且容易理解.然而有些更加难理解的技术,例如:在一个方法或者任意作用域里面定义内部类.这么做有两个理由:

1.实现了某一个类型的接口,创建并返回接口的引用.(通常是希望创建局部的权限,希望在这个区域之外不能访问和修改)
2.要解决一个复杂的问题,想创建一个辅助类帮助,但不希望这个类是公用的.

一般可以在下面这些地方使用:

  1. 定义在方法中
  2. 定义在作用域内,此作用域在方法内有效
  3. 实现接口的匿名类
  4. 一个匿名类,扩展了非默认构造器的类
  5. 一个匿名类,执行了字段初始化
  6. 一个匿名类,通过实例初始化实现构造(匿名类不可能有构造器)
    解释:
    1.在方法内部创建的内部类,只有在该方法内可以调用,其他地方是不能调用的,因为此内部类是方法的一部分,而不是之外的外部类的一部分.在其他地方可以出现同名的类,不会冲突.
    2.在作用域之内的,编译的时候和其他的类一起同时编译,但是除了在此作用域之外是不可访问的,其他的和普通类一样.可以在if,switch...case..中.

10.6匿名内部类

package tinking_in_java.innerClass;
/**
 * Created by leon on 17-12-15.
 */
public class Parcel8 {
    //传递构造函数参数
    public Wrapping getWrap(int value) {
        return new Wrapping(value) {
            @Override
            public int getValue() {
                return super.getValue() * 3;
            }
        };
    }
    //创建新的字段,初始化
    public Describ getDesrib(String describ) {
        String out = "sss";
        return new Describ() {
            private String label = describ;
            @Override
            public String readLabel() {
                return label + out;
            }
        };
    }
    //进行匿名类里面做构造器的行为,
    public Base getBase(int base) {
        return new Base(base) {
            //这里是实例初始化 形式
            {
                System.out.println("inner Constructor");
                y = base;
            }
            @Override
            public void f() {
                System.out.println("inner y=" + y);
            }
        };
    }
    public static void main(String[] args) {
        Parcel8 parcel8 = new Parcel8();
        Wrapping wrapping = parcel8.getWrap(1);
        System.out.println(wrapping.getValue());
        Describ describ = parcel8.getDesrib("parcel8");
        System.out.println(describ.readLabel());
        Base base = parcel8.getBase(300);
        base.f();
    }
}



package tinking_in_java.innerClass;
/**
 * Created by leon on 17-12-15.
 */
public class Wrapping {
    private int i;
    Wrapping(int i) {
        this.i = i;
    }
    public int getValue() {
        return i;
    }
}

package tinking_in_java.innerClass;
/**
 * Created by leon on 17-12-15.
 */
public interface Describ {
    String readLabel();
}

package tinking_in_java.innerClass;
/**
 * Created by leon on 17-12-15.
 */
public abstract class Base {
    public Base(int i) {
        System.out.println("Base Constructor" + i);
    }
    
    public abstract void f();
}

10.6.1 利用内部匿名类构造工厂方法优化.

(把生产工厂合并到具体的产品中,避免为每个产品单独创建一个工厂类)
工厂方法 原理UML


image.png

以下是标准工厂方法和 采用匿名内部类优化的工厂方法对比:


image.png
其中:pencil,pencilFactory 是标准工厂方法,pen是改良后的.
发现改良后的 少了一个PenFactory类.
Ifactory.java
package tinking_in_java.innerClass.FactoryMethod;
/**
 * Created by leon on 17-12-16.
 */
public interface IFactory {
    IProduct creatProduct();
}

Iproduct.java
package tinking_in_java.innerClass.FactoryMethod;
/**
 * Created by leon on 17-12-16.
 */
public interface IProduct {
    void write();
}
Pencil.java
package tinking_in_java.innerClass.FactoryMethod;
/**
 * Created by leon on 17-12-16.
 */
public class Pencil implements IProduct {
    String factory;
    public Pencil(String factoryName) {
        factory = factoryName;
    }
    @Override
    public void write() {
        System.out.println(factory + "制造,用铅笔写字");
    }
}

 PencilFactory.java
package tinking_in_java.innerClass.FactoryMethod;
/**
 * Created by leon on 17-12-16.
 */
public class PencilFactory implements IFactory {
    @Override
    public IProduct creatProduct() {
        return new Pencil("福建");
    }
}
Pen.java
 package tinking_in_java.innerClass.FactoryMethod;
/**
* Created by leon on 17-12-16.
 */
public class Pen implements IProduct {
    String factory;
    public Pen(String factory) {
        this.factory = factory;
    }
    @Override
    public void write() {
        System.out.println(factory + "制造,使用圆珠笔写字");
    }
    public static IFactory penFactory = new IFactory() {
        @Override
        public IProduct creatProduct() {
            return new Pen("上海");
        }
    };
}

Client.java
package tinking_in_java.innerClass.FactoryMethod;
/**
 * Created by leon on 17-12-16.
 */
public class Client {
    public void write() {
        //标准的工厂方法
        IFactory factory = new PencilFactory();
        IProduct pencil = factory.creatProduct();
        pencil.write();
        //用匿名内部类改造后的 工厂方法.
        IProduct pen = Pen.penFactory.creatProduct();
        pen.write();
    }
    public static void main(String[] args) {
        Client client = new Client();
        client.write();
    }
}

10.7嵌套类

如果不需要内部对象与外围类对象有关系,可以采用将内部类申明为static.这通常称嵌套类,因为是static类型,嵌套类不保存外围对象的this引用,所以嵌套类是无法访问外围类的非static类型的变量.

1.创建嵌套对象,不需要依赖其外围对象. 可以直接new XXX.ssss();
2.不能从嵌套类中访问外围对象的非静态变量(对象)
嵌套类和普通内部类的区别.普通内部类 里面不能有静态变量static xxx(可以有静态常量static final xxx,不能是对象),也不能包含嵌套类.
但是嵌套类具备这些限制.可以使用static xxx和static final xxx

嵌套类可以用于作为测试代码:编译的时候会单独生成一个NetsTest$Test.class ,打包时可以把这个删除即可,可以方便的测试

package tinking_in_java.innerClass;
/**
 * Created by leon on 17-12-16.
 */
public class NetsTest {
    public void test() {
        System.out.println("test----");
    }
    public static class Test {
        public static void main(String[] args) {
            NetsTest netsTest = new NetsTest();
            netsTest.test();
        }
    }
}

10.7.1 接口内部的类(接口嵌套类)

通常不能在接口中实现方法,但是可以在接口中定义嵌套类(因为在接口中).可以实现外围接口.通常接口内的常量,class 都会自动是 public static .原因在 :

static修饰就表示它属于类的,随的类的加载而存在的,如果是非static的话,就表示属于对象的,只有建立对象时才有它,而接口是不能建立对象的,所以接口的常量必须定义为static

package tinking_in_java.innerClass;
/**
 * Created by leon on 17-12-16.
 */
public interface innerInterface {
    void show();
    class inner implements innerInterface {
        @Override
        public void show() {
            System.out.println("show in interface");
        }
        public static void main(String[] args) {
            inner inner = new inner();
            inner.show();
        }
    }
}

10.7.2多重内部类

一个内部类嵌套多少层不重要,意思是可以嵌套任意个.但是都是会关联前一个父对象.

10.8为什么需要内部类

内部类可以继承或者实现某些接口,内部类可以操作和访问外围类对象.所以说内部类提供了某种进入外围类的窗口.内部类可以独立继承一个(接口)的实现,与外围类是否已经继承没有关系.
用内部类解决需要多重继承(继承多个具体类或抽象类)问题.
如果不需要解决”多重继承”的问题可以使用其他方法.

内部类还有一些特性:
1.内部类可以有多个实例,并且独立,保持和外围类信息相互独立.
2.单个外围类 可以让多个内部类以不同的方式实现同一接口,或继承同一类.
3.创建内部类的时刻,不依赖外围类对象的创建.(内部类和外围类是同时创建的)
4.对于实现接口或者继承关系,内部类不需要使得外围类符合is-a关系,只需要内部类符合即可.(例如在 迭代器模式中的Sequence 和selector的关系)

10.8.1 闭包和回调(同一个意思的不同说法)

10.9内部类的继承

继承内部类的构造器必须指向外围对象的引用,在继承内部类的时候会比较复杂.问题在于:指向外围对象的秘密引用必须初始化,而导出类中不存在可链接的默认对象.必须采用特殊的语法.
在导出类构造函数加外围类对象,并且 .super();

package tinking_in_java.innerClass;
/**
 * Created by leon on 17-12-16.
 */
class WithInner {
    public class inner {
    }
}
public class InnerHertInner {
    public class InnerHerit extends WithInner.inner {
        public InnerHerit(WithInner withInner) {
            withInner.super();
        }
    }
    public static void main(String[] args) {
        InnerHertInner innerHertInner = new InnerHertInner();
    }
}

10.10内部类可以覆盖吗?

他们是独立的类,不会有影响.

相关文章

  • 内部类

    成员内部类 局部内部类(定义在方法内和定义在作用域内的类) 匿名内部类

  • Java 内部类

    内部类包括成员内部类、方法内部类、*静态内部类、匿名内部类*。 内部类的作用 由于内部类的实现和外部类没有关系,内...

  • Java学习——内部类

    内部类 一,成员内部类(包括静态内部类和非静态内部类) 非静态内部类可以直接访问外部类的成员,反之则不行 非静态内...

  • Java 内部类、静态内部类、方法内部类(未完待续)

    内部类 什么是内部类?内部类其实就是在一个类中创建的类。内部类有四种: 成员内部类 静态内部类 局部内部类 匿名内...

  • java 内部类

    一般有四种内部类的使用方式: 嵌套的内部类 方法内的内部类 静态内部类 匿名内部类 什么时候用内部类: 有时候明显...

  • 内部类

    内部类 1.可以访问访问其外部类所有属性和方法,无需创建外部类对象 2.必须创建内部类对象,否则无法从外部类访问内...

  • Java 中的方法内部类

    Java 中的方法内部类 方法内部类就是内部类定义在外部类的方法中,方法内部类只在该方法的内部可见,即只在该方法内...

  • java——内部类

    内部类 定义: 内部类是指在一个外部类的内部再定义一个类。内部类作为外部类的一个成员,并且依附于外部类而存在的。内...

  • java之面向对象5

    方法内部类: 简而言之就是定义在外部类的方法内的类。 方法内部类只在该方法的内部可见,即只在该方法内可以使用。 方...

  • Java 内部类

    1. 内部类 概念:在一个类的内部再定义一个完整的类内部类分为 4 类:成员内部类、静态内部类、局部内部类、匿名内...

网友评论

      本文标题:第十章 内部类

      本文链接:https://www.haomeiwen.com/subject/okonwxtx.html