定义两个类(人类、学生类):
class Person{
private String name;
private int age;
public String getName(){
return this.name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return this.age;
}
public void setAge(int age){
this.age = age;
}
}
class Student{
private String name;
private int age;
private String school;
public String getName(){
return this.name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return this.age;
}
public void setAge(int age){
this.age = age;
}
public String getSchool(){
return this.school;
}
public void setSchool(String school){
this.school = school;
}
}
以上程序即使我们之前一直采用的模式,单独的Java类,含有大量重复性代码。不仅代码上重复,而且从概念上讲,学生一定是人,学生和人相比学生更加具体,学生类描述的范围更小,具备的属性更多,具有的方法也更多。
这个时候要想消除结构定义上的重复,就要用到继承。
在现实生活中,继承一般指的是子女继承父辈的财产。在程序中,继承描述的是事物之间的所属关系,通过继承可以使用多种事物之间形成一种关系体系。
举例:
学生和教师都属于人,程序中便可以描述为学生和教师继承人,同理,中学生和大学生继承学生,而讲师和教授继承教师。这些人之间形成一个继承体系,具体实现如下图所示。
在Java中,类的继承是指在一个现有类的基础上去创建一个新的类,创建出来的新类被称作子类,现有类被称作父类,子类会自动拥有父类所有可继承的属性和方法。
在Java中,继承使用extends关键字来实现,定义语法如下:
class 子类 extends 父类
关于继承定义的说明:
子类在一些书上也被称为派生类,父类也被称为超类(Super Class)
举例:继承的基本实现
class Person{
private String name;
private int age;
public String getName(){
return this.name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return this.age;
}
public void setAge(int age){
this.age = age;
}
}
// 定义了一个Student子类继承了Person父类
class Student extends Person{
}
public class Test{
public static void main(String[] args){
Student stu = new Student();
stu.setName("zhangsan");
stu.setAge(18);
System.out.println("姓名:"+stu.getName()+",年龄:"+stu.getAge());
}
}
通过上述代码可以发现,当发生自类继承关系之后,子类可以直接继承父类的操作,可以实现代码的重用。子类最低也维持和父类相同的功能。子类可以进行功能的扩充。例如:扩充属性和方法。
举例:子类进行功能的扩充
// 定义了一个Student子类继承了Person父类
class Student extends Person{
//扩充新的属性
private String school;
public String getSchool(){
return this.school;
}
public void setSchool(String school){
this.school = school;
}
}
public class Test{
public static void main(String[] args){
Student stu = new Student();
stu.setName("zhangsan");
stu.setAge(18);
stu.setSchool("University");
System.out.println("姓名:"+stu.getName()+",年龄:"+stu.getAge()+",学校:"+stu.getSchool());
}
}
继承的主要作用:对类进行扩充以及代码重用
举例:观察子类对象创建
class Person{
public Person(){
Sustem.out.println("**Person类对象产生**");
}
}
class Student extends Person{
public Student(){
super(); //此语句在无参时写不写都一样,但有参必须写
System.out.println("**Student类对象的产生**");
}
}
public class Test{
public static void main(String[] args){
new Student();
}
}
运行结果如下所示:
以上代码我们发现,没有任何一条语句调用 父类构造方法。因此子类对象实例化之前一定先实例化父类对象。
注意:
① 实际上在子类的构造方法之中,相当于隐含了一个语句super();
② 如果父类里没有提供无参构造,就必须使用super()明确指出你要调用的父类构造方法。
举例:错误的多继承
class A{}
class B{}
class C extends A,B{}
C同时继承A和B的主要目的是同时拥有A和B中的操作,为了实现这样的目的,可以采用多层继承的形式完成。
class A{}
class B extends A{}
class C extends B{}
多层继承的层数不建议太多。类的继承关系最多3层。
总结:Java不允许多重继承,但允许多层继承
举例:显示继承与隐式继承
class Person{
private String name;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
}
class Student extends Person{
public void fun(){
System.out.println(getName());
}
}
public class Test{
public static void main(String[] args){
Student stu = new Student();
stu.setName("zhangsan");
System.out.println(stu.getName());
stu.fun();
}
}
此时父类中的属性的确被子类所继承了,但发现子类能够使用的是所有非private操作,而所有的非private操作无法直接使用,所以称为隐式继承。
在上面学习过继承之后,我们就可能遇到下列问题:
如果子类中定义了与父类相同的方法或属性的时候,这样的操作就称为覆写(override)
如果子类定义了与父类方法名称、参数类型及个数完全相同的方法。但是被覆写不能够拥有比父类更为严格(>=)的访问控制权限。
举例: 简单的覆写
class Person{
public void print(){
System.out.println("[Person]类的print方法");
}
}
class Student extends Person{
public void print(){
System.out.println("[Student]类的print方法");
}
}
public class Test{
public static void main(String[] args){
new Student().print();
}
}
运行结果如下所示:
判断调用的方法到底是父类方法还是子类方法:
在进行方法覆写的时候,有明确要求:被覆写的方法不能拥有比父类更为严格的访问控制权限。
三种访问控制权限的比较:private < default(什么也不用写,包访问权限) < public
意味着如果父类使用public进行方法声明,那么子类也必须使用public;如果父类使用default,那么子类可以使用default或者public。
举例: 错误的覆写
class Person{
public void print(){
System.out.println("[Person]类的print方法");
}
}
class Student extends Person{
//默认为default权限,比public更加严格的访问权限
void print(){
System.out.println("[Student]类的print方法");
}
}
public class Test{
public static void main(String[] args){
new Student().print();
}
}
问题: 如果现在父类方法使用private定义,子类中使用public覆写,可以吗?
举例: 父类使用private定义的方法,子类中使用public覆写
class Person{
public void fun(){
this.print();
}
//如果现在父类中使用了private定义,那么就表示该方法只能被父类使用,子类无法使用。换言之,子类根本不知道有这样的方法
private void print(){
System.out.println("[Person]类的print方法");
}
}
class Student extends Person{
//这时候定义的方法只是子类定义的新方法,并没有和父类方法有任何关系
public void print(){
System.out.println("[Student]类的print方法");
}
}
public class Test{
public static void main(String[] args){
new Student().fun();
}
}
运行结果如下所示:
总结:
如果父类中定义的方法使用了private定义,那么就表示该方法只能被父类使用,子类无法使用。换而言之。父类中被private修饰的,只能被父类使用,子类无法使用。这时子类中定义的与父类的同名方法是新方法,和父类方法并没有任何关系。
区别 | 重载(overload) | 覆写(override) |
---|---|---|
概念 | 方法名称相同,参数的类型及个数不同 | 方法名称、返回值类型、参数的类型及个数 |
范围 | 一个类 | 继承关系 |
限制 | 没有权限要求 | 被覆写的方法不能拥有比子类更严格的访问控制权限 |
重载时尽量保持方法返回值类型一致。
当子类定义了和父类属性名称完全形同的时候,就成为属性的覆盖。
举例:属性覆盖
class Person{
public String info = "Person";
}
class Student extends Person{
//属性覆盖
public String info = "Student";
}
public class Test{
public static void main(String[] args){
System.out.println(new Student().info);
}
}
运行结果如下:
按照就近原则,取得是被覆盖的属性。
这种操作本身就没有什么意义,其核心的原因在于:类中属性都要求使用private封装,一旦封装了,子类不知道父类具有什么属性,那么也就不存在属性覆盖的问题。
当子类重写父类方法后,子类对象将无法访问父类中被重写的方法。为了解决这一问题,Java中专门提供了一个super关键字用于访问分类成员。
上面在子类对象实例化时,我们使用过super(),当时主要的作用是子类调用父类构造方法时才使用的。那么在进行覆写的操作过程中,子类也可以使用super()方法/super.属性明确调用父类中的方法或属性。
super([参数1, 参数2, ……])
(1)必须要写,要告诉编译器当前调用的是有参构造
(2)子类构造方法中调用父类构造方法必须是第一行语句
(3)this与super不能同时使用
举例:使用super调用父类构造方法
class Person{
private String name;
public Person(){}
public Person(String name)
{
this.name = name;
}
public String getName(){
return this.name;
}
}
class Student extends Person{
private String school;
public Student(String name, String school){
super(name);
this.school = school;
}
public void print(){
System.out.println("我是" + this.school + "的" + getName());
}
}
public class Test{
public static void main(String[] args){
new Student("张三", "星光小学").print();
}
}
使用super()调用父类构造方法的代码必须位于子类构造方法的第一行,且参数列表必须与调用父类构造方法的参数列表一致。
super.成员变量 //表示明确调用父类中被覆写的属性
super.成员方法([参数1, 参数2, ……]) //用于子类中明确调用被覆写的方法
举例:使用super调用父类的同名方法
class Person{
public void print(){
System.out.println("父类的print方法");
}
}
class Student extends Person{
public void print(){
super.print();
System.out.println("子类的print方法");
}
}
public class Test{
public static void main(String[] args){
new Student().print();
}
}
class Person{
public String info = "Person";
}
class Student extends Person{
public String info = "Student";
public void print(){
System.out.println(super.info);
System.out.println(this.info);
}
}
public class Test{
public static void main(String[] args){
new Student().print();
}
}
通过上述代码,我们可以发现super和this在使用上非常的相似,但是两者最大的区别就是super是子类访问父类的操作,this是本类访问处理的操作。
区别 | this | super |
---|---|---|
概念 | 访问本类中的属性和方法 | 由子类访问父类中的属性、方法 |
查找范围 | 先查找本类,如果本类没有就调用父类 | 不查找本类而直接调用父类定义 |
特殊 | 表示当前对象 | 无 |
总结: