JAVA学习笔记9,抽象类和接口及内部类

一 抽象类和抽象方法

1.1 抽象类

用abstract关键字来修饰一个类,这个类叫做抽象类。

  • 此类不能实例化
  • 抽象类中一定有构造器,便于子类实例化时调用
  • 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作

1.2 抽象方法

用abstract来修饰一个方法,该方法叫做抽象方法。

  • 只有方法的声明,没有方法的实现。以分号结束
  • 含有抽象方法的类必须被声明为抽象类。反之,抽象类中可以没有抽象方法的。
  • 抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类,需要用abstract修饰。

1.3 abstract使用注意事项

  • 不能用abstract修饰变量、代码块、构造器;
  • 不能用abstract修饰私有方法、静态方法、final的方法、final的类。

1.4 练习

  1. 编写工资系统,实现不同类型员工(多态)的按月发放工资。如果当月出现某个Employee对象的生日,则将该雇员的工资增加100元。
  • 定义一个Employee类,该类包含:private成员变量name,number,birthday,其中birthday 为MyDate类的对象;abstract方法earnings();toString()方法输出对象的name,number和birthday。
  • MyDate类包含:private成员变量year,month,day ;
    toDateString()方法返回日期对应的字符串:xxxx年xx月xx日
  • 定义SalariedEmployee类继承Employee类,实现按月计算工资的员工处理。该类包括:private成员变量monthlySalary;实现父类的抽象方法earnings(),该方法返回monthlySalary值;toString()方法输出员工类型信息及员工的name,number,birthday。
  • 参照SalariedEmployee类定义HourlyEmployee类,实现按小时计算工资的员工处理。该类包括:private成员变量wage和hour;实现父类的抽象方法earnings(),该方法返回wage*hour值;toString()方法输出员工类型信息及员工的name,number,birthday。
  • 创建Employee变量数组并初始化,该数组存放各类雇员对象的引用。利用循环结构遍历数组元素,输出各个对象的类型,name,number,birthday,以及该对象生日。当键盘输入本月月份值时,如果本月是某个Employee对象的生日,还要输出增加工资信息。
    结果图
import java.util.Scanner;
public class Test3 {
    public static void main(String[] args) {
        Employee[] emps= new Employee[]{
            new SalariedEmployee("tom", new MyDate(1999, 2, 11), 6000),
            new SalariedEmployee("john", new MyDate(1992, 5, 28), 7000),
            new HourlyEmployee("wang", new MyDate(2000, 8, 1), 60, 31)
        };
        Scanner scan = new Scanner(System.in); 
        System.out.print("输入月份:");
        int m=scan.nextInt();
        for (int i = 0; i < emps.length; i++) {
            System.out.println(emps[i]);
            if (m==emps[i].getBirthday().getMonth()) {
                System.out.println("今天是你的生日,增加工资100元");
            }
        }
        scan.close();
    }
}
class HourlyEmployee extends Employee {
    private double wage;
    private int hours;

    public HourlyEmployee(String name, MyDate birthday, double wage, int hours) {
        super(name, birthday);
        this.wage = wage;
        this.hours = hours;
    }

    @Override
    public double earnings() {
        return wage*hours;
    }

    @Override
    public String toString() {
        return "HourlyEmployee [名字:"+getName()+"  员工号:"+getNumber()+"  薪水:" + earnings() +"  生日:"+getBirthday() +"]";
    }
    
}
class SalariedEmployee extends Employee {
    private double monthlySalary;
    @Override
    public double earnings() {        
        return monthlySalary;
    }

    public SalariedEmployee(String name, MyDate birthday, double monthlySalary) {
        super(name,birthday);
        this.monthlySalary = monthlySalary;
    }

    @Override
    public String toString() {
        return "SalariedEmployee [名字:"+getName()+"  员工号:"+getNumber()+"  薪水:" + earnings() +"  生日:"+getBirthday() +"]";
    }
    
}

class MyDate{
    private int year;
    private int month;
    private int day;

    public MyDate(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    @Override
    public String toString() {
        return year+"年" +  month + "月" + day + "日";
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }
    
    
}
abstract class Employee{
    private String name;
    private int number;
    private MyDate birthday;
    public abstract double earnings();
    private static int total=0;
    private static int id=1000;

    public Employee(String name,MyDate birthday) {
        this.name = name;
        this.number = (total++)+id;
        this.birthday = birthday;

    }

    public String getName() {
        return name;
    }


