【Java编程思想】第十章 内部类

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/chuxue1989/article/details/88322711

一、为什么要内部类

我认为内部类的出现

  • 第一解决类和类之间的关系,让类之间模块性、关联性、可阅读性增强
  • 因此内部实现细节
  • 匿名内部类,让代码更简洁

转载一篇文章:内部类的应用


三、内部类的访问权限

package com.hfview.AccessPermission;

public interface ICpu {
    void showName();
}

public interface IHardDisk {
    void showName();
}

public interface IMotherboard {

    void showName();
}


/**
 * 内部类的访问权限
 *
 * @author: zhw
 * @since: 2019/3/7 15:44
 */
public class Pc {

    static int disk = 2;

    private class Cpu implements ICpu{

        //Inner classes cannot have static declaration
        //static int aa = 1;
        @Override
        public void showName() {
            System.out.println("cpu");
        }
    }

    public Cpu cpu(){
        return new Cpu();
    }

    protected class HardDisk implements IHardDisk{

        @Override
        public void showName() {
            System.out.println("harddisk");
        }
    }

    public HardDisk hardDisk(){
        return new HardDisk();
    }

    public class Motherboard implements IMotherboard{

        @Override
        public void showName() {
            System.out.println("montherboard");
        }
    }

    public Motherboard motherboard(){
        return new Motherboard();
    }
}

package com.hfview.AccessPermission;

/**
 * //TODO 写注释
 *
 * @author: zhw
 * @since: 2019/3/7 15:47
 */
public class Main {

    public static void main(String[] args) {
        Pc pc = new Pc();

        ICpu cpu = pc.cpu();
        //pc.new Cpu(); 这里会报错,private只要在Pc内部才可以访问,这种方式算是对内部类完全隐藏
        //只能访问接口中的方法,想访问内部类的成员是不可能的

        cpu.showName();

        IHardDisk hardDisk = pc.hardDisk();
        //pc.new HardDisk();//protected PC内部、PC的子类、相同的包
        hardDisk.showName();


        IMotherboard motherboard = pc.motherboard();
        //pc.new Motherboard();//public 所有地方
        motherboard.showName();
    }

}

大概理解private、默认、protected、public,接可以理解。

三、内部类可以访问外部类所有成员

这个是建立内部类的好处之一,内部类可以和外部类紧密通信。为什么?

创建内部类(非静态内部类)的时候,内部来会隐式的包含外部类的对象,就是利用这个钥匙来完成通信的。

所以创建内部类的时候,首先需要一个外部类对象,才能创建内部类。

package com.hfview.DoThis;

/**
 * 内部类的创建和内部类返回外部类对象
 *
 * @author: zhw
 * @since: 2019/3/7 15:05
 */
public class Demo1 {

    public void f(){
        System.out.println("outer f()");
    }

    class Inner{

        public Demo1 outer(){
            return Demo1.this;
        }
    }

    public Inner inner(){
        return new Inner();
    }

    public static void main(String[] args) {
        Demo1 demo1 = new Demo1();
        Inner inner = demo1.inner();//第一种创建方式,通过外部类的方法来返回

        //or Inner inner1=demo1.new Inner(); //第二种方式,这个就是 .new语法

        Demo1 demo11 = inner.outer();
        demo11.f();

        System.out.println(demo1==demo11);
    }
}

比如第二种创建方式 .new语法: 外部类对象.new 内部类名称,这样内部类就有了外部类的引用。

.this语法:如果通过内部类想显示的获取外部类的引用。做法是 外部类.this

   //这个结果是true,说明通过内部类显示的获取外部类引用和创建内部类的外部类对象是同一个
   System.out.println(demo1==demo11);

四、内部类与向上转型

内部类可以实现某个接口,然后向上转型成该接口,该接口是公用的,但是内部类如果是private,这样内部类针对客户端来说就是完全隐藏的,做到真正的隐藏细节

public interface IContents {
    int value();
}

public interface IDestination {
    String readLabel();
}

