抽象类、接口、枚举

一、抽象类

1.1 简介

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

1.2 抽象类的特点

1.由abstract修饰的方法为抽象方法,抽象方法没有方法体,需要使用使用分号结尾。

2.方法不提供{},那就必须使用abstract修饰。

3.有抽象方法的类,一定是抽象类,需要使用abstract来修饰。

4.如果子类继承了抽象父类,一定要实现抽象父类里的所有方法(重写),或者子类也声明为抽象类。

5. 抽象类就是用来被继承的,所有不能使用final修饰。

6. 抽象类里可以没有抽象方法。

7. 抽象类不能实例化,因此不能使用new关键字调用构造器,虽然可以提供构造器。

1.3 抽象类的意义

1)为其子类提供一个公共的父类型。

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

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

1.4 案例

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{
    public Dog() {
    }
    public Dog(String name, int age, String color) {
        super(name, age, color);
    }
    @Override
    public void noise(){
        System.out.println("~~汪汪汪~~");
    }
    public void lookHouse(){
        System.out.println("~~看家~~");
    }
}
class Cat extends Animal{
    public void getMouse(){
        System.out.println("~~抓老鼠~~");
    }
    public void noise(){
        System.out.println("~~喵喵喵~~");
    }
}
//如果不想实现抽象类里的抽象方法,那么该类需要使用abstract修饰
abstract class Duck extends Animal{
    public void swim(){
        System.out.println("~~游泳~~");
    }
}
public class AnimalTest {
    public static void main(String[] args) {
        //直接定义一个cat类型,调用cat里的功能。
        Cat cat = new Cat();
        cat.noise();
        cat.getMouse();
    //使用多态的向上造型
    Animal a = new Dog();
    a.noise();
    //下面代码编译错误,因为抽象类不能使用new关键字实例化。
    //Animal b = new Duck();
    }
}
~~喵喵喵~~
~~抓老鼠~~
~~汪汪汪~~

二、接口 

2.1 简介

java没有多继承的语法,而有些时候需要使用这种形式,比如一个子类想要拥有两个父类的属性或者方法时,可以使用另一个知识点来达到这种目的,就是接口。接口也可以理解为是特殊的抽象类,也可以理解为是一种规范。

2.2 接口的特点

1. 接口的关键字interface。(public interface 接口名)

2. 接口里可以提供成员变量,默认提供public static final进行修饰,即常量。

3. 接口里可以提供成员方法,默认使用public abstract进行修饰,即抽象方法。

4. 接口里不能提供构造器,更不能使用new实例化,没有意义。

5. 与继承不同,一个类可以实现多个接口,接口之间用逗号隔开。

6. 一个类实现接口,需要使用implements进行实现。

7. 一个类实现接口,必须实现接口中的所有抽象方法,否则该类需要使用abstract声明为抽象类。

public interface InterfaceA {
    double PI = 3.14;
    public static final double NUM = 0.618;
    void showInfo();
    int sum(int a, int b);
}
class ClassA implements InterfaceA {
    public void showInfo() {}
    public int sum(int a, int b) {
        return a + b;
    }
}
abstract class ClassB implements InterfaceA {
    public void showInfo() {}
}

2.3 接口间的继承

接口与接口之间的继承:

1.接口可以继承多个接口,使用extends关键字。

2. 子接口里面拥有父接口里面的所有抽象方法,也可以提供自己独有的抽象方法。

3. 类实现子类接口时,要重写里面的所有抽象方法,包括其实现的子接口所继承的父类接口里的抽象方法。

public interface InterfaceB {
    void methodB();
}
interface InterfaceC{
    void methodC();
}
interface InterfaceD extends InterfaceC,InterfaceB{
    void methodD();
}
class classT implements InterfaceD{

    //如果不重写下面所有的抽象方法,类就要用abstract修饰。
    //即abstract class classT implements InterfaceD{}
    @Override
    public void methodD() {

    }

    @Override
    public void methodB() {

    }

    @Override
    public void methodC() {

    }
}

2.4 接口在1.8之后的新特性

1)提供了默认方法default

1. 就是使用default修饰词修饰的具有方法体的方法。

2. 该方法默认使用public修饰。

3. 该方法,逻辑不能满足子类时,子类可以重写。

4. 该方法,可以在类中重写。

2)提供了静态方法static

1. 就是使用static修饰的具有方法体的方法。

2. 该方法默认使用public修饰。

3. 这个方法,在类中不能重写。

