继承

  • 继承的本质是避免重复
  • DRY,事不过三,三便重构
  • 继承父类,便拥有父类的方法与变量

    继承体系

  • Java是单根继承,所有类都隐式继承Object类,可以保证所有对象都至少有一些相同的方法

  • 单根继承的特性使得Java任何一个类只能继承一个类,反例为C++,其是多重继承的
  • Object常用方法

    • equals 自定义两个对象相等的方法,默认判断地址相同
    • toString 默认打印对象哈希,提供一种以字符串表示对象内容的方法,sout对象时被默认调用

      类的结构与初始化顺序

  • 子类拥有父类的一切数据和行为

  • 先初始化父类
    • 静态成员的初始化
    • 静态初始化块
    • 成员的初始化
    • 初始化块
    • 执行父类构造器函数 supper()
  • 再初始化子类

    • 静态成员的初始化
    • 静态初始化块
    • 成员的初始化
    • 初始化块
    • 执行子类构造器函数

      实例方法的覆盖override

  • 一个类可以通过重写父类的方法,实现相同签名的不通逻辑

  • 永远要使用@Override注解防止手残,使用编译器帮助检查方法名
  • instanceof关键字判断是否是某类的实例
  • 改写Obejct equals案例如下 ```java import java.util.Objects;

public class User { private Integer id; private String name;

  1. public User(Integer id, String name) {
  2. this.id = id;
  3. this.name = name;
  4. }
  5. public Integer getId() {
  6. return id;
  7. }
  8. public String getName() {
  9. return name;
  10. }
  11. @Override
  12. public boolean equals(Object o) {
  13. if (this == o) return true;
  14. if (o == null || getClass() != o.getClass()) return false;
  15. User user = (User) o;
  16. return Objects.equals(id, user.getId()) && Objects.equals(name, user.getName());
  17. }
  18. @Override
  19. public int hashCode() {
  20. return Objects.hash(id);
  21. }
  22. public static void main(String[] args) {
  23. System.out.println(new User(1, "user1") == new User(1, "user1"));
  24. System.out.println(new User(1, "user1").equals(new User(1, "user1")));
  25. System.out.println(new User(1, "user1").equals(new User(2, "user2")));
  26. }

}

  1. <a name="s51Tq"></a>
  2. # 设计模式-模板方法
  3. - 按部就班的实现一个逻辑时,可在父类先定义一个模板方法,然后覆盖模板方法的某几个方法,来改变模板方法的逻辑
  4. - 在覆盖父类方法的时候,可以使用**supper.methods()**执行父类代码逻辑
  5. ```java
  6. // 父类
  7. public class Story {
  8. public final void tellStory() {
  9. startStory();
  10. story();
  11. endStory();
  12. }
  13. public void startStory() {
  14. System.out.println("开始讲故事啦");
  15. }
  16. public void story() {
  17. System.out.println("从前有个老和尚");
  18. }
  19. public void endStory() {
  20. System.out.println("故事讲完啦");
  21. }
  22. public static void main(String[] args) {
  23. new Story().tellStory();
  24. }
  25. }
  1. // 子类
  2. public class MonsterStory extends Story {
  3. @Override
  4. public void story() {
  5. System.out.println("从前有个老妖怪");
  6. }
  7. @Override
  8. public void endStory() {
  9. super.endStory();
  10. System.out.println("你还想听吗");
  11. }
  12. public static void main(String[] args) {
  13. new MonsterStory().tellStory();
  14. }
  15. }

向上向下转型

  • 一个子类型的对象永远是一个父类得对象 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最小的工作单元为对象,如果我们需要一个全局的不可变对象(例如一个类只用来创建一个被使用的对象)比如地球对象

    1. public class World {
    2. public static final world SINGLETON_INSTANCE = new World();
    3. public static World getInstance(){
    4. return SINGLETON_INSTANCE;
    5. }
    6. private World() {}
    7. }

    组合composition与继承 is or has

  • 继承可以最大程度的复用代码,但它并不总是完成工作最好的工具,不正确使用时可能降低软件健壮性(fragile),在同一个包内使用继承是安全的,但是如果跨越包的边界来使用继承,破坏了封装性,java复用代码的方式还有组合

  • java不能多重继承,多重继承会产生菱形继承的问题,如果想复用多个类的代码可以使用组合
  • 组合的实现方式就是将自身需要实现的方法,转发通过成员来实现
  • 继承is-a 组合has-a
  • 组合可以比继承提供更好的封装,对父类依赖度更低
    1. // 组合实现方式
    2. class DoctorDriver {
    3. Driver diver;
    4. Doctor doctor;
    5. void drive(){
    6. driver.drive();
    7. }
    8. void treatPatient(){
    9. doctor.treat();
    10. }
    11. }
    ```java // 我们希望创建一个Set,能够统计”有史以来”添加到其中去的元素个数 // 但是,如果使用继承结果明显不对 // 通过组合方式得到了互相不依赖的方法,提升了封装性 import java.util.Arrays; import java.util.Collection; import java.util.HashSet;

public class CountingSet {

  1. /** 统计"有史以来"向该集合中添加过的元素个数 */
  2. private int count = 0;
  3. HashSet<Object> _set = new HashSet<>();
  4. public boolean add(Object obj) {
  5. count++;
  6. return _set.add(obj);
  7. }
  8. public boolean addAll(Collection c) {
  9. count += c.size();
  10. return _set.addAll(c);
  11. }
  12. public int size() {
  13. return _set.size();
  14. }
  15. public boolean remove(Object obj) {
  16. return _set.remove(obj);
  17. }
  18. public boolean removeAll(Collection c) {
  19. return _set.removeAll(c);
  20. }
  21. public int getCount() {
  22. return count;
  23. }
  24. public static void main(String[] args) {
  25. CountingSet countingSet = new CountingSet();
  26. countingSet.add(new Object());
  27. countingSet.addAll(Arrays.asList(1, 2, 3));
  28. System.out.println(countingSet.getCount());
  29. }

} ```