Java由浅入深刨析继承

作者:最爱吃鱼罐头 时间:2023-11-23 08:04:24 

茫茫人海千千万万,感谢这一秒你看到这里。希望我的面试题系列能对你的有所帮助!共勉!

愿你在未来的日子,保持热爱,奔赴山海!

Java基础知识(继承)

继承

继承的介绍

继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。描述的是事物之间的所属关系,这种关系是:is-a 的关系。

继承:就是子类继承父类的属性和行为,使得子类对象(实例)可以直接具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为。

生活中的继承

Java由浅入深刨析继承

兔子和长颈鹿属于食草动物类,老虎和狮子属于食肉动物类。而食草动物和食肉动物又是属于动物类。

那是不是兔子、长颈鹿、老虎、狮子都属于动物类呢?答案是没错滴!虽然食草动物和食肉动物都是属于动物,但是两者的属性和行为上有差别,所以子类会具有父类的一般特性也会具有自身的特性。我们就可以再多个类中存在相同属性和行为时,我们可以将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那一个类即可。

继承的好处

  • 提高代码的复用性(减少代码冗余,相同代码重复利用)。

  • 使类与类之间产生了关系。

  • 子类拥有父类非 private 的属性、方法。

  • 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。

  • 子类可以用自己的方式实现父类的方法。

  • 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。

  • Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。

继承的格式

在Java当中会通过extends关键字可以申明一个类是从另外一个类继承而来的,一般形式如下:

class 父类 {
}

class 子类 extends 父类 {
}

需要注意一点: Java 不支持多继承,但支持多重继承。就如下:

class A {
}

class B extends A { (对的)
}

class C extends A, B { (错的)
}

class C extends B { (对的)
}

Java由浅入深刨析继承

顶层父类是Object类。所有的类默认继承Object,作为父类。

继承的demo

编写一个父类极其对应的子类信息

结构如下:

Java由浅入深刨析继承

代码如下:

父类Person:


package com.nz.pojo;

public class Person {
   private String name ;
   private int age ;

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;
   }
}

子类Student没有额外的属性和方法:


package com.nz.pojo;

/**
* 继承了Person特有的name, age,
* 没有额外的独有属性和方法
*/
public class Student extends Person{
}

子类Teacher多了一个工资的属性和独有的教书方法:


package com.nz.pojo;

/**
* 继承了Person特有的name, age,
* 多了自己独有的工资属性还有独有的教书方法
*/
public class Teacher extends Person{

// 工资
   private double salary ;

// 特有方法
   public void teach(){
       System.out.println("老师在认真教书!");
   }

public double getSalary() {
       return salary;
   }

public void setSalary(double salary) {
       this.salary = salary;
   }
}

编写测试代码:


package com.nz;

import com.nz.pojo.Student;
import com.nz.pojo.Teacher;

public class InheritDemo {
   public static void main(String[] args) {
       Teacher teacher = new Teacher();
       teacher.setName("最爱吃鱼罐头");
       teacher.setAge(18);
       teacher.setSalary(1999.99);
       System.out.println(teacher.getName());
       System.out.println(teacher.getAge());
       System.out.println(teacher.getSalary());
       teacher.teach();

Student student = new Student();
       student.setName("罐头");
       student.setAge(12);
       //student.setSalary(1999.99); // student没有工资属性,报错!
       System.out.println(student.getName());
       System.out.println(student.getAge());
   }
}

结果如下:

最爱吃鱼罐头
18
1999.99
老师在认真教书!
罐头
12

从结果来看,子类继承父类,就可以直接得到父类的成员变量和方法。而子类可以编写一些特有的属性和方法,但是是否可以继承所有成分呢?

子类不能继承的内容

并不是父类的所有内容都可以给子类继承的:

super 与 this 关键字

这里先将这两个关键字,super和this在继承关系中,运用比较频繁。

  • super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。

  • this关键字:指向自己本类的引用。

super和this完整的用法如下:

this.成员变量 -- 本类的
super.成员变量 -- 父类的

this.成员方法名() -- 本类的
super.成员方法名() -- 父类的

  • 具体演示,创建测试InheritDemo2:


package com.nz;

public class InheritDemo2 {
   public static void main(String[] args) {
       Animal a = new Animal();
       a.eat();
       Cat cat = new Cat();
       cat.eatFish();
   }
}

