Java中类的继承和多态

一.类的继承:
1.继承是一个“是”的关系,这个类既是一个新的类,也是其所继承的那个类。
在java中使用extends关键字来表示继承关系。当创建一个类时,总是在继承,如果没有明确指出要继承的类,就总是隐式地从根类Object进行继承!也就是说所有类都是Object类的子类。
我们一般把被继承的类称为父类或者基类,把继承得来的类称为子类或者派生类。子类与父类不仅有继承的关系,而且子类还是父类的扩展。

2.继承的几点注意事项:

  • 1.基类不含默认无参构造函数,派生类必须显式调用基类的有参构造函数。
  • 2.Java 中的继承来说是单继承,即一个类最多只能显示地继承于一个父类,但是一个类可以拥有多个子类。
  • 3.派生类并不是基类的子集,而是比基类包含更多的信息和方法。
  • 4.父类的构造方法以及父类的private的变量与方法是不会被继承的!

举例说明类的继承:

class Employee{
    private String name;
    private double salary;
    private int workAge;
    public Employee(){
    
    }
    Employee(String name, double salary, int workAge){
        this.name = name;
        this.salary = salary;
        this.workAge = workAge;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setSalary(double salary) {
        this.salary = salary;
    }
    public void setWorkAge(int workAge) {
        this.workAge = workAge;
    }
    public String getName() {
        return name;
    }
    public double getSalary() {
        return salary;
    }
    public int getWorkAge() {
        return workAge;
    }
    public double countSalary(){
        if(workAge > 12){
            return salary;
        }else if(workAge > 6 && workAge <= 12){
            return salary * 0.9;
        }else{
            return salary * 0.8;
        }
    }
}

在这里我们定义了一个名为“Employee”的类,现在我们想要再定义一个名为“Manager”的类去继承这个类。

class Manager extends Employee{
    private double bonous;
    public Manager(){
        super();
    }
    public Manager(String name, double salary, int workAge, double bonous){
        super(name, salary, workAge);
        this.bonous = bonous;
    }
    public void setBonous(double bonous) {
        this.bonous = bonous;
    }
    public double getBonous() {
        return bonous;
    }
    protected void setSalary(double salary, double other){
        super.setSalary(salary+other);
    }
    public double countSalary(){
        return this.bonous + super.countSalary();
    }
}

这里的“extends”表示了一个继承关系,表示了我们新定义的“Manager”类是继承于“Employee”这个类的。
注意1:子类在被定义出来的同时就继承了父类的成员变量以及成员方法。所以就算我们并没有在子类中写出来,但是实际上子类也是拥有自己的这些成员变量和成员方法。但是在子类中重新定义的成员变量以及成员方法则是只在子类中有的!

例如上边“Manager”类里边的bonous变量,如果我们想通过Employee类的对象进行调用的话那么就会造成错误。

注意2:子类在继承的同时并不会生成一个默认的构造函数,需要我们去手动生成一个构造函数!
例:

public Manager(String name, double salary, int workAge, double bonous){
    super(name, salary, workAge);
    this.bonous = bonous;
}

因为Manager类继承的时候不会继承构造函数,所以我们给Manager类定义了一个构造函数,如果不去定义一个构造函数,那么程序会报错。

注意3:子类中如果定义与父类名称相同的成员变量时,父类的该名称的成员变量在子类中会被覆盖,子类中如果定义与父类名称相同的成员方法时会产生一个方法被重写的现象。这时如果需要在子类中调用父类的成员变量或者成员方法时要加一个super修饰符,如果不加那么会默认为this修饰符从而产生错误。
例:

 public double countSalary(){
        return this.bonous + super.countSalary();
    }

这段代码是子类对父类的countSalary方法的一个重写,而这个重写需要调用父类的countSalary方法,故加上super修饰符来表明这是对于父类的方法的调用。

3.重写和重载:
重载:参数列表不同,方法名都相同。
重写:1.在具有继承关系的不同类里面。2.参数列表相同,方法名也相同。

面试题:重载与重写有什么区别?
1.方法重写发生在具有继承关系的不同类当中,方法重载可以发生在同一类中,
也可以发生在具有继承关系的不同类中
2.方法重写具有同样的签名,方法重载具有不同的签名,其两者使用
方法名都必须保持一致

二.类的多态:
多态的定义:
多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才能够确定具体的类。
多态的优点:
在多态的基础上,让程序运行时才能够确定具体的类,这样就不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态。
可以达成某些一个父类有多个子类时,我们可以通过这一个父类的引用来执行其下的多个子类的方法,而不需要去重复声明。

1.向上转换与向下转换:
例:我们在前边的例子中创建了一个名为Employee的类,然后我们又创建了一个名为Manager的类,并且让它继承了Employee类。

Employee emp = new Manager();
//父类的引用指向创建的子类的对象

向上转换:派生类的对象赋值给一个基类的引用。

向上转换作为方法参数的应用:

public void sleep(Object obj){
}

在上边的代码中我们发现作为形参的是Object obj,也就是传进来一个Object类的对象作为参数,这样写的好处是Java中的所有类都是Object类的子类,在传入时就会进行一个自动的向上转换。这样写的好处是就是如果方法体中涉及到类似对多种类型的对象进行不同操作时,更加方便。有助于代码优化。

同样,以此类推
向下转换:基类的对象赋值给一个派生类的引用,这个必须要显式转换,向编译器表明用途。为使得转换成功,必须确保要转换的对象是子类的实例!

注意:使用向下转换时要加上一个显式转换,这是因为我们的编译器是声明了什么就会要求什么,子类既是子类自己也可以当做是一个父类,所以不会报错,但是父类是不能当做一个子类来看的,这样就会导致声明与赋值是矛盾的,故需要一个显式转换。
例:

Employee emp1 = (Employee) obj;
//加强转让编译器认为指向的是声明的引用对应的类。

注意:强转成功的前提是本身这个父类的对象是子类的实例才行。
例:

Object obj = new Employee();
Object obj1 = new Object();
Employee emp1 = (Employee) obj;//正确,因为是子类的实例
Employee emp = (Employee) obj1;//错误,因为本来就是一个父类的实例。

关键字instanceOf:
例:obj instanceOf class 判断obj是否是class类的实例。如果obj是class类的实例那么返回true,如果不是或者obj为空,那么返回false。

2.多态的必要条件:
Java实现多态的三个必要条件:继承、重写、向上转型(基类的引用可以引用派生类的对象)!

注意:指向子类的父类引用由于向上转型了,它只能访问父类中拥有的方法和属性,而对于子类中存在而父类中不存在的方法,该引用是不能使用的,尽管是重载该方法。若子类重写了父类中的某些方法,在调用该些方法的时候,必定是使用子类中定义的这些方法(动态连接、动态调用)。

这是因为,在声明的时候我们进行向上转换,在这个过程中,因为JAVA的编译器是赋值时声明的是什么,那么在程序运行的过程中始终就会把我们的这个对象视作父类,所以在用这个引用实现一些操作时只能使用子类从父类当中所继承的。而由于这个父类的引用实际上指向的是一个子类的对象,故用这个父类引用去实现一些被子类重写过的方法时,调用的一定是子类重写过的方法。

例:根据我们之前写过的例子,我们知道Manager类是Employee的子类。Manager类中的Bonous是独有的成员变量,setBonous、getBonous是独有的成员方法,Manager类的countSalary是重写过的。

Employee emp = new Manager();
emp.setName("zhangsan");
emp.setSalary(8000);
emp.setWorkAge(12);
emp.setBonous(5000);//错误,因为不能调用子类独有的方法。
System.out.println(emp.countSalary());
//这里调用的实际上是子类中重写过后的方法。

如果在多态的情况下一定要实现子类中独有的方法或者成员变量的话,那么我们需要使用一个显式转换。
例:

Employee emp = new Manager();
if(emp instanceof Manager){
//加判断的原因是显式转换能成功的前提是被转换的对象必须是要转换的类的实例化对象。
    Manager man = (Manager)emp;
    man.setBonous(7000);
}

上边的代码就是通过一个显式转换从而让父类的引用可以调用子类中独有的方法和成员变量。

java的引用有两个类型:一个是编译时类型,一个是运行时类型
编译时的类型由声明类型决定,运行时的类型由赋值的实例决定!

例:Employee emp = new Manager();
这段代码在编译的时候是视为父类Employee类,所以如果我们想要用emp去调用子类Manager中独有的成员时会出错,但是如果我们用emp去调用子类重写的父类当中的方法时,我们可以通过运行结果发现实际上是使用子类中重写的方法。

静多态:编译时确定调用哪个方法 重载
动多态:运行确定调用哪个方法 重写

3.多态的优点:
不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码。有助于代码的优化。

面试题:static方法可以被重写吗?
静态方法可以被重写,但是重写后,使用向上转换的时候不能去调用静态方法,静态方法是属于类的,只能用“类名.”去调用,无法构成多态。

如何防止类被继承,如何防止方法被重写?
final 修饰类 密封类
final 修饰方法 密封方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值