·继承机制是面向对象程序设计不可缺少的关键概念,是实现代码可重用的根基,是提高软件系统的可拓展性与可维护性的主要途径。
·所谓继承是指一个类的定义可以基于另外一个已经存在的类,即子类基于父类,从而实现父类代码的重用,子类能吸收已有类的属性和行为,并能拓展新的能力。
形式:【访问权限修饰符】【修饰符】子类名 extends 父类名{子类体}
例:
//类:图形
public class Shape {
private int x;
private int y;
public Shape() {
}
// 方法:绘图
public void draw() {
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
//类:圆形
public class Circle extends Shape {
private int r;
public Circle() {
}
// 方法:获取直径
public int getDiameter() {
return 2 * r;
}
public int getR() {
return r;
}
public void setR(int r) {
this.r = r;
}
}
我们观察上面的代码发现,类Circle的定义中通过使用extends关键字继承了类Shape,此时,我们把Shape称为Circle的父类,反之,把Circle称为Shape的子类。而且,子类Circle新增了半径属性r,新增了获取直径的方法getDiameter()。
继承的主要作用是代码复用。代码表现为子类对象可以直接调用父类的属性和方法。
//类:使用场景
public class Client {
public static void main(String[] args) {
Circle circle = new Circle();
// 访问父类的属性和方法
circle.setX(1);
circle.setY(1);
circle.draw();
// 访问子类的属性和方法
circle.setR(9);
circle.getDiameter();
}
}
子类构造方法总是先调用父类构造方法。
默认情况下,调用父类无参构造方法。
可以在子类构造方法的第一行,使用super关键字调用父类任意一个构造方法。
子类构造方法总是先调用父类构造方法。
默认情况下,调用父类无参构造方法。
可以在子类构造方法的第一行,使用super关键字调用父类任意一个构造方法。
//类:图形
public class Shape {
public int x;
public int y;
public Shape() {
System.out.println("这是父类的无参构造方法!");
}
public Shape(int x, int y) {
this.x = x;
this.y = y;
System.out.println("这是父类的有参构造方法!");
}
public void draw() {
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
//类:圆形
public class Circle extends Shape {
private int r;
public Circle() {
// super();
// super(0, 0);
System.out.println("这是子类的无参构造方法!");
}
// 方法:获取直径
public int getDiameter() {
return 2 * r;
}
public int getR() {
return r;
}
public void setR(int r) {
this.r = r;
}
}
//测试
public class Client {
public static void main(String[] args) {
Circle circle = new Circle();
}
}
//输出
这是父类的无参构造方法!
这是子类的无参构造方法!
Java 语言中,要求子类有责任保证它所继承的父类尽快进入到一个稳定、完整的状态中。如果没有这个约束,那么子类的某个继承自父类的方法可能会使用到父类中的一些变量,而这些变量并没有进行初始化,从而产生一些难以预料的后果。
必须将调用父类构造方法的这条语句放在子类构造方法的第一条语句位置。如果第一条语句没有调用父类的构造方法,系统将会自动地在这个位置上插入一条调用父类默认构造方法的语句,即super(); 由于默认的构造方法不带参数,所以,如果在父类中定义了带参数的构造方法,而没有定义不带参数的构造方法将会出现编译错误。这也正是建议大家在定义带参数的构造方法时,一定要定义一个不带参数的构造方法的原因所在。 父类中那些带参数的构造方法,子类将不会自动地调用它们,必须人工地将调用它们的语句写入子类的构造方法中。
this关键字其实与继承没有关系,之所以放在这里学习,是因为this和super的作用非常类似。都有两大作用,即调用属性和方法,调用构造方法。区别是,this调用的都是本类的,super调用的都是父类的。
使用this关键字在自身构造方法内部引用其它构造方法。 在一个类的构造方法内部,也可以使用this关键字引用其它的构造方法,这样可以降低代码的重复,也可以使所有的构造方法保持统一,这样方便以后的代码修改和维护,也方便代码的阅读。
例:
public class Demo {
private int a;
private int b;
public Demo() {
// this引用构造方法
this(1, 2);
}
public Demo(int a, int b) {
super();
this.a = a;
this.b = b;
}
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
}
-直接使用this
-使用this关键字引用成员变量
-使用this关键字引用成员方法
注意:this关键字必须放在非静态方法里面
this代表自身对象 在一个类的内部,也可以使用this代表自身类的对象,或者换句话说,每个类内部都有一个隐含的成员变量,该成员变量的类型是该类的类型,该成员变量的名称是this
public class Demo {
private Demo instance;
public Demo() {
instance = this;
}
}
this引用成员变量 在一个类的方法或构造方法内部,可以使用“this.成员变量名”这样的格式来引用成员变量名,常常用来区分同名的成员变量和局部变量。
public class Demo {
private int a;
private int b;
public Demo() {
}
public int getA() {
return a;
}
public void setA(int a) {
// this引用成员变量
this.a = a;
}
public int getB() {
return b;
}
public void setB(int b) {
// this引用成员变量
this.b = b;
}
}
this引用成员方法 在一个类的内部,成员方法之间的互相调用时也可以使用“this.方法名(参数)”来进行引用。
public class Demo {
public Demo() {
}
public void method1() {
//this引用成员方法
this.method2();
}
public void method2() {
}
}
super关键字的使用方式与this很类似,不过super一定是在继承关系中使用,指的是父类或者父类的对象。
在子类构造方法中要调用父类的构造方法,需要注意:super语句只能出现在子类构造方法体的第一行。
当子类方法体中的局部变量或者子类的成员变量与父类成员变量同名时,即子类局部变量覆盖父类成员变量时,用“super.成员变量名”来引用父类成员变量
当子类的成员方法覆盖了父类的成员方法时,也就是子类和父类有完全相同的方法定义(方法体可以不同),此时,用“super.方法名(参数列表)”的方式访问父类的方法。
this通常指代当前对象,super通常指代父类。
继承的作用就是复用,即子类直接使用父类的属性和方法。
然而,有些时候,子类希望修改父类的方法的方法体,可以怎么做呢?
第一种做法是子类创建一个不同名字的新方法,实现新的逻辑,然而,这种做法会导致子类依然包含父类中的那个方法,却不应该使用,破坏封装性。 第二种我们希望子类中的方法依然和父类方法的声明形式一样,但是具体方法体却不同,这种做法就叫做方法覆盖。
建议:抽象这章从头到尾使用一个例子扩展,一直到抽象部分。
子类可以重写父类中某一个方法,称为方法覆盖,也称方法重写,是继承中非常重要的知识点。如果子类需要修改从父类继承到的方法的方法体,就可以使用方法覆盖。
同名 同参 同返回值 访问权限不能缩小
方法覆盖例子:
//父类
public class Animal {
String name;
Integer age;
public Animal() {
}
public Animal(String name, Integer age) {
this.name = name;
this.age = age;
}
void run() {
System.out.println("一只" + this.age + "岁的" + this.name + "在奔跑!~~");
}
void sound() {
System.out.println("一只" + this.age + "岁的" + this.name + "在吼叫!~~");
}
}
//子类:
public class Tiger extends Animal {
String color;
public Tiger(String name, Integer age, String color) {
super(name, age);
this.color = color;
}
void hunt() {
System.out.println("一只" + this.color + "的" + this.name + "在捕猎!~~");
}
void sound() {
System.out.print("我是一只老虎~");
super.sound();
}
}
此处可以提使用tigerSound()方法,而不使用方法覆盖的方式,sound()会被tiger类继承,这违反了面向对象的封装性特征。对象封装了属性和方法,对象的属性和方法都是该对象可以使用的。因此,上面的方法虽然实现了新算法,却存在严重漏洞,破坏了对象的封装性。如果子类需要修改自父类继承到的新方法,可以使用“方法覆盖”来完成。在子类中,声明一个与父类同名、同参、同返回值类型、访问权限不缩小的方法,就可以将父类中的方法覆盖。子类对象调用该方法,将自动绑定到子类覆盖后的新方法。
示例代码-测试类
public class Tester {
public static void main(String[] args) {
Tiger t = new Tiger("东北虎", 1, "白色");
t.sound();
}
}