Java之继承


1. 思考

1.1 看以下代码示例。
//描述学生。
class Student 
{
	//属性。
	String name;
	int age;

	//行为。
	void study()
	{
		System.out.println("good good study");
	}
}

//描述工人。
class Worker
{
	//属性。
	String name;
	int age;

	//行为
	void work()
	{
		System.out.println("hard work");
	}
}
1.2 发现类Student、Worker有相同的属性name和age。有相同的代码,那能不能提高代码的复用性呢?
能。
1.3 怎么提高呢?
为了提高复用,只建立一份代码,让一个类只要和另一个类产生关系就可以了。
1.4 什么关系?
继承
1.5 发现了获取到所需内容的同时也获取到不该具备的内容。为什么?
因为这个两个类(Student、Worker)之间根本就不存在继承关系。
1.6 怎么解决呢?
找到Student和Worker的共性类型。将需要提供复用的代码进行抽取。定义到一个共性类型的类Person中。
class Person
{
	String name;
	int age;
}
1.7 怎么在代码体现中分别让Student和Worker与Person产生关系呢?
只要通过关键字 extends(继承) 就可以了。
class Person
{
	String name;
	int age;
}

class Student extends Person//学生继承了Person 学生就是子类 Person就是父类(基类,超类)
{
	void study()
	{
		System.out.println("good good study");
	}
}
class Worker extends Person
{
	void work()
	{
		System.out.println("hard work");
	}
}

2. 继承的作用。

好处:提高了代码的复用性。让类与类产生了关系,给另一个特征 多态 提供了前提。
可以对对象创建进行初始化。
弊端:打破封装性。

3. 什么时候定义继承?

必须保证类与类之间有所属(is a)关系。 xxx是zzz中的一种。
例如:苹果是水果中一种。狗是犬科中一种。

4. 在Java中继承的体现

Java允许单继承。不直接支持多继承,将多继承用其他方式(implements关键字)的体现。
单继承:一个子类只能有一个父类。
多继承:一个子类可以有多个父类。
多重继承: 一个子类可以有多个父类,父类又可以有多个父类
4.1 单继承代码示例
class Fu1
{
	void show1() {}
}
//单继承。
class Zi extends Fu1
{
	
}

Zi z = new Zi();
z.show1();
4.2 多继承代码示例
class Fu1
{
	void show1() {}
}
class Fu2
{
	void show2() {}
}
//多继承。
class Zi extends Fu1, Fu2
{
	
}

Zi z = new Zi();
z.show1();
z.show2();
似乎没有问题。问题随之而来,万一多个父类具备了相同的功能呢?
class Fu1
{
	void show()
	{
		sop("fu1 show run");
	}
}
class Fu2
{
	void show()
	{	
		sop("fu2 show run");
	}
}
//多继承。
class Zi extends Fu1, Fu2
{
	
}

Zi z = new Zi();
z.show(); //调用就会产生不确定性。所以java保留的多继承的好处,改良它的弊端。用多实现来体现。
4.3 多重继承代码示例
class A
{}
class B extends A
{}
class C extends B
{}

5. 子父类中成员变量的特点

5.1 子类不可直接访问父类私有成员
//父类。
class Fu
{
	private int num1 = 3; //父类中私有的内容子类不可以直接访问。
}
//子类
class Zi extends Fu
{
	void show()
	{
		System.out.println("num1 = " + num1);
		System.out.println("num2 = " + num2);
	}
}

class ExtendsDemo 
{
	public static void main(String[] args) 
	{
		Zi z = new Zi();
		z.show();
	}
}

运行结果:
在这里插入图片描述

5.2 子父类中定义了一模一样的成员变量。
//父类。
class Fu
{
	public int num = 3;
}

class Zi extends Fu
{
	private int num = 400;
	void show()
	{
		System.out.println("num = " + num);
		System.out.println("zi num = " + this.num);
		System.out.println("fu num = " + super.num);

	}
}

