JAVA的抽象类,接口,枚举,内部类

在Java中,抽象类、接口和枚举是三种常见的特殊类型。这三种特殊类型在Java中都有其特定的用途,抽象类用于定义具有共同特性的类的抽象概念,接口用于定义规范和实现类之间的解耦,而枚举则用于限制变量的取值范围。

一、 抽象类(Abstract Class)

1.简介

抽象类是不能被实例化的类,它用于定义一组相关的类的共同特性和行为。抽象类可以包含抽象方法(没有实现的方法)和具体方法(有实现的方法)。其他类可以通过继承抽象类来扩展其功能,并实现抽象方法。抽象类一般用于定义抽象的概念和通用的方法,而具体实现则需要由子类完成。

 2.抽象类的特点

1)抽象类使用abstract关键字进行修饰;

2)抽象类没有方法体,需要使用封号结尾;

3)若类中包含抽象方法,那么该类就必须使用关键字abstract声明成抽象类,final不能修饰抽象类;

4)抽象类里可以没有抽象方法;

5)一个类继承了抽象类,那么必须重写里面的所有抽象方法,除非该子类也声明为抽象类;

6)抽象类里可以提供构造器,但是不能实例化,没有意义;

7)继承了抽象类的子类,可以用构造器创建实例化对象。

3.抽象类的意义

1)为子类提供一个公共的父类;

2)封装子类中的重复内容,如成员变量和方法;

3)定义抽象方法,子类虽然有不同的实现逻辑,但是该方法的定义是相同的。

代码示例:

public class AbstractDemo01 {
    public static void main(String[] args) {
//        代码编译错误,因为抽象类不能使用new关键字实例化
//        Animal animal = new Animal();

        Animal cat = new Cat();//向上转型
        cat = new Cat("小花", 2);
        Cat cat1 = (Cat) cat;//直接定义Cat类型的变量,向下转型
        cat.eat();
        cat1.action();

        Animal dog = new Dog();
        dog.eat();

//        dog是Dog类型,但是dog是Animal类型变量,所以dog不能调用action方法
//        需使用向下转型((Dog)dog).action();
//        dog.action();
        ((Dog)dog).action();

        Dog dog1 = (Dog) dog;
        dog1.action();
        dog1.eat();

        Dog dog2 = new Dog("小黑", 2);
        System.out.println(dog2);
        dog2.action();
        dog2.eat();


    }
}

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 int getAge() {
        return age;
    }

    public abstract void eat();
    public abstract String toString();
}

class Dog extends Animal {
    public Dog() {
    }

    public Dog(String name, int age) {
        super(name, age);
    }

    public void action() {
        System.out.println(super.getName()+"叫~~~~汪汪汪");
    }

    @Override
    public void eat() {
        System.out.println(super.getName()+"吃骨头");
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + super.getName() + '\'' +
                ", age=" + super.getAge() +
                '}';
    }
}

class Cat extends Animal {
    public Cat() {
    }

    public Cat(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(super.getName()+"吃鱼");
    }
    public void action() {
        System.out.println(super.getName()+"叫~~~~喵喵喵");
    }

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + super.getName() + '\'' +
                ", age=" + super.getAge() +
                '}';
    }
}


二、接口(Interface)

1.简介

接口是一种完全抽象的类,它只包含方法的声明而没有方法的实现。接口可以看作是一组约定,定义了一组类应该实现的方法。其他类可以通过实现接口来达到多重继承的效果,一个类可以实现多个接口。接口主要用于定义规范和实现类之间的解耦,实现类必须实现接口中定义的所有方法。

2.接口的特点

  • 使用interface进行定义

  • 可以提供成员变量,默认提供public static final进行修饰

  • 可以提供成员方法,默认使用public abtract进行修饰

  • 接口中不能存在构造器,接口不能实例化,没有任何意义

抽象性:接口中的方法通常是抽象的,这意味着它们只有声明而没有具体的实现。实现接口的类必须提供这些方法的具体实现。
多态性:接口允许类实现多个接口,每个接口可以包含不同的抽象方法。这提供了高度的多态性,使类能够扮演多种角色。
封装性:接口提供了一种封装抽象行为的方式。通过定义接口,你可以隐藏具体实现细节,只暴露类需要遵守的协议。
解耦性:接口降低了类之间的耦合度。实现接口的类不需要关心其他类的内部实现,它们只需要关注接口中定义的行为。
可维护性:使用接口可以提高代码的可维护性。如果接口的定义发生改变,所有实现它的类都需要相应地更新。
扩展性:接口使得软件系统更容易扩展。你可以创建新的类来实现已有的接口,而不需要修改依赖于这些接口的其他类。
标准化:接口可以作为一种标准化的契约,确保不同组件之间的一致性和互操作性。
设计灵活性:接口支持设计模式,如策略模式、适配器模式等,使得类的设计更加灵活。

