【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类就适合用组合。


相关文章
|
2天前
|
Java
【JAVA基础篇教学】第五篇:Java面向对象编程:类、对象、继承、多态
【JAVA基础篇教学】第五篇:Java面向对象编程:类、对象、继承、多态
|
3天前
|
Java 编译器 开发者
Java一分钟之-继承:复用与扩展类的特性
【5月更文挑战第9天】本文探讨了Java中的继承机制,通过实例展示了如何使用`extends`创建子类继承父类的属性和方法。文章列举了常见问题和易错点,如构造器调用、方法覆盖、访问权限和类型转换,并提供了解决方案。建议深入理解继承原理,谨慎设计类结构,利用抽象类和接口以提高代码复用和扩展性。正确应用继承能构建更清晰、灵活的代码结构,提升面向对象设计能力。
9 0
|
3天前
|
Java
java面向对象——包+继承+多态(一)-2
java面向对象——包+继承+多态(一)
16 3
|
3天前
|
SQL Java 编译器
java面向对象——包+继承+多态(一)-1
java面向对象——包+继承+多态(一)
16 2
|
4天前
|
Kubernetes Java 调度
Java容器技术:Docker与Kubernetes
Java容器技术:Docker与Kubernetes
16 0
|
4天前
|
存储 安全 Java
深入理解Java字节码与反编译技术
深入理解Java字节码与反编译技术
13 0
|
4天前
|
监控 Java Maven
揭秘Java Agent技术:解锁Java工具开发的新境界
作为JDK提供的关键机制,Java Agent技术不仅为Java工具的开发者提供了一个强大的框架,还为性能监控、故障诊断和动态代码修改等领域带来了革命性的变革。本文旨在全面解析Java Agent技术的应用场景以及实现方式,特别是静态加载模式和动态加载模式这两种关键模式。
26 0
|
6天前
|
机器学习/深度学习 Java C++
Java 继承
5月更文挑战第1天
|
11天前
|
存储 缓存 前端开发
Java串口通信技术探究3:RXTX库线程 优化系统性能的SerialPortEventListener类
Java串口通信技术探究3:RXTX库线程 优化系统性能的SerialPortEventListener类
36 3
|
11天前
|
安全 IDE Java
Java串口通信技术探究2:RXTX库单例测试及应用
Java串口通信技术探究2:RXTX库单例测试及应用
27 4