【java筑基】浅谈代码复用技术——继承、组合

简介: 【java筑基】浅谈代码复用技术——继承、组合

一、继承

1.1 继承的特点

子类可以获得父类的全部成员变量和方法称为继承,注意子类不能够继承父类的构造器。Java具有单继承的特点,一个类只能够有一个直接父类。所有类都继承了java.lang.Object类。

1.2 重写

子类可以对父类方法进行重写,重写必须遵守两小两同一大,即子类方法返回值的类型要比父类方法返回值类型要更小或者相同(儿子排场要小),子类方法的异常类型要比父类方法的异常类型要更小或者相同(儿子犯的错误要更少),方法名要相同,形参列表要相同,访问权限要比父类访问权限更大或者相同(儿子要搞出点名堂,让别人看见)。特别注意的是子类与父类重写的方法不能一个是属于类,一个是属于对象的。

如果父类定义了一个private修饰的方法,子类无法访问该方法,不可以对该方法进行重写。此时,即使子类中有一个与父类private具体相同名字,相同形参列表,相同返回值类型的方法,依然不是重写。

1.3 super限定

可以在子类对象中使用super限定该对象调用父类中被子类覆盖的方法、成员变量。和this关键字一样。super关键字也不能出现在静态方法中。

其实,子类定义的同名实例变量并不会真的覆盖掉父类实例变量,只是简单的被隐藏了。当创建一个类的实例对象时,依然会为该对象父类中的实例变量分配内存空间,即使他被隐藏了。

class Parent {
  public String tag ="疯狂java讲义";
}
class Child extends Parent {
  private String tag = "轻量级java EE企业应用实战";
}
public class HideTest {
  public static void main(String[] args) {
    Child a = new Child();
    // 该语句不能通过编译,因为Child中的tag标签是私有的
    // System.out.println(a.tag);
    //输出疯狂java讲义
    System.out.println(((Parent) a).tag);
  }
}

上面的代码第13行之所以不能通过编译,是因为访问哪个实例变量是由声明该变量的类型决定,a声明的是Child类型,故访问Child类中的tag标签。进行强转后,则可以访问父类中被隐藏的tag。创建Child对象后内存如下图。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nxk564gt-1657196387175)(https://uploadfiles.nowcoder.com/images/20210315/408218111_1615820134290/DECF015354E84983C28DB8828842FF44 “图片标题”)]

虽然子类不会获得父类的构造器,但是仍然可以通过super关键字调用父类构造器的代码。由于this调用本类被重载的构造器和super调用父类构造器的代码都必须放在发出调用的构造器的第一行,它们不能同时出现。不管是否显示调用父类的构造器,实际上执行子类构造器前都会先调用父类构造器。

class Creature {
  public Creature() {
    System.out.println("Creature无参构造器。");
  }
}
class Animal extends Creature {
  public Animal(String name) {
    System.out.println("Animal的一个参数构造器" + "Animal名字为" + name);
  }
  public Animal(String name, int age) {
    this(name);
    System.out.println("Animal的两个参数的构造器" + "其年龄为" + age);
  }
}
public class Wolf extends Animal {
  public Wolf(String name, int age) {
    super(name, age);
    System.out.println("Wolf的一个参数构造器");
  }
  public static void main(String[] args) {
    //输出:Creature无参构造器。
        //Animal的一个参数构造器Animal名字为灰太狼
        //Animal的两个参数的构造器其年龄为18
        //Wolf的一个参数构造器
    new Wolf("灰太狼", 18);
  }
}

二、继承的缺陷

继承和组合都可以实现复用,但是继承带来一个坏处,破坏封装。如果权限允许,子类不仅可以访问父类的成员变量、方法,还可以改变它的实现细节(重写)),父类可能被子类恶意篡改。因此为了使父类保持良好的封装,我们应遵循以下规则。

1.父类的成员变量尽量设置为private类型。

2.父类的辅助其它方法的工具方法尽量设置为private,需要被别的类访问但是不希望被重写的方法添加final关键字。只希望被子类访问的方法设置为protected。

3.尽量不要在父类的构造方法中调用可能被子类改写的类。

class Base {
  public Base() {
    test();
  }
  // 一号test方法
  public void test() {
    System.out.println("父类的Test");
  }
}
public class Sub extends Base {
  private String name;
  // 二号Test方法
  public void test() {
    System.out.println("子类重写的test" + name.length());
  }
  public static void main(String[] args) {
    // 下列语句将引发空指针异常
    Sub s = new Sub();
  }
}

上面代码第22行创建Sub类型的对象,需要先执行父类Base的构造方法,而父类中的构造方法调用了已经被重写的test方法,语句"test();“其实已经省略了this关键字,完整的写法是"this.test();”,而this指代调用方法的对象,故我们调用的是二号test方法,此时sub的成员变量是null,执行name.length()出现空指针异常。

如果希望某一个类不被继承,可以用final关键字进行修饰,比如java.lang.System类和java.lang.String类,我们也可以把构造器设置为private类型,另外提供一个静态方法来创建实例。

