• 第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

  • 相关阅读:
    C++ and OO Num. Comp. Sci. Eng.
    C++ and OO Num. Comp. Sci. Eng.
    C++ and OO Num. Comp. Sci. Eng.
    C++ and OO Num. Comp. Sci. Eng.
    C++ and OO Num. Comp. Sci. Eng.
    C++ and OO Num. Comp. Sci. Eng.
    WPF 应用程序使用程序的模型视图 ViewModel 设计模式 WVVM
    穿透Session 0 隔离(二)(How to use Windows service run a GUI application.)
    穿透Session 0 隔离(一)(how to use Windows service run a GUI application.)
    .Net 远程路径权限访问。
  • 原文地址:https://www.cnblogs.com/Pikzas/p/11341893.html
Copyright © 2020-2023  润新知