public interface InterfaceM {
    public default void print(){
        System.out.println("--欢迎来到中国,我的家--");
    }
    static void print2(){
        System.out.println("--地球只有一个,人人有责--");
    }
}
class classU implements InterfaceM{
    //重写接口里的默认方法
    @Override
    public void print(){
        System.out.println("--欢迎来到长春,我的家--");
    }
    //@Override   添加注解报错,因此print2方法不是重写,此时是自己独有的静态方法
    static void print2(){
        System.out.println("--地球只有一个,人人有责--");
    }
}

2.5 演示

public interface InterA {
    void showInfo();
}
interface InterB {
    void showInfo();
    int calculate(int a, int b);
}

class ClassA implements InterA,InterB{
    @Override
    public void showInfo() {
        System.out.println("--ClassA--");
    }

    @Override
    public int calculate(int a, int b) {
        return a+b;
    }
}
public class InterATest {
    public static void main(String[] args) {
        //因为ClassA是InterA和InterB的子类型,因此可以向上造型。

        InterA x = new ClassA();
        x.showInfo();

        //此时,想要使用x指向的对象,调用其里面的计算功能。
        //针对于这道题来说,可以向下转型成ClassA,也可以强制转换成InterB.
        //instanceof用于检验x是否属于ClassA类和InterB类

        //第一种方式
        if(x instanceof ClassA){
            ClassA c = (ClassA)x;
            int result = c.calculate(3,5);

            //第二种方式
            if(x instanceof InterB){
                InterB b = (InterB)x;
                b.calculate(3,5);
                System.out.println("result:"+result);
            }
        }
    }
}

 2.6 常用接口

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的相关属性

public class Person implements Comparable<Person>{
    private String name;
    private int age;
    private int height;
    private int weight;
public Person(String name, int age, int height, int weight) {
    this.name = name;
    this.age = age;
    this.height = height;
    this.weight = weight;
}
    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;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                ", weight=" + weight +
                '}';
    }

    /**
     * 如果想要进行比较,那么除了要实现comparable接口,还要实现里面的比较方法compareTo
     * @param o the object to be compared.
     * @return
     * 升序:this的相关属性-传入的0的相关属性
     * 降序:传入的o的相关属性-this的相关属性
     */
    @Override
    public int compareTo(Person o) {
        //按照年龄升序:  返回负数,证明this小,返回0,证明相等,返回正数,说明this大。
        //return this.age - o.age;
        //按照身高降序
        //return o.height - this.height;
        //先按照年龄升序,如果年龄相同,按照身高降序
        int r = this.age - o.age;
        if(r == 0){
           r = o.height - this.height;
        }
        return r;
    }
}

3)Comparator接口

比较器、比较仪的含义。用于在compareble的基础上去修改比较guize。

public class PersonTest {
    public static void main(String[] args) {
        Person[] ps = new Person[3];
        ps[0] = new Person("小明",18,176,70);
        ps[1] = new Person("小黑",18,175,65);
        ps[2] = new Person("小张",19,177,64);
        Arrays.sort(ps);
        System.out.println(Arrays.toString(ps));
        //现在想要修改比较规则,按照体重进行升序。不能修改源代码,因为Person类可能有人已经使用了,并不是自己一个人再用。
        //此时,可以使用comparator重新自定义比较规则。
        //使用匿名内部类创建一个比较器对象
         Comparator c1 = new Comparator<Person>() {
            @Override
            public int compare(Person p1, Person p2) {
                return p1.getWeight()-p2.getWeight();
            }
        };
         //数组工具类sort方法,重载了很多个方法,包含一个sort(Object[] a,Comparator c);
        Arrays.sort(ps, c1);
        System.out.println(Arrays.toString(ps));
    }
}

三、枚举

3.1 简介

在Java中,枚举是一种特殊的引用数据类型 ,是一个被命名的整型常数的集合,用于声明一组带标识符的常数,枚举在日常生活中很常见,例如表示星期的SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY、SATURDAY就是一个枚举。

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

3.2 枚举的定义

1)自定义类实现枚举

1.提供一堆可以像外界暴露的该类型的对象,通常使用public static final关键字共同修饰。

2.构造器私有化。

3.可以提供属性,用来描述对象信息,使用private final修饰。

4.给属性提供getXXX方法,向外界暴露,不需要提供setXXX方法,因为只读。

public class Season {
    //声明Season对象的属性:private final修饰
    private final String name;//对象的名字
    private final String desc;//对象的描述
    //构造器私有化。
    private Season(String name,String desc){
      this.name = name;
      this.desc = desc;
    }
    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("冬天","白雪皑皑");

    public String getName() {
        return name;
    }

    public String getDesc() {
        return desc;
    }