class Animal {
   void eat() {
       System.out.println("animal : eat");
   }
}

class Cat extends Animal {
   void eat() {
       System.out.println("cat : eat");
   }
   void eatFish() {
       this.eat();   // this 调用自己的方法
       super.eat();  // super 调用父类方法
   }
}

调用结果如下:

animal : eat
cat : eat
animal : eat

  • 注意:

子类的每个构造方法中均有默认的super(),调用父类的空参构造。手动调用父类构造会覆盖默认的super()。

super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。

构造器不能被继承

  • 子类不能继承父类的构造器(构造方法或者构造函数),它只是调用(隐式或显式)。因为子类有自己的构造器。值得注意的是子类可以继承父类的私有成员(成员变量,方法),只是子类无法直接访问而已,可以通过getter/setter方法访问父类的private成员变量。

  • 如果父类的构造器带有参数,则必须在子类的构造器中显式地通过super 关键字调用父类的构造器并配以适当的参数列表。

  • 如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。

  • 演示过程:


package com.nz;

public class InheritDemo3  {
   public static void main(String[] args) {
       System.out.println("------Teacher 类继承------");
       Teacher teacher = new Teacher();
       Teacher teacher2 = new Teacher("张三");
       System.out.println("------Student 类继承------");
       Student student = new Student();
       Student student2 = new Student("张三三");
   }
}
// 父类
class Person {
   private String name;
   Person(){
       System.out.println("调用了父类的无参构造器: Person()");
   }
   Person(String name) {
       System.out.println("调用了父类的带参构造器: Person(String name)");
       this.name = name;
   }
}
// Teacher子类继承Person
class Teacher extends Person{
   private String name;
   Teacher(){
       // 自动调用父类的无参数构造器 因为会有默认super();
       System.out.println("Teacher");
   }

public Teacher(String name){
       super("最爱吃鱼罐头");  // 调用父类中带有参数的构造器
       System.out.println("Teacher(String name):"+name);
       this.name = name;
   }
}
// Student子类继承Person
class Student extends Person{
   private String name;

Student(){
       super("heihei");  // 调用父类中带有参数的构造器
       System.out.println("SubClass2");
   }

public Student(String name){ // 自动调用父类的无参数构造器
       System.out.println("Student(String name):"+name);
       this.name = name;
   }
}

结果如下:

------Teacher 类继承------
调用了父类的无参构造器: Person()
Teacher
调用了父类的带参构造器: Person(String name)
Teacher(String name):张三
------Student 类继承------
调用了父类的带参构造器: Person(String name)
SubClass2
调用了父类的无参构造器: Person()
Student(String name):张三三

final修饰的类不能被继承

final 关键字主要用在三个地方:变量、方法、类。

  • 修饰类:表示该类不能被继承;

  • 修饰方法:表示方法不能被重写;

  • 修饰变量:表示变量只能一次赋值以后值不能被修改(常量)。

final 的特点:

  • 对于一个 final 变量,如果是基本数据类型的变量,则其数值一旦在初始 化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不 能再让其指向另一个对象。

  • 当用 final 修饰一个类时,表明这个类不能被继承。final 类中的所有成员 方法都会被隐式地指定为 final 方法。

  • 使用 final 方法的原因有两个。第一个原因是把方法锁定,以防任何继承 类修改它的含义;第二个原因是效率。在早期的 Java 实现版本中,会将 final 方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用 带来的任何性能提升(现在的 Java 版本已经不需要使用 final方法进行这些优化了)。类中所有的 private 方法都隐式地指定为 final。

我们测试下修饰类后到底能不能继承:


package com.nz;

public class InheritDemo4 {
}

// 父类
final class Fu {
   private String name;
}

//class Zi extends Fu{ // Cannot inherit from final 'com.nz.Fu' 会显示没办法继承Fu
//}

结果:可以看出来在被final修饰的Fu类没办法继承,而且在编译期间就会报错了,没办法通过运行。

方法重写

介绍

子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。

使用场景与案例

发生在子父类之间的关系。 子类继承了父类的方法,但是子类觉得父类的这方法不足以满足自己的需求,子类重新写了一个与父类同名的方法,以便覆盖父类的该方法。

写个测试案例:


