工学1号馆

home

java核心系列6-继承

By Wu Yudong on June 10, 2015

原创文章,转载请注明: 转载自工学1号馆

继承的概念:指在已存在的类的基础上扩展产生新的类。

已存在的类称为父类(或基类、超类),新产生的类称为子类(或派生类)。上面提到的Person类就是父类,Student类是子类。

子类继承了父类,它拥有父类的所有特性(除构造方法不被继承外),当然也可以向子类添加新的属性、方法,或改写父类原有方法,这些新变化的内容仅仅属于子类所有。

意义:继承是面向对象程序设计最重要的特征之一,是实现代码重用、扩展软件功能的重要手段。

父类:更一般、更泛化   子类:更具体、更细化

一个类的子类可以是另一个类的父类,由继承关系可以形成类的层次性。

继承的实现

类的继承格式:

class 子类名 extends 父类名{
    //新增属性、方法,或改写父类原有方法
}

说明:

如果没有用extends指明父类,则默认继承Object根类,Object类是所有类的直接父类或间接父类,有关Object类的内容稍后介绍;

Java只支持单一继承,不允许多重继承,即:

一个子类只能有一个父类

一个父类可以派生出多个子类

采用继承机制后,简化了类的设计:只要写子类中不同父类的内容(新增属性、方法,或改写父类原有方法)

//B类继承A类
class A{				//父类
	int x=10;
	public void setX(int x){
		this.x=x;
	}
	public int getX(){
		return x;
	}		
	public String toString(){
		String information="";
		information=information+"x= "+x+"\n";
		return information;
	}		
}
class B extends A{			//子类继承父类
	int y=100;			//子类新增属性
	public void setY(int y){	//子类新增方法
		this.y=y;
	}
	public int getY(){		//子类新增方法
		return y;
	}		
	public String toString(){	//子类改写父类方法
		String information="";
		information=information+"x= "+x+"\n";	
		information=information+"y= "+y+"\n";
		return information;
	}		
}
public class InheritTest {
	public static void main(String args[]) {
		A a=new A();
		B b=new B();
		System.out.println(a.toString());
		System.out.println(b.toString());
	}
}

重用

重用的方式大致有两种:组合、继承

举个例子:

先创建一个Employee类:

public class Employee{
	private String name;
	private int age;
	private double salary;
	
	Employee (String name, int age,double salary){
		this.name=name;
		this.age=age;
		this.salary=salary;
	}
	Employee (){
		this(null,0,0);
	}
	public  double getSalary(){
		return salary;
	}
	public void setSalary(double salary){
		this. salary =  salary;
	}
	public double getIncome (){
		return salary;
	}
}

加入还要增加一个经理类Manager,要求其包含name、age和salary(月薪)、bonus(奖金),并且完成获得收入等的方法与构造器。

组合式重用

public class Manager{
	private Employee employee;
	private double bonus;
	
	Manager (String name, int age,double salary,double bonus){
		 employee =new Employee(name,age,salary);
		 this.bonus=bonus;
	}
	 Manager (){
		this(null,0,0,0);
	}
	……
}

注意:

1.组合是在类中,定义要复用的类的对象,作为属性。

2.通过这个属性达到复用目的。类似于一种代码组合。

3.它们之间的关系是has-a关系

继承式重用

public class Manager extends Employee{
	private double bonus;
	
	Manager (String name, int age,double salary,double bonus){
		 super(name,age,salary);
		 this.bonus=bonus;
	}
	 Manager (){
		this(null,0,0,0);
	}
	……
}

子和父类间的关系是is-a关系

判断继承关系的一条定律是看它是否符合is-a的定义 ,组合则要符合has-a的定义

变量隐藏与方法覆盖

通过前面的介绍,我们已经知道:

子类继承了父类的所有成员(变量和方法,除构造方法外)(注意:由于访问权限的限制,并不意味着父类的所有变量、方法都可以在子类中直接使用);

子类还可以根据需要,增加自己的变量和方法;

除此之外,我们还可以对父类已有变量、方法进行隐藏、覆盖。

Java允许在子类定义与父类同名(类型可以不同)的变量,在子类中直接使用这一变量名时,访问的是子类定义的变量,而父类同名的变量则被隐藏起来,这称为变量的隐藏

Java还允许在子类中对父类原有的方法进行重写,以实现新的功能。

所谓方法覆盖,是指在子类中重新定义一个方法的内容,该方法与父类的某一方法在方法名、参数(包括类型、个数、次序)、返回值类型 完全相同。

当子类对象中调用这一方法时,调用的是子类改写过的方法,而父类中的原有方法被覆盖。

方法重写必须满足如下要求:

  • 重写方法和被重写方法必须具有相同的方法名;
  • 重写方法和被重写方法必须具有相同的参数列表;
  • 重写方法的返回值类型必须和被重写方法的返回值类型相同或者是其子类;
  • 重写方法的不能缩小被重写方法的访问权限。
  • 子类方法不能抛出比父类方法更多的异常。

看下面的例子:

public class Point {
	public  int x, y;
	public String toString(){…}		
}
public class Circle extends Point {
    public int x, y ,radius;  // 变量隐藏
    public String toString(){…}  //方法覆盖
    public String toString(int x, int y, int radius){…}  //方法重载
}

