继承的使用以及细节问题

目录

一、继承的基本介绍

二、继承给编程带来的便利

三、继承使用的细节问题

四、继承本质分析(重要)

五、继承练习题

六、super关键字

6.1 基本介绍

6.2 基本语法

6.3 super 给编程带来的便利/细节

6.4 Super使用细节

6.4.1 演示访问方法的规则

6.4.2 演示访问属性的规则

Super和this的比较


一、继承的基本介绍

继承可以解决代码复用,让我们的编程更加靠近人类思维.当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过 extends 来声明继承父类即可。 

二、继承给编程带来的便利

  1. 代码的复用性提高了
  2. 代码的扩展性和维护性提高了

三、继承使用的细节问题

(1) 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问

(2) 子类必须调用父类的构造器, 完成父类的初始化

Base类是一个父类,里面有无参构造,Sub类继承了Base类,此时new一个Sub对象的时候,先调用父类的构造器,然后调用Sub的构造器

(3) 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过(怎么理解。) [举例说明]

当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器

这里有一个父类Base

public class Base {
	private int n1 = 100;
	private int n2 = 200;
	private int n3 = 300;

	public Base() {
		System.out.println("父类Base()构造器被调用...");
	}
	
}

 子类Sub

public class Sub extends Base{
	public Sub() {
		System.out.println("子类Sub()构造器被调用");
	}
	
	public Sub(String name){
		System.out.println("子类Sub(String name)构造器被调用");
	}
}

测试类

public class Test {
	public static void main(String[] args) {
		Sub sub = new Sub();
		System.out.println("======第二个对象======");
		Sub sub1 = new Sub("jack");
	}
}


如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过 

在Base加个有参构造器,然后把无参构造器删除,这时候我们写的构造器就把默认的无参构造器覆盖了,无参构造器就没有了

这时候去保存代码的时候,会发现,子类的构造器报错了

那么此时需要用带参数构造器,用到super

(4)如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表)  

public class Base {
	private int n1 = 100;
	private int n2 = 200;
	private int n3 = 300;

	public Base() {
		System.out.println("父类Base()构造器被调用...");
	}

	public Base(String name, int age) {
		System.out.println("父类Base(String name,int age)构造器被调用...");
	}

	public Base(String name) {
		System.out.println("父类Base(String name)构造器被调用...");
	}
	
}
public class Sub extends Base{
	public Sub() {
		super("smith",10);
		System.out.println("子类Sub()构造器被调用");
	}

	public Sub(String name,int age) {
		//1.调用父类的无参构造器,或者什么都不写,默认就是调用父类的无参构造器
		super();
		System.out.println("子类Sub(String name,int age)构造器被调用");
	}
	
	public Sub(String name){
		super("tom",30);
		System.out.println("子类Sub(String name)构造器被调用");
	}
}
public class Test {
	public static void main(String[] args) {
		System.out.println("======第三个对象======");
		Sub sub2 = new Sub("king",10);
	}
}

如果此时我们要调用父类的Base(String name)这个构造器,此时我们这样做

(5) super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)

(6) super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器

(7) java 所有类都是 Object 类的子类, Object 是所有类的基类

(8) 父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)

(9)子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制。 思考:如何让 A 类继承 B 类和 C 类?

【让A 继承 B, B 继承 C】

(10) 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系


四、继承本质分析(重要)

public class ExtendsTheory {

	public static void main(String[] args) {
		Son son = new Son();
	}
}

class GrandPa {
	String name = "大头爷爷";
	String hobby = "旅游";
}

class Father extends GrandPa {
	String name = "大头爸爸";
	int age = 39;
}

class Son extends Father {
	String name = "大头儿子";
}

当我们去new一个Son的时候,在内存里面发生了什么?

这里我们new一个Son,Son的父类是Father,而Father的父类是GrandPa,因为是先加载父类。在加载Son类信息的时候,会先查找父类,先加载父类信息,但是GrandPa也有一个父类,首先加载的是Object类,然后加载GrandPa,然后是Father类,最后加载Son。并且它们之间还有一种关联关系。

这些东西好了之后,就在堆中分配地址空间,那么这个空间里面到底有什么属性??

它首先会在GrandPa类分配属性,那么这里面由于是有值的,大头爷爷是放在常量池的,name的值用一个地址来表示,指向常量池,hobby也是。

接下来是Father类,我们会发现Father有name属性,GrandPa也有name属性,这样是不会冲突的,因为还会开辟一个空间,里面存放Father的属性值,name的属性值:大头爸爸,也是存放在常量值,然后用一个地址来指向常量池。

最后就是Son这个类了,又开辟一个空间,里面存放属性。


 那么当我们用son去访问name、age、hobby的时候,规则又是什么?

System.out.println(son.name);

这个本类中有,直接返回大头儿子

这时候我们就要按照查找关系来返回信息

(1) 首先查看子类是否有该属性
(2) 如果子类有这个属性,并且可以访问,则返回信息
(3) 如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息)
(4) 如果父类没有,就按照(3)的规则,继续找上级父类,直到Object

System.out.println(son.age);

这个Son类中没有,所以往上找自己的父类,父类里有age属性,那么这个age就是39。

System.out.println(son.hobby)

Son类没有,往Father找,Father也没有,去找GrandPa,这里面有,那么返回的就是旅游

public static void main(String[] args) {
	Son son = new Son();
	System.out.println(son.name); //大头儿子
	System.out.println(son.age); //39
	System.out.println(son.hobby); //旅游
}

那么现在又有一个问题,当Father类中的age属性,我们用private去修饰的时候,这时候new了对象过后,在内存里面还有没有这个age?

答案是这个age属性还是有的,但是我们不能去访问,私有只能在本类访问,我们可以写一个公有方法来使用

  


