11_Package包,抽象类,接口,多态

Java SE Package包,抽象类,接口,多态

Package包

包本质来说就是文件夹, 用来管理类文件
建包的语法格式:package 域名倒写.自定义名称。 通常小写。

导包

  • 相同包下的类可以直接访问,不同包下的类必须导包,才可以使用。 格式:import 包名.类名;
  • 假如一个类中需要用到不同类,而这个两个类的名称是一样的,那么默认只能导入一个类,另一个类要带包名访问。
具体:

如果有相同的两个类Student,在不同包下,此时第一次调用会导包,但是如果需要用到另一个***.***.b.Student时,就要用全类名创建对象:

1
2
3
4
5
6
7
import ***.***.a.Student;
public static void main(String[] args) {
Student stu = new Student();
stu.eat();
com.itheima.b.Student stu2 = new com.itheima.b.Student(); // 使用全类名创建对象 : 包名 + 类名
stu2.sleep();
}

同理,如果创建类名为Scanner的类,和util中的重名,将无法正常导包使用Scanner,需要用下述的 全类名 格式

1
2
3
4
5
public class Scanner {
public static void main(String[] args) {
java.util.Scanner sc = new java.util.Scanner(System.in);
}
}

抽象类

  • 抽象方法:将共性的行为(方法)抽取到父类之后,发现该方法的实现逻辑无法在父类中给出具体明确,该方法就可以定义为抽象方法。

​ 定义格式:public abstract 返回值类型 方法名(参数列表);

  • 抽象类:如果一个类中存在抽象方法,那么该类就必须声明为抽象类

​ 定义格式:public abstract class 类名{}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
abstract class Animal {
public abstract void eat();
}

class Cat extends Animal {

@Override
public void eat() {
System.out.println("猫");
}
}

class Dog extends Animal {

@Override
public void eat() {
System.out.println("狗");
}
}

注意事项

1.抽象类不能实例化

如果抽象类允许创建对象, 就可以调用内部没有方法体的抽象方法,没有意义

2.抽象类存在构造方法

交给子类, 通过super进行访问,进行初始化等功能实现

3.抽象类中可以存在普通方法

子类继承来使用

4.抽象类的子类
1). 要么重写抽象类中的所有抽象方法
2). 要么成为抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
abstract class A {
public abstract void showA();
}

abstract class B extends A {
@Override
public void showA() {}
public abstract void showB();
}

class C extends B {
@Override
public void showB() {}
}

abstract 关键字的冲突

  • final:被 abstract 修饰的方法,强制要求子类重写,被 final 修饰的方法子类不能重写
  • private:被 abstract 修饰的方法,强制要求子类重写,被 private 修饰的方法子类不能重写
  • static:被 static 修饰的方法可以类名调用,类名调用抽象方法没有意义

接口

体现的思想是对规则的声明

思路: 如果发现一个类, 所有的组成, 都是抽象方法*[ 没有成员变量,没有普通方法时 ]*

这种类, 我们通常会设计为Java中的接口, 因为现在这个类存在的唯一价值, 就只是声明规则了


定义格式: interface 接口名 {}

注意: 接口不允许实例化

接口和类之间是实现关系, 通过implements关键字来完成

1
class 类名 implements 接口名 {}

实现类(接口的子类)的两种方式:

               1. 重写所有抽象方法
  1. 将实现类变成抽象类 [此时还是不能实例化,要再继承类,一般不用]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface Inter {
public abstract void A();
public abstract void B();
}

class InterImpl implements Inter {
super(); //此时是调用Object的构造方法,不是Inter的
@Override
public void A() {
System.out.println("A");
}
@Override
public void B() {
System.out.println("B");
}
}

所有的类都会默认继承Object类;

1
super();  // IDEA中ctrl+点击查看方法的具体实现--->Object的构造方法

接口的成员特点

1.成员变量 : 只能定义常量, 因为系统会默认加入三个关键字
public static final 这三个关键字没有顺序关系