/**
 * 内部类与向上转型。
 *
 * @author: zhw
 * @since: 2019/3/7 15:25
 */
public class Parce14 {

    private class Contents implements IContents{

        @Override
        public int value() {
            return 0;
        }
    }

    protected class Destination implements IDestination{

        @Override
        public String readLabel() {
            return "Destination";
        }
    }

    public IContents contents(){
        return new Contents();
    }

    public IDestination destination(){
        return new Destination();
    }
}

public class Main {

    public static void main(String[] args) {
        Parce14 parce14 = new Parce14();
        IContents contents = parce14.contents();
        IDestination destination = parce14.destination();

        System.out.println(contents.value());

        System.out.println(destination.readLabel());
    }
}

比如接口引用 contents ,这个在客户端调用的时候根本就不能向下转型成类Contents,完全实现了实现细节的隐藏。

五、方法和作用域中的内部类

package com.hfview.Method;

import com.hfview.Upcase.IDestination;

/**
 * 局部内部类:感觉这个东西就是辅助你解决的一个结构。
 * 而且是你不想被别人调用的。
 * 它和成员内部类主要区别就是,作用域更小
 * 还有就是他可以有构造方法,可以有多个构造方法,如果你不想用内部类的初始化来模拟构造方法,可以采用局部内部类
 *
 * @author: zhw
 * @since: 2019/3/7 16:17
 */
public class Method1 {

    public IDestination destination(){

        return new IDestination() {
            @Override
            public String readLabel() {
                return "destination";
            }
        };
    }

    public static void main(String[] args) {
        Method1 method1 = new Method1();
        IDestination destination = method1.destination();
        System.out.println(destination.readLabel());
    }

}

六、匿名内部类

匿名内部类:定义就开始创建对象,而且该结构就用一次

package com.hfview.anonymous;

import com.hfview.Upcase.IContents;

/**
 * 匿名内部类,类创建的时候就生成对象,而且不能实现多个接口
 * @author: zhw
 * @since: 2019/3/7 16:39
 */
public class Parce7 {

    public IContents contents(){
        return new IContents() {
            @Override
            public int value() {
                return 10;
            }
        };
    }

    public static void main(String[] args) {
        Parce7 parce7 = new Parce7();
        IContents contents = parce7.contents();
        System.out.println(contents.value());
    }
}

匿名内部类调用有参数构造器

package com.hfview.anonymous;

import com.hfview.Upcase.IContents;

/**
 * 匿名内部类,调用有参构造器(基类是类或者抽象类)
 * @author: zhw
 * @since: 2019/3/7 16:39
 */
public class Parce8 {

    public A a(int i){
        return new A(i){

        };
    }

    public B b(int i){
        final int count =10;//这里必须声明为final(因为它会被内部类直接使用,这里有篇博客有详细的解释
        //主要原因是防止内部类和外部类数据不一致才设置成final的。

        return new B(i){//然后I却不需要声明为final(因为它不会被内部类直接使用,而是传递给了基类)
            int myCount = count;
        };
    }

    private class A{
        int i;
        public A(int i){
            this.i = i;
        }

        public void show(){
            System.out.println(i);
        }
    }

    public static void main(String[] args) {
        Parce8 parce7 = new Parce8();
        A a = parce7.a(10);
        a.show();
    }
}

abstract class B{

    int i;
    public B(int i){
        this.i = i;
    }

    public void show(){
        System.out.println(i);
    }

}

匿名内部类的初始化

package com.hfview.anonymous;

/**
 * 匿名内部类,初始化(不是继承,而是实现接口的情况,接口中没有构造方法,所以不能初始化)
 * @author: zhw
 * @since: 2019/3/7 16:39
 */
public class Parce9 {

    /**
     * 这里切记 变量a的名字不能和count一样,否则赋值不了,这个待研究
     * @param a
     * @param b
     * @return
     */
    public IA ia(final int a,final int b){
        return new IA(){

            int count,price;

            {
                this.count = a;
                this.price = b;
            }

            @Override
            public void show() {
                System.out.println("count:"+count+",price:"+price);
            }
        };
    }