但是这里又会有一个有意思的问题,当Father类里的age属性值是私有的时候,此时GrandPa类有个public的age属性值,我们通过:son.age的时候,会不会跳过Father类这个私有的,到GrandPa里面去找这个公有的Age呢? 

答案是不会的,因为在Father里面已经找到了这个age,只是被设为私有不能访问而已。 


五、继承练习题

Computer.java

public class Computer {
	
	private String cpu;
	private int memory;
	private int disk;

	public Computer(String cpu, int memory, int disk) {
		this.cpu = cpu;
		this.memory = memory;
		this.disk = disk;
	}

	public String getCpu() {
		return cpu;
	}

	public void setCpu(String cpu) {
		this.cpu = cpu;
	}

	public int getMemory() {
		return memory;
	}

	public void setMemory(int memory) {
		this.memory = memory;
	}

	public int getDisk() {
		return disk;
	}

	public void setDisk(int disk) {
		this.disk = disk;
	}
	
	public String getDetails() {
		return "cpu="+cpu+" memory="+memory+" disk="+disk;
	}
}

PC.java

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("PC信息如下:");
		System.out.println(getDetails()+" brand="+brand);
	}
}

因为继承Computer,这里生成构造器的时候,会自动把父类的另外属性给你,子类的属性brand由自己初始化,父类由父类初始化,也就是super(cpu,memory,disk)

NotePad.java

public class NotePad extends Computer{
	
	private String color;
	public NotePad(String cpu, int memory, int disk, String color) {
		super(cpu, memory, disk);
		this.color = color;
	}
	public String getColor() {
		return color;
	}
	public void setColor(String color) {
		this.color = color;
	}
	
	public void printInfo(){
		System.out.println(getDetails()+" color="+color);
	}
}

Test.java 

public class Test2 {
	public static void main(String[] args) {
		PC pc = new PC("Intel",32,500,"IBM");
		pc.printInfo();
		NotePad notePad = new NotePad("AMD", 64, 1000, "white");
		notePad.printInfo();
	}
}

六、super关键字

6.1 基本介绍

super 代表父类的引用,用于访问父类的属性、方法、构造器


6.2 基本语法

public class A {
	
	public int n1=100;
	protected int n2=200;
	int n3=300;
	private int n4=400;
	public void test100(){}
	protected void test200(){}
	void test300(){}
	private void test400(){}
	
}
public class B extends A {
	
	public void hi() {
		System.out.println(super.n1+" "+super.n2+" "+super.n3);
	}
	
	public void ok(){
		super.test100();
		super.test200();
		super.test300();
	}
}

6.3 super 给编程带来的便利/细节


6.4 Super使用细节

6.4.1 演示访问方法的规则

这里就举个例子,这里有A类和B类

public class A {
	
	public void sum(){
		System.out.println("B类的sum()方法...");
	}

	public void cal(){
		System.out.println("A类的cal()方法...");
	}
	
}
public class B extends A {
	
	public void sum(){
		System.out.println("B类的sum()方法...");
		cal();
	}

}

我们想调用A类的cal()这个方法的时候,有3种办法:

(1)直接写cal(),它的查找顺序就是先从本类进行查找,如果没有则向上找父类,如果再没有还是继续往上找,直到Object

(2)可以直接写this.cal(),这个效果和直接写cal()一样,也是从本类开始找

(3)可以写成super.cal(),这个是没有查找本类的过程,而是直接进入到父类里面去找super的cal()方法,如果本类有cal()方法,这里不会去找。

为了演示第三种方法,此时我们在B类也增加一个cal方法

public void cal(){
	System.out.println("B类的cal()方法...");
}

这里输出的是B类的cal()方法。


6.4.2 演示访问属性的规则

属性的访问规则和方法也是一样的,这里也举2个类举例一下

public class A {
	
	public int n1=100;
	protected int n2=200;
	int n3=300;
	private int n4=400;
	
	public void test100(){}
	protected void test200(){}
	void test300(){}
	private void test400(){}
	
	public void sum(){
		System.out.println("B类的sum()方法...");
	}

	public void cal(){
		System.out.println("A类的cal()方法...");
	}
}
public class B extends A {

	public void hi() {
		System.out.println(super.n1 + " " + super.n2 + " " + super.n3);
	}

	public void ok() {
		super.test100();
		super.test200();
		super.test300();
	}

	public void sum() {
		System.out.println("B类的sum()方法...");
		super.cal();

		//演示访问属性的规则
		//n1 和 this.n1 查找的规则是
		//(1) 先找本类,如果有,则调用,此时本类是有的
		System.out.println(n1); //100
		System.out.println(this.n1); //100
		System.out.println(super.n1); //100
	}

	public void cal() {
		System.out.println("B类的cal()方法...");
	}
}

这时候,B类中没有n1属性,此时调用sum()会打印出3个100,如果本类中加个n1=888,此时输出的顺序就是888,888,100

主要注意的是用super调用的时候,会直接去调用父类的方法和属性,而不考虑本类的。



 

基于刚刚的例子,我们多加一个Base类  

public class Base {

	public int n1 = 999;
	public int age = 111;

	public void cal() {
		System.out.println("Base类的cal()方法...");
	}

	public void eat() {
		System.out.println("Base类中的eat()方法...");
	}
}

此时我们写在B类中写test方法

//编写测试方法
public void test(){
	System.out.println("super.n1="+super.n1); //直接定位到A类去找,输出:100
	super.cal(); //定位到A类去找,因为A类中有cal()方法
}

 如果A类中没有cal()方法,那么就会去Base类中去找cal()方法,这里遵循就近原则

七、Super和this的比较

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GodAiro

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值