下面的例子演示了变量隐藏与方法覆盖的使用方式:

//B类继承A类
class A{					//父类
	int x=10;                                                             //被隐藏的变量
	public void setX(int x){
		this.x=x;
	}
	public int getX(){
		return x;
	}		
	public String toString(){                                  //被覆盖的方法
		String information="";
		information=information+"x= "+x+"\n";
		return information;
	}		
}
//父类代码与前面相同,这里省略不写
class B extends A{				//子类继承父类
	double x=-12.345			//变量隐藏
	int y=100;				//子类新增属性
	public void setY(int y){			//子类新增方法
		this.y=y;
	}
	public int getY(){			//子类新增方法
		return y;
	}		
	public String toString(){			//子类改写父类方法,即方法覆盖
		String information="";
		information=information+"x= "+x+"\n";	
		information=information+"y= "+y+"\n";
		return information;
	}		
}
public class InheritTest {
	public static void main(String args[]) {
		A a=new A();
		B b=new B();
		System.out.println(a.toString());
		System.out.println(b.toString());
	}
}

程序运行结果:

x= 10

x= -12.345

y= 100

方法覆盖与方法重载的异同:

相同点:

都要求方法同名

都可以于于抽象方法和非抽象方法之间

不同点:

方法覆盖要求参数签名必须一致,而方法重载要求参数签名必须不一致

方法覆盖要求返回类型必须一致,而方法重载不做限制

方法覆盖只能用于子类覆盖父类的方法,方法重载用于同一个类的所有方法(包括从父类中继承而来的方法)

方法覆盖对方法的访问权限和抛出的异常有特殊的要求,而方法重载没有限制

父类的一个方法只能被子类覆盖一次,而一个方法所在的类中可以被多次重载

super关键字

如果要访问父类的隐藏变量、覆盖方法(需要有相应的访问权限),可以使用super关键字。

Super 可用来引用父类的成分,它有两种主要用法:

引用父类的成员(需要相应的访问权限):

super.变量

或  super.方法([参数列])

在子类构造方法中调用父类的构造方法

super([…]);//与this用法类似,应放在构造方法的第一行位置上。

还是以前面的B类继承A类为例说明如下:

子类的构造顺序

下面通过一个例子来说明子类的构造顺序:

class A{							
	public A(){
		System.out.println("调用了A类的构造方法");
	}		
}
class B extends A{					
	public B(){
		System.out.println("调用了B类的构造方法");
	}	
}
public class C extends B {
	public C(){
		System.out.println("调用了C类的构造方法");
	}
	public static void main(String args[]) {
		C c=new C();
	}
}

程序运行结果:

调用了A类的构造方法

调用了B类的构造方法

调用了C类的构造方法

从上可以看出:在构造子类的对象时,编译器会一直上溯到最初类,执行该类构造方法,然后依次执行各子类构造函数,即以:A->B->C 顺序来初始化子对象各部分。

子类的构造方法:

作用:用来完成对该类中的所有变量(即属性)进行初始化,子类中的变量包含从父类继承下来的变量和自己新增加的变量.

分工:各类只负责在该中新增变量的初始化工作,

在子类的构造方法中可以用super关键字来调用父类的构造方法,格式如下:

继承条件下构造方法的调用规则如下:

如果子类的构造方法中没有通过super显式调用父类的有参构造方法,也没有通过this显式调用自身的其他构造方法,则系统会默认先调用父类的无参构造方法。在这种情况下,写不写“super();”语句,效果是一样的。

如果子类的构造方法中通过super显式调用父类的有参构造方法,那将执行父类相应构造方法,而不执行父类无参构造方法。

如果子类的构造方法中通过this显式调用自身的其他构造方法,在相应构造方法中应用以上两条规则。

特别注意的是,如果存在多级继承关系,在创建一个子类对象时,以上规则会多次向更高一级父类应用,一直到执行顶级父类Object类的无参构造方法为止。

访问权限

通过继承,子类能够获得父类中除构造方法以外的所有变量和方法,但并不意味着子类可以调用父类的这些变量、方法,这要受到访问权限的限制。

//private权限类
class Person {
	private String name="";		//私有访问权限
	private int age=0;
	public Person(String name, int age){
		this.name=name;
		this.age=age;
	}
	public String getName(){
		return name;
	}
	public int getAge(){
		return age;
	}
}
class Student extends Person{
	public Student(String name, int age){
		super(name, age);
	}	
	public String toString(){
		String str="";
		str=str+"姓名:"+name;
		str=str+", 年龄:"+age;
		return str;
	}
} 
public class PrivateTest {
	public static void main(String args[]){
		Student wang=new Student("王小丽", 20);
		System.out.println(wang.toString());
	}
}

这是因为name、age两个变量是private权限,不允许子类直接访问。若调用public权限的getName()、getAge(),则问题可以迎刃而解。

 

 

 

 

 

如果文章对您有帮助,欢迎点击下方按钮打赏作者

Comments

No comments yet.
To verify that you are human, please fill in "七"(required)