接口和抽象类为我们提供了更强又有力的接口和实现分离的方法.

抽象类和抽象方法

抽象类的目的是为了给子类提供一个通用接口,通过这样一个通用的接口,可以操纵一系列的子类. 也就是说,只需要通过相同的接口调用就可以操作不用的实现. 这也就是所谓的接口和实现的分离.

抽象类简单来说就是包含抽象方法的类. 那么,要了解抽象类就得先了解抽象方法. abstract void f();像这样的由abstract修饰的方法叫做抽象方法. 抽象方法只有返回值类型和方法签名,没有方法体. 抽象类同样适用abstract修饰,并且抽象类不能实例化.

1.abstract class Instrument{
2.
3. abstract public void paly(Note m);
4. private void test1(){
5. System.out.println("Instrument");
6. }
7.}
8.class Wind extends Instrument{
9. public void paly(Note m){
10. System.out.println("Wind play()");
11. }
12.
13. public void test1(){
14. System.out.println("111");
15. }
16.}
17.
18.public class Music {
19. public static void tune(Instrument i){
20. i.paly(Note.C_SHARP);
21. }
22. public static void main(String[] args) {
23. Wind flute = new Wind();
24. tune(flute);
25. }
26.}

抽象类作为基类,对子类中需要改变的方法仅提供abstract方法,对于其他方法,可以正常提供方法体,算是一种介于接口和正常类之间的一种中庸的方案. 抽象类使类的抽象性明确起来了. 抽象类是很重要的重构工具,因为它可以容易的将公共方法沿着继承层次结构向上移动.(埃大爷说的好学院范儿…)

接口

如果一个抽象类中的方法都是抽象方法,那么它的作用就更抽象了. 这么一种纯粹的抽象类,它只提供一个类型的接口定义,没有任何实现. 那么这么一种抽象的令人发指的抽象类如果不给它定义成一个新的概念简直天理难容. 于是这种玩意儿就叫做接口. 它只提供一个类的接口定义于是就被粗暴的叫成了接口… 注意这个接口是java的一种类型机制,平时所说的类的接口是指类中暴露出的可以使用的方法. 可以说接口是用用来被建立类和类之间的协议. (有些语言会直接使用protocol完成这一功能)

interface不仅仅是一个纯粹的抽象类,它允许人们通过创建一个能够被向上转型的多种基类的类型. 除了这个,interface还有一个功能就是可以实现类似多继承的功能. 需要注意的几个点:

  • 接口中定义的方法,隐式添加 public static final
  • 接口中可以定义成员变量,默认都是static final的
  • 要让一个类实现一个接口需要使用implements关键字
  • 实现一个接口的类必须要实现这个接口中定义的所有方法.
  • 实现一个接口的类,可以定义自己的public方法. 也就是说实现一个interface的类对外提供的接口是所实现interface的子集.
1.interface Instrument2{
2. int Value = 5;
3. void paly(Note n);
4. void adjust();
5.}
6.
7.public class Wind2 implements Instrument2{
8. public void paly(Note n ){
9. System.out.println(this+".play() "+n);
10. }
11. public void adjust(){
12. System.out.println(this+".adjust()");
13. }
14. public String toString(){
15. return "Wind";
16. }
17. public void foo1(){
18. System.out.println("hoho");
19. }
20.
21. public static void main(String[] args) {
22. Wind2 wind = new Wind2();
23. wind.adjust();
24. wind.paly(Note.B_FLAT);
25. wind.foo1();
26. System.out.println(Instrument2.Value);
27. }
28.}

完全解耦和策略模式

1.import java.util.Arrays;
2.
3.class Processor{
4. public String name(){
5. return getClass().getSimpleName();
6. }
7. Object process(Object input){
8. return input;
9. }
10.}
11.
12.class UpCase extends Processor{
13. String process(Object input){
14. return ((String)input).toUpperCase();
15. }
16.}
17.
18.class LowerCase extends Processor{
19. String process(Object input){
20. return ((String)input).toLowerCase();
21. }
22.}
23.
24.class Splitter extends Processor{
25. String process(Object input){
26. return Arrays.toString(((String)input).split(" "));
27. }
28.}
29.
30.public class Apply {
31. public static void process(Processor p,Object s){
32. System.out.println("using Processor "+p.name());
33. System.out.println(p.process(s));
34. }
35. public static String s = "hello world";
36. public static void main(String[] args) {
37. Apply.process(new UpCase(), s);
38. Apply.process(new LowerCase(), s);
39. Apply.process(new Splitter(), s);
40. }
41.}

