1、抽象类与抽象方法(abstract关键字)

1.1、在Java中,抽象类是一种不能实例化的类,它被用作其他类的基类。抽象类通过关键字abstract进行声明。抽象类可以包含抽象方法和非抽象方法。

抽象类: 被abstract修饰的类

public abstract class Animal{}

抽象方法:被abstract修饰的方法:在父类的方法中,没有具体的函数实现,只给出了声明,其中包含抽象方法的必须是一个抽象类

public abstract void eat();

如下代码所示:

Animal类(抽象类)

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

    //这里定义了抽象方法,类就必须写成抽象类
    public abstract void eat();


    public Animal() {
    }

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

    /**
     * 获取
     *
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     *
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     *
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     *
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Animal{name = " + name + ", age = " + age + "}";
    }
}

Dog类:

public class Dog extends Animal{

    private double high;

    public Dog() {
        super();
    }

    public Dog(String name,int age,double high) {
        super(name,age);
        this.high = high;
    }


    @Override
    public void eat() {
        System.out.println("狗吃骨头!");
    }

    /**
     * 获取
     * @return high
     */
    public double getHigh() {
        return high;
    }

    /**
     * 设置
     * @param high
     */
    public void setHigh(double high) {
        this.high = high;
    }

    public String toString() {
        return "Dog{name = " + super.getName() + "age = " + super.getAge() + "high = " + this.getHigh() + " }" ;
    }
}

Cat类:

public class Cat extends Animal {
    private double high;

    public Cat() {
        super();
    }

    public Cat(String name,int age,double high) {
        super(name,age);
        this.high = high;
    }


    @Override
    public void eat() {
        System.out.println("猫吃老鼠!");
    }

    /**
     * 获取
     * @return high
     */
    public double getHigh() {
        return high;
    }

    /**
     * 设置
     * @param high
     */
    public void setHigh(double high) {
        this.high = high;
    }

    public String toString() {
        return "Cat{name = " + super.getName() + "age = " + super.getAge() + "high = " + this.getHigh() + " }" ;
    }
}

测试类:

public class MyTest {
    public static void main(String[] args) {
        Animal an1 = new Cat();
        Animal an2 = new Dog();

        //调用各自的方法
        an1.eat();
        an2.eat();
    }
}

输出结果:

猫吃老鼠! 狗吃骨头!

结论:子类必须重写抽象类中所有的抽象方法,不然得话子类要是一个抽象类。对于非抽象方法,可以在子类或者通过子类创建的实例对象中调用。抽象类中可以没有抽象方法,但是有抽象方法一定要是抽象类!!!

==注意:不能用abstract修饰私有方法,静态方法,final的方法,final的类!!!==

2、接口(interface)

2.1、接口就是规范,定义的时一组规则,体现了现实社会中“如果你是/要 ….则必须能….”的思想,继承是一个”是不是“的is-a关系,二接口实现规则是”能不能“的has-a关系。只要实现了接口的规范,就会有接口里面提供的方法。

2.2、接口里面可以声明的结构:

①、属性:必须使用public static final修饰。

②、方法:在jdk8之前,声明抽象方法,修饰为public abstract jdk8:声明静态方法,默认方法 jdk9:声明私有方法。

③、不可以声明:构造器、代码块等等。

④、接口不可以实例出具体的对象,但是可以创建接口类型的数组。但是在这个数组中要存放具体实现类的对象。

2.3、接口的简单使用如下所示:

定义飞行的接口:

public interface Fly {
    public static final int MIN_SPEED = 0;
    //public static final可以省略不写
    int MAX_SPEED = 0;

    //抽象方法
    public abstract void fly();
    //public abstract也可以省略不写
    void attack();

}

类实现上面的接口:

//如果是实现了多个接口,就打个逗号写在后面
public class Plane implements Fly{

    @Override
    public void fly() {
        System.out.println("飞机在飞!");
    }

    @Override
    public void attack() {
        System.out.println("飞机在攻击目标!");
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        Plane plane = new Plane();
        plane.attack();
        plane.fly();
    }
}

输出结果:

飞机在攻击目标! 飞机在飞!

2.4、如果是又有继承又有接口的情况:

格式: 访问权限 class 类名 extends 父类 implements B,C...{} 

从上面可以看出,接口在一定程度上弥补了类单继承的局限性。类必须实现接口中的所有抽象方法!!!

2.5、接口与接口之间的关系:继承,并且可以多继承

public interface 接口名 extends 接口1,接口2{ }

2.6、接口也具有多态性,如下所示:

public class Test {
    public static void main(String[] args) {
        //测试接口的多态
        Fly fly = new Bird();
        fly.fly();
        fly.attack();

        fly = new Plane();
        fly.fly();
        fly.attack();
    }
}

输出结果:

鸟在飞行! 鸟在捕食! 飞机在飞! 飞机在攻击目标!

接口的应用场景,例如电脑的USB接口,代码如下所示:

定义USB的接口

public interface USB {
    void work();
    void stop();
}

打印机实现这些接口:

//打印机实现USB的接口
public class Printer implements USB {

    @Override
    public void work() {
        System.out.println("打印机开始工作了!");
    }

    @Override
    public void stop() {
        System.out.println("打印机停止工作了!");
    }
}

电脑已经对进口进行了功能的封装:

public class Computer {
    public void ataack(USB usb){
        System.out.println("设备连接成功!");
        usb.work();
        usb.stop();
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.ataack(new Printer());
    }
}

输出结果:

设备连接成功! 打印机开始工作了! 打印机停止工作了!

还有一种创建对象的方式,如下所示:

public class Test {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.ataack(new Printer());

        //还可以通过下面这种方式创建临时对象
        USB usb = new USB() {
            @Override
            public void work() {
                //具体的重写内容
                System.out.println("111开始工作了!");
            }

            @Override
            public void stop() {
                //具体的重写内容
                System.out.println("111停止工作了!");
            }
        };
        computer.ataack(usb);

    }
}

输出结果:

设备连接成功! 打印机开始工作了! 打印机停止工作了! 设备连接成功! 111开始工作了! 111停止工作了!

简写成:

        computer.ataack(new USB(){
            @Override
            public void work(){
                
            }
            @Override
            public void stop(){
                
            }
        });
3、接口新特性:jdk8:声明静态方法,默认方法 jdk9:声明私有方法

如下所示:

//接口
public interface Compare1 {
    //定义静态方法
    public static void show1(){
        System.out.println("这是接口中的静态方法!");
    }

    //默认方法
    public default void show2(){
        System.out.println("我是默认方法2!");
    }

    public default void show3(){
        System.out.println("我是默认方法3!");
    }

}

//实现类
public class Pare1 implements Compare1 {
    @Override
    public void show3() {
        System.out.println("实现类重写了默认方法3!");
    }
}

//测试类
public class Test {
    public static void main(String[] args) {
        //通过接口直接调用静态方法,接口中的静态方法只能被接口调用,不能用实现类调用!!!继承可以
        Compare1.show1();
        //创建对象
        Pare1 pare1 = new Pare1();
        //调用默认方法
        pare1.show2();
        pare1.show3();
    }
}


输出结果:

这是接口中的静态方法! 我是默认方法2! 实现类重写了默认方法3!

3.1、接口异常:类实现了两个接口,并且接口中有两个完全一样的方法,并且在实现类中没有对该方法进行重写。要重写之后才不会报错,虽然他们俩是完全一样的函数,但是只要重写了就相当于重写了他们的任何一个,从而在进行调用的时候才可以进行正确的调用。

3.2、类优先原则:子类继承父类并且实现了接口,父类中有和接口同名同形式的方法,其中接口中的方法为默认方法,那么通过子类调用这个同名方法的时候优先调用的是父类的方法。如果进行重写,那么调用的就是重写后的方法。如果要调用接口里面的方法,可以用以下方式进行调用:

接口名.super.方法名();

4、内部类

4.1、定义:定义在一个类中的类称为内部类。

分类:

成员内部类 (又分为静态内部类和非静态内部类)

局部内部类(同理)

4.2、如何创建成员内部类的实例?

4.3、如何在成员内部类中调用外部类的结构?

代码如下所示:

Person.java

public class Person {
    private int age;
    private String name = "Tom";

