Java第三章第1节:继承与多态 之 类的继承 实验+图解讲解

简介

👨‍💻个人主页@云边牧风
👨‍🎓小编介绍:欢迎来到云边牧风破烂的小星球🌝
📋专栏:Java基础知识
🔑本章内容:Java继承与多态 之 类的继承
记得 评论📝 +点赞👍 +收藏😽 +关注💞哦~

从这一章开始,就不叫Java入门啦,因为前两章已经把入门的知识都讲到了,相信小伙伴们也都入门了,所以从第三章开始叫Java基础进阶了,这一章的内容主要有👇:

3.1 类的继承

3.2 对象的上转型对象

3.3 多态性

3.4 abstract 类和abstract方法

3.5 super关键字

我们分为两到三节的篇幅,由浅入深地讲明白以上内容;第一节讲类的继承和对象的上传型对象

一、类的继承

继承是一种 由已有的类创建新类 的机制。(由class生成class,即可以由.java生成.java)
利用继承,我们可以先创建一个共有属性的一般类,根据该一般类再创建具有特殊属性的新类,新类继承一般类的状态和行为,并根据需要增加它自己的新的状态和行为。

1.1类的继承  相关的几个概念

基类 (base class)——    一般
也称 超类 (super class) 父类 (parent class) ,是被直接或间接继承的类
(我们一般都叫它父类就好了)
派生类 (derived-class)——    具体
也称 子类 (sub class) ,是继承自其他类而得到的类
继承所有父类的属性和行为,也可以增加变量和方法,也可以覆盖 (override) 继承的方法
一般都是子类要比父类更具体—即包含的信息更详细,父有的子必有,子有的父不一定有
子类对象与父类对象存在“ is  a 或者“ is a kind of 的关系
例如:公司中,有职员( Employee )及管理人员( Magager )两类人员,管理人员是职员,但具有不同于一般职员的属性

针对以上的例子,有👇:

