Java学习随笔(2)

继承

类,超类,子类

超类又称父类、基类,是被继承的对象。子类又称派生类,是继承父类的对象。子类能从超类那里继承字段和方法,并可以添加新的方法和字段,但不会删除任何字段和方法。也就是说,子类比超类的功能更多。在设计类时,应当将最一般的方法放在超类中,将更特殊的方法放在子类中。

一个祖先类可以有多个派生类,且可以有多层次。即子类的下面还可以有子类。但一个子类只能有一个父类

覆盖方法

超类中的方法可能对于子类来说并不适用,因此,需要提供一个新的方法来覆盖超类的这个方法。

import java.time.LocalDate;

public class Employee {		//超类
    private String name;
    private double salary;
    private LocalDate hireDay;

    /**
     * @有参构造器
     * */
    public Employee(String name, double salary,int year,int month,int dayOfMonth){
        this.name = name;
        this.salary = salary;
        hireDay = LocalDate.of(year,month,dayOfMonth);
    }

    /**
     * @无参构造器
     * */
    public Employee(){
        name = "none";
        salary = 0;
        hireDay = LocalDate.of(0000,1,1);
    }
     public String getName() {
        return name;
    }

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

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public LocalDate getHireDay() {
        return hireDay;
    }

    public void setHireDay(LocalDate hireDay) {
        this.hireDay = hireDay;
    }

    @Override
    public String toString() {
        return  getClass().getName()+"{" +
                "name='" + name + '\'' +
                ", salary=" + salary +
                ", hireDay=" + hireDay +
                '}';
    }
 }
 

//子类
public class Manager extends Employee{
    private double bonus;

    /**
     * @有参构造器
     * */
    public Manager(String name, double salary,int year,int month,int dayOfMonth,double bonus) {
        super(name,salary,year,month,dayOfMonth);//调用父类的构造器,且该语句必须是子类构造器中的第一个语句
        this.bonus = bonus;
    }


    public double getBonus() {
        return bonus;
    }

    public void setBonus(double bonus) {
        this.bonus = bonus;
    }

    @Override
    public double getSalary() {     //覆盖父类的方法
        double baseSalary = super.getSalary();//调用父类的getSalary方法
        return baseSalary + getBonus();
    }
    @Override
    public String toString() {
        return super.toString() +
                "{ bonus=" + bonus+"}";
    }
}

上述代码中子类Manager覆盖了父类的getSalary()方法,因为子类中有bonus字段,经理的薪资总和必须是工资+奖金。

注意一点,由于子类不能直接访问父类的字段,因此需要调用到父类的getSalary()方法。
调用方法是super.getSalary()。(注意,不能直接写成getSalary(),这样会一直调用自己陷入死循环。)

警告:在覆盖一个方法时,子类方法不能低于超类方法的可见度性。比如超类方法是public,则子类方法也要声明为public。

子类构造器

子类要创建自己的构造器,首先需要调用父类的构造器,这里同时也是super关键字的第二种用法

public Manager(String name, double salary,int year,int month,int dayOfMonth,double bonus) {
        super(name,salary,year,month,dayOfMonth);//调用父类的构造器,且该语句必须是子类构造器中的第一个语句
        this.bonus = bonus;
    }

super(参数)是调用父类中带有指定参数的构造器,特别注意的是,该语句必须在构造器方法中的第一行。如果构造子类对象时没有显示的调用超类的构造器,那么超类中必须有一个无参构造器供子类调用。

super和this关键字很像,但两者用法不同:
this:一是指示隐式参数的引用,二是调用该类的其他构造器。
super:一是调用父类的方法,二是调用超类的构造器

多态

var staff = new Employee[3];
	  var boss = new Manager("boss",50000,1999,12,31,10000);
      staff[0] = boss;
      staff[1] = new Employee("zhangsan",10000,2001,12,31);
      staff[2] = new Employee("lisi",5000,2002,12,31);

        for (Employee e: staff) {
            System.out.println(e.getName()+" "+e.getSalary());
        }