    public Person() {
    }

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

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    public String toString() {
        return "Person{age = " + age + ", name = " + name + "}";
    }

    //非静态成员内部类
    public class Dog1{
        public void work(){
            System.out.println("Dog1在看家护院!");
        }
        //内部类调用外部类的结构
        public void show(){
            System.out.println("姓名为;" + Person.this.name);      //调用外部结构的属性
            Person.this.print();                                   //调用外部结构的方法
        }

    }

    //静态成员内部类
    public static class Dog2{
        public void work(){
            System.out.println("Dog2在看家护院!");
        }
    }


    public void show(){
        System.out.println("姓名:" + this.name + "\t年龄:" + this.age);
    }

    public void print(){
        System.out.println("HHHHHHH");
    }

}

测试类:

public class Test {
    public static void main(String[] args) {
        //创建内部类的实例
        //非静态内部类
        Person person = new Person();
        Person.Dog1 dog1 = person.new Dog1();
        dog1.work();
        dog1.show();

        //静态内部类的实例
        Person.Dog2 dog2 = new Person.Dog2();
        //调用方法
        dog2.work();
    }
}

输出结果:

Dog1在看家护院! 姓名为;Tom HHHHHHH Dog2在看家护院!

4.4、局部内部类的基本使用。

如下所示:

public class MyComparable {

    //我们现在需要返回一个接口的实现类对象
    public Comparable returnCompared(){
        //创建局部内部类,实现comparable接口
        class InComparable implements Comparable{
            @Override
            public int compareTo(Object o) {
                return 0;
            }
        }
        //返回实现类的对象
        return new InComparable();
        
    }
}

也可以写成匿名对象:

public class MyComparable {

    //我们现在需要返回一个接口的实现类对象
    public Comparable returnCompared(){
        //返回实现类的匿名
        return new Comparable(){
            @Override
            public int compareTo(Object o) {
                return 0;
            }
        };
    }
}
5、枚举

5.1、枚举本质上也是一种类,只不过是这个类的对象是有限的,固定的几个,不能让用户随意创建。

5.2、开发中的建议:如果针对于某个类,其实实例是确定个数的,那么推荐将此类声明为枚举类。如果枚举类的实例只有一个,那么可以考虑使用单例设计模式。

简单的使用如下所示:

季节的枚举:

public enum Season {
	//本质上是public static final Season SPRING = new Season("春天", "春暖花开");
    SPRING("春天", "春暖花开"),
    SUMMER("夏天", "夏日炎炎"),
    AUTUMN("秋天", "秋高气爽"),
    WINTER("冬天", "寒风刺骨");

    private final String seasonName;      //季节名称
    private final String seasonDesc;      //季节的描述

    //限制实例对象的个数,那么就需要私有化构造器
    private Season(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }

    //让对象可以访问内部的属性
    public String getSeasonDesc() {
        return seasonDesc;
    }

    public String getSeasonName() {
        return seasonName;
    }

}

测试类:

public class Test {
    public static void main(String[] args) {
        System.out.println(Season.AUTUMN.getSeasonDesc());
        System.out.println(Season.SPRING.getSeasonDesc());
    }
}

输出结果:

秋高气爽 春暖花开

5.3、枚举类有默认的父类,所以不支持继承其它的父类。

5.4、枚举中常用的方法:

①、toString(使用的时候要进行重写!!)

②、name()(返回对象名)

③、values(获取所有的枚举类对象,并且返回一个数组)

④、valueof(通过传入的字符串在枚举中查找,然后返回查找结果,如果不存在,会报错!!)

⑤、ordinal(返回当前枚举常量的序号)

代码如下所示:

public enum Season {

    SPRING("春天", "春暖花开"),
    SUMMER("夏天", "夏日炎炎"),
    AUTUMN("秋天", "秋高气爽"),
    WINTER("冬天", "寒风刺骨");

    private final String seasonName;      //季节名称
    private final String seasonDesc;      //季节的描述

    //限制实例对象的个数,那么就需要私有化构造器
    private Season(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }

    //让对象可以访问内部的属性
    public String getSeasonDesc() {
        return seasonDesc;
    }

