3.java——继承及拓展(保姆级别教程,万字解析,匠心制作)

三.继承——节省了共有属性和方法的代码:语法 class Student extends Person

1.继承基础

1.继承首先是面向对象中非常强的一种机制,他首先可以复用代码(name ,age),让我们的获得了Person全部功能和属性,只需要为Student编写新增的功能。获得分数等等

  • java使用extends继承
  • 注意点:1.Person被称为基类,超类,父类。Student被称为子类,拓展类
    - 2.继承树
  • 定义Person类时,我们没有加上extends,在java中,没有写extends的类编译器会自动加上extends Object(对象)。所以,任何类,除了Object(对象),都会继承某个类
  • Preson和Student的继承树。
  • student ——> Person——>Object

- 3.java只允许class继承一个类,而C++可以继承很多类

  • java一个类clas只允许有且只有一个父类。只有特殊的Object特殊,它没有父类,他是所有继承的头部。

  • 在这里插入图片描述

  • 4.继承有个特点:java中所有类都是公有继承。拿到全部属性,但子类没有办法访问父类的private字段和方法。

  • 就是Student没有办法访问name和age, Student s1=new Student(); s1.name 报错error,这使得继承的作用大大削弱了
    - 这时我们需要用protect替换private了,这样可以访问了。所以,protect关键字可以把字段和方法全部控制在继承树内部。

class Student extends Person{public String hello(){return "hello, "+super.name;    }
}

2.super关键字和this关键字

**

(1)this.属性或方法,别的不多讲,就拿下面的这个吃苹果的经典故事展开。**

this在C++是指针,是类本身属性地址,java中是当前对象,this可以调用方法,属性,指向对象本身。
为什么eatApple()后面还可以接函数。apple可以用 . 操作符访问,因为人家是引用。但是eatApple方法的返回类型是是类名并且返回的是this相当于结果

public class Apple { 
int i = 0; 
Apple eatApple(){ 
i++; 
return this; 
} 
public static void main(String[] args) { 
Apple apple = new Apple(); 
apple.eatApple().eatApple(); 
} 
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(2)super关键字表示超类(父类)。有父亲构造才有孩子构造。先造出父亲再搞出你

先造出父亲再搞出你
在Java中,任何类的构造方法的第一行语句必须是调用父类的构造方法,是因为Java中的继承关系。当一个类继承自父类时,它会继承父类的属性和方法。在创建子类的对象时,必须先创建父类的对象,并进行初始化。通过调用父类的构造方法来完成父类对象的初始化,然后再对子类对象进行初始化。

子类引用父类字段时,可以用super.fieldName 领域名
实际上super.name, this name, name; 效果都是一样的。编译器会自动定位到父类的name字段
但有些情况必须用super,比如说:

