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

抽象类和抽象方法

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

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

  1. 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. 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. 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. 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. 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. 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. 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. 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. 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. leetcode51. N-Queens

    The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens ...

  2. PRCR-1065 Failed to stop resource ora.asm 处理

    在网上看到的一些关闭Oracle Grid Infrastructure教程中,很多在关闭数据后就开始关闭ASM,结果提示如下的错误. [grid@rhvm1 ~]$ srvctl stop asm ...

  3. hdu4691 Front compression ——暴力 || 后缀数组

    link:http://acm.hdu.edu.cn/showproblem.php?pid=4691 暴力,数据明显太水了吧,n=10^5, O(n^2)的复杂度哎喂.想让大家暴力写直接让n=100 ...

  4. JavaWeb用Jdbc操作MySql数据库(一)

    一.添加开发包.在JavaWeb中用jdbc操作数据库,使用方法与java一样,但是在处理开发包的问题上有点差别.JavaWeb不能将mysql-connector-java-5.1.7-bin.ja ...

  5. ps颜色模式

    HSB(hue.saturation.bright)  基于人眼 RGB 基于光 CMYK 基于色 LAB 基于大自然颜色库(理论)

  6. spring boot Swagger 集成

    1. pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://ww ...

  7. PageRank与TrustRank影响因素分析

    PageRank(PR)里的page不是指网页,而是指Google创始人拉里?佩奇(Larry Page),是他在2001年申请的专利中以自己名字命名的,Google的PageRank根据网站的外部链 ...

  8. ruby md5加签验签方法

    # md5签名def md5_sign(data,key) return OpenSSL::Digest::MD5.hexdigest(data+key)end # md5验签def md5_veri ...

  9. 随机数生成器console

    #include <stdio.h> #include <stdlib.h> #include <time.h> int main() { ]; ]; int i; ...

  10. ADF_Starting系列7_使用EJB/JPA/JSF通过ADF构建Web应用程序之创建UI View

    2013-05-01 Created By BaoXinjian