zoukankan      html  css  js  c++  java
  • 第10章-内部类II

    Think in java 读书笔记

    pikzas

    2019.05.05

    第十章 内部类

    知识点

    1.什么是内部类

    可以将一个类定义在另一个类的内部

    class OuterClass{
        class InnerClass{
            
        }
    }
    

    2.内部类的主要特征是什么

    内部类的主要特征是内部类可以随意访问外部类的属性和方法,不论其访问修饰符是什么

    public interface Selector {
        boolean end();
        Object current();
        void next();
    }
    
    public class Seq {
        private Object[] items;
        private int next = 0;
        public Seq(int size){
            items = new Object[size];
        }
        public void add(Object item){
            if(next < items.length){
                items[next++] = item;
            }
        }
    
        private class SeqSelector implements Selector{
            private int i = 0;
    
            @Override
            public boolean end() {
                return i == items.length;
            }
    
            @Override
            public Object current() {
                return items[i];
            }
    
            @Override
            public void next() {
                if(i<items.length){
                    i++;
                }
            }
        }
    
        public Selector getSelector(){
            return new SeqSelector();
        }
    
        public static void main(String[] args) {
            Seq sq = new Seq(10);
            for (int i =0 ; i < sq.items.length ; i++){
                sq.add(Integer.toString(i));
            }
            Selector selector = sq.getSelector(); //此时,外部类对象的引用会被传递给内部类对象
            while (!selector.end()){
                System.out.println(selector.current());
                selector.next();
            }
        }
    
    }
    
    

    3.适用于内部类的一些特殊语法

    .new 由外部类对象新创建内部类对象

    class Outer{
        class Inner{}
    }
    
    class Demo{
        public static void main(String[] args){
          Outer outer = new Outer();
          Outer.Inner inner = outer.new Inner();
        }
    }
    

    .this 在内部类如果想要获取外部类对象的引用

    class DotThis{
        void f() {System.out.println("DotThis.f()");}
        class Inner{
            public DotThis getOuterObj(){
                return DotThis.this;
            }
        }
        
        public Inner getInner(){
            return new Inner();
        }
        
        public static void main(String[] args){
          DotThis dotThis = new DotThis();
          DotThis.Inner inner = dotThis.getInner();
          System.out.println(inner.getOuterObj().equals(dotThis));  // 输出为true
          inner.getOuterObj().f();  // inner.getOuterObj() 等同于 dotThis
          
        }
    }
    

    .new 语法也可已看出,必须要先有外部类实例,再由这个实例.new 出内部类。(静态内部类除外)

    4.内部类与接口的配合使用

    内部类实现接口,并将内部类访问限定为private或者protected,从而将实现隐藏在该内部类中。

    public interface Dest {
        int value();
    }
    
    public class Demo {
        private class DestImp implements Dest{
            @Override
            public int value() {
                return 0;
            }
        }
    
        public Dest getDest(){
            return new DestImp();
        }
    }
    
    
    public class Test {
        public static void main(String[] args) {
            Demo demo = new Demo();
            Dest dest = demo.getDest();
            dest.value();
        }
    }
    
    

    5.局部内部类

    以上看到的内部类都是直接定义在外部类的作用域内,如果让一个内部类定义在外部类的某个方法内部时,这就称作局部内部类。
    并且当作用域超出了这个方法的时候,外部是访问不到该类的。

    public interface Fruit {
        String desc();
    }
    
    public class Test {
        public Fruit getFruit(){
            class Apple implements Fruit{
                @Override
                public String desc() {
                    return "i am apple";
                }
            }
            return new Apple();
        }
        //   getFruit方法之外,是访问不到Apple类的
        //   private Fruit fruit = new Apple();
    }
    
    

    6.匿名内部类

    上面的局部内部类将Apple的定义和new 创建新对象的语法放在一起就是匿名内部类。如果返回的是具体的类,看起来也就没什么特殊了。

    public interface Fruit {
        String desc();
    }
    
    public class Test2 {
        public Fruit getFruit(){
            return new Fruit(){
                @Override
                public String desc() {
                    return "i am apple";
                }
            };
        }
        public static void main(String[] args) {
            Test2 test2 = new Test2();
            Fruit fruit = test2.getFruit();
            System.out.println(fruit.desc());
        }
    }
    

    6.1.匿名内部类可以是具体类,甚至可以对其方法进行override

    public class Wrapping {
        private int i;
        public Wrapping(int x){
            this.i = x;
        }
        public int value(){
            return i;
        }
    }
    
    public class Outer {
        public Wrapping getItem(int x){
            return new Wrapping(x){
                @Override
                public int value(){
                    return super.value() * 47;
                }
            };
        }
    
        public Wrapping getItem2(int x){
            return new Wrapping(x);
        }
    
        public static void main(String[] args) {
            Outer outer = new Outer();
            Wrapping wrapping = outer.getItem(10);
            Wrapping wrapping2 = outer.getItem2(10);
            System.out.println(wrapping.value());
            System.out.println(wrapping2.value());
        }
    }
    
    ------输出结果------
    470
    10
    

    6.2.对匿名内部类的字段进行初始化

    • 使用外部类传入的数据进行初始化,此时该数据必须为final的
    public interface SampleInterface {
        int value();
    }
    
    
    public class InitDemo {
        public SampleInterface getSample(final int x){  //此处final必须为final的 可以理解为外部对象指向的数据不能变动,否则内部类对象会很困惑。
            return new SampleInterface() {
                private int i = x;
                @Override
                public int value() {
                    return i;
                }
            };
        }
    }
    
    
    • 通过初始化代码块完成类似于构造器的功能
    public abstract class Base {
        public Base(int i){
            System.out.println("base constructor, i = " + i);
        }
        public abstract void f();
    }
    
    public class AnonymousConstructor {
        public static Base getBase(int i){ //此处的变量不用是final的,因为内部类并没用到i
            return new Base(i){
                {
                    System.out.println("inside instance");  // 通过静态代码块可以实现初始化
                }
                public void f(){
                    System.out.println("in inner f()");
                }
            };
        }
    
        public static void main(String[] args) {
            Base base = getBase(47);
            base.f();
        }
    }
    
    

    从上面对于传入内部类的参数是否要加上final的修饰规定是,如果内部类用到了外部传入的那个变量,则需要为final的。
    内部类可以拿来扩展类,也可以拿来实现接口,但是不能两个都做到,也只能一次实现一个接口。

    7.工厂方法的内部类实现方式

    public interface Game {
        boolean move();
    }
    
    public interface GameFactory {
        Game getGame();
    }
    
    public class Chess implements Game {
        private Chess(){};
        private int moves = 0;
        private static final int MOVES = 4;
        @Override
        public boolean move() {
            System.out.println("Chess moves" + moves);
            return ++moves != MOVES;
        }
    
        public static GameFactory factory = new GameFactory(){
            public Game getGame(){
                return new Chess();
            }
        };
    
    }
    
    public class Checkers implements Game {
        private Checkers(){}
        private int moves = 0;
        private static final int MOVES = 3;
    
        @Override
        public boolean move() {
            System.out.println("Checker moves" + moves);
            return ++moves != MOVES;
        }
    
        public static GameFactory factory = new GameFactory() {
            @Override
            public Game getGame() {
                return new Checkers();
            }
        };
    }
    
    public class Test {
        public static void playGame(GameFactory factory) {
            Game game = factory.getGame();
            while(game.move());
        }
        public static void main(String[] args) {
            playGame(Chess.factory);
            playGame(Checkers.factory);
        }
    }
    -------输出结果----
    
    Chess moves0
    Chess moves1
    Chess moves2
    Chess moves3
    Checker moves0
    Checker moves1
    Checker moves2
    
    

    8.嵌套类(也成为静态内部类)

    前面讲到的内部类的创建都依赖于外部类的对象,如果内部类定义的时候是static的,那么外部对象就不需要了。
    同时因为内部类是static的,那么如果想要使用外部类中的属性或者方法,那么那些属性或者方法必须也是static的。

    
    public class Outer {
        private int x = 123;
        private static int y = 999;
        public static class OneImpl implements OneInterface{
            public void f(){
    //            System.out.println("inner class" + x); 此处因为x不是static的,会提示编译错误。
                System.out.println("inner class " + y);
            }
        }
    
        public static void main(String[] args) {
            OneInterface one = new OneImpl();
            one.f();
        }
    }
    

    8.1.嵌套类的应用

    由于接口中的属性和方法默认都是public static的,所以其中可以来嵌套一些需要子类实现都通用的方法。

    public interface DemoInterface {
        void fun();
        class DemoImpl implements DemoInterface{
            public void fun(){
                System.out.println("可以实现我自己的外部接口");
            }
        }
    }
    
    public class DemoTest implements DemoInterface {
        @Override
        public void fun() {
            System.out.println("DemoTest");
        }
    
        public static void main(String[] args) {
            DemoInterface demo = new DemoTest();
            DemoInterface demo2 = new DemoImpl();
            demo.fun();
            demo2.fun();
        }
    }
    
    -----输出结果-----
    DemoTest
    可以实现我自己的外部接口
    

    嵌套类不同于普通的内部类的另一点在于,编译生成的.class文件名称都是形如Outer$Inner.class这个格式。
    而且嵌套类内部还能有嵌套类,以及static声明的属性及方法。

    9.内部类无论内部有多少层,内一层的类都可以无条件的访问外部类

    10.内部类存在的意义

    内部类存在的主要目的是为了解决java没有多继承导致的一些麻烦

    • java可以很好的解决多接口的实现问题,如下例子
    public interface A {
    }
    
    public interface B {
    }
    
    public class X implements A,B {
    }
    
    public class Y implements A {
        B getB(){
            return new B() {};
        }
    }
    
    public class TestOne {
        public static void funA(A a) {
    
        }
    
        public static void funB(B b) {
    
        }
        
        public static void main(String[] args) {
            X x = new X();
            Y y = new Y();
            funA(x);
            funA(y);
            funB(x);
            funB(y.getB());
        }
    }
    
    • 但是对于要求子类同时实现抽象类和父类问题时,只有内部类可以派上用场
    public class M {
    }
    
    public abstract class N {
    }
    
    public class Z extends M {
        N getN(){
            return new N() {};
        }
    }
    
    public class TestTwo {
        public static void funM(M m) {
    
        }
        public static void funN(N n) {
    
        }
        public static void main(String[] args) {
            Z z = new Z();
            funM(z);
            funN(z.getN());
        }
    }
    
    

    11.内部类的继承

    内部类与外部类紧紧联系在一起,那么如果想要继承一个外部类的内部类,就需要使用到特殊的语法 OutClass.super();

    public class DemoOuter {
        class Inner{}
    }
    
    public class ExtInner extends DemoOuter.Inner {
        public ExtInner(DemoOuter demoOuter){
            demoOuter.super(); //必须添加如此这般的构造器并显示指定父类对象引用
        }
    }
    

    12.内部类初始化顺序

    内部类的构造器会先调用,然后才会调用外部类构造器。
    内部类不存在覆盖问题,Egg中的Yolk和BigEgg中的Yolk完全是两个不同的类。

    public class Egg {
        class Yolk{
            public Yolk(){
                System.out.println("Egg York"); // 1 3
            }
            public void f(){
                System.out.println("Egg f()");
            }
        }
        private Yolk york = new Yolk();
        public Egg(){
            System.out.println("Egg"); // 2
        }
        public void insert(Yolk y){
            this.york = y;
        }
        public void g(){
            york.f();
        }
    }
    
    public class BigEgg extends Egg {
        class Yolk extends Egg.Yolk{
            public Yolk(){
                System.out.println("BigEgg Yolk"); // 4
            }
            public void f(){
                System.out.println("BigEgg f()"); // 5
            }
        }
    
        public BigEgg(){ 
            insert(new Yolk());
        }
    
        public static void main(String[] args) {
            BigEgg egg = new BigEgg(); // 0 
            egg.g();
        }
    }
    
    -----输出结果-----
    Egg York
    Egg
    Egg York
    BigEgg Yolk
    BigEgg f()
    

    13.局部内部类和匿名内部类的区别

    前面讲过,将显示的类声明写出来,在返回一个对象的方式叫做局部内部类,但是如果直接new 接口并返回,那么他就是匿名内部类。
    如果某个场景下,需要返回一个接口的多种不同实现,那么只有局部内部类做得到,匿名内部类是做不到的。

    14.内部类标识符

    匿名内部类 --> LocalInnerClass$1.class
    局部内部类 --> LocalInnerClass$Inner.class

  • 相关阅读:
    百度之星资格赛1001——找规律——大搬家
    HDU1025——LIS——Constructing Roads In JGShining's Kingdom
    DP(递归打印路径) UVA 662 Fast Food
    递推DP UVA 607 Scheduling Lectures
    递推DP UVA 590 Always on the run
    递推DP UVA 473 Raucous Rockers
    博弈 HDOJ 4371 Alice and Bob
    DFS(深度) hihoCoder挑战赛14 B 赛车
    Codeforces Round #318 [RussianCodeCup Thanks-Round] (Div. 2)
    DP(DAG) UVA 437 The Tower of Babylon
  • 原文地址:https://www.cnblogs.com/Pikzas/p/11341893.html
Copyright © 2011-2022 走看看