java6—抽象类、接口、枚举

1.抽象类

1.1 简介

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

1.2 特点

 abstract方法
1.方法不提供花括号{},那就必须使用abstract修饰,必须以分号结尾。
2.有抽象方法的类,必须是抽象类。需要使用abstract修饰类
3.子类需要实现抽象父类里的所有抽象方法除非子类也声明为抽象类
4.抽象类就是用来被继承的,所以不能使用final修饰。(final修饰的类不能被继承)
5.抽象类里面可以没有抽象方法(就如Duck类)
6.抽象类不能实例化,因此不能使用new关键字调用构造器,虽然可以提供构造器。

 1.3 意义

  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 {

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


    public void noise(){
        System.out.println("汪汪汪汪");
    }

    public void lookHouse(){
        System.out.println("看家");
    }
}

class Cat extends Animal{
    public Cat() {}

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

    }
    public void noise(){
        System.out.println("喵喵喵喵");
    }

    public void getMouse(){
        System.out.println("抓老鼠。");
    }
}

//如果不想实现抽象类里的抽象方法,该类需要使用abstract修饰。
abstract class Duck extends Animal{
    public Duck(){};
    public Duck(String name, int age, String color) {
        super(name, age, color);
    }

    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();
//        a.lookHouse();//报错,父类没有该方法。

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

    }
}

2.接口 

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

 2.1 概念

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

2.2 类调用接口的特点

  • 接口的关键字:interface
  • 接口里可以提供成员属性,默认使用public static final 修饰的,即常量。
  • 接口里不能提供构造器,更不能使用new实例化,没有意义。
  • 接口里可以提供成员方法,默认使用public abstract修饰
  • 与类的继承不同,一个类可以实现多个接口。接口间使用逗号分开。
  • 一个类实现接口,需要使用关键字implements调用。
  • 一个类实现接口时,需要实现里面的所有抽象方法否则需要使用abstract修饰class。 

 代码测试:

接口代码:

public interface InterfaceA {
    double PI = 3.14159265358979323846;
    double NIM = 0.618;

//    public InterfaceA() {}//不允许构造

    void showInfo();
    public abstract int sum(int a, int b);
}

类调用接口:

class A implements InterfaceA {
    @Override
    public void showInfo() {}
    public int sum(int a, int b) {
        return a + b;
    }
}

abstract class B implements InterfaceA {
    @Override
    public void showInfo() {

    }
}

 2.3接口调用接口的特点

接口与接口之间:
    1.接口可以继承多个接口,使用extends,多继承使用逗号隔开。
    2.子接口拥有了父接口里的所有抽象方法。
    3.子接口可以提供自己独有的抽象方法
    4.类实现子接口时,要重写里面所有的抽象方法

代码测试:(C,D接口调用InterfeceB,ClassT继承接口D)

public interface InterfaceB {
    void methodB();
}

interface C{
    void methodC();
}
interface D extends C,InterfaceB{

    void methodD();
}

class ClassT implements D{

    @Override
    public void methodD() {

    }

    @Override
    public void methodC() {

    }

    @Override
    public void methodB() {

    }
}

 2.4 接口在1.8之后的新特性(default 、 static)

JDK1.8以后的接口新特性
1.提供了默认方法:使用default 修饰词修饰的具有方法体的方法。
    --1)该方法,默认使用public修饰。
    --2)该方法,逻辑不能满足子类时,子类可以重写。
2.提供了静态方法:使用static修饰的具有方法体的方法。
    --1)该方法,默认使用public修饰

    --2) 该方法不可重写。

测试代码: 

public interface InterfaceM {
     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 常用接口

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的相关属性
降序:  -(就使用this的相关属性-传入的对象o的相关属性)   拆开括号  :传入的对象o的相关属性-this的相关属性

测试代码(类):

package com.oop.day03.eInterface;

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) {}
    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 getWeight() {
        return weight;
    }

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

    public int getHeight() {
        return height;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                ", weight=" + weight +
                '}';
    }