class ExtendsDemo 
{
	public static void main(String[] args) 
	{
		Zi z = new Zi();
		z.show();
	}
}

运行结果:
在这里插入图片描述
结论:

  • 都存在于子类对象中。
  • 直接访问num相当于访问this.num。
  • 如何在子类中直接访问同名的父类中的变量呢?通过关键字 super来完成。
  • super和this的用法很相似。
    this:代表的是本类的对象的引用。
    super:代表的是父类的内存空间。

5. 子父类中成员函数的特点

5.1 子类不可直接访问父类私有函数
class Fu
{
	private int show()
	{
		System.out.println("fu show run");
		return 0;
	}
}
class Zi extends Fu
{
}
class ExtendsDemo2 
{
	public static void main(String[] args) 
	{
		Zi z = new Zi();
		z.show();
	}
}

运行结果:
在这里插入图片描述


5.2 子父类中的定义了一模一样的函数。
class Fu
{
	void show()
	{
		System.out.println("fu show run");
	}
}
class Zi extends Fu
{
	void show()
	{
		System.out.println("zi show run");
	}
}
class ExtendsDemo2 
{
	public static void main(String[] args) 
	{
		Zi z = new Zi();
		z.show();
	}
}

运行结果:

运行的结果:子类的函数在运行。
这种情况在子父类中,是函数的另一个特性:override(重写,覆盖,复写)

在这里插入图片描述

6. 函数重写

思考:重写什么时候用?
6.1 重写的应用

举例:

//描述手机。
class Phone
{
	int number;
	//打电话。
	void call(){}
	//来电显示。
	void show()
	{
		System.out.println("电话号码.." + number);
	}
}
class Demo
{
	public static void main(String[] args) 
	{
		Phone p = new Phone();
		p.show();
	}
}
随着电话的升级,只显示号码不爽,希望显示姓名,大头贴。修改源码,虽然费劲但是可以解决,不利于后期的维护和扩展。
为了扩展方便。新功能是不是新的电话具备呢?单独描述单独封装。新电话也是电话中的一种。
可以继承。直接获取父类中的功能。但是新电话的来显功能已经变化了。需要重新定义。
那么定义一个新功能合适吗?比如newShow,不合适,因为父类已经将来显功能定义完了,
子类完全不需要重新定义新功能。直接用就可以了。如果子类的来显功能内容不同。
直需要保留来显功能,定义子类的内容即可:这就是重写的应用!
class NewPhone extends Phone
{
	String name;
	String picPath; //图片路径。
	void show()
	{
		// System.out.println("电话号码");
		super.show(); // 如果还需要父类中原有的部分功能,可以通过super调用。
		System.out.println("姓名" + name);
		System.out.println("大头贴" + picPath);
	}
}
6.2 重写的注意事项
6.2.1 子类覆盖父类,必须保证全要大于或者等于父类的权限。(private不包括在覆盖范围内,因为private只属于父类本类)
class Fu
{
	void show()
	{
		System.out.println("fu show run");
	}
}
class Zi extends Fu
{
	public void show() // 必须保证全要大于或者等于父类的权限
	{
		System.out.println("zi show run");
	}
}
6.2.2 静态覆盖静态。
// 父类定义为静态
class Fu
{
	static void show()
	{
		System.out.println("fu show run");
	}
}
class Zi extends Fu
{
	void show()
	{
		System.out.println("zi show run");
	}
}
class ExtendsDemo2 
{
	public static void main(String[] args) 
	{
		Zi z = new Zi();
		z.show();
	}
}

运行结果:
在这里插入图片描述


// 子类定义为静态
class Fu
{
	void show()
	{
		System.out.println("fu show run");
	}
}
class Zi extends Fu
{
	static void show()
	{
		System.out.println("zi show run");
	}
}
class ExtendsDemo2 
{
	public static void main(String[] args) 
	{
		Zi z = new Zi();
		z.show();
	}
}

运行结果:
在这里插入图片描述
结论:

只能静态覆盖静态
6.2.3 写法
必须一模一样:函数的返回值类型、函数名、参数列表都要一样。
当一个类是另一个类中的一种时,可以通过继承,来扩展功能。如果从父类具备的功能内容需要子类特殊定义时,使用重写。

7. 子父类中构造函数的特点

7.1 当子父类都有构造函数时
class Fu
{
	Fu()
	{
		System.out.println("fu constructor run......");
	}
}
class Zi extends Fu
{
	Zi()
	{
		System.out.println("zi constructor run......");
	}
}
class ExtendsDemo2 
{
	public static void main(String[] args) 
	{
		new Zi();
	}
}

运行结果:
在这里插入图片描述
结论:

先执行了父类的构造函数,再执行子类的构造函数。
7.1.1 为什么?
因为子类的所有的构造函数中的第一行都有一句隐式语句 super(); //默认调用的是父类中的空参数的构造函数。
class Zi extends Fu
{
	Zi()
	{
		super(); // 隐式语句
		System.out.println("zi constructor run......");
	}
}	
7.1.2 子类中的构造函数为什么有一句隐式的super()呢?
原因:子类会继承父类中的内容,所以子类在初始化时,必须先到父类中去执行父类的初始化动作。才可以更方便的使用父类中的内容。

思考一下代码:

class Fu
{
	Fu(int x)
	{
		System.out.println("fu constructor run......" + x);
	}
}
class Zi extends Fu
{
	Zi(int x)
	{
		System.out.println("zi constructor run......" + x);
	}
}
class ExtendsDemo2 
{
	public static void main(String[] args) 
	{
		new Zi(666);
	}
}

运行结果:
在这里插入图片描述

原因:

因为子类的所有的构造函数中的第一行都有一句隐式语句 super();调用了父类的空参构造函数,然而父类并没有空参构造函数。

解决方法:

当父类中没有空参数构造函数时,子类的构造函数必须同构显示的super语句指定要访问的父类中的构造函数。
这就是传说中的子类实例化过程。
class Fu
{
	Fu(int x)
	{
		System.out.println("fu constructor run......" + x);
	}
}
class Zi extends Fu
{
	Zi(int x)
	{
		super(999); // 当父类中没有空参数构造函数时,子类的构造函数必须同构显示的super语句指定要访问的父类中的构造函数。
		System.out.println("zi constructor run......" + x);
	}
}
class ExtendsDemo2 
{
	public static void main(String[] args) 
	{
		new Zi(666);
	}
}

运行结果:
在这里插入图片描述


示例:

class Fu
{
	Fu() 
	{
		System.out.println("fu constructor run......A......");
	}
	Fu(int x)
	{
		System.out.println("fu constructor run......B......" + x);
	}
}
class Zi extends Fu
{
	Zi()
	{
		System.out.println("zi constructor run......C......");
	}
	Zi(int x)
	{
		System.out.println("zi constructor run......D......" + x);
	}
}
class ExtendsDemo2 
{
	public static void main(String[] args) 
	{
		new Zi();
		new Zi(666);
	}
}

运行结果:
在这里插入图片描述

7.1.3 实例化过程的细节

<1> 如果子类的构造函数第一行写了this调用了本类其他构造函数,那么super调用父类的语句还有吗?

没有的,因为this()或者super(),只能定义在构造函数的第一行,因为初始化动作要先执行。

<2> 父类构造函数中是否有隐式的super呢?

也是有的。记住:只要是构造函数默认第一行都是super();父类的父类是谁呢?super调用的到底是谁的构造函数呢?
Java体系在设计,定义了一个所有对象的父类Object。
7.1.4 总结
类中的构造函数默认第一行都有隐式的super()语句,在访问父类中的构造函数。
所以父类的构造函数既可以给自己的对象初始化,也可以给自己的子类对象初始化。
如果默认的隐式super语句没有对应的构造函数,必须在构造函数中通过this或者super的形式明确调用的构造函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值