上面是一个经典的策略模式,同一个方法,根据入参不同会有不同的行为. 所谓见人说人话,见鬼说鬼话. 注意实现方式,本质其实就是多态. 策略就是传入的参数的对象.它包含所要执行的代码.

但是看下面这个例子

1.public class Waveform {
2. private static long counter;
3. private final long id = counter++;
4. public String toString(){
5. return "Waveform "+id;
6. }
7.}
8.//=========================================================
9.public class Filter {
10. public String name(){
11. return getClass().getSimpleName();
12. }
13. public Waveform process(Waveform input){
14. return input;
15. }
16.}
17.//=========================================================
18.public class LowPass extends Filter{
19. double cutoff;
20.
21. public LowPass(double cutoff){
22. this.cutoff=cutoff;
23. }
24. public Waveform process(Waveform input){
25. return input;
26. }
27.}
28.//=========================================================
29.public class HighPass extends Filter{
30. double cutoff;
31.
32. public HighPass(double cutoff) {
33. this.cutoff = cutoff;
34. }
35. public Waveform process(Waveform input) {
36. return input;
37. }
38.}
39.//=========================================================
40.public class BandPass extends Filter{
41. double lowCutoff,HighCutoff;
42.
43. public BandPass(double lowCutoff,double HighCutoff){
44. this.HighCutoff=HighCutoff;
45. this.lowCutoff=lowCutoff;
46. }
47. public Waveform process(Waveform input){
48. return input;
49. }
50.}
51.

上面这段代码就会有个问题,Filter和Processor虽然有相同的接口元素,但是他并非继承自Processor. 因为Filter的创建者可能并不知道你想将其用作processor. 这样就不能通过Apply.process()来调用了. 虽然也有办法可以正常运行,但是processor和Apply.process()之间的耦合太紧. 想复用Apply.process()的时候就出现问题. 所以有个办法就是把processor抽象为一个接口,然后处理不同问题的时候,再去实现这个接口.

1.public interface Processor{
2. public String name();
3. public Object process(Object input);
4.}
5.//=========================================================
6.import java.util.Arrays;
7.public abstract class StringProcessor implements Processor {
8. public String name() {
9. return getClass().getSimpleName();
10. }
11.
12. public abstract String process(Object input);
13.
14. public static String s = "hello world";
15.
16. public static void main(String[] args) {
17. Apply.process(new UpCase(), s);
18. Apply.process(new LowerCase(), s);
19. Apply.process(new Splitter(), s);
20. }
21.}
22.
23.class UpCase extends StringProcessor {
24.
25. public String process(Object input) {
26. return ((String) input).toUpperCase();
27. }
28.
29.}
30.
31.class LowerCase extends StringProcessor {
32. public String process(Object input) {
33. return ((String) input).toLowerCase();
34. }
35.}
36.
37.class Splitter extends StringProcessor {
38. public String process(Object input) {
39. return Arrays.toString(((String) input).split(" "));
40. }
41.}
42.//=========================================================
43.public abstract class FilterProcessor implements Processor{
44.
45. @Override
46. public String name() {
47.
48. return getClass().getSimpleName();
49. }
50. @Override
51. abstract public Waveform process(Object input);
52.
53. public static Waveform w = new Waveform();
54. public static void main(String[] args) {
55. Apply.process(new HighPass(1222.222), w);
56. Apply.process(new LowPass(1222.222), w);
57. Apply.process(new BandPass(1222.222,3333.3333), w);
58. }
59.}
60.//=========================================================
61.public class HighPass extends FilterProcessor{
62.
63. double cutoff;
64.
65. public HighPass(double cutoff) {
66. this.cutoff = cutoff;
67. }
68. @Override
69. public Waveform process(Object input) {
70. return (Waveform)input;
71. }
72.}
73.class LowPass extends FilterProcessor{
74.
75. double cutoff;
76.
77. public LowPass(double cutoff){
78. this.cutoff=cutoff;
79. }
80. @Override
81. public Waveform process(Object input){
82. return (Waveform)input;
83. }
84.}
85.class BandPass extends FilterProcessor{
86. double lowCutoff,HighCutoff;
87.
88. public BandPass(double lowCutoff,double HighCutoff){
89. this.HighCutoff=HighCutoff;
90. this.lowCutoff=lowCutoff;
91. }
92. @Override
93. public Waveform process(Object input) {
94. return (Waveform)input;
95. }
96.}

