面向对象【三】(代码块、继承、构造方法、方法重写)

代码块

  • 代码块概述

    在Java中,使用{}括起来的代码被称为代码块。

  • 代码块分类
    根据其位置和声明的不同,可以分为局部代码块,构造代码块,静态代码块,同步代码块(多线程讲解)。

  • 常见代码块的应用:

    1.局部代码块,在方法中出现;限定变量生命周期,及早释放,提高内存利用率

    2.构造代码块,在类中方法外出现;在创建对象时执行,优先于构造方法执行,每创建一个对象都会执行

    3.静态代码块,在类中方法外出现,加了static修饰,用于给类进行初始化,在加载的时候就执行,并且只执行一次。静态代码块里面只能访问静态变量

看程序写结果,语句后面的数字为执行顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

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

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

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

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

public static void main(String[] args) {
System.out.println("我是main方法");//2

Student s1 = new Student();
Student s2 = new Student();
}
}

继承

  • 继承概述
    多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。

  • 继承格式

    1. 通过extends关键字可以实现类与类的继承
    2. class 子类名 extends 父类名 {}
    3. 单独的这个类称为父类,基类或者超类;这多个类可以称为子类或者派生类。

继承的好处和弊端

​ 案例:

父类Person

1
2
3
4
5
6
7
8
9
10
11
12
public class Person {
String name;
int age;

public void eat() {
System.out.println("吃饭");
}

public void sleep() {
System.out.println("睡觉");
}
}

子类Student

1
2
3
4
5
6
public class Student extends Person{

public void talkLove(){
System.out.println("谈恋爱");
}
}

子类Teacher

1
2
3
4
5
6
7

public class Teacher extends Person{

public void teache(){
System.out.println("教书");
}
}

从上述代码可以看出:

  • Student和Teacher类继承了Person类的成员变量和成员方法,因为Teacher和Student作为人都会具有年龄和姓名两个属性,而且具有吃饭和睡觉的功能.
  • 因为我们将这些子类所具有的相同功能都抽取到一个类中作为父类,所以Teacher和Student类不需要再写这些代码,只需要从父类那里继承就行,并且继承后可以在类中加入自己独特的功能代码,比如Teacher会教书,Student会谈恋爱。
  • 继承完成后我们就可以利用StudnetTeacher来创建各自的对象

测试类MyTest

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MyTest {
public static void main(String[] args) {
Student student = new Student();
student.name="张三"; //调用了继承了父类的成员变量
student.age=19;
System.out.println(student.name+"==="+student.age);
student.sleep(); //调用了继承了父类的成员方法
student.eat();
student.talkLove(); //调用自己特有的方法
System.out.println("-----------------");
Teacher teacher = new Teacher();
teacher.name="沈某某";
teacher.age=10;
System.out.println(teacher.name+"==="+teacher.age);
teacher.sleep();
teacher.eat();
teacher.teache();

}
}

继承的好处:

  1. 提高了代码的复用性
  2. 提高了代码的维护性(比如要想修改吃饭吃米饭便可直接在父类中进行修改,而不用在两个子类中分别修改,提升了代码的维护性)
  3. 让类与类之间产生了关系,是多态的前提

继承的弊端:

类的耦合性增强了。(可能改变了父类中的一些成员,导致它的子类中的东西也会改变,这也是耦合的缺点)

  • 开发的原则:高内聚,低耦合。
  • 耦合:类与类的关系
  • 内聚:就是自己完成某件事情的能力

类的继承特点

Java中类的继承特点:

  • Java 只支持单继承不支持多继承,有些语言是支持多继承,格式:extends 类1,类2,…
  • Java支持多层继承(继承体系)
  • Object是所有类的顶层父类,所有类都是直接,或间接继承自他

继承的注意事项

  • 继承的注意事项:

    子类只能继承父类所有非私有的成员(成员方法和成员变量)

    子类不能继承父类的构造方法,但是可以通过super(待会儿讲)关键字去访问父类构造方法。

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

  • 什么时候使用继承:

    如果有两个类A,B。只有他们符合A是B的一种,或者B是A的一种,就可以考虑使用继承。

