【从零开始的Java开发】1-3-4 Java继承:继承(概念、特点、实现、初始化顺序)、方法重写、访问修饰符(分类、作用、对方法重写的影响)、super关键字

继承的概念和特点

不同的类中可能会有重复的代码。
在这里插入图片描述
我们抽取一个公共的父类:
在这里插入图片描述
这样子类继承了父类后,只需要写自己不同的代码即可:
在这里插入图片描述
特点:

  1. 利于代码复用
  2. 缩短开发周期

继承:

  • 一种类与类之间的关系
  • 使用已存在的类的定义作为基础建立新类
  • 已存在的类:父类(基类)
  • 新类:子类(派生类)
  • 新类的定义可以增加新的数据或功能,也可以用父类的功能,但不能选择性地基础父类

继承的实现

关键字:extends
语句:子类名称 extends 父类名称
在这里插入图片描述
一个子类只能继承一个父类。
子类只能继承父类中非私有的成员。
子类自己的私有成员,它的兄弟类是无法访问的。

如:
Animal类中有privatename属性和publicgetName方法,则当Cat类继承了Animal类,它只能调用getName,而不能直接访问name

在这里插入图片描述
用eclipse自带的快捷键继承:在Choose a type中写上我们想要继承的类即可。
在这里插入图片描述
则新建的类的自带的语句:

public class Dog extends Animal {

}

Animal类:

package com.animal;

public class Animal {
	private String name;//昵称
	private int month;//月份
	private String species;//品种
	
	public Animal() {
		
	}
	
	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name=name;
	}
	
	public int getMonth() {
		return month;
	}
	
	public void setMonth(int month) {
		this.month=month;
	}
	
	public String getSpecies() {
		return species;
	}
	
	public void setSpecies(String species) {
		this.species=species;
	}
	
	public void eat() {
		System.out.println(this.getName()+" is eating!");
	}
}

Cat类:

package com.animal;

public class Cat extends Animal{
	
	private double weight;//体重
	
	public Cat() {
		
	}

	public double getWeight() {
		return weight;
	}

	public void setWeight(double weight) {
		this.weight = weight;
	}
	
	public void run() {
		System.out.println("品种:"+this.getSpecies()+" "+this.getName()+" is running!");
	}
	
	
	
}

Dog类:

package com.animal;

public class Dog extends Animal {
	private String sex;//性别
	
	public Dog() {
		
	}

	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}
	
	public void sleep() {
		System.out.println("月份:"+this.getMonth()+" "+this.getName()+" is sleeping");
	}
}

测试类:

public static void main(String[] args) {
		// TODO Auto-generated method stub
		Cat one = new Cat();
		one.setName("A");
		one.setSpecies("中华田园猫");
		one.eat();
		one.run();
		System.out.println("-----------------------");
		Dog two = new Dog();
		two.setName("B");
		two.setMonth(1);
		two.eat();
		two.sleep();
	}

输出:

A is eating!
品种:中华田园猫 A is running!
-----------------------
B is eating!
月份:1 B is sleeping

方法的重写

关于方法重载:

  1. 同一个类中
  2. 方法名相同,参数列表不同(参数顺序,个数,类型)
  3. 方法返回值,访问修饰符任意
  4. 与方法参数名无关

关于方法重写:

  1. 在子类中定义,重写父类的方法
  2. 方法名,参数列表(参数顺序,个数,类型)要与父类完全一致
  3. 当父类的方法返回值是void或基本数据类型时,子类的要与父类的完全相同;若是引用类型时,子类的返回值可以是父类或是它的子类。

父类Animal中有:

public void eat() {
		System.out.println(this.getName()+" is eating!");
	}

在子类Dog中进行方法的重写:

public void eat() {
		System.out.println(this.getName()+"自己的eat");
	}

在测试类中调用:

Dog two = new Dog();
two.eat();

输出:

B自己的eat

一个关于返回值不同的方法重写
在父类Anmial中有一个void类型的jump方法:

public void jump() {
		System.out.println("jump!");
	}

在子类Dog中写一个int类型的jump方法:

public int jump() {
		System.out.println("Dog Jump!");
		return 0;
	}

则:报错:这个返回值类型与父类Animal中的jump 不兼容
在这里插入图片描述
但是,若把父类中的void改成int,Dog类中的jump方法就不会报错了。

那么这是否意味着子类的方法重写的返回值一定要与父类的相同呢?我们这里再做一个测试:

