抽象类和接口

本文详细介绍了Java中的抽象类和接口的概念、特点及使用示例。抽象类主要用于定义部分实现,不能实例化,而接口则提供了一种多继承的方式,其中的方法默认为抽象的。文章还探讨了抽象类和接口的区别,以及匿名类和Lambda表达式在函数式编程中的应用。

抽象类

抽象类概念

◼ Java类可定义一些不含方法体的方法,它的实现交给子类根据自己的情况去 实现,这样的方法就是抽象方法,包含抽象方法的类叫抽象类

◼ 抽象关键字:abstract

◼ 抽象类只是一个类型的部分实现,所以不能被实例化(不能new对象) (那有啥用?)

◼ abstract不能用于static方法或者构造函数,也不能与private、final共同修饰 同一个方法 (想想)

抽象类示例

image-20211125224640790

抽象类示例2 (带构造函数的抽象类)

抽象类Animal:

◼ 公有属性:animalName(String)

◼ 2个构造函数:Animal()、Animal(String)

◼ 抽象方法:eat()

子类: Dog和Cat类

◼ 各自都有构造函数: Dog()/Dog(String)、Cat()/Cat(String)

◼ 各自实现抽象方法:eat()

◼ 分别输出: "某某狗吃骨头!", "某某猫吃老鼠!"

image-20211125225133445

image-20211125225145623

测试类

public class Test {
    public static void main(String args[]) {
		//Animal a = new Animal(); //报错
        Animal a = new Dog("哈士奇");
		//Animal a = new Cat("大橘");
        a.eat();
    }
}

抽象类特点归纳

◼ 含有抽象方法的类必须被声明为抽象类。但抽象类中不一定包含的都是抽象方法

◼ 抽象类可以有构造方法,但构造方法不能声明为抽象★

◼ 抽象类提供一个类型的部分实现,所以不能被实例化(即不能用new去产生对象), 但可声明对象(用于多态) ★

◼ 抽象类不能用final来修饰,即一个类不能既是最终类又是抽象类

◼ 抽象类的子类必须重写所有抽象方法后才能被实例化,否则子类还是个抽象类

◼ 抽象方法只需声明,而不需实现

◼ abstract不能与private、static、final并列修饰同一个方法

 

接口

1. 接口概念

接口是一种特殊的抽象类:

◼ 如果一个抽象类中的所有方法都是抽象的,这个类就定义为接口 (interface)

◼ 接口的所有方法通常由子类全部实现 (是implements而非extends),不同子类的 实现可以具有不同的功能

◼ 如果一个类没有全部实现某个接口的所有方法,则这个类必须声 明为抽象的

接口示例

定义Shape接口,包含两个抽象方法:

◼ double getPerimeter(); //求周长

◼ double getArea(); //求面积

两个实现类:

◼ 矩形类(Rectangle)和圆(Circle)类

◼ 有自有属性,能求周长和面积

Shape接口

public interface Shape {
	public abstract double getPerimeter(); //求周长
	public abstract double getArea(); //求面积
}
其中public abstract可省略不写,即接口内方法默认是公有、抽象的

Rectangle类

class Rectangle implements Shape { //implements实现接口
    private double height;
    private double width;
    public Rectangle(double height,double width) {
        this.height = height;
        this.width = width;
    }
    @Override
    public double getPerimeter() {	//实现接口方法
        return 2 * (height + width);
    }
    @Override
    public double getArea() {//实现接口方法
        return height * width;
    }
}

Circle类

class Circle implements Shape {//implements实现接口
    private double radius;
    public Circle(double radius) {
        this.radius = radius;
    }
    @Override
    public double getPerimeter() {//实现接口方法
        return 2 * Math.PI * radius;
    }
    @Override
    public double getArea() {//实现接口方法
        return Math.PI * radius * radius;
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        Shape r = new Rectangle(10,10);	//多态体现	
		// Shape r=new Circle(10);		//多态体现	
        System.out.println("矩形面积="+ r.getArea() );
        System.out.println("矩形周长="+ r.getPerimeter() );
    }
}
//Shape s = new Shape(); × 接口不能被实例化

2. 接口常量

◼ 接口中不能有变量,但可有常量

◼ 常量默认是 public static final 类型(公有全局静态常量),且必须被 显示初始化。

为什么不能定义变量:由于接口中的方法都是抽象的,如果接口可以定义变量,那么在接口中 无法通过行为(方法)来修改属性。

为什么是public static:接口提供的是一种统一的"协议",接口中的属性也属于"协议"成员,所 有实现类都可以共享这一"协议"。

