Java 中的 extends 关键字

概要

  • Java中的继承实现方式与执行顺序

本文主要探究如何使用Java中的继承(extends)?以及子父类中,static{}{}和构造器执行顺序。

  • 子父类的equals重写注意事项

Java中的继承实现方式与执行顺序

A:

/*
 * @ProjectName: 编程学习
 * @Copyright:   2018 HangZhou xiazhaoyang Dev, Ltd. All Right Reserved.
 * @address:     http://xiazhaoyang.tech
 * @date:        2018/9/17 22:28
 * @email:       xiazhaoyang@live.com
 * @description: 本内容仅限于编程技术学习使用,转发请注明出处.
 */
package com.capsule.chapter.extend;

import java.util.Date;

/**
 * <p>
 *
 * </p>
 *
 * @author xiazhaoyang
 * @version V1.0
 * @date 2018/9/17 22:28
 * @modificationHistory=========================逻辑或功能性重大变更记录
 * @modify By: {修改人} 2018/9/17
 * @modify reason: {方法名}:{原因}
 * ...
 */
public class A {
    public A() {
        System.out.println("Class A");
    }
    {
        System.out.println("{} A");
        overrideMe("{}");
    }
    static{
        System.out.println("static A");
        overrideMeStatic("static");
    }

    public void overrideMe(String area){
        System.out.println("overrideMe A - " + area);
    }

    public static void overrideMeStatic(String area){
        System.out.println("overrideMeStatic A - " + area);
    }

    public static void main(String[] args) {
        new A();
        //static A
        //overrideMeStatic A - static
        //{} A
        //overrideMe A - {}
        //Class A
    }
}

分析

  • 类在初始化时,方法块{}、静态方法块static{}、构造器className()的执行顺序依次是:static{} > {} > className()
  • 静态方法只能在静态方法块中执行,所以静态方法的执行顺序和静态方法块一样,是最高的(除非静态方法快中并没有使用该静态方法)。

B:

/*
 * @ProjectName: 编程学习
 * @Copyright:   2018 HangZhou xiazhaoyang Dev, Ltd. All Right Reserved.
 * @address:     http://xiazhaoyang.tech
 * @date:        2018/9/17 22:29
 * @email:       xiazhaoyang@live.com
 * @description: 本内容仅限于编程技术学习使用,转发请注明出处.
 */
package com.capsule.chapter.extend;

/**
 * <p>
 *
 * </p>
 *
 * @author xiazhaoyang
 * @version V1.0
 * @date 2018/9/17 22:29
 * @modificationHistory=========================逻辑或功能性重大变更记录
 * @modify By: {修改人} 2018/9/17
 * @modify reason: {方法名}:{原因}
 * ...
 */
public class B extends A{

    public B() {
        System.out.println("Class B");
    }
    {
        //方法快中无论什么方法都可以执行
        System.out.println("{} B");
        isStatic();
        notStatic();
    }
    static{
        //静态方法块中只能执行静态方法
        System.out.println("static B");
        //notStatic(); 报错
        isStatic();//不报错,使用的是静态方法
    }

    public static void isStatic(){
        System.out.println("is static method B");
    }

    public void notStatic(){
        System.out.println("not static method B");
    }

    public static void overrideMe(){
        overrideMeStatic("static method B");//父类静态方法
        System.out.println("overrideMe B");
    }

    public static void main(String[] args) {
        B b = new B();
        b.overrideMe();
        B.overrideMe();
        //---父类static{}
        //static A
        //overrideMeStatic A - static
        //---子类static{}
        //static B
        //is static method B
        //---父类方法块
        //{} A
        //overrideMe A - {}
        //---父类构造器
        //Class A
        //---子类方法块
        //{} B
        //is static method B
        //not static method B
        //---子类构造器
        //Class B
        //--- 实例方法调用
        //overrideMeStatic A - static method B
        //overrideMe B
        //--- 子类静态方法调用
        //overrideMeStatic A - static method B
        //overrideMe B
    }

}

