继承
- 继承的本质是避免重复
- 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;
}
@Override
public 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());
}
@Override
public 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 {
@Override
public void story() {
System.out.println("从前有个老妖怪");
}
@Override
public 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());
}
} ```