Java基础——(四)继承

1. 类、超类和子类

在Java中,通过关键字extends表示继承。extends表明正在构造的新类派生与一个已存在的类,已存在的类称为超类(superclass)、基类(base class)或父类(parent class);新类成为子类(subclass)、派生类(derived class)。
假设我们有两个类——Employee类和Manager类,其中Manager类派生自Employee。

public class Employee {private String name;private double salary;public String getName() {return name;}public void setName(String name) {this.name = name;}public double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;}
}public class Manager extends Employee {private double bouns;public void setBouns(double bouns) {this.bouns = bouns;}public double getBouns() {return this.bouns;}
}

子类可以使用父类的公有方法,而父类不能使用子类的扩展方法。以Manager和Employee为例,虽然Manager类没有定义getName方法,但是因为父类Employee定义了,所以Maanger自动继承了父类Employee中的这些方法,同时还自动继承了name、salary这2个域。而对于getBouns方法,因为只有Manager有这个方法,而父类Employee没有这个方法,因此Employee对象不能使用这个方法。
在这里插入图片描述
在通过扩展父类定义子类的时候,仅需要指出子类与超类的不同之处,因此在设计类的时候,应该将通用的方法放在父类中,而将具有特殊用途的方法放在子类中。
然而,父类中的有些方法对子类并不一定适用,比如Maanger类中的getSalary方法应该返回薪水和奖金的综合,因此需要提供一个新的方法来覆盖(override)超类中的这个方法。因为salary在父类定义的访问操作符为private,因此子类Manager不能直接访问到salary,所以需要使用getSalary这个公有方法来访问:

package org.example.test;public class Manager extends Employee {private double bouns;public void setBouns(double bouns) {this.bouns = bouns;}public double getBouns() {return this.bouns;}@Overridepublic double getSalary() {return getSalary() + this.bouns;}
}

但是,上面的代码是错误的,问题出在调用getSalary的语句上,因为Manager类也有一个getSalary方法,所以这条语句会导致无限次地调用字节,导致整个程序崩溃。
这里我们的本意是调用父类Employee中的getSalary方法,而不是当前类的getSalary方法,因此,可以使用super关键字来解决:

package org.example.test;public class Manager extends Employee {private double bouns;public void setBouns(double bouns) {this.bouns = bouns;}public double getBouns() {return this.bouns;}@Overridepublic double getSalary() {return super.getSalary() + this.bouns;}
}

super也适用于构造函数中,假设我们的父类Employee有以下构造函数:

public Employee(String name, double salary) {this.name = name;this.salary = salary;
}

然后子类要在构造函数中,去初始化name,salary和bouns的信息,这个使用可以使用super来调用父类的构造函数对父类的私有域进行初始化:

public Manager(String name, double salary, double bouns) {super(name, salary);this.bouns = bouns;}

1.1 多态

在下面代码中,我们定义一个包含三个雇员的数组,然后将经理和普通雇员都放到数组中,在循环中输出每个人的薪水,此时会看到1和2仅输出基本薪水,而0输出的是基本薪水加奖金,因为0对应的是Manager对象。
在这里插入图片描述
尽管我们这里将所有雇员对象都声明为Employee类型,但是在实例化对象是,因为staff[0]引用的是Manager对象,所以在getSalary时调用的是Manager类中的getSalary方法,也就是说虚拟机直到实际引用的对象类型,并且能够正确地调用相应的方法。
一个对象变量可以指示多种实际类型的现象我们称之为多态(polymorphism)。在java中,对象变量是多态的,一个Employee变量既可以引用一个Employee类对象,也可以引用一个Employee类的任何一个子类的对象。
在运行时能够自动地选择调用哪个方法的现象成为动态绑定(dynamic binding)。动态绑定有一个非常重要的特性,它无需对现存的代码进行修改,就可以对程序进行扩展。

1.2 继承层次

继承并不仅限于一个层次,例如Manager类可以派生自Person类。由一个公共超类派生出来的所有类的集合被成为继承层次(inheritance hierarchy)。在继承层次中,从某个特定的类到其祖先的路径被成为该类的继承链(inheritance chain)。

1.3 阻止继承:final类和方法

有时候,我们希望阻止人们利用某个类定于子类,不允许被扩展的类被成为final类,如果在定义类的时候,使用final修饰符,就表明这个类是final类。声明格式如下:

final class Executive extends Manager {
}

类中的特定方法也可以被声明为final,这样子类就不能覆盖这个方法(final类中的所有方法自动地成为final方法)。