分析

  • 子类初始化时,父类static{}和其中的静态方法 > 子类static{}和其中的静态方法 > 父类方法块{} > 父类构造方法 > 子类方法块 > 子类构造器
  • static{}方法块执行优先级最高(父>子)
  • 单个类中,方法块调用在类初始化构造器之前,子父类中,子类方法块的执行在父类构造器方法之后执行

Java中的继承实现方式与执行顺序 注意事项

要么为继承而设计,并提供文档说明,要么就禁止继承

  • 继承对于final变量域的修改

Super:

/*
 * @ProjectName: 编程学习
 * @Copyright:   2018 HangZhou Yiyuery Dev, Ltd. All Right Reserved.
 * @address:     http://xiazhaoyang.tech
 * @date:        2018/9/12 08:50
 * @email:       xiazhaoyang@live.com
 * @description: 本内容仅限于编程技术学习使用,转发请注明出处.
 */
package com.capsule.chapter.extend;

/**
 * <p>
 *
 * </p>
 *
 * @author xiazhaoyang
 * @version V1.0
 * @date 2018/9/12 08:50
 * @modificationHistory=========================逻辑或功能性重大变更记录
 * @modify By: {修改人} 2018/9/12
 * @modify reason: {方法名}:{原因}
 * ...
 */
public class Super {

    public Super() {
        overrideMe();
    }

    public void overrideMe(){
        System.out.println("Super overrideMe!");
    }
}

Sub:

/*
 * @ProjectName: 编程学习
 * @Copyright:   2018 HangZhou Yiyuery Dev, Ltd. All Right Reserved.
 * @address:     http://xiazhaoyang.tech
 * @date:        2018/9/12 08:52
 * @email:       xiazhaoyang@live.com
 * @description: 本内容仅限于编程技术学习使用,转发请注明出处.
 */
package com.capsule.chapter.extend;

import java.util.Date;

/**
 * <p>
 *
 * </p>
 *
 * @author xiazhaoyang
 * @version V1.0
 * @date 2018/9/12 08:52
 * @modificationHistory=========================逻辑或功能性重大变更记录
 * @modify By: {修改人} 2018/9/12
 * @modify reason: {方法名}:{原因}
 * ...
 */
public class Sub extends Super{

    private final Date date;

    Sub(){
        date = new Date();
    }
    @Override
    public void overrideMe(){
        System.out.println(date);
    }

    public static void main(String[] args) {
        Sub sub = new Sub();
        sub.overrideMe();
        //null
        //Wed Sep 12 08:55:57 CST 2018
    }

}

继承之后,子类实例化之前会先执行被重载的父类方法overrideMe(),此时子类的实例化尚未完成,静态块也未执行,所以虽然是final修饰的字段,date变量仍然是null,实例化时打印null,实例化之后打印复制后的新时间。

继承父类,子类重写equals方法如何正确使用instance of

基类 Lombok自动生成

/*
 * @ProjectName: 编程学习
 * @Copyright:   2018 HangZhou Yiyuery Dev., Ltd. All Right Reserved.
 * @address:     http://xiazhaoyang.tech
 * @date:        2018/7/29 22:24
 * @email:       xiazhaoyang@live.com
 * @description: 本内容仅限于编程技术学习使用,转发请注明出处.
 */
package com.example.chapter3.extend;

/**
 * <p>
 *
 * </p>
 *
 * @author xiachaoyang
 * @version V1.0
 * @date 2018年11月02日 10:11
 * @modificationHistory=========================逻辑或功能性重大变更记录
 * @modify By: {修改人} 2018年11月02日
 * @modify reason: {方法名}:{原因}
 * ...
 */
public class EqualsSuper {

    private Integer count;

    private String superName;

    public EqualsSuper() {
    }

    public Integer getCount() {
        return this.count;
    }