2.成员方法 : 只能是抽象方法, 因为系统会默认加入两个关键字 注:JDK8 和 JDK9 中有一些新特性 见后续补充
public abstract

3.构造方法 : 没有

1
2
3
4
5
interface MyInter {
public static final int NUM = 10; //final命名规则:大写
public abstract void show();
void method();
}

接口和类之间的各种关系 :

  • 类和类之间 : 继承关系, 只支持单继承, 不支持多继承, 但是可以多层继承

  • 类和接口之间 : 实现关系, 可以单实现, 多实现, 同时可以在继承一个类的同时, 实现多个接口

    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 Fu {
    public void show() {
    System.out.println("此时不需要再重写了");
    }
    }

    interface A {
    void showA();
    void show();
    }

    interface B {
    void showB();
    void show();
    }

    class Zi extends Fu implements A, B {
    @Override
    public void showA() { //interface A中的方法
    }

    @Override
    public void showB() { //interface B中的方法
    }

    //注:A,B接口中show()同名,那么只需要实现一个即可,已经继承fu类了,所以不需要在此再次实现
    }
  • 接口和接口之间 : 继承关系, 可以单继承, 也可以多继承

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    interface InterA {
    void show();
    }

    interface InterB {
    void show();
    }

    interface InterC extends InterA, InterB {
    void show();
    }

    class InterCImpl implements InterC {
    @Override
    public void show() {} //同名,实现一个就是实现所有的逻辑了; 如果方法不同名,每一个接口的抽象方法都要实现;
    }

抽象类&接口 对比

抽象类 : 对事物做抽象 (描述事物)
接口 : 对行为抽象 (制定规则)

  • 成员变量 :

​ 抽象类 : 可以定义变量, 也可以定义常量
​ 接口 : 只能定义常量

  • 成员方法

​ 抽象类 : 可以是定义具体方法, 也可以定义抽象方法
​ 接口 : 只能定义抽象方法

  • 构造方法

​ 抽象类 : 有
​ 接口 : 没有

注:接口可以为程序制定规则, 代码更加规范;如果有抽象方法没实现,就无法创建类

多态

同一个行为具有多个不同表现形式或形态的能力

多态的前提 :

  • ​ 有继承 / 实现关系
  • ​ 有方法重写
  • 有父类引用指向子类对象

多态的好处

提高了程序的扩展性

  1. 对象多态
    Animal a = new Dog();
    Animal b = new Cat();
    好处: 方法的形参定义为父类类型, 这个方法可以接收该父类的任意子类对象

  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
public class Polymorphism {
public static void main(String[] args) {
useAnimal(new Dog());
useAnimal(new Cat());
}
public static void useAnimal(Animal a) { // Animal a = new Dog()/Cat();
a.eat(); // 行为多态
}
}

abstract class Animal {
public abstract void eat();
}

class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗");
}
}

class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫");
}
}

多态的成员访问特点

  • 成员变量:编译看左边(父类),执行看左边(父类)
  • 成员方法:编译看左边(父类),执行看右边(子类)

在编译的时候, 会检查父类中有没有这个方法
​ 没有 : 编译出错
​ 有 : 编译通过, 但是运行的时候, 一定会执行子类的方法逻辑

原因: 担心你调用的方法, 在父类中是一个抽象方法

1
2
3
interface Inter {
void method();
} //例如:父类为接口
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
public class Polymorphism {
public static void main(String[] args) {
Fu f = new Zi();
System.out.println(f.num); // 10
f.show(); // Zi
f.print(); // FuStatic
}
}

class Fu {
int num = 10;

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

public static void print(){
System.out.println("FuStatic");
}
}

class Zi extends Fu {
int num = 20;

@Override
public void show() {
System.out.println("Zi");
}

public static void print(){
System.out.println("ZiStatic");
}
}
  • 多态创建对象, 调用静态成员时 : 看类名

静态的成员, 推荐类名进行调用
原因: 静态的成员, 可以使用对象名调用, 但这是一种假象;实际上生成字节码文件后, 会自动将对象名调用, 改成类名调用.