class Employee {...public final String getName() {...}
}

1.4 强制类型转换

对象引用的转换语法与数值表达式的类型转化类似,仅需要用一对圆括号将目标类名括起来,并放置在需要转换的对象引用之前即可。如:

Manager boss = (Manager)staff[0];

将一个值存入变量时,编译器将检查是否允许该操作。将一个子类的引用赋给一个超类变量,编译器是允许的,但将一个超类的引用赋给一个子类变量,必须进行类型转换,这样才能通过运行时的检查。
对于强制类型转换,有以下要求:

  • 只能在继承层次内进行类型转换
  • 在将超类转换为子类之前,最好使用instanceof进行检查

1.5 抽象类

对于Employe和Student,它们都有一个特性,都是一个Person,因此,我们可以做进一步的抽象,抽象一个Person类,而Employee和Student都是Person的子类。假设我们现在需要对Employee和Student增加一个getDescription方法来返回对这个人的简要描述,此时可以很方便的在Employee类和Student类实现,但是在Person类中应该提供什么内容?这个时候,我们就可以使用abstract关键字,通过abstract关键字,我们就完全不需要实现这个方法。

public abstract String getDescription();

如果一个类包含一个或多个抽象方法,那么这个类本身必须被声明为抽象的。抽象类除了抽象方法之外,还可以包含具体数据和具体方法。

abstract class Person {private String name;public String getName() {return this.name;}public void setName(String name) {this.name = name;}public Person(String name) {this.name = name;}public abstract String getDescription();
}

抽象方法充当着占位的角色,它们的具体实现在子类,扩展抽象类可以有两种选择,一种是在子类中定义部分抽象方法或抽象方法也不定义,这样子类也必须标记为抽象类;另一种是定义全部的抽象方法,这样子类就不是抽象的。
注意,抽象类不能被实例化。

在这里插入图片描述

1.6 受保护访问

在一般情况下,我们将类中的域标记为private,而方法标记为public,任何声明为private的内容对其他类都是不可见的,包括子类。但有时候,我们希望父类中的某些方法运行被子类访问或子类的方法能访问父类的某个域,这个时候,需要将这些方法或域声明为protected。

2. Object:所有类的超类

Object类是Java中所有类的始祖,在Java中每个类都是由它扩展而来,如果没有明确地指出某个类的超类,那么Object就被认为是这个类的超类。但我们不需要显示地继承它,而言就是不需要这样写:

class Employee extends Object

在java中,只有基本类型不是对象,所有的数组类型,不过是对象数组还是基本类型的数组都扩展自Object类。

2.1 equals方法

Object类中的equals方法用于检测一个对象是否等于另一个对象,在Object类中,这个方法将判断两个对象是否具有相同的引用。如果两个对象具有相同的引用,它们一定是相等的。但对于多数类来说,这种判断并没有意义,一般来说,应该检测两个对象状态的相等性,如果两个对象的状态相等,就认为这两个对象是相等的。
以Empolyee类为例,但两个Employee对象的姓名、薪水等都相等时,我们便认为它们是相等的,因此我们复写equals类:

 @Overridepublic boolean equals(Object other) {if (this == other) {return true;}if (other == null) {return false;}if (getClass() != other.getClass()) {return false;}Employee otherEmployee = (Employee) other;return this.getName().equals(otherEmployee.getName())&& salary == otherEmployee.salary;}

在子类中定义equals方法时,首先调用超类的equals,如果检测失败,对象就不可能相等,如果超类中的域都相等,就需要比较子类中的实例域,比如下面是Maanger的equals方法:

 @Overridepublic boolean equals(Object other) {if (!super.equals(other)) {return false;}Manager otherManager = (Manager) other;return bouns == otherManager.bouns;}

2.2 hashCode方法

散列码(hash code)是由对象导出的一个整型值,散列码是没有规律的,如果x和y是两个不同的对象,x.hashCode()和y.hashCode()基本上不会相等。
由于hashCode方法定义在Object类中,因此每个对象都有一个默认的散列码,其值为对象的存储地址。
一般来说,如果我们重新定义了equals方法,那么就必须重新定义hashCode方法,以便用户可以将对象插入到散列表中。
hashCode方法应该返回一个整型数值(可以是负数),并合理组合实例域的散列码,以便使各个不同的对象产生的散列码更加均匀,比如下面是Employee类的hashCode方法:

@Overridepublic int hashCode() {return 7 * getName().hashCode()+ 11 * new Double(salary).hashCode();}