职员对象( Employee )可能有的属性信息包括:
员工号( employeeNumber
姓名( name
地址( address
电话号码( phoneNumber
管理人员( Manager )除具有普通员工的属性外,还可能具有下面的属性
职责( responsibilities
所管理的职员( listOfEmployees

 对应的关系图为:

Java 继承性的特点:
通过继承, 新类拥有已有类的所有功能
Java 只支持类的单继承 ,即:每个子类只能有一个直接父类
继承机制可以 提高程序的抽象程度 提高代码的可重用性

1.2子类

在类的声明中,通过使用关键字 extends 来创建一个类的子类,格式如下 :

class 子类名  extends  父类名

{…

}

例如:

class Student extends People    //StudentPeople的子类

{…

}

注意:
如果一个类的声明中没有使用 extends 关键字,则系统默认其为 Object 类的子类
Object java.lang 包中的类,不需使用 import 显式引入
Object
Java 所有类的直接或间接父类 ,处在类层次最高点
包含了所有 Java 类的公共属性,其构造方法是 Object()

Object 类定义了所有对象必须具有的状态和行为,其中主要的方法如下:
public final Class getClass () 
获取当前对象所属的 类信息 ,返回 Class 对象
public String toString ()
返回当前对象本身的有关信息,按 字符串对象 返回
public boolean equals(Object  obj) 
比较两个对象 是否是同一对象 ,是则返回 true

Object——getClass方法     (主要面对多代码时用来定位某个类源于哪里—“他爸是谁”)

返回一个 Class 对象 ,用来代表对象隶属的类
通过 Class 对象,可以查询其代表的对象的各种信息:比如该对象所属类的名字,基类名,有哪些成员等。

void PrintClassName(Object obj) {

      System.out.println("The Object's class is " + obj.getClass().getName());

}

 对以上的理解是这样的👇:

由此我们可以得到一个“查户口”的一般形式(例子):

有个问题,不知道同学们想过没有:如果查到两个class都源于一个父类,我们怎么确定他俩是不是一样的?

——为此,有个自带的方法(函数)可以帮我们解决——equals()

由于 Object 是所有类的父类,因此其所有子类 都继承了 equals() 方法
Object 类中的 equals() 方法的定义如下:

public boolean equals(Object x) {

    return this == x;

}

可见, equals 也是判断两个对象是否同一

1.3相等同一的概念

两个对象 具有相同的类型及相同的成员值 ,则称二者 相等 (equal)
如果 两个引用类型的变量引用的是同一个对象 ,则称这两个变量 同一 (identical)
如果 两个对象同一,则肯定相等 ;但如果 两个对象相等,则不一定同一
比较运算符 ==” 判断的是这两个对象是否同一

为了方便大家理解,请看例子👇:

1.3.1使用“==”判断同一对象

class 人物 {  

  double 身高;

  double 体重;

}

执行代码:

人物   八戒 = new  人物();

人物   悟空 = new  人物();

if (八戒 == 悟空)

        System.out.println("YES");

else

        System.out.println("NO"); 

运行结果是:NO     //八戒和悟空不是“同一”对象

1.3.2修改代码后执行

执行代码:

人物   八戒 = new  人物();

人物   悟空 = 八戒;

if (八戒 == 悟空)

        System.out.println("YES");

else

        System.out.println("NO"); 

运行结果是:YES    //八戒和悟空指向“同一”对象

1.3.2使用equal()方法判断

例子:人物

class People {  

  double height;

  double weight;

  public People(double height,

  double weight)

}

执行代码:

People   a = new People(180,70);

People   b = new People(180,70);

if ( a.equals(b) )

        System.out.println("YES");

else

        System.out.println("NO"); 

运行结果是:NO//ab不是“同一”对象 (尽管数据一样但分配的地址是不一样的—对象不同)

还有👇:

执行代码:

String s1=new String(“Hello”);

String s2=new String(“Hello”);

if ( s1==s2 )

        System.out.println("YES");

else

        System.out.println("NO"); 

运行结果:NO

执行代码:

String s1=new String(“Hello”);

String s2=new String(“Hello”);

if ( s1.equals(s2) )

        System.out.println("YES");

else

        System.out.println("NO"); 

运行结果:YES//两个引用类型的变量引用的是同一个对象

👆问题:为什么String类的equals方法可以判断“相等”?

String 类中已经 重写了 Object 类的 equals 方法 ,可以判别两个字符串是否内容相同
要判断两个对象各个属性域的值是否相同,不能直接使用从 Object 类继承来的 equals 方法,而需要在子类中对 equals 方法进行重写

再如:重写“People”类的equals方法

class People {  

  double height;

  double weight;

     public boolean equals(Object o){

  if (this.getClass() != o.getClass())

  return false;     

  People r = (People)o;    

  return this. height ==r. height &&this. weight ==r. weight;

  }

}

可以得到:

执行代码:

People   a = new People(180,70);

People   b = new People(180,70);

if ( a.equals(b) )

        System.out.println("YES");

else

        System.out.println("NO"); 

运行结果是:YES//重写equals方法后,源于一个父类&&值相等——即return true

1.4子类的继承性

对于子类从父类继承而来的成员或方法,就 好象是在子类中直接声明的一样 ,可以被子类中的任何实例方法访问。(只要老爹有的,儿子都可以随便霍霍

例如👇:

public class People{

  String name;

  int age;

  void eat(){…}

}

子类代码为👇

public class Student extends People{
	String hometown;
	int grade;
	void study(){…}
}

在Main中可以这样写👇:

Student student=new Student();
student.eat();//没毛病
student.study();//没毛病

1.5隐藏和覆盖

隐藏 :子类中 重定义 从父类继承来的 成员变量及方法(子类对父类进行使用、DIY)
成员变量 的隐藏: 只要子类与父类的成员变量同名即可 ,类型可改变
方法 的隐藏:子类方法的 名字 返回类型 参数个数和类型 与父类 完全相同

覆盖 :子类对继承而来的 方法 进行“隐藏”(即重新定义)时,称为“覆盖”。(子对父 方法的DIY)
覆盖也是隐藏,但二者有区别的
覆盖是 针对方法 而言的, 成员变量只能隐藏
注意: 静态方法只能隐藏,不能被覆盖

1.5.1隐藏与覆盖的联系与区别

都是指 子类重新定义父类的同名成员变量或方法
习惯上,通常“ 隐藏 ”专指 成员变量的隐藏 ;而“ 覆盖 ”专指 成员方法的隐藏
在后文中,如果不特别指明,“隐藏”专指成员变量的隐藏。
对于 隐藏 ,子类对象 不仅拥有子类重定义的成员变量,而且 还拥有父类原有的同名成员变量 ,只是这些成员变量感觉上被“隐藏”了。
对于 覆盖 ,子类对象 只拥有覆盖后的成员方法 ,“感觉上”父类的同名成员方法“消失了”(这也是为什么静态方法不能被覆盖的原因)。
无论隐藏或覆盖,都可以使用“ super 去访问父类的成员变量或方法。(很重要)

例如👇:

问题1:父类中,哪些成员变量被隐藏?哪些方法被覆盖?

问题2:创建一个好人的实例a
“好人 a = new 好人();”

此时,a拥有哪些成员变量和方法?

1.5.2隐藏成员的访问

如果子类隐藏了父类的成员变量,那么:
子类对象 执行继承自父类的方法 时,访问的是继承自父类的成员变量,即 被隐藏的成员变量
子类对象 执行自己新增的方法 时,所操作的就是它自己声明的成员变量,即 重定义的成员变量
使用“ super. 成员 ”可以 在子类中 显式的访问 被隐藏的成员变量

例如👇:

class 人 {  

  double 寿命;

  void set寿命(double d){

  寿命=d;

  }

}
class 好人 extends 人 {   
	double 寿命;          //隐藏了继承的成员变量“寿命”
	double get寿命(){     //新增的方法,访问自己声明的“寿命”
	    return 寿命;
	}
	double get隐藏寿命(){  //新增的方法,访问被隐藏的“寿命”
	    return super.寿命;
	}
}
执行代码:
好人 a = new 好人();
a.set寿命(100);      //由于方法是直接继承而来的(在“好人”中
//没有声明(定义)属于好人自己的set寿命()方法),
//因此访问的是被隐藏的“寿命”
//即这个100是存到了父类“人”中的寿命(所以说是隐藏的),而子类“好人”的寿命还是初始值0
System.out.println(a.get寿命());
System.out.println(a.get隐藏寿命());

运行结果:

  0.0

  100.0

1.5.3覆盖方法的访问

如果子类覆盖了父类的方法,那么:
子类对象只会 调用覆盖后的方法
子类对象执行 覆盖后的方法 时,所操作的是它 自己声明的成员变量(借父之手搞自己的事)
使用“ super. 被覆盖的方法名 ”可以 在子类中 显式的访问 被覆盖的方法

例如👇:

class 人 {
	String 姓名;   
	double 寿命;
	String get姓名( ){
		return 姓名;
	}
	void set寿命(double d ){
		寿命=d;
	}
}
class 好人 extends 人 {   
	double 寿命;
	void set寿命(double d){    //覆盖了父类的“set寿命”方法,这样操作的是子类自己的寿命
		寿命=d;
	}
	void set隐藏寿命(double d){    //访问被覆盖的父类的“set寿命”方法
		super.set寿命(d);         //改变的是他爹的寿命
	}
	double get寿命(){
		return 寿命;    //访问的是子类自己的寿命
	}
	double get隐藏寿命(){
		return super.寿命;    //访问的是他爹的寿命
	}
}
执行代码:
好人 a = new 好人();    //此时,a拥有两个成员变量“寿命”
a.set寿命(100);     //由于方法是覆盖的,因此访问的是重定义的“寿命”(子类自己的)
System.out.println(a.get寿命());//访问的是子类自己的寿命
System.out.println(a.get隐藏寿命());	//他爹的寿命
a.set隐藏寿命(120);    //调用父类的“set寿命”方法,因此访问的是被隐藏的“寿命”(他爹的)
System.out.println(a.get寿命());//子类自己的寿命
System.out.println(a.get隐藏寿命());//他爹的寿命

运行结果:

  100.0

  0.0

  100.0

  120.0

1.5.4覆盖的注意事项

覆盖父类的方法时, 不可以降低方法的访问权限 。例如:覆盖父类的 protected 方法,则覆盖方法的访问权限不能低于 protected
为什么?

因为:如果覆盖方法时降低了访问权限,例如变成private方法,那么子对象的上转型对象无法调用覆盖的方法,类的继承性多态就无法实现

必须覆盖 的方法
子类 必须覆盖父类中的抽象方法 abstract 方法),否则派生类自身也成为抽象类
不能覆盖 的方法
父类中 声明为 final 的终结方法
父类中 声明为 static 的静态方法

1.5.5构造方法的继承问题

构造方法在类继承时的机制与普通方法有很大不同,主要原则有:
子类 不能从父类继承构造方法 ,而且子类在构造过程中 必须调用其父类的构造方法
子类的构造方法中 可使用“ super” 调用父类的构造方法 ,但 必须写在方法体第一行

注意:super语句调用父类构造方法的格式:
正确:super(参数表)
错误:super.父类名(参数表)

例如:使用super调用父类构造方法👇

class {  

  String 姓名;

  (String s){

  姓名 = s;

  }

class 好人 extends {  

  String 姓名;

  好人(String s){

  super(s);//必须在第一行

  姓名 = s;

  }

}

执行:

好人 a = new 好人("雷锋");

System.out.println(a.姓名);

运行结果:

  雷锋

如果子类的构造方法中 没有显式地调用父类构造方法 ,则系统 调用父类的默认构造方法 无参数 的构造方法 ;但是, 如果此时父类中只定义了带参数构造方法,则编译出错

(1)同一包中的继承性

如果子类和父类在 同一个包 中,那么:
继承其父类中 private 的成员变量作为自己的成员变量
继承父类中 private 的方法作为自己的方法
继承的成员变量或方法的 访问权限保持不变

(2)不同包中的继承性 

如果子类和父类 不在同一个包 中,那么:
继承父类的 protected public 成员变量 做为子类的成员变量
继承父类的 protected public 方法 为子类的方法
继承的成员变量或方法的 访问权限保持不变
不能继承父类的友好成员变量和友好方法
涉及到继承 :如果类 A 和类 B 不在同一个包中,
对于 protected 成员变量或方法 ,在 B a 能访问 自己的 protected 成员变量和方法
对于 友好的成员变量或方法 无法访问 ,不考虑父类继承问题(因为子类无法继承不在一个包中的父类的友好成员变量或方法)。

(3)总结:类的访问限制表

 由上表可以得到👇:

访问权限 由高到低 依次是:
public protected 、友好的、 private
友好的”访问权限又称为“ 包访问权限 ”,限制类的对象之间在一个包中访问。

二、final关键字

final 称为“ 终结 ”,表示“ 不可改变 ”。
final 可以修饰:
“终结类” 不能被继承 ,即没有子类
成员变量 :该成员是一个 常量 必须赋初值且不能被修改
方法 “终结方法” 不能被覆盖
方法中的参数 :该参数值 不能被修改

 

2.1final类的应用:

安全 :黑客用来搅乱系统的一个手法是建立一个类的派生类,然后用他们的类代替原来的类
设计 :你认为你的类是最好的,或者你不希望该类再有子类

2.2final成员变量的应用:

可以结合 static 定义一些程序中能直接使用的常量,如:
//Math 类中的 PI
public  static  final double PI = 3.141592653589793 ;

再如:
// 网站数据库访问类中的“连接字符串”
// DBHelper
 
public static final 连接字符串 = " Server= (local) ;  UID= sa ;  PWD= sa ;  database= Study Java " ;
注: 良好的编程习惯不提倡过多的使用 static

三、对象的上转型对象

3.1、定义

上转型:用 父类声明的对象指向子类创建的实体 ,此时称为“上转型”操作,而父类对象是子类对象的 “上转型”对象 。(认祖宗)
如👇:

下转型 :将“上转型” 操作逆反,即: 声明一个子类,指向父对象所指的实体 ,此时称为“下转型”操作,而子类对象是父类对象的 “下转型”对象  (认儿子)
如👇:

3.2、对象转型与“类型转换”(塑型)

实际上,对象转型就是“ 类型转换 ”,只不过对象转型是 针对引用类型变量 的。
上转型 :即“ 隐式类型转换 ”,或称为“ 上塑
下转型 :即“ 显式类型转换 ”,或称为“ 下塑

3.3、对象转型的注意事项

上转型:只要 A 是类 B 的直接 / 间接父类 ,那么 B 的对象就可以上转型为 A 的对象
下转型: 必须确保父类的对象指向的是子类的实体 ,否则在下转型时会出错;另外,要进行显式类型转换,格式为:

   子类 b = (子类)a;

接口也可以转型 ,用法与类一致

 

Manager类的继承关系图为👇:

3.4、上转型对象的访问限制

上转型对象将 丧失子类的一些属性和功能
例如:假设 A B 的父类,执行:

子类  b = new 子类();

父类  a = b;

a能访问对象b的哪些属性和功能👇:

上转型对象能够访问:
如果 未涉及到隐藏 / 覆盖 :能访问 继承的成员 方法 ;不能访问子类新增的成员、方法;
如果 涉及到隐藏 / 覆盖
成员:能访问 被隐藏的成员 (即父类原有的成员),而不能访问子类重定义的成员
方法:仅 能够访问覆盖后的方法 ,父类原有的方法“消失”了
强调:
如果子类 覆盖了父类的某个方法 ,那么上转型对象调用这个方法时,一定是 调用了这个重写的方法
由于静态方法只能被隐藏,因此 访问父类的静态方法时不受限制

3.5、关于instanceof运算符

双目运算符,左操作数为对象,右操作数为类。
如:假设 People 类派生出 Student 类, Student 类派生出 UniverStudent 类, zhangsan lisi 分别是 Student UniverStudent 创建的对象,则:

lisi  instanceof  UniverStudent;//true

lisi  instanceof  Student;//true

lisi  instanceof  People;//true

zhangsan  instanceof  Student;//true

zhangsan  instanceof  UniverStudent;//false

 

结束语:

以上是Jav第三章的第1节,希望大家喜欢

下一节开始讲第三章第2节多态性、abstract 类和abstract方法、super关键字

喜欢的可以点赞+关注哈 ❤

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云边牧风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值