一、继承的概述
1、继承(extends):
A:多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
B:多个类可以称为子类,单独这个类称为父类或者超类。
C:通过 extends 关键字让类与类之间产生继承关系。
• class SubDemo extends Demo{}
2、继承的体系结构:就是对要描述的事物进行不断的向上抽取,就出现了体系结构。
A:要了解这个体系结构中最共性的内容,就看最顶层的类。
B:要使用这个体系的功能,就用最底层的类创建对象。
3、继承的好处:
A:继承的出现,提高了代码的复用性。
B:继承的出现,让类与类之间产生了关系,extends来表示,这个关系的出现,提供了多态的前提。
4、继承的特点
A:Java只支持单继承,不支持多继承(其实确切的说是java对多继承进行了优化,避免了安全问题)。
• 一个类只能有一个父类,不可以有多个父类。
• class SubDemo extends Demo{} //ok
• class SubDemo extends Demo1,Demo2...//error
B:Java支持多层继承(继承体系)
• class A{}
• class B extends A{}
• class C extends B{}
5、定义继承需要注意:
• 子类可以直接访问父类中的非私有的属性和行为。
• 不要仅为了获取其他类中某个功能或部分功能而去继承。
• 类与类之间要有所属( " is a " )关系,xx1是xx2的一种。
如何判断A和B是否有继承关系?
A如果继承B,那么就可以说A是B的一种。
二、super关键字
A:super和this的含义:
this代表本类对象的引用。
super代表本类对象父类的引用。
B:super和this的用法:
this可以用于区*部变量和成员变量同名的情况。
super可以用于区分子类和父类成员变量同名的情况。
注:一般,子类中不会出现和父类同名的成员变量。
C:子类要调用父类构造函数时,可以使用super语句。
三、继承后子父类之间成员的关系
1、成员变量
1 class Fu { 2 int num1 = 5; 3 } 4 5 class Zi extends Fu { 6 int num1 = 20; 7 public void show() { 8 int num1 = 30; 9 System.out.println("num1:"+num1); 10 //当局部变量和成员变量重名的时候用this来区分 11 System.out.println("this num1:"+this.num1); 12 //当子类和父类出现了同名变量,用super来区分 13 System.out.println("father num1:"+super.num1); 14 } 15 }
总结:
A:在一个类中,如果方法中的局部变量和方法外的成员变量重名:
• 那么如果在方法内输出这变量,就是方法自己的变量里的值;
• 想要区分要用this,加上this就是输出成员变量的值。
B:在子父类中,如果出现成员变量重名的时候:
• 在子类输出会输出自己的变量里的值,
• 想要区分要用super,加上super,就是输出父类里变量的值。
2、成员方法
1 class Fu { 2 public void show() { 3 System.out.println("fu show"); 4 } 5 public void method() {} 6 } 7 8 class Zi extends Fu{ 9 /*public void show(){ 10 System.out.println("zi show"); 11 }*/ 12 public void show(){ 13 System.out.println("zi show1"); 14 } 15 }
总结:
A:子类中存在和父类成员方法相同的这种现象,叫做重写,复写,覆盖。
B:重写(override)和重载(overload)的区别:
• 重载的特点:
在同一类中。
方法名相同,参数列表不同(与返回值类型无关)。
• 重写的特点:
要有继承关系,在子父类中。
方法的声明相同(方法名和参数列表和返回值类型都相同)。
C:覆盖注意事项:
• 父类中的私有方法不可以被覆盖。
• 覆盖时,子类方法权限一定要大于等于父类方法权限;
• 静态只能覆盖静态。
D:覆盖的应用:
• 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以复写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。
• 在子类覆盖方法中,继续使用被覆盖的方法可以通过super.函数名获取。
3、构造方法
1 class Fu{ 2 //Fu(){} 3 Fu(int age){ 4 System.out.println("father age:"+age); 5 } 6 } 7 8 class Zi extends Fu{ 9 Zi(){ 10 this(40); 11 System.out.println("son"); 12 } 13 14 Zi(int age){ 15 super(age); 16 System.out.println("son age:"+age); 17 } 18 } 19 20 //Zi z = new Zi(); 21 Zi z = new Zi(30);
总结:
• 子类中所有的构造方法默认都会访问父类中空参数的构造方法,因为每一个构造方法的第一行都有一条默认的语句super();
• 当父类中没有空参数的构造方法时,子类的构造函数必须通过this或者super语句指定要访问的构造方法,或者手动提供无参构造方法。
this(...):调用本类中的构造方法
super(...):调用父类中的构造方法
• 构造方法用于创建对象,并进行初始化。建议如果你写了有参的构造函数,也要把空参的构造函数再手动加上。
否则你定义了有参的构造函数,空参的构造函数系统就不会再给了。这时候如果出现继承,往往会出错。
这样创建子类对象的时候就会报错Zi zi = new Zi(); //这句话是会去找空参的构造函数
四、子类的实例化过程
A:子类中所有的构造函数默认都会访问父类中空参数的构造函数。
因为每一个构造函数的第一行都有一条默认的语句super();
子类会具备父类中的数据,所以要先明确父类是如何对这些数据初始化的。
B:当父类中没有空参数的构造函数时,子类的构造函数必须通过this或者super语句指定要访问的构造函数。
五、final关键字
1、final可以用来修饰什么呢?
A:final可以用来修饰类:被fainl修饰的类不能被继承。
B:final可以用来修饰成员方法:被final修饰的成员方法不能被重写。
C:final可以用来修饰变量:被final修饰的变量为常量,值不能被修改(即只能被赋值一次)。
常量的命名规范:要求大写。
final int PI = 3.14;
2、final必须声明的时候就赋值吗?
一般来说,是这样的。但是特殊情况:在构造方法可以给final修饰的变量赋值。
六、抽象类
1、抽象类概述
A:抽象定义:
• 抽象就是从多个事物中将共性的,本质的内容抽取出来。
• 例如:狼和狗共性都是犬科,犬科就是抽象出来的概念。
B:抽象类和抽象方法:
• Java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。
• 没有方法体的方法称为抽象方法:
抽象方法只有方法声明,没有方法体,定义在抽象类中。
格式:修饰符 abstract 返回值类型 函数名(参数列表);
• 抽象类和抽象方法必须用 abstract 关键字来修饰。
C:抽象方法的由来:
• 多个对象都具备相同的功能,但是功能具体内容有所不同,那么在抽取过程中,只抽取了功能定义,并未抽取功能主体,
那么只有功能声明,没有功能主体的方法称为抽象方法。
• 例如:狼和狗都有吼叫的方法,可是吼叫内容是不一样的。所以抽象出来的犬科虽然有吼叫功能,但是并不明确吼叫的细节。
2、抽象类的特点
1)抽象类的特点
A:一个类如果有了抽象方法,那么这个类必须是抽象类;但抽象类里边可以没有抽象方法。
B:抽象类是不能够被实例化的,不可以用new创建对象的。原因如下:
• 抽象类是具体事物抽取出来的,本身是不具体的,没有对应的实例。例如:犬科是一个抽象的概念,真正存在的是狼和狗。
• 而且抽象类即使创建了对象,调用抽象方法也没有意义。
C:抽象类通过其子类实例化,而子类需要覆盖掉抽象类中所有的抽象方法后才可以创建对象,否则该子类也是抽象类。
2)抽象类的成员特点:
A:成员变量:子类可以直接继承抽象类中的成员变量。(抽象类中的成员变量可以和以前是一样的)
B:成员方法:抽象类中分为两种方法,
一种是抽象方法,这种方法在子类中需要被实现;
一种是普通的方法,可以被子类直接继承使用。
C:构造方法:抽象类不能被实例化,
那么它有构造方法吗?抽象类是class,那么它就有构造方法。
它的构造方法有用吗?有,为了让子类实例化的时候使用。
3、抽象类举例代码讲解
雇员示例:
• 需求:公司中程序员有姓名,工号,薪水,工作内容。
• 项目经理除了有姓名,工号,薪水,还有奖金,工作内容。
• 对给出需求进行数据建模。
4、抽象类相关问题
A:抽象类中是否有构造函数?抽象类是class,那么它就有构造方法。它的构造方法有用吗?有,为了让子类实例化的时候使用。
B:抽象关键字abstract不可以和哪些关键字共存?
• private
私有的,外部直接无法访问。
• static
那么这个时候抽象方法就可以可以通过类名调用,但是这样是没有意义的。
• final
final修饰的方法不能被重写。所以它和abstract冲突。
C:抽象类中可不可以没有抽象方法?
可以。如果这么做只有一个目的不让你创建这个类的对象
七、接口
1、接口
1)格式:
interface {}
2)解决了java中只能单继承的问题(对多继承进行了优化)。
接口的出现将“多继承”通过另一种形式体现出来,即“多实现”。
A:类与类之间是继承关系:只能是单继承。 extends
B:接口与接口之间是继承关系:可以是单继承,也可以是多继承。 extends
C:类与接口之间是实现关系:可以是单实现,也可以是多实现。 implements
2、接口的特点
1)成员特点:接口中的成员修饰符是固定的。
A:只有成员变量和成员方法。
B:成员变量默认修饰符 public static final
int X = 20;
其实是这样的 public static final int X = 20;
C:成员方法 默认修饰符 public abstract
void show();
其实是这样的 public abstract void show();
注意:接口中的成员都是public的。
建议:为了便于阅读,自己手动加上修饰符。
2)接口特点:
A:接口是对外暴露的规则
B:接口是功能的扩展
C:接口降低了程序的耦合性。
内聚(自己实现功能的能力)
高内聚,低耦合。
举例:主板和CPU,USB接口,电源插座。
D:扩展说了下接口的理解
狭义的理解就是java中的接口。
广义的理解就是:任何定义的规范都是接口。
3、接口与抽象类
A:抽象类体现继承关系,一个类只能单继承;接口体现实现关系,一个类可以多实现。
B:抽象类中可以定义非抽象方法,供子类直接使用;接口的方法都是抽象,接口中的成员都有固定修饰符。
抽象类中的成员:
成员变量:可以是常量,也可以是变量。
成员方法:可以是抽象的,也可以是非抽象的。
构造方法:虽然不可以创建对象,但是可以给子类实例化用。
接口中的成员:
成员变量:只能是常量。默认修饰符 public static final
成员方法:只能是抽象的。默认修饰符 public abstract
C:抽象类中定义的是体系结构中的共性的内容;接口中定义的是对象的扩展功能。
D:抽象类被继承表示的是:"is a"的关系。xx是yy中的一种。
接口被实现表示的是: "like a"的关系。xx像yy中的一种。
4、接口的应用举例
原理:类可以继承一个类的同时实现多个接口。
学生:Student
A:属性:学号,姓名,年龄
B:方法:学习(study),吃饭(抽象eat),抽烟(是不是所有的学员都抽烟呢?),篮球(是不是所有的人都会打篮球呢?)
分析:学员都具备学习的行为和吃饭的行为,但是并不是所有的学员都抽烟,也不是所有的学员都打篮球。
interface Smoking {
public abstract void smoking();
}
interface Sport{
public abstract void playBasketBall();
}
描述即会抽烟又会打篮球的学生:SmokeStudent extends Student implements Smoking,Sport
一个类只能继承一个类,但是可以实现多个接口,每实现一个接口,功能就扩展了一部分
SmokeStudent ss = new SmokeStudent();
ss.eat();
ss.study();
ss.smoking();
ss.playBasketBall();
八、多态
1、多态的定义
1)定义:某一类事物的多种存在形态。
例:动物中猫,狗。
猫这个对象对应的类型是猫类型
• 猫 x = new 猫();
同时猫也是动物中的一种,也可以把猫称为动物。
• 动物 y = new 猫();
• 动物是猫和狗具体事物中抽取出来的父类型。
• 父类型引用指向了子类对象。
2)程序中体现:父类或者接口的引用指向或者接收自己的子类对象。
3)好处和作用:多态的存在提高了程序的扩展性和后期可维护性
多态思想:可以指挥同一类型的一批对象做事情。多态的出现让我们复杂的问题简单化了。
A:Animal Cat、Dog
method(Animal a){a.eat();}//Animal a = new Cat();
2、多态的前提
A:类与类(或接口)要有继承(或实现)关系。
B:一定要有方法的重写(即要有覆盖操作)。
C:一定要有父类(或者接口)的引用指向子类的对象。
Person p = new SuperMan();
SuperMan sm = (SuperMan)p;
3、多态中成员的特点
1)对于名字相同的:
Fu f = new Zi();
A:成员变量:编译和运行都看Fu。
B:非静态方法:编译看Fu,运行看Zi。
C:静态方法:编译和运行都看Fu。
2)要调用子类特有的内容,需要向下转型。
总结:无论是向上转型还是向下转型,变化的都是子类对象,绝对不能把父类对象强转为子类类型。