📅  最后修改于: 2020-03-23 01:42:49             🧑  作者: Mango
先决条件: 继承
在Java中,所有非静态方法都基于基础对象的运行时类型,而不是指向该对象的引用的类型。因此,在对象的声明中使用哪种类型都没有关系,其行为将相同。
如何引用子类对象
有两种方法可以引用子类对象。两者都具有相对于其他的一些优点/缺点。声明在编译时可见的方法上可见。
考虑一个说明这两种方法的示例。
// Java展示引用子类
// 基类
class Bicycle
{
// Bicycle类有两个fields
public int gear;
public int speed;
// Bicycle构造器
public Bicycle(int gear, int speed)
{
this.gear = gear;
this.speed = speed;
}
// Bicycle三个方法
public void applyBrake(int decrement)
{
speed -= decrement;
}
public void speedUp(int increment)
{
speed += increment;
}
// toString()打印Bicycle信息
public String toString()
{
return("齿轮数 "+gear
+"\n"
+ "速度 "+speed);
}
}
// 测试代码
class MountainBike extends Bicycle
{
// MountainBike子类增加了一个field
public int seatHeight;
// MountainBike子类构造函数
public MountainBike(int gear,int speed,
int startHeight)
{
// 调用基类构造器
super(gear, speed);
seatHeight = startHeight;
}
// MountainBike增加了一个方法
public void setHeight(int newValue)
{
seatHeight = newValue;
}
// 重写toString()打印更多信息
@Override
public String toString()
{
return (super.toString()+
"\n座位高度 "+seatHeight);
}
}
// 测试代码
public class Test
{
public static void main(String args[])
{
// 使用子类引用
// 方法1
Bicycle mb2 = new MountainBike(4, 200, 20);
// 使用子类引用
// 方法2
MountainBike mb1 = new MountainBike(3, 100, 25);
System.out.println("第一个座位高度 "
+ mb1.seatHeight);
System.out.println(mb1.toString());
System.out.println(mb2.toString());
/* 如下语句不可用,因为Bicycle
没有定义变量seatHeight.
// System.out.println("第二个座位高度 is "
+ mb2.seatHeight); */
/* 如下语句不可用,因为Bicycle
没有定义设定座位高度setHeight()方法.
mb2.setHeight(21);*/
}
}
输出:
第一个座位高度 25
齿轮数 3
速度 100
座位高度 25
齿轮数 4
车速 200
座位高度 20
以上程序说明:
MountainBike mb1 = new MountainBike(3, 100, 25);
Bicycle mb2 = new MountainBike(4, 200, 20);
System.out.println("seat height of first bicycle is " + mb1.seatHeight);
System.out.println(mb1.toString());
System.out.println(mb2.toString());
System.out.println("seat height of second bicycle is " + mb2.seatHeight);
mb2.setHeight(21);
使用类型转换
在上面的示例中,我们已经看到,通过使用Bicycle类型的引用’mb2’,我们无法调用特定于子类的方法或访问子类字段。使用Java中的类型转换可以解决此问题。例如,我们可以声明另一个引用,例如MountainBike类型的“ mb3″,并使用类型转换将其分配给“ mb2″。
// 声明MountainBike引用
MountainBike mb3;
// 适用类型转换,把mb3赋给mb2.
mb3 = (MountainBike)mb2;
因此,现在以下语句有效。
f(double): 6.3
f(double): 6.6
而不是假定的输出:
f(int): 6
f(double): 6.6
重载不适用于C++编程语言中的派生类。编译器查看派生类的范围,找到单个函数“ double f(double)”并调用它。它永远不会干扰基类的(封装)范围。在C++中,没有作用域之间的重载,派生类作用域不是该一般规则的例外。
class Base
{
public int f(int i)
{
System.out.print("f (int): ");
return i+3;
}
}
class Derived extends Base
{
public double f(double i)
{
System.out.print("f (double) : ");
return i + 3.3;
}
}
class myprogram3
{
public static void main(String args[])
{
Derived obj = new Derived();
System.out.println(obj.f(3));
System.out.println(obj.f(3.3));
}
}
上面程序的输出是:
f (int): 6
f (double): 6.6
因此,在Java中,重载可在与C++相反的方式进行。Java编译器根据用于调用方法的参数类型,确定要在编译时执行的重载方法的正确版本,并根据这两个类的重载方法的参数,接收调用中使用的参数的值并执行重载方法。
最后,让我们尝试以下C#程序的输出:
using System;
class Base
{
public int f(int i)
{
Console.Write("f (int): ");
return i + 3;
}
}
class Derived : Base
{
public double f(double i)
{
Console.Write("f (double) : ");
return i+3.3;
}
}
class MyProgram
{
static void Main(string[] args)
{
Derived obj = new Derived();
Console.WriteLine(obj.f(3));
Console.WriteLine(obj.f(3.3));
Console.ReadKey(); // write this line if you use visual studio
}
}
注意:Console.ReadKey()用于暂停控制台。它类似于C / C++中的getch。
上面程序的输出是:
f(double) : 6.3
f(double): 6.6
而不是假设的输出
f(int) : 6
f(double) : 6.6
原因与C++程序中说明的相同。像C++一样,在父类和派生类之间没有重载解析。在C#中,没有作用域之间的重载,派生类作用域也不是该一般规则的例外。这与C++相同,因为C#语言的创建者Anders hejlsberg认为,C#的设计与C++更加接近。