复用
随着码代码的时间增长,程序员会越发需要"复用"这个能力,比如最简单的对String
类进行判空:
str == null || "".equals(str);
我们需要每次都这样写么?在面向对象中,我们可以利用类来封装这块逻辑进行复用。
现在,我们来思考:
除了方法,能否基于类的层面进行复用呢,当许多的对象都拥有相同的状态,我们是否可以用一个基类来对这些属性进行抽象?
答案是可以的,Java可以利用“继承”(Inheritance)的方式对类进行复用,只需要一个extends
即可获取父类的能力(包括方法与属性)。
使用继承
UML
编写Employee的子类Manager
- com.tea.modules.model.base.Employee
package com.tea.modules.model.base;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
/**
* com.tea.modules.model.base <br>
* 雇员类
*
* @author jaymin
* @since 2021/5/25
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee {
/**
* 名称
*/
private String name;
/**
* 工资
*/
private double salary;
/**
* 入职日期
*/
private LocalDate hireDay;
/**
* 涨工资
* @param byPercent 涨薪幅度,例如: 1.25
*/
public void raiseSalary(double byPercent) {
double raise = salary * byPercent / 100;
salary *= raise;
}
}
这是一个基类-Employee,有三个成员属性: name,salary,hireDay,提供了一个涨工资的方法: raiseSalary.
- com.tea.modules.model.po.Manager
现在我们需要创建一个经理类用来直接继承Employee:
经理类,继承自雇员。经理属于特殊的雇员:
除了领工资外,经理还可以达到绩效后领取奖金
package com.tea.modules.model.po;
import com.tea.modules.model.base.Employee;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.time.LocalDate;
/**
* com.tea.modules.model.po <br>
* 经理类,继承自雇员。经理属于特殊的雇员:<br>
* 除了领工资外,经理还可以达到绩效后领取奖金 <br>
* Employee->超类(super class)|基类(base class)|父类(parent class)<br>
* Manager->子类(sub class)|派生类(derived class)|孩子类(child class) <br>
*
* @author jaymin
* @since 2021/5/25
*/
@Data
@NoArgsConstructor
@ToString(callSuper = true)
public class Manager extends Employee {
/**
* 奖金
*/
private double bonus;
public Manager(String name, double salary, LocalDate hireDay, double bonus) {
super(name, salary, hireDay);
this.bonus = bonus;
}
/**
* 经理的工资由[工资+奖金]组成
*
* @return double
*/
@Override
public double getSalary() {
// 注意这里要使用super关键字,否则会无限递归
double baseSalary = super.getSalary();
return baseSalary + bonus;
}
}
Employee->超类(super class)|基类(base class)|父类(parent class)
Manager->子类(sub class)|派生类(derived class)|孩子类(child class)
- Demo
package com.tea.modules.java8.extend;
import com.tea.modules.model.base.Employee;
import com.tea.modules.model.po.Manager;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
/**
* com.tea.modules.java8.extend <br>
* 理解继承
*
* @author jaymin
* @since 2021/5/25
*/
public class ManagerDemo {
public static void main(String[] args) {
List<Employee> employees = new ArrayList<>();
LocalDate hireDay = LocalDate.of(2021, 5, 25);
Employee manager = new Manager("经理", 20000, hireDay, 10000);
Employee jack = new Employee("Jack", 10000, hireDay);
Employee james = new Employee("James", 10000, hireDay);
employees.add(manager);
employees.add(jack);
employees.add(james);
employees.forEach(System.out::println);
}
}
- Result
Manager(super=Employee(name=经理, salary=30000.0, hireDay=2021-05-25), bonus=10000.0)
Employee(name=Jack, salary=10000.0, hireDay=2021-05-25)
Employee(name=James, salary=10000.0, hireDay=2021-05-25)
多态
在最后一句employees.forEach(System.out::println);
中:
对于Manager
类型的manger引用,调用的是Manager
类的toString
(不仅打印了自己的信息,还打印了父类的信息).
对于Employee
类型的jack引用,调用的是Employee
类的toString
.
这个过程由虚拟机来控制,可以用一个变量来表示多种实际类型的现象称为多态
(polymorphism),在运行时可以自动选择调用哪个方法的现象叫做动态绑定
(dynamic bingding).
- 子类赋值给超类变量
Employee manager = new Manager("经理", 20000, hireDay, 10000);
反之则无效,无法用Manager去接收一个Employee变量.
单一继承原则
java只支持单一继承,不支持同时继承多个类。但是你可以多层级继承,如图所示:
方法调用
再聊final
被final修饰的变量不可变,同样,被final修饰的类或者方法也是不可以被扩展和重写的。某种意义上来说,被final修饰的方法就像被"锁"住了一样.
这个特性比较重要,在后面的Spring框架中,我们会了解到,被final修饰的方法使用CGLIB是无法做增强的.
类型转换
这里我们创建了List<Employee>的列表,里面既存放了Employee类型的对象,也有Manager类型的对象.那么我们需要用到Manager的方法时,就需要用到类型强制转换了
public static void main(String[] args) {
List<Employee> employees = new ArrayList<>();
LocalDate hireDay = LocalDate.of(2021, 5, 25);
Employee manager = new Manager("经理", 20000, hireDay, 10000);
Employee jack = new Employee("Jack", 10000, hireDay);
Employee james = new Employee("James", 10000, hireDay);
employees.add(manager);
employees.add(jack);
employees.add(james);
// 类型强制转换
for (Employee employee : employees) {
if(employee instanceof Manager){
Manager managerPerson = (Manager) employee;
System.out.println(managerPerson.getSalary());
}
}
}
Object
所有的Java对象都继承自Object类,也就是说,你可以用Object接收任意类型的对象。在Object中,包含了8个方法,下面我们来简单了解一下:
-
equals
Object类中的equals方法用于检测一个对象是否等于另外一个对象。在Object类中,比较的是两个对象的引用值是否一致。但通常情况,我们都需要重写equals,来比较两个对象的状态是否一致。
public boolean equals(Object obj) {
return (this == obj);
}
-
getClass
返回此 Object 的运行时类。返回的 Class 对象是被表示的类的静态同步方法锁定的对象 -
hashCode
返回对象的哈希码值。
关于hashCode有以下规定:
对于同一个对象,多次调用hashCode必须返回相同的整数.
equals方法比较相等,hashCode返回值必须相等.
equals方法比较不相等,hashCode可以相等也可以不等. -
toString
toString返回对象值的字符串,在平时开发中,应该始终覆盖toString方法,否则只会打印引用信息,无法查看对象的状态。
数组toString推荐使用Arrays.toString(array);
剩下的方法,在后面的章节进行讲解.
加载具有继承关系的对象
父类的static->父类构造器->子类的static->子类的构造器.