    public String getSuperName() {
        return this.superName;
    }

    public void setCount(Integer count) {
        this.count = count;
    }

    public void setSuperName(String superName) {
        this.superName = superName;
    }

    public boolean equals(Object o) {
        if (o == this) return true;
        if (!(o instanceof EqualsSuper)) return false;
        final EqualsSuper other = (EqualsSuper) o;
        //此处o针对的是实际类型,如果是EqualsSub, other.canEqual 进入的是子类的canEqual方法
        //判断equalsSuper.equals(equalsSub)    return other instanceof EqualsSub;
        //(Object) this) 指向 EqualsSuper
        // new EqualsSuper() instanceof EqualsSub; //false
        if (!other.canEqual((Object) this)) return false;
        final Object this$count = this.count;
        final Object other$count = other.count;
        if (this$count == null ? other$count != null : !this$count.equals(other$count)) return false;
        final Object this$superName = this.superName;
        final Object other$superName = other.superName;
        if (this$superName == null ? other$superName != null : !this$superName.equals(other$superName)) return false;
        return true;
    }

    public int hashCode() {
        final int PRIME = 59;
        int result = 1;
        final Object $count = this.count;
        result = result * PRIME + ($count == null ? 0 : $count.hashCode());
        final Object $superName = this.superName;
        result = result * PRIME + ($superName == null ? 0 : $superName.hashCode());
        return result;
    }

    public boolean canEqual(Object other) {
        return other instanceof EqualsSuper;
    }

    public String toString() {
        return "com.example.chapter3.extend.EqualsSuper(count=" + this.count + ", superName=" + this.superName + ")";
    }
}

子类 Lombok自动生成

/*
 * @ProjectName: 编程学习
 * @Copyright:   2018 HangZhou Yiyuery Dev., Ltd. All Right Reserved.
 * @address:     http://xiazhaoyang.tech
 * @date:        2018/7/29 22:24
 * @email:       xiazhaoyang@live.com
 * @description: 本内容仅限于编程技术学习使用,转发请注明出处.
 */
package com.example.chapter3.extend;

/**
 * <p>
 *
 * </p>
 *
 * @author xiachaoyang
 * @version V1.0
 * @date 2018年11月02日 10:12
 * @modificationHistory=========================逻辑或功能性重大变更记录
 * @modify By: {修改人} 2018年11月02日
 * @modify reason: {方法名}:{原因}
 * ...
 */
public class EqualsSub extends EqualsSuper{

    private String subName;

    public EqualsSub() {
    }

    public String getSubName() {
        return this.subName;
    }

    public void setSubName(String subName) {
        this.subName = subName;
    }

    public boolean equals(Object o) {
        if (o == this) return true;
        if (!(o instanceof EqualsSub)) return false;
        final EqualsSub other = (EqualsSub) o;
        if (!other.canEqual((Object) this)) return false;
        final Object this$subName = this.subName;
        final Object other$subName = other.subName;
        if (this$subName == null ? other$subName != null : !this$subName.equals(other$subName)) return false;        
        return true;
    }

    public int hashCode() {
        final int PRIME = 59;
        int result = 1;
        final Object $subName = this.subName;
        result = result * PRIME + ($subName == null ? 0 : $subName.hashCode());
        return result;
    }

    public boolean canEqual(Object other) {
        return other instanceof EqualsSub;
    }

    public String toString() {
        return "com.example.chapter3.extend.EqualsSub(subName=" + this.subName + ")";
    }

    public static void main(String[] args) {
        EqualsSub equalsSub = new EqualsSub();
        equalsSub.setSubName("sub2");
        equalsSub.setCount(2);
        equalsSub.setSuperName("super2");
        EqualsSuper equalsSuper = new EqualsSuper();
        equalsSuper.setCount(2);
        equalsSuper.setSuperName("super2");
        System.out.println(equalsSub.equals(equalsSuper));//false
        System.out.println(equalsSuper.equals(equalsSub));//false
    }
}

