JAVA面向对象后篇(多态与接口+抽象类)

2023-10-19 10:59

本文主要是介绍JAVA面向对象后篇(多态与接口+抽象类),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.多态

1.多态的概述

Java 多态是指同一种类型的对象,在不同的情况下有着不同的状态和行为。它是基于继承、重写和向上转型等特性实现的,多态是面向对象编程的三大特征之一,其他两个分别是封装和继承。

  • 多态的前提

    • 要有继承或实现关系
    • 要有方法的重写
    • 要有父类引用指向子类对象
    • 向上转型:父类引用指向子类对象Parent p = new Child();

多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。——动态绑定 

1.2多态的成员访问特点

  • 成员变量

    ​ 编译看父类,运行看父类

  • 成员方法

    ​ 编译看父类,运行看子类

 动物类:

public class Animal {public int age = 40;public void eat() {System.out.println("动物吃东西");}
}

猫类:

public class Cat extends Animal {public int age = 20;public int weight = 10;@Overridepublic void eat() {System.out.println("猫吃鱼");}public void playGame() {System.out.println("猫捉迷藏");}
}

测试类:

public class AnimalDemo {public static void main(String[] args) {//有父类引用指向子类对象Animal a = new Cat();System.out.println(a.age);  //40
//        System.out.println(a.weight);a.eat();  //猫吃鱼
//        a.playGame();}
}

3.多态的优缺点

优点

可扩展能力更强
如果要新增一种新的形状, 使用多态的方式代码改动成本也比较低。对于类的调用者来说, 只要创建一个新类的实例就可以了, 改动成本很低。而对于不用多态的情况, 就要把 drawShapes 中的 if - else 进行一定的修改, 改动成本更高。

  • 定义方法时候,使用父类型作为参数,在使用的时候,使用具体的子类型参与操作

缺点

不能使用子类的特有成员;代码运行效率降低。

4.重写

重写(override):也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
方法重写的规则

子类在重写父类的方法时,一般必须与父类方法原型一致: 返回值类型 方法名 (参数列表) 要完全一致 被重写的方法返回值类型可以不同,但是必须是具有父子关系的。
访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被public修饰,则子类中重写该方法就不能声明为 protected.
父类被static、private、final修饰的方法、构造方法都不能被重写。
重写的方法, 可以使用 @Override 注解来显式指定。有了这个注解能帮我们进行一些合法性校验. 例如不小心将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法, 就会编译报错, 提示无法构成重写。

重写的设计原则

对于已经投入使用的类,尽量不要进行修改。最好的方式是:重新定义一个新的类,来重复利用其中共性的内容,并且添加或者改动(重写)新的内容。

重写和重载的区别

区别点重写(override)重载(override)
参数列表一定不能修改必须修改
返回类型一定不能修改【除非可以构成父子类关系】可以修改
访问限定符一定不能做更严格的限制(可以降低限制)可以修改

 通常认为:方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。

5.多态中的转型

1.向上转型

向上转型:实际就是创建一个子类对象,将其当成父类对象来使用。(父类引用指向子类对象)
基本语法格式父类类型 对象名 = new 子类类型()

向上转型的几种应用场景:

1. 直接赋值

shap是父类类型,但可以引用一个子类对象,此时是从子类向父类转换,从小范围向大范围的转换。

Shap shap=new Circle();

2. 父类数组存放子类对象

父类数组可以放子类类型的对象-自动发生向上转型

Shape[] shapes = {cycle,rect,cycle,rect,flower};

3. 方法传参

形参为父类型引用,可以接收任意子类的对象

public static void drowshap(Shap a){a.drow();
}

4. 方法返回

父类接收返回任意子类对象

public static Shap shap() {return new Circle;
}

注意:向上转型后不能调用子类特有的方法。

2.向下转型

向下转型:将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可,即向下转换。

格式:子类型 对象名 = (子类型)父类引用;

//向上转型
Shap shap = new Circle();
//向下转型,本来指向的就是圆,因此将shap还原为圆也是安全的
Circle cir = (Circle)shap;//非父子关系,需要强制类型转换
//此时可以访问子类的方法
cir.drow();
//向上转型
Shap shap = new Circle();
//向下转型
//shap实际指向的是圆,现在要强制还原为三角,
//无法正常还原,运行时抛出:ClassCastException
Triangle tri = (Triangle)shap;//error

 向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛异常。如上代码。Java中为了提高向下转型的安全性,引入了 instanceof ,如果该表达式为true,则可以安全转换。

instanceof关键字:的作用是判断左边对象是否是右边类的实例,返回的boolean类型,truefalse.

