JAVA面向对象3(抽象类、接口、内部类、枚举)

抽象类

由于继承这个显著特点,我们可以将子类设计的更加具体,而父类更加一般化,通用化。

抽象类的特点:  

1.作为父类,里面的方法不能满足任何一个子类的需,那么提供的逻辑根本就用不上,那么就不添加方法体 ,此时这个方法需要使用关键字abstract来修饰,表示为抽象方法,而抽象方法也要使用abstract来修饰,所以,该类就是抽象类。
2.抽象类中可以没有抽象方法
3.抽象类里面可以提供构造方法,但是不能被调用,即不能new对象,因为没有意义。
4.抽象类可以被继承,但是子类必须重写所有的抽象方法,否则子类也必须是抽象的
        public class 子类型名 extends 抽象类名{

        }

 抽象类的意义:

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

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

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

public abstract class Person {
    private String name;
    private int age;
    public Person() {}
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void sleep(){
        System.out.println(name+"-正在睡觉中------");
    }
    public void eat(){
        System.out.println("都可以吃饭");
    }
    public abstract void work();
}


public abstract class Student extends Person {
    private String sid;
    public Student(){}
//    public void work(){
//        System.out.println("---学生的工作是学习---");
//    }
    public abstract void doHomeWork();
}
public class Teacher extends Person {
    private String tid;
    public Teacher(){}
    public void work(){
        System.out.println("---老师的工作是教学---");
    }
}
public class Test {
    public static void main(String[] args) {
        Teacher teacher = new Teacher();
        //Student student = new Student();
        testWork(teacher);
        //testWork(student);

        //测试抽象类的构造器可以new对象吗?   不能
        //Person p1 = new Person("小红",23);
    }
    public static void testWork(Person p){
        p.work();
    }
}

 接口

接口,可以理解为是特殊的抽象类,里面的方法全都是抽象方法,里面的成员变量都是常量。

因为JAVA是单继承的,而JAVA为了完善多继承的概念,引用了接口。类可以实现多个接口,来达到多继承的效果。

 接口的特点:

  •  接口的关键字,是interface,不再是class
  •  子类是实现接口的,关键字是implements

    public class 子类型名 implements 接口名,接口名...{
    
    }
  实现接口时,所有的抽象方法都应该重写,否则该类是抽象类。

  • 子接口与父接口是继承关系:

    pubilc interface 子接口名 extends 父接口名{
    //接口里的变量就是常量,默认使用public static final修饰,可省略。常量注意赋值。
    //接口里的抽象方法,默认使用public abstract来修饰,可以省略
    }

  • 接口不能提供构造器,没有意义。

 实现接口:

  • 1) 与继承不同,一个类可以实现多个接口。接口间使用逗号分开。
  • 2) 使用关键字implements进行实现,必须实现接口中的所有抽象方法
  • 3) 若一个类中没有全部实现接口中的抽象方法,那么该类需要使用abstract声明成抽象类

 接口间的继承:

  • 1)接口之间可以存在继承关系,即一个接口通过关键字extends可以继承另一个接口。
  • 2)子接口继承了父接口中的所有抽象方法

接口在1.8之后的新特性:

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

1)default

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

public 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");
   }
}

两道练习题:

 

代码:

public class Computer {
    private USB usb1;
    private USB usb2;
    public void setUsb1(USB usb1){
        this.usb1 = usb1;
        usb1.charge();
    }
    public void setUsb2(USB usb2){
        this.usb2 = usb2;
        usb2.charge();
    }
}
public class KeyBoard implements USB{
    private String brand;
    @Override
    public void charge() {
        System.out.println("---键盘正在充电中---");
    }

    @Override
    public String getInfo() {
        return brand;
    }
}
public class Mouse implements USB {
    private String color;
    @Override
    public void charge() {
        System.out.println("---鼠标正在充电中---");
    }

    @Override
    public String getInfo() {
        return "鼠标的颜色:"+color;
    }
}
/**
 * 接口
 */
public interface USB {
    /**
     * 充电功能
     */
    void charge();

    /**
     * 获取信息功能
     * @return
     */
    String getInfo();
}
public class Program {
    public static void main(String[] args) {
        //创建一个电脑对象
        Computer computer = new Computer();
        //创建一个键盘对象
        KeyBoard keyBoard = new KeyBoard();
        //创建一个鼠标对象
        Mouse mouse = new Mouse();
        //将鼠标插入到电脑上
        computer.setUsb1(mouse);
        //将键盘插入到电脑上
        computer.setUsb2(keyBoard);
    }
}

代码:

public interface Employee {
    void work();
    void punchCard();
}
public interface Family {
    void cook();
    void wash();
}
public class Person implements Employee,Family,Comparable{
    private String name;
    private int age;
    public Person(){}
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void play(){
        System.out.println("---人喜欢娱乐-----");
    }
    public String toString(){
        return name+","+age;
    }

    @Override
    public void work() {
        System.out.println("---喜欢工作----");
    }

    @Override
    public void punchCard() {
        System.out.println("---上下班需要打卡----");
    }

    @Override
    public void cook() {
        System.out.println("---正在做饭---");
    }

    @Override
    public void wash() {
        System.out.println("---正在洗涮---");
    }