如果有时候碰到了一些你想使用,但是又不能修改的类. 那么就可以使用一种新的模式,适配器模式. 适配器中的代码将接受你所拥有的接口,并且产生你需要的接口.

1.class FilterAdapter implements Processor{
2. Filter filter;
3. public FilterAdapter(Filter filter){
4. this.filter= filter;
5. }
6. @Override
7. public String name(){
8. return filter.name();
9. }
10. @Override
11. public Waveform process(Object input){
12. return filter.process((Waveform)input);
13. }
14.}
15.
16.public class NewFilterProcessor{
17. public static void main(String[] args) {
18. Waveform w = new Waveform();
19. Apply.process(new FilterAdapter(new LowPass(1.0)), w);
20. Apply.process(new FilterAdapter(new HighPass(2.0)), w);
21. Apply.process(new FilterAdapter(new BandPass(1.0,2.0)), w);}
22.}


(这个生成UML的插件貌似有bug,还是我对UML的理解有bug,总感觉哪里不对劲…)

像这个样子,FilterAdapter接受processor的接口,然后生成我们需要的process()接口.这是应该是适配器模式的重要作用之一.
还有一个用途, 比如ContainerAdapter这个类,实现了ContainerListener接口,但是所有方法都是空并未提供实际的方法代码.那这种Adapter的作用很巧妙,这样我们只需要继承这个类,就等于实现了这个接口,于此同时可以只实现自己感兴趣的方法. 而不像直接实现接口那样即使只对其中部分方法感兴趣也要实现所有的方法.

interface从具体实现中解耦,使得接口可以应用于多种不同的具体实现,因此代码就更具有可复用性.

java中的多继承
C++中允许多重继承,即一个子类可以有多个基类.这样会造成一些混淆. java中禁止了这种方式的多重继承. java中的类,一个子类只能有一个基类,但是是可以实现多个接口的.

接口间的继承关系

接口之间是可以存在继承关系的,通过继承,可以扩展一个interface的所拥有的接口.

1.interface Monster {
2. void menace();
3.}
4.
5.interface DanderousMonster extends Monster {
6. void destroy();
7.}
8.
9.interface Lethal {
10. void kill();
11.}
12.
13.class DragonZilla implements DanderousMonster {
14.
15. @Override
16. public void menace() {
17. // TODO Auto-generated method stub
18.
19. }
20.
21. @Override
22. public void destroy() {
23. // TODO Auto-generated method stub
24.
25. }
26.
27.}
28.
29.interface Vampire extends DanderousMonster, Lethal {
30. void drinkBlood();
31.}
32.
33.class VeryBadVampire implements Vampire {
34.
35. @Override
36. public void destroy() {
37. // TODO Auto-generated method stub
38.
39. }
40.
41. @Override
42. public void menace() {
43. // TODO Auto-generated method stub
44.
45. }
46.
47. @Override
48. public void kill() {
49. // TODO Auto-generated method stub
50.
51. }
52.
53. @Override
54. public void drinkBlood() {
55. // TODO Auto-generated method stub
56.
57. }
58.
59.}

组合接口时的名字冲突问题
这是一个可能会遇到的问题, 不同的接口中的两个方法,方法名一样但是,签名或者返回类型不同. 那这种时候会发生什么事情呢?
看一段让人心碎的代码