 可以在代码中加入这些代码保障向下转型的安全:

if(shap instanceof Circle){Circle cir = (Circle)shap;cir.drow();
}
if(shap instanceof Triangle){Triangle tri = (Triangle)shap;tri.drow();
}

代码实现: 

动物类:

public class Animal {public void eat() {System.out.println("动物吃东西");}
}

猫类:

public class Cat extends Animal {@Overridepublic void eat() {System.out.println("猫吃鱼");}public void playGame() {System.out.println("猫捉迷藏");}
}

 测试类:

public class AnimalDemo {public static void main(String[] args) {//多态//向上转型Animal a = new Cat();a.eat();
//      a.playGame();//向下转型Cat c = (Cat)a;c.eat();c.playGame();}
}

附:

对于多态,是同一个行为具有多个不同表现形式或形态的能力,而这种能力实现的本质其实是动态绑定,谈到这我们有必要谈谈起动态绑定和静态绑定:

动态绑定:也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体调用那个类的方法。

静态绑定:也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代表方法重载。 

 6.多态的应用

  • 案例需求

    ​ 请采用多态的思想实现猫和狗的案例,并在测试类中进行测试

动物类:

public class Animal {private String name;private int age;public Animal() {}public Animal(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public void eat() {System.out.println("动物吃东西");}
}

 猫类:

public class Cat extends Animal {public Cat() {}public Cat(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println("猫吃鱼");}
}

狗类:

public class Dog extends Animal {public Dog() {}public Dog(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println("狗吃骨头");}
}

 测试类:

public class AnimalDemo {public static void main(String[] args) {//创建猫类对象进行测试Animal a = new Cat();a.setName("加菲");a.setAge(5);System.out.println(a.getName() + "," + a.getAge());a.eat();a = new Cat("加菲", 5);System.out.println(a.getName() + "," + a.getAge());a.eat();}
}

 2.抽象类

1.抽象类概述

普通类是一个完善的功能类,可以直接产生实例化对象,并且在普通类中可以包含有构造方法、普通方法、static方法、常量和变量等内容。而抽象类是指在普通类的结构里面增加抽象方法的组成部分。

当我们在做子类共性功能抽取时,有些方法在父类中并没有具体的体现,这个时候就需要抽象类了!

​ 在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类!

2.抽象类的特点

abstract class A{//定义一个抽象类public void fun(){//普通方法System.out.println("存在方法体的方法");}public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰}

  • 抽象类和抽象方法必须使用 abstract 关键字修饰

  • 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类

  • 抽象类不能实例化

    ​ 抽象类如何实例化呢?参照多态的方式,通过子类对象实例化,这叫抽象类多态

  • 抽象类的子类

    ​ 要么重写抽象类中的所有抽象方法

    ​ 要么是抽象类

抽象类的使用原则如下:
(1)抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public;
(2)抽象类不能直接实例化,需要依靠子类采用向上转型的方式处理;

(3)抽象类必须有子类,使用extends继承,一个子类只能继承一个抽象类;
(4)子类(如果不是抽象类)则必须覆写抽象类之中的全部抽象方法(如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类。); 

 3.抽象类的成员特点

  • 成员变量
    • 既可以是变量
    • 也可以是常量
  • 构造方法
    • 空参构造
    • 有参构造
  • 成员方法
    • 抽象方法
    • 普通方法

代码演示:

动物类

public abstract class Animal {private int age = 20;private final String city = "北京";public Animal() {}public Animal(int age) {this.age = age;}public void show() {age = 40;System.out.println(age);
//        city = "上海";System.out.println(city);}public abstract void eat();}

 猫类

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

测试类

public class AnimalDemo {public static void main(String[] args) {Animal a = new Cat();a.eat();a.show();}
}

4.抽象类的应用

  • 案例需求

    ​ 请采用抽象类的思想实现猫和狗的案例,并在测试类中进行测试

动物类

public abstract class Animal {private String name;private int age;public Animal() {}public Animal(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public abstract void eat();
}

