Java——面向对象三大特性2(继承)

简介: Java——面向对象三大特性2(继承)

2 继承


2.1 继承的基本概念


我们让 Cat 类和 Dog 类继承自 Animal 这个类。这样,Animal 类与 Cat 和 Dog类之间,就形成了继承关系。而被继承的类 Animal,被称为“父类”;而 Cat 与 Dog 这两个类继承自 Animal,被称为“子类”。


image.png



由于父类是子类的共性的抽象,是一个一般的类,因此,我们在进行继承关系设计的时候,应当尽量把子类的共性放在父类,特性放在子类。


image.png



这是一个非常典型的继承关系。生物,是一个很宽泛的概念,包括植物、动物等,都可以看做是生物。而动物,就是一种特殊的生物,因此,动物类是生物类的子类。同样的,哺乳动物是特殊的动物,而人又是特殊的哺乳动物。



父类是对子类共性的抽象,父类和子类的关系,是由一般到特殊的关系。在设计类的继承关系时,应当把共性放在父类,特性放在子类。


2.2 继承的基本语法


从语法上说,继承使用关键字:extends。在定义子类的时候,可以用 extends 关键字说明这个类的父类是哪一个类。代码如下:

class Animal{
    int age;
    boolean sex;
    public void eat(){
        System.out.println("Animal eat");
    }
    public void sleep(){
        System.out.println("sleep 8 hours");
    }
}
class Dog extends Animal{
    public void lookAfterHouse(){
        System.out.println("look after house");
    }
}
class Cat extends Animal{
    public void catchMouse(){
        System.out.println("catch mouse");
    }
}
public class TestDog {
    public static void main(String args[]){
        Dog d = new Dog();
        d.sex = true;
        d.age = 3;
        d.eat();
        d.lookAfterHouse();
    }
}


父类中的属性和方法,被子类继承之后,相当于子类中也有了相应的属性和方法。



在父类中定义了属性和方法之后,子类中就能够直接继承,这样,就让父类中的代码得到了重用,从而提高了代码的可重用性。同时,子类也能够写一些子类的特性,这样,就在父类的基础上增加一些功能,体现了面向对象的可扩展性。



2.3 什么能被继承?

class Parent{
    int value1;
    private int value2;
}
class Child extends Parent{
    public void method(){
        value1 = 100; // 编译正确 Child 类从父类中继承到 value1 属性
        value2 = 200; // 编译错误,Child 没有继承到 value2 属性
    }
}

image.png


那么 Child 类中有没有 value2 属性呢?从空间上来说,在创建 Child 对象的时侯,会在 Child 对象的内部,包含着一个 Parent 对象。



只有子类能够访问的属性和方法,才能够被子类继承。



2.4 访问权限修饰符


在 Java 中,总共有四种访问修饰符。除了我们之前介绍的 private 和 public 两个修饰符之外,还有两个跟访问权限相关的修饰符:default 以及 protected。要注意的是,default修饰符指的是:如果在属性或方法前面,不加任何的访问修饰符(即不加 private、public、protected),则访问权限是 default 权限。



要注意的是,要把一个属性设为 default 时,千万不能写上“default”这个单词。



访问权限为 default 的属性或者方法,只能被本类或者同包的其他类访问。


对于 default 的属性和方法而言,只有同包的类才能够访问。

package p1;
public class Parent{
    int value = 20;
}
package p1; //与 Parent 同一个包
public class Child1 extends Parent{
    public void m1(){
        System.out.println(value); //继承了 Parent 的 value 属性
    }
}
package p2; //与 Parent 不同包
public class Child2 extends p1.Parent{
    public void m2(){
    //编译错误!Child2 类和 Parent 不同包,没有继承 value 属性
    System.out.println(value); 
    }
}


用 protected 修饰符修饰的属性和方法,能够被本类内部、同包的类以及非同包的子类访问。

package p1;
public class Parent{
    protected int value;
}
package p1;
public class SamePackage{
    public void m1(){
        Parent p = new Parent();
        System.out.println(p.value); //同包的类可以访问
    }
}
package p2;
public class Child extends p1.Parent{
    public void m2(){
    //非同包的子类可以访问父类的 value 属性
        System.out.println(value);
    }
}
package p2;
public class Other {
    public void m2(){
        p1.Parent p = new p1.Parent();
        //编译错误!Other 类和 Parent 类并不同包,也没有继承关系,不能访问
        System.out.println(p.value); 
    }
}


