抽象类
抽象类的定义
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));
}
}