输出结果:
boss 60000.0
zhangsan 10000.0
lisi 5000.0

尽管这里e声明为Employee对象,但实际上e既可以引用Employee对象,也可以引用Manager对象,当e引用为Manager对象时,e.getSalary()调用的是Manager对象的getSalary方法。
一个对象变量可以指示多种实际类型,这被称为多态。在运行时能够自动的选择适当的方法,称为动态绑定
在这个例子中,变量staff[0]与boss引用同一个对象,但编译器只将staff[0]看做是一个Employee对象
这意味着,可以这样调用:
boss.setBonus(5000);
而不能这样:
staff[0].setBonus(5000);

注意,不能将超类的引用赋值给子类
在Java中,子类引用数组可以转换成超类引用数组,而不需要使用强制转换:
Manager manager[] = new Manager[10];
Employee staff[] = manager;

这是完全合法的,如果manager[i]是一个Manager,它也一定是一个Employee。但是,注意下面这段:
staff[0] = new Employee(“zhangsan”,5000,1999,12,31);

这段代码可以通过编译器,但是有个问题,staff和manager引用的是同一个对象,并且该对象是Manager类型,但是我们把Employee对象放入到Manager对象数组中,等于说我们把员工放到了经理行列中,这是不行的,如果我们此时调用manager[0].setBonus(5000),将会访问一个不存在的实例字段,进而搅乱相邻的存储空间内容。因此,每次创建数组时应当牢记其元素类型,并负责监督将类型兼容的引用存储到数组中。

理解方法调用

假设要调用x.f(args),隐式参数x声明为c类的一个对象,调用过程如下:
① 编译器查看对象的声明类型和方法,但有可能存在多个名字为f但参数不一样的方法,编译器将会一一列举c类中所有名为f的方法和其超类中所有名为f且可访问的方法
② 编译器根据提供的参数类型找到对应的方法,如果在所有名为f的方法中存在一个与所提供参数类型完全匹配的方法,就选择这个方法,该过程称为重载解析。如果没有找到或者发现经过类型转换后有多个方法与之匹配,则会报错。
③ 如果是private、static、final方法或者构造器,那个编译器可以准确地知道应该调用哪个方法,这称为静态绑定。如果要调用的方法依赖于隐式参数的实际类型,则称为动态绑定。

每次调用方法都要完成这个搜索,时间开销相当。因此虚拟机预先为每个类计算了一个方法表,其中列出了所有方法的签名(方法名和参数列表)和要调用的实际方法。

阻止继承

不允许扩展的类被称为final类,使用final修饰符。
如果方法被声明为final,表示所有子类都不能覆盖这个方法。
不过如果一个类被声明为final,则其中所有方法都自动声明为final。
其目的是为了确保他们不会在子类中改变语意。

强制转换类型

强制转换有两点需要注意:

1.只能在继承层次结构内进行强制类型转换
进行强制类型转换的唯一原因是:要在暂时忘记对象的实际类型之后使用对象的全部功能。

Java 编译器允许在具有直接或间接继承关系的类之间进行类型转换。如果把引用类型转换为子类类型,则称为向下转型;如果把引用类型转换为父类类型,则称为向上转型。
对于向下转型,必须进行强制类型转换;对于向上转型,不必使用强制类型转换。

如果两者之间没有继承关系,则不允许转换!

2.再将超类强制转换成子类之前,应当使用instanceOf进行检查
在进行强制类型转换之前,先查看能否成功的转换,为此需要使用instanceOf操作符,例如:

if(staff1 instanceof Manager){      //判断左边对象是否是右边的子类对象,或是否是右边对象类型本身,返回值为boolean类型
            Manager manager2 = (Manager) staff1;
        }
        else{
            System.out.println("无法强制转换");
        }

因为staff是Manager的父类对象,因此无法转换。

子类中的东西要比父类多,因此将承诺夺得转换为承诺少的会出问题,因为可能转换后父类没有子类中的某些方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值