3.实现接口

1) 与继承不同,一个类可以实现多个接口。接口间使用逗号分开。

2) 使用关键字implements进行实现,必须实现接口中的所有抽象方法

3) 若一个类中没有全部实现接口中的抽象方法,那么该类需要使用abstract声明成抽象类

4.接口间的继承

1)接口之间可以存在继承关系,即一个接口通过关键字extends可以继承另一个接口。

2)子接口继承了父接口中的所有抽象方法

代码示例: 

public interface InplementsDemo {
    void eat();
    void sleep();
}
public interface action extends InplementsDemo {
    void action();

    @Override
    void eat();

    @Override
    void sleep();
}
public class PersonTest implements InplementsDemo{
    private String name;
    private int age;
    private String sex;

    private PersonTest(String name,int age,String sex){
        this.name = name ;
        this.age = age;
        this.sex = sex;
    }

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

    //必须实现接口中的所有方法
    @Override
    public void eat() {
        System.out.println(name+"吃东西");
    }

    @Override
    public void sleep() {
        System.out.println(name+"睡觉");
    }

    public void action() {
        System.out.println(name+"干活");
    }

    public static void main(String[] args) {
        PersonTest personTest = new PersonTest("张三",18,"男");
        System.out.println(personTest);
        personTest.eat();
        personTest.sleep();
        personTest.action();
    }
}

5.接口在jdk1.8之后的新特性

 在 jdk 1.8 之后,给接口添加了若干个新的特性。

1)default

给接口中的方法,添加默认的实现方式。此时,这个方法,可以在实现类中实现,也可以不在实现类 中实现。如果,实现类没有重写实现这个方法,以接口中的实现为准。

interface Calculate {
   int calculate(int a, int b);

   public default void testMethod(int a, int b) {
      System.out.println("calculate: " + (a + b));
   }
}

2)static

⽤static修饰的接口中的方法,表示是静态的方法。此时,这个方法必须要添加一个实现。这个方法,在实现类中不能重写实现。只能通过接口来调用。

interface Calculate {
   int calculate(int a, int b);

   public static void show() {
      System.out.println("Calculate Show");
   }
}

6.常用接口

1)序列化接口

2)Comparable接口

3)Comparator接口

7.可不可以认为接口是一种特殊的抽象类?

不可以简单地理解接口就是一个特殊的抽象类,尽管两者有一些相似之处。
接口和抽象类在Java中都是用于定义抽象行为的机制。然而,它们之间有几个关键的区别:
1.声明方式:接口使用interface关键字声明,而抽象类使用abstract class关键字。
2.继承限制:一个类可以实现多个接口,但只能继承一个抽象类(单继承)。这种多实现性使得接口在设计中提供了更大的灵活性,允许类具备多种角色。
3.方法实现:接口中所有的方法默认都是抽象的,没有方法体。这意味着实现接口的类必须提供所有接口方法的具体实现。相比之下,抽象类可以包含抽象方法和已实现的方法。
4.成员变量:接口中所有的成员变量默认都是public static final修饰的,这意味着它们是公开的、静态的且不可更改的。而抽象类可以包含各种类型的成员变量,包括非静态和非最终的。
5.设计哲学:接口更强调行为的定义,它代表了一种“我能做什么”的契约。而抽象类更侧重于提供一个基类,它代表了一种“我是什么”的类型。
6.用途:接口常用于定义模块之间的通信协议,使得各个模块之间可以通过接口进行通信。抽象类则更多地用于代码的复用和扩展性的设计。
7.组成:接口只能包含抽象方法和静态常量(static final)。抽象类可以包含实例变量、抽象方法和已实现的方法。
尽管接口与抽象类有这些显著的差异,但它们在Java的面向对象编程中都有其特定的用途。 

接口示例:

interface Flyable {
    void fly();  // 抽象方法
}

class Bird implements Flyable {
    @Override
    public void fly() {
        System.out.println("Bird is flying");
    }
}

 抽象类示例:

abstract class Vehicle {
    protected String brand;  // 实例变量

    public abstract void start();  // 抽象方法

    public void honk() {  // 已实现的方法
        System.out.println("Honking the horn");
    }
}

class Car extends Vehicle {
    @Override
    public void start() {
        System.out.println(brand + " car is starting");
    }

    public static void main(String[] args) {
        Car myCar = new Car();
        myCar.brand = "Toyota";
        myCar.start();
        myCar.honk();
    }
}

三、枚举(Enumeration)

1.简介

枚举是一种特殊的类,它限制一个变量只能取一组固定的值。枚举类型通过关键字"enum"来定义,其中每个枚举值都是枚举类型的一个实例。枚举常用于定义一组固定的常量,例如表示颜色、星期几等。枚举还支持方法的定义,可以为每个枚举值定义不同的行为。例如表示星期的SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY、SATURDAY就是一个枚举。

