一,继承
Java 中的继承使用关键字 extends ,跟 C# 的语法略有差别.
1. 子类构造器
java 会自动在子类的构造器中插入对父类构造器的调用,也就是说在子类可以访问父类之前已经完成了父类的初始化.
如果想调用带参数的父类构造器,应该使用 super 关键字.
/**
* @author 陈敬
* @date 18/1/17
*/
public class Product {
private String name;
public Product(String name) {
this.name = name;
System.out.println("[Product constructor]");
}
}
public class Bread extends Product {
private int price;
public Bread(String name, int price) {
super(name); //调用父类构造器
this.price = price;
System.out.println("[Bread constructor]");
}
}
我们创建一个 Bread 类的实例,看看调用顺序.
@Test
public void testConstructor(){
Bread bread=new Bread("毛毛虫面包",10);
}
打印结果:
[Product constructor]
[Bread constructor]
2. 调用父类方法
子类是不能直接访问到父类的私有域的,如果想访问只能借助父类公开的 get 访问器.子类调用父类中的方法也需要使用 super 关键字.
public class Product {
private String name;
public String getName() {
return name;
}
public Product(String name) {
this.name = name;
}
}
public class Bread extends Product {
public Bread(String name) {
super(name);
}
public void display() {
System.out.println(getName());
}
}
然后写个单元测试:
@Test
public void testPrivate(){
Bread bread=new Bread("毛毛虫面包");
bread.display();//毛毛虫面包
}
需要说明一点,super 并不是一个对象的引用,不能将 super 赋值给变量,它只是一个特殊的关键字,告诉编辑器要调用父类中的方法.
3. 关于重载
如果父类中存在重载方法,子类又进行了重载,会覆盖父类中的方法吗?实际上,父类和子类中的方法都可以正常重载,不会被覆盖.
首先在父类 Product 中添加方法 getDescription():
public class Product {
......
public String getDescription() {
return "[Product]name="+name;
}
}
然后在子类中重载该方法:
public class Bread extends Product {
......
public String getDescription(String storeName) {
return "[Bread]storename="+storeName;
}
}
增加一个单元测试:
public class ExtendClassTests {
@Test
public void testOverload(){
Bread bread=new Bread("豆沙面包",9);
System.out.println(bread.getDescription());
System.out.println(bread.getDescription("味多美"));
}
}
输出:
[Product]name = 豆沙面包
[Bread]storename = 味多美
4. 继承准则
继承准则:尽量少用继承.一般用继承表达行为间的差异,用组合表示状态上的变化.
二,多态
1. 变量多态
在 Java 中对象变量是多态的,一个 Product 变量可以引用 Product 对象,也可以引用一个 Product 子类的对象.
@Test public void testParent() {
Product product = new Bread("毛毛虫面包", 10);
product.display();
//强制类型转换
if (product instanceof Bread) {
Bread brand = (Bread) product;
brand.display("味多美");
}
}
由于 Bread 实例向上转型为 Product 类型,所以不能再调用 Bread.getDescription(String storeName) 方法.
如果需要将父类强制转换为子类时,要先通过 instanceof 检测对象类型,我们最好尽量避免使用强制类型转换.
2. 动态绑定
所谓动态绑定,就是在运行时根据对象的类型决定要调用的方法.在 java 中,动态绑定是默认行为,不需要添加额外的关键字实现多态.
再写个 demo 来看一下,在父类和子类中重载了 display 方法.
public class Product {
private String name;
public Product(String name) {
this.name = name;
}
public void display() {
System.out.println("[Product]getDescription()");
}
}
public class Bread extends Product {
private int price;
public Bread(String name, int price) {
super(name);
this.price = price;
}@Override public void display() {
System.out.println("[Bread]getDescription()");
}
public void display(String storeName) {
System.out.println("[Bread]getDescription(String storeName)");
}
}
添加单元测试:
@Test public void dynamicBind() {
Product product = new Product("product");
product.display(); //[Product]getDescription()
Bread bread = new Bread("毛毛虫", 9);
bread.display(); //[Bread]getDescription()
bread.display("maimai"); //[Bread]getDescription(String storeName)
Product product1 = bread;
product1.display(); //[Bread]getDescription()
}
虚拟机为每个类创建一个方法表,列出所有方法的签名和实际调用的方法.这样一来,当动态调用方法的时候,只需要查找方法表就能快速的找到真正调用的方法了.
Product:
display()->Product.display()
Bread:
display()->Bread.display()
display(String name)->Bread.display(String name)
完整源码参见: https://github.com/cathychen00/cathyjava /_08_extend
三,抽象类
定义抽象方法用用 abstract 关键字,它仅有声明而没有方法体.
包含抽象方法的类叫做抽象类,如果一个类包含一个或多个抽象方法,那么必须被定义为抽象类.
如果一个类从抽象类继承,那么必须为抽象类中的所有抽象方法提供实现,否则该类也必须被定义为抽象类.
看一个场景:我们有一些定时任务,要进行的工作流程类似,只有具体一部分细节内容不同.我们可以定义一个抽象基类 BaseJob,再不同的部分封装为抽象方法,具体的实现在子类中进行.
public abstract class BaseJob {
public void run() {
System.out.println("==START " + getDescription() + "==");
String lastJobId = getLastJobId();
execute(lastJobId);
writeLog();
System.out.println("==END " + getDescription() + "==");
}
protected abstract String getDescription();
protected abstract void execute(String jobId);
private void writeLog() {
System.out.println("write log to DB");
}
private String getLastJobId() {
return "job1221";
}
}
public class ArticleJob extends BaseJob {@Override protected String getDescription() {
return "抓取文章任务";
}@Override protected void execute(String jobId) {
System.out.println("抓取站点新闻文章 jobid=" + jobId);
}
public static void main(String[] args) {
BaseJob articleJob = new ArticleJob();
articleJob.run();
}
}
创建单元测试,调用 ArticleJob 看看.
@Test
public void articleJob(){
BaseJob articleJob=new ArticleJob();
articleJob.run();
}
运行结果:
==START 抓取文章任务==
抓取站点新闻文章 jobid=job1221
write log to DB
==END 抓取文章任务==
当再次添加符合该流程的定时任务时,只需要新建一个类,实现 BaseJob 就可以了.
完整例子: https://github.com/cathychen00/cathyjava /09_abstract
来源: https://www.cnblogs.com/janes/p/8309741.html