令父类的jump方法返回值为Animal
ps:这样写只为了测试代码语法是否合法,这段代码其实本身没有逻辑。

public Animal jump() {
		System.out.println("jump!");
		return new Animal();
	}

子类Dog的jump也改成返回值为Animal的:

public Animal jump() {
		System.out.println("Dog Jump!");
		return new Animal();
	}

确实没有报错!
在这里插入图片描述
我们再把子类Dog的jump改成返回值为Dog的——也没有报错
在这里插入图片描述
改成Cat
在这里插入图片描述
都没有报错!

原因:
若父类的方法返回值类型是基本数据类型或void时,则子类的方法返回值要与父类的相同
若父类的方法返回值类型是引用数据类型,则子类的方法返回值可以是其引用数据类型的父类或子类

注意:在子类中,可以定义与父类重名的属性。

访问修饰符的分类及作用

  • 公有:public
  • 私有:private
  • 受保护的:protected
  • 默认

我们从本类、同包、子类、其他 这四个方面来认识一下上述的访问修饰符。

private
private只允许在本类中进行访问。

如:父类Animal中有private类型的三个属性:

private String name;//昵称
private int month;//月份
private String species;//品种

我们在子类中定义一个Animal类的对象,然后通过对象调用属性——会发现调用不到Animal类的属性。
在这里插入图片描述
public
public允许在任意位置进行访问。

protected

  1. 允许在当前类访问
  2. 允许在同包子类中访问
  3. 允许挎包子类中访问
  4. 允许同包非子类中访问
  5. 不允许挎包非子类访问

即:

  1. 允许当前类
  2. 允许同包
  3. 允许子类
  4. 不允许挎包非子类

访问。

——允许在当前类访问:类内属性为protected,且其getter类和setter类没有报错:
在这里插入图片描述
——允许在同包子类中访问:在同包的子类Cat中可以调用到Animal的属性namemonthspecies
在这里插入图片描述
——允许挎包子类中访问:把Cat移动到另一个包下,也可以访问。

——不允许挎包非子类访问:在另一个包中的test类(非子类)定义Animal对象,发现访问不到protected的属性:
在这里插入图片描述
——允许同包非子类中访问:把test挪到同包下,就可以访问。

默认
允许在当前类、同包子类/非子类调用;
不允许挎包子类/非子类调用。

即:

  • 允许当前类
  • 允许同包
  • 不允许挎包

访问。

总结

在这里插入图片描述

访问修饰符对方法重写的影响

当子类进行方法重写时,访问修饰符允许改变:子类的重写的方法的访问范围要大于等于父类的访问范围。

举个例子:
父类:public
子类:只能是public

父类:protected
子类:可以protected或public

即,子类的访问权限只能比父类的大(等)。
无端联想:父母希望孩子比自己更好,而不是更差。

继承的初始化顺序

父类Animal:各种访问权限的属性,各种访问权限的静态属性,代码块与构造方法。

public class Animal {
	private String name="A";//昵称
	protected int month=1;//月份
	String species="中华田园猫";//品种
	public int temp=15;
		
	private static int st1=22;
	public static int st2=23;
	
	//静态代码块
	static {
		System.out.println("我是父类的静态代码块!");
	}
	
	//构造代码块
	{
		System.out.println("我是父类的构造代码块!");
	}
	
	public Animal() {
		System.out.println("我是父类的无参构造方法!");
	}

}

子类Cat:

public class Cat extends Animal{
	
	private double weight=6;//体重
	public static int st3=44;
	public int temp=12;
	
	//静态代码块
	static {
		System.out.println("我是子类的静态代码块!");
	}
	
	//构造代码块
	{
		System.out.println("我是子类的构造代码块!");
	}
	
	public Cat() {
		System.out.println("我是子类的无参构造方法!");
	}

}

测试类语句:

Cat one=new Cat();
System.out.println(one.temp);

输出:

我是父类的静态代码块!
我是子类的静态代码块!
我是父类的构造代码块!
我是父类的无参构造方法!
我是子类的构造代码块!
我是子类的无参构造方法!
12

调试一下,得知执行顺序:

  1. 加载父类:执行父类的静态赋值语句和静态代码块
  2. 加载子类:执行子类的静态赋值语句和静态代码块
  3. ——类在进行加载的时候会优先加载父类的静态信息
  4. 在new语句会先找父类的构造方法(然后找父类的父类——object类),再找子类
  5. 执行输出temp语句

ps:object是所有类的父类。