背景:枚举是在JDK1.5以后引入的。

主要用途是:将一组常量,也可以说成是一组离散值组织起来。

2.枚举的定义

 1)自定义类实现枚举

  • 类内部创建一组对象,通常使用public f static final关键字共同修饰,对外进行暴露

  • 枚举对象名通常全部都会大写,这是常量的命名规范

  • 可以提供属性,属性应使用private final共同修饰

  • 将构造器私有化

  • 属性,可以提供getXXX方法,但是不需要提供setXxx方法,属性是只读的。

  • 自定义类实现的枚举是不能使用enum里的方法的。 

public class Season {
    public static final Season SPRING = new Season("春天","春暖花开");
    public static final Season SUMMER = new Season("夏天","烈日炎炎");
    public static final Season AUTUMN = new Season("秋天","落叶归根");
    public static final Season WINTER = new Season("冬天","白雪皑皑");

    private final String name;
    private final String desc;

    public Season(String name, String desc){
        this.desc = desc;
        this.name = name;
    }


    public String getName() {
        return name;
    }

    public String getDesc() {
        return desc;
    }

    @Override
    public String toString() {
        return super.toString();
    }

2)enum关键字实现枚举

  • 使用enum关键字定义一个枚举,默认会继承java.lang.Enum类,而且是一个final类,因此不能再继承其他类

  • 必须在枚举类的第一行声明枚举类对象。有多个枚举对象时,使用逗号隔开,最后一个用分号结尾

  • 可以提供私有的属性

  • 可以提供构造器,必须是私有的,如果构造器有形参,定义对象时必须调用显式调用构造器

  • 如果使用无参构造器创建枚举对象,则定义对象时,小括号可以省略

​​​​
/**
 * enum 枚举类演示
 */
public enum Season {
    SPRING("春天","春暖花开"),
    SUMMER("夏天","烈日炎炎"),
    AUTUMN("秋天","落叶归根"),
    WINTER("冬天","白雪皑皑");

    private String name;
    private String desc;
    Season(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }

    public String getName() {
        return name;
    }

    public String getDesc() {
        return desc;
    }

    @Override
    public String toString() {
        return "Season{" +
                "name='" + name + '\'' +
                ", desc='" + desc + '\'' +
                '}';
    }

 重写方法形式
  • 1.使用enum关键字后,就不能再继承其它类,因为enum会隐式继承Enum,而Java是单继承机制

  • 2.枚举类和普通类一样,可以实现接口

public class EnumDemo01Test {
    public static void main(String[] args) {
        // 使用枚举的values()方法,可以获取到所有的枚举对象,返回的是一个数组
        // 自定义类实现的枚举
        EnumDemo01[] values = EnumDemo01.values();
        for( EnumDemo01 value : values){
            System.out.println(value.toString());
        }

        // 使用枚举的valueOf()方法,可以根据枚举对象的名称获取到枚举对象
        EnumDemo01 va = EnumDemo01.valueOf("LEFT");
        System.out.println(va);

        String name = va.name();
        System.out.println(name);

        EnumDemo01.DOWN.ShowInfo();
        EnumDemo01.RIGHT.ShowInfo();

    }
}
public enum EnumDemo01 {
    LEFT{
        @Override
        public void ShowInfo() {
            System.out.println("left");
        }
    }, RIGHT{
        @Override
        public void ShowInfo(){
        System.out.println("right");
        }
    }, UP{
        @Override
        public void ShowInfo(){
        System.out.println("up");
        }
    }, DOWN{
        @Override
        public void ShowInfo(){
        System.out.println("down");
        }
    };
    public abstract void ShowInfo();
}

 3.enum的常用方法

说明:使用关键字enum时,会隐式继承Enum类,这样我们就可以使用Enum类相关的方法

方法名详细描述
toString得到当前枚举常量的名称。子类可以通过重写这个方法来使结果更易读
name返回当前对象名(常量名),子类中不能重写
ordinal返回当前对象的位置号(编号),默认从0开始
values返回当前枚举类中所有的常量
valueOf将字符串转换成枚举对象,要求字符串必须为已有的常量名,否则报异常!
compareTo比较两个枚举常量,比较的就是位置号(编号)
hashCodeEnum实现了hashCode()来和equals()保持一致,它也是不可变的
getDeclaningClass得到枚举常量所属枚举类型的Class对象。可以用它来判断两个枚举常量是否属于同一个枚举类型
clone枚举类型不能被Clone。为了防止子类实现克隆方法Enum实现了一个仅抛出Clone Not Supported Exception异常的不变clone()