为什么是final:实现接口的对象如果修改了接口中的属性,那么所有对象也都会自动拥有这一 改变后的值,这和接口提供的统一的抽象这种思想相抵触。

接口常量示例

接口常量调用方法:接口名.常量

public interface A {
    public static final int END=0; // OK 标准写法
    private final int OK=1; // × 不能为private
    int a; // × 没有初始化属于变量,接口不能有变量
    int b=1; // OK 初始化的变量会自动被识别为public static final常量
    public final int NO=-1; // OK 不写也默认是static
    static int share=0; // OK 不写也默认是public、final
}
//建议后三行public static final都写全
接口常量用法

image-20211125231509268

3. 接口default/static方法

◼ Java 8 以后,接口可添加 default 或者 static 方法 (有实现的)

static方法:实现接口的类或者子接口不会继承(不能重写),用接口.方法名()调用
default方法:实现接口的类可以继承,可以选择重写 (注:多接口实现如有同名default方法则必须重写)
接口default方法
public interface A {
    public default void foo(){		//default不能省略
        System.out.println("这是default方法");
    }
}
class B implements A {
    public void foo() {		//实现类可以继承default方法或根据需要重写 (重写时不加default)
        System.out.println("重写default方法");
    }
}
class Test {
    public static void main(String[] args) {
        A a = new B();
        a.foo(); // 多态,输出 重写default方法
    }
}

接口static方法
public interface A {
    public static void a(){		//static不能省略
        System.out.println("这是A");
    }
}
public class B implements A {	//实现类不会继承static方法(不能重写)
}
public class test {
    public static void main(String[] args) {
        A a = new B();
        a.a(); // ×		//实现类不会继承接口中的 static 方法
        A.a(); // 正确调用
    }
}

4. 多接口

◼ Java一个类可以实现多个接口,从而间接的实现多继承

interface A {
    double fun1();
}
    Interface B {
        double fun2();
        }
class C implements A , B {
    public double fun1() {
//实现方法
    }
    public double fun2() {
//实现方法
    }
}
/*实现类要实现所有接口的所有方法
否则要声明为抽象类*/
多接口编程

◼ 将Shape接口用多接口实现

image-20211125232932603

Rectangle类实现多接口

class Rectangle implements Shape1, Shape2 {			//实现多接口
    private double height;
    private double width;
    public Rectangle(double height,double width) {
        this.height = height;
        this.width = width;
    }
    @Override
    public double getArea() {		//实现shape1接口的所有方法
        return height * width;
    }
    @Override
    public double getPerimeter() {		//实现shape2接口的所有方法
        return 2 * (height + width);
    }
}

Circle类实现多接口

class Circle implements Shape1, Shape2 {
    private double radius;
    public Circle(double radius) {
        this.radius = radius;
    }
    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
    @Override
    public double getPerimeter() {
        return 2 * Math.PI * radius;
    }
}

5. 接口继承多接口

Java类只支持单继承,不支持多继承 但接口可以继承多个其他接口

public interface A {
    void fa();
}
public interface B {
    void fb();
}
public interface C extends A, B {
    void fc();
}
public class E implements C {
…//必须实现接口(包括父接口)所有的方法,如fa()、fb()、fc()
}

6. 接口的特点总结

◼ 接口中的方法默认都是 public abstract 类型(可省略),没有方法体

◼ 接口不能有构造函数,不能被实例化 (注:抽象类可以有构造函数)

◼ 接口中不能有变量,常量默认是 public static final,必须被显示初始化

◼ 接口中不能包含静态抽象方法,可以有default 或者 static 方法 (Java 8以后)

示例

public interface A {
    int var=1; // OK,实际是public static final常量
    public A() { } // 报错,接口不能有构造函数
    void method1() { } // 报错,接口抽象方法没有方法体
    protected void method2(); // 报错,接口方法必须是public abstract
    static void method3(); // 报错,接口不能包含静态抽象方法
    static void method4() { } // OK,接口可以包含静态方法,此方法可以有方法体
    default void method5() { } // OK,接口可以包含default方法
}

◼ 当一个类实现某个接口时,它必须重写这个接口的所有抽象方法(包括这个接口的父接口中的方法),否则这个类必须声明为抽象的

◼ 一个类可以实现多个接口

class C implements A , B { … }

◼ 一个接口可继承多个其它的接口 ★

interface C extends A, B { … }

◼ 一个接口不能实现(implements)另一个接口:

interface a implements b{ … } //错,接口不能实现接口

◼ 一个类在继承一个父类的同时,可实现一个或多个接口,但extends关键字必 须位于implements关键字之前:

public class A extends B implements C, D {...}

