面向对象三大特征之继承

一.代码块

1.含义

用{}括起来的代码

2.分类

局部代码块

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

静态代码块

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

构造代码块

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

同步代码块

3.静态代码块,构造代码块,构造方法的执行顺序?

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

代码示例

/*
    代码块:在Java中,使用{}括起来的代码被称为代码块。
    根据其位置和声明的不同,可以分为
        局部代码块:局部位置,用于限定变量的生命周期。
        构造代码块:在类中的成员位置,用{}括起来的代码。每次调用构造方法执行前,都会先执行构造代码块。
            作用:可以把多个构造方法中的共同代码放到一起,对对象进行初始化。
        静态代码块:在类中的成员位置,用{}括起来的代码,只不过它用static修饰了。
            作用:一般是对类进行初始化。

    面试题?
        静态代码块,构造代码块,构造方法的执行顺序?
        静态代码块 -- 构造代码块 -- 构造方法
        静态代码块:只执行一次
        构造代码块:每次调用构造方法都执行
*/
class Code {
    static {
        int a = 1000;
        System.out.println(a);
    }

    //构造代码块
    {
        int x = 100;
        System.out.println(x);
    }

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

    //构造方法
    public Code(int a){
        System.out.println("code");
    }

    //构造代码块
    {
        int y = 200;
        System.out.println(y);
    }

    //静态代码块
    static {
        int b = 2000;
        System.out.println(b);
    }
}

class CodeDemo {
    public static void main(String[] args) {
        //局部代码块
        {
            int x = 10;
            System.out.println(x);
        }
        //找不到符号
        //System.out.println(x);
        {
            int y = 20;
            System.out.println(y);
        }
        System.out.println("---------------");

        Code c = new Code();    
        System.out.println("---------------");
        Code c2 = new Code();
        System.out.println("---------------");
        Code c3 = new Code(1);
    }
}

二.继承

1 .含义

把多个类中相同的成员提取出来,定义到一个独立的类中,然后让这多个类和该独立类产生一个关系,使这多个类也具备这些内容,这个关系就叫继承

2.格式

Java用关键字 extends 表示

格式: class 子类名 extends 父类名 { }

3.好处

提高了代码的复用性

提高了代码的维护性

让类和类产生关系,是多态的前提

4.弊端

开发原则: 高内聚,低耦合

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

耦合: 类和类的关系

(1).让类的耦合性增强,这样这个类改变,就会影响其他的相关类

(2).打破了封装性

5.特点

(1).Java只支持单继承,不支持多继承

有些语言是支持多继承,格式:extends 类1,类2,…

(2).Java支持多层继承(继承体系)

6.注意事项

(1).子类不能继承父类的私有成员

(2).子类不能集成父类的构造方法,但可以通过super去访问

(3).不要为了部分功能而去继承

7.使用时机

继承其实体现的是一种 is a 的关系

采用假设法: 如果有两个类A,B.如果符合A是B的一种,或B是A的一种,就可以考虑使用继承

8.继承中成员的关系

(1).成员变量

子类的成员变量名称和父类的成员变量名称一样,如何访问?子类的方法访问变量的查找顺序?

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

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

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

找不到,就报错。

(2).构造方法

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

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

注意:子类每一个构造方法的第一条语句默认都是:super();

父类如果没有构造方法,那么子类的构造方法会报错,如何解决?

a.让父类提供无参构造

b.子类通过super去明确的调用父类的带参构造方法

c.子类通过this调用本类的其他构造方法,子类中一定要有一个去访问了父类的构造方法,否则父类数据就没有初始化。

注意事项

this(…)或者super(…)必须出现在第一条语句上。如果不是放在第一条语句上,就可能对父类的数据进行了多次初始化,所以必须放在第一条语句上。

(3).成员方法

子类的成员方法名称和父类的成员方法名称一样,如何访问?子类对象调用方法的顺序是?

先找子类中,看有没有这个方法,有就使用

再看父类中,有没有这个方法,有就使用

如果没有就报错。

9.this和super

区别

this: 代表本类对应的引用

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

如何使用?

( 1). 调用成员变量

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

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

(2). 调用构造方法

this(…) 调用本类的构造方法

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

(3). 调用成员方法

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

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

10.Override和Overload

Override 方法重写:

含义

在子类中,出现和父类中一模一样的方法声明的现象。

注意事项:子类重写父类方法的时候,最好声明一模一样。

a.父类中私有方法不能被重写,因为父类私有方法子类根本就无法继承

b.子类重写父类方法时,访问权限不能更低,最好就一致

c.父类静态方法,子类也必须通过静态方法进行重写

Overload 方法重载:

含义

同一个类中,出现的方法名相同,参数列表不同的现象。

注意事项

参数列表不同包含: 参数名称,参数类型,参数个数

方法重载能改变返回值类型,因为它和返回值类型无关。

11.final

(1).含义

由于继承中方法有一个现象:方法重写。所以,父类的功能,就会被子类给覆盖调。
有些时候,我们不想让子类去覆盖掉父类的功能,只能让他使用。
这个时候,针对这种情况,Java就提供了一个关键字:final

(2).用法

可以修饰类,方法,变量。

(3).特点

final可以修饰类,该类不能被继承。

final可以修饰方法,该方法不能被重写。(覆盖,复写)

final可以修饰变量,该变量不能被重新赋值。因为这个变量其实常量。

(4).面试相关

final修饰局部变量的问题

基本类型:基本类型的值不能发生改变。

引用类型:引用类型的地址值不能发生改变,但是,该对象的堆内存的值是可以改变的。

final修饰变量的初始化时机

被final修饰的变量只能赋值一次。

在构造方法完毕前即定义的时候。(非静态的常量)

12.数据初始化相关面试题

代码示例(1)

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

代码示例(2)

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

代码示例(3)

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

你可能感兴趣的:(Java基础)