 代码示例:

/**
 * enum 枚举类演示
 */
public enum Season {
    SPRING("春天","春暖花开"),
    SUMMER("夏天","烈日炎炎"),
    AUTUMN("秋天","落叶归根"),
    WINTER("冬天","白雪皑皑");

    private String name;
    private String desc;
    Season(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }

    public String getName() {
        return name;
    }

    public String getDesc() {
        return desc;
    }

    @Override
    public String toString() {
        return "Season{" +
                "name='" + name + '\'' +
                ", desc='" + desc + '\'' +
                '}';
    }

    public static void main(String[] args) {
        Season season = Season.WINTER;
        System.out.println(season);
//        使用values()方法,可以获取一堆枚举对象。
        Season[] spring = season.values();
        for (Season s: spring) {
            System.out.println(s);
        }
//        使用name()返回枚举对象的名称。
        System.out.println(season.name());
//        使用ordinal()返回枚举对象的索引。默认从0开始
        System.out.println(season.ordinal());
//        使用valueOf()返回枚举对象,根据名称获取枚举对象。
        //2.如果找到了就返回,如果没有找到就报错
        Season autumn = Season.valueOf("AUTUMN");
        System.out.println("autumn"+autumn);
        Season autumn1 = Season.AUTUMN;
        System.out.println(autumn == autumn1);
//        使用compareTo()比较两个枚举常量,比较的就是编号
        //解读
        //1.就是把Season.SPRING枚举对象的编号 和 Season.WINTER枚举对象的编号进行比较
        //2.就是Season.SPRING的编号 - Season.WINTER的编号
        System.out.println(season.SPRING.compareTo(season.WINTER));
    }
}

四、内部类(inner class)

1)成员内部类

 定义在一个类的内部,与这个类的成员(属性、方法)平级,并且没有用static修饰的类;

1.访问权限可以是任意的权限,类似于一个类中的成员。

2.实例化的过程,需要先实例化外部类对象,在使用外部类对象进行内部类的实例化;

3.内部类编译后,也会生成,class文件。格式:外部类$内部类.class

public class InnerDemo01 {
    public static void main(String[] args) {
        outer outer = new outer();
        outer.show();
        //创建内部类对象
        outer.inner inner = outer.new inner();
        System.out.println("-------------------");
        inner.show();
    }
}

class outer{
    public String name = "outer";
    public int age = 20;

//    内部类inner
    //由于这个类是写在类的内部,所以他就叫内部类,
    // 并且该类没有static修饰符,被称为成员内部类
    //内部类可以访问外部类的成员变量和方法

    public class inner{
        inner(){
            System.out.println("实例化一个内部类对象inner");
        }
        public String name = "inner";
        public int age2 = 21;

        public void show(){
            System.out.println("outer age" + age);
            System.out.println("inner age" + age2);
        }
    }

    public void show(){
        System.out.println("name: " + name);
//        创建内部类对象
        inner inner = new inner();
        inner.show();
    }
}

2)静态内部类

 基本内容和成员静态类相似,静态内部类用static修饰。

public class StaticDemo01 {
    public static void main(String[] args) {
//        outer1 outer = new outer1();
//        outer.show();
        //创建内部类对象,
//        实例化静态内部类的对象的时候,不需要借助外部对象实例化
//        outer1.Inner inner = new outer1.inner();
//        不想上面那样写,就先导包,在用静态内部类实例化
//        不然会报错
        Inn inner = new Inn();
        System.out.println("-------------------");
        inner.show();
    }
}
class out {
    public String name = "outer";

    static class Inn {
        public String name = "inner";
        public int age = 21;

        public void show() {
            System.out.println("outer name" + name);
            System.out.println("inner name" + name);
        }
    }

    public void show() {
        System.out.println("name: " + name);
        //创建内部类对象
        Inn inner = new Inn();
        inner.show();
    }

}

3)局部内部类

 定义在某一个代码段中的中。
1、没有访问权限修饰符。
2、在当前方法中,直接实例化即可
3、内部类编译后,也会生成.class字节码文件。格式:外部类$序号内部类 .class

public class Program {
    public static void main(String[] args) {
        int a;
        // 写在某一个局部代码段中(例如:方法中)
        // 这个类,只能在当前的方法中使用
        class Inner {

        }
        Inner inner = new Inner();
        test();
    }

    static void test() {
        class Inner {

        }
    }
}

4)匿名内部类

没有名字的内部类,内部类,通常是需要配合其他的类或者接口一块使用的

在匿名内部类中,一般情况下不去添加新的成员(属性、方法),因为即便进行了添加,得到的对象也是向 上转型后的对象,不能访问子类中的成员。在匿名内部类中,一般是用来做方法的重写实现的。
匿名内部类也会生成 .class字节码文件,命名格式 : 外部类$序号 .class

  • 25
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值