一、继承
关于继承的描述
我们在定义类的时候,比如电视和电脑类,在类中我们分别定义每个事物中的属性和行为,但是,具体看来,电视和电脑有共同的地方,比如都有有显示屏,都有插头,这些如果没定义一个类的话就会显得比较臃肿,所以可以把这些共有的或者说共性的东西提起出来,单独进行描述,只要让电脑和电视与我们单独描述的类联系上就可以了。
那么现实生活中,我们就以不断的某些事物的共性进行不断的抽取,然后进行单独描述,然后使之与原来事物建立联系,这样就方便我们描述和编程了。
要建立联系,就要用到java中的一个关键字:extends
示例代码如下:
class Person{
String name;
int age;
}
class Student{
//String name;
// age;
void Study(){}
}
class Worker{
//String name ;
//int age;
void work(){}
}
有上述示例代码可看出,Person类把后面的两个类中共性的东西抽取出来,单独在一个类中进行描述,而那两个类只需描述本类所特有的属性和行为即可。那么如何用extends建立两者之间的连接?
格式为: class Student extends Person
{
Void Study(){}
}
这样继承Person类后,Student就具有Person类中的属性和行为,这样就是两者之间产生了联系,我们之前所希望的就发生了。这里,把向上抽取取来的,就是被继承的类称为父类,继承这个类的类称为子类,这就是面向对象中的继承。
面向对象的继承关系,提高了代码的复用性,而且让类与类之间产生了关系,有了这个关系,才有了之后的多态。继承的时候,要注意,继承这个特征会把父类的所有东西都会继承过来,所以,要注意使用继承的情况。这就又回到我们最开始的地方,有两点,一是事物共性,二是两者联系(个人是理工男,所以两者联系觉得说成附属关系更好理解点)。当事物没有上述两点的时候,硬性抽取变成父类时没有意义的。
(关于联系或者父子类的关系,做一点距离,这里说的父类更多是抽象的,比如说:鸟类,这就是父类,而各个鸟种,像鸽子,鹰,海鸥,都是子类,父类时有哪些子类所抽取出来的共性,那么鸟类这个抽象的就有那么鸟的共有特征,不如飞行,觅食。这就有了关系,有了共性的描述。)
关乎于继承的一点特性
Java语言中,只支持单继承,不支持多继承,因为多继承容易带来安全隐患,当多个父类中定义了相同的功能,当功能内容不同的时候,不确定要运行哪一个。比方说C是子类,A、B是父类,C同时继承了A和B类,那么当A和B类中各有一个相同功能(方法名和方法参数都一样),那么当A运行时,就不知道该运行哪个类了。
但java中保留了这种多继承机制,用另一种形式来体现,多实现。
上述图标来说,子类继承父类,父类有的东西就可以访问,而父类又继承父类的父类,这是可以的。
这里就涉及到如何使用一个继承体系中的功能?
想要使用体系,先查阅系统父类的描述,因为父类中定义的是该体系中共性的功能。通过了解共性功能,就可以知道该体系的基本功能,那么这个体系已经可以基本使用了。那么在具体调用时,要创建最子类的对象,因为一、因为父类可能是抽象的,不能创建对象,二、创建子类对象,可以使用更多的功能,包含共有的和特有的。
总结的来说,继承:
1) 我们实际需要把某类事物的共性进行抽取并独立描述,以方便我们描述同时简化代码。同时这一点表示,不要为了简化代码二随便继承,继承必须在类与类之间有共性,有所属关系才能用继承。
2) 注意继承的关键字extends
3) 继承,可以简化书写,提高复用性,也为多态打下基础
4) 继承是有共性内容,事物间有联系的属性和行为的抽取,然后单独描述的,所以父类和子类要有联系,有附属关系,就像猫科和猫。不要进行硬性的抽取和继承。
5) 继承是单继承,就是说一个子类只能有一个父类来继承,通俗的说一个孩子只能有一个父亲
6) 想使用继承体系中的功能,知道父类功能的描述,基本就可以使用这个继承体系了。
7) 在继承体系中创建对象,要创建子类的对象,因为有两点:一、父类可能不能创建对象,二、子类有更多功能,含有共有的和特有的。
二、子父类成员
1变量:
子类继承父类,在主函数中建议对象,那么,子类引用就可以通过:对象名.变量,就可以进行调用。就相当于子类中有父类的变量,当然实际情况就是如此。
但当子父类中变量名称一样时,对象调用这个变量,就会调用子类的变量而不会调用父类中的同名变量,因为有对象的建立,通过对象名调用,就相当于变量名前加上this关键字,使得引用指向现在这个对象。如果想指向的父类的变量,就用到一个新关键字,super。
情况如下:
class Fu{
int num = 4;
}
class Zi{
int num=3;
}
子父类成员变量同名情况就是这样。
关键字super:
解读super关键字,就得说到this关键字。我们知道,this关键字是指向本类对象,代表本类对象的引用,super就是指向父类的,代表父类对象的引用。
对于this和super使用的情况如下:
如果子类中出现非私有同名成员变量时,1)子类要访问本类中的变量,用this或什么都不加2)子类要访问父类中的同名变量,用super。
如果父类有定义的同名变量函数,那么,子类就尽量不要定义了。
2函数:
子类既然继承了父类,那么就有父类中的所有功能,函数也不例外。调用规则同对象调用本类方法一致,对象名.函数。但是如果子父类函数同名,这个结果是什么样。如果子类出现和父类一模一样的函数时,当子类对象调用该函数,会运行子类函数的内容,就如同父类函数被覆盖一样,这就是函数的另一个特性:重写。当然父类函数还在,但是没有运行。
思考下,当子类继承父类,有了父类的功能,如果到子类中,子类虽具备这个功能,但是和父类工能不一致,比如相同的方法work(),父类中是人的work,这可能是写作,也可能是唱歌,但子类中就不一样,我们就让他学习,但同样是work()这个方法。这时,就没必要定义新功能,而是使用覆盖重写,保留父类定义的功能,但是具体内容重新编写。当我执行子类对象的方法时,就会执行子类自己重新编写的方法。这就是沿袭父类功能,编写
子类特有功能。
示例:
class Fu{
work(){ 唱歌,写作 }
}
class Zi extends Fu{
work(){ 学习 }
}
通过以上例子和描述,这可以得值,可以通过这个方法进行功能的拓展。我们在开发程序时,就可以用到重写,比如当变更新功能时,不能直接在原码上进行修改,而是在创建一个可继承这个的程序,进行继承并复写功能,写入新功能。这样,不改原码,但定义了新功能,这就是功能的拓展。而某些我们不需要修改的,直接用super指向父类的函数几好,节省了开发时间。
重写时注意:
1) 子类覆盖父类,必须保证子类权限大于等于父类权限,才可以重写
2) 静态只能覆盖静态(但这个极少用到)
重载和重写的区别:
1) 重载:只看同名函数的参数列表
2) 重写:字符类方法要一模一样(包括返回类型,因为返回类型如果不一样的话,子类中就相当于有返回类型不一样但是其他都一样的函数,那么在调用这个函数的时候,该返回什么类型呢)。
3构造函数
先看程序:class Fu{
Fu(){ 父类 }
}
class Zi extends Fu{
Zi(){ 子类 }
}
在主函数中建立对象,程序一运行,就会执行构造函数里的语句,结果是父类先运行,然后子类运行。为什么呢??因为在子类构造函数的第一行,有一句默认的隐式语句super(),代表调用父类的构造函数(前面我们学习的,this(),代表的是调用本类的构造函数)。super():会访问父类中空参数的构造函数,而且子类中所有构造函数(不管是有参数或没参数),默认第一行都是super()。注意,1)引用的都是空参数的,如果想引用非空参数的父类的构造函数,在子类构造函数第一行,写super(参数)。2)子类构造函数一定会访问父类构造函数,如果父类没有空参数的构造函数,那么就在子类造函数第一行,手动添加super(参数)。
为什么子类一定要访问父类中的构造函数?
因为父类中的数据,子类可以直接获取,所以子类对象在建立的时候,就要看看父类时如何对数据进行初始化,所以要访问以下父类中构造函数。看一下示例:
class Person{
private String name ;
Person(String name){
this.name =name;
}
}
class Student extends Person{
Student(String name){
super(name);
}
}
上路例子中,Student类中,也需要定义name,但是父类中有这个构造方法了,且父类中name被私有化了,所以用super(name),使用父类中的构造方法进行初始化。
4、final关键字
这货是一个修饰符,什么都可以修饰,看看他的特征。
特征:1)可以修饰类,方法,变量
3) 被修饰的类不可以被继承
4) 被修饰的方法不可以被覆盖
5) 被修饰的变量是一个常量,只能被赋值一次(潜规则:标示符全部大写),而且可以配合public和static修饰符,可以变成全局常量。
6) 内部类在局部时只能访问被final修饰的局部变量(这句注意,以后内部类用的多,记住这小知识点)
final的出现,其实与继承有关,因为继承在一定程度上打破了对象的封装功能,我们在使用时,如果继承某个类,就可以把里面的内容及进行修改,这样就打破了封装。而现实需求中,不希望被修改,所以final来修饰,就不可继承的。
5、抽象类
先看程序:
class Student
{
void study(){}
}
class BaseStudent extends Student
{
void study(){
语句1;
}
}
classAdvStudent extends Student
{
void study(){
语句2;
}
}
看上面的语句,后两个进行抽取方法study,在Student类中重新描述,建立一个空语句的函数。既然是空语句,那么执行起来是对于的。所以,当多个类中出现相同功能的时候,但主题功能不同,这是可以向上抽取的。这里,只抽取功能定义,而不抽取功能主题。则,第一类中的方法改为:void study();
因为这种方法没有方法体,所以这里用一个关键字修饰一下,这个关键字是:abstract。(只可修饰类和方法)。这是写法就改为: abstract void study(); 。因为这个方法没有方法体,那么这个类创建对象就没有意义,所以这个类也应该是抽象的,这就告诉我们,这个类中有抽象方法,不能建立对象。
总结的来说:(当然,首先要继承)
1) 抽象方法一定是定义在抽象类中。就是说,只要类中有一个抽象方法,那么这个类就是抽象类。
2) 抽象方法和抽象类都必须被abstract关键字修饰
3) 抽象类不可以建立对象,因为抽象方法没有意义。(简单说,无法实例化)
4) 抽象类中的抽象方法要被使用,必须有子类复写其所有的抽象方法,然后建立对象调用
5) 如果子类只覆盖部分抽象方法,那么该类也是一个抽象类
注意:抽象类有两类:1)里面方法全部是抽象方法,那么我们调用的时候,要全部复写
2)里面部分方法时抽象方法,那么我们调用的时候,只把抽象方法复写即可。
3)里面部分方法是抽象方法,那么我们在调用的是,如果没有全部复写里面的抽象方法,那么这个类也必须的是抽象类(意思就是不能建立对象)
4)如果我们必须具备某个父类的功能,但具体实现方式不一样,我们就可以去复写抽象类的抽象方法,那么其他的功能我们可以直接使用父类的,就不用复写,直接拿过来用即可。所以定义抽象类时,要注意对功能的描述,有的是不明确的或者说具体功能描述不清的(即子类功能内容不同),定义抽象。有的是子类用的那么就不定义抽象。
6、接口
接口可以理解为一个特殊的抽象类,就是当抽象类中的方法都是抽象的,那没该类可以通过接口的形式来表示。接口的表示方法为 : interface {
}
class用于定义类,interface用于定义接口,注意interface也是一个类,编译完也是class文件。
定义接口,格式特点:
1) 接口中常见定义:常量,抽象方法
2) 接口中成员都有固定修饰符
常量:public static final
方法: public abstract
Interface Inter{
public static final int NUM = 3;
public abstract void show();
}
看上述程序,接口中成员都是public的,成员都有固定修饰符,就像程序里的。我们可以省略int前面的修饰符,也可以省略void前面的修饰符,因为是接口,所以即便省略,系统会自动给加上,所以要注意。
既然是接口,是抽象的,那么我们使用其中的方法,所以我们就建立子类对象继承接口的方法,但现在这里使用关键字implement来实现,注意是实现接口中的方法。
示例程序:
class Test implement Inter{
覆盖接口中所有方法;
}
接口的另一大功能:多实现.
这个功能也是对多继承不支持的转换形式,java支持多实现。
1、类和接口之间
就是说,一个类可以实现多个接口,比如一个子类,可实现接口A和接口B,程序例子:
class Test implement A,B
为什么可以多实现了,因为接口中方法没有主体即执行部分,所以两个接口中同名方法,子类只需实现一次就好,不会出现不知道该执行哪一个方法的错误。一个类,在继承一个类的同时还可以实现多个接口,这就像一个儿子只有一个亲爹,但是可以有多个干爸。程序:
class Test extends C implements A, B
2、接口和接口之间
接口与接口之间,如果用实现的话,就得覆盖接口的方法,这是不现实的也没有意义的。所以接口与接口之间是继承关系。
而且接口与接口之间,可以实现多继承,但是要方法的返回类型也是一样的。否则,我们在实现时,就会出现之前多继承出现的状况:不知道执行哪个方法。
注意:我们定义接口中成员都是public的,所以实现接口,并覆盖方法时要注意,所有方法权限修饰符都是public的。
总结来说:
1) 接口是一个特殊的抽象类,里面的方法全都是抽象的。所以无法实例化。
2) Interface 最后生成的文件也是class文件,所以里面的静态成员变量可以直接类名调用
3) 接口中定义一般是常量和抽象方法,注意是常量
4) 接口中成员都有固定修饰符,即是是省略了,系统也自动加上,但是为方便读写,还是编译时期就加上。
5) 用接口的方法,需要子类对接口实现,是由implement来完成的。要全部覆盖接口抽象方法,否则子类也是抽象的。
6) 接口可以多实现
7) 类与接口之间:类可以再继承某个类的同时,再实现多个接口
接口与接口之间:是继承关系,而不是实现关系
可以实现多继承,但是注意方法格式(返回值类型一致)
8)接口的一大特点就是:功能拓展。对外暴露规则,就相当于一个接口,只要你符合我的接口这个规则,就可以。降低耦合性,使得程序可以模块式开发。
要注意一下继承和实现的区别,继承是一个属于另一个或者说有所属关系,接口则是一个可拓展的功能。这样,应该就理解了接口的功能拓展的意思。