多态的弊端

不能使用子类的特有成员

解决方法——多态中的转型
  • 向上转型

    ​ 从子到父(父类引用指向子类对象)

1
Fu f = new Zi();   //堆内存中开辟空间
  • 向下转型

​ 从父到子(将父类引用所指向的对象, 转交给子类类型)

1
Zi z = (Zi)f;      //空间地址赋值给z,加上强转

问题:如果被转的引用类型变量,对应的实际类型和目标类型不是同一种类型,那么在转换的时候就会出现ClassCastException

判断工具:关键字 instanceof

使用格式: 对象名 instanceof 类型
判断一个对象是否是一个类的实例
即:判断关键字左边的对象,是否是右边的类型,返回boolean类型结果

实例:

实现支付功能,需要支持多种支付方式:支付平台支付、银行卡网银支付、信用卡快捷支付,选择支付方式和输入金额后,输出为:通过—支付了—元。

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 Test {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请选择支付方式: 1. 支付平台支付 2. 银行卡网银支付 3. 信用卡快捷支付");
int choice = sc.nextInt();

Payment payment = null; //重点:引用型先赋null,否则payment.pay(money);会因为没有实例报错

switch (choice) {
case 1 :
payment = new PlatformPaymentImpl();
break;
case 2:
payment = new BankCardPaymentImpl();
break;
case 3:
payment = new CreditCardPaymentImpl();
break;
}

System.out.println("请输入支付金额: ");
double money = sc.nextDouble();
payment.pay(money);
}
}
1
2
3
4
5
6
public class BankCardPaymentImpl implements Payment{
@Override
public void pay(double money) {
System.out.println("通过银行卡网银支付了:" + money + "元!");
}
}
1
2
3
4
5
6
public class CreditCardPaymentImpl implements Payment {
@Override
public void pay(double money) {
System.out.println("通过信用卡快捷支付:" + money + "元!");
}
}
1
2
3
4
5
6
public class PlatformPaymentImpl implements Payment {
@Override
public void pay(double money) {
System.out.println("通过支付平台支付了:" + money + "元!");
}
}
1
2
3
public interface Payment {
void pay(double money);
}

补充:JDK 8,9对接口的新特性

JDK8版本接口特性:

1.允许定义非抽象方法, 需要加入default关键字

​ 作用: 解决接口的升级问题[ 接口中添加新方法,实现更多功能 , 不影响之前的 ]

​ 注意事项:

  1. public可以省略, 但是default不能省略
  2. 默认方法, 实现类是允许重写的, 但是需要去掉default关键字
  3. 如果实现了多个接口, 多个接口中存在相同的默认方法, 实现类必须重写默认方法 [因为不知道要实现哪一个默认方法,必须重写]
2.允许定义静态方法

​ 理解: 既然接口已经允许方法带有方法体了, 干脆也放开静态方法, 可以类名调用

​ 注意事项:

        1.public可以省略, 但是static不能省略

​ 2.接口中的静态方法, 只允许接口名进行调用, 不允许实现类通过对象调用

1
2
3
4
5
interface A {
[public] default void method(){}

[public] static void function(){}
}

JDK9 接口特性

接口中允许定义 private 私有方法

接口中静态方法的定义格式:
格式1:private 返回值类型 方法名(参数列表) {}
范例1:private void show() {}

格式2:private static 返回值类型 方法名(参数列表) {}
范例2:private static void method() {}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
interface Inter {
void show();
void print();

public static void start(){
System.out.println("start方法执行...");
log();
}

public static void end(){
System.out.println("end方法执行...");
log();
}

private static void log(){
System.out.println("日志记录");
}
}

好处:抽取log()方法为private static,这样可以在内部静态方法中使用,减少冗余代码并且不被其他调用。


11_Package包,抽象类,接口,多态
http://example.com/2023/02/10/JavaSE学习笔记-Package包,抽象类,接口,多态/
作者
zhanghao
发布于
2023年2月10日
许可协议