1.interface I1{
2. public void f();
3.}
4.interface I2{
5. public int f(int i);
6.}
7.interface I3{
8. public int f();
9.}
10.class C{
11. public int f(){
12. return 2;
13. }
14.}
15.
16.
17.class C2 implements I1,I2{
18.
19. @Override
20. public int f(int i) {
21. return 0;
22. }
23.
24. @Override
25. public void f() {
26. }
27.}
28.
29.class C3 extends C implements I2{
30.
31. @Override
32. public int f(int i) {
33. return 0;
34. }
35.
36.}
37.
38.class C4 extends C implements I3{
39. @Override
40. public int f(){
41. return 3;
42. }
43.}
44.
45.//The return types are incompatible for the inherited methods I1.f(), C.f()
46.class C5 extends C implements I1{
47.
48.}

其实从最简单的角度去理解,interface实现,类继承,反正无论多少东西混到一起,最终的作用效果就是,构建一个类的接口(方法).也就是说所有的方法最终都是要进到一个类中去 所以,方法名相同的时候,可不可以的衡量标准其实就是方法能不能重载.

但是,组合不同接口的时候,方法名相同可能会造成代码的可读性下降. 最好还是不要这么搞.

工厂模式

工厂方法模式与直接调用构造器不同. 我们在工厂对象上调用创建的方法. 这样做的好处是,代码完全与接口实现分离. 像下面这段代码中. 要创建不同的对象,只需要调用同一个方法Factories.serviceCustomer()方法即可.
工厂模式在创建框架的过程中使用的最多.

1.interface Service{
2. void method1();
3. void method2();
4.}
5.interface ServiceFactory{
6. Service getService();
7.}
8.
9.class Implementation1 implements Service{
10.
11. @Override
12. public void method1() {
13. System.out.println("111:method1");
14. }
15.
16. @Override
17. public void method2() {
18. System.out.println("111:method2");
19. }
20.}
21.
22.class Implementation2 implements Service{
23.
24. @Override
25. public void method1() {
26. System.out.println("222:method1");
27. }
28.
29. @Override
30. public void method2() {
31. System.out.println("222:method2");
32. }
33.}
34.
35.class Implementation1Factory implements ServiceFactory{
36.
37. @Override
38. public Service getService() {
39. return new Implementation1();
40. }
41.
42.}
43.
44.class Implementation2Factory implements ServiceFactory{
45.
46. @Override
47. public Service getService() {
48. return new Implementation2();
49. }
50.
51.}
52.
53.public class Factories {
54. public static void serviceCustomer(ServiceFactory fact){
55. Service s = fact.getService();
56. s.method1();
57. s.method2();
58. }
59.
60. public static void main(String[] args) {
61. Factories.serviceCustomer(new Implementation1Factory());
62. Factories.serviceCustomer(new Implementation2Factory());
63. }
64.}/*out
65.111:method1
66.111:method2
67.222:method1
68.222:method2
69.*/

乱七八糟不知道怎么归类的知识点

接口中的域
接口中是可以声明成员变量的,接口中的所有变量默认就是static fianl的. 所以在1.5之前,使用interface来构建枚举类型是个不错的选择.
接口中变量的域不能是空的final类型. 但是可以通过非常量表达式赋值.

接口的嵌套
接口是可以相互嵌套的. 但是实在是没看懂. 以后再说吧.