执行顺序:子类——父类——父类的父类…
在这里插入图片描述
省流:继承后的初始化顺序

  1. 父类静态成员
  2. 子类静态成员
  3. 父类对象构造
  4. 子类对象构造

对于静态的赋值语句和静态代码块:
访问修饰符不影响成员加载顺序,跟书写位置有关

即:这里是赋值语句先加载,静态代码块后加载。

public static int st2=23;
	
//静态代码块
static {
	System.out.println("我是父类的静态代码块!");
}

而:这里是静态代码块先加载,赋值语句后加载。

//静态代码块
static {
	System.out.println("我是父类的静态代码块!");
}

public static int st2=23;

super关键字

super:父类对象的引用。

可以通过super去访问父类可以被子类派生的任意成员

子类如果想调用父类的构造方法——直接调用会报错:Animal不是Cat类的方法:父类的构造不允许被继承、不允许被重写、但会影响子类的实例化过程
在这里插入图片描述

若父类有两个构造方法(带参,无参),子类也有两个构造方法(带参,无参),那么在子类实例化的时候会如何选择呢?

父类:

public Animal() {
		System.out.println("父类-无参构造");
	}
	
public Animal(String name,int month) {
	System.out.println("父类-双参构造");
}

子类:

public Cat(String name,int month) {
		System.out.println("子类-双参构造");
	}
	
public Cat() {
	System.out.println("子类-无参构造");
}

测试类语句:Cat one=new Cat("A",2);

输出:

父类-无参构造
子类-双参构造

原因:
在子类构造方法中,没有显式标注的时候,默认调用父类的无参构造方法。

因此,若我们把父类的无参构造方法注释掉,则子类的构造方法会直接报错:Animal的无参构造方法并没有被定义
在这里插入图片描述

如何在子类构造方法中调用父类的带参构造?
答案:super

如果想调用双参的父类构造函数,则:super(name,month);传入参数即可,如:

public Cat(String name,int month) {
		super(name,month);
		System.out.println("子类-双参构造");
	}

测试类:Cat one=new Cat("A",2);
输出:

父类-双参构造
子类-双参构造

若没有传入参数,super()

public Cat(String name,int month) {
		super();
		System.out.println("子类-双参构造");
	}

则输出:

父类-无参构造
子类-双参构造

注意:构造方法的调用必须放在构造方法里。

如:若想在其他方法中用super调用构造函数——报错:
在这里插入图片描述

总结:

代表父类引用:

  • 访问父类成员方法 super.print()
  • 访问父类属性 super.name
  • 访问父类构造方法 super()
  • 子类构造必须调用父类构造方法
  • 若子类的构造方法中没有显式标注,则系统默认调用父类无参构造放啊
  • 可以通过super()调用父类允许被访问的其他构造方法
  • super()必须放在子类构造方法有效代码第一行

super VS this

在这里插入图片描述

构造方法调用时,super和this不能同时出现
在这里插入图片描述

总结

继承

  1. 一种类与类之间的关系
  2. 使用已存在的类的定义作为基础建立新类
  3. 新类的定义可以增加新的数据或功能,也可以用父类的功能,但不能选择性地继承父类
  4. 满足 A is a B的关系就可以继承,如:学生是人,老师是人,则学生和老师都可以继承 人这一父类 。

特点

  1. 利于代码复用
  2. 缩短开发周期

语法

  1. 使用extends实现封装
  2. 单一继承,只能有一个父类
  3. 子类也叫派生类
  4. 父类也叫基类,超类

如:

class Dog extends Animal{
	//子类特有的属性和方法
}

继承后的初始化顺序

  1. 父类静态成员
  2. 子类静态成员
  3. 父类对象成员
  4. 子类对象成员

通过super关键字访问父类相关成员

  • 访问父类成员方法 super.print()
  • 访问父类属性 super.name
  • 访问父类构造方法 super()

super

  • 子类构造必须调用父类构造方法
  • 若子类的构造方法中没有显式标注,则系统默认调用父类无参构造放啊
  • 可以通过super()调用父类允许被访问的其他构造方法
  • super()必须放在子类构造方法有效代码第一行
  • 如果子类构造方法中既没有显式标注,而父类有没有无参构造方法,则编译出错

super VS this

  • super 父类的引用
  • this 当前对象的引用
  • 构造方法调用时,super和this不能同时出现

方法重写 VS 方法重载
在这里插入图片描述

访问修饰符
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

karshey

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

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

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

打赏作者

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

抵扣说明:

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

余额充值