备注

equals方法通过lombok插件delombok生成,可见lombok插件在自动生成eauls方法时不会去考虑父类属性。

写法调整 V1 子父类Equals判断

EqualsSub


public boolean equals(Object o) {
    if (o == this) return true;
    if (o instanceof EqualsSub){
        final EqualsSub other = (EqualsSub) o;
        if (!other.canEqual((Object) this)) return false;
        final Object this$subName = this.subName;
        final Object other$subName = other.subName;
        if (this$subName == null ? other$subName != null : !this$subName.equals(other$subName)) return false;
    }else if(o instanceof EqualsSuper){
        return super.equals(o);
    }
    return false;
}

//...

public static void main(String[] args) {
    EqualsSub equalsSub = new EqualsSub();
    equalsSub.setSubName("sub2");
    equalsSub.setCount(2);
    equalsSub.setSuperName("super2");
    EqualsSuper equalsSuper = new EqualsSuper();
    equalsSuper.setCount(2);
    equalsSuper.setSuperName("super2");
    System.out.println(equalsSub.equals(equalsSuper));//true
    System.out.println(equalsSuper.equals(equalsSub));//false
}

子父类 instance of 比较区别

new EqualsSuper() instanceof EqualsSub //false
new EqualsSub() instanceof EqualsSuper //true

写法调整 V2 IDEA 默认实现方式

EqualsSuper

@Override
 public boolean equals(Object o) {
     if (this == o) return true;
     if (!(o instanceof EqualsSuper)) return false;
     EqualsSuper that = (EqualsSuper) o;
     return Objects.equals(count, that.count) &&
             Objects.equals(superName, that.superName);
 }

 @Override
 public int hashCode() {
     return Objects.hash(count, superName);
 }

EqualsSub

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof EqualsSub)) return false;
    if (!super.equals(o)) return false;
    EqualsSub equalsSub = (EqualsSub) o;
    return Objects.equals(subName, equalsSub.subName);
}

@Override
public int hashCode() {
    return Objects.hash(super.hashCode(), subName);
}


public static void main(String[] args) {
    EqualsSub equalsSub = new EqualsSub();
    equalsSub.setSubName("sub2");
    equalsSub.setCount(2);
    equalsSub.setSuperName("super2");
    EqualsSuper equalsSuper = new EqualsSuper();
    equalsSuper.setCount(2);
    equalsSuper.setSuperName("super2");
    System.out.println(equalsSub.equals(equalsSuper));//false
    System.out.println(equalsSuper.equals(equalsSub));//true
}

写法调整 V2 + V1 调整判断一致性

EqualsSub

@Override
public boolean equals(Object o) {
   if (this == o) return true;
   if (o instanceof EqualsSub){
       if (!super.equals(o)) return false;
       EqualsSub equalsSub = (EqualsSub) o;
       return Objects.equals(subName, equalsSub.subName);
   }else if(o instanceof EqualsSuper){
       return super.equals(o);
   }
   return false;
}

//...

public static void main(String[] args) {
      EqualsSub equalsSub = new EqualsSub();
      equalsSub.setSubName("sub2");
      equalsSub.setCount(2);
      equalsSub.setSuperName("super2");
      EqualsSuper equalsSuper = new EqualsSuper();
      equalsSuper.setCount(2);
      equalsSuper.setSuperName("super2");
      System.out.println(equalsSub.equals(equalsSuper));//true
      System.out.println(equalsSuper.equals(equalsSub));//true
}

总结

  • 比对lombok和idea模板两种比较方法,各有优点和缺点
  • lombok不对父类进行属性比较,idea模板不具有一致性,只支持Super.equals(Sub)

微信公众号

[JDK] Java 中的 `extends` 关键字_子类

扫码关注或搜索架构探险之道获取最新文章,不积跬步无以至千里,坚持每周一更,坚持技术分享_