上面的写法可能导致空指针异常,我们可以使用null安全的方法Objects.hashCode进行优化,如果参数为null,这个方法返回0:

@Overridepublic int hashCode() {return 7 * Objects.hash(getName())+ 11 * new Double(salary).hashCode();}

当需要组合多个散列值是,可以调用Objects.hash并提供多个参数,这个方法会对各个参数调用Objects.hashCode并组合这些散列值。

  @Overridepublic int hashCode() {return Objects.hash(getName(), salary);}

注意,equals与hashCode的定义必须一致,也就是说,如果x.equals(y)返回true,那么x.hashCode()就必须与y.hashCode()具有相同的值。

2.3 toString方法

在Object中还有一个重要的方法,就是toString方法,用于返回表示对象值的字符串。如果没有复写,返回的是“类路径@地址”。
在这里插入图片描述
一般我们的toString会遵循这样的格式:类的名字,随后是一对方括号括起来的域值,下面是Employee类的toString方法:

  @Overridepublic String toString() {return getClass().getName() + "[name=" + getName()+ ",salary=" + salary+ "]";}

在这里插入图片描述

3. 泛型数组列表

在之前,我们定义数组的时候,一般会给数组一个确定的大小,数组的大小一旦确定,后续我们想要动态更改数组长度会比较困难。在Java中,提供了一个ArrayList的类,它使用起来有点像数组,但在添加或删除元素时,具有自动调节数组容量的功能,而不需要为此编写任何代码。
ArrayList是一个采用类型参数(type parameter)的泛型类(generic class)。为了指定数组列表保持的元素对象类型,需要用一对尖括号将类名括起来加载后面,例如ArrayList。下面声明和构造一个保存Employee对象的数组列表:

ArrayList<Employee> staff = new ArrayList<>();
// 或
ArrayList<Employee> staff = new ArrayList<Employee>();

第一种写法被称为“菱形”写法,因为尖括号<>就像是一个菱形,可以结合new操作符使用菱形语法,编译器会检测新值是声明,如果赋值给一个变量,或传递某个方法,或者从某个方法返回,编译器会检测这个变量、参数或方法的泛型类型,然后将这个类型放在<>中。

方法作用
boolean add(T obj)在数组列表尾端添加一个元素,永远返回true
int size()返回存储在数组列表中当前元素数量
T get(int index)返回数组列表中的第index 个元素
void set(int index, T obj)设置数组列表中的第index个元素为obj
T remove(int index)删除数组列表中你的第index个元素

在这里插入图片描述

4. 对象包装器与自动转箱

有时候,需要将int这样的基本类型转换为对象,所有的基本类型都有一个与之对应的类,例如Integer类对应基本类型int。通常,这些类称为包装器(wrapper),这些对象包装器拥有很鲜明的名字:Integer, Long, Float, Double, Short, Byte, Character, Void和Boolean。对象包装器类还是final,因此不能定义它们的子类。
当我们像定义一个整型数组列表时,不能写成ArrayList,而是要写成ArrayList。此外,在使用整型数组列表添加元素时,可以直接使用基本类型:

list.add(3); 
// java会自动将其变换为:
list.add(Integer.valueOf(3));

这种变换被称为自动装箱(autoboxing)。
相反,当将一个Integer对象赋给一个int值时,将会自动地拆箱:

int n = list.get(i);
// java会自动将其变换为:
int n = list.get(i).intValue();

对于基本类型,我们可以直接使用==来判断两者的值是否相等,但是对于包装类就不一定了,因为包装类是一个对象。以Integer类为例,在Integer类中,会维护一段缓存,缓存值位于-128到127。
在这里插入图片描述
在这段区间内,通过自动装箱得到的Integer对象,它们的值是相等的,如下图所示:
在这里插入图片描述
如果超过-128到127这个区间,那么通过自动装箱时获取的值是不相等的
在这里插入图片描述
如果不是通过自动装箱,而是通过实例化(也就是new)出来的对象,即便处于-128到127这个区间,使用 ==也是不相等的。
在这里插入图片描述

5. 参数数量可变的方法

java提供了可以用可变的参数数量调用的方法,以下列代码为例:
在这里插入图片描述
这里的省略号…是java代码的一部分,它表明这个方法可以接收任意数量的对象。实际上,上面的printWord接收两个参数,一个是Date类型的参数,另一个是String[]数组,也就是说,String…参数与String[]完全一样。

6. 枚举类

public enum Size {SMALL, MEDIUM, LARGE, EXTRA_LARGE};

