Java 编程思想笔记六:接口和内部类


第9章《接口》和第10章《内部类》主要介绍了抽象类、接口、内部类、匿名类以及嵌套类等概念,以及它们的区别和联系。主要内容如下:
在这里插入图片描述

1.抽象类(abstract)

抽象类也称抽象基类,用于表示所有导出类的共同部分,是普通类和接口间的中庸之道,用关键字 abstract 限定。例如我们之前的“乐器”例子中的 Instrument 类就可以定义为抽象类。

我们可以通过抽象类操作一系列导出类,然而创建抽象类对象则没有什么意义,为防止使用者创建抽象类对象做一些没有意义的事情,Java 提供了抽象方法机制,抽象方法只有声明没有方法体。以创建 Instrument 抽象类为例:

abstract class Instrument {         // 抽象类
    public abstract void play();    // 抽象方法
    public String what() { return "Instrument"; }   // 抽象类中的方法也可以包含方法定义
}

如果一个类包含一个或多个抽象方法,该类必须被限定为抽象类,否则编译会报错。

如果从一个抽象类继承,并想创建该导出类的对象,那么导出类必须要为基类中的所有抽象方法提供方法定义,否则导出类也是抽象类,必须用 abstract 关键字限定。

2.接口(interface)

2.1.接口概念

接口和抽象方法相比更向前迈进一步,interface 关键字会产生一个完全抽象的类,没有提供任何具体实现,只允许创建者确定方法名、参数列表和返回类型,没有任何方法体。以创建 Instrument 接口为例:

interface Instrument {         // 接口
    void play(Note n);
    String what();
}

注意,上面接口中 interface 前面没有加 public 关键字,说明具有包访问权限,该接口只能在同一个包中可用。接口中的方法没有使用 public 关键字限定,但是它们自动就是 public 的

要想让某个类遵循(实现)某个接口,需要用 implements 关键字:

class Wind implements Instrument {
    public void play(Note n) {
        print(this + ".play() " + n);
    }

    public String what() {
        return "Wind";
    }
}

2.2.几种常用设计模式

策略设计模式

创建一个能够根据所传递的参数对象的不同而具有不同行为的方法,即被称为策略设计模式。通过接口即可实现,接着上面的例子,如果想要实现一个 “演奏乐曲” 的方法,不同的乐器演奏出的效果是不同的,可以这样设计:

// Wind 类上面已经实现了 Instrument 接口,这里忽略
class Stringed implements Instrument {
    public void play(Note n) {
        print(this + ".play() " + n);
    }

    public String what() {
        return "Stringed";
    }    
}

// 演奏乐曲
public class PlayMusic {
    String name;
    public void tune(Instrument i) {
        i.play(Note.MIDDLE_C);
    }
}

public static void main(String[] args) {
    PlayMusic player = new PlayMusic();
    player.tune(new Wind());
    player.tune(new Stringed());
}

Wind.play() MIDDLE_C
Stringed.play() MIDDLE_C

策略模式和多态是一个思想,只不过多态是通过继承和方法重写实现的一种语言机制,而策略模式是一种算法,强调做一件事情的不同方法,方法间不能重复。

适配器设计模式

以榨汁机为例,榨汁机(Juicer)可以将水果(Fruits)榨成果汁:

// 水果
public class Fruits {
    private String name;
    private Fruites(String n) { name = n; }
    public string toString() { return "Fruits " + name; }
}

// 榨汁机
public interface Juicer {
    String work(Fruits f);
}

现在我们购买一个九阳榨汁机开始榨汁:

// 九阳榨汁机
public class JiuyangJuicer implements Juicer {
    public String work(Fruits f) { return "Jiuyang juicing: " f.toString; }
}

// 榨汁机开始榨汁
public class JuicerWord {
    JiuyangJuicer juicer = new JiuyangJuicer();
    String result = juicer.work(new Fruits("apple"));
    print(result);
}

Jiuyang juicing: apple

但是问题来了,现在插排只能插两孔插座的榨汁机(老式榨汁机 OldJuicer),九阳榨汁机是三孔的,现在怎么办?-- 我们可以买个适配器(JuicerAdapter):

public class JuicerAdapter implements Juicer {
    JiuyangJuicer juicer;
    public JuicerAdapter(JiuyangJuicer juicer) {
        this.juicer = juicer;
    }
    public String work(Fruits f) { juicer.work("apple"); }
}

public class JuicerAdapterWork {
    JiuyangJuicer juicer = new JiuyangJuicer();                 // 我们的九阳榨汁机
    JuicerAdapter juicerAdapter = new JuicerAdapter(juicer);    // 买的适配器
    String result = juicerAdapter.work(new Fruits("apple"));    // 通过适配器开始榨汁
    print(result);
}

2.3.多重继承

一个类只能继承自一个基类,但是可以继承自多个接口:

