8. 继承和多态

8.1 继承的用法

  • 通过extends关键字可以实现类与类的继承
class 子类名 extends 父类名{}
class boys extends person{}
class girls extends person{}

继承使得定义一个通用的类(父类),之后扩充该类为一个更加特定的类(子类)

8.2 继承的利弊

  • 提高了代码的复用性:多个相同的成员可以放到同一个类中
  • 提高了代码的行为属性:如果功能的代码需要修改,修改一处即可
  • 让类与类之间产生了关系,是多态的前提:其实这也是继承的一个弊端, 类的耦合性很强

开发原则:低耦合,高内聚
耦合:类与类的关系
内聚:就是自己完成某件事情的能力

8.3 继承特点

  • java只支持单继承,不支持多继承,即一个类只能有一个父类
  • 子类只能继承父类所有非私有的成员
  • 子类不能继承父类的构造函数

子类函数中访问变量步骤:

子类局部范围->子类成员范围->父类成员范围

8.4 super关键字

关键字super代指所在类的父类,用于调用父类的普通函数和构造函数

作用:

  1. 调用父类构造方法
public class Test {
	public static void main(String[] args) {
		//创建子类对象,初始化参数
		student s1 = new student(19,"赵文耀",12);
		System.out.println(s1.getAge());//父类age的访问器函数也被继承
		System.out.println(s1.getName());//父类name的访问器函数也被继承
		System.out.println(s1.getGrade());
	}
}
//父类
class person{
	private int age;
	private String name;
	public person() {
		
	}
	public person(int age, String name) {
		this.age = age;
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public String getName() {
		return name;
	}
}
//子类
class student extends person{
	private int grade;
	public student(){
		
	}
	public student(int age,String name,int grade) {
		super(age,name);//调用父类person的构造函数初始化年龄,姓名
		this.grade = grade;
	}
	public int getGrade() {
		return grade;
	}
	public void setGrade(int grade) {
		this.grade = grade;
	}
}

注意

  • super()调用父类无参构造方法(若上例使用,则结果为0,null,12)
  • 要调用父类构造函数必须使用super关键字
  • 语句super()必须出现在子类构造函数的第一句
  1. 调用父类方法
super.方法名(参数)
  1. 构造方法链

构造一个类的实例时,将会调用沿着继承链的所有父类构造方法

public class boy extends man{
	public static void main(String[] args) {
		new boy();
	}
	public boy() {
		//super()隐式调用
		System.out.println(4);
	}
}
class man extends person{
	public man(){
		//super(),隐式调用
		this(2);
		System.out.println(3);
	}
	public man(int a) {
		System.out.println(a);
	}
}
class person{
	public person() {
		System.out.println(1);
	}
}

输出结果: 1
2
3
4
在这里插入图片描述

8.5 方法重写

重写一个方法,要在子类中使用和父类一样的签名以及一样的返回值类型,对该方法进行重新定义

public class Test {
	public static void main(String[] args) {
		man m = new man();
		m.eat();//吃米饭
		m.run();//跑马拉松
		person.run();//跑步
	}
}
class person{
	//公有
	public void eat() {
		System.out.println("吃饭");
	}
	//静态
	public static void run() {
		System.out.println("跑步");
	}
	//私有
	private void talk() {
		System.out.println("说话");
	}
}
class man extends person{
	//重写eat()
	@Override
	public void eat() {
		System.out.println("吃米饭");
	}
	//重写run()
	public static void run() {
		System.out.println("跑马拉松");
	}
}

注意事项:

  • @Override(重写标注)
    表示被标注的方法必须重写父类的一个方法
    用标注就要重写,否则报错
    不用标注也可省略不写

  • 父类中私有方法不能被重写

  • 静态方法不能被覆盖,若在子类中重新定义,则父类静态方法被隐藏, 用person.run();(父类名.静态方法名)调用

8.6 多态

父类的变量可以指向子类对象

public class Test {
	public static void main(String[] args) {
		person p1 = new person();//创建父类对象
		p1.write(new man());//调用父类write(),函数参数为子类对象
	}
}
class person{
	private int age;
	public person(){
		
	}
	public person(int age) {
		this.age = age;
	}
	public int getAge() {
		return age;
	}
	public void write(man m){//父类的变量可以指向子类对象
		System.out.println(m.getAge()+m.getName());
	}
}
class man extends person{
	private String name;
	public man(){
		
	}
	public man(int age, String name) {
		super(age);
		this.name = name;
	}
	public String getName() {
		return name;
	}
}

8.7 动态绑定

方法沿着继承链在多个类中实现,JVM决定运行时调用那个方法

  • 声明类型
    (变量p1的声明类型为person类型)
person p1 = new man();
  • 实际类型(变量引用的对象的实际类)
    变量p1的实际类型为man类型

  • 动态绑定机制

public class StringTest {
	public static void main(String[] args) {
		m(new Object());//调用实际类型Object类的toString()
		m(new person());//调用实际类型persont类的toString()
		m(new man());//调用实际类型man类的toString()
		Object o = new boy();
		m(o)//;调用实际类型boy类的toString()(继承自man)
	}
	public static void m(Object x) {//引用变量的声明类型为Object
		System.out.println(x.toString());
	}
}
class boy extends man {
	
}	
class man extends person{
	@Override
	public String toString(){
		return "man";
	}
}
class person extends Object{
	@Override
	public String toString(){
		return "person";
	}
}

结果:
java.lang.Object@15db9742
person
man
man
引用变量的声明类型决定编译时匹配那个方法

引用变量的实际类型决定运行时JVM动态绑定方法的实现

8.8 对象转换

  • 向上转换
    子类实例总是父类实例
Object o = new boy();//隐式转换,正确(boy类是Object的实例)
man m = o;//错误
  • 向下转换
    父类实例转换为子类变量
man m = (man)o;

注意
必须确保转换的对象是一个子类实例

  • instanceof关键字
    判断对象是否是另一个对象的实例
 Object O = new person();
 if(O instanceof person){//判断父类对象是否是person类
 	(person)O;//转换为person类
 }

注意

  1. 对象成员访问运算符.优先于类型转换运算符,调用函数时注意加()
((person)O).eat();
  1. 基本类型转换返回一个新的值
double a = 5.5;
int newa = (int)a; 
  1. 转换一个对象引用使指向同一个对象
person p = new man();
man m = (man)p;//引用变量p,m指向同一个对象

8.9 ==运算符还可以检测两个引用变量是否指向同一 个对象

8.10 方法权限

数据和方法的可见性

类中成员修饰符 同类中 同类不同包 子类中 不同包中
public 1 1 1 1
protected 1 1 1 0
(default) 1 1 0 0
private 1 0 0 0

猜你喜欢

转载自blog.csdn.net/weixin_43498251/article/details/87928117
今日推荐