AnthonyCJ's Blogs

Find your passion, and you'll feel the light.

JavaSE-5-面向对象-3

JavaSE-学习笔记,主要内容为面向对象三大特性(封装、继承、多态)- 多态相关知识,Object类,抽象类。接口


1. 多态

1.1 概念

多态是继封装、继承之后,面向对象的第三大特性。

生活中,比如跑的动作,小猫、小狗和大象,跑起来是不一样的。再比如飞的动作,昆虫、鸟类和飞机,飞起来也是不一样的。可见,同一行为,通过不同的事物,可以体现出来的不同的形态。多态,描述的就是这样的状态。

多态 (多种形态) 是同一个行为具有多个不同表现形式或形态的能力,多态就是同一个接口,使用不同的实例而执行不同操作。

1
2
3
父类类型 变量名 = new 子类对象;
变量名.方法名();
父类类型:指子类对象继承的父类类型,或者实现的父接口类型。

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去
调用子类的重写方法。

使用多态的好处是,可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。

实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现
出多态的扩展性与便利。

多态代码示例

父类、子类定义

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
import java.util.Objects;

class Cat extends Animal{
//新增的属性
private String color;

@Override
public void run(){
System.out.println("猫跑起来.....");
}

//
public void catchMouse(){
System.out.println("抓老鼠......");
}

}

class Dog extends Animal{
//新增的属性
private String kind;

@Override
public void run(){
System.out.println("狗狗跑起来......");
}

public void keepDoor(){
System.out.println("看门........");
}
}

public class Animal {
//成员变量
private String name;
private int age;

public void run(){
System.out.println("动物跑起来......");
}
}

多态的调用方式

1
2
3
4
5
6
7
8
9
10
11
12
public class Demo_Polymorphic {
public static void show(Animal a) {
//会根据传入的参数的不同,调用不同的子类重写的方法,体现了多态的特征
a.eat();//分别调用子类的重写,体现了多态的特征
}

public static void main(String[] args) {
show(new Animal());
show(new Cat());//以Cat为对象调用show方法;
show(new Dog());//以Dog为对象调用show方法;
}
}

1.2 引用类型转换

父子对象之间的转换分为了向上转型和向下转型 , 它们区别如下:

  • 向上转型: 通过子类对象 (小范围) 实例化父类对象 (大范围),这种属于自动转换

注意: 其意义在于当我们需要多个同父的对象调用某个方法时,通过向上转换后,则可以确定参
数的统一。方便程序设计

对象的向上转型(安全的):会失去子类新增。 子类对象,被看做了父类的类型。那么就不能访问子类
的新增, 只能访问父类的属性和方法。以及子类重写。

