抽象类、接口、枚举

1.抽象类

  由于继承这个显著特点,我们可以将子类设计的更加具体,而父类更加一般化,通用化。父类可以封装不同子类的共同特征或者共同行为.而有的时候,父类中封装的方法无法具体完成子类中需要的逻辑,因此我们可以将此方法设计成抽象方法,即使用关键字abstract进行修饰。而有抽象方法的类,也必须使用abstract关键字进行修饰,因此我们称之为抽象类。

  1.1抽象方法的特点

(1)有abstract修饰的方法为抽象方法,抽象方法没有方法体,不需要{},只需要使用;结尾

(2)若类中包含至少一个抽象方法,那么该类必须使用abstract关键字声明为抽象类,因为抽象类是用来继承的,所以final关键字不能修饰抽象类。

(3)在一个抽象类里可以没有抽象方法。

(4)抽象类不能实例化,所以不能使用new关键字调用构造器,所以即使可以提供构造器,但是也没有任何意义。但是子类可以在自己的构造器内用super();来调用父类的构造器。

(5)如果一个类继承了抽象类,那么必须重写里面所有的抽象方法,否则必须声明子类也为抽象类。

1.2抽象类的意义

  (1)抽象类为子类提供一个公共的父类。

  (2)封装子类中重复的内容,比如成员变量和方法。这样可以被所有子类继承和调用,减少代码的重复性,提高了代码的可维护性。

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

 案例代码:

public abstract class Animal {
    private String name;
    private int age;
    private String color;

    public Animal(){}
    public Animal(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }


    //抽象的方法,由子类实现
    public abstract void noise();

}
class Dog extends Animal {
//    private String name;
//    private int age;
//    private String color;
    public Dog(){}
    public Dog(String name, int age, String color) {
        super(name, age, color);
    }

    public void noise(){
        System.out.println("Woof!");
    }
    public void lookHouse(){
        System.out.println("Looking house!");
    }
}
class Cat extends Animal{
//    private String name;
//    private int age;
//    private String color;
    public Cat(){}
    public Cat(String name, int age, String color) {
        super(name, age, color);
    }
    public void noise(){
        System.out.println("Meow!");
    }
    public void getMouse(){
        System.out.println("Get a mouse!");
    }

}

//如果不想使用抽象方法类里的抽象呢方法,该类需要使用abstrct修饰。
abstract class Duck extends Animal{
    public void swim(){

    }

}
public class AnimalTest {
    public static void main(String[] args) {
        //直接定义一个cat类型,调用cat里的功能
        Cat cat = new Cat();
        cat.noise();

        //使用多态的向上造型
        Animal a = new Dog();
        a.noise();

        //下面代码编译错误,因为抽象类不能使用new关键字实例化
//        Animal b = new Animal();



    }
}

2.接口

  有的时候,我们需要从几个不相关的类中派生出一个子类,继承他们的所有成员变量和方法,但是java不支持多继承。此时,我们可以使用接口,来达到多继承的效果

2.1接口特点

(1)使用interface关键字定义接口

(2)接口里面可以提供成员属性,默认使用public static final修饰,并且只能是常量

(3)接口里面不能有构造器,因为接口没办法使用new实例化,提供构造器没有任何意义

(4)接口里面可以提供成员方法,默认使用public abstract修饰,不能有方法体

(5)一个类想要实现接口,需要使用implements关键字,必须实现接口中的所有抽象方法。

(6)一个类实现接口时,需要实现里面所有的抽象方法,否则需要使用abstract关键字修饰class。

(7)一个类可以实现多个接口,接口之间使用逗号分开。

(8)接口可以继承多个接口,使用extends关键字

(9)子接口拥有父接口的所有抽象方法,也可以提供自己独有的抽象方法。

2.2JDK1.8以后的新特性

1.提供了默认方法:使用default关键字修饰的具有方法体的方法
     -该方法默认使用public修饰,可以省略public关键字
     -该方法逻辑不能满足子类时,子类可以重写该方法
2.提供了静态方法:
     -使用static关键字修饰的具有方法体的方法
     -该方案默认使用public修饰,可以省略public关键字

2.3常用接口

1)Serializable序列化接口