什么时候需要继承?

1.子类需要额外增加特有属性。比如Person类可以派生出Student类,因为Student有需要额外增加的属性,比如grade.但是Animal没有必要因为体型不一样而派生出BigAnimal和SmallAnimal,只需要在Aniaml类中增加一个成员变量size。

2.子类需要额外增加特有方法。

三、组合

如果仅仅是需要实现复用,我们也可以使用组合。继承子类可以直接获得父类的方法,而组合是把旧类对象作为新类的一个变量组合进来,通常在新类中用private修饰这个旧类对象。

选择继承还是组合呢?

继承表达的是一个is a的关系,是从一个已有类别中做一番改造,生成一个独特的版本,扩展出一个新的类,比如Animal和Wolf就适合选用继承。组合表达的是一种has a的关系,比如Person类需要复用Arm类就适合用组合。


相关文章
|
3月前
|
安全 Java API
Java Web 在线商城项目最新技术实操指南帮助开发者高效完成商城项目开发
本项目基于Spring Boot 3.2与Vue 3构建现代化在线商城,涵盖技术选型、核心功能实现、安全控制与容器化部署,助开发者掌握最新Java Web全栈开发实践。
386 1
|
3月前
|
安全 Cloud Native Java
Java 模块化系统(JPMS)技术详解与实践指南
本文档全面介绍 Java 平台模块系统(JPMS)的核心概念、架构设计和实践应用。作为 Java 9 引入的最重要特性之一,JPMS 为 Java 应用程序提供了强大的模块化支持,解决了长期存在的 JAR 地狱问题,并改善了应用的安全性和可维护性。本文将深入探讨模块声明、模块路径、访问控制、服务绑定等核心机制,帮助开发者构建更加健壮和可维护的 Java 应用。
274 0
|
3月前
|
监控 Cloud Native Java
Quarkus 云原生Java框架技术详解与实践指南
本文档全面介绍 Quarkus 框架的核心概念、架构特性和实践应用。作为新一代的云原生 Java 框架,Quarkus 旨在为 OpenJDK HotSpot 和 GraalVM 量身定制,显著提升 Java 在容器化环境中的运行效率。本文将深入探讨其响应式编程模型、原生编译能力、扩展机制以及与微服务架构的深度集成,帮助开发者构建高效、轻量的云原生应用。
379 44
|
4月前
|
Java 测试技术 API
2025 年 Java 开发者必知的最新技术实操指南全览
本指南涵盖Java 21+核心实操,详解虚拟线程、Spring Boot 3.3+GraalVM、Jakarta EE 10+MicroProfile 6微服务开发,并提供现代Java开发最佳实践,助力开发者高效构建高性能应用。
716 4
|
4月前
|
安全 Java 编译器
new出来的对象,不一定在堆上?聊聊Java虚拟机的优化技术:逃逸分析
逃逸分析是一种静态程序分析技术,用于判断对象的可见性与生命周期。它帮助即时编译器优化内存使用、降低同步开销。根据对象是否逃逸出方法或线程,分析结果分为未逃逸、方法逃逸和线程逃逸三种。基于分析结果,编译器可进行同步锁消除、标量替换和栈上分配等优化,从而提升程序性能。尽管逃逸分析计算复杂度较高,但其在热点代码中的应用为Java虚拟机带来了显著的优化效果。
151 4
|
4月前
|
Java API Maven
2025 Java 零基础到实战最新技术实操全攻略与学习指南
本教程涵盖Java从零基础到实战的全流程,基于2025年最新技术栈,包括JDK 21、IntelliJ IDEA 2025.1、Spring Boot 3.x、Maven 4及Docker容器化部署,帮助开发者快速掌握现代Java开发技能。
854 1
|
4月前
|
JavaScript 安全 前端开发
Java开发:最新技术驱动的病人挂号系统实操指南与全流程操作技巧汇总
本文介绍基于Spring Boot 3.x、Vue 3等最新技术构建现代化病人挂号系统,涵盖技术选型、核心功能实现与部署方案,助力开发者快速搭建高效、安全的医疗挂号平台。
247 3
|
4月前
|
存储 人工智能 算法
Java 大视界 -- Java 大数据在智能医疗影像数据压缩与传输优化中的技术应用(227)
本文探讨 Java 大数据在智能医疗影像压缩与传输中的关键技术应用,分析其如何解决医疗影像数据存储、传输与压缩三大难题,并结合实际案例展示技术落地效果。
|
4月前
|
机器学习/深度学习 算法 Java
Java 大视界 -- Java 大数据在智能物流运输车辆智能调度与路径优化中的技术实现(218)
本文深入探讨了Java大数据技术在智能物流运输中车辆调度与路径优化的应用。通过遗传算法实现车辆资源的智能调度,结合实时路况数据和强化学习算法进行动态路径优化,有效提升了物流效率与客户满意度。以京东物流和顺丰速运的实际案例为支撑,展示了Java大数据在解决行业痛点问题中的强大能力,为物流行业的智能化转型提供了切实可行的技术方案。