◼ 不允许创建接口的实例(实例化),但允许定义接口类型的引用变量,该引用变 量引用实现了这个接口的类的实例(多态)

抽象类和接口对比

image-20211125234346441

 

匿名类

◼ 匿名类:是指没有类名的内部类,必须在创建时使用 new 语句来声 明类(编译成功后还是会安排一个特殊名字)

◼ 语法形式如下:

new 类或接口() {		//可以是普通类、抽象类或接口
	// 类的主体 通常是重写父类方法或实现接口方法
};

image-20211125234920983

匿名类特点

◼ 匿名类总是一个内部类,并且不能是static的

◼ 匿名类不能是抽象的,必须要实现继承的类或者实现的接口的所有抽象方法

◼ 匿名类总是隐式的final

◼ 匿名内部类中是不能定义构造函数的

◼ 匿名内部类中不能存在静态成员变量和静态方法(静态常量可以有 static final)

◼ 匿名类的class文件命名是:主类+$+1,2,3....

◼ 普通内部类的class文件命名是:主类+$+内部类名

 

Lambda表达式

◼ Lambda 表达式是一个匿名函数(也称为箭头函数)

◼ Lambda语法格式:

(类型 参数) ‐> { 代码语句; }

(int a, int b) -> {
int c = a + b;
return c;
}

//进一步省略
(a, b) -> a + b

Lambda表达式示例

() -> 5 // 无参数,返回值为 5 return都被省略
(int x, int y) -> x + y // 2个int参数,返回和
(x, y) -> x – y // 2个参数(数字),返回差值
x -> 2 * x // 接收一个参数(数字),返回其2倍 x仅仅是一个形参名
(String s) -> System.out.print(s) // String参数,只输出,无返回值

 

函数式接口

◼ 函数式接口:有且只有一个抽象方法的接口

◼ @FunctionalInterface注解:当接口中声明的抽象方法多于或少于一 个时就会报错

@FunctionalInterface
interface Person{
    void speak;
    void eat;
}//报错 多于一个

函数式接口的匿名类实现

@FunctionalInterface
interface Person {
void speak();
}
public class Test {
    public static void main(String[] args) {
        Person p = new Person() {
            @Override
            public void speak() {
                System.out.println("boy");
            }
        };
        p.speak();
    }
}

函数式接口的Lambda表达式实现

public class Test {
    public static void main(String[] args) {
        Person p = ()->System.out.println("boy") ;
        p.speak();
    }
}

 

### 抽象类接口的区别与相同点 #### 相同点 1. 抽象类接口都不能直接实例化对象,只能作为其他类的基类或实现类使用[^1]。 2. 抽象类接口都可以包含抽象方法,这些方法需要被子类实现类覆写后才能使用[^4]。 3. 两者都位于继承结构的顶端,用于定义通用的行为或规范[^4]。 #### 区别 1. **继承关系** - 抽象类通过 `extends` 关键字进行继承,一个类只能继承一个抽象类。 - 接口通过 `implements` 关键字实现,一个类可以实现多个接口,这弥补了 Java 的单继承限制[^1]。 2. **实现细节** - 抽象类可以包含普通方法(即带有具体实现的方法)以及抽象方法子类可以选择性地覆写这些方法。 - 接口在 JDK 8 之前只能包含抽象方法声明,在 JDK 8 及之后可以通过 `default` `static` 关键字提供默认实现或静态方法[^5]。 3. **成员变量** - 抽象类中可以有成员变量,并且其修饰符可以是任意类型(如 `private`、`protected` 等)。 - 接口中所有的变量默认是 `public static final` 类型,不能有普通的成员变量[^3]。 4. **多态性** - 由于 Java 的单继承限制,一个类只能继承一个抽象类,因此在设计灵活性上受到一定限制。 - 接口允许多个实现,一个类可以同时实现多个接口,从而提供更高的灵活性[^1]。 5. **设计目的** - 抽象类主要用于定义一种通用的类,其子类可以基于这个通用类进行扩展。 - 接口更多地用于定义一组规范,实现多种功能的类可以实现同一个接口。 6. **抽象类的特殊性** - 即使一个抽象类不包含任何抽象方法,也可以被定义为抽象类,目的是防止该类被直接实例化[^2]。 #### 示例代码 以下是一个简单的代码示例,展示抽象类接口的使用: ```java // 抽象类示例 abstract class Animal { // 普通方法 public void breathe() { System.out.println("Breathing..."); } // 抽象方法 public abstract void makeSound(); } // 接口示例 interface Swimmable { default void swim() { System.out.println("Swimming..."); } } // 子类实现 class Dog extends Animal implements Swimmable { @Override public void makeSound() { System.out.println("Barking..."); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值