系统类库提供好的一个接口。当涉及到数据传输时,比如将内存的对象保存到磁盘,磁盘上的文件变成内存中的对象,或者对象在两台电脑之间传输。那么该对象的类必须实现序列化接口。否则传输失败。该接口就是一个规范,里面没有任何东西,源码如下:

public interface Serializable {
   
}

比如Person类的对象想要进行传输:

public class Person implements Serializable {
   //.....person的代码
}

2)Comparable接口

汉语翻译成: 可比的,可比较的,是一个形容词。 当一个类的多个对象之间想要进行比较时,比如排序等操作,那么类必须实现该接口,然后自定义比较规则。否则不能比较,会报如下错误:

Exception in thread "main" java.lang.ClassCastException: xxx.类型名 cannot be cast to java.lang.Comparable

源码如下:

public interface Comparable<T> {
   public int compareTo(T o);
}

重写接口里提供好的compareTo方法。 

升序: 就使用this的相关属性-传入的对象o的相关属性

降序:传入的对象o的相关属性-this的相关属性

3)Comparator接口

用于在compareble的基础上去修改比较规则。

3.枚举

枚举是一种特殊的引用数据类型,是一个被命名的整型常数的集合,用于声明一组带标识符的常数,枚举在日常生活中很常见,例如表示星期的SUNDAY、MONDAY、TUESDAY、 WEDNESDAY、THURSDAY、FRIDAY、SATURDAY就是一个枚举。枚举的主要用途是将一组常量,也可以说成是一组离散值组织起来。

3.1自定义实现

  (1)提供一些可以向外界暴露的该类型的对象,注意枚举对象的名称都会大写。

  (2)构造器需要私有化

  (3)可以提供属性,用来描述对象的信息,需要使用private final修饰

 (4)给属性提供get方法向外界暴露,但是不需要提供set方法,因为只读。

public class Season{
   //1.声明Season对象的属性:private final修饰
   private final String seasonName;
   private final String seasonDesc;

   //2.私有化类的构造器,并给对象属性赋值
   private Season(String seasonName,String seasonDesc){
      this.seasonName = seasonName;
      this.seasonDesc = seasonDesc;
   }

   //3.提供当前枚举类的多个对象:public static final的
   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("冬天","冰天雪地");

3.2enum关键字实现

1)使用enum定义一个枚举,默认继承java.lang.Enum类,这是一个final类,所以不能再继承其他类。

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

3)可以提供属性但是必须私有化

4)可以提供构造器,必须是私有的,如果构造器有形参,定义对象时必须显式调用构造器,如果使用无参构造器创建对象时小括号可以省略。

public enum Season {
    SPRING("春天","春暖花开"),
    SUMMER("夏天","夏日炎炎"),
    AUTUMN("秋天","秋高气爽"),
    WINTER("冬天","冰天雪地");

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

    public static void main(String[] args) {
        Season seasons = Season.AUTUMN;
        System.out.println(seasons.name);
    }
}

还可以重写方法形式

public enum Direction implements InterA {
    FRONT("前"){
        public void showInfo() {
            System.out.println("向前走,如箭离弦,永不回头");
        }
    },
    BACK("后"){
        @Override
        public void showInfo() {
            System.out.println("向后走");
        }
    },
    LEFT("左"){
        public void showInfo() {
            System.out.println("向左走");
        }
    },
    RIGHT("右"){
        public void showInfo() {
            System.out.println("向右走");
        }
    };
    private String name;
    private Direction(String name){
        this.name = name;

    }