    @Override
    public String toString() {
        return name;
    }
    public static void main(String[] args) {
        Season s = Season.AUTUMN;
        //直接输出。打印的是对象的地址
        System.out.println(s);
        //打印这个季节的名字
        System.out.println(s.getName());
        //获取这个季节的描述信息
        System.out.println(s.getDesc());
    }
}
秋天
秋天
落叶归根

 2)enum关键字实现枚举

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

2. 第一行,必须是枚举类的对象,有多个枚举对象时,使用逗号隔开,最后一个用分号结尾, 名称自定义,应该符合常量的命名规则。

3. 内部系统提供了一个无参构造器,因为创建枚举对象时,调用的是无参构造器。  因此,对象后面的小括号可以省略。 

注意:构造器是私有的。

4. 可以提供私有的属性。

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

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

案例一:

public enum Week {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
    public static void main(String[] args) {
        System.out.println(Week.FRIDAY);
    }
}

案例二:

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 String getName(){
        return name;
    }
    public String getDesc(){
        return desc;
    }

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

案例三:


public enum Direction implements InterA {
     BEFORE("前"){
        public void showInfo(){
            System.out.println("向前进,如箭离弦,永不回头");
        }
    },AFTER("后") {
        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("--方向--");
    }

    public static void main(String[] args) {
    Direction direction = Direction.BEFORE;
        System.out.println(direction);
        direction.showInfo();
    }
}
interface InterA{
    void showInfo();
}

四、内部类

定义在一个类内部的类,就是内部类。内部类可以分为:成员内部类、静态内部类、局部内部类、匿名内部类。

4.1 成员内部类

1. 定义在一个类里面,与类的其他成员是平级关系。并且没有用static修饰的类。

2. 该内部类的访问权限可以是private,默认的,protected,public。

3. 需要先实例化外部类对象,再使用外部类对象进行内部类的实例化。

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

public class Outer {
    private String name;
    private int age;
    public Outer(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void showInfo(){
        System.out.println(name+","+age);
    }
    //定义一个成员内部类
    class Inner{
        private String name;
        private int age;
        public Inner(String name, int age) {
            this.name = name;
            this.age = age;
        }
        public void showInfo(){
            System.out.println(name+","+age);
            System.out.println(Outer.this.name+","+Outer.this.age);
        }
    }
    public static void main(String[] args) {
        //先创建一个外部类对象
        Outer outer = new Outer("妈妈",29);
        //然后通过外部类对象来实例化一个内部类对象
        Inner inner = outer.new Inner("儿子",1);
        inner.showInfo();
    }
}

4.2 静态内部类 

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

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

3. 实例化的过程中,直接使用 new实例化一个外部类 . 内部类对象即可。

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

public class Outer {
    private String name;
    public Outer(String name) {
        this.name = name;
    }
    public void showInfo() {
        System.out.println(name);
    }
    static class Inner {
        private String name;
        public Inner(String name) {
            this.name = name;
        }
        public void showInfo() {
            System.out.println(name);
            //不能直接访问外部类成员
            //System.out.println(Outer.this.name);
        }

        public static void main(String[] args) {
            //创建内部类的对象:new 外部类名.内部类构造器
            Inner i = new Outer.Inner("儿子");
            i.showInfo();
        }
    }
}

4.3 局部内部类

1. 在方法中定义的内部类,和局部变量的用法一样。出了作用域就失效了。

2. 没有访问权限修饰词。

3. 在当前方法中,直接实例化即可。

4. 内部类编译后,也会生成.class字节码文件。格式:外部类$序号内部类 .class

public class Outer {
    public static void main(String[] args) {
        int a = 10;
        System.out.println(a);

        class Inner {
            private String name;
            public Inner(String name) {
                this.name = name;
            }
            public String getName() {
                return name;
            }
        }
        Inner inner = new Inner("小明");
        System.out.println(inner.getName());
    }

4.4 匿名内部类

就是没有名字的子类型对象。

语法:

new 接口名|抽象类名|父类名(){ 

方法的重写 

};

在匿名内部类中,一般情况下不去添加新的成员(属性、方法),因为即便进行了添加,得到的对象也是向上转型后的对象,不能访问子类中的成员。在匿名内部类中,一般是用来做方法的重写实现的。

public class Outer {
    public static void main(String[] args) {
        A a =new A(){
            //匿名内部类提供成员属性
            private String name = "--你好,欢迎来到中国--";
            public void showInfo(){
                System.out.println(name);
            }
            //子类提供了独有的getXXX方法
            public String getName(){
                return name;
            }
        };
        a.showInfo();
    }
}
interface A{
    public void showInfo();
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值