Java中的继承详解

继承的基本知识

继承是面向对象程序设计中的三个基本原则1之一。

在Java语言中,被继承的类被称为超类,继承类被称为子类2

比如狗类是动物类,牧羊犬类又是狗类。于是我们可以说狗类继承了动物类,而牧羊犬类就继承了狗类。于是狗类就是动物类的子类(或派生类),动物类就是狗类的父类(或基类)。

实现继承将需要使用extends关键字。

如下:

// A simple class hierarchy
class TwoDShape{
	double length;
	double width;
		
	void show(){
		System.out.println("length:" + length + " width:" + width);
	}
}
	
class Triangle extends TwoDShape{
	String style;
	
	double area(){
		return length * width;
	}
	
	void showStyle() {
		System.out.println("style:" + style);
	}
}

class Shapes {
	public static void main(String[] args) {
		Triangle triangle = new Triangle();
		triangle.length = 2;
		triangle.width = 1;
		triangle.style = "Rectangle";
		
		triangle.showStyle();
		triangle.show();
	}
}

输出结果如下:

style:Rectangle
length:2.0 width:1.0

需要注意的是:在Java中不存在多继承,与C++等支持多继承语言不同,所以在转换代码的过程中需要注意,而Java想要实现多继承可以使用implements(实现)接口,在后续博客中将会解释接口知识。

继承的优点:如果有两个类相似,那么它们会有许多重复的代码,导致后果就是代码量大,后期的维护性不高。通过继承就可以解决这个问题,将两段代码中相同的部分提取出来组成一个父类,实现代码的复用。

继承的特点:

  1. 子类拥有父类除 private 以外的所有属性和方法
  2. 子类可以拥有自己的属性和方法
  3. 子类可以重写实现父类的方法
  4. Java 中的继承是单继承,一个类只有一个父类

成员访问与继承

由类的成员变量具有访问限制可知,private可以防止未经过授权而进行修改,在继承中,也不会超越private的访问权限,如果将上述程序中的length,width声明为private,则会存在错误,Triangle类将会无法直接使用length和width。对此,程序员通常使用访问器方法来进行访问私有成员。

// A simple class hierarchy
class TwoDShape{
	private double length;
	private double width;
		
	void show(){
		System.out.println("length:" + length + " width:" + width);
	}

	public double getLength() {
		return length;
	}

	public void setLength(double length) {
		this.length = length;
	}

	public double getWidth() {
		return width;
	}

	public void setWidth(double width) {
		this.width = width;
	}
}
	
class Triangle extends TwoDShape{
	private String style;
	
	double area(){
		return getLength() * getWidth();
	}
	
	public String getStyle() {
		return style;
	}

	public void setStyle(String style) {
		this.style = style;
	}

	void showStyle() {
		System.out.println("style:" + style);
	}
}

class Shapes {
	public static void main(String[] args) {
		Triangle triangle = new Triangle();
		triangle.setLength(2);
		triangle.setWidth(1);
		triangle.setStyle("Rectangle");
		
		triangle.showStyle();
		triangle.show();
	}
}

输出结果如下:

style:Rectangle
length:2.0 width:1.0

构造函数与继承

如果超类与子类都存在构造函数,那么是由哪个构造函数来构造子类对象呢?答案是超类构造函数构造超类部分,子类构造函数构造子类部分。

// A simple class hierarchy
class TwoDShape{
	private double length;
	private double width;
		
	void show(){
		System.out.println("length:" + length + " width:" + width);
	}

	public double getLength() {
		return length;
	}

	public void setLength(double length) {
		this.length = length;
	}

	public double getWidth() {
		return width;
	}

	public void setWidth(double width) {
		this.width = width;
	}
}
	
class Triangle extends TwoDShape{
	private String style;
	
	Triangle(String style, double length, double width){
		setLength(length);
		setWidth(width);
		this.style = style; 
	}
	
	double area(){
		return getLength() * getWidth();
	}
	
	public String getStyle() {
		return style;
	}

	public void setStyle(String style) {
		this.style = style;
	}

	void showStyle() {
		System.out.println("style:" + style);
	}
}

class Shapes {
	public static void main(String[] args) {
		Triangle triangle = new Triangle("Rectangle", 2, 1);
		
		triangle.showStyle();
		triangle.show();
	}
}

输出结果如下:

style:Rectangle
length:2.0 width:1.0