在上面的代码中,这个声明定义的类型是一个类,它有4个实例。在定义枚举的时候,我们已经将它所拥有的实例都定义出来了,因此,在比较两个枚举类型的值时,不需要调用equals方法,直接使用==就可以了。当然,如果我们需要的话,还可以为枚举类型添加一些构造器、方法和域:

public enum Size {SMALL("S"),MEDIUM("M"),LARGE("L"),EXTRA_LARGE("XL");private String abbreviation;Size(String abbreviation) {this.abbreviation = abbreviation;}public String getAbbreviation() {return this.abbreviation;}
}

所有的枚举类型都是Enum类的子类,它们继承这个类的许多方法,其中最有用的是toString,这个方法能够返回枚举常量名,比如Size.SMALLL.toString()将返回字符串"SMALL"。toString的逆方法是静态方法valueOf,例如

Size s = Enum.valueOf(Size.class, "SMALL");
或者
Size s = Size.valueOf("SMALL");

上面的代码会将s设置为Size.SMALL。
每个枚举类型都有一个静态的values方法,它将返回一个包含全部枚举值的数组,比如下面的代码会返回Size.SMALL, SIZE.MEDIUM, SIZE.LARGE和SIZE.EXTRA_LARGE的数组。

Size[] values = Size.values();

参考链接