package com.nz;

public class InheritDemo5 {

public static void main(String[] args) {
       // 创建子类对象
       Cat lanMao = new Cat();
       // 调用父类继承而来的方法
       lanMao.run();
       // 调用子类重写的方法
       lanMao.sing();

}
}

class Animal{
   public void sing(){
       System.out.println("动物都可以唱歌!");
   }
   public void run(){
       System.out.println("动物都可以跑!");
   }
}

class Cat extends Animal {
   public void sing(){
       System.out.println("我们一起学猫叫,一起喵喵喵!让我们一起撒个娇");
   }
}

运行结果:

动物都可以跑!
我们一起学猫叫,一起喵喵喵!让我们一起撒个娇

可以看出,蓝猫调用了重写后的sing方法。

@Override重写注解

  • @Override:注解,重写注解校验!

  • 这个注解标记的方法,就说明这个方法必须是重写父类的方法,否则编译阶段报错。

  • 建议重写都加上这个注解,一方面可以提高代码的可读性,一方面可以防止重写出错!

加上后的子类代码形式如下:


class Cat extends Animal {
   // 声明不变,重新实现
   // 方法名称与父类全部一样,只是方法体中的功能重写了!
   @Override
   public void sing(){
       System.out.println("我们一起学猫叫,一起喵喵喵!让我们一起撒个娇");
   }
}

注意事项

  • 方法重写是发生在子父类之间的关系。

  • 子类方法覆盖父类方法,必须要保证权限大于等于父类权限。

  • 子类方法覆盖父类方法,返回值类型、函数名和参数列表都要一模一样。、

完结

相信各位看官看到这里,都对Java继承有了一定的了解吧,继承在Java的特性里还是占据比较大得多作用,他还有很多特点:

  • 高代码的复用性(减少代码冗余,相同代码重复利用)。

  • 使类与类之间产生了关系。

  • 子类拥有父类非 private 的属性、方法。

  • 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展,可以用自己的方式实现父类的方法。

  • 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。

让我们也一起加油吧!本人不才,如有什么缺漏、错误的地方,也欢迎各位人才大佬评论中批评指正!

学到这里,今天的世界打烊了,晚安!虽然这篇文章完结了,但是我还在,永不完结。我会努力保持写文章。来日方长,何惧车遥马慢!

感谢各位看到这里!愿你韶华不负,青春无悔!

Java由浅入深刨析继承

来源:https://blog.csdn.net/Saturn_Mentos/article/details/122614060

标签:Java,继承
0
投稿

猜你喜欢

  • Android网络开发中GET与POST请求详解

    2022-05-28 23:23:53
  • android加密参数定位实现方法

    2022-05-03 08:28:55
  • C# 设计模式系列教程-状态模式

    2022-11-07 13:31:55
  • C#特性 匿名类型与隐式类型局部变量使用介绍

    2023-09-29 12:42:50
  • VScode 打造完美java开发环境最新教程

    2023-02-24 16:02:10
  • 详解Java中ArrayList类

    2021-10-25 21:42:33
  • SpringAOP+RabbitMQ+WebSocket实战详解

    2023-01-01 18:40:25
  • c#获取当前年的周数及当前月的天数示例代码

    2021-10-04 09:42:19
  • C#实现加密与解密详解

    2023-08-11 16:54:42
  • android实现筛选菜单效果

    2022-10-22 12:41:05
  • Service Activity的三种交互方式(详解)

    2022-03-16 20:55:08
  • C#调用Java方法实例详解

    2021-10-16 22:50:20
  • C#实现图片上传与浏览切换的方法

    2023-10-06 06:44:05
  • Android绘制动态折线图

    2021-12-28 12:59:50
  • 支持SpEL表达式的自定义日志注解@SysLog介绍

    2023-08-27 09:38:42
  • Java实现新建有返回值的线程的示例详解

    2022-02-13 17:09:33
  • Android判断当前App是在前台还是在后台

    2022-09-20 10:37:49
  • idea项目的左侧目录没了如何设置

    2022-03-21 15:45:43
  • java中成员变量与局部变量区别分析

    2023-07-12 13:45:21
  • Android帧动画、补间动画、属性动画用法详解

    2023-02-06 15:02:47
  • asp之家 软件编程 m.aspxhome.com