【软件构造】第三章第四节 面向对象编程OOP

第三章第四节 面向对象编程OOP

本节讲学习ADT的具体实现技术:OOP

Outline

  • OOP的基本概念
    • 对象
    • 接口
    • 抽象类
  • OOP的不同特征
    • 封装
    • 继承与重写(override)
    • 多态与重载(overload)
    • 泛型
  • 设计好的类

Notes

## OOP的基本概念

【对象】

  • 对象是类的一个实例,有状态和行为。
  • 例如,一条狗是一个对象,它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等。
  • 概念:一个对象是一堆状态和行为的集合。
    • 状态是包含在对象中的数据,在Java中,它们是对象的fields。
    • 行为是对象支持的操作,在Java中,它们称为methods。

【类】

  • 类是一个模板,它描述一类对象的行为和状态。
  • 每个对象都有一个类
  • 类定义了属性类型(type)和行为实现(implementation)
  • 简单地说,类的方法是它的应用程序编程接口(API)。
  • 类成员变量(class variable)又叫静态变量;类方法(class method)又叫静态方法:
  • 实例变量(instance variable)和实例方法(instance method)是不用static形容的实例和方法;
  • 二者有以下的区别:
    • 类方法是属于整个类,而不属于某个对象。 
    • 类方法只能访问类成员变量(方法),不能访问实例变量(方法),而实例方法可以访问类成员变量(方法)和实例变量(方法)。 
    • 类方法的调用可以通过类名.类方法和对象.类方法,而实例方法只能通过对象.实例方法访问。 
    • 类方法不能被覆盖,实例方法可以被覆盖。
    • 当类的字节码文件被加载到内存时,类的实例方法不会被分配入口地址 当该类创建对象后,类中的实例方法才分配入口地址, 从而实例方法可以被类创建的任何对象调用执行。
    • 类方法在该类被加载到内存时,就分配了相应的入口地址。 从而类方法不仅可以被类创建的任何对象调用执行,也可以直接通过类名调用。 类方法的入口地址直到程序退出时才被取消。
  • 注意:
    • 当我们创建第一个对象时,类中的实例方法就分配了入口地址,当再创建对象时,不再分配入口地址。
    • 也就是说,方法的入口地址被所有的对象共享,当所有的对象都不存在时,方法的入口地址才被取消。
  • 总结:
    • 类变量和类方法与类相关联,并且每个类都会出现一次。 使用它们不需要创建对象。
    • 实例方法和变量会在每个类的实例中出现一次。
  • 举例:

【接口】

  • 概念:接口在JAVA编程语言中是一个抽象类型,用于设计和表达ADT的语言机制,其是抽象方法的集合,接口通常以interface来声明。
  • 一个类通过继承接口的方式,从而来继承接口的抽象方法。
  • 接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
  • 一个接口可以扩展其他接口,一个类可以实现多个接口;一个接口也可以有多重实现
  • 除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
  • 接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。

 一个接口的实例:

/** MyString represents an immutable sequence of characters. */
public interface MyString { 

    // We'll skip this creator operation for now
    // /** @param b a boolean value
    //  *  @return string representation of b, either "true" or "false" */
    // public static MyString valueOf(boolean b) { ... }

    /** @return number of characters in this string */
    public int length();

    /** @param i character position (requires 0 <= i < string length)
     *  @return character at position i */
    public char charAt(int i);

    /** Get the substring between start (inclusive) and end (exclusive).
     *  @param start starting index
     *  @param end ending index.  Requires 0 <= start <= end <= string length.
     *  @return string consisting of charAt(start)...charAt(end-1) */
    public MyString substring(int start, int end);
}

一种实现:

 1 public class FastMyString implements MyString {
 2 
 3     private char[] a;
 4     private int start;
 5     private int end;
 6 
 7     /** Create a string representation of b, either "true" or "false".
 8      *  @param b a boolean value */
 9     public FastMyString(boolean b) {
10         a = b ? new char[] { 't', 'r', 'u', 'e' } 
11               : new char[] { 'f', 'a', 'l', 's', 'e' };
12         start = 0;
13         end = a.length;
14     }
15 
16     // private constructor, used internally by producer operations.
17     private FastMyString(char[] a, int start, int end) {
18         this.a = a;
19         this.start = start;
20         this.end = end;
21     }
22 
23     @Override public int length() { return end - start; }
24 
25     @Override public char charAt(int i) { return a[start + i]; }
26 
27     @Override public MyString substring(int start, int end) {
28         return new FastMyString(this.a, this.start + start, this.end + end);
29     }
30 }

客户端如何使用此ADT?这是一个例子:

MyString s = new FastMyString(true);
System.out.println("The first character is: " + s.charAt(0));

  但其中有问题,这么实现接口打破了抽象边界,接口定义中没有包含constructor,也无法保证所有实现类中都包含了同样名字的constructor。 故而,客户端需要知道该接口的某个具体实现类的名字。因为Java中的接口不能包含构造函数,所以它们必须直接调用其中一个具体类的构造函数。该构造函数的规范不会出现在接口的任何地方,所以没有任何静态的保证,即不同的实现甚至会提供相同的构造函数。

  在Java 8中,我们可以用valueof的静态工厂方法 代替构造器。

public interface MyString { 
    /** @param b a boolean value
     *  @return string representation of b, either "true" or "false" */
    public static MyString valueOf(boolean b) {
        return new FastMyString(true);
    }
    // ...

  此时,客户端使用ADT就不会破坏抽象边界:

MyString s = MyString.valueOf(true);
System.out.println("The first character is: " + s.charAt(0));

总结:接口的好处

  • Safe from bugs 
    ADT是由其操作定义的,接口就是这样做的。 
    当客户端使用接口类型时,静态检查确保他们只使用由接口定义的方法。 
    如果实现类公开其他方法,或者更糟糕的是,具有可见的表示,客户端不会意外地看到或依赖它们。 
    当我们有一个数据类型的多个实现时,接口提供方法签名的静态检查。

  • Easy to understand 
    客户和维护人员确切知道在哪里查找ADT的规约。 
    由于接口不包含实例字段或实例方法的实现,因此更容易将实现的细节保留在规范之外。

  • Ready for change 
    通过添加实现接口的类,我们可以轻松地添加新类型的实现。 
    如果我们避免使用静态工厂方法的构造函数,客户端将只能看到该接口。 
    这意味着我们可以切换客户端正在使用的实现类,而无需更改其代码。

【抽象类】

  • 抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
  • 由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。
  • 父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。
  • 在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
  • 如果一个类包含抽象方法,那么该类必须是抽象类。
  • 任何子类必须重写父类的抽象方法,或者声明自身为抽象类。
  • 构造方法,类方法(用static修饰的方法)不能声明为抽象方法。

## OOP的不同特征

猜你喜欢

转载自www.cnblogs.com/hithongming/p/9170815.html