    public String getSeasonName() {
        return seasonName;
    }

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

//测试类
public class Test {
    public static void main(String[] args) {
        System.out.println(Season.AUTUMN.getSeasonDesc());
        System.out.println(Season.SPRING.getSeasonDesc());

        //toString方法
        System.out.println(Season.WINTER);
        //name方法
        System.out.println(Season.WINTER.name());
        //values方法
        Season[] seasons = Season.values();
        for (int i = 0; i < seasons.length; i++) {
            System.out.println(seasons[i]);
        }
        //valueof
        String season1 = "WINTER";
        System.out.println(Season.valueOf(season1));
        //ordinal
        System.out.println(Season.WINTER.ordinal());

    }
}

输出结果:

秋高气爽 春暖花开 Season{seasonName='冬天', seasonDesc='寒风刺骨'} WINTER Season{seasonName='春天', seasonDesc='春暖花开'} Season{seasonName='夏天', seasonDesc='夏日炎炎'} Season{seasonName='秋天', seasonDesc='秋高气爽'} Season{seasonName='冬天', seasonDesc='寒风刺骨'} Season{seasonName='冬天', seasonDesc='寒风刺骨'} 3

5.5、枚举类实现接口的操作

情况1:枚举类实现接口,在枚举类中重写接口中的抽象方法。当通过不同的枚举类对象调用此方法是,执行的是同一个方法。

情况2:枚举类实现接口,在枚举类中重写接口中的抽象方法。当通过不同的枚举类对象调用此方法是,执行的是不同的方法。这个时候直接将方法重写在不同的枚举类对象中。如下所示:

//枚举类
public enum Season implements Show {

    //下面这种重写方式,当枚举类对象调用接口中的方法时,调用的就是不同的方法
    SPRING("春天", "春暖花开") {
        @Override
        public void show2() {
            System.out.println("枚举类对象中实现接口中的方法!1");
        }
    },
    SUMMER("夏天", "夏日炎炎") {
        @Override
        public void show2() {
            System.out.println("枚举类对象中实现接口中的方法!2");
        }
    },
    AUTUMN("秋天", "秋高气爽") {
        @Override
        public void show2() {
            System.out.println("枚举类对象中实现接口中的方法!3");
        }
    },
    WINTER("冬天", "寒风刺骨") {
        @Override
        public void show2() {
            System.out.println("枚举类对象中实现接口中的方法!4");
        }
    };

    private final String seasonName;      //季节名称
    private final String seasonDesc;      //季节的描述

    //限制实例对象的个数,那么就需要私有化构造器
    private Season(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }

    //让对象可以访问内部的属性
    public String getSeasonDesc() {
        return seasonDesc;
    }

    public String getSeasonName() {
        return seasonName;
    }

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

    //这种实现方式,如果通过不同的枚举类对象调用此方法,那么调用的是同一个方法,即只会调用一次
    @Override
    public void show1() {
        System.out.println("枚举类中实现接口中的方法!");
    }

}

//测试类
public class Test {
    public static void main(String[] args) {
       Season[] seasons = Season.values();
        for (int i = 0; i < seasons.length; i++) {
            seasons[i].show1();
            seasons[i].show2();
        }
    }
}

输出结果:

枚举类中实现接口中的方法! 枚举类对象中实现接口中的方法!1 枚举类中实现接口中的方法! 枚举类对象中实现接口中的方法!2 枚举类中实现接口中的方法! 枚举类对象中实现接口中的方法!3 枚举类中实现接口中的方法! 枚举类对象中实现接口中的方法!4

通过枚举实现单例模式:

//用枚举实现单例模式
public enum Person {
    //当枚举中只有一个对象时,相当于一种单例模式
    PERSON("酋长",21);

    private final String name;
    private final int age;

    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String toString() {
        return "Person{name = " + name + ", age = " + age + "}";
    }
    
    public void show(){
        System.out.println("我是一个单例模式!");
    }
    
}

测试类:

public class Test {
    public static void main(String[] args) {
        System.out.println(Person.PERSON);
        Person.PERSON.show();
    }
}

输出结果:

Person{name = 酋长, age = 21} 我是一个单例模式!