0%

java-Day3——继承

Java中的继承

继承是类和类之间的关系

继承的理解:

自上而下:定义了一个类A,在定义另一个类B时,发现类B的功能与类A相似,考虑类B继承于类A

自下而上:定义B,C,D有类似的属性和,则考虑可以将相同的属性和方法进行抽取,封装到类A中,让类B,C,D继承于类A

语法格式:

1
2
3
class B extends A{

}

B类继承了A类。

注意:java中的继承是单继承,只能有一个父类。但可以多重继承

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
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

class Father {
public String name;
int age; //默认的属性
private int id;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public Father() {
System.out.println("Constructor Father");
}

public void eat() {
System.out.println("Manger");
}
void work() {
System.out.println("Courir");
}
private void fumer() {
System.out.println("Fumer");
}
public void test() {
fumer();
}
}

class Son extends Father{
public Son() {
System.out.println("Constructor Son");
}
}

public class Demo1{
public static void main(String[] args) {
Son son = new Son();
son.name = "YY";
son.age = 1;
System.out.println(son.name);
System.out.println(son.age);
son.eat();
son.work();
//使用set/get访问父类私有化属性
son.setId(999);
son.test();
}
}

重写override

重写目的:子类可以继承父类的非私有化的方法,有时父类的需求满足不了子类的需求,这时需要重写父类非私有的方法

重写的要求

  • 必须要有继承关系
  • 父类的方法必须是非私有化
  • 在子类中重写的方法,除方法体外其他的都一样
  • 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
  • 关于返回值
    -父类的方法的返回值类型是void,则子类重写的返回值必须是void
    -父类返回值为基本数据类型,子类必须是一模一样的基本数据类型
    -父类返回值是引用数据类型(比如类),子类返回值可以与被重写的返回值类型相同或是被重写的方法的返回值类型的子类
  • 关于异常
    -子类重写抛出的异常可以与父类方法的异常类型相同或是异常类型的子类
    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

    class Father{
    public void eat() {
    System.out.println("Manger F");
    }
    public void drive() {
    System.out.println("Conduire F");
    }
    }

    class Son extends Father{
    //重写的方法和父类方法的返回值、参数、方法名字一模一样但是方法体不一样。
    @Override //重写的严格限定
    public void eat() {
    System.out.println("Manger Son");
    }

    @Override
    public void drive() {
    System.out.println("drive Son");
    }
    }

    public class Demo1 {
    public static void main(String[] args) {
    Son son = new Son();
    son.eat();
    son.drive();
    }
    }

重载overload

java同一个类中有很多个方法,方法名一样,但参数类型不一样,这就重载

总结:

  • 方法的重载必须在同一个类中
  • 方法名字必须一致
  • 方法的参数类型必须不一致
  • 方法的返回值可以不一样
  • 无参构造和有参构造,也是一种重载

Super关键字

只能用于继承,并在子类中使用。代表父类对象
Case1: 子类继承父类后,对父类方法进行重写,在子类中对父类的方法进行调用
Case2: 子类继承父类后,发现子类和父类中定义类了同名的属性,在子类中区分同名属性

this:

  • ​ 代表当前类对象
  • ​ 可以调用属性和方法
  • ​ 可以调用构造方法

super:

  • ​ 代表父类的对象
  • ​ 可以调用父类的成员变量和成员方法
  • ​ 当super调用属性时,就是内存里面那一个,子类和父类的共有属性都是同一个内存地址
  • ​ 可以调用父类的构造方法

子类对象实例化全过程

1.从结构角度来看:体现为类的继承性

当我们创建子类对象后,子类对象就获取了其父类中声明所有的属性和方法,在权限允许的情况下,可以直接调用

2.从过程角度来看:

当我们通过子类的构造器创建对象时,子类的构造器一定会直接或间接调用其父类的构造器,而其父类的构造器同样会直接或间接的调用其父类的父类构造器…直到调用了Object类中的构造器为止

正因为我们调用过子类所有父类的构造器,所有我们就会将父类中声明的属性、方法加载到内存中,供子类对象使用

抽象

在面向对象中,所有的对象可以通过类来创建。并不是所有类可以通过创建对象。如果一个类中没有足够完整的信息来描述具体的对象的话,那么这个类叫抽象类

abstract修饰

  • 此类成为抽象类
  • 抽象类不能实例化
  • 抽象类中是包含构造器的,因为子类实例化时会直接或间接调用到父类的构造器
  • 抽象类可以没有抽象方法。

