面向对象(抽象类与接口)

本文详细介绍了Java中的抽象类和接口的概念、特点、使用方法以及它们的区别。抽象类用于定义模板,不能实例化,其抽象方法需要子类实现。接口则提供了多实现和标准定义,常量、抽象方法、默认方法和静态方法是其组成部分。通过实例展示了如何创建和使用抽象类和接口,以及如何实现多继承和比较器。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

抽象类

抽象类的定义

public abstract class 类名{
    
}

含义:

  • 抽象方法 :没有方法体的方法。

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

抽象类与普通类的区别

  • 抽象类可以有抽象方法,普通没有
  • 抽象类有构造方法,但不能new对象。普通类可以

抽象方法

被abstract关键字修饰的方法就是抽象方法,抽象方法没有方法体。

定义格式:

修饰符 abstract 返回值类型 方法名 (参数列表);

代码示例:

public abstract void work();

抽象类的特点

  • 抽象类被定义出来后,一定要有子类去实现它,否则抽象类没有意义
  • 一个类继承了抽象类,一定要实现抽象类中的所有抽象方法,除非该类也是抽象类。
  • 抽象类不能new对象,但可以通过super来创建对象

如果一个类包含抽象方法,那么该类必须是抽象类。

定义格式:

修饰符 abstract class 类名字 {
	抽象方法
}

代码示例:

public abstract class Employee { 
	public abstract void work(); 
}

抽象类的基本使用

继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该父类的抽象方法,否则,从最初的父类到最终的子类都不能创建对象,就失去意义。

代码示例:

// 1、抽象类
public abstract class Employee { 
	public abstract void work(); 
}
// 2、子类
public class Lecturer extends Employee {
	@Override
	public void work() {
		System.out.println("讲师在讲课");	
	}
}
// 3、测试类
public class EmployeeTest {
	public static void main(String[] args) {
		Lecturer lt= new Lecturer();
		lt.work();
	}
}

注意:此时的方法重写,是子类对父类抽象方法的完成实现,我们将这种方法重写的操作,也叫做实现方法

接口

Java中接口同现实生活中接口类似,一个接口可以不同的实现。例如一个USB接口,实现的设备有键盘、鼠标、麦克风、摄像头、....。

接口的好处

  • 定义标准,便于扩展
  • 接口的双方只需要面向接口编程(解耦与扩展)

接口的定义

public interface 接口名{
    //常量
    //抽象方法
    //默认方法
    //静态方法
}

实例:

public interface InterfaceName {
	// 1、常量
	public static final String DRIVER_URL = "jdbc:mysql://localhost:3306/mydb";
	// 2、抽象方法
	public abstract void work();
	// 3、静态方法
	public static void sleep() {
		System.out.println("睡觉了");
	}
	// 4、默认方法
	public default void eat() {
		System.out.println("吃饭了");
	}
}

接口常量

如果一个变量在运行过程中不能被发生改变,那么这个变量就可以称为常量。常量定义时需要给初始值。

public static final 数据类型 常量名 = 值;

接口中只能声明常量而不能声明变量,因此在接口中定义的变量全是常量。

在接口中声明常量可以省略:public static final 

  • 接口中的常量可以使用对象名调用,也可以使用类名调用

实例:

public class InterfaceTest implements InterfaceName{
	public static void main(String[] args) {
		InterfaceTest ift=new InterfaceTest();
		// 1、通过接口调用接口常量
		System.out.println(InterfaceName.DRIVER_URL);
		// 2、通过实现类调用接口常量
		System.out.println(InterfaceTest.DRIVER_URL);
		// 3、通过实现类对象调用接口常量
		System.out.println(ift.DRIVER_URL);
	}
}

抽象方法

JDK1.8以前,接口中所有的方法都是抽象方法,因此,在接口中声明抽象方法时,可以省略abstract关键字。另外接口中的方法都是公共的,可以省略public

返回值 方法名();
  • 接口中的抽象方法只能通过对象来调用

 实例:

public class InterfaceTest implements InterfaceName{
	// 重写接口方法
	@Override
	public void work() {
		System.out.println("该工作了");	
	}
	// 入口测试方法
	public static void main(String[] args) {
		InterfaceTest ift=new InterfaceTest();
		// 1.1、通过接口调用接口常量
		System.out.println(InterfaceName.DRIVER_URL);
		// 1.2、通过实现类调用接口常量
		System.out.println(InterfaceTest.DRIVER_URL);
		// 1.3、通过实现类对象调用接口常量
		System.out.println(ift.DRIVER_URL);
		
		// 2、通过实现类对象调用重写的接口方法
		ift.work();
	}
}

默认方法

default void 方法名(){
    //方法体
}
  • 默认方法只能通过接口的对象来调用
  • 默认方法只能写在接口中

 实例:

public class InterfaceTest implements InterfaceName{
	@Override
	public void work() {
		System.out.println("该工作了");	
	}
	// 重写接口默认方法,去除default关键字
	@Override
	public void eat() {
		System.out.println("吃饭了22");
	}
	
	// 入口测试方法
	public static void main(String[] args) {
		InterfaceTest ift=new InterfaceTest();
		// 1.1、通过接口调用接口常量
		System.out.println(InterfaceName.DRIVER_URL);
		// 1.2、通过实现类调用接口常量
		System.out.println(InterfaceTest.DRIVER_URL);
		// 1.3、通过实现类对象调用接口常量
		System.out.println(ift.DRIVER_URL);
		
		// 2、通过实现类对象调用重写的接口方法
		ift.work();
		
		// 3、通过接口名来调用接口静态方法
		InterfaceName.sleep();
		
		// 4、通过子类对象调用重写的默认方法
		ift.eat();	
	}
}

静态方法

static void test2(){
​
}
  • 静态方法只能通过接口的类名来调用

 实例:

public class InterfaceTest implements InterfaceName{
	// 重写接口方法
	@Override
	public void work() {
		System.out.println("该工作了");	
	}
	// 入口测试方法
	public static void main(String[] args) {
		InterfaceTest ift=new InterfaceTest();
		// 1.1、通过接口调用接口常量
		System.out.println(InterfaceName.DRIVER_URL);
		// 1.2、通过实现类调用接口常量
		System.out.println(InterfaceTest.DRIVER_URL);
		// 1.3、通过实现类对象调用接口常量
		System.out.println(ift.DRIVER_URL);
		
		// 2、通过实现类对象调用重写的接口方法
		ift.work();
		
		// 3、通过接口名来调用接口静态方法
		InterfaceName.sleep();
	}
}

接口的使用

电脑上的USB接口是无法单独使用的,它还需要具体的USB设备才能工作。即(接口无法new对象,只能通过实现类来new对象)

实现类的写法:

public class 类名 implements 接口名{
    
}

接口的使用:

接口必须配置实现类才能使用。

//接口数据类型 接口名 = new 实现类();
UsbInterface usb = new KeyBoard();
usb.work();

接口的多实现

  • 子类进行多接口实现时,如果接口常量没有重名,可以按照之前的方法进行使用;如果接口常量出现重名,就必须通过接口名调用常量。
  • 子类进行多接口实现时,接口中有多个抽象方法时,实现类必须重写所有抽象方法。如果抽象方法有重名的,只需要重写一次。
  • 子类进行多接口实现时,接口中存在同名的静态方法并不会冲突,原因是只能通过各自接口名访问静态方法。
  • 子类进行多接口实现时,接口中有多个默认方法时,实现类都可以选择继承使用。如果默认方法有重名的,必须重写一次。
  • 当一个类既继承一个父类,又实现若干个接口时,父类中的成员方法与接口中的默认方法重名,子类就近选择执行父类的成员方法。

实例: 

