文章目录
Software Construction of HIT💯
Chapter 7 Object-Oriented Programming (OOP)
1 Basic concepts: object, class, attribute, and method
-
Class variables and class methods are associated with a class and occur once per class. Using them doesn’t require object creation. 类方法中不能调用实例变量
-
Instance methods and variables occur once per instance of a class.
2 Interface
接口中是没有构造器的,为了让客户端不需要知道接口的某个具体类的实现名字,可以在接口中采用静态工厂方法,在其中可以选择一种默认的构造方法。
default方法可以在接口中统一实现一些方法,无需在各个类中重复实现(DRY)。在List这个接口中有以下两个default方法,因此在ArrayList和LinkedList中没有这两个方法。
default void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
ListIterator li = this.listIterator();
while(li.hasNext()) {
li.set(operator.apply(li.next()));
}
}
default void sort(Comparator<? super E> c) {
Object[] a = this.toArray();
Arrays.sort(a, c);
ListIterator<E> i = this.listIterator();
Object[] var4 = a;
int var5 = a.length;
for(int var6 = 0; var6 < var5; ++var6) {
Object e = var4[var6];
i.next();
i.set(e);
}
}
3 Inheritance and Overriding
3.1 Overriding
使用final
修饰子类无法重写超类的方法。
子类不会继承任何父类的构造方法。子类默认的构造方法是编译器自动生成的,不是继承的。
3.2 Abstract Class
如果一个class
定义了方法,但没有具体执行代码,这个方法就是抽象方法,抽象方法用abstract
修饰。因为无法执行抽象方法,因此这个类也必须申明为抽象类(abstract class)。我们无法实例化一个抽象类。
抽象类本身被设计成只能用于被继承,因此,抽象类可以强迫子类实现其定义的抽象方法,否则编译会报错。因此,抽象方法实际上相当于定义了**“规范”**。
面向抽象编程的本质就是:
- 上层代码只定义规范(例如:
abstract class Person
); - 不需要子类就可以实现业务逻辑(正常编译);
- 具体的业务逻辑由不同的子类实现,调用者并不关心。
4 Polymorphism, subtyping and overloading
可以参考这一篇csdn博客多态的分类。
-
特殊多态:同一个名(操作符、函数名)在不同的上下文中有不同的类型。也就是重载。
重载:多个方法具有同样的名字,但有不同的参数列表(必须不同),返回值类型和修饰可同可不同。是静态多态,会进行静态类型检查。
这里要区分一下重写与重载在子类与超类对于方法的调用的区别,可以看到重载的时候对于这样
Animal animalRefToDog = new Dog();
的形式。重载的调用根据的是引用类型Animal
;而重写是根据对象类型Dog
,但是在在这种情况下是不能调用超类中没有的方法的,一定要调用的话只能通过类型强制转化((Dog)ARefToD).bark();
。// 对于一个这样关系的子类和超类 class Animal { public void eat() { System.out.println("Animal eat!"); } } class Dog extends Animal { public void eat(String food) { System.out.println("Dog eat " + food); } public void bark() { System.out.println("Woof!!!"); } } public class UseAnimals { public void doStuff(Animal a) { System.out.println("Animal"); } public void doStuff(Dog h) { System.out.println("Dog"); } } // 有以下的几个实例对象 Animal animalObj = new Animal(); Dog dogObj = new Dog(); Animal animalRefToDog = new Dog(); // ua.doStuff(animalobj); // "Animal" ua.doStuff(dogobj); // "Dog" ua.doStuff(animalRefToDog); // "Animal"
-
参数化多态:采用参数化模板,通过给出不同的类型参数,使得一个结构有多种类型。也就是泛型。
-
子类型多态、包含多态:同样的操作可用于一个类型及其子类型。(注意是子类型,不是子类)包含多态一般需要进行运行时的类型检查。也就是重写,是动态类型检查。
instanceof:Java的一个保留关键字,左边是对象,右边是类,返回类型是Boolean类型。它的具体作用是测试左边的对象是否是右边类或者该类的子类创建的实例对象,是,则返回true,否则返回false。除了在实现相等操作外,不应该使用。
Animal ARefToD = new Dog(); System.out.println(ARefToD instanceof Dog); // true System.out.println(ARefToD instanceof Animal); // true System.out.println(ARefToD instanceof String); // 编译错误
对于一个引用类型是子类的对象,不能把父类变为子类,因为子类功能比父类多,多的功能无法凭空变出来。
5 Some important Object methods in Java
5.1 ToString()
5.2 hashCode()
5.3 equals()
6 Designing good classes
7 History of OOP
Chapter 8 Equality in ADT and OOP
1 Equivalence Relation
自反、对称、传递
2 Equality of Immutable Types
站在外部观察者角度:对两个对象调用任何相同的操作,都会得到相同的结果,则认为这两个对象是等价的。反之亦然!
不可变对象的观察者等价性和行为等价性是完全等价的,因为没有mutator。
3 == vs. equals()
==
是引用等价性equals
是对象等价性
4 Implementing equals()
一个例子:
@Override
public boolean equals(Object o) {
if (!(o instanceof PhoneNumber)) // Does null check
return false;
// 以下这一部分必须严格按照规约来写,实现观察者等价性
PhoneNumber pn = (PhoneNumber) o;
return pn.lineNumber == lineNumber
&& pn.prefix == prefix
&& pn.areaCode == areaCode;
}
5 The Object contract
-
除非对象被修改了,否则调用多次equals应同样的结果
-
non-null reference x , x.equals(null) 返回false
-
“相等”的对象,其hashCode()的结果必须一致;不相等的对象,也可以映射为同样的hashCode,但性能会变差。
重写equals()的时候总是重写hashCode()。除非你能保证你的ADT不会被放入到Hash类型的集合类中。
6 Equality of Mutable Types
observational equality and behavioral equality
-
observational equality:在不改变状态的情况下(针对observers, creator, producer),两个mutable对象是否看起来一致
-
behavioral equality:调用对象的任何方法(相比于immutable多了一个mutators)都展示出一致的结果
-
So immutable types must override both equals() and hashCode()
-
So mutable types should not override equals() and hashCode() at all, 也就是说进行的是行为等价性的判断,只有指向同样内存空间的objects,才是相等的。如果一定要判断两个可变对象看起来是否一致(观察等价性),最好定义一个新的方法,像similar()之类的。
如果某个mutable的对象包含在Set集合类中,当其发生改变后,集合类的行为不确定,务必小心。
7 Autoboxing and Equality
Map<String, Integer> aa = new HashMap<>(), bb = new HashMap<>();
aa.put("bigger", 128); // 传入的int被自动转换为了Integer
bb.put("bigger", 128);
aa.put("smaller", -128);
bb.put("smaller", -128);
System.out.println(aa.get("bigger") == bb.get("bigger")); // false 取出的时候是Integer
System.out.println(aa.get("smaller") == bb.get("smaller")); // true 在[-128,127]取出的时候是int
Integer x = new Integer(2);
Integer y = new Integer(2);
System.out.println(x == y); // false
Integer xx = 2;
Integer yy = 2;
System.out.println(xx == yy); // true 数字大了之后是fasle