我的编程经历(27)----------面对对象编程(2)

上篇重点总结了向上转型,以及向上转型中的重写。在了解了这些之后才能更进一步理解何为多态。先创建一个父类和一个方法,并创建复数个子类和同名的构造方法进行复写再进行继承。这样在main函数中就可以进行调用,但由于发生了动态绑定,这个时候实际上调用的是子类的方法而非父类的方法:

class Shape{
    public void draw (){

    }
}

class Square extends Shape{
    @Override
    public void draw() {
        System.out.println("♦");
    }
}

class Triangle extends Shape{
    @Override
    public void draw() {
        System.out.println("▲");
    }
}

public class test1 {
    public static void main(String[] args) {
        Square square = new Square();
        square.draw();
    }
}

运行程序可得到一个方片图片,因为发生了动态绑定,也可以将main函数中的引用及类型进行向上转型:

public class test1 {
    public static void main(String[] args) {
        Shape shape = new Square();
        shape.draw();
    }

这样也能运行得到方片。

如果再构造一个方法,统一调用draw方法,即:

public class test1 {
    public void getDraw (Shape shape){
        shape.draw();
    }

这样就能只调用getDraw的情况下打印多个图形,就可以构成多态:

class Shape{
    public void draw (){

    }
}

class Square extends Shape{
    @Override
    public void draw() {
        System.out.println("♦");
    }
}

class Triangle extends Shape{
    @Override
    public void draw() {
        System.out.println("▲");
    }
}

public class test1 {
    public static void getDraw (Shape shape){
        shape.draw();
    }

    public static void main(String[] args) {
        getDraw(new Square());
        getDraw(new Triangle());
    }
}

在Shape类中,其方法并未实现,只是单纯的用于了继承,那么可以将其方法设计为抽象方法,用abstract进行修饰,则其类也用abstract修饰成为抽象类:

abstract class Shape{
     abstract public void draw ();
}

注意其中的一些细节:

1.抽象类不可以在主函数中被实例化

2.只能单纯用于继承

3.抽象类中可以包含普通成员方法

4.如果一个普通类要继承抽象类,就必须重写抽象类中的所有抽象方法

5.若抽象类B继承了抽象类A,那么就不必再重写抽象类A的抽象方法,但在此基础上若一个普通方法继承了抽象类B,则需要重写抽象类A B中的所有方法:

abstract class A{
    int a;
    String name;
    abstract public void draw();
}
abstract class B extends A{
    abstract public void func();
}
class C extends B{
    @Override
    public void draw() {
        System.out.println("测试1");
    }

    @Override
    public void func() {
        System.out.println("测试2");
    }
}
public class test2 {
    public static void main(String[] args) {
        C c = new C();
        c.draw();
        c.func();
    }
}

6.抽象类及抽象方法不可被final修饰

再抽象类的基础上,可以进一步优化。因为Java中类的继承只能继承一个,但实际写代码过程中只能继承一个过于局限,因此可以将抽象类进一步简化为接口,接口跟在类的后面以extends来表示,不再意为继承,而是改为扩展,其中需要注意的点是:

1.接口不再是类,要以interface来修饰

2.接口当中的普通方法不能有具体的实现,若非要实现只能通过关键字default来修饰

3.接口当中可以有static静态方法

4.接口中的方法都是public的,因此可以省略不写

5.抽象方法默认是public abstract,也可以省略

6.接口也不能实例化

7.类和接口之间以implement实现,若有多个接口,接口间以逗号隔开即可

8.当一个类实现了一个接口后,就必须重写其中的所有抽象方法

9.接口当中的成员变量默认是public static final修饰,可省略但是切记必须初始化赋值

10.注意权限问题,当一个类重写某个接口的抽象方法时必须加上public,否则默认的是包访问权限,而抽象方法默认的是public,比包访问权限更广

11.若接口之间存在扩展,当类实现接口时,就要重写包括扩展前的所有接口的抽象方法

interface IA{
    int a = 10;
    default void func2(){
        System.out.println("测试3");
    }
     static void func3(){
        System.out.println("测试4");
    }
    void draw();
}
interface IB extends IA{
     void func();
}
class C implements IA,IB{
    @Override
    public void draw() {
        System.out.println("测试1");
    }