继承中成员变量的关系

  • 在使用继承时可能会存在下面两种情况:

    1.子类中的成员变量和父类中的成员变量名称不一样

    2.子类中的成员变量和父类中的成员变量名称一样

  • 上述问题如何解决?这就涉及到之前讲过的变量查找顺序遵循的就近原则,是对之前的就近原则的拓展

在子类中访问一个变量的查找顺序(就近原则):

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

​ 2.在子类的成员范围找,有就使用

​ 3.在父类的成员范围找,有就使用

​ 4.如果还找不到,就报错

代码示例:
执行下面的测试类(子类中的成员方法形参、成员变量以及父类的成员变量名一样)后,如果我想输出

1
2
3
1
200
100

程序中的成员方法应该如何定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class MyTest {
public static void main(String[] args) {
B b = new B();
int num=1;
b.show(num);
//变量的访问原则:遵循就近原则,先在局部找这个变量,找到就使用,如果局部没找到,去本类的成员位置找,找到就使用
//如果本类的成员位置没找到,去父类的成员位置找,找到就使用。
}
}

class A{
int num=100;
}

class B extends A{
int num=200;
public void show(int num){
//就近访问原则
System.out.println(num);
System.out.println(this.num);
//System.out.println(new A().num); //涉及到匿名变量
System.out.println(super.num); //引入super关键字
}

}

this和super的区别和应用

  • thissuper的区别:
    this 代表的是本类对象的引用,谁调用成员方法this就代表谁
    super代表的是父类存储空间的标识(可以理解成父类的引用,可以操作父类的成员)

  • this和super的使用:

    • 调用成员变量:
      1.this.成员变量 调用本类的成员变量

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

    • 调用构造方法:
      1.this(...) 调用本类的构造方法

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

    • 调用成员方法:
      1.this.成员方法 调用本类的成员方法
      2.super.成员方法 调用父类的成员方法

代码演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class MyTest {
public static void main(String[] args) {
B b = new B(); //this() 访问本类的空参构造
b.show(1); // super() 访问父类的空参构造
b.hehe();
System.out.println("------------");
b.test();

}
}

class A {
int a = 200;

public void hehe() {
System.out.println("这是父类的一个方法");
}
}

class B extends A {
int a = 100;

public void show(int a) {
System.out.println(a);
System.out.println(this.a);
System.out.println(super.a);
}

public void test2() {
System.out.println("这是子类test2方法");
}

public void test() {
this.test2();
this.hehe();//本类对象调用父类的方法
super.hehe();//使用父类的空间标识去调用父类的方法
}
}

根据上述讲解可知该代码演示最终的输出结果为

1
2
3
4
5
6
7
8
1
100
200
这是父类的一个方法
------------
这是子类test2方法
这是父类的一个方法
这是父类的一个方法

继承中构造方法的关系

子类中所有的构造方法默认都会访问父类中空参数的构造方法(来实现对父类的初始化)

原因:因为子类会继承父类中的数据,可能还会使用父类的数据,所以,子类初始化之前,一定要先完成父类数据的初始化。

其实:
每一个构造方法的第一条语句默认都是:super(),只要在写构造方法时系统就会默认在第一条语句之前写上这条语句,但是不会显示,所以我们可以写也可以不写,不会影响对父类构造的初始化,注意Object没有父类。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class MyTest {
public static void main(String[] args) {
Son son = new Son(10);
System.out.println(son.b);
}
}

class Father{
int num=1000;
public Father() {
super();
System.out.println("这是父类的空参构造");
}
}

class Son extends Father{
int b=1;
public Son() {
super();
System.out.println("这是子类的空参");

}

public Son(int b) {
super();
this.b = b;
System.out.println("子类有参构造执行了");
}
}

按照上述讲解可知上述代码最终输出结果如下:

1
2
3
这是父类的空参构造
子类有参构造执行了
10

继承中构造方法的案例演示及注意事项

  • 父类没有无参构造方法,子类怎么办?

    在父类中添加一个无参的构造方法

    子类通过super去显示调用父类其他的带参的构造方法

    子类通过this去调用本类的其他构造方法,本类其他构造也必须首先访问了父类构造

  • 注意事项:
    super(…)或者this(…)必须出现在第一条语句上,要在执行构造方法中的业务逻辑之前完成对父类构造方法的调用或者对本类其他构造方法的调用

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class MyTest {
public static void main(String[] args) {
////假如我父类里面没有提供空参构造怎么办?
////1.那子类可以去调父类有参构造
////2.先用this(参数)本类的有参构造,然后你调用的那个构造又去调用父类的有参构造
//System.out.println(zi.num);
Zi zi = new Zi();

}
}

class Fu{
int num=10;

public Fu(int num) {
super();
this.num = num;
System.out.println("父类的有参构造执行了");
}
}

class Zi extends Fu{
int num=100;
public Zi() {
super(10);
// this(10000);
System.out.println("子类的空参构造执行了");
}

public Zi(int num) {
super(num);
System.out.println("子类的有参构造执行了");
}
}

根据上述知识点讲解可知上述代码最终输出结果为:

1
2
父类的有参构造执行了
子类的空参构造执行了

继承中的面试题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
A:案例演示
看程序写结果1
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 Test {
public static void main(String[] args) {
Zi z = new Zi();
z.show();
}
}

B:案例演示
看程序写结果2
class Fu {
static {
System.out.println("静态代码块Fu"); //1
}

{
System.out.println("构造代码块Fu"); //3
}

public Fu() {
System.out.println("构造方法Fu"); //4
}
}

class Zi extends Fu {
static {
System.out.println("静态代码块Zi"); //2
}

{
System.out.println("构造代码块Zi"); //5
}

public Zi() {
System.out.println("构造方法Zi"); //6
}
}

Zi z = new Zi(); 请执行结果。

A案例的输出结果为:

1
2
3
4
5
fu
zi
30
20
10

B案例的输出结果为:

1
2
3
4
5
6
静态代码块Fu
静态代码块Zi
构造代码块Fu
构造方法Fu
构造代码块Zi
构造方法Zi

继承中成员方法关系

在使用继承时可能会出现下面两种情况:

  • 当子类的方法名和父类的方法名不一样的时候
  • 当子类的方法名和父类的方法名一样的时候

通过子类调用方法的查找顺序:

  1. 先查找子类中有没有该方法,如果有就使用
  2. 再看父类中有没有该方法,有就使用
  3. 如果没有就报错
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class MyTest {
public static void main(String[] args) {
Zi zi = new Zi();
zi.show();
zi.test();

}
}

class Fu {
int num2=100;
public void show(){
System.out.println("我是父类的show方法");
}
}
class Zi extends Fu{
int num=200;

public void test(){
System.out.println("我是子类的test方法");
}

public void show() {
System.out.println("我是子类的show方法");
}
}

方法重写

  • 什么是方法重写:

    子类中出现了和父类中一模一样的方法声明(方法名,参数列表,返回值类型),也被称为方法覆盖,方法复写。

  • 方法重写的应用:

    如果说子类对父类的方法实现不满意,那么子类就可以覆盖他,或者说,子类想要对父类的方法的实现功能进行扩展,也可以使用方法重写的这种机制

方法重写的注意事项

  • 方法重写注意事项:

    1.父类中私有方法不能被重写,因为父类私有方法子类根本就无法继承
    2.子类重写父类方法时,访问权限不能更低,最好就一致
    3.父类静态方法,子类也必须通过静态方法进行重写 其实这个算不上方法重写,但是现象确实如此

    4.子类重写父类方法的时候,最好声明一模一样。

-------------本文结束感谢您的阅读-------------
0%