super关键字

super关键字在子类内部使用,代表父类对象。

使用super调用超类构造函数

// A simple class hierarchy
class TwoDShape{
	private double length;
	private double width;
		
	TwoDShape(double length, double width) {
		this.length = length;
		this.width = width;
	}
	void show(){
		System.out.println("length:" + length + " width:" + width);
	}

	public double getLength() {
		return length;
	}

	public void setLength(double length) {
		this.length = length;
	}

	public double getWidth() {
		return width;
	}

	public void setWidth(double width) {
		this.width = width;
	}
}
	
class Triangle extends TwoDShape{
	private String style;
	
	Triangle(String style, double length, double width){
		super(length, width);
		this.style = style; 
	}
	
	double area(){
		return getLength() * getWidth();
	}
	
	public String getStyle() {
		return style;
	}

	public void setStyle(String style) {
		this.style = style;
	}

	void showStyle() {
		System.out.println("style:" + style);
	}
}

class Shapes {
	public static void main(String[] args) {
		Triangle triangle = new Triangle("Rectangle", 2, 1);
		
		triangle.showStyle();
		triangle.show();
	}
}

输出结果如下:

style:Rectangle
length:2.0 width:1.0

子类构造方法需要调用父类的构造方法时,在子类的构造方法体里最前面的位置:super()

使用super访问超类成员

super访问超类成员的用法与this相似,只不过super引用的是子类的超类。

这种用法多用于被子类覆盖的同名成员和同名方法。

创建多级层次结构

如果存在三个类,分别为A,B,C,而它们的关系为A是B的超类,B是C的超类,这样就形成了一个简单的多级层次结构,此时,C将继承A和B所有的成员。

// A simple class hierarchy
class TwoDShape{
	private double length;
	private double width;
		
	TwoDShape(double length, double width) {
		this.length = length;
		this.width = width;
	}
	void show(){
		System.out.println("length:" + length + " width:" + width);
	}

	public double getLength() {
		return length;
	}

	public void setLength(double length) {
		this.length = length;
	}

	public double getWidth() {
		return width;
	}

	public void setWidth(double width) {
		this.width = width;
	}
}
	
class Triangle extends TwoDShape{
	private String style;
	
	Triangle(String style, double length, double width){
		super(length, width);
		this.style = style; 
	}
	
	double area(){
		return getLength() * getWidth();
	}
	
	public String getStyle() {
		return style;
	}

	public void setStyle(String style) {
		this.style = style;
	}

	void showStyle() {
		System.out.println("style:" + style);
	}
}

class ColorTriangle extends Triangle{
	private String color;
	
	ColorTriangle(String color, String style, double length, double width) {
		super(style, length, width);
		
		this.color = color;
	}

	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}
	
	void showColor() {
		System.out.println("Color:" + color);
	}
}

class Shapes {
	public static void main(String[] args) {
		ColorTriangle triangle = new ColorTriangle("Red", "Rectangle", 2, 1);
		
		triangle.showColor();
		triangle.showStyle();
		triangle.show();
	}
}

输出结果如下:

Color:Red
style:Rectangle
length:2.0 width:1.0

怎样调用构造函数

对于多级层级结构来说,就会存在有构造函数使用的先后顺序的问题。

因为super()必须是构造函数中的第一条语句,所以根据递归的思想,将会先调用第一个超类的构造函数,依次下来,最后构造最后一个子类构造函数。

超类引用与子类对象

Java是强类型语言,在类型方面十分严格,因此在超类对象与子类对象中如果出现需要转换时,该是如何操作的呢?

以上面的Shapes.java程序为例,ColorTriangle是Triangle类的子类。

ColorTriangle t1 = new ColorTriangle("Red", "Rectangle", 2, 1);
Triangle t2 = new Triangle("Rectangle", 2, 1);

t1 = t2;			//ERROR
t2 = t1;			//OK

对于这一小段代码可以简单的这样理解,子类的成员中包含了超类的所有成员,因此超类引用可以引用子类对象。

所以,在构造中也可以直接使用子类对象来进行super()构造。

Triangle(Triangle t1){
	super(t1);
	style = t1.style;
}

方法重写

在类层次结构中,当子类中的方法与超类中方法有相同的签名和返回类型,就成称子类中的方法重写(override)了超类的方法。此时在子类中调用被重写的方法时,将会使用子类定义的方法,而超类的方法被覆盖。