abstract修饰方法

  • 此方法即为抽象方法
  • 抽象方法只有方法声明,没有方法体。
  • 子类必须要重写父类的所有抽象方法后才能实例化

abstract不能修饰:属性、构造器、代码块等

abstract不能修饰:私有方法、静态方法、final方法、final类

  • 私有方法不能重写
  • 静态方法不支持重写
  • final的方法不能被重写
  • final修饰的类无子类

对象的多态性

1.如何理解多态性:一个事物的多种形态。

2.Java多态性的体现:

​ 子类对象的多态性:父类的引用指向子类的对象

​ 比如 Peple p = new Man();

3.多态性的应用:

多态性的应用:虚拟方法的调用

在多态场景下,调用方法时:

​ 编译时,认为方法是左边声明的父类的类型的方法

​ 执行时,实际执行的是右边子类重写父类的方法

简称:编译看左边,运行看右边

多态是运行时的行为。

4.多态使用前提:

  • 要有类的继承关系
  • 要有方法的重写

5.多态的实用性

开发中:

使用父类做方法的形参,是多态使用最多的场合。即使增加了新的子类,方法也无需改变,提高了扩展性,符合开闭原则。

开闭原则

  1. 对扩展开放,对修改关闭
  2. 简单来说,软件系统的各种组件,如模块、类、功能等,应该在不修改现有代码基础上,引入新功能。

ex:

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
class Account{
public void withdraw(){} //取钱
}

class CheckAccount extends Account{
@Override
public void withdraw(){} //取钱
}

class SavingAccount extends Account{
@Override
public void withdraw(){} //取钱
}

class Customer{
Account account;

public void setAccount(Account account){
this.account = account;
}

public Account getAccount(){
return account;
}

class Test{
main(){
Customer cust = new Customer();
cust.setAccount(new CheckAccount()); //直接使用子类
cust.withdraw(); //使用的也是子类重写的方法
}
}
}

好处: 减少代码冗余,不需要定义多个重载的方法

弊端:

在多态场景下,创建了子类的对象,也加载了子类特有的属性和方法。但是由于声明为父类的引用导致我们没有办法直接调用子类特有的属性和方法。

向上转型和向下转型

因为多态一定会有把子类对象赋值给父类变量的时候,这个时候,在编译期间,就会出现类型转换的现象。

但是,父类变量接受了子类对象之后,我们就不能调用子类拥有而父类没有的方法了。想要调用子类特有的方法,必须做类型转换,使得编译通过。

VR33I.png

向下转型后就可以调用子类的特有方法。但在转型前,先使用instanceof进行判断

格式: a instanceOf A (判断对象a是否是类A的实例)

Object类

1.类java.lang.Object是类层次结构的根类,即其他所有层次的父类。

  • Object类中没有声明属性

  • Object类提供了一个空参构造器

Equals()

1.适用性:

任何引用数据类型都可以使用

2.子类使用说明:

  • 自定义类在没有重写Object中的equals()方法时,调用的就是Object类中声明的equals(),比较两个对象的引用地址是否相同。(或比较两个对象是否指向了堆空间中的同一个对象实体)

  • 像String、File、Date和包装类等,它们都重写了Object类中的equals()方法,用于比较两个对象的实体内容是否相等

3.开发中使用说明:

实际开发中,针对于自定义的类,常常会判断两个对象是否equals(),而此时主要是判断两个对象的属性值是否相等。所有我们需要重写object类的equals()方法

重写方法:

  • 自己重写

  • 调用IDEA自动生成

4.注意:判断两个对象是否equals(),除了该对象的类需要重写equals之外,其内部的类类型的属性所在的类,也需要重写equals()

5.常见问题:区分 == 和equals()

== :运算符

  1. 使用范围:基本数据类型、引用数据类型
  2. 基本数据类型:判断数据值是否相等

equals():方法

  1. 使用范围:只能使用在引用数据类型上
  2. 具体使用:对于类来说,重写equals()和不重写equals的区别

toString()

Object中的toString()调用后,返回当前对象所属的类和地址值

1.开发中的使用场景

平时在调用system,out.println()打印对象变量时,其实就调用了对象的toString()

2.子类使用说明

  • 自定义的类,在没有重写Object类的toString()的情况下,默认返回的是当前对象的地址

  • 像String、file、Date或包装类等object子类,它们都重写了Object类的toString(),在调用toString()时,返回当前对象的实体内容

3.开发中使用说明

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