JavaSE基础——(9)面向对象之多态、抽象类和接口
目录
一、多态
1.1多态概述
多态即事务存在的多种形态,同一个行为具有多个不同表现形式或形态的能力。
1.2多态的前提
- 要有继承关系
- 要有方法的重写
- 要有父类引用指向子类对象
我们用一个例子来体会多态的用法:
public class Main {
public static void main(String[] args){
Employee e1=new Employee();
System.out.println(e1.getSalary());
Employee e2=new Manager();
System.out.println(e2.getSalary());
}
}
class Employee{
private double salary;
public Employee(){
this.salary=5000;
}
public double getSalary(){
return this.salary;
}
}
class Manager extends Employee{
private double Bonus;
public Manager(){
this.Bonus=5000;
}
public double getSalary(){
return (this.Bonus+super.getSalary());
}
}
我们定义一个Employee职员类,里面有一个获取工资的方法gerSalary(),
然后让Manager继承Employee类,重写getSalary()方法,
接着我们在主函数里面用Employee创建两个对象,用Employee和Manager分别对这两个对象进行初始化,
然后输出工资,得到的结果如下:
可以看到同样是Employee创建的对象,用Employee和Manager初始化的输出工资不同,
这种同一个行为具有多个不同表现形式的特点叫做多态。
1.3多态中成员访问特点
1.3.1成员变量
如果我们同时在父类和子类中定义相同名称的成员变量,将父类引用指向子类对象,
然后将对象的成员变量输出,其结果会是父类定义的值还是子类定义的值呢?我们用代码测试一下:
public class Main {
public static void main(String[] args){
Father f=new Son();
System.out.println((f).num);
}
}
class Father{
int num=1;
}
class Son extends Father{
int num=2;
}
其输出结果如下:
由结果可知访问到的变量num是父类中定义的值,
由此可见在多态中,成员变量的值看左边的类,
左边的类定义的成员变量是什么值就是什么值。
1.3.2静态方法
如果我们对静态方法在子类中进行重新实现,那么将父类引用指向子类对象,
再使用对象的该静态方法会调用子类的还是父类的呢?我们用代码进行测试:
public class Main {
public static void main(String[] args){
Father f=new Son();
f.print();
}
}
class Father{
int num=1;
public static void print(){
System.out.println("Father Static Method");
}
}
class Son extends Father{
int num=2;
public static void print(){
System.out.println("Son Static Method");
}
}
运行结果如图所示:
由此可以看出当在父类和子类中同时实现了同名的静态方法时,
看左边创建的类是什么,那么调用时使用的静态方法就是那个类定义的静态方法。
1.4多态中向上转型和向下转型
如果我们在子类中写一个特有的方法,然后让父类引用指向子类对象,
那么可以访问子类中特有的方法吗?我们用代码测试一下:
public class Main {
public static void main(String[] args){
Father f=new Son();
f.playToys();
}
}
class Father{
int num=1;
public static void print(){
System.out.println("Father Static Method");
}
}
class Son extends Father{
int num=2;
public static void print(){
System.out.println("Son Static Method");
}
public void playToys(){
System.out.println("play toys");
}
}
编译的时候就直接无法通过,说找不到方法playToys(),这个方法是子类独特的方法,没有在父类中声明,
所以该方法无法被访问到,那么有没有什么方法可以帮助我们访问到子类特有的方法呢?
答案是肯定的,这种方法就是向下转型。
在基本数据类型中,我们可以使用自动类型转换和强制类型转换来解决两个数据类型不同的赋值问题,
在多态中,也存在相似的方法,我们称为向上转型和向下转型:
- 向上转型:父类引用指向子类对象
- 向下转型:父类对象强制转换为子类对象
一般来说只有先向上转型才会有向下转型,我们在主函数中使用向下转型的方法访问playToys()再看看是否可以正确运行,
((Son)f).playToys();
编译没有错误,然后可以看到结果确实访问到了该方法:
所以使用向下转型可以将父类对象转换为子类对象,
这样就可以访问子类定义的一些特殊方法,其使用方法和高精度数据强制转换为低精度数据一样。
1.5多态的优缺点
- 优点:
- 提高了代码的维护性
- 提高了代码的扩展性
- 在作为形参的时候,可以接受任何子类对象的参数
- 缺点
- 不能使用子类特有的属性和方法
二、抽象
2.1抽象类的概念
抽象类是类的一种,除了不能实例化对象,其余都与普通的类相同。
抽象类是用来描述对象的,同样也有成员变量,成员方法和构造函数等等。
2.2抽象类特点
- 抽象类和抽象方法的特点必须使用abstract关键字修饰
- abstract class 类名()
- public abstract void 方法名()
- 抽象类不一定有抽象方法,但有抽象方法的类一定是抽象方法或者接口
- 抽象类不能直接实例化对象,通过多态的方法让其子类实例化
- 抽象类的子类
- 要么是抽象类
- 要么则重写抽象类中的所有抽象方法
接下来我们使用一个抽象动物类来熟悉抽象类的使用方法:
public class Main {
public static void main(String[] args) {
Animal animal1=new Cat();
animal1.eat();
Animal animal2=new Dog();
animal2.eat();
}
}
abstract class Animal{
public abstract void eat();
}
class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("狗吃骨头");
}
}
我们可以看到输出结果如下:
2.3抽象类的成员特点
- 成员变量:既可以是变量,也可以是常量。注:abstract不能修饰成员变量;
- 构造方法:用于子类访问父类数据的初始化;
- 成员方法:既可以是抽象的,也可以是非抽象的。
抽象类中的抽象方法是强制子类要求重写的方法,
而非抽象方法则是子类继承的,可以提高代码的复用性。
2.4 抽象类中的面试题
- 一个抽象类中如果没有抽象方法,那么其意义在于不让其他类创建该类对象,只能交给子类完成
- abstract不能和以下关键字共存
- static:被static修饰的可以通过类名调用方法,但是调用抽象方法没有意义
- final:被final修饰的方法不可被重写,而抽象方法必须要重写
- private:被private修饰的方法子类无法访问,则不能被重写
三、接口
3.1接口的概述
接口可以理解为特殊的抽象类,在接口中所有的方法都是抽象方法,
java中我们用interface关键字描述,从广义来说所有对外提供规则的都是接口。
3.2接口的特点
- 接口使用interface关键字修饰:interface 接口名{}
- 类实现接口用implements表示:class 类名 implements 接口名 {}
- 接口通过多态的方式来实例化
- 接口的子类
- 可以是抽象类,意义不大
- 可以是具体类,要重写接口中的所有抽象方法
我们使用一个简单例子熟悉一下接口的使用
public class Main {
public static void main(String[] args) {
Inter test=new testInter();
test.print();
}
}
interface Inter{
public abstract void print();
}
class testInter implements Inter{
public void print(){
System.out.println("接口测试");
}
}
在主函数中定义一个子类,调用重写后接口的方法print() ,其输出结果如下:
3.3接口的成员特点
- 成员变量:只能是常量,并且是静态的和共有的,默认修饰符为public static final
- 构造方法:接口没有构造方法,其实现的类中一般调用的构造方法为默认继承的Object类
- 成员方法:只能是抽象方法,默认修饰符为public abstract
四、类与接口的关系
4.1类与类的关系
继承关系,只能单继承,不可以多继承,但是可以多层继承
class test extends classA
4.2类与接口的关系
实现关系,可以单实现,也可以多实现
并且可以在继承一个类的同时实现多个接口
class test extends classA implements InterA,InterB
4.3接口与接口的关系
继承关系,可以单继承也可以多继承
interface test extends InterA,InterB
本文地址:https://blog.csdn.net/weixin_39478524/article/details/109584380