    /**
     *   默认是升序:
     *    返回值 是负数   说明前面的小,后面的大    不需要交换
     *    返回值  0      相等                   不需要交换
     *    返回值 是正数   说明前面的大,后面的小    需要交换
     *   想要降序:
     *
     *   升序: this-other
     *   降序:  other-this
     * @param o the object to be compared.
     * @return
     */
    @Override
    public int compareTo(Object o) {
//        Person p = (Person)o;
//        return p.name.compareTo(this.name);
        return 0;
    }
}
import java.util.Arrays;

public class Pragram {
    public static void main(String[] args) {
        Person p = new Person("xiaoming", 23);
        Employee e = p;
        e.punchCard();
        e.work();
        Family f = p;
        f.cook();
        f.wash();
        System.out.println("-------------------");
        testPlay(e);
        Person p2 = new Person("xiaohong", 24);

        //将这两个人放进数组中
        Person[] ps = new Person[]{p,p2};
        Arrays.sort(ps);
        System.out.println(Arrays.toString(ps));

    }
    public static void testPlay(Employee e){
        //调用cook方法
        if(e instanceof Family){
            Family f = (Family)e;
            f.cook();
        }
        //调用play方法
        if(e instanceof Person){
            Person p = (Person)e;
            p.play();
        }
    }
}

 常用接口:

Serializable序列化接口:

系统类库提供好的一个接口。当涉及到数据传输时,比如将内存的对象保存到磁盘,磁盘上的文件变成内存中的对象,或者对象在两台电脑之间传输。那么该对象的类必须实现序列化接口。否则传输失败。

该接口就是一个规范,里面没有任何东西,源码如下:

public interface Serializable {
   
}


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

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

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

返回值时负数:前面小后面大
返回值0:相等
返回值正数:前面大后面小

Comparator接口:

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

内部类

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

成员内部类:

定义在一个类的内部,与这个类的成员(属性、方法)平级,并且没有用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);
        }
    }
}

 静态内部类:

定义在一个类的内部,与这个类的成员(属性、方法)平级,并且使用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,此时无法访问");
        }
    }
}

 局部内部类:

定义在某一个代码段中的中。
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 {

        }
    }
}

 匿名内部类:

  • 没有名字的内部类,内部类,通常是需要配合其他的类或者接口一块使用的
  • 在方法中定义的没有名字的子类型对象,然后将对象的地址存到子类型的父类型的变量中
  • 通过变量来操作这个没有名字的子类型对象

                格式:
                父类型名 变量名=new 父类型名(){
                    //重写父类里的方法逻辑
                };

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


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

枚举

枚举是一种特殊的引用数据类型,是一个被命名的整型常数的集合,用于声明一组带标识符的常数。

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

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

枚举定义:

1.自定义类实现枚举

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

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

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

  • 将构造器私有化

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


    //自定义枚举类
    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("冬天","冰天雪地");
    
       //4.其他诉求1:获取枚举类对象的属性
       public String getSeasonName() {
          return seasonName;
       }
    
       public String getSeasonDesc() {
          return seasonDesc;
       }
       //4.其他诉求1:提供toString()
       @Override
       public String toString() {
          return "Season{" +
             "seasonName='" + seasonName + '\'' +
             ", seasonDesc='" + seasonDesc + '\'' +
             '}';
       }
    }
    public class SeasonTest {
    
       public static void main(String[] args) {
          Season spring = Season.SPRING;
          System.out.println(spring);
    
       }
    }

 2.enum关键字实现枚举

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

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

  • 可以提供私有的属性

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

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


    public enum Week {
        //MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
        周一,周二,周三,周四,周五,周六,周日;
    
        public static void main(String[] args) {
            System.out.println(Week.周五);
        }
    }

    使用构造器:

public enum Week {
    周一,周二,周三,周四,周五,周六,周日;

    public static void main(String[] args) {
       Person p = new Person();
       p.age = 23;
       p.name = "小物";

       //获取季节这个枚举中的一个值
        Season a = Season.AUTUMN;
        System.out.println(a);
    }
}
class Person{
    String name;
    int age;
    Week[] week;
}
enum Season{

    SPRING("春天"),SUMMER("夏天"),AUTUMN("秋天"),WINTER("冬天");

    private String describe;
    private Season(String describe){
        this.describe =describe;
    }

}

重写方法形式:

public enum Demo1 {
    LEFT{
        @Override
        public void show() {
            System.out.println("左:重写了show方法");
        }
    },RIGHT{
        @Override
        public void show() {
            System.out.println("右:重写了show方法");
        }
    },FRONT{
        @Override
        public void show() {
            System.out.println("前:重写了show方法");
        }
    },BEHIND{
        @Override
        public void show() {
            System.out.println("后:重写了show方法");
        }
    };
    public abstract void show();
}
public class Test {
    public static void main(String[] args) {
        Demo d=Demo.BEHIND;
        System.out.println(d);//BEHIND
        System.out.println("-------------------");
        String name = Demo.BEHIND.getName();
        System.out.println(name);
        String name1 = Demo.FRONT.getName();
        System.out.println(name1);
        System.out.println("-------------------");
        Demo1 d1=Demo1.RIGHT;
        d1.show();//右:重写了show方法
        Demo1 d2=Demo1.BEHIND;
        d2.show();//后:重写了show方法
    }
}

 enum常用方法:

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

 

/*
    演示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));
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值