java为了解决相似属性和行为能共用相同的代码,也是为了便于标准模块化。减少相同代码要多次重复编写的问题,提供了继承、重载和覆盖的功能。继承是针对类,重载和覆盖是针对方法的。
一、继承:为了便于理解。我直接举例说明:
比如我们现在有图形(Shape)、矩形(Rect)、正方形(Square)、圆(Circle)。Shape类有两个属性变量int x; int y;来表示Shape的位置,一个成员方法来up(int dy)来表示Shape的移动。
public class Shape {
int x;
int y;
public Shape(int x,int y){
this.x = x;
this.y = y;
}
public void up(int dy){
y -= dy;
}
矩形(Rect)和圆(Circle)继承图形(Shape),正方形(Square)继承矩形(Rect)继承用extends关键字表示,那么Rect和Circle和继承Shape的属性和方法。就是Rect和Circle不需要声明变量x、y和up方法,就已经有这些属性,如下代码,我们并没有在Rect类中声明x、y变量和up方法。Rect的引用变量可以访问x、y、up方法。是因为Rect从父类Shape类中继承类这些变量和方法。在这里要特别强调一下,一个子类只能继承一个父类,比如正方形(Square)只能继承矩形(Rect),不能同时继承矩形(Rect)和图像(Shape)。一个父类可以别多个子类继承。比如Shape同时被Circle和Rect继承。
public class Rect extends Shape {
int width;
int height;
public static void main(String[] args) {
Rect r = new Rect();
r.x = 3;
r.y = 3;
r.up(2);
}
在继承中子类会默认继承父类的构造器(构造方法),如果父类没有写构造器,java会默认生产一个与类名相同的无参构无返回值的造器,子类会默认继承父类的无参默认构造器。如果父类定义类有参构造器,那么java就不在为其提供给无参默认构造器。此时如果父类没有无参构造器,字类需要用super关键字调用父类构造器。
public class Shape {
int x;
int y;
//比如这个Shape类,没有定义任何构造器,java虚拟机会默认给我们加下面这个构造器
Shape(){
}
子类Rect也会默认调用父类的这个默认的构造器。
如果类中已经定义类有参构造器,即便我们再定义一个无参构造器,该构造器在ide中会是灰色。只有被调用类才会高亮
父类中已经定义类有参构造器,子类需要用super关键字调用父类构造器,注意此时即便父类中也定义类无参构造器,子类也需要super调用:并且super必须在构造器第一行。
二、重载(overload)
overload是指方法名相同,参数不同的方法:
有些子类的构造方法可以理解成是对父类方法的重载:
三:覆盖(overide)
overide一般是子类对父类方法的覆盖,要求方法名,返回值,参数类型都相同,但方法里的逻辑可以不同。
比如Rect和Circle都覆盖里Shape的contains方法:
一个点的坐标x、y到圆心的距离的平方和开根号小于等于半径就是在圆内或圆上
一个点到矩形的顶点横坐标之差在0到矩形的宽的范围并且纵左边之差在0到矩形高的范围,即为在矩形内或矩形上。
矩形和圆的contains方法的方法名,返回值类型,参数类型都完全相同,但方法里面的逻辑完全不一样,同一个方法可以解决各种不同具体对象的不同问题,这就是多态
在这里要特别提醒大家注意的是:在继承中,属性由有引用类型决定访问那个属性,方法是由对象类型决定访问那个方法。属性绑定到引用类型,方法动态绑定到对象。
在这里先补充一个知识点,字类可以给父类赋值,但父类不能给子类赋值,上图中s是Shape 类型,所以s只能访问Shape的成员变量,而Shape类中没有r这个成员变量,所以s.r会报错,而s指向的实例是一个矩形类。所以s.contains实际执行的是矩形中的contains方法,不说执行Shape中的contains方法。
思考题:下面这个代码打印结果说什么:
public class TestOverrideAndOverload {
public static void main(String[] args) {
Woo w = new Woo();
Super s = new Sub();
w.t(s);
}
}
class Woo{
public void t(Super obj){
System.out.println("Goo t(super)");
obj.s();
}
public void t(Sub obj){
System.out.println("Goo t(Sub)");
obj.s();
}
}
class Super{
public void s(){
System.out.println("Super s()");
}
}
class Sub extends Super{
public void s(){
System.out.println("Sub s()");
}
}