继承的概述
父类是找到子类的共性内容不断抽取而来的,先有的父类(子类不断加入到父类之中)。好处:
1、提高代码的复用性,少写很多代码
2、让类与类之间产生了关系,有了这个关系,才有了多态的特性
注意:千万不要为了获取其他类的功能简化代码而继承。必须是类与类之间有所属关系才可以继承。所属关系叫做 is a。
Java只支持单继承,不支持多继承。因为多继承容易带来安全隐患:当多个父类中定义了相同功能但功能内容不同时,子类对象不确定要运行哪一个。但是java保留了这种机制,并用另一种形式来体现。多实现。
java支持多层继承。也就是一个继承体系。
想要使用体系,先查阅体系中父类的描述。因为父类中定义的是该体系中的共性功能。通过了解共性功能,就可以知道该体系的基本功能。那么这个体系已经可以基本使用了。
在具体调用时,要创建最子类的对象。一是因为有可能父类不能创建对象(抽象类,接口),二是创建子类对象能使用更多的功能,包括基本的,也包括特有的
简单一句话:查阅父类功能,创建子类对象使用功能。
聚集关系
事物间除了继承关系,还有聚集关系。has a根据联系的紧密程度又分为聚合关系与组合关系。
聚合:球员是球队的一员
组合:手是身体的一部分
子父类中成员的特点
类中的成员:变量,函数,构造函数变量的特点
如果子父类中出现了非私有的同名成员变量时,子类要访问本类中的变量用this,子类要访问父类中的变量用super。this代表本类对象的引用
super代表父类对象的引用
两者可能会省略。
它们都存在于方法区中的非静态区
当有父类时,通过new会在堆内存中分配两个空间存储变量值,一个存储子类的变量,一个存储父类的变量(即使是私有的)加载子类文件时,会先加载父类文件。如下图所示:
多态:父类引用指向本类对象,代码如下:
class Fu
{
int num = 4;}
class Zi extends Fu
{
void show()
{
Sop(this.num);//此时方法区中非静态区中的super指向了本类的this对象,所以会打印4
}
}
函数的特点
当子类出现和父类一模一样的函数时,当子类对象调用该函数,会运行子类函数,如同父类的函数被覆盖了一样。这种情况是函数的另一种特性:覆盖(重写)。第一种特性叫做重载当子类继承了父类,沿袭了父类的功能到子类中,但是功能的内容却和父类不一致,这时,没有必要定义新功能,而是使用覆盖特性,保留父类功能的定义,并重写功能内容
覆盖可用于功能扩展。在子类的同名功能中使用super.功能名();来重用父类中的功能。
覆盖注意事项:
1、子类覆盖父类,必须保证子类权限大于等于父类权限,才可以覆盖,否则编译失败。注意父类中的private功能无法被覆盖,子类不知道有这个功能2、静态只能覆盖静态
重载和重写:
重载:只看同名函数的参数列表。参数列表一样但返回值不一样不够成重载重写:子父类方法要一模一样,否则编译出错。父类方法私有,子类的一个方法与之一模一样,不是override
构造函数的特点(子类的实例化过程)
构造函数无法覆盖,因为子父类类名不能一致在对子类对象进行初始化时,父类的构造函数也会运行,是因为子类所有的构造函数第一行有一条隐式语句super();会访问父类中空参数的构造函数。
当父类中没有空参数的构造函数时,子类中必须手动通过super语句来显示指定要访问的父类中的构造函数
为什么子类一定要访问父类中的构造函数?
因为父类中的数据子类可以直接获取,所以子类对象在建立时需要先查看父类是如何对这些数据进行初始化的。所以子类在对象初始化时,要先访问父类中的构造函数。super语句和this语句一定定义在子类构造函数的第一行,this访问的是子类本类中的构造函数。两者只能存在一个。子类当中至少会有一个构造函数访问父类中的构造函数
java中所有类的父类是Object类
继承的弊端:打破了封装性。为了不让类被继承,使用final修饰符修饰该类
final关键字
1、final作为一个修饰符,可以修饰类、方法、变量。
2、final修饰的类不可以被继承。为了避免被子类覆写功能
3、final修饰的方法不可以被覆盖
4、final修饰的变量是一个常量。只能被赋值一次,既可以修饰成员变量,又可以修饰局部变量。当在描述事物时,一些数据的值是固定的(比如π),为了增强阅读性,都给这些值取一个名字,方便阅读,而这个值不需要改变,加上final修饰。作为常量,所有字母都大写,如果多个单词组成,单词间通过_连接5、内部类只能访问被final修饰的局部变量
6、使用final关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的
抽象类
当多个类中出现了相同功能,但是功能主体不同,这时可以进行向上抽取,这时只抽取功能定义,而不抽取功能主体,例如:abstract void study();//注意:没有大括号,即没有方法体。看不懂的方法加上abstract标识。所述的类也是抽象类,也用其标识。
区别:当子类中功能定义和主体都相同时,就进行直接抽取。
抽象类的特点:
1、抽象方法一定定义在抽象类中。抽象类中既可以有抽象方法,也可以有非抽象方法2、抽象方法和抽象类都必须被abstract关键字修饰
3、抽象类不可以用new创建对象,因为调用抽象方法没意义。
4、抽象类中的抽象方法要被使用,必须由子类覆写其所有的抽象方法后建立子类对象调用。如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类
如果不对父类方法进行抽象,子类可以覆写也可以不覆写该方法。
abstract只能用于修饰类和方法,不能修饰变量
抽象类中可以不定义抽象方法,这样做仅仅是不让该类建立对象
模板方法模式
需求:获取一段程序运行的时间原理:获取程序开始和结束时间并相减
获取时间:System.currentTimeMillis();
代码:
abstract class GetTime
{
public final void getTime()//final防止该方法被子类覆盖
{
long start = System.currentTimeMillis();
runcode();
long end = System.currentTimeMillis();
System.out.println("毫秒:"+(end-start));
}
public abstract void runcode();//不一定是抽象的,有时候模板有默认的实现方式
}
class SubTime extends GetTime
{
public void runcode()
{
要检测的程序代码
}
}
当代码完成优化后,就可以解决这类问题。
这种方式叫做模板方法设计模式。其实就是在定义功能时,功能的一部分是确定的,但是还有一部分是不确定的,确定的部分在使用不确定的部分,这时就将不确定的部分暴露出去,由该类的子类去实现。
接口
如果抽象类里面的方法全部都是抽象的,可以表现为另外一种形式--接口 like a格式:
interface 接口名 {}
接口中的成员修饰符是固定的,即使少写哪个也会补上哪个:
成员常量:public static fianl
成员函数:public abstract
记住:接口中的成员都是public的
接口的出现将“多继承”通过另一种形式体现出来,即“多实现”。
接口是不可以创建对象的,因为有抽象方法。需要被子类实现,子类对接口中的抽象方法全都覆盖后子类才可以实例化,否则子类是一个抽象类
interface也生成class文件
接口可以被类多实现,即使接口中的方法名重复。因为多继承方法中有方法体,多实现方法中没有方法体,可以由子类任意定义。
一个类在继承一个类的同时可以实现多个接口
接口与接口之间是继承关系,代码如下:
interface A
{
void methodA();
}
interface B extends A
{
void methodB();
}
interface C extends B
{
void methodC();
}
class D implements C//D必须覆盖三个方法
{
public void methodA(){}//注意public必须要加
public void methodB(){}
public void methodC(){}
}
接口之间存在多继承,因为都没有方法体,不冲突,随便弄
interface C extends A,B
{
void methodC();
}
接口的特点
- 接口是对外暴露的规则
- 接口是程序的功能扩展
- 接口降低了耦合性
- 接口可以用来与类多实现
- 类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口
- 接口与接口之间可以有继承关系