interface CanFight { void fight(); }
interface CanSwim { void swim(); }
class ActionCharacter { public void fight() {} }

class Hero extends ActionCharacter implements CanFight, CanSwim {
    public void fight() {}
    public void swim() {}
}

3.内部类

将一个类的定义置于另一个类内部,就是内部类。例如:

public class Parcel {
    class Contents {
        private int i = 11;
        public int value() { return i; }
    }
    class Destination {
        private String lable;
        Destination(String whereTo) {
            lable = whereTo;
        }
        String readLable() { return label; }
    }
    public Destination to(String s) {
        return new Destination(s);      // 返回内部类的引用
    }
    public Contents contents() {
        return new Contents();
    }
    public void ship(String dest) {     // 外围类的内部使用内部类
        Contents c = new Contents();
        Destination d = new Destination(dest);
        System.out.println(d.readLable());
    }
    public static void main(String[] args) {
        Parcel p = new Parcel();        
        p.ship("Tasmania");
        Parcel.Contents c = p.contents();   // 对外围类使用内部类
        Parcel.Destination d = p.to("Borneo");
    }
}

3.1.内部类的特性

1.可以访问外围对象的所有方法和字段,但外围对象不可以访问内部类的方法和元素;
2.如果内部类位于方法的作用域内(即局部内部类),对于外围类的对象是不可见的。

3.2.生成内部类对象

想要创建外围类对象,必须通过创建外围类实现。在拥有外围类之前是不可能创建内部类对象的。因为内部类对象会暗暗地连接到创建它的外围类对象上(但如果你创建的是嵌套类 – 静态内部类,就不需要对外围类对象的引用)。

public class DotThis {
    void f() { System.out.println("DotThis.f()"); }
    public class Inner {
        public DotThis outer() { return DotThis.this; }
    }
    public Inner inner() { return new Inner(); }
    public static void main(String[] args) {
        DotThis dt = new DotThis();             // 1.生成外围类
        DotThis.Inner dti = dt.inner();         // 2.1.方法一:通过外围类生成内部类
        DotThis.Inner dtiN = dt.new Inner();    // 2.2.方法二:通过 .new 生成内部类
        dti.outer().f();                        // 3.内部类调用外围类的方法
    }
}

客户端无法访问 private 内部类,通过这种方式可以完全阻止任何依赖于类型的编码,并且完全隐藏实现的细节。

3.3.匿名类

匿名类是一种继承自某个类或者实现某个接口没有名字的类:

public class Parcel9 {
    public Destination destination(final String dest) {
        return new Destination() {
            private String lable = dest;
            public String readLable() { return lable; }
        };
    }
}

使用匿名类需要注意以下几点:
1.匿名类要使用一个外部定义的对象,为保证内部类和外围类参数的一致性,其参数引用必须是 final 的;
2.使用匿名类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口;
3.匿名类中是不能定义构造函数的;
4.匿部类中不能存在任何的静态成员变量和静态方法;
5.匿名类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效;
6.匿名类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。

3.4.嵌套类

嵌套类是静态内部类,即嵌套类对象和外围类对象之间没有任何联系。所以与普通内部类相比嵌套类具有以下特点:

1.创建嵌套类对象前不需要外围类的对象;
2.不能从嵌套类对象中访问非静态的外围类对象;
3.普通内部类的字段和方法只能放在外部层次上,不能有 static 数据和 static 字段,也不能包含嵌套类,但是嵌套类可以包含所有这些东西。

例如可以使用嵌套类来放置我们的测试代码:

public class TestBed {
    public void f() { System.out.println("f()"); }
    public static class Tester {
        public static void main(String[] args) {    // 嵌套类
            TestBed t = new TestBed();
            t.f();
        }
    }
}

3.5.闭包

内部类这部分提到了闭包的概念,但是我并没有看懂,然后自己在网上看了一些帖子,感觉这篇帖子解释的很明白:

闭包能够将一个方法作为变量去存储 --> Java里将内部类对象向上转型为接口类型即可实现闭包!

3.6.局部内部类

局部内部类创建于方法体内部,可以访问外围类所有成员,但是外围成员不可以访问局部内部类。

interface ILog { void write(); }
public class LocalInnerClass {
    private int length =0;
    public ILog logger() {
        class InnerClass implements ILog {  // 局部内部类
            public void write(String message) {
                length = message.length();
                System.out.println("LocalInnerClass.InnerClass:" + length);
            }
        }
        return new InnerClass();
    }
}

3.7.内部类继承

class WithInner { class Inner() {} }

public class InheritInner extends WithInner.Inner {
    // InheritInner() {}    -- 编译错误
    InheritInner(WithInner wi) { wi.super(); }
}

首先继承时要指明外围类和内部类的关系,其次是构造内部类对象时必须使用 .super 提供指向外围类的引用。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

红薯的Java私房菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值