黑马毕向东Java课程笔记(day07):面向对象(第三部分)继承+抽象类+模板方法设计模式+接口+final+继承补充(就业班)

  在这一部分中,我们将讲解有关继承的相关内容,包括继承的概述、继承的特点、super关键字、函数覆盖、子类的实例化过程、final关键字这几个部分的内容。

1、继承的概述以及特点
1.1、概述
  多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承单独的那个类即可。多个类可以称为子类,单独这个类称为父类或者超类。
  **子类可以直接访问父类中的非私有的属性和行为。**通过extends关键字让类与类之间产生继承关系,格式为

class SunClass extends FatherClass{}

  继承的出现提高了代码的复用性,并且让类与类之间产生了关系,提供了多态的前提。
1.2、特点
  Java只支持单继承,不支持多继承,多继承容易带来安全隐患(25-day07-01-7.27分钟开始的部分说明)。也就是说,一个类只能有一个父类,不可以有多个父类。另一方面,java又支持多层继承,也就是说,多个类之间可以相互继承,形成继承体系。如下

class SubDemo extends Demo{}//ok
class SubDemo extends Demol,Demo2...//error 

  Java支持多层继承(继承体系)

class A{}
class B extends A{}
class C extends B{}

  定义继承需要注意:不要仅为了获取其他类中某个功能而去继承,类与类之间要有所属(“is a")关系,也就是子类xx1应该要是父类xx2的一种,**也就是说,父类的内容,子类应该全部具备,而子类又可以有自己新的内容。**如果父类的某一个特性子类不应该具备,那么这两个类之间就不应该有继承关系。
  继承的基本思想:基于某个父类的扩展,制定出一个新的子类,子类可以继承父类原先的属性与方法,也可以增加父类所不具备的属性与方法,或者重写父类的某些方法。

//不能多继承的原因
//当多个父类中定义了相同功能,当功能内容不同时,子类对象不确定要运行哪一个。
class A 
{
   
	void show()
	{
   
		System. out. println("a");
	}
}
classB
{
   
	void show()
	{
   
		System. out. print1n("b"); 
	}
}
class C extends A,B
{
   
	C c=new C(); 
	c. show();//不知道调用的是A的show()方法还是B的show()方法
	//接口不会有这种困境,因为我们实现多个接口后,必须对接口里面的方法进行复写后才能调用,因此不会有多个父类(接口)方法重名而不知道调用谁的困境。
}

  java的多继承还是存在的,优化了c++的多继承功能,用多实现的方式来提现,后面会讲到。
  如何使用一个继承体系中的功能呢?
  想要使用体系,先查阅体系父类的描述,因为父类中定义的是该体系中共性功能,通过了解共性功能,就可以知道该体系的基本功能,那么这个体系已经可以基本使用了。
  在具体调用时,要创建最子类的对象,有两个原因
1)因为有可能父类不能创建对象;
2)创建子类对象可以使用更多的功能,包括基本的也包括特有的。
  也就是,我们要先查阅父类功能,再创建子类对象来使用功能。

2、子类与父类关系确定之后,类成员的特点
2.1、子父类中变量的特点
  我们先说一下super关键字,super和this的用法相同,this代表本类引用
super代表父类引用。
  当子父类出现同名成员时,可以用super进行区分子类与父类;要调用父类构造函数时,可以使用super语句。
  例子:

/*
1、变量
如果子类中出现非私有的同名成员变量时,子类要访问本类中的变量,用this;子类要访问父类中的同名变量,用super。super的使用和this的使用几乎一致。
this代表的是本类对象的引用。
super代表的是父类对象的引用。
*/
public class ExtendsDemo {
   
	public static void main(String[] args) {
   
		Son obj = new Son();
		System.out.println(obj.num);//打印2(子类),父类与子类属性相同时,用对象调用会调用子类属性
		obj.show1();//打印2(子类)
		obj.show2();//打印1(父类)
	}
}
class Father
{
   
	int num = 1;
}

class Son extends Father
{
   
	int num = 2;
	void show1()
	{
   
		System.out.println(this.num);//this表示本类的引用
	}
	void show2()
	{
   
		System.out.println(super.num);//super表示父类的引用
	}
}

