欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

JAVA继承和Object

时间:2023-07-06
继承 类、超类和子类

子类将拥有父类/超类中的全部方法和域(private不算)。同时也可以在这基础上加入新的方法和域。子类能否访问并继承父类方法和域详见下表。
值得注意的是,访问protected方法和变量可以通过导入包的方式实现访问

访问包位置类修饰符privateprotectedpublic本类可见可见可见同包其他类或子类不可见可见可见其他包的类或子类不可见不可见可见子类的定义

格式如下

public class ChildClass extends ParentClass{}

重写(override)

有些书里会翻译成覆盖,其实就是重写。常常与重载相比较:

重载:方法名相同,参数不同的多个重名函数重写:子类中要求方法名和参数与父类完全相同的函数。要求返回值和异常比父类小或相同, 访问修饰符比父类大或相同。并且子类的静态方法不能重写,也能不能重写父类的静态方法。但是可以子类静态方法重写父类静态方法。

特殊注意:

final修饰的方法不能被重写,可以被重载final修饰的类不能被继承private 方法隐式添加了final若父类构造函数非空,则子类必须也有一个与父类同参的构造函数 super方法

由于在子类中,重写过的父类方法是隐藏的,所以调用时无法直接调用,否则会直接调用子类的方法,因此,我们需要使用super关键字来调用父类方法。
格式为:super.method();

super并非一种对象的引用,也无法赋值给其他对象变量,他只是个单纯的关键字,类似C++中的:ParentClass::method();

若父类构造函数含显式参数,则子类必须重写一个构造函数
并且需要用super();方法进行初始化。

好了,现在你已经基本掌握了JAVA中继承的基础知识了,让我们来编写一个范例吧

例:
创建一个Employee类,定义name、salary、hireDay等属性
创建一个Manager类,使其继承Employee类并额外加一个奖金属性
在main函数中测试这两个类

class Employee{ private String name; private double salary; private LocalDate hireDay; public Employee(String name,double salary,LocalDate hireDay) { this.name=name; this.salary=salary; this.hireDay=hireDay; } public String getName() { return name; } public double getSalary() { return salary; } public LocalDate getHireDay() { return hireDay; } public void raiseSalary(double byPercent) { double raise=salary*byPercent/100; salary+=raise; }}class Manager extends Employee{ double bonus; public Manager(String name,double salary,double bonus,LocalDate hireDay) { super(name,salary,hireDay); this.bonus=bonus; } public double getSaraly() { double baseSaraly=super.getSalary(); return (baseSaraly+bonus); }}

其中,Employee类显然是含有一个带三个参数的构造函数,那么子类Manger就需要一个至少带三个以上的参数构造函数。
而且,这个构造方法需要用super方法来调用父类完成参数的继承。

即,类继承时,构造函数也要相应继承

除此之外,super还可以用于帮助子类调用父类私有变量

理解方法调用

假设要调用x.f(INTgs)。x为类C的一个对象,f为C中一个方法,INT为一个int型参数(同理STRING、DOUBLE等)

编译器查看对象的声明类型和方法名。假设调用x.f(INT)。需要注意的是:可能存在多个名字为f但是参数不同的方法。例如:可能同时存在f(int)和f(String)。编译器会一一列举所有C中的f方法和其超类中能访问到的名为f的方法。

编译器获得了所有待选方法名单后,就会查看方法的参数类型。若有完全匹配的参数类型就会立即调用。例如:类C中同时有f(int) and f(double),而调用语句为x.f(INT),则会立即调用x.f(int)。这个过程被称为重载解析

若是private、static、final方法或构造器,那么编译器会准确获知需要调用哪个方法。这种调用方式被称为静态绑定。

若每次调用方法都需要进行搜索,则会造成巨大的资源和时间的开销。因此,虚拟机为每个类预先创建了一个方法表,需要调用方法时,虚拟机直接查表即可。但仅仅在动态绑定时需要,静态方法由于在加载时直接和类信息一起加载到方法区,无需创建对象,因此也无须通过方法表调用

额外理清一下动态绑定与静态绑定

动态绑定:对大多数方法和类来说都需要通过构造器创建或绑定对象来创建。为了减少频繁搜索带来的巨额开销,虚拟机会为每个类创建一个方法表,每次调用时只需要查表即可获取需要调用的方法。这个过程即为动态绑定

-静态绑定:以private、static、final修饰的静态方法,在每次加载类时就会直接被加载到方法区,系统直接调用即可,无需构造器方法表等。此过程即为静态绑定

final类和方法——阻止继承

类中的final无法继承到子类中,子类也无法读取,除非使用super方法或关键字。除此之外fianl类也无法被其他类继承(final类中的方法也会自动成为fianl方法)

fianl可以让我们阻止某些重要的类或方法被错误重写,final
也可以避免动态绑定带来的额外系统开销

强制类型转换

double x=3.1415926;int nx=(int)x;

基本数据类型转换没啥好说的,转换时可能会丢失部份信息量
重点是,有时很可能需要将某个类对象引用转换成其他类对象引用。
例如,此前我们的员工管理系统中, staff[] 数组中存储了Employee对象,但是我们如果要将 staff[0] 变为Manager对象呢