 猫类

public class Cat extends Animal {public Cat() {}public Cat(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println("猫吃鱼");}
}

狗类

public class Dog extends Animal {public Dog() {}public Dog(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println("狗吃骨头");}
}

测试类

public class AnimalDemo {public static void main(String[] args) {//创建对象,按照多态的方式Animal a = new Cat();a.setName("加菲");a.setAge(5);System.out.println(a.getName()+","+a.getAge());a.eat();System.out.println("--------");a = new Cat("加菲",5);System.out.println(a.getName()+","+a.getAge());a.eat();}
}

5.抽象类的使用限制

(1)抽象类中有构造方法么?
由于抽象类里会存在一些属性,那么抽象类中一定存在构造方法,其存在目的是为了属性的初始化。
并且子类对象实例化的时候,依然满足先执行父类构造,再执行子类构造的顺序。

(2)抽象类可以用final声明么?
不能,因为抽象类必须有子类,而final定义的类不能有子类;

(3)抽象类能否使用static声明?

部抽象类不允许使用static声明,而内部的抽象类运行使用static声明。使用static声明的内部抽象类相当于一个外部抽象类,继承的时候使用“外部类.内部类”的形式表示类名称。

(4)可以直接调用抽象类中用static声明的方法么?
任何时候,如果要执行类中的static方法的时候,都可以在没有对象的情况下直接调用,对于抽象类也一样。

(5)有时候由于抽象类中只需要一个特定的系统子类操作,所以可以忽略掉外部子类。这样的设计在系统类库中会比较常见,目的是对用户隐藏不需要知道的子类。

3.接口

1.接口的概述

官方解释:Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)

通俗讲:接口可以理解为一种特殊的类,里面全部是由全局常量公共的抽象方法所组成。接口是解决Java无法使用多继承的一种手段,但是接口在实际中更多的作用是制定标准的。或者我们可以直接把接口理解为100%的抽象类,既接口中的方法必须全部是抽象方法。(JDK1.8之前可以这样理解)

接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用。

​ Java中的接口更多的体现在对行为的抽象! 

2.接口的特点

 就像一个类一样,一个接口也能够拥有方法和属性,但是在接口中声明的方法默认是抽象的。(即只有方法标识符,而没有方法体)。

  • 接口用关键字interface修饰

    public interface 接口名 {} 
    
  • 类实现接口用implements表示

    public class 类名 implements 接口名 {}
  • 接口不能实例化

    ​ 接口如何实例化呢?参照多态的方式,通过实现类对象实例化,这叫接口多态。

    ​ 多态的形式:具体类多态,抽象类多态,接口多态。

  • 接口的子类

    ​ 要么重写接口中的所有抽象方法

    ​ 要么子类也是抽象类

 抽象方法只能存在于抽象类或者接口中,但抽象类中却能存在非抽象方法,即有方法体的方法。接口是百分之百的抽象类

 为什么要用接口

  1. 接口被用来描述一种抽象。
  2. 因为Java不像C++一样支持多继承,所以Java可以通过实现接口来弥补这个局限
  3. 接口也被用来实现解耦。
  4. 接口被用来实现抽象,而抽象类也被用来实现抽象,为什么一定要用接口呢?接口和抽象类之间又有什么区别呢?原因是抽象类内部可能包含非final的变量,但是在接口中存在的变量一定是final,public,static的。

 3.接口的语法实现

为了声明一个接口,我们使用interface这个关键字,在接口中的所有方法都必须只声明方法标识,而不要去声明具体的方法体,因为具体的方法体的实现是由继承该接口的类来去实现的,因此,接口并不用管具体的实现。接口中的属性默认为Public Static Final.一个类实现这个接口必须实现这个接口中定义的所有的抽象方法。
  一个简单的接口就像这样:拥有全局变量和抽象方法。

   为了实现这个接口,我们使用implements关键词去实现接口:

其中testClass类实现了我们上面刚才定义的 in1 这个接口,既然你要实现接口,也就是实现接口代表的一种能力,那么你就必须去实现接口给你规定的方法,只有把接口给你规定的抽象方法都给实现了,才承认你这个类实现了这个接口,实现了这个接口代表的某种功能。上图实现了接口中规定的display()方法。

  写一个测试类,用来测试一下我们刚才实现的这个接口,因为testclass类的对象t实现了接口规定的display方法,那么自然而然就可以调用display()方法

 4.接口的成员特点

  • 成员变量

    ​ 只能是常量
    ​ 默认修饰符:public static final

  • 构造方法

    ​ 没有,因为接口主要是扩展功能的,而没有具体存在

  • 成员方法

    ​ 只能是抽象方法

    ​ 默认修饰符:public abstrac

代码演示

接口

public interface Inter {public int num = 10;public final int num2 = 20;
//    public static final int num3 = 30;int num3 = 30;//    public Inter() {}//    public void show() {}public abstract void method();void show();
}