    public int getNumber() {
        return number;
    }

    public MyDate getBirthday() {
        return birthday;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setBirthday(MyDate birthday) {
        this.birthday = birthday;
    }    
}

二 接口interface

接口(interface)是抽象方法和常量值定义的集合。

2.1 接口的特点:

  1. 用interface来定义。
  2. 接口中的所有成员变量都默认是由public static final修饰的。
  3. 接口中的所有抽象方法都默认是由public abstract修饰的。
  4. 接口中没有构造器。
  5. 接口采用多继承机制。

2.2 接口语法格式

先写extends,后写implements

class SubClass extends SuperClass implements InterfaceA{ } 
interface Person{
    String name="人";//省略public final static
    public final static String con="中国";
    void eat();//省略public abstract
    public abstract void walk();
}
interface Student extends Person{
    void study();
}
class Pupil implements Student{

    @Override
    public void eat() {
    }

    @Override
    public void walk() {
     }

    @Override
    public void study() {
    }

}

2.3 注意事项

  • 一个类可以实现多个接口,接口也可以继承其它接口。
  • 实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则,仍为抽象类。
  • 接口的主要用途就是被实现类实现。(面向接口编程)
  • 与继承关系类似,接口与实现类之间存在多态性
  • 接口和类是并列关系,或者可以理解为一种特殊的类。从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义(JDK7.0及之前),而没有变量和方法的实现。

2.4 接口和抽象类之间的对比

  1. 抽象类
  • 包含抽象方法的类
  • 由构造方法、抽象方法、普通方法、常量、变量组成
  • 子类继承抽象类(extends)
  • 抽象类可以实现多个接口
  • 抽象类有单继承的局限
  • 常见的设计模式有模板方法
  1. 接口
  • 主要是抽象方法和全局常量的集合
  • 由常量、抽象方法、(jdk8.0:默认方法、静态方法)构成
  • 子类实现接口(implements)
  • 接口不能继承抽象类,但允许继承多个接口
  • 常见的设计模式有简单工厂、工厂方法、代理模式
  1. 相同点
  • 都通过对象的多态性产生实例化对象
  1. 使用建议
    • 如果抽象类和接口都可以使用的话,优先使用接口,避免单继承的局限

在开发中,常看到一个类不是去继承一个已经实现好的类,而是要么继承抽象类,
要么实现接口。

2.5 interface Java 8新特性

  • 静态方法:使用 static 关键字修饰。可以通过接口直接调用静态方法,并执行其方法体。可以在标准库中找到像Collection/Collections或者Path/Paths这样成对的接口和类。
  • 默认方法:默认方法使用 default 关键字修饰。可以通过实现类对象来调用。在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。比如:Java 8 API中对Collection、List、Comparator等接口提供了丰富的默认方法。
    • 若一个接口中定义了一个默认方法,而另外一个接口中也定义了一个同名同参数的方法(不管此方法是否是默认方法),在实现类同时实现了这两个接口时,会出现接口冲突。
      • 解决办法:实现类必须覆盖接口中同名同参数的方法,来解决冲突。
    • 若一个接口中定义了一个默认方法,而父类中也定义了一个同名同参数的非抽象方法,则不会出现冲突问题。此时遵守:类优先原则。接口中具有相同名称和参数的默认方法会被忽略。

2.6 练习

  1. 定义一个接口CompareObject用来实现两个对象的比较。
  • 若返回值是 0 , 代表相等; 若为正数,代表当
    前对象大;负数代表当前对象小
  • 定义一个Circle类,声明redius属性,提供getter和setter方法
  • 定义一个ComparableCircle类,继承Circle类并且实现CompareObject接口。在ComparableCircle类中给出接口中方法compareTo的实现体,用来比较两个圆的半径大小。
  • 定义一个测试类InterfaceTest,创建两个ComparableCircle对象,调用compareTo
    方法比较两个类的半径大小。
  • 思考 :参照上述做法定义矩形类 Rectangle 和 ComparableRectangle类,在ComparableRectangle类中给出compareTo方法的实现,比较两个矩形的面积大小。
public class Test4 {
    public static void main(String[] args) {
        ComparableCircle cc1 = new ComparableCircle(1.5);
        ComparableCircle cc2 = new ComparableCircle(1.5);
        System.out.println(cc1.compareTo(cc2));//0
    }
}

class ComparableCircle extends Circle implements CompareObject {
    
