Java面向对象专题(三) 继承

继承

概述

多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承该类即可
单独的这个类称为父类、基类或者超类,这多个类可以称为子类或者派生类

有了继承之后,我们定义一个类的时候,可以在父类的基础上,定义自己新的成员方法和成员变量

方式

通过extends关键字可以实现类与类的继承
格式:class 子类名 extends 父类名{ };

好处与弊端

  • 好处
    • 提高了代码的复用性
    • 提高了代码的维护性
    • 让类与类之间产生了关系,是多态的前提
  • 弊端
    • 使类的耦合性增强了,这样某个类的改变就会影响其他和该类相关的类
    • 打破了封装性

开发的原则:低耦合,高内聚
耦合:类与类之间的关系
内聚:独立完成某事的能力

特点

  • Java只支持单继承,不支持多继承
  • Java支持多层继承(继承体系)

注意事项

  • 子类只能继承父类所有非私有的成员(成员方法和成员变量)
  • 子类不能继承父类的构造方法,但可以通过super关键字访问父类构造方法
  • 不要为了部分功能而去继承
  • 使用继承的情况:
    如果有两个类A,B,只有他们符合A是B的一种,或者B是A的一种,就可以考虑使用继承

继承体现的是一种"is a"的关系。

成员关系

成员变量

  • 子类中的成员变量和父类中的成员变量名称不一样时,调用对应的变量即可
class Father{
	public int num = 10;
}
class Son extends Father{
	public int num2 = 20;
	public void show(){
		System.out.println(num); //10
		System.out.println(num2); //20
	}
}
  • 子类中的成员变量和父类中的成员变量名称一样时,就近原则

在子类方法中访问一个变量的查找顺序(找到就使用):
子类的局部范围 -> 子类的成员范围 -> 父类的成员范围 -> 报错

问题:子类中怎么明确变量名称一样的不同变量呢?
解决方法:引入this 和 super 关键字

this 和 super 的定义:
this:代表本类对应对象的引用
super:代表父类存储空间的标识(可以理解为父类引用)

例子:

扫描二维码关注公众号,回复: 5922827 查看本文章
class Father{
	public int num = 10;
}
public class Son extends Father{
	public int num = 20;
	public void show() {
		int num = 30;
		System.out.println(num); //30
		System.out.println(this.num); //20
		System.out.println(super.num); //10
	}
}

构造方法

子类中所有的构造方法默认都会访问父类中的无参构造方法

原因:子类会继承父类中的数据,可能还会使用父类的数据,所以子类初始化前一定要先完成父类数据的初始化

子类每一个构造方法的第一条语句默认都是:super();

问题:如果父类没有无参构造方法(自己只定义一个有参构造方法,即可去除默认的无参构造方法),那么子类的构造方法会报错。如何解决?
解决方法:
1、在父类中加一个无参构造器
2、通过使用super关键字去显示调用父类的带参构造方法
3、子类通过this关键字去调用本类的其他构造方法,间接访问父类构造方法

class Father{
	String name;
	//带参构造器会取消默认的无参构造器
	public Father(String name){
		this.name = name;
		System.out.println("Father 带参构造");
	}
}
class Son extends Father{
	public Son(String name){
		super(name);
		//如果没有上面那句话,系统会默认调用super(); ,但由于父类中没有无参构造,会报错
		System.out.println("Son 有参构造");
	}
}

总之:子类中的构造方法一定访问父类中的一个构造方法,否则父类数据就没有初始化
另外,需注意的是:super(…)或this(…)必须出现在第一条语句上,否则,就会出现父类数据多次初始化,报错("…"表示需传入的参数)
例子:

class Father{
	public Father() {
		System.out.println("Father 无参构造方法");
	}
	private void Fathe(String name) {
		System.out.println("Father 带参构造方法");
	}
	public int num = 10;
}
public class Son extends Father{
	public int num = 20;
	public Son() {
		System.out.println("Son 无参构造方法");
	}
	public Son(String name) {
		System.out.println("Son 带参构造方法");
	}
	public void show() {
		int num = 30;
		System.out.println(num);
		System.out.println(this.num);
		System.out.println(super.num);
	}
}
class Test{
	public static void main(String[] args) {
		Son son = new Son();
		son.show();
		System.out.println("------------------");
		Son son2 = new Son("AAA");
	}
}

运行结果:

运行结果

成员方法

  • 子类中的方法和父类中的方法声明不一样,就调用对应的方法
  • 子类中的方法和父类中的方法声明一样,最终使用的是子类自己的方法

通过子类调用方法的查找顺序(找到就使用):
子类 -> 父类 -> 报错

其中涉及到了方法重写

方法重写和方法重载的区别:
方法重写(Override):子类中出现了和父类中方法声明一模一样的方法
方法重载(Overload):本类中出现的方法名一样,参数列表不同的方法,与返回值无关

方法重写的应用:
当子类需要父类的功能,而功能主体子类有自己特有的内容时,可以重写父类中的方法。
这样,既沿袭了父类的功能,又定义了子类特有的内容

例子:

class Phone{
	public void call(String name) {
		System.out.println("call " + name);
	}
}
class NewPhone extends Phone{
	@Override
	public void call(String name) {  //方法重写
		super.call(name);
		System.out.println("呼叫过程中可以听到彩铃");
	}
	public void call() {	//方法重载
		System.out.println("打电话");
	}
}
public class OverrideTest {
	public static void main(String[] args) {
		NewPhone np = new NewPhone();
		np.call();
		np.call("Jay");
	}
}

运行结果:
运行结果

  • 方法重写的注意事项:
    • 父类中私有方法不能被重写, 因为父类私有成员根本无法被继承
    • 子类重写父类方法时,访问权限不能更低, 最好一致
    • 父类静态方法,子类也必须通过静态方法重写(本质上不是方法重写

    子类重写父类方法时,最好声明一模一样

this 和 super 的使用

使用 this super
成员变量 调用本类的成员变量 调用父类的成员变量
构造方法 调用本类的构造方法 调用父类的构造方法
成员方法 调用本类的成员方法 调用父类的成员方法

具体的用法可参考:https://www.cnblogs.com/hasse/p/5023392.html

final 关键字

概述

由于继承中方法有一个现象:方法重写。所以,父类的功能,会被子类给覆盖掉。有些时候,我们不想让子类去覆盖父类的功能。这个时候,针对这种情况,Java提供了一个关键字:final

final 可以修饰类,方法,变量。

特点

  • final 可以修饰类,该类不能被继承
  • final 可以修饰方法,该方法不能被覆盖重写
  • final 可以修饰变量,该变量不能被重新赋值(这个变量其实是常量)

常量:

  • 字面值常量
    “hello” ,10 ,true
  • 自定义常量
    final int x = 10;

注意事项

  • final 修饰局部变量的问题:
    • 基本类型:值不能发生改变
    • 引用类型:地址值不能发生改变,但是,该对象堆内存中的内容可以修改

例子

class Student{
	int age = 10;
}

class FinalTest{
	public static void main(String[] args){
		int x = 10;
		x =100;
		System.out.println(x);
		final int y = 10 ;
		//无法为最终变量赋值
		//y = 100;
		System.out.println(y);
		
		Student s = new Student();
		System.out.println(s.age);
		s.age = 100;
		System.out.println(s.age);
		
		final Student ss = new Student();
		System.out.println(ss.age);
		ss.age = 100; //正确
		System.out.println(ss.age);
		
		//ss = new Student();//错误		
	}
}

  • final 修饰变量的初始化问题:
    • 被final修饰的变量只能赋值一次(不包括系统默认初始值)
    • 对于非静态的常量,尽量在构造方法完毕前赋值。

面试题

1、看程序写结果:

class Fu{
	static {
		System.out.println("静态代码块Fu");
	}
	{
		System.out.println("构造代码块Fu");
	}
	public Fu() {
		System.out.println("构造方法Fu");
	}
}
class Zi extends Fu{
	static {
		System.out.println("静态代码块Zi");
	}
	{
		System.out.println("构造代码块Zi");
	}
	public Zi() {
		System.out.println("构造方法Zi");
	}
}
public class ExtendsTest {
	public static void main(String[] args) {
		Zi zi = new Zi();
	}
}

结果:
结果
分析要点:

A:一个类的静态代码块,构造代码块,构造方法的执行顺序:
静态代码块>构造代码块>构造方法
B:静态的内容随着类的加载而加载
静态代码块的内容会优先执行
C:子类初始化之前会先进行父类的初始化

从main()方法入口,执行Zi zi = new Zi();时,程序加载Zi类,发现Zi类继承自Fu类,便把Fu类也加载了(实际的加载顺序是先父类后子类)。类加载后便会优先执行静态代码块。子类初始化之前会先进行父类的初始化,所以会先执行父类的构造代码块和构造方法,之后再执行子类的构造代码块和构造方法。
2、看程序写结果

class X {
	Y b = new Y();
	X(){
		System.out.println("X");
	}
}
class Y{
	Y(){
		System.out.println("Y");
	}
}
public class Z extends X{
	Y y = new Y();
	Z() {
		System.out.println("Z");
	}
	public static void main(String[] args) {
		new Z();
	}
	
}

结果:
结果
分析要点:

A:成员变量的问题
int x = 10; //成员变量是基本类型
Student s = new Student(); //成员变量是引用类型
B:一个类中成员变量的初始化过程
默认初始化 -> 显示初始化 -> 构造方法的初始化
C:子类的初始化(分层初始化)
先进行父类初始化,后进行子类初始化

从main()方法入口,执行new Z();时,加载类X和类Z,先执行类X的成员变量初始化,Y b = new Y();创建类Y的对象b,调用构造方法,打印出“Y”,再执行类X的构造方法,输出“X”;类X初始化完毕后,执行类Z的初始化,Y y = new Y();创建类Y的对象y,调用构造方法,打印出“Y”,再执行类Z的构造方法,输出“Z”。

关于Java中父类和子类的加载和初始化顺序,可参考:
https://blog.csdn.net/sunroyfcb/article/details/81637565
关于Java中初始化的本质,可参考:
https://blog.csdn.net/sunroyfcb/article/details/81637565

猜你喜欢

转载自blog.csdn.net/JayFan_Ma/article/details/87091865