 实现类

public class InterImpl extends Object implements Inter {public InterImpl() {super();}@Overridepublic void method() {System.out.println("method");}@Overridepublic void show() {System.out.println("show");}
}

测试类

public class InterfaceDemo {public static void main(String[] args) {Inter i = new InterImpl();
//        i.num = 20;System.out.println(i.num);
//        i.num2 = 40;System.out.println(i.num2);System.out.println(Inter.num);}
}

5.接口的应用

  • 案例需求

    ​ 对猫和狗进行训练,他们就可以跳高了,这里加入跳高功能。

    ​ 请采用抽象类和接口来实现猫狗案例,并在测试类中进行测试。

动物类

public abstract class Animal {private String name;private int age;public Animal() {}public Animal(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public abstract void eat();
}

 跳高接口

public interface Jumpping {public abstract void jump();
}

猫类

public class Cat extends Animal implements Jumpping {public Cat() {}public Cat(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println("猫吃鱼");}@Overridepublic void jump() {System.out.println("猫可以跳高了");}
}

测试类

public class AnimalDemo {public static void main(String[] args) {//创建对象,调用方法Jumpping j = new Cat();j.jump();System.out.println("--------");Animal a = new Cat();a.setName("加菲");a.setAge(5);System.out.println(a.getName()+","+a.getAge());a.eat();
//        a.jump();a = new Cat("加菲",5);System.out.println(a.getName()+","+a.getAge());a.eat();System.out.println("--------");Cat c = new Cat();c.setName("加菲");c.setAge(5);System.out.println(c.getName()+","+c.getAge());c.eat();c.jump();}
}

6.类和接口的关系

  • 类与类的关系

    ​ 继承关系,只能单继承,但是可以多层继承

  • 类与接口的关系

    ​ 实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口

  • 接口与接口的关系

    ​ 继承关系,可以单继承,也可以多继承

7.抽象类和接口的区别

  • 成员区别

    • 抽象类

      ​ 变量,常量;有构造方法;有抽象方法,也有非抽象方法

    • 接口

      ​ 常量;抽象方法

  • 关系区别

    • 类与类

      ​ 继承,单继承

    • 类与接口

      ​ 实现,可以单实现,也可以多实现

    • 接口与接口

      ​ 继承,单继承,多继承

  • 设计理念区别

    • 抽象类

      ​ 对类抽象,包括属性、行为

    • 接口

      ​ 对行为抽象,主要是行为

 4.综合应用

抽象人类:

public abstract class Person {private String name;private int age;public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public abstract void eat();
}

 抽象运动员类:

public abstract class Player extends Person {public Player() {}public Player(String name, int age) {super(name, age);}public abstract void study();
}

抽象教练类:

public abstract class Coach extends Person {public Coach() {}public Coach(String name, int age) {super(name, age);}public abstract void teach();
}

 学英语接口:

public interface SpeakEnglish {public abstract void speak();
}

篮球教练:

public class BasketballCoach extends Coach {public BasketballCoach() {}public BasketballCoach(String name, int age) {super(name, age);}@Overridepublic void teach() {System.out.println("篮球教练教如何运球和投篮");}@Overridepublic void eat() {System.out.println("篮球教练吃羊肉,喝羊奶");}
}

乒乓球教练:

public class PingPangCoach extends Coach implements SpeakEnglish {public PingPangCoach() {}public PingPangCoach(String name, int age) {super(name, age);}@Overridepublic void teach() {System.out.println("乒乓球教练教如何发球和接球");}@Overridepublic void eat() {System.out.println("乒乓球教练吃小白菜,喝大米粥");}@Overridepublic void speak() {System.out.println("乒乓球教练说英语");}
}

 乒乓球运动员:

public class PingPangPlayer extends Player implements SpeakEnglish {public PingPangPlayer() {}public PingPangPlayer(String name, int age) {super(name, age);}@Overridepublic void study() {System.out.println("乒乓球运动员学习如何发球和接球");}@Overridepublic void eat() {System.out.println("乒乓球运动员吃大白菜,喝小米粥");}@Overridepublic void speak() {System.out.println("乒乓球运动员说英语");}
}

篮球运动员:

public class BasketballPlayer extends Player {public BasketballPlayer() {}public BasketballPlayer(String name, int age) {super(name, age);}@Overridepublic void study() {System.out.println("篮球运动员学习如何运球和投篮");}@Overridepublic void eat() {System.out.println("篮球运动员吃牛肉,喝牛奶");}
}

这篇关于JAVA面向对象后篇(多态与接口+抽象类)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/239335

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟 开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚 第一站:海量资源,应有尽有 走进“智听

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定