原来你是这样的JAVA[03]-继承、多态、抽象类

简介:

一、继承


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






    本文转自 陈敬(Cathy) 博客园博客,原文链接:http://www.cnblogs.com/janes/p/8309741.html,如需转载请自行联系原作者


相关文章
|
27天前
|
Java
在Java中,接口之间可以继承吗?
接口继承是一种重要的机制,它允许一个接口从另一个或多个接口继承方法和常量。
77 1
|
1月前
|
Java
Java基础(13)抽象类、接口
本文介绍了Java面向对象编程中的抽象类和接口两个核心概念。抽象类不能被实例化,通常用于定义子类的通用方法和属性;接口则是完全抽象的类,允许声明一组方法但不实现它们。文章通过代码示例详细解析了抽象类和接口的定义及实现,并讨论了它们的区别和使用场景。
|
1月前
|
Java 测试技术 开发者
Java零基础-抽象类详解
【10月更文挑战第17天】Java零基础教学篇,手把手实践教学!
29 2
|
2月前
|
Java 测试技术 开发者
Java零基础-抽象类详解
【10月更文挑战第15天】Java零基础教学篇,手把手实践教学!
27 2
|
2月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
27 3
|
2月前
|
Java
在Java多线程编程中,实现Runnable接口通常优于继承Thread类
【10月更文挑战第20天】在Java多线程编程中,实现Runnable接口通常优于继承Thread类。原因包括:1) Java只支持单继承,实现接口不受此限制;2) Runnable接口便于代码复用和线程池管理;3) 分离任务与线程,提高灵活性。因此,实现Runnable接口是更佳选择。
51 2
|
2月前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
38 2
|
2月前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
44 1
|
2月前
|
存储 Java 测试技术
Java零基础-多态详解
【10月更文挑战第10天】Java零基础教学篇,手把手实践教学!
35 4
|
2月前
|
Java 编译器 程序员
Java多态背后的秘密:动态绑定如何工作?
本文介绍了Java中多态的实现原理,通过动态绑定和虚拟方法表,使得父类引用可以调用子类的方法,增强了代码的灵活性和可维护性。文中通过具体示例详细解析了多态的工作机制。
68 4
下一篇
DataWorks