《Java核心技术卷I》(网盘链接:https://pan.quark.cn/s/06c58d47dce1)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/61521.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python语法基础(四)

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 高阶函数之map 高阶函数就是说&#xff0c;A函数作为B函数的参数&#xff0c;B函数就是高阶函数 map&#xff1a;映射 map(func,iterable) 这个是map的基本语法&#xff0c;…

《datawhale2411组队学习 模型压缩技术7:NNI剪枝》

文章目录 一、NNI简介二、 NNI剪枝快速入门2.1 加载并训练模型2.2 模型剪枝2.3 模型加速&#xff08;剪枝永久化&#xff09;2.4 微调压缩模型2.5 Slim Pruner测试 三、 使用NNI3.0进行Bert压缩&#xff08;剪枝、蒸馏)3.1 数据预处理3.2 训练模型3.3 设置模型蒸馏函数3.4 修剪…

day22:lamp项目部署

一&#xff0c;lamp概述 lamp概述 LAMP 是一组开源软件的缩写&#xff0c;用于搭建动态网站或Web应用程序的基础环境。LAMP 代表了四个主要的组成部分&#xff1a; Linux&#xff1a;操作系统&#xff0c;LAMP 环境的基础。通常使用的是 Linux 发行版&#xff0c;如 CentOS、…

【提高篇】3.6 GPIO(六,寄存器介绍,下)

目录 2.3 输出速度寄存器OSPEEDR(GPIOx_OSPEEDR) (x = A..I) 2.4 上拉/下拉寄存器 (GPIOx_PUPDR) (x = A..I) 2.5 输入数据寄存器(IDR) 2.6 输出数据寄存器(ODR) 2.7 置位/复位寄存器(BSRR) 2.8 BSRR与ODR寄存器的区别 2.3 输出速度寄存器OSPEEDR(GPIOx_OSPEEDR) (…

Java中的“抽象类“与“接口“之间的关系

在Java中&#xff0c;抽象类和接口都是用于实现抽象的机制&#xff0c;但它们在设计目的、使用方式以及功能上有一些不同。下面我将通过文字和代码示例来详细讲解它们之间的关系。 1. 抽象类&#xff08;Abstract Class&#xff09; 特点&#xff1a; 抽象类使用abstract关键字…

ROS2-参数服务器

在 ROS 2 中&#xff0c;节点之间可以通过参数服务器共享和获取参数。这意味着一个节点可以声明一个参数&#xff0c;而其他节点可以读取或修改这个参数。这是通过 ROS 2 的参数系统实现的&#xff0c;它允许节点在参数服务器上声明、设置和获取参数 。 0. 背景 系统有多个 RO…

CSS3网站

&#xff08;看不懂英文的记得点击右上角Change language更改语言&#xff09;&#xff1a; https://developer.mozilla.org/en-US/docs/Learn/CSS/Introduction_to_CSS/Selectors CSS选择器 https://developer.mozilla.org/en-US/docs/Web/CSS/color_value 颜色值 https://ser…

js进阶-中括弧运算

一、题目 下面代码的输出是什么 console.log([[]][[]] [[]]); 二、变量类型转换 1.对于数字运算[负号-]、[减号-]、[乘号*]、[除号/]、[自加号]等&#xff0c;其后的变量都会被尝试转换为数字&#xff1b; 如果其后变量是基础数据类型&#xff0c;那么按基础数据类型转换…

【IMF靶场渗透】

文章目录 一、基础信息 二、信息收集 三、flag1 四、flag2 五、flag3 六、flag4 七、flag5 八、flag6 一、基础信息 Kali IP&#xff1a;192.168.20.146 靶机IP&#xff1a;192.168.20.147 二、信息收集 Nmap -sP 192.168.20.0/24 Arp-scan -l nmap -sS -sV -p- -…

记一次腾讯云海外服务器http能正常访问https访问拒绝问题处理过程

最近双十一, 购了一台腾讯云的海外服务器&#xff0c; 开通后就是一堆的服务器软件安装数据上传和配置&#xff0c;没想到&#xff0c;等待配置完成后才发现https无法正常访问&#xff0c;于是开启了自查。 1. 检查nginx软件的ssl配置 nginx http https配置参考 server {l…

ubuntu多版本安装gcc

1.ubuntu安装gcc 9.3.1 $ sudo apt update $ sudo apt install gcc-9 g-9 二、配置GCC版本 安装完成后&#xff0c;需要使用update-alternatives命令来配置GCC版本。这个命令允许系统在多个安装的版本之间进行选择 1.添加GCC 9.3.1到update-alternatives管理 $ sudo update-a…

hdlbits系列verilog解答(mt2015_muxdff)-90

文章目录 一、问题描述二、verilog源码三、仿真结果一、问题描述 本节要实现的电路来自于ECE253 2015中期测试问题5。要实现以下如图所示的时序电路中复用器及D触发器子模块。 模块声明 module top_module ( input clk, input L, input r_in, input q_in, output reg Q); 思路…

Word - 图片右键保存

以右键另存为的方式保存word里面的图片&#xff0c;确实会导致图片质量降低。一般图片可能看不出差别&#xff0c;但一些文字内容较多的图片&#xff0c;就会影响阅读。 针对这个问题&#xff0c;可以通过解压word文件的方法得到里面的图片。 首先&#xff0c;备份一下原文件…

Android 图形系统之七:SurfaceFlinger

一. 引言 什么是 SurfaceFlinger&#xff1f;SurfaceFlinger 的核心作用和地位&#xff1f;为什么需要了解 SurfaceFlinger&#xff1f; 二. SurfaceFlinger 的基本概念 Surface 和 SurfaceFlinger 的关系SurfaceFlinger 与图形渲染&#xff08;OpenGL ES 和 Vulkan&#xf…

YOLOv11原创改进专栏|专栏介绍目录

一、专栏简介 本专栏自2024年12月01日开始持续更新&#xff0c;专栏主要面向YOLOv11的各种改进&#xff0c;主要改进方向为Backbone&#xff08;主干&#xff09;、Conv、C2f、注意力机制、Neck以及检测头的改进&#xff0c;本专栏会涉及到提高精度、轻量化、分割等方面的内容。…

shell脚本小练习#002:通过shell脚本创建目录

实例1&#xff1a; # 编写一个shell脚本实现以下需求&#xff1a; # 执行脚本&#xff0c;当前目录下创建一个名为test的目录&#xff0c; # 然后在新建的test目录下创建3个文件夹分别命名为case1~case3&#xff0c;在每个文件下创建一个log.txt文件 # 编写一个shell脚本实现…

AI开发-深度学习框架-PyTorch-torchnlp

1 需求 Welcome to Pytorch-NLP’s documentation! — PyTorch-NLP 0.5.0 documentation 2 接口 3 示例 4 参考资料

基于Java Springboot宠物服务中心微信小程序

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 微信…

算法妙妙屋-------1.递归的深邃回响:全排列的奇妙组合

全排列的简要总结 全排列&#xff08;Permutation&#xff09;是数学中一个经典的问题&#xff0c;指的是从一组元素中&#xff0c;将所有元素按任意顺序排列形成的所有可能序列。 特点 输入条件&#xff1a; 给定一组互异的元素&#xff08;通常为数组或字符串&#xff09;。…

内网穿透步骤

步骤 第一次需要验证token window和linux的方法不同。 然后 启动 cpolar 服务&#xff1a; 在命令窗口中输入 cpolar.exe htttp 8080&#xff0c;启动内网穿透服务。确保命令窗口保持开启状态&#xff0c;以维持穿透效果。 cpolar.exe hhttp 8080 成功后 注意事项 命令窗口…