    @Override
    public int compareTo(Object o) {
        if(o == null)
            throw new RuntimeException();
        if(this == o)
            return 0;
        if(o instanceof ComparableCircle){
            ComparableCircle cc=(ComparableCircle) o;
            if (cc.getRedius()==this.getRedius()){
                return 0;
            }else if(this.getRedius()<cc.getRedius()){
                return -1;
            }else{
                return 1;
            }
        }
        throw new RuntimeException();
    }

    public ComparableCircle(double redius) {
        super(redius);
    }
    
}
class Circle{
    private double redius;

    public Circle() {
    }

    public Circle(double redius) {
        this.redius = redius;
    }

    public double getRedius() {
        return redius;
    }

    public void setRedius(double redius) {
        this.redius = redius;
    }
    
}
interface CompareObject{
    int compareTo(Object o);
}

三 内部类

3.1 内部类定义

  • 一个类A的定义位于另一个类B的内部,前者A称为内部类,后者B称为外部类。
    • Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。
    • Inner class的名字不能与包含它的外部类类名相同;

3.2 分类

  1. 成员内部类(static成员内部类和非static成员内部类)
    • 作为外部类的成员
      • 和外部类不同,Inner class还可以声明为private或protected;
      • 可以调用外部类的结构
      • Inner class 可以声明为static的,但此时就不能再使用外层类的非static的成员变量;
    • 作为一个类
      • 可以在内部定义属性、方法、构造器等结构
      • 可以声明为abstract类 ,因此可以被其它的内部类继承
      • 可以声明为final的
      • 编译以后生成OuterClass$InnerClass.class字节码文件(也适用于局部内部类)
    • 注意
      1. 非static的成员内部类中的成员不能声明为static的,只有在外部类或static的成员内部类中才可声明static成员。
      2. 外部类访问成员内部类的成员,需要“内部类.成员”或“内部类对象.成员”的方式
      3. 成员内部类可以直接使用外部类的所有成员,包括私有的数据
      4. 当想要在外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态的
        结果图
public class Test5 {
    private int num = 123;
    public class Inner {
        private int num = 456;
        public void mb(int num) {
            System.out.println(num); // 局部变量
            System.out.println(this.num); // 内部类对象的属性
            System.out.println(Test5.this.num); // 外部类对象属性s
        } 
    }
    public static void main(String args[]) {
        Test5 out = new Test5();
        Test5.Inner inner =  out.new Inner();
        inner.mb(110);
    } 
}
  1. 局部内部类(不谈修饰符)、匿名内部类
  • 局部内部类的申明
class 外部类{
    方法(){//方法内
        class 局部内部类{ } 
        }
    {//代码块内
        class 局部内部类{ } 
    } 
}
  • 局部内部类使用
    • 只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类
    • 但是它的对象可以通过外部方法的返回值返回使用,返回值类型只能是局部内部类的父类或父接口类型
  • 局部内部类的特点
    • 内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号,以及数字编号。
    • 只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类。
    • 局部内部类可以使用外部类的成员,包括私有的。
    • 局部内部类可以使用外部方法的局部变量,但是必须是final的。由局部内部类和局部变量的声明周期不同所致。
    • 局部内部类和局部变量地位类似,不能使用public,protected,缺省,private
    • 局部内部类不能使用static修饰,因此也不能包含静态成员
  • 匿名内部类
    • 不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
    • 格式:
    new 父类构造器(实参列表)|实现接口(){
    //匿名内部类的类体部分
    }
    
    • 匿名内部类的特点
      • 匿名内部类必须继承父类或实现接口
      • 匿名内部类只能有一个对象
      • 匿名内部类对象只能使用多态形式引用
public class Test5 {
    public static void main(String args[]) {
        Test5 test = new Test5();
        test.getRunnable1().run();//我是局部内部类
        test.gRunnable2().run();//我是匿名局部内部类
    }
    
    public Runnable getRunnable1() {
        class Runn implements Runnable{
            @Override
            public void run() {
                System.out.println("我是局部内部类");
            }
        }
        return new Runn();        
    }
    public Runnable gRunnable2() {
        return new Runnable(){
            @Override
            public void run() {
                System.out.println("我是匿名局部内部类");
            }
        };
    }
}
Runnable{
            @Override
            public void run() {
                System.out.println("我是局部内部类");
            }
        }
        return new Runn();        
    }
    public Runnable gRunnable2() {
        return new Runnable(){
            @Override
            public void run() {
                System.out.println("我是匿名局部内部类");
            }
        };
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值