    @Override
    public void func() {
        System.out.println("测试2");
    }
}
public class test2 {
    public static void main(String[] args) {
        C c = new C();
        c.draw();
        c.func();
        c.func2();
    }
}

简单介绍两个常用的接口。先来说说Comparable接口,还是以实例来说,先自定义一个数组,但是这个数组每个元素中所包含的属性不止一个:

class Student implements{
    String name;
    int age;
    double score;

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
public class test3 {
    public static void main(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("小hhh", 15, 85.5);
        students[1] = new Student("小hhhh", 12, 80);
        students[2] = new Student("小h", 10, 98);
        System.out.println(Arrays.toString(students));

平常比较数组的大小都是调用Array.sort来进行的。但是如这个代码中的自定义数组,每个元素所包含的内容不是唯一的,这种情况下就得先明确,是用什么来进行比较,因此就可以使用Comparable接口:

class Student implements Comparable<Student>

加入接口后摁住ctrl点击该接口,下拉会发现只有一个compareTo方法,由于是接口,所以要在类中对该方法进行重写,若要比较age,则调用age:

 public int compareTo(Student o) {
        return this.age - o.age;
    }

this.所代指的就是调用的对象。

但是若要比较的是name这样的String类型,就得再调用一次compareTo方法,不能直接比较。因为本体的student只是一个引用,引用与引用直接比较Java是做不到的,所以最后代码写出来就是这样:

import java.util.Arrays;

class Student implements Comparable<Student>{
    String name;
    int age;
    double score;

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }

    @Override
    public int compareTo(Student o) {
        return this.name.compareTo(o.name);
    }
}
public class test3 {
    public static void main(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("小hhh", 15, 85.5);
        students[1] = new Student("小hhhh", 12, 80);
        students[2] = new Student("小h", 10, 98);
        System.out.println(Arrays.toString(students));
        Arrays.sort(students);
        System.out.println(Arrays.toString(students));
    }
}

但是这个方法有一个非常局限的地方。其比较的属性是固定死了的,若要改变只能改变其compareTo中的代码,若随意改动,会造成一系列的连锁反应。因此更多推荐的是使用比较器Comparator进行比较。

例如,要对age进行比较,即可单独令一个类使用比较器接口,摁住ctrl再点击Comparator可再源码中看到其第一个抽象方法,在该类中进行重写即可获得相应的比较器:

class AgeComparator implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.age - o2.age;
    }
}

同样的道理也可以写出比较name与比较score的比较器:

class ScoreComparator implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return (int) (o1.score - o2.score);
    }
}
class NameComparator implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.name.compareTo(o2.name);
    }
}

这样的话需要比较什么就调用相应的比较器即可,无需对代码本身进行修改,对代码的破坏性大大降低:

import java.util.Arrays;
import java.util.Comparator;

class Student{
    String name;
    int age;
    double score;

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}
class AgeComparator implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.age - o2.age;
    }
}
class ScoreComparator implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return (int) (o1.score - o2.score);
    }
}
class NameComparator implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.name.compareTo(o2.name);
    }
}
public class test4 {
    public static void main(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("aaaaa", 15,66.3);
        students[1] = new Student("bbbb", 18,75);
        students[2] = new Student("ccc", 14,89);
        System.out.println(Arrays.toString(students));
        AgeComparator ageComparator = new AgeComparator();
        Arrays.sort(students, ageComparator);
        System.out.println(Arrays.toString(students));
    }
}

在具体实践中使用哪一个跟具体的业务存在关系,但比较器是较为推荐的一个,其功能相比之下更为安全。

---------------------------------最后编辑于2023.4.14下午四点左右

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值