 class Person 
{public static void main(String[] args){Student s1=new Student("张三",12,67);      }   
}class Person
{protected String name;protected int age;Person(String name;int age){this.name=name;this.age= age;    }
}class Student extends Person{protected int score;public Student(String name,int age,int score){this.score =score;    }
} 

这里会报错,大意是在student的构造方法中,无法调用Person的构造方法。
这是因为在java中,任何class的构造方法中,(包括父类自己),第一行语句必须是调用父类的构造方法。如果没有明确地调用父类的构造方法,编译器会帮我们自动加一句super(无参);
所以,Student类的构造方法实际上必须在Student(){super() }加上 , super(有参数列表),必须加上参数列表,否则编译失败
‘错误写法没有形式参数’

class Student extends Person{
protected int score;
public Student(String name,int age,int score)
{
super();  //'自动调用父类的构造方法',Person(), 爸爸空手来了
this.socre =score;
}'正确写法:'
class Student extends Person{
protected int score;
public Student(String name,int age,int score)
{
super(name ,age);//自动调用父类的构造方法Person(Sting,int),爸爸带刀来了
this.socre =score;
}

所以我们得出结论:如果父类没有默认构造方法,子类就必须显示调用super()并给出参数以便以编译器定位到父类的一个合适的构造方法。
这里顺便引出了结论:即子类不会继承父类的任何构造方法。子类默认的构造方法student()是编译器自动生成的,而不是继承的!!!

class Person{private String name;private int age;public void setName(String name){}public String getName(){}
}class Student extends Person{private int score;//Student获得了Person全部属性public void setScore(){}public void getScore(){}    
}

3.方法重写

方法的重写与重载虽然名字很类似,但是确是完全不一样的东西,重写描述是子类和父类的东西。重载必须在同一个类中。
不同点:重写必须全部方法签名和方法返回值类型一模一样。
@Override注解可写可不写!!!
在这里插入图片描述

4.阻止继承(难点):

关键字:final, sealed,static

final——最后的,固定的,不可以改变的,java中被誉为最终变量,最终方法,最终类——起到限制作用,俗称:铁公鸡——一毛不拔!!!一点也不给机会

最终变量(Final Variables): 在Java和C#中,使用"final"关键字声明的变量被称为最终变量或常量。
最终变量只能被赋值一次,一旦赋值后就不能再改变其值。
最终方法(Methods): 使用"final"关键字声明的方法不能被子类重写(override)。
这意味着如果你在一个父类中声明了一个方法为"final",那么在任何继承这个父类的子类中都不能有与之同名且参数列表相同的方法。
最终类(Final Classes): 当一个类被声明为"final"时,它不能被其他类继承。
这种限制可以用来保护类的设计和实现细节,防止被不适当或意外的修改。
总的来说,"final"关键字主要用于限制变量的可变性、方法的可重写性和类的可继承性,以增强代码的稳定性和可预测性。

sealed——有封条的,密封的,permits(允许)他们两一般同时用 ——封条其实可以被公家撕下来,也可以被无赖撕下来,permits就是屁股后面跟着的就是“特殊人群”。特殊人群特殊对待!!!

"sealed"关键字主要用于限制类或接口的继承性,以增强代码的控制力和可预测性。在某些情况下,它可以提高编译时的安全性和性能优化。
sealed class Shape permits Rect,Circle.。。。只允许跟着屁股后面的东西继承**
正常情况下,只要某个class没有final修饰符,那么任何类都可以从该class继承。!!
从Java 15开始,允许使用sealed修饰class,并通过permits明确写出能够从该class继承的子类名称。
例如,定义一个Shape类:

public sealed class Shape permits Rect, Circle, Triangle {'你的形状只允许能由于矩形圆形或者三角形继承'
}

上述Shape类就是一个sealed类,它只允许指定的3个类继承它。如果写:

public final class Rect extends Shape {...}

是没问题的,因为Rect出现在Shape的permits列表中。但是,如果定义一个Ellipse(椭圆)就会报错:

public final class Ellipse extends Shape {...}

// Compile error: class is not allowed to extend sealed class: Shape

原因是Ellipse并未出现在Shape的permits列表中。这种sealed类主要用于一些框架,防止继承被滥用。
sealed类在Java 15中目前是预览状态,要启用它,必须使用参数–enable-preview和–source 15。

1.sealed 密封的,不能被继承和重写
2.final 最后的,相当于C++中const不可以改 属性不可以改

static ——静态的,停止的 ,共享,在堆区开辟,static面前众生平等,堆区,这片区域很安静,来来往往方法和属性都必须带有static的标签老老实实。俗称:校长

静态成员变量(Static Member Variables): 静态变量是属于类的,而不是属于类的实例。
当类被加载时,静态变量就会被创建并分配内存。 所有类的实例共享同一个静态变量的值,也就是说,无论创建多少个对象,静态变量只有一份拷贝。
静态变量可以通过类名直接访问,不需要创建类的实例。

静态方法(Static Methods): 静态方法也是属于类的,而不是属于类的实例。
静态方法可以在没有类的实例的情况下被调用,同样通过类名直接访问。
静态方法不能访问非静态成员变量或非静态方法,因为它们在没有对象实例的情况下无法确定。

静态块(Static Blocks): 静态块是在类加载时执行的一段代码,通常用于初始化静态变量或者执行一些必要的设置操作。

静态内部类(Static Nested Classes): 静态内部类与非静态内部类的主要区别在于,它不需要对外部类的实例进行引用。
静态内部类可以直接通过外部类名加"."来访问。

静态导包(Static Import): 在Java中,静态导入允许程序员在不使用类名的情况下直接访问静态成员(如静态变量和静态方法)。
这可以减少代码中的冗余,并提高可读性。

总的来说,"static"关键字主要用于表示那些与类的实例无关,而是与类本身相关的属性和行为。这些静态元素在类的生命周期中有着特殊的地位和作用。

1.静态方法只能访问静态方法和属性。
2. 静态方法和静态成员变量可以直接有类名.静态方法(静态变量)的方式调用。并且还会引入方法重写 的概念!!!
3.静态方法可以被子类继承或重载,但是不可以被子类重写
4.如果子类中的静态方法和父类的静态方法名,参数,返回值类型都一样,这是被允许的,属于再次声明。

public class Test{public static int func(int num){return num;    }
}public test extends Test
{public int func(int num){return num+2;    }//报错了func也必须是 static
}

static 除了修饰属性和方法外,还有的功能:
静态代码块
在这里插入图片描述

可用于类的初始化操作。进而提升程序的性能
由于静态代码块随着类的加载而执行,因此,很多时候会将只需要进行一次的初始化操作放在 static 代 码块中进行。**

5.向上转型,向下转型——人往高处走,水往低处流。

个人总结:类可以看成一种复杂的数据类型对象看成普通的引用变量,向上转型成功的原因更可能是因为对象拥有两种数据类型,
对象拥有两种数据类型他可以随心所欲了。因为数据类型决定了你可以拥有多大的活动(内存)空间,和你每走一步能走多远(运算,数据操作)

向上转型——铁定成功!!!

如果一个引用变量的类型是Student,那么它可以指向一个Student类型的实例:

Student s = new Student();

如果一个引用类型的变量是Person,那么它可以指向一个Person类型的实例:

Person p = new Person();

现在问题来了:如果Student是从Person继承下来的,那么,一个引用类型为Person的变量,能否指向Student类型的实例?

Person p = new Student(); // ???
多态性(如果加上方法重写就是有多态性,p有了两重身份,他本身数据类型就是Person 但是指向类型确是Student

测试一下就可以发现,这种指向是允许的!
这是因为Student继承自Person,因此,它拥有Person的全部功能。Person类型的变量,如果指向Student类型的实例,对它进行操作,是没有问题的!
这种把一个子类类型安全地变为父类类型的赋值,被称为向上转型(upcasting)。
向上转型实际上是把一个子类型安全地变为更加抽象的父类型:

Student s = new Student();
Person p = s; // upcasting, ok
Object o1 = p; // upcasting, ok
Object o2 = s; // upcasting, ok

注意到继承树是Student > Person > Object,所以,可以把Student类型转型为Person,或者更高层次的Object。

向下转型——几乎失败!!!

和向上转型相反,如果把一个父类类型强制转型为子类类型,就是向下转型(downcasting)。例如:

Person p1 = new Student(); // upcasting, ok
Person p2 = new Person();
Student s1 = (Student) p1; // ok
Student s2 = (Student) p2; // runtime error! ClassCastException!

如果测试上面的代码,可以发现:
Person类型p1实际指向Student实例,Person类型变量p2实际指向Person实例。在向下转型的时候,把p1转型为Student会成功,因为p1确实指向Student实例(P1有双重身份),把p2转型为Student会失败,因为p2的实际类型是Person,不能把父类变为子类,因为子类功能比父类多,多的功能无法凭空变出来。(子类条件更为苛刻)
因此,向下转型很可能会失败。失败的时候,Java虚拟机会报ClassCastException。
为了避免向下转型出错,Java提供了instanceof操作符,可以先判断一个实例究竟是不是某种类型:

Person p = new Person();
System.out.println(p instanceof Person); // true
System.out.println(p instanceof Student); // falseStudent s = new Student();
System.out.println(s instanceof Person); // true
System.out.println(s instanceof Student); // trueStudent n = null;
System.out.println(n instanceof Student); // false

instanceof实际上判断一个变量所指向的实例是否是指定类型,或者这个类型的子类。如果一个引用变量为null,那么对任何instanceof的判断都为false。
利用instanceof,在向下转型前可以先判断:

Person p = new Student();
if (p instanceof Student) {// 只有判断成功才会向下转型:Student s = (Student) p; // 一定会成功
}

从Java 14开始,判断instanceof后,可以直接转型为指定变量,避免再次强制转型。例如,对于以下代码:

Object obj = "hello";
if (obj instanceof String) {String s = (String) obj;System.out.println(s.toUpperCase());
}

区分继承和组合——is和has逻辑性

在使用继承时,我们要注意逻辑一致性。
考察下面的Book类:

class Book {protected String name;public String getName() {...}public void setName(String name) {...}
}

这个Book类也有name字段,那么,我们能不能让Student继承自Book呢?

**class Student extends Book {protected int score;
}**

显然,语法上是合理的,但从逻辑上讲,这是不合理的,,Student不应该从Book继承,而应该从Person继承。
究其原因,是因为Student是Person的一种,它们是is关系,而Student并不是Book。实际上Student和Book的关系是has关系。
具有has关系不应该使用继承,而是使用组合,即Student可以持有一个Book实例:人是书的持有者,书只是学生类中的一个属性
**

class Student extends Person {protected Book book;protected int score;
}

**

因此,继承是is关系,组合是has关系。

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

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

相关文章

分而治之:Fork/Join框架(构造一个1到200 000求和的任务)

Fork一词的原始含义是吃饭用的叉子,也有分叉的意思。在linux平台中, 方法fork用来创建子进程。使得系统进程可以多一个执行分组。 而join方法这里表示等待,也就是使用fork方法后系统多了一个执行分支(线程) 所以需要等待这个执行分支执行完毕…

实习课知识整理3:首页商品列表的展示

对于一个购物商城的项目,主体还得是商品,这篇博客主要介绍如何将数据库中的信息渲染到页面上,这边后端是SpringBoot,前端是html配合thymeleaf模板 1. 编写查询数据库的方法 在这边我在页面上需要两部分的信息,一个是所有的商品&am…

vue前端上传图片到阿里云OSS,超详细上传图片与视频教程

vue前端直传图片与视频到阿里云OSS 1. 简介与日常使用2. 为什么要这么干?是因为我司后端不行吗???(确实!)3. vue前端直传的操作4. 如何上传到阿里OSS指定文件夹呢? 1. 简介与日常使用 阿里云…

html5实现最智能大气的公司年会抽奖(源码)

文章目录 1.设计来源1.1 主界面1.3 数据配置1.4 抽奖效果1.5 中奖效果 2.效果和源码配置2.1 动态效果2.2 员工信息配置2.3 奖品信息配置2.4 抽奖音效配置2.5 源代码2.6 项目结构 源码下载 作者:xcLeigh 文章地址:https://blog.csdn.net/weixin_43151418/…

Python字符串处理全攻略(三):常用内置方法轻松掌握

目录 引言Python字符串常用内置方法str.index()功能介绍语法注意事项总结 str.startswith()功能介绍语法示例注意事项 str.expandtabs()功能介绍语法示例注意事项总结 str.splitlines()功能介绍语法示例注意事项总结 str.swapcase()功能介绍语法示例注意事项 结束语 引言 欢迎…

Python数据分析 Matplotlib篇 基本方法初识 (第1讲)

Python数据分析 Matplotlib篇(第1讲)         🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹…

《每天一分钟学习C语言·八》字符串,指针与二维数组,补码,按位取反

1、 枚举 enum WeekDay //这是一个数据类型 {//MonDay, TusDay,.... }; enum WeekDay day MonDay; printf(“%d\n”, day); //输出0,枚举里面元素都是从0开始排列2、 字符串 (1)在字符串中加双引号前面需要有反斜杠 (2&#xf…

深入探讨多模态模型和计算机视觉

近年来,机器学习领域在从图像识别到自然语言处理的不同问题类型上取得了显着进展。然而,这些模型中的大多数都对来自单一模态的数据进行操作,例如图像、文本或语音。相比之下,现实世界的数据通常来自多种模态,例如图像…

string的库函数reserve、resize

系列文章 http://t.csdnimg.cn/u80hL 目录 系列文章[TOC](目录) 一、reserve——请求容量的变化二、resize——操作对象使用的空间 一、reserve——请求容量的变化 改变对象的capacity——他会请求开辟和缩小对象所占的空间,reserve只能操作对象未使用的空间&…

vue的表单收集案例

Vue的表单收集案例 这只是最基础的表单收集&#xff0c;并未涉及到element-ui。 <!DOCTYPE html> <html><head><meta charset"UTF-8" /><title>收集表单数据</title><script type"text/javascript" src"../js…

美好的⼀天 从ActionTab开始 美观、智能、⾼效的新标签⻚ iTab 新标签页iTab新标签页Atop100工具推荐

文章目录 ActionTabiTab 新标签页iTab新标签页&#xff0c;小组件&#xff0c;起始页&#xff0c;标签页&#xff0c;日历&#xff0c;股票&#xff0c;浏览器扩展 https://www.actiontab.cn/ ActionTab 收费&#xff1f;&#xff1f;&#xff1f;&#xff1f; iTab 新标签页iT…

docker-compaose部署openldap

前段时间在本地搭建了一套gitlab geo测试环境&#xff0c;因为需要集成ldap&#xff0c;所以特意搭建下&#xff0c;特此作为笔记记录下。 文章目录 1. 前置条件2. 编写docker-openldap.yml文件3. 登录4. 使用创建组创建用户登录测试 1. 前置条件 安装docker-compose 安装docke…