继承
- 继承的本质是避免重复
- DRY,事不过三,三便重构
-
继承体系
Java是单根继承,所有类都隐式继承Object类,可以保证所有对象都至少有一些相同的方法
- 单根继承的特性使得Java任何一个类只能继承一个类,反例为C++,其是多重继承的
Object常用方法
子类拥有父类的一切数据和行为
- 先初始化父类
- 静态成员的初始化
- 静态初始化块
- 成员的初始化
- 初始化块
- 执行父类构造器函数 supper()
再初始化子类
一个类可以通过重写父类的方法,实现相同签名的不通逻辑
- 永远要使用@Override注解防止手残,使用编译器帮助检查方法名
- instanceof关键字判断是否是某类的实例
- 改写Obejct equals案例如下 ```java import java.util.Objects;
public class User { private Integer id; private String name;
public User(Integer id, String name) {this.id = id;this.name = name;}public Integer getId() {return id;}public String getName() {return name;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;User user = (User) o;return Objects.equals(id, user.getId()) && Objects.equals(name, user.getName());}@Overridepublic int hashCode() {return Objects.hash(id);}public static void main(String[] args) {System.out.println(new User(1, "user1") == new User(1, "user1"));System.out.println(new User(1, "user1").equals(new User(1, "user1")));System.out.println(new User(1, "user1").equals(new User(2, "user2")));}
}
<a name="s51Tq"></a># 设计模式-模板方法- 按部就班的实现一个逻辑时,可在父类先定义一个模板方法,然后覆盖模板方法的某几个方法,来改变模板方法的逻辑- 在覆盖父类方法的时候,可以使用**supper.methods()**执行父类代码逻辑```java// 父类public class Story {public final void tellStory() {startStory();story();endStory();}public void startStory() {System.out.println("开始讲故事啦");}public void story() {System.out.println("从前有个老和尚");}public void endStory() {System.out.println("故事讲完啦");}public static void main(String[] args) {new Story().tellStory();}}
// 子类public class MonsterStory extends Story {@Overridepublic void story() {System.out.println("从前有个老妖怪");}@Overridepublic void endStory() {super.endStory();System.out.println("你还想听吗");}public static void main(String[] args) {new MonsterStory().tellStory();}}
向上向下转型
- 一个子类型的对象永远是一个父类得对象 Object - Number - Integer
- instanceof 运算符 用于判断一个地址指向的对象是不是一个类的实例(父类也返回true)
- null instanceof ? ==false null不是任何类的实例
- 当需要一个父类型时,总是可以传递一个子类型,因为任何子类对象都是也是父类的实例
向上转型是安全的,向下不安全,可能异常,例如狗是动物,但动物不是狗
final关键字与单例模式
final
final 在变量上只能在初始化时被赋值,后面不能再改写,优点是保证线程是安全的
- 如果final 声明一个地址,只是表示地址无法再改变,但指向的对象中数据是可以改变的
- 声明不可变常量 private static final double PI = 3.1415926,常量命名约定为全大写,使用常量可以声明许多变量的实际意义,比使用一个数字意义更为具体
- final 修饰class 使得该类不可再被继承,例如Integer、Boolean、String;但HashMap不是final
- 如果一个类不准备被继承,一定要用final以防止破坏性事件发生,比如构造一个子类并override父类方法,破坏一些程序约定?
如果一个方法被声明为final,也不可以被继承,也不可以被override,没有多态,可以用于优化jvm性能
单例模式
java最小的工作单元为对象,如果我们需要一个全局的不可变对象(例如一个类只用来创建一个被使用的对象)比如地球对象
public class World {public static final world SINGLETON_INSTANCE = new World();public static World getInstance(){return SINGLETON_INSTANCE;}private World() {}}
组合composition与继承 is or has
继承可以最大程度的复用代码,但它并不总是完成工作最好的工具,不正确使用时可能降低软件健壮性(fragile),在同一个包内使用继承是安全的,但是如果跨越包的边界来使用继承,破坏了封装性,java复用代码的方式还有组合
- java不能多重继承,多重继承会产生菱形继承的问题,如果想复用多个类的代码可以使用组合
- 组合的实现方式就是将自身需要实现的方法,转发通过成员来实现
- 继承is-a 组合has-a
- 组合可以比继承提供更好的封装,对父类依赖度更低
```java // 我们希望创建一个Set,能够统计”有史以来”添加到其中去的元素个数 // 但是,如果使用继承结果明显不对 // 通过组合方式得到了互相不依赖的方法,提升了封装性 import java.util.Arrays; import java.util.Collection; import java.util.HashSet;// 组合实现方式class DoctorDriver {Driver diver;Doctor doctor;void drive(){driver.drive();}void treatPatient(){doctor.treat();}}
public class CountingSet {
/** 统计"有史以来"向该集合中添加过的元素个数 */private int count = 0;HashSet<Object> _set = new HashSet<>();public boolean add(Object obj) {count++;return _set.add(obj);}public boolean addAll(Collection c) {count += c.size();return _set.addAll(c);}public int size() {return _set.size();}public boolean remove(Object obj) {return _set.remove(obj);}public boolean removeAll(Collection c) {return _set.removeAll(c);}public int getCount() {return count;}public static void main(String[] args) {CountingSet countingSet = new CountingSet();countingSet.add(new Object());countingSet.addAll(Arrays.asList(1, 2, 3));System.out.println(countingSet.getCount());}
} ```