/*
如果想要进行比较,那么除了要是实现comparable接口,还要实现里面的比较方法compareTo
升序:this的相关属性-传入的o的相关属性
降序:传入的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;
//        if(this.age == o.age){
//            return o.height - this.height;
//        }
//        return this.age-o.age;
//
    }
}

测试代码:(测试类)

package com.oop.day03.eInterface;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Comparator;

public class PersonTest {
    public static void main(String[] args) {
        Person[] ps =new Person[3];
        ps[0] = new Person("小明",19,166,70);
        ps[1] = new Person("小黑",18,176,65);
        ps[2] = new Person("小张",19,186,64);
        Arrays.sort(ps);
        System.out.println(Arrays.toString(ps));

        //现在想要修改比较规则,按照体重进行升序排序。不能修改源代码,因为Person类可能有人已经使用了,并不是自己一个人在用。
        //此时,就可以使用Comparator比较器进行重新自定义比较规则。
        //使用匿名内部类创建一个比较器对象,
        Comparator c1 = new Comparator<Person>(){
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getWeight()-o2.getWeight();
            }
        };
        //数组工具类sort方法,重载了很多个方法,包含一个sort(Object[] a,Comparator c);
        Arrays.sort(ps,c1);
        System.out.println(Arrays.toString(ps));
    }
}
3) Comparator接口

 汉语翻译成:比较器,比较仪。用于在compareble的基础上去修改比较规则。

 3.枚举

3.1 简介

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

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

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

3.2 枚举的定义 

1)自定义类实现枚举 的规则:
  • 类内部创建一组对象,通常使用public static final关键字共同修饰,对外进行暴露

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

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

  • 构造器私有化

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

测试代码:

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 desc;
    private final String name;
    private Season(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }

    public String getDesc() {
        return desc;
    }
    public String getName() {
        return name;
    }
    public String toString() {
        return name+","+desc;
    }

}
 2) enum关键字实现枚举

enum关键字实现枚举的规则

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

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

  • 可以提供私有的属性

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

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

/**
 * 案例1:简单定义一个枚举
 * 注意:
 * 1. 第一行,必须是枚举类的对象.  名称自定义,应该符合常量的命名规则。
 * 2. 内部系统提供了一个无参构造器,因为创建枚举对象时,调用的是无参构造器,因此
 *    对象后面的小括号是可以省略的。
 *    注意:构造器是私有的。
 */
public enum Week {
    //小括号可以省略
    MONDAY("周一"),TUESDAY(),WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
    private String a;
    private Week(){}//无参构造器也是私有的
    private Week(String a){
        this.a=a;
    }
    public void showInfo(){
        System.out.println(a);
    }

    public static void main(String[] args) {
        System.out.println(Week.MONDAY);
        Week.MONDAY.showInfo();
    }
}
  • 使用enum关键字后,就不能再继承其它类,因为enum会隐式继承Enum,而Java是单继承机制

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

/*
方向的枚举
 */
public enum Direction implements interA{
    BEFORE("前"){
        @Override
        public void showInfo() {
            System.out.println("向前进,如箭离弦,永不回头");
        }
    },
    AFTER("后"){
        public void showInfo() {
            System.out.println("向后退");
        }
    },
    LEFT("左"){
        public void showInfo() {
            System.out.println("left");
        }
    },
    RIGHT("right"){
        public void showInfo() {
            System.out.println("right");
        }
    };


    private String name;
    private Direction(String name){
        this.name = name;
    }


    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();
}

 3.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()

4.内部类 

4.1 成员内部类

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

/*
成员内部类:
1.定义在一个类的里面,与类的其他成员是平级关系。没有static修饰
2.该内部类的访问权限可以是private,默认的,protected,public

 */
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 int age;
        private String name;
        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);
        }
    }

    public static void main(String[] args) {
//        先创建一个外部类对象
        Outer outer = new Outer("妈妈",29);
        //然后通过外部类对象,来实例化一个内部类对象。
        Inner inner =outer.new Inner("儿子",0);
        inner.showInfo();
    }
}

4.2 静态内部类

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

/*

1、访问权限可以是任意的权限,类似于一个类中的成员。
2、实例化的过程中,直接使用 new实例化一个外部类.内部类对象即可
3、内部类编译后,也会生成.class字节码文件。格式:外部类$内部类.class

 */
public class Outer {
    private String name;
    public Outer() {

    }
    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("Inner showInfo");
            //不能直接访问外部类的成员
//            System.out.println(Outer.this.name);
        }
    }
    public static void main(String[] args) {
        //创建内部类的对象:new 外部类名.内部类构造器。
        Inner inner = new Outer.Inner("Inner showInfo");
    }
}

4.3局部内部类

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

4、 出了作用域就失效了。

代码:

/*
局部内部类:
    再方法中定义的内部类,和局部变量用法一样。出了作用域就失效了。
 */
public class Outer {
    public static void main(String[] args) {
        int a = 10;
        System.out.println(a);

        class Inner {
            private int a;
            public Inner(int a) {
                this.a = a;
            }
            public int getA() {
                return a;
            }
        }

        Inner inner = new Inner(10);
        System.out.println(inner.getA());
    }
}

 4.4匿名内部类(重点)

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

用法:

接口名|抽象类名|父类名 变量=new 接口名|抽象类名|父类名(){
        方法的重写;
    };

代码:

/*
匿名内部类:
    就是没有名字的子类型对象
    
     接口名|抽象类名|父类名 变量=new 接口名|抽象类名|父类名(){
        方法的重写
    };
 */
public class Outer {
    public static void main(String[] args) {
        A a = new A(){
            //匿名内部类提供成员属性
            public String name ="asdasad";
            //重写接口A里的抽象方法
            @Override
            public void showInfo(){
                System.out.println("Hello World");
            }
            //子类提供了独有的getXXX方法
            public String getName(){
                return name;
            }

        };
        //编译期间,看变量类型,因此能调用到showInfo
        a.showInfo();
//        a.getName();//编译期间,a里根本没有getName方法。
    }
}

interface A{
    public void showInfo();
}
//一般A的子类写法
//class B implements A{
//    public void showInfo() {
//        System.out.println("B");
//    }
//}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值