08-Java继承
8.1 为什么需要继承
看一个案例
创建了一个小学生的类
创建了一个大学生的类
最后调用了这两个类
Pupil和graduate这两个类的属性和方法都是一摸一样,如果按照上面这样子写代码就会冗余,麻烦,这个时候就可以使用继承来解决这个问题。
8.2 继承的介绍
继承可以解决代码复用,让我们的编程更加靠近人类的思维,多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有子类不需要再重新定义这些属性和方法,只需要通过 extends 来声明继承父类就可以了。
继承的示意图
- 如上图所示,B类和C类的属性有很多的相同的属性和方法
- 再写一个A类,把B类和C类共有的属性和方法写在A类里面
- 通过关键字extends让B类和C类都继承A类的属性和方法,A类就是父类,B类和C类继承了A类的属性和方法,叫字类。
- D类和E类也可以i继承B类和C类的属性和方法,同时也可以继承A类的属性和方法,关系类似于:孙子--爸爸--爷爷。
8.3 继承入门案例
对上面提到案例的改进
- 新建一个父类Student,把子类Pupil和子类Grauate共有的属性和方法写在父类里面。
- 再在Pupil和Graduate子类里写子类特有的属性和方法,然后继承父类。
- 最后直接调用Student父类里面的方法和属性就可以了
继承的好处 - 代码的复用性提高了
- 代码的扩展性和维护性提高了,以后我如果需要加共有属性和方法的话,直接在父类Student写就行。
8.4 继承使用细节
- 子类继承了所有的属性和方法,但是私有属性不能在子类直接访问,需要通过公共的方法去访问。
父类
package com.edu.extens_;
public class Base {
//四个属性
public int n1 = 100;
protected int n2 = 200;
int n3 = 300;
private int n4 = 400;
public Base() {//无参构造器
System.out.println("base()...");
}
//父类提供一个public的方法,返回了N4
public int getN4() {
return n4;
}
public void test100() {
System.out.println("test100");
}
protected void test200() {
System.out.println("test200");
}
void test300() {
System.out.println("test300");
}
private void test400() {
System.out.println("test400");
}
}
Sub子类
package com.edu.extens_;
//子类
public class Sub extends Base {//子类
public void sayok() {
//非私有的属性和方法可以在子类直接访问
//但是私有属性和方法不能在子类直接访问
System.out.println(n1 + " " + n2 + " " + n3);
test100();
test200();
test300();
//test400();错误,私有属性和方法不能直接访问
//需要通过父类提供的公共方法来访问
System.out.println("n4=" + getN4());
}
}
- 子类必须调用父类的构造器,完成父类的初始化。
3. 当创建子类对象时,不管使用子类的哪个构造器,默认情况下都会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类构造器中用super();去指定使用父类的哪个构造器完成对父类的初始化工作,否则会编译不通过。
4. 如果希望指定去调用父类的某个构造器,则显示调用以下
5. super在使用时,需要放在构造器的第一行。
6. super()和this()都是只能放在构造器第一行,因此这两个方法不能共存在一个构造器,只能使用其中的一个。
7. Java所有类都是Object类的子类,Object是所有类的基类。
8. 父类构造器的调用不限于直接父类,将一直网上追溯直到Object类。
8.5 继承的本质分析
看一个案例,GranPa类是Father类的父类,Father类是son的父类,是这种继承关系。
当执行son son = new son();后会在内存里面发生什么事情呢?
- new son();的时候会加载类信息,首先加载GranPa类信息,然后再加载Father类信息,最后再加载son类信息。因为他们是继承关系,son--继承了Father类,Father--继承了GranPa类,GranPa继承了--Object。
- 加载完类信息后,GranPa类开始堆里面开空间,name,hoby是字符串,放在常量池里。
- Father类也堆里面开空间,name放在常量池里,age变量放在堆里面。
- son类也是在堆里面开空间,那么放在常量池。
5.当堆里面的空间和数据分配好之后,堆里面的地址把它返回给主方法的son对象。
返回信息的时候要按照查找关系来返回信息: - 首先看子类是有该属性。
- 如果子类有这个属性,并且可以访问,则返回信息。
- 如果子类没有这个熟悉那个,就看父类有没有这个属性(如果父类有,并且可以访问,就返回信息)。
- 如果父类没有,就按照第3条规则,继续找上级父类,直到Object,如果还没有就报错。
8.6 继承练习题
父类
package com.edu.extens_.exercise;
public class PC extends Computer{
private String brand;
public PC(String cpu, int memory, int disk, String brand) {
super(cpu, memory, disk);
this.brand = brand;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public void printInfo() {
System.out.println(getDetals() + " brand=" + brand);
}
}
子类
package com.edu.extens_.exercise;
public class PC extends Computer{
private String brand;
public PC(String cpu, int memory, int disk, String brand) {
super(cpu, memory, disk);
this.brand = brand;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public void printInfo() {
System.out.println(getDetals() + " brand=" + brand);
}
}
打印输出
package com.edu.extens_.exercise;
/*
* 编写Computer类,包含CPU、内存、
* 硬盘等属性,getDetails方法用于
* 返回Computer的详细信息
* 编写PC子类,继承Computer类,添加特有属性品牌brand
* 在ExtendsExercise类main方法中创建PC对象,分别给
* 对象中特有的属性赋值,以及从Computer类继承的属性赋值,并使用方法打印输出信息。
* */
public class ExtendsExercise {
public static void main(String[] args) {
PC pc = new PC("intel",16,500,"ibm");
pc.printInfo();
}
}