Java中的继承使用关键字extends ,跟C#的语法略有差异。
java
java会自动在子类的构造器中插入对父类构造器的调用,也就是说在子类能够访问父类以前已经完成了父类的初始化。
若是想调用带参数的父类构造器,应该使用super关键字。git
/** * @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类的实例,看看调用顺序。github
@Test public void testConstructor(){ Bread bread=new Bread("毛毛虫面包",10); }
打印结果:
[Product constructor]
[Bread constructor]编辑器
子类是不能直接访问到父类的私有域的,若是想访问只能借助父类公开的get访问器。子类调用父类中的方法也须要使用super关键字。ide
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赋值给变量,它只是一个特殊的关键字,告诉编辑器要调用父类中的方法。测试
若是父类中存在重载方法,子类又进行了重载,会覆盖父类中的方法吗?实际上,父类和子类中的方法均可以正常重载,不会被覆盖。
首先在父类Product中添加方法getDescription():this
public class Product { …… public String getDescription() { return "[Product]name="+name; } }
而后在子类中重载该方法:spa
public class Bread extends Product { …… public String getDescription(String storeName) { return "[Bread]storename="+storeName; } }
增长一个单元测试:code
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=味多美
继承准则:尽可能少用继承。通常用继承表达行为间的差别,用组合表示状态上的变化。
在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检测对象类型,咱们最好尽可能避免使用强制类型转换。
所谓动态绑定,就是在运行时根据对象的类型决定要调用的方法。在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