image.png


private  default  protected  public


访问权限依次变宽



2.5 方法覆盖

class Dog extends Animal{
    public void lookAfterHouse(){
        System.out.println("Dog can look after house");
    }
    public void sleep(){
        System.out.println("sleep 6 hours");
    }
}
public class TestDog {
    public static void main(String args[]){
        Dog d = new Dog();
        d.sleep();
    }
}


子类中用一个特殊实现,来替换从父类中继承到的一般实现,这种语法叫做“方法覆盖”。



从语法上说,方法覆盖对方法声明的五个部分都有要求。



1. 访问修饰符相同或更宽


例如,父类的方法如果是 protected 方法,子类如果想要覆盖这个方法,则修饰符至少是 protected,也可以是 public,但是不能是 default 或者 private 的。



2. 返回值类型相同。


如果返回值类型不同,则会产生一个编译错误。


3. 方法名相同。


4. 参数表相同。



2.6 对象创建的过程


在有了继承关系之后,对象创建过程如下:



1. 分配空间。要注意的是,分配空间不光是指分配子类的空间,子类对象中包含的父类对象所需要的空间,一样在这一步统一分配。在分配空间的时候,会把所有的属性值都设为默认值。


2. 递归的构造父类对象。


3. 初始化本类属性。


4. 调用本类的构造方法。

class A{
    int valueA = 100;
    public A(){ valueA=150; }
}
class B extends A{
    int valueB = 200;
    public B(){ valueB=250; }
}
class C extends B{
    int valueC = 300;
    public C(){ valueC=350; }
}
public class TestInherit{
    public static void main(String args[]){
        C c = new C();
    }
}

我们在主方法中创建了一个 C 对象,则创建时的过程如下。


1. 分配空间。在分配空间时,会把 C、B、A 这三个对象的空间一次性都分配完毕,然后把这三个对象的属性都设为默认值。这样,value1,value2,value3 这三个属性都被设置为 0


2. 递归构造 C 对象的父类对象。在这里,要 C 对象的父类对象,就是 B 对象。因此,在这一步需要创建一个 B 对象。


3. 初始化 C 的属性,即把 valueC 赋值为 300


4. 调用 C 的构造方法。



其中,第 2 步,C 对象的父类为 B 对象,因此必须要先创建一个 B 对象。创建 B 对象不用重新分配空间,需要以下几步:


2.1 递归的构造 B 对象的父类对象


2.2 初始化 B 属性:把 vauleB 赋值为 200


2.3 调用 B 的构造方法。



在 2.1 这个步骤中,递归的创建 B 对象的父类对象,也就是创建 A 对象。创建 A对象不需要分配空间,因此,A 对象的创建有这样几步:


2.1.1 创建 A 对象的父类对象。这一步在运行时,没有任何的输出。


2.1.2 初始化 A 的属性,把 valueA 赋值为 100


2.1.3 调用 A 的构造方法。




总结一下:创建 C 对象的步骤一共有 7 步:


1. 分配空间。在一次分配空间时,会把整个继承关系中涉及到的类所需要的空间,都分配完毕,并把所有属性都设为默认值 0。


image.png


2. 初始化 A 类的属性。把 valueA 赋值为 100。


image.png


3. 调用 A 类的构造方法。会把 valueA 的值设为 150。


image.png


4. 初始化 B 类的属性,把 vauleB 赋值为 200。


image.png


5. 调用 B 类的构造方法,会把 valueB 的值设为 250。

image.png



6. 初始化 C 类的属性,把 valueC 赋值为 300。


image.png


7. 调用 C 类的构造方法,会把 valueC 的值设为 350。


image.png



2.7 super 关键字


在默认情况下,创建子类对象时,都会调用父类的无参构造方法。


super 关键字用法一:super 用在构造方法上


super 关键字的第一种用法,就是可以指定在递归构造父类对象的时候,调用父类的哪一个构造方法。


要格外注意的是,super 用在构造方法中时,只能作为构造方法的第一句。


然而,我们曾经介绍过,this 关键字可以在构造方法中,指明调用本类的其他构造方法。并且,对 this()来说,这个语句也只能作为构造方法的第一个语句。这样,在构造方法中,就不能够既使用 this(),又使用 super()。


