Java学习之继承中的执行顺序详解

时间:2023-03-09 05:48:32
Java学习之继承中的执行顺序详解

代码块(理解)

(1)用{}括起来的代码。

(2)分类:

A:局部代码块

用于限定变量的生命周期,及早释放,提高内存利用率。

B:构造代码块

把多个构造方法中相同的代码可以放到这里,每个构造方法执行前,首先执行构造代码块。

C:静态代码块

static{}对类的数据进行初始化,仅仅只执行一次。

(3)静态代码块,构造代码块,构造方法的顺序问题?

静态代码块 > 构造代码块 > 构造方法

class Student {
	static {
		System.out.println("Student 静态代码块");
	}

	{
		System.out.println("Student 构造代码块");
	}

	public Student() {
		System.out.println("Student 构造方法");
	}
}

class StudentDemo {
	static {
		System.out.println("studentDemo静态代码块");
	}

	public static void main(String[] args) {
		System.out.println("我是main方法");
		Student s1 = new Student();
		Student s2 = new Student();
	}
}

运行结果如下:

/*
	写程序的执行结果。
	<span style="font-family: Arial, Helvetica, sans-serif;">studentDemo静态代码块</span>
	我是main方法
	Student 静态代码块
	Student 构造代码块
	Student 构造方法
	Student 构造代码块
	Student 构造方法
*/

继承(掌握)

(1)把多个类中相同的成员给提取出来定义到一个独立的类中。然后让这多个类和该独立的类产生一个关系,

  这多个类就具备了这些内容。这个关系叫继承。

(2)Java中如何表示继承呢?格式是什么呢?

A:用关键字extends表示

B:格式:

class 子类名 extends 父类名 {}

(3)继承的好处:

A:提高了代码的复用性

B:提高了代码的维护性

C:让类与类产生了一个关系,是多态的前提

(4)继承的弊端:

A:让类的耦合性增强。这样某个类的改变,就会影响其他和该类相关的类。

原则:低耦合,高内聚。

耦合:类与类的关系

内聚:自己完成某件事情的能力

B:打破了封装性

(5)Java中继承的特点

A:Java中类只支持单继承

B:Java中可以多层(重)继承(继承体系)

(6)继承的注意事项:

A:子类不能继承父类的私有成员

B:子类不能继承父类的构造方法,但是可以通过super去访问

C:不要为了部分功能而去继承

(7)什么时候使用继承呢?

A:继承体现的是:is a的关系。

B:采用假设法

(8)Java继承中的成员关系

A:成员变量

a:子类的成员变量名称和父类中的成员变量名称不一样,直接访问

b:子类的成员变量名称和父类中的成员变量名称一样,这个怎么访问呢?

子类的方法访问变量的查找顺序:就近原则

在子类方法的局部范围找,有就使用。

在子类的成员范围找,有就使用。

在父类的成员范围找,有就使用。

找不到,就报错。

B:构造方法

a:子类的构造方法默认会去访问父类的无参构造方法

1:子类中所有的构造方法默认都会访问父类中空参数的构造方法

2:为什么呢?

因为子类会继承父类中的数据,可能还会使用父类的数据。

所以,子类初始化之前,一定要先完成父类数据的初始化。父类的初始化是调用方法区中的构造方法进行初始化,不会创建父类对象,对象是要new关键字来创建的(

new关键字有两个作用。一是分配内存,

创建对象。二是调用构造方法,完成对象的初始化工作。完成这两步之后,才算创建了一个完整的Java对象。

所以new子类的时候,调用父类的构造方法不是创建了一个父类对象,而是只对它的数据进行初始化,那么父类这些数据存储在哪里呢?通俗说子类对象内存区域中会划一部分区域给父类的数据的存储,即子类对象内存中封装了父类的初始化数据,创建子类对象时,父类的数据就是子类的对象的一部分,不存在独立的父类的对象,所有的东西在一起才是一个完整的子类的对象)注意:子类每一个构造方法的第一条语句默认都是:super();

class Son extends Father {
	public Son() {
		//super();
		System.out.println("Son的无参构造方法");
	}

	public Son(String name) {
		//super();
		System.out.println("Son的带参构造方法");
	}
}	

class ExtendsDemo6 {
	public static void main(String[] args) {
		//创建对象
		Son s = new Son();
		System.out.println("------------");
		Son s2 = new Son("林青霞");
	}
}

运行结果:

Father的无参构造方法
Son的无参构造方法
------------
Father的无参构造方法
Son的带参构造方法

b:父类中如果没有无参构造方法,怎么办?

子类通过super去明确调用带参构造(子类用super();调用父类构造方法只能在构造方法中的第一行调用)

子类通过this调用本身的其他构造,但是一定会有一个去访问了父类的构造

习惯:最好每次都让父类提供无参构造

class Father {

	/*public Father() {
		System.out.println("Father的无参构造方法");
	}
	*/

	public Father(String name) {
		System.out.println("Father的带参构造方法");
	}
}

class Son extends Father {
	public Son() {
		super("随便给");
		System.out.println("Son的无参构造方法");
		//super("随便给");
	}

	public Son(String name) {
		//super("随便给");
		this();
		System.out.println("Son的带参构造方法");
	}
}

class ExtendsDemo7 {
	public static void main(String[] args) {
		Son s = new Son();
		System.out.println("----------------");
		Son ss = new Son("林青霞");
	}
}

运行结果:

Father的带参构造方法
Son的无参构造方法
----------------
Father的带参构造方法
Son的无参构造方法
Son的带参构造方法

C:成员方法

a:子类的成员方法和父类中的成员方法名称不一样,直接访问

b:子类的成员方法和父类中的成员方法名称一样,这个怎么访问呢?

通过子类对象访问一个方法的查找顺序:就近原则

在子类中找,有就使用

在父类中找,有就使用

找不到,就报错

(9)两个面试题:

A:Override和Overload的区别?Overload是否可以改变返回值类型?可以

B:this和super的区别和各自的作用?

this代表本类对应的引用。



super代表父类存储空间的标识(可以理解为父类引用,可以操作父类的成员)

怎么用呢?

A:调用成员变量

this.成员变量 调用本类的成员变量

super.成员变量 调用父类的成员变量

B:调用构造方法

this(..参数.)
调用本类的构造方法

super(..参数.)
调用父类的构造方法

C:调用成员方法

this.成员方法 调用本类的成员方法

super.成员方法 调用父类的成员方法

(10)数据初始化的面试题

A:一个类的初始化过程

/*
	看程序写结果:
		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();
	}
}

B:子父类的构造执行过程

/*
	看程序写结果:
		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();
	}
}

C:分层初始化

/*
	看程序写结果:
		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();
	}
}