Java语言基础9:继承的概念、继承的特点、继承中成员变量的关系、继承中成员方法的关系、继承中构造方法的关系、静态代码块、构造代码块、局部代码块。
1、继承的概念
(1)多个类中存在相同属性和行为时,将这些内容抽取到单独的一个类中,那么,这许多个类中无需再定义这些相同的属性和行为,只要继承那个类即可。
(2)通过extends关键字可以实现类与类之间的继承: class 子类名 extends 父类名 { }
(3)单独的这个类称为父类,基类或者超类;这多个类可以称为子类或者派生类。
(4)有了继承以后,我们定义一个类的时候,可以在一个已经存在的类的基础上,还可以定义自己的新成员:成员变量和成员方法。
好处:提高了代码的复用性和维护性,让类与类之间产生关系,这是多态的前提(这其实也是继承的一个弊端:类的耦合性增强了)。
开发的原则:低耦合(类与类之间的关系)、高内聚(自己完成某件事情的能力)。
(5)继承的举例子:
class Person {
String name;
int age;
public Person(){}
//getXxx()/setXxx()
public void eat() {
System.out.println("吃饭");
}
}
class Student extends Person {
public Student(){}
}
class Teacher extends Person {
public Teacher(){}
}
2、Java继承的特点:
(1)Java支持单继承,不支持多继承。一个类只能有一个父类,不可以有多个父类。
class SubDemo extends Demo{ } //ok
class SubDemo extends emo1,Demo2... //error
(2)Java支持多层继承(继承体系)class A{}
class B extends A{ }
class C extends B{ }
多层继承的例子:来形成继承体系。class GrandFather {
public void show() {
System.out.println("我是爷爷");
}
}
class Father extends GrandFather {
public void method(){
System.out.println("我是老子");
}
}
class Son extends Father {}
class ExtendsDemo2 {
public static void main(String[] args) {
Son s = new Son();
s.method(); //使用父亲的
s.show(); //使用爷爷的
}
}
3、继承的注意事项
(1)子类只能继承父类的所有非私有成员(成员变量和成员方法)。其实这也体现了JAVA的另一个弊端:打破了父类的封装性。
(2)子类不能继承父类的构造方法,但是可以通过super 关键字去访问父类的构造方法。
(3)不要为了部分功能而去使用继承。
(4)采用假设法。继承体现的是一种关系:“is a”,而不是“has a”的关系。子类是父类的一种。
4、继承中成员变量的关系:
(1)子类中的成员变量和父类中的成员变量 名称不一样。
(2)子类中的成员变量和父类中的成员变量 名称一样。(采用就近原则)
在子类方法中查找一个变量的查找顺序:
A:在子类方法的局部范围内查找,有就使用;
B:在子类的成员范围内查找,有就使用;
C:在父类的成员范围查找,有就使用;
D:如果还是找不到,则报错(先不考虑父类的父类.....)。
(3)我不仅仅要输出局部范围的num,还想要输出本类成员范围的num,怎么办?
this和super 分别是什么:
this:this代表本类对象的引用。
super:super代表父类存储空间的标识(可以理解为父类对象的引用,可以操作父类的成员)。
this和super 该怎么使用呢:
调用成员变量:
this.成员变量 调用本类的成员变量
super.成员变量 调用父类的成员变量
调用构造方法:
this(...) 调用本类的构造方法
super(...) 调用父类的构造方法
调用成员方法:
this.成员方法 调用本类的成员方法
super.成员方法 调用父类的成员方法5、继承中构造方法的关系:
(1)子类中所有的构造方法都会默认先去访问父类的无参构造方法,再回头执行自己构造方法。而不会访问父类的带参构造方法。
(2)因为子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化。
(3)注意:子类的每一个构造方法的第一条语句默认都是:super();没写的话默认有它。
(4)如果父类中没有无参构造方法,那子类的构造方法中会出现先什么现象呢?如何解决呢?
答:报错。 解决方法:
A:在父类中加一个无参构造方法。
B:子类通过 super 去显示调用父类其他的带参构造方法。
C:子类通过 this 去调用本类的其他构造方法。(子类中一定要有一个去访问了父类的构造方法,否则父类数据就没有初始化。) 本类其他构造方法也必须首先访问父类构造方法。
D:一定要注意:super(…) 或者this(….) 必须出现在第一条语句上。否则,就可能会对父类数据进行多次初始化。
(5)一个类的初始化过程:成员变量的初始化( 默认初始化——>显示初始化——>构造方法初始化 )
/*
看程序写结果:
A:成员变量 就近原则
B:this和super的问题
this访问本类的成员
super访问父类的成员
C:子类构造方法执行前默认先执行父类的无参构造方法
D:一个类的初始化过程
成员变量进行初始化
默认初始化
显示初始化
构造方法初始化
结果:
fu
zi
30
20
10
*/
class Fu{
public int num = 10;
public Fu(){
System.out.println("fu");
}
}
class Zi extends Fu{
public int num = 20;
public Zi(){
System.out.println("zi");
}
public void show(){
int num = 30;
System.out.println(num); //30
System.out.println(this.num); //20
System.out.println(super.num); //10
}
}
class ExtendsTest {
public static void main(String[] args) {
Zi z = new Zi();
z.show();
}
}
(6)一个类的静态代码块、构造代码块、构造方法的执行流程:
静态代码块——>构造代码块——>构造方法
类的加载:静态的内容是随着类的加载而加载的,所以静态代码块的内容会在类的加载时优先执行!!!
类的构造:分层初始化:先进行父类初始化,再进行子类初始化!!!
/*
看程序写结果:
A:一个类的静态代码块,构造代码块,构造方法的执行流程
静态代码块 > 构造代码块 > 构造方法
B:静态的内容是随着类的加载而加载
静态代码块的内容会优先执行
C:子类初始化之前先会进行父类的初始化
结果是:
静态代码块Fu
静态代码块Zi
构造代码块Fu
构造方法Fu
构造代码块Zi
构造方法Zi
*/
class Fu {
static {
System.out.println("静态代码块Fu");
}
{
System.out.println("构造代码块Fu");
}
public Fu() {
System.out.println("构造方法Fu");
}
}
class Zi extends Fu {
static {
System.out.println("静态代码块Zi");
}
{
System.out.println("构造代码块Zi");
}
public Zi() {
System.out.println("构造方法Zi");
}
}
class ExtendsTest2 {
public static void main(String[] args) {
Zi z = new Zi();
}
}
/*
看程序写结果:
A:成员变量的问题
int x = 10; //成员变量是基本类型
Student s = new Student(); //成员变量是引用类型
B:一个类的初始化过程
成员变量的初始化
默认初始化
显示初始化
构造方法初始化
C:子父类的初始化(分层初始化)
先进行父类初始化,然后进行子类初始化。
结果:YXYZ
问题:
虽然子类中构造方法默认有一个super(),但是,初始化的时候,不是按照那个顺序进行的。
而是按照分层初始化进行的。它仅仅表示要先初始化父类数据,再初始化子类数据。
*/
class X {
Y b = new Y();
X() {
System.out.print("X");
}
}
class Y {
Y() {
System.out.print("Y");
}
}
public class Z extends X {
Y y = new Y();
Z() {
//super
System.out.print("Z");
}
public static void main(String[] args) {
new Z();
}
}
虽然子类中的构造方法默认有一个super(),但是,初始化的时候,不是按照那个顺序进行的,而是按照分层初始化进行的。super()它仅仅表示要先初始化父类数据,再初始化子类数据。6、继承中成员方法的关系:
(1)子类中的方法和父类中方法声明 不一样,这很简单。
(2)子类中的方法和父类中方法声明 一样。
通过子类对象调用方法的顺序:
A:先找子类中,看有没有这个方法,有就使用;
B:再看父类中,看有没有这个方法,有就使用;
C:如果没有,就报错(先不考虑父类的父类.....)。
(3)方法重写:子类中出现了和父类中 方法声明一模一样的方法。
另:方法重载:本类中出现的方法名一样、参数列表不同的方法、与方法返回值无关。
子类对象调用方法的时候:先找子类本身,再找父类。
使用特点:如果方法名不同,就调用对应的方法。如果方法名相同,最终使用的是子类自己的。
方法重写的应用:当子类需要父类的功能,而功能主体子类有自己的特有内容时,可以重写父类中的方法,这样,既沿袭了父类的功能,又定义了子类特有的内容。这时,使用关键字super(...)来调用父类被子类重写的方法内容。
class Phone {
public void call(String name) {
System.out.println("给"+name+"打电话");
}
}
class NewPhone extends Phone {
public void call(String name) {
//System.out.println("给"+name+"打电话");
super.call(name);
System.out.println("可以听天气预报了");
}
}
class ExtendsDemo9 {
public static void main(String[] args) {
NewPhone np = new NewPhone();
np.call("林青霞");
}
}
(4)方法重写的注意事项:
1)父类中的私有方法不能被重写。因为父类的私有方法子类根本就不会继承,所以就谈不上重写。
2)子类重写父类方法时,访问权限不能更低,但可以更高!但是,最好一致!
3)父类的静态方法,子类也必须通过静态方法进行重写!
子类重写父类方法的时候,最好在方法声明上一模一样。
7、两个面试题:
1:方法重写和方法重载的区别? 方法重载能改变返回值类型吗?
方法重写:在子类中,出现和父类中一模一样的方法声明的现象。
方法重载:同一个类中,出现的方法名相同,参数列表不同的现象。
方法重载能改变返回值类型,因为它和返回值类型无关。
Override:方法重写
Overload:方法重载
2:this关键字和super关键字分别代表什么? 以及他们各自的使用场景和作用?
this: 代表当前类的对象引用。
super: 代表父类存储空间的标识。(可以理解为父类的引用,通过这个东西可以访问父类的成员)。
场景:
成员变量:
this.成员变量
super.成员变量
构造方法:
this(...)
super(...)
成员方法:
this.成员方法
super.成员方法
8、类的继承
先找到具体的事物,然后发现具体的事物之间有共性,这时才提取出这些共性,定义到一个父类中,然后出继承。(构造方法一定要自己给出!默认的无参构造方法和带参的构造方法!)