Manager boss =(Manager) staff[0];

但是staff[0]未必能满足转换为Manager对象的条件

若是将一个子类引用赋给超类变量,编译器是允许的(即可以向上)若是将一个超类引用赋给子类变量,则会抛出一个ClassCastException的异常(不能向下)。

我们可以使用instanceof运算符来判断能否转换
boolean result = obj instanceof Class
其中obj表示一个对象,Class为一个类。若obj是Class的一个对象或是其直接或间接子类、接口实现对象等,result都返回true,否则false
那么我们就可以用下面这个办法来判断staff[]能否转换

if(staff[0] instanceof Manager){ boss =(Manager) staff[0];}

若转换失败则会自动跳过而不会使得整个程序停止运行

总结:

只能在继承层次内进行转换,而且一般最好从下向上转换

超类转换为子类时最好使用 instanceof进行检查

尽量少使用类型转换和instanceof运算符,只要类设计合理,其实往往类型转换都是可以避免的

抽象类

通常来说,位于上层的类往往有着较高的通用性,甚至可能更抽象。比如说,Person类就可以作为Employee类和Student类的超类,因为无论雇员还是学生都是“人”。他们可以具有共有的属性,比如姓名、年龄等就可以直接放在高层次的Person类中。

但是雇员和学生显然存在着诸多不同的属性。如果我们需要对不同种类的人都有相应的“描述”,应该在Person类中提供何种内容呢?

一种办法是:在Person类中创建getDescription方法 ,并使得该方法返回空值,到了子类中再对这个方法重写

最好的办法还是:使用abstract关键字创建一个抽象类

特别提醒:拥有一或多个抽象方法的类本身也必须被定义为抽象类,但是抽象类中可以有非抽象方法

抽象类通常起到占位作用,相当于一个接口,他的具体实现在子类中。一般可以将所有的抽象方法在抽象超类全部定义出来。这样就不需要再定义抽象子类了。

定义一个Person抽象类,并在子类中实现相关功能

public abstract class Person{ private String name; private int age; //private Date date; public Person(String name,int age) { this.name=name; this.age=age; //this.date=date; } public abstract String getDescription(); public String getName() { return name; } public int getAge() { return age; }}

public class text{ public static void main(String[] args) { //Student student=new Student("San",17); //System.out.println(student.getDescription()); Person [] people=new Person[3]; people[0]=new Student("San",17); people[1]=new Employee("San",22,200000, LocalDate.of(2022,2,20)); people[2]=new Manager("San",29,100000000,10000000, LocalDate.of(2022,2,20)); for (Person p:people) { System.out.println(p.getDescription()); } }}class Student extends Person{ private String name; private String major= this.getClass().getName(); public Student(String name, int age) { super(name, age); } public String getDescription() { return String.format("A %d yeas old "+major+" named "+getName(),getAge()); }}class Employee extends Person{ private String name; private double salary; private LocalDate hireDay; private String major=this.getClass().getName(); public Employee(String name,int age,double salary,LocalDate hireDay) { super(name, age); this.name=name; this.salary=salary; this.hireDay=hireDay; } @Override public String getDescription() { return String.format("A %d years %s with a salary of $ %.2f named "+name+" hired on "+hireDay,getAge(),major,getSalary()); } public String getName() { return name; } public double getSalary() { return salary; } public LocalDate getHireDay() { return hireDay; } public void raiseSalary(double byPercent) { double raise=salary*byPercent/100; salary+=raise; }}class Manager extends Employee{ double bonus; private String major=this.getClass().getName(); public Manager(String name,int age,double salary,double bonus,LocalDate hireDay) { super(name,age,salary,hireDay); this.bonus=bonus; } public String getDescription() { return String.format("A %d years %s with a salary of $ %.2f named "+getName()+" hired on "+getHireDay(),getAge(),major,getSalary()+bonus); } public double getSaraly() { double baseSaraly=super.getSalary(); return (baseSaraly+bonus); }}

抽象类不可被实例化,但是可以定义对象,但是他只能引用非抽象子类对象。例如:
Person person=new Student("San",17);
这是上面的例子的引用代码。最终实现和直接创建子类Student类对象是一样的:
A 17 yeas old Student named San
同理,p.getDescription()也是调用的子类的方法

受保护访问

让我们再复习一下访问修饰符权限范围

域修饰符privateprotectedpublic同类可以可以可以同包不同类不可以可以可以不同包不可以不可以可以

一般来说最好将类中的域标记为private,将方法标记为public。
private私有内容对其他类是不可见的,包括子类也是如此。
但是protected是对子类可见的,即便不是同包也可见

Object:所有类的超类

Object是java所有类的始祖,但是不需要写明extends Object

可以用Object类型变量引用任何类型的对象:
Object obj = new Student("San",17)

但是Object数据类型只能作为各种值的“容器”,我们无法直接对Object类型的数据进行修改,而必须转换为其原始类型(一般Object向下转换是被允许的):
Student student=(Student) obj;

除此之外,当你不确定数组中的对象的类型时,也可以先都统一定为Object,后面根据需求可以对数组中进行变换。

后续还有150余种equals,hashCode和toString
未完待续

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。