    public B b(){
        return new B(10){

        };
    }

    private interface IA{

        void show();

    }

    public static void main(String[] args) {
        Parce9 parce9 = new Parce9();
        IA ia = parce9.ia(10,20);
        ia.show();
    }
}

七、静态内部类(嵌套类)

普通内部类需要隐式的含有外部类的引用,但是静态内部类在编译阶段已经存在,这个时候就不存在外部类的引用了。

  • 不需要外部类的引用
  • 只能访问静态的外围类成员

我的理解:静态内部类只是为了让内部类具有模块化功能和部分的访问外部类权限。他的使用场景很特殊,比如不需要访问外部类的成员或者只需要访问静态成员。

6.1、接口内部类

接口中的成员都是 public static final 所以如果接口中放入内部类,那么应该也是这样的。
存在意义:
如果你想创建某些公共代码,使用接口内部类会方便?我感觉还不如接口适配器


七、内部类的其他特性

7.1 多层嵌套类中访问外部类成员

class A{
	prvate void f(){};
	class B{
		prvate void f(){};
		class C{
			void h(){
				f();
				g();
			}
		}
	}
}

它能透明的访问所有它所嵌入的外围类的所有成员

7.2 内部类的继承

package com.hfview.extend;

import net.bytebuddy.agent.ByteBuddyAgent;

/**
 * 如果其他类想继承某个内部类,因为内部类需要有外围类的引用,所以一定要初始化外围类
 * 这里必须采用特殊的语法。
 * enclosingClassReference.close()
 *
 * @author: zhw
 * @since: 2019/3/11 09:12
 */
class WithInner {

    public WithInner(){
        System.out.println("WithInner");
    }

    class Inner {
        public Inner(){
            System.out.println("WithInner of Inner");
        }
    }
}

public class InheritInner extends WithInner.Inner{

    InheritInner(WithInner wi){
        wi.super();
    }

    public static void main(String[] args) {
        WithInner wi = new WithInner();
        InheritInner ii = new InheritInner(wi);
    }
}

关于内部类是否可以被覆盖:我感觉覆盖的是方法,类不存在覆盖


八、为什么用内部类

1.类的模块化(最重要)
2. 提供外围类的访问窗口,这样在代码上更简洁、结构更完整(比如给内部类继承一个接口,这样其他类得到这个接口,就可以有效的访问外围类了。类似set/get增强版本)
3. 内部类可以实现多重继承(高级用法)

第三种的说明
如果有些方案确实需要多重继承,那么如果要继承的类是具体的类或者抽象类,只能通过内部类来实现多重继承。

比如第二种的特殊说明
重点说下如果只是需要一个接口的引用,既可以用外围类实现这个接口,也可以用外类类的内部类去实现某个接口。记住如果满足要求,必须考虑用外围类来实现这个接口

什么情况下不满足要求的?

package com.hfview.closure;

/**
 * //TODO 写注释
 *
 * @author: zhw
 * @since: 2019/3/11 10:48
 */
interface Readable{

    void read();
}

class MyReadable{

    void read(){
        System.out.println("我自己特殊的read方法和接口Readable中的read意义不一样");
    }
}

/**
 * Demo已经继承了MyReadable,而且有自己的read方法,这个方法是有特殊意义的。
 * 这个时候就不能实现Readable,而直接实现该接口。
 * 这个时候,就可以采用Demoq的内部类来继承该接口
 *
 */
public class Demoq extends MyReadable{

    class inner implements Readable{

        @Override
        public void read() {
            System.out.println("Readable 接口特殊的read方法");
        }
    }

}

【闭包与回调机制】
可以通过在内部类中的 外部类.this 来回调外围类,通过可以同过向上转型暴露部分回调方法,来实现安全的访问

猜你喜欢

转载自blog.csdn.net/chuxue1989/article/details/88322711