CSDN话题挑战赛第2期
参赛话题:Java技术分享
Java之extends关键字
一、继承的概念引入
1、继承是什么?有什么好处?
所谓继承,它是java面向对象编程技术的一块基石,有了这块基石,我们便可以通过extends关键字让一个类和另一个类建立起一个父子关系
对于继承的好处,主要有以下三点👇
- 可以提高代码复用性【将共同的功能统一写在一个父类中】
- 减少代码冗余【同一个功能无需多写,继承自父类即可】
- 增强类的功能扩展性【子类可以重写父类中的普通方法】
2、怎么继承?格式是怎样的?
对于怎么继承,既然本文讲extends关键字,那一定是使用extends来继承自一个父类
具体的模式写法如下
class 父类{
...
}
class 子类 extends 父类{
...
}
3、继承之后会怎样呢?
在继承之后,子类便可以得到父类的属性(成员变量)和行为(各种方法),而且在此基础上,子类还可以继续增加属于自己的功能
- 所以都说子类比父类强大,确实是这样,有一句话说得也和这个继承的关系类型,那就是长江后浪推前浪,浮事新人换旧人
4、Java继承与C++继承的区别
说完以上三点,相信大家对继承也有了一个初步地了解,如果有学过C++的小伙伴肯定也知道,在C++中也有继承,那对于继承在Java和C++中有什么不一样呢,我们来探索一下🔍
- 对于C++,可以进行多继承,意思就是一个子类可以有多个父类
- 对于Java,只有单继承,不可以进行多继承,但是Java可以像C++那样实现多重继承,意思就是B类继承了A类,而且C类又继承了B了,我们管C类叫做A类的子孙类
- 其实在Java中也是可以进行多继承的,但是它不叫多继承,它叫多接口,意思就是一个类可以有同时实现多个接口,接口中也有一些方法可供实现类进行重写,主要用到implement关键字,这个我们在后续会讲到
二、简单案例&继承后的内存原理
说完了继承的基本概念,下面我们到编译器中带大家先看一个简单案例观察一下继承到底是怎么样的💻
People父类
public class People {
private String name;
private int age;
//共同行为
public void queryCourse(){
System.out.println(name + "在查看课表");
}
/**
父类对外获取私有私有成员变量的方法
*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Teacher子类
public class Teacher extends People{
int run;
void teach()
{
System.out.println("教书育人");
}
}
Student子类
public class Student extends People{
int eat;
void study()
{
System.out.println("努力学习");
}
}
test测试类
public class test {
public static void main(String[] args) {
Teacher t = new Teacher();
t.setName("王老师");
System.out.print(t.getName() + "在");
t.teach();
Student s = new Student();
s.setName("小明");
System.out.print(s.getName() + "在");
s.study();
System.out.println("----------");
t.queryCourse();
s.queryCourse();
}
}
运行结果
1、简要分析
- 从上述代码来看,有一个People类作为父类,然后Teacher类和Student类作为People类的子类。观看People类,有两个私有成员变量和一些父类对外获取私有私有成员变量的方法,然后又看到了一个子类所拥有的共同行为这个方法,可供其所有子类调用
- 然后两个子类中有它们各自在继承了父类的属性和行为后新增的独有功能
- 透过测试类和运行结果我们不难发现,两个子类所创建的对象都完成了他么自己独有的功能,而且他们还完成了相同的功能
2、内存原理剖析
- 从这张堆内存的原理图可以看出,在子类继承完父类之后,在子类的堆内存空间中会有被分为两块区域,一块是从父类继承而来的,我们通常称为super,一块是子类存放自己变量和方法的区域,通常称为this,
- 但我们也不用刻意去区别这个东西,只要心中有数就行,子类继承过来之后会分为两块空间存储变量和方法
三、子类继承父类后的特性
1、基本特性
- 子类可以继承父类的属性和行为,但是子类不能继承父类的构造器
- Java是单继承模式 :一个类只能继承一个直接父类
- Java不支持多继承,但是支持多层继承,这个我们在上面有讲到过
- 而且Java中所有的类都是Object类的子类,这个的话可以去看官方给的API文档,我的是最新的JDK18
例如这个Array类,就是继承自Object一个总的对象类
2、隐藏特性
- 子类不能继承父类的构造器
- 子类可以继承父类的私有成员,但只是不能访问
- 假设你爸爸给了你一个保险箱,你算是继承了,但只是忘记了密码打不开而已,因此无法访问
- 子类不可以继承父类的静态成员,只是共享
- 假设你爸爸的车子借你开一天,算是共享给你的,但是并没有给你,所以不算继承
具体地实现我们到代码里来看看
public class Test {
public static void main(String[] args) {
//1.子类不能继承父类的构造器
//2.子类可以继承父类的私有成员吗? - 可以继承,但只是不能访问
Tiger t = new Tiger();
//t.eat();
//3.子类可以继承父类的静态成员吗 - 不可以,只是共享
System.out.println(Tiger.location);
//静态成员用类访问
}
}
class Animal extends Object{
//Object类是默认继承的
private void eat(){
System.out.println("动物正在吃东西~~");
}
public static String location = "长隆动物园";
}
class Tiger extends Animal{
}
- 好,我们来详细说明一下二三两点,关于父类的私有成员,子类是肯定不能访问的,但是在Java的底层,其实
子类是继承了父类的私有成员
的,对于访问的话是不可以直接访问的,但是可以通过父类的get()方法来访问。这样讲述可能还是有点抽象 - 【例如说你爸爸给了你一个保险箱,但是只是把箱子给了你,你继承了这个箱子,但是呢你又打不开,无法看到里面有什么东西,这个时候就需要你爸爸给你一把钥匙是打开这个箱子,这个钥匙就好比是父类提供的访问给子类访问自己私有成员变量的对外接口】
- 然后是对于第三点,子类可以继承父类的静态成员吗?答案是否,虽然说静态的东西大家都可以访问,但是对于子类来说,却
不能继承父类的静态成员
,无论是成员变量或是成员方法,再举个例子 - 【你爸爸把他开了三四年的车借你一个刚考出驾照的人开,那这个车会直接是你的吗?有直接给到你吗?或许不会,这只是一个共享,并没有真正地得到,再说你爸也不会傻到把车送个一个新手司机开吧👈】
3、成员变量与成员方法的访问机制
好,说完了子类继承父类后的一些属性和特性,我们再来详细说一下成员变量和成员方法的一个访问机制
- 对于这个访问机制其实就四个字:【就近原则】
public class Test {
public static void main(String[] args) {
Dog d = new Dog();
d.lookdoor();
d.run();
}
}
class Animal{
public void run(){
System.out.println("动物会跑~~");
}
}
class Dog extends Animal{
public void lookdoor(){
System.out.println("狗会看门");
}
}
- 从上述代码可以看出,我们在Dog类中定义类一个run()方法,然后在主方法中调用,结果就显示的是【动物会跑】,然后调用lookdoor()就是【狗会看门】
class Dog extends Animal{
public String name = "汪汪";
public void lookdoor(){
System.out.println("狗会看门");
}
public void run() {
System.out.println("狗跑得贼快");
}
}
- 但是又通过一个子类Dog去继承父类后,在子类中有声明了一个run()方法,这时在主方法显示的便是调用子类所声明的run()方法
以上就是成员方法的调用机制,接下来说说成员变量
- 在Dog子类中,我写了一个showname的方法,然后,定义了一个姓名的成员变量和一个局部变量,现在假设我们没有局部变量,那主方法访问的便是【汪汪】,但如果加上局部变量【名字】,那就会优先访问局部变量
class Dog extends Animal{
public String name = "汪汪";
public void showname(){
String name = "名字";
System.out.println(name);
}
public void lookdoor(){
System.out.println("狗会看门");
}
public void run() {
//super.run();
System.out.println("狗跑得贼快");
}
}
- 但是这样的话我们要怎么访问成员变量和父类中的成员变量呢,这里就要使用到我们开头有提到够的【this】和【super】关键字,有关这两个关键字之后我会做再写一篇文章做详细讲解
class Animal{
public String name = "叫声";
public void run(){
System.out.println("动物会跑~~");
}
}
class Dog extends Animal{
public String name = "汪汪";
public void showname(){
String name = "名字";
System.out.println(name);
System.out.println(this.name); //访问当前子类对象name
System.out.println(super.name); //访问当前父类对象name
}
public void lookdoor(){
System.out.println("狗会看门");
}
public void run() {
//super.run();
System.out.println("狗跑得贼快");
}
}
- 从运行结果我们可以看出,通过直接访问和两个关键字的访问,我们都得到了想要的结果
四、子类方法重写【override关键字】
对于子类继承自一个父类,并且获取了父类中的一些成员方法,但是我们说过子类功能是强于父类的,原因是子类可以通过【override】关键字来重写父类中的成员方法
1、重写父类方法的规则
- 子类重写方法的名称、形参列表被必须和父类的一样【⭐】
- 父类私有方法子类不允许重写
- 子类重写父类方法时,访问权限必须
大于或者等于父类被重写的方法的权限
,不允许降低方法的访问权限 - 不可重写父类的静态方法【
上面有讲到子类其实并没有真正意义上继承了父类的静态方法
】- 父类的静态方法与子类的静态方法可以各自使用类名调
2、具体案例展示
University父类
public class University {
void admit(double chinese, double math, double english){
double aggregate = chinese + math + english;
if(aggregate >= 200)
System.out.println(aggregate + "\t双非过线了!!!");
else
System.out.println(aggregate + "\t双非没过线~~>_<~~");
}
}
ImportantUniversity子类
public class ImportantUniversity extends University{
@Override //1.重写校验注解,加上之后,这个方法必须是正确重写的,更安全
//2.提高程序的可读性,代码优雅
void admit(double chinese, double math, double english) {
double aggregate = chinese + math + english;
if(aggregate >= 240)
System.out.println(aggregate + "\t211过线了!!!");
else
System.out.println(aggregate + "\t211没过线~~>_<~~");
}
}
C9ImportantUniversity子孙类
public class C9ImportantUniversity extends ImportantUniversity{
@Override
void admit(double chinese, double math, double english) {
double aggregate = chinese + math + english;
if(aggregate >= 270)
System.out.println(aggregate + "\t985过线了!!!");
else
System.out.println(aggregate + "\t985没过线~~>_<~~");
}
}
test测试类
public static void main(String[] args) {
double chinese = 80;
double math = 90;
double english = 98;
University university = new University();
university.admit(chinese, math, english);
ImportantUniversity g = new ImportantUniversity();
g.admit(chinese, math, english);
C9ImportantUniversity gg = new C9ImportantUniversity();
gg.admit(chinese, math, english);
}
运行结果
- 好我们来看一下这个案例,这是一个高考录取分数的案例,父类为双非本科院校,子类为211重点大学,子孙类为985的C9联盟高校,从运行结果我们可以看出,通过不同类声明的对象去调用,展现出的是各自类不同的特性
- 可以看到这里的子类去重写父类的成员方法时,用到了@override,其实这个关键字时可以省略的,也不会报错。但是加上这个关键字之后有两点好处:一点是编译器后台后自动判断这个方法必须是正确重写的,更安全,第二点是可以提高程序的可读性,使得你书写的代码优雅更加优雅
3、注意事项
- 这个我们在上面对于重写的基本规则里有提到,就是在重写父类方法的时候,不允许降低方法的访问权限,但是可以提高方法的访问权限,具体的访问权限按从高到低排列是这样的【public > protected > 缺省(友好的)> private】
- 所以大家在通过子类去重写父类方法的时候,不要出现父类中的方法是public的,但是你重写之后变成了protected
五、技术小结与回顾
通过本文的阅读,您是否有对Java中的extends关键字有了一个比较全面的认识,这个关键字主要是体现在面向对象的第二大特征——继承,之后还会继续出品Java中其他关键字和对应知识点的说明,例如多态的abstruct关键字、接口的implements关键字,记得关注我哦🌸
若本文有任何疑问,可以于评论区留言或者私信我,感谢您对本文的观看,