    @Override
    public void showInfo() {
        System.out.println("方向:");
    }

3.3enum常用方法

/*
    演示Enum类的各种方法的使用
*/
public class EnumMethod {
    public static void main(String[] args) {
        //使用Season2枚举类,来演示各种方法
        Season2 autumn = Season2.AUTUMN;
        //1.输出枚举对象的名字
        System.out.println(autumn.name());
        //2.ordinal()输出的是该枚举对象的次序(编号),从0开始编号
        //AUTUMN枚举对象是第三个,因此输出2
        System.out.println(autumn.ordinal());
        //3.从反编译可以看出values方法,返回的是Season2[],该数组含有定义的所有枚举对象
        Season2[] values = Season2.values();
        //遍历取出枚举对象
        for(Season2 season: values){ //增强for循环
            System.out.println(season);
        }
        //4.valueOf:将字符串转换成枚举对象,要求字符串必须为已有的常量名,否则报异常
        //执行流程
        //1.根据输入的"AUTUMN"到Season2的枚举对象去查找
        //2.如果找到了就返回,如果没有找到就报错
        Season2 autumn1 = Season2.valueOf("AUTUMN");
        System.out.println("autumn1" + autumn1);
        System.out.println(autumn == autumn1);
        //5.compareTo:比较两个枚举常量,比较的就是编号
        //解读
        //1.就是把Season2.AUTUMN枚举对象的编号 和 Season2.SUMMER枚举对象的编号进行比较
        //2.就是Season2.AUTUMN的编号 - Season2.SUMMER的编号
        /*
             public final int compareTo(E o) {
                return self.ordinal - other.ordinal;
             }
        */
        System.out.println(Season2.AUTUMN.compareTo(Season2.SUMMER));
    }
}

4.内部类

1)成员内部类

定义在一个类的内部,与这个类的成员(属性、方法)平级,并且没有用static修饰的类。
1、访问权限可以是任意的权限,类似于一个类中的成员。
2、实例化的过程,需要先实例化外部类对象,再使用外部类对象进行内部类的实例化
3、内部类编译后,也会生成.class字节码文件。格式:外部类$内部类 .class

public class Program {
    public static void main(String[] args) {
        // 1. 实例化一个外部类的对象
        Outer outer = new Outer();
        outer.name = "outer";
        // 2. 通过外部类的对象,实例化内部类对象
        Outer.Inner inner = outer.new Inner();
        inner.name = "inner";
        inner.show("hello");
    }
}

/**
 * 外部类
 */
class Outer {
    public String name;
    public int age1;
    // 这个类,由于是书写在一个类的内部,因此这个类被称为--内部类
    // 这个类,由于是写在Outer类中,和类中的属性、方法平级,可以称为是一个类的成员。
    // 并且,这个类没有使用 static 修饰,这样的类,被称为 -- 成员内部类
    class Inner {
        Inner() {
            System.out.println("实例化了一个Inner的对象");
        }
        public String name;
        public int age2;

        public void show(int age3) {
            System.out.println("参数age3: " + age3);
            System.out.println("内部类属性age2: " + age2);
            System.out.println("外部类属性age1: " + age1);
        }

        public void show(String name) {
            System.out.println("参数name: " + name);
            System.out.println("内部类属性name: " + this.name);
            System.out.println("外部类属性name: " + Outer.this.name);
        }
    }
}

2)静态内部类

  定义在一个类的内部,与这个类的成员(属性、方法)平级,并且使用static修饰的类。
1、访问权限可以是任意的权限,类似于一个类中的成员。
2、实例化的过程中,直接使用 new实例化一个外部类 .内部类对象即可。
3、内部类编译后,也会生成.class字节码文件。格式:外部类$内部类 .class

public class Program {
    public static void main(String[] args) {
        // 1. 实例化静态内部类对象的时候,不需要借助外部类对象的。
        // Outer.Inner inner = new Outer.Inner();
        // 2. 如果已经事先导包了,还可以直接进行实例化
        Inner inner = new Inner();

    }
}

class Outer {
    public String name;
    // 因为这个类,书写与Outer类内,并且是用static修饰的类
    // 这样的类,被称为 -- 静态内部类
    static class Inner {
        Inner() {
            System.out.println("实例化了一个Inner对象");
        }

        public String name;

        public void show(String name) {
            System.out.println("参数name: " + name);
            System.out.println("内部类属性name: " + this.name);
            System.out.println("外部类属性name,此时无法访问");
        }
    }
}

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、匿名内部类

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

// 实例化了一个匿名子类对象,并向上转型为父类类型
Person xiaoming = new Person() {
   // 这里,其实就是一个内部类的类体
   // 这个类,因为没有名字,因此是一个匿名内部类 // 这个类,是继承自 Person类的
   // 因此,这个类是Person类的匿名子类
   @Override
   public void work() {
      System.out.println("搬砖 "); }
};

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值