class Animal {
    public void bark() {
        System.out.println("动物叫!");
    }
}

class Dog extends Animal {
    public void bark() {
        System.out.println("汪!汪!汪!");
    }
}

public class test{
    public static void main(String args[]){
        Animal a = new Animal(); 
        Dog d = new Dog();   

         Animal b = new Dog(); 

        a.bark();
        d.bark();
        b.bark();
    }
}

输出结果如下:

动物叫!
汪!汪!汪!
汪!汪!汪!

方法重写也使得Java能够实现动态方法分配,而动态方法分配这个机制是Java实现运行时多态性的机制。

抽象类

在定义类时,前面加上abstract关键字修饰的类叫抽象类。 抽象类中有抽象方法,这种方法是不完整的,仅有声明而没有方法体。抽象方法声明语法如下:

abstract void f();  //f()方法是抽象方法

使用抽象类的主要情况:

  1. 在某些情况下,某个父类只是知道其子类应该包含怎样的方法,但无法准确知道这些子类如何实现这些方法。也就是说抽象类是约束子类必须要实现哪些方法,而并不关注方法如何去实现。
  2. 从多个具有相同特征的类中抽象出一个抽象类,以这个抽象类作为子类的模板,从而避免了子类设计的随意性。

所以由上可知,抽象类是限制规定子类必须实现某些方法,但不关注实现细节。

抽象类的规则:

  1. 用 abstract 修饰符定义抽象类
  2. 用 abstract 修饰符定义抽象方法,只用声明,不需要实现
  3. 包含抽象方法的类就是抽象类
  4. 抽象类中可以包含普通的方法,也可以没有抽象方法
  5. 抽象类的对象不能直接创建,通常是定义引用变量指向子类对象。
abstract class TwoDShape{
	private double length;
	private double width;
		
	TwoDShape(double length, double width) {
		this.length = length;
		this.width = width;
	}
	void show(){
		System.out.println("length:" + length + " width:" + width);
	}

	public double getLength() {
		return length;
	}

	public void setLength(double length) {
		this.length = length;
	}

	public double getWidth() {
		return width;
	}

	public void setWidth(double width) {
		this.width = width;
	}
	
	abstract double area();
}
	
class Triangle extends TwoDShape{
	private String style;
	
	Triangle(String style, double length, double width){
		super(length, width);
		this.style = style; 
	}
	
	double area(){
		return getLength() * getWidth();
	}
	
	public String getStyle() {
		return style;
	}

	public void setStyle(String style) {
		this.style = style;
	}

	void showStyle() {
		System.out.println("style:" + style);
	}
}

class Shapes {
	public static void main(String[] args) {
		Triangle triangle = new Triangle("Rectangle", 2, 1);
		triangle.showStyle();
		triangle.show();
	}
}

输出结果如下:

style:Rectangle
length:2.0 width:1.0

final关键字

final关键字可以修饰类、方法、属性和变量

  1. final 修饰类,则该类不允许被继承,为最终类
  2. final 修饰方法,则该方法不允许被覆盖(重写)
  3. final 修饰属性:则该类的属性不会进行隐式的初始化(类的初始化属性必须有值)或在构造方法中赋值(但只能选其一)
  4. final 修饰变量,则该变量的值只能赋一次值,即常量

Object类

Java中定义了一个特殊的类Object类,它是所有类的隐式超类,换而言之,其他的类都是Object类的子类。

方法目的
clone()创建并返回此对象的一个副本。
toString()返回该对象的字符串表示。
notify()唤醒在此对象监视器上等待的单个线程。
notifyAll()唤醒在此对象监视器上等待的所有线程。
registerNatives()私有方法
getClass()返回此 Object 的运行类。
hashCode()用于获取对象的哈希值。
equals(Object obj)用于确认两个对象是否“相同”。
wait(long timeout)在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或 者超过指定的时间量前,导致当前线程等待。
wait(long timeout, int nanos)在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。
wait()用于让当前线程失去操作权限,当前线程进入等待序列
finalize()当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。

  1. 面向对象程序设计中的三个基本原则:封装、继承和多态。 ↩︎

  2. 在不同的地方,超类也可称为父类或基类,子类可被称为派生类。
    一般超类和父类对应子类,基类对应派生类。 ↩︎

  • 7
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值