1
A a = new B();`// 向上转型 (B 是 A 的子类)

  • 向下转型: 通过父类对象 (大范围) 实例化子类对象 (小范围),这种属于强制转换

为什么要转型

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。所以,父类对象想要调用子类特有的方法,必须做向下转型。

instanceof关键字

在 Java 中,向下转型则是为了,通过父类强制转换为子类,从而来调用子类独有的方法,为了保证向
下转型的顺利完成,在 Java 中提供了一个关键字 instanceof, 通过 instanceof 可以判断某对象是否
是某类的实例,如果是则返回 true, 否则为 false

instanceof:判断某个对象是否是某个类的实例:类以及继承的父类。

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
/**
* 向下转型
*/
public class HelloDown {
public static void func(A a) {
a.print();
if (a instanceof B) {
// 向下转型,通过父类实例化子类
B b = (B) a;
// 调用 B 类独有的方法
b.funcB();
} else if (a instanceof C) {
// 向下转型,通过父类实例化子类
C c = (C) a;
// 调用 C 类独有的方法
c.funcC();
}
}
public static void main(String args[]) {
func(new A());
func(new B());
func(new C());
}
}

class A {
public void print() {
System.out.println("A:print");
}
}

class B extends A {
@Override
public void print() {
System.out.println("B:print");
}

public void funcB() {
System.out.println("funcB");
}
}

class C extends A {
@Override
public void print() {
System.out.println("C:print");
}

public void funcC() {
System.out.println("funcC");
}
}

父类 对象1 = new 父类();
子类 对象2 = new 子类();
父类 对象3 = new 子类();

结论:

  • 一个对象能够访问哪些成员,取决于 = 左边是定义的是什么类型
    • 父类类型:只能访问父类的属性和方法
    • 子类类型:可以访问父类的属性和方法,子类新增,子类重写
  • 一个对象最终访问哪个方法,取决于 = 右边是什么类型的对象
    • 父类对象:父类属性和方法
    • 子类对象:父类的属性和方法,以及子类重写的方法



2. Object类

2.1 概述

所有的类,都是以继承结构存在的。如果没有显示的父类,默认继承 Object 类。

  • 超类、基类,所有类的直接或间接父类,位于继承树的最顶层。
  • 任何类,如没有使用 extends 显示继承某个类,都默认直接继承Object类,否则为间接继承。
  • Object类中所定义的方法,是所有对象都具备的方法。
  • Object类型可以存储任何对象。
  • 作为参数,可接受任何对象。
  • 作为返回值,可返回任何对象。

2.2 常用方法

  • getClass() 方法
    • 用于获取对象的运行时对象的类。
    • 应用:通常用于判断两个引用中实际存储对象类型是否一致。
1
public final Class<?> getClass(){...}

  • hashCode() 方法
    • 返回该对象的十进制的哈希码值。
    • 哈希算法根据对象的地址或字符串或数字计算出来的 int 类型的数值。
1
public String toString(){...}

  • toString() 方法
    • 返回该对象的字符串表示(表现形式)。
    • 可以根据程序需求覆盖该方法,如:展示对象各个属性值。

  • equals() 方法
    • 默认实现为(this == obj),比较两个对象地址是否相同。
    • 可进行覆盖,比较两个对象的内容是否相同。
1
2
3
public boolean equals(Object obj) {
return (this == obj);
}

equals重写步骤:

  • 比较两个引用是否指向同一个对象。
  • 判断obj是否为null。
  • 判断两个引用指向的实际对象类型是否一致。
  • 强制类型转换。
  • 依次比较各个属性值是否相同。

  • finalize() 方法
    • 当对象被判定为垃圾对象时,由JVM自动调用此方法,用以标记垃圾对象,进入回收队列。
    • 垃圾对象:没有有效引用指向此对象时,为垃圾对象。
    • 垃圾回收: 由GC销毁垃圾对象,释放数据存储空间。
    • 自动回收机制:JVM的内存耗尽,一次性回收所有垃圾对象。
    • 手动回收机制:使用System.gc(); 通知JVM执行垃圾回收。



3. 抽象类

例子:人、猫、狗都属于动物,将这些生物的共同特征进行抽取,可以统称为动物,而对于各种不同的具体动物的相同行为,动物(父类)不作具体定义,交给猫、狗、人(子类)去实现。

将子类的共同特征进行抽取,抽象成一个父类。如果有的功能在父类中定义了,但是没有具体的实
现,那么可以定义为抽象的。等待子类实现即可。

  • 抽象类 :包含抽象方法的类

    • abstract 修饰的类,称为抽象类
    • 抽象类意为不够完整的类、不够具体的类
  • 抽象方法:没有方法体的方法

  • 作用:

    • 可被子类继承,提供共性属性和方法。
    • 可声明为引用,更自然的使用多态。

3.1 抽象类与抽象方法的定义

  • 抽象方法定义的格式
1
public abstract 返回值类型 方法名(参数);
  • 抽象类定义的格式
1
2
3
public abstract class 类名 {
...
}

3.2 注意事项

  • 抽象类和抽象方法都需要被 abstract 修饰。抽象方法一定要定义在抽象类中。
  • 抽象类不可以直接创建对象,原因:调用抽象方法没有意义。
  • 只有覆盖了抽象类中所有的抽象方法后,其子类才可以创建对象。否则该子类还是一个抽象类。
  • 之所以继承抽象类,更多的是在思想,是面对共性类型操作会更简单.
  • 抽象类不允许被实例化,因为可能包含有抽象方法,必须等待子类来继承,并且实现抽象方法。子类积极的实现父类中的抽象方法,但是如果没有全部实现,那么子类也是抽象的,要再等子类来继承,并且实现抽象方法。

重要的结论:抽象类和类比较,除了不能实例化之外,没有任何区别

  • 抽象类一定是个父类 ?
    • 是的,因为不断抽取而来的。
  • 抽象类中是否可以不定义抽象方法。
    • 是可以的,那这个抽象类的存在到底有什么意义呢?不让该类创建对象,方法可以直接让子类去使用.
  • abstract关键字,不能和static,private,final等关键字搭配使用。

abstract与static

abstract:用来声明抽象方法,抽象方法没有方法体,不能被直接调用,必须在子类overriding后才能使用。 static:用来声明静态方法,静态方法可以被类及其对象调用。

static与abstract不能同时使用。
用static声明方法表明这个方法在不生成类的实例时可直接被类调用,而abstract方法不能被调用,两者矛盾。



4. 接口

4.1 接口概述

接口,是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员变量、构造方法和成员方法,那么接口的内部主要就是封装了方法,包含抽象方法(JDK 7及以前),默认方法和静态方法(JDK 8),私有方法(JDK 9)。

接口的定义与定义类方式相似,但是使用 interface 关键字。它也会被编译成 .class 文件,但一定要明确它并不是类,而是另外一种引用数据类型。

引用数据类型:数组,类,接口。

接口就是一种约定,契约。一种规范。(例如生活中的USB接口,鼠标:键盘,U盘等等只要遵循了USB接口规范,那么就可以正常使用)


4.2 接口定义


4.2.1 含有抽象方法

抽象方法:使用 abstract 关键字修饰,可以省略,没有方法体。该方法供子类实现使用。
代码如下:

1
2
3
public interface InterFaceName {
public abstract void method();
}

4.2.2 含有默认方法和静态方法

默认方法:使用 default 修饰,不可省略,供子类调用或者子类重写。

静态方法:使用 static 修饰,供接口直接调用。

1
2
3
4
5
6
7
8
public interface InterFaceName {
public default void method() {
// 执行语句
}
public static void method2() {
// 执行语句
}
}

4.2.3 含有私有方法和私有静态方法

私有方法:使用 private 修饰,供接口中的默认方法或者静态方法调用。


4.3 接口实现

接口要有实现类来实现接口中的抽象方法。
子类和抽象父类的关系:extends
实现类和接口的关系:implements
类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。

非抽象子类实现接口:

  1. 必须重写接口中所有抽象方法。
  2. 继承了接口的默认方法,即可以直接调用,也可以重写。

实现格式

1
2
3
4
class 类名 implements 接口名 {
// 重写接口中抽象方法【必须】
// 重写接口中默认方法【可选】
}
  • 注意
    • 接口中抽象方法必须全部实现
    • 接口中默认方法可以继承,可以重写,二选一,但是只能通过实现类的对象来调用
    • 接口中静态方法只能使用接口名调用,不可以通过实现类的类名或者实现类的对象调用

4.3.1 一个类可以实现多个接口

  • 接口最重要的体现:解决单继承的弊端。将多继承这种机制在Java中通过多实现完成了
  • 怎么解决多继承的弊端?
    • 弊端:多继承时,当多个父类中有相同的功能时,子类调用会产生不确定性。其实核心原因就是在于多继承父类中功能有主体,而导致调用运行时,不确定运行哪个主体内容。
  • 为什么多实现能解决呢?
    • 因为接口中的功能都没有方法体,由子类来明确

4.3.2 一个类继承类通过可以实现接口

  • 接口和类之间可以通过实现产生关系,同时也学习了类与类之间可以通过继承产生关系。当一个类已经继承了一个父类,它又需要扩展额外的功能,这时接口就派上用场了。
  • 子类通过继承父类扩展功能,通过继承扩展的功能都是子类应该具备的基础功能。如果子类想要继续扩展其他类中的功能呢?这时通过实现接口来完成。
  • 接口的出现避免了单继承的局限性。父类中定义的事物的基本功能。接口中定义的事物的扩展功能。

4.3.3 接口的多继承

多个接口之间可以使用 extends 进行继承

1
2
3
public interface Interface4 extends Interface1, Interface2, Interface3 {
void show();
}

在开发中如果多个接口中存在相同方法,这时若有个类实现了这些接口,那么就要实现接口中的方
法,由于接口中的方法是抽象方法,子类实现后也不会发生调用的不确定性。


4.4 接口和抽象类的区别

  • 相同点
    • 都存在抽象方法。
    • 不能创建对象,不能实例化。
    • 可以作为引用类型。
    • 具备Object类中所定义的方法。
  • 不同点
    • 接口的所有属性都是公有静态常量,隐式使用 public static final 修饰。
    • 接口的所有方法都是公有抽象方法,隐式使用 public abstract 修饰。
    • 接口没有构造方法、动态代码块、静态代码块。

4.5 接口的好处

  • 接口的出现降低了耦合性。
  • 设计与实现完全分离,更容易更换具体实现。
  • 更容易搭建程序框架。



用爱发电,无需打赏哦~