2.2、子父类中函数的特点
  当子类出现和父类一模一样的函数时,当子类对象调用该函数,会运行子类函数的内容,如同父类的函数被覆盖一样,这种情况是函数的另一个特性:重写(覆盖)
  使用情景:当子类继承父类,沿袭了父类的功能,到子类中,但是子类虽具备该功能,但是功能的内容却和父类不一致,这时,没有必要定义新功能,而是使用覆盖功能,保留父类的功能定义,并重写功能内容。

重载见:(day03—第二部分):函数,注意区别重载与重写。(注意,子类可以重写父类方法,也可以重载父类方法!)
  重载与覆盖(重写)的区别,看下面这个链接:
重载与覆盖(重写)的区别

  对于重载与覆盖,记住:
重载:只看同名函数的参数列表,同名参数列表不一样就是重载
重写/覆盖:子父类方法形式要一模一样,包括返回值类型、参数列表,而子类的访问权限修饰符大于等于父类,子类抛出的异常不能大于父类,但是内容可以不同,在子父类中。子父类的构造方法不能算覆盖!

  覆盖(重写)需要注意的点:
1)父类中的私有方法不可以被覆盖;
2)在子类覆盖方法中,继续使用父类中被覆盖的方法可以通过super.函数名获取;
3)覆盖时,子类方法权限一定要大于等于父类方法权限;
4)静态只能覆盖静态。

  例子1

public class ExtendsDemo {
   
	public static void main(String[] args) {
   
		Son obj = new Son();
		//当子类与父类中含有同名的方法时,调用的是子类的方法,这种情况称为“覆盖”或“重写”
		//我们前面提到的“重载”,重载是同一个类中可以有同名的方法,但是他们的形参列表必须不同
		//覆盖(重写)指的是子类父类的方法可以同名,但是子类方法与父类方法的内容不同,可以定义子类特有的内容
		obj.show();//结果是son
	}
}
class Father
{
   
	void show()
	{
   
		System.out.println("father");
	}
}

class Son extends Father
{
   
	void show()
	{
   
		System.out.println("son");
	}
}

  例子2

public class ExtendsDemo {
   
	public static void main(String[] args) {
   
		NewTel obj = new NewTel();
		obj.show();//结果是number name pic
	}
}
class Tel
{
   
	void show()
	{
   
		System.out.println("number");//初代手机只显示号码
	}
}

class NewTel extends Tel
{
   
	void show()
	{
   
		//新手机要显示号码、姓名、图片
		//System.out.println("number");我们这一句可以不用写,直接使用super关键字调用父类show()方法的部分
		//用super关键字将父类的show()方法加进来,这样我们在重写的show()方法里面就不需要加入父类show()方法的内容,可以直接使用
		super.show();
		System.out.println("name");
		System.out.println("pic");
	}
}

2.3、子父类中构造函数的特点
  子父类构造函数的相关特点:
1)子类中所有的构造函数默认都会访问父类中空参数的构造函数(想调用其他得显式用super来调用),因为子类每一个构造函数的第一行都有一条默认的语句super();
  例子1

public class ExtendsDemo {
   
	public static void main(String[] args) {
   
		//结果是father、son,也就是说,创建子类的对象,父类的构造方法先执行,再执行子类的构造方法
		//因为子类构造方法中默认有一句super();,调用父类的构造方法
		//注意,在同一类中,this()可以代表调用本类的构造方法,调用哪一个构造方法与this(参数)中参数相关
		Son obj = new Son();
		//这一部分的结果是father son4,因为子类每一个构造函数的第一行都有一条默认的语句super(),来调用父类空参数的构造函数
		Son obj1 = new Son(4);
	}
}
class Father
{
   
	Father()
	{
   
		System.out.println("father");
	}
}
class Son extends Father
{
   
	Son()
	{
   
		//super();
		System.out.println("son");
	}
	Son(int x)
	{
   
		System.out.println("son"+x);
	}
}

2)为什么子类一定要访问父类中的构造函数?
  因为父类中的数据子类可以直接获取,所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的,所以子类在对象初始化时,要先访问一下父类中的构造函数。如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。