// 接口A
public interface InterfaceA {
	// 1、常量
	int A=1;
	// 2、抽象方法
	public abstract void work();
	// 3、静态方法
	public static void sleep() {
		System.out.println("睡觉了A");
	}
	// 4、默认方法
	public default void eat() {
		System.out.println("吃饭了A");
	}
}
// 接口B
public interface InterfaceB {
	// 1、常量
	int A=2;
	// 2、抽象方法
	public abstract void work();
	// 3、静态方法
	public static void sleep() {
		System.out.println("睡觉了B");
	}
	// 4、默认方法
	public default void eat() {
		System.out.println("吃饭了B");
	}
}
// 实现类
public class MoreInterfaceTest implements InterfaceA,InterfaceB {
	// 必须实现所有的接口方法
	@Override
	public void work() {
		System.out.println("该工作了");	
	}
	// 实现的接口默认方法重名,必须重写一次
	@Override
	public void eat() {
		System.out.println("重写的接口的eat()默认方法");	
	}
	public static void main(String[] args) {
		// 1、实现多接口时的接口常量使用
		System.out.println(InterfaceA.A);
		System.out.println(InterfaceB.A);
		
		// 2、接口抽象方法调用
		MoreInterfaceTest mit=new MoreInterfaceTest();
		mit.work();
		
		// 3、调用接口的静态方法
		InterfaceA.sleep();
		InterfaceB.sleep();
		
		// 4、调用默认方法
		mit.eat();
	}
}

接口的多继承

在Java中,类不可以多继承,但接口可以多继承。

 一个接口能继承另一个或者多个接口,这和类之间的继承比较相似。接口的继承使用 extends 关键字,子接口继承父接口的方法。如果父接口中的默认方法有重名的,那么子接口需要重写一次。代码如下:

定义父接口:

interface A { 
    public default void method(){ 
        System.out.println("A"); 
    } 
}
interface B { 
    public default void method(){ 
        System.out.println("B"); 
    } 
}

定义子接口:

interface C extends A,B{ 
    @Override public default void method() { 
        System.out.println("C"); 
    } 
}

接口比较器

当我们使用自定义类型数组进行排序时,会发现报出java.lang.ClassCastException: com.blb.demo5.User cannot be cast to java.lang.Comparable

原因是自定义数据类型不知道如何比较大小。

在JDK中,内置了2种方式可供我们进行大小的比较(支持任意数据类型)

Comparable(内置比较器)

在原类的基础上实现Comparable接口,会修改原类。

Comparable的中文意思就是可被排序的,代表本身支持排序功能。只要我们的类实现了这个接口,那么这个类的对象就会自动拥有了可被排序的能力。而且这个排序被称为类的自然顺序。这个类的对象的列表可以被Collections.sort和Arrays.sort来执行排序。同时这个类的实例具备作为sorted map的key和sorted set的元素的资格。

Comparable接口内部只有一个比较方法:

public int compareTo(T o);

这个方法主要就是为了定义我们的类所要排序的方式。compareTo方法用于比较当前元素a与指定元素b,结果为int值,如果a > b,int>0;如果a=b,int=0;如果a

Comparator(外置比较器)

新建一个比较器类,实现Compatator接口,不会改变原类。

Comparator中文译为比较器,它可以作为一个参数传递到Collections.sort和Arrays.sort方法来指定某个类对象的排序方式。同时它也能为sorted set和sorted map指定排序方式。

Comparator接口内部只有一个比较方法:

int compare(T o1, T o2);

Comparable可以看做是内部比较器,Comparator可以看做是外部比较器。一个类,可以通过实现Comparable接口来自带有序性,也可以通过额外指定Comparator来附加有序性, 二者的作用其实是一致的。

比较器使用

下面,使用Comparable和Comparator两种比较器的方式完成上述Student类型数组的比较排序。

// 学生类
class Student implements Comparable<Student> {
    private String name;
    private int age;
    public Student() {
    }
    public Student(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;
    }
    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + "]";
    }
    @Override
    public int compareTo(Student o) {
        return this.age - o.age;
    }
}
// 自定义比较器类
class MyComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.getName().charAt(0)-o2.getName().charAt(0);
    }
}
// 测试类
public class CompareTest {
    public static void main(String[] args) {
        // 初始化学生类数组
        Student[] str=new Student[5];
        str[0]=new Student("tom",22);
        str[1]=new Student("kitty",18);
        str[2]=new Student("Jacklove",23);
        str[3]=new Student("uzi",23);
        str[4]=new Student("tom",17);
        // 打印原生数组
        System.out.println(Arrays.toString(str));       
        // 使用比较器默认排序
        Arrays.sort(str);
        // 排序后再次打印
        System.out.println(Arrays.toString(str));
        // 使用比较器排序
        Arrays.sort(str, new MyComparator());
        // 排序后再次打印
        System.out.println(Arrays.toString(str));
    }
}   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力学习Java的鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值