TJI读书笔记12-接口的更多相关文章

  1. TJI读书笔记16-异常处理

    TJI读书笔记16-异常处理 概念 基本异常情形 异常的捕获 自定义异常 异常说明 捕获所有异常 栈轨迹 重新抛出异常 Java标准异常 使用finally 异常的限制 构造器 异常的匹配 其他乱七八 ...

  2. TJI读书笔记15-持有对象

    TJI读书笔记15-持有对象 总览 类型安全和泛型 Collection接口 添加元素 List 迭代器 LinkedList 栈 Set Map Queue Collection和Iterator ...

  3. TJI读书笔记14-闭包与回调

      TJI读书笔记14-闭包与回调 闭包与回调 为什么要使用内部类?内部类继承自某个类或者实现某个接口,内部类的代码可以操作外嵌类的对象. 这不是使用内部类的理由. 那么为什么使用内部类呢? 我觉得如 ...

  4. TJI读书笔记13-内部类

    TJI读书笔记13-内部类 TJI读书笔记13-内部类 创建内部类 内部类和外部类的关系 .this和.new 内部类和向上转型 局部内部类 匿名内部类 匿名内部类的定义和初始化 使用匿名内部类来实现 ...

  5. TJI读书笔记17-字符串

    TJI读书笔记17-字符串 不可变的String 重载”+”和StringBuilder toString()方法的一个坑 String上的操作 格式化输出 Formatter类 字符串操作可能是计算 ...

  6. TJI读书笔记11-多态

    TJI读书笔记11-多态 再说说向上转型 多态的原理 构造器和多态 协变返回类型 使用继承进行设计 多态是数据抽象和继承之后的第三种基本特征. 一句话说,多态分离了做什么和怎么做(再次对埃大爷佩服的五 ...

  7. TJI读书笔记10-复用类

    TJI读书笔记10-复用类 组合语法 继承语法 代理 final关键字 final的数据 final的参数 final的方法 final的类 初始化和类的加载 乱七八糟不知道怎么归类的知识点 代码复用 ...

  8. TJI读书笔记09-访问控制权限

    TJI读书笔记09-访问控制权限 包,package和import 权限修饰符 接口和实现 类的访问权限控制 首先问一个问题,为什么要有访问控制权限? 安全,这当然是一个很重要的原因. 让类库的使用者 ...

  9. TJI读书笔记07-初始化

    TJI读书笔记07-初始化 成员初始化 构造方法初始化 初始化块 初始化的顺序 成员初始化 java尽量去保证每个变量在使用前都会得到初始化. 对于方法局部变量,java不会自动初始化他们,如果没有显 ...

随机推荐

  1. 解决R速度太慢问题

    R的速度慢一直被人诟病,最近做一个比较大的dataset的分析,跑得实在太慢,发现症结是R的data frame的index太慢: 以下为测试: gene_list = 1:100000 eQTL_m ...

  2. python数据结构与算法——栈

    # 栈# 其实python里面的list就可以当栈使用啦,用collections.deque也可以# 1. 入栈 list.append(item)# 2. 出栈 item = list.pop() ...

  3. Windows 下的 Sublime Text 2 配置汇总, Sublime Text 3 亦可借鉴

    1)软件下载地址:http://www.sublimetext.com/2 2)安装 Package Control ,方便安装和管理插件,网络资源很多,这里附上一篇:http://www.imjef ...

  4. RobotFramwork + Appium+ Andriod 环境搭建

    RF+Appium+Android环境搭建教程 - 1.RF安装 一.适用操作系统 Win7 旗舰版Sp1 32位操作系统 RF环境搭建,请参考文档<RobotFramwork安装指南> ...

  5. [转载]BT656/BT601/BT1120协议

     [转载] BT656/BT601/BT1120协议以及DM365/DM355/DM6467上使用的YUV颜色空间说明   ITU-R BT.601和ITU-RBT.656国际电信联盟(Interna ...

  6. Discovering the Computer Science Behind Postgres Indexes

    This is the last in a series of Postgres posts that Pat Shaughnessy wrote based on his presentation ...

  7. Squid代理服务器的安装与配置

    一.简介 代理服务器英文全称是Proxy Server,其功能就是代理网络用户去取得网络信息. Squid是一个缓存Internet 数据的软件,其接收用户的下载申请,并自动处理所下载的数据.当一个用 ...

  8. Python copy and deepcopy

    Python中的对象之间赋值时是按引用传递的,如果需要拷贝对象,需要使用标准库中的copy模块. 1. copy.copy 浅拷贝 只拷贝父对象,不会拷贝对象的内部的子对象. 2. copy.deep ...

  9. tomcat配置及优化

    jdk安装 su - root mkdir jdk cd jdk wget https://mirror.its.sfu.ca/mirror/CentOS-Third-Party/NSG/common ...

  10. js call()和apply()

    一.call()和apply(),实例如下: function add(a,b) {     alert(a+b); } function sub(a,b) {     alert(a-b); } a ...