3)当父类中没有空参数的构造函数时(既我们设置了自己的有参数的构造函数),子类的构造函数必须通过this(用this是访问本类的其他构造函数,不可以再用super(),但是,子类总有一个构造函数会有super来访问父类的构造函数)或者super(用super是直接访问父类的构造函数)语句指定要访问的构造函数。也就是说,子类一定要访问父类的至少一个构造函数!!!

public class ExtendsDemo {
   
	public static void main(String[] args) {
   
		//结果是father4	son
		//我们在子类的构造函数中用super(4)显式调用了父类中含参数的构造函数
		Son obj = new Son();
	}
}
class Father
{
   
	Father(int x)
	{
   
		System.out.println("father"+x);
	}
}
class Son extends Father
{
   
	Son()
	{
   
		//当父类中没有空参数的构造函数时(既我们设置了自己的有参数的构造函数,又没有创建新的空参数构造函数)
		//子类的构造函数必须通过super语句指定要访问的构造函数,否则会报错 
		super(4);
		System.out.println("son");
	}
}

  父类中定义完的内容,子类没必要重新定义,子类直接调用即可。子类调用父类的构造函数super(),子类调用父类的普通函数super.普通函数。
  注意:super语句一定定义在子类构造函数的第一行。
  总结:子类的实例化过程。
1)子类的所有的构造函数,默认都会访问父类中空参数的构造函数。因为子类每一个构造函数内的第一行都有一句隐式super();当父类中没有空参数的构造函数时,子类必须手动通过super语句形式来指定要访问父类中的构造函数。
2)当然,子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数。但是子类中至少会有一个构造函数会访问父类中的构造函数。

3、final关键字
注意final关键字是基于子父类继承的基础来说的
  final关键字的特点:
1)final可以修饰类,方法,变量。final修饰的类不可以被继承。final修饰的方法不可以被覆盖(但是可以被重载,因为重载是在同一个类中,不涉及继承)。final修饰的变量是一个常量,只能被赋值一次,可以修饰成员变量与局部变量。
2)类不能被继承,父类的方法不能被重写,这可以保障代码的封装性,保证一些代码不因为子类的覆盖(重写)而出现错误,或者说是保证一些重要代码不被子类重写;
final修饰的变量是常量,只能赋值一次。当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这些值起个名字,并加上final修饰保证其不变。常量的书写规范所有字母都大写,如果由多个单词组成,单词之间通过“_”连接。

//定义一个全局常量
public static final double PI = 3.14;
//public保证其权限足够大,可以被其他类调用;static使其在类一加载进来的时候就存在直到这个类消失,其他类可以通过类名直接访问这个常量;final使其为不变的常量;

3)内部类只能访问被final修饰的局部变量。

4、抽象类(抽象只能定义类与方法)
4.1、抽象类概述
  抽象定义:抽象就是从多个事物中将共性的,本质的内容抽取出来。例如:狼和狗共性都是犬科,犬科就是抽象出来的概念。
  抽象类:Java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。
  抽象方法的由来:多个对象都具备相同的功能,但是功能具体内容有所不同,那么在抽取过程中,只抽取了功能定义,并未抽取功能主体,那么只有功能声明,没有功能主体的方法称为抽象方法。例如:狼和狗都有吼叫的方法,可是吼叫内容是不一样的。所以抽象出来的犬科虽然有吼叫功能,但是并不明确吼叫的细节。

4.2、抽象类的特点
  抽象类和抽象方法必须用abstract关键字来修饰。抽象方法只有方法声明,没有方法体,定义在抽象类中。格式:修饰符 abstract 返回值类型 函数名( 参数列表 )
  抽象类特点
1)抽象方法一定在抽象类中,抽象方法和抽象类都必须被abstract关键字修饰(抽象类可以有非抽象方法,但是只要有一个抽象方法其就是抽象类);

2)抽象类不可以被实例化,也就是不可以用new创建对象。 原因如下:抽象类是具体事物抽取出来的,本身是不具体的,没有对应的实例。例如:犬科是一个抽象的概念,真正存在的是狼和狗。而且抽象类即使创建了对象,调用抽象方法也没有意义。

3)抽象类中的抽象方法要被使用

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值