这样,我们构造方法的第一个语句,就有了三种可能


1. super (参数) 指明调用父类哪个构造方法


2. this (参数) 指明调用本类哪个构造方法


3. 既不是 this(参数)又不是 super(参数)。


编译器会自动在这个构造方法中增加一个语句:“super();” ,即调用父类的无参构造方法。


super 关键字用法二:super 用作引用


最典型的用途是,使用 super在子类中,调用父类被覆盖的方法。

class Parent{
    public void m(){ 
        System.out.println(“m in Parent”);
    }
}
class Child extends Parent{
    public void m (){
        System.out.println(“m in Child”);
    }
    public void m1 (){
           this.m();
    }
    public void m2(){
        super.m();
    }
}
public class TestSuper{
    public static void main(String args[]){
        Child c = new Child();
        c.m1();
        c.m2();
    }
}

super 可以调用父类被覆盖的方法,这种特性在实际编程中有着广泛的应用。


2.8 单继承


Java 语言规定,每一个类只能有一个直接父类。


因此,Java 语言中单继承的特性, 被认为是 Java 语言相对于 C++语言,“简单性”的一个重要体现。


相关文章
|
7月前
|
Java API 数据处理
Java新特性:使用Stream API重构你的数据处理
Java新特性:使用Stream API重构你的数据处理
|
7月前
|
Java
Java基础语法与面向对象
重载(Overload)指同一类中方法名相同、参数列表不同,与返回值无关;重写(Override)指子类重新实现父类方法,方法名和参数列表必须相同,返回类型兼容。重载发生在同类,重写发生在继承关系中。
199 1
|
7月前
|
存储 Java 关系型数据库
Java 项目实战基于面向对象思想的汽车租赁系统开发实例 汽车租赁系统 Java 面向对象项目实战
本文介绍基于Java面向对象编程的汽车租赁系统技术方案与应用实例,涵盖系统功能需求分析、类设计、数据库设计及具体代码实现,帮助开发者掌握Java在实际项目中的应用。
284 0
Java API 开发者
235 0
|
8月前
|
安全 Java 编译器
Java面向对象
本文深入讲解了Java面向对象编程(OOP)的四大特性:封装、继承、多态与抽象,以及方法的设计与使用。通过示例展示了如何用类和对象组织代码,提升程序的可维护性与扩展性。
|
9月前
|
存储 Java 测试技术
Java基础 - 面向对象
面向对象编程是Java的核心,包含封装、继承、多态三大特性。封装隐藏实现细节,提升代码可维护性与安全性;继承实现类间IS-A关系,支持代码复用;多态通过继承、重写与向上转型,实现运行时方法动态绑定,提升系统扩展性与灵活性。
151 0
|
9月前
|
并行计算 Java API
Java List 集合结合 Java 17 新特性与现代开发实践的深度解析及实战指南 Java List 集合
本文深入解析Java 17中List集合的现代用法,结合函数式编程、Stream API、密封类、模式匹配等新特性,通过实操案例讲解数据处理、并行计算、响应式编程等场景下的高级应用,帮助开发者提升集合操作效率与代码质量。
410 1
|
9月前
|
安全 Java 微服务
Java 最新技术和框架实操:涵盖 JDK 21 新特性与 Spring Security 6.x 安全框架搭建
本文系统整理了Java最新技术与主流框架实操内容,涵盖Java 17+新特性(如模式匹配、文本块、记录类)、Spring Boot 3微服务开发、响应式编程(WebFlux)、容器化部署(Docker+K8s)、测试与CI/CD实践,附完整代码示例和学习资源推荐,助你构建现代Java全栈开发能力。
907 1
|
9月前
|
缓存 安全 Java
Java 并发新特性实战教程之核心特性详解与项目实战
本教程深入解析Java 8至Java 19并发编程新特性,涵盖CompletableFuture异步编程、StampedLock读写锁、Flow API响应式流、VarHandle内存访问及结构化并发等核心技术。结合电商订单处理、缓存系统、实时数据流、高性能计数器与用户资料聚合等实战案例,帮助开发者高效构建高并发、低延迟、易维护的Java应用。适合中高级Java开发者提升并发编程能力。
416 0
下一篇
开通oss服务