Java-继承和多态

1.继承:

1.1继承的概念

  • 概念
    一个类继承另一个类,则称继承的类为子类,被继承的类为父类。
  • 主要解决的问题
    共性的抽取,实现代码复用

1.共性:都具备的特性。例如猫,狗都属于动物类
2.java中不支持多层继承 建议不超过3层继承 而且只能继承一个类
3.父类=基类 子类=派生类

1.2继承的语法

java中表示类之间继承关系,用extend关键字来实现,具体如下;

	修饰符 class 子类 extends 父类{
	//代码的实现
    } 

例:

//父类->动物类
class Animal{
	String name;
	int age;
	public void eat(){
		System.out.println(name + "正在吃饭");
	}
	public void sleep(){
		System.out.println(name + "正在睡觉");
	}
}
//子类->猫类 继承 动物类
class Cat extends Animal{
    //mew是Cat类独有的方法
	void mew(){
		System.out.println(name + "喵喵喵~~~");
	}
}
//子类->狗类 继承 动物类
class Dog extends Animal{
	//bark是Dog类独有的方法
	void bark(){
		System.out.println(name + "汪汪汪~~~");
	}
}
public class Test {
	public static void main(String[] args) {
		Dog dog = new Dog();
		// Dog类中并没有定义任何成员变量,name和age属性肯定是从父类Animal中继承下来的
		System.out.println(dog.name);
		System.out.println(dog.age);
		//Dog中没有eat()和sleep()方法,因为继承父类,所以可以调用父类的eat()和sleep()方法
		dog.eat();
		dog.sleep();
		//bark是Dog类独有的方法
		dog.bark();
	}
}

注:

1.子类继承父类,会把父类的成员变量和方法继承到子类。
2.子类要有独有的方法,否则继承无意义。

1.3父类成员的调用

1.3.1子类调用父类的成员变量

1.子类与父类的成员变量不同名

public class Base {
	int a;
	int b;
}
public class Derived extends Base{
	int c;
	public void method(){
		a = 10; // 访问从父类中继承下来的a 并修改
		b = 20; // 访问从父类中继承下来的b 并修改
		c = 30; // 访问子类自己的c 并修改
	}
}

2.子类与父类的成员变量同名

class Base {
	int a;
	int b;
	int c;
} 

class Derived extends Base{
	int a; // 与父类中成员a同名,且类型相同
	char b; // 与父类中成员b同名,但类型不同
	public void method(){
		a = 100; // 访问父类继承的a,还是子类自己新增的a?
		b = 101; // 访问父类继承的b,还是子类自己新增的b?
		c = 102; // 子类没有c,访问的肯定是从父类继承下来的c
		// d = 103; // 编译失败,因为父类和子类都没有定义成员变量d
	}
}

在子类方法中 或者 通过子类对象访问成员时:

  1. 如果访问的成员变量子类中有,优先访问自己的成员变量。
  2. 如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。
  3. 如果访问的成员变量与父类中成员变量同名,则优先访问自己的。
  4. 成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找。

1.3.2子类中访问父类的成员方法

1.成员方法名字不同

class Base {
	public void methodA(){
		System.out.println("Base中的methodA()");
	}
}
class Derived extends Base{
	public void methodB(){
		System.out.println("Derived中的methodB()方法");
}
public void methodC(){
	methodB(); // 访问子类自己的methodB()
	methodA(); // 访问父类继承的methodA()
	// methodD(); // 编译失败,在整个继承体系中没有发现方法methodD()
}
总结:成员方法没有同名时,在子类方法中或者通过子类对象访问方法时,则优先访问自己的,
     自己没有时再到父类中找,如果父类中也没有则报错。

1.成员方法名字相同

class Base {
	public void methodA(){
		System.out.println("Base中的methodA()");
	}
	public void methodB(){
		System.out.println("Base中的methodB()");
	}
}
class Derived extends Base{
	public void methodA(int a) {
		System.out.println("Derived中的method(int)方法");
	}
	public void methodB(){
		System.out.println("Derived中的methodB()方法");
	}
	public void methodC(){
		methodA(); // 没有传参,访问父类中的methodA()
		methodA(20); // 传递int参数,访问子类中的methodA(int)
		methodB(); // 直接访问,则永远访问到的都是子类中的methodB(),基类的无法访问到
	}
}

【说明】

派生类(子类):在保持原有类特性的基础上进行扩展,增加新功能,这样产生新的类,称派生类。

通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错。
通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用方法适传递的参数选择合适的方法访问,如果没有则报错

1.4supe和this关键字

  • super作用
    在子类方法中访问父类的成员变量和方法
    super.变量名:访问父类的普通成员变量
    super.方法名();调用父类的的普通成员方法
    super():调用父类的构造方法
  • this作用
    this是当前对象的引用,当前对象即调用实例方法的对象

:不能利用super调用父类的静态方法

this 会优先范围跟子类自己的,如果子类没有,访问的是父类的

class Base {
	int a;
	int b;
	public void methodA(){
		System.out.println("Base中的methodA()");
	}
	public void methodB(){
		System.out.println("Base中的methodB()");
	}
}

class Derived extends Base{
	int a; // 与父类中成员变量同名且类型相同
	char b; // 与父类中成员变量同名但类型不同
	// 与父类中methodA()构成重载
	public void methodA(int a) {
		System.out.println("Derived中的method()方法");
	} // 与父类中methodB()构成重写(即原型一致)
	public void methodB(){
		System.out.println("Derived中的methodB()方法");
	}
	public void methodC(){
		// 对于同名的成员变量,直接访问时,访问的都是子类的->就近原则
		a = 100; // 等价于: this.a = 100;
		b = 101; // 等价于: this.b = 101;
		// 注意:this是当前对象的引用
		// 访问父类的成员变量时,需要借助super关键字
		// super是获取到子类对象中从父类继承下来的部分
		super.a = 200;//修改的父类成员变量数据
		super.b = 201;//修改的父类成员变量数据
		// 父类和子类中构成重载的方法,直接可以通过参数列表区分清访问父类还是子类方法
		methodA(); // 没有传参,访问父类中的methodA()
		methodA(20); // 传递int参数,访问子类中的methodA(int)
		// 如果在子类中要访问重写的基类方法,则需要借助super关键字
		methodB(); // 直接访问,则永远访问到的都是子类中的methodA(),父类类的无法访问到
		super.methodB(); // 访问父类的methodB()
	}
}

注:

  1. 只能在非静态方法中使用
  2. 在子类方法中,访问父类的成员变量和方法。

1.5super和this的区别

  1. this是当前对象的引用,当前对象即调用实例方法的对象
  2. super相当于是子类对象中从父类继承下来部分成员的引用
  3. 在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性
  4. 在构造方法中:this()用来访问本类构造方法,super()用于调用父类构造方法,两种调用不能同时在构造方法中出现
  5. 构造方法中一定会存在super()的调用,用户没有写编译器也会增加,但事实this()用户不写则没有
  6. super this不能同时出现,只能出现一次(在一个方法中) super this 必须写在方法的第一行
  7. 当子类构造完成之前,先帮助父类完成初始化

1.5.1代码块的执行顺序

		静态代码块>实例代码块>构造代码块

1:父类静态代码块优先于子类代码块执行,且最早执行
2:父类实例代码块和父类构造方法紧接着再执行
3:子类的实例代码块和子类的构造方法紧接着再执行
4:第二次实例化子类对象时,父类和子类的静态代码块都不会再执行

1.6子类构造方法

public class Base {
	public Base(){
		System.out.println("Base()");
	}
}
public class Derived extends Base{
	public Derived(){
	// super(); // 注意子类构造方法中默认会调用基类的无参构造方法:super(),
	// 用户没有写时,编译器会自动添加,而且super()必须是子类构造方法中第一条语句,
	// 并且只能出现一次
		System.out.println("Derived()");
	}
}
public class Test {
	public static void main(String[] args) {
		Derived d = new Derived();
	}
}
结果打印:
Base()
Derived()


在子类构造方法中,并没有写任何关于基类构造的代码,但是在构造子类对象时,先执行基类的构造方法,然后执行子类的构造方法,因为:子类对象中成员是有两部分组成的,基类继承下来的以及子类新增加的部分 。父子父子肯定是先有父再有子,所以在构造子类对象时,先要调用基类的构造方法,将从基类继承下来的成员构造完整,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整 。
注:

  • 在子类构造方法第一行默认有隐含的super()调用,即调用基类构造方法。
  • 在子类构造方法中,super(…)调用父类构造时,必须是子类构造函数中第一条语句。
  • super(…)只能在子类构造方法中出现一次,并且不能和this同时出现

1.7访问修饰符

在这里插入图片描述

2.多态

2.1多态的概念

  • 概念
  • 多态的概念:指多种形态,就是去完成某个行为,当不同的对象去完成时会产生出不同 的状态。

例如:狗和猫都去吃饭,狗吃狗粮,猫吃猫粮。

2.2多态的实现条件

1. 必须在继承体系下
2. 子类必须要对父类中方法进行重写
3. 通过父类的引用调用重写的方法

例:

class Animal {
	String name;
	int age;
	public Animal(String name, int age){
	this.name = name;
	this.age = age;
	}
	public void eat(){
		System.out.println(name + "吃饭");
		}
	}
	class Cat extends Animal{
		public Cat(String name, int age){
			super(name, age);
		} 
	public void eat(){
		System.out.println(name+"吃小鱼干");
		}
	}
	public class Dog extends Animal {
		public Dog(String name, int age){
			super(name, age);
		} 
	public void eat(){
		System.out.println(name+"吃狼骨头");
	}
} 
public class Test {
// 编译器在编译代码时,并不知道要调用Dog 还是 Cat 中eat的方法
// 等程序运行起来后,形参a引用的具体对象确定后,才知道调用那个方法
// 注意:此处的形参类型必须时父类类型才可以
	public static void eat(Animal a){
		a.eat();
	}
	public static void main(String[] args) {
		Cat cat = new Cat("元宝",2);//传入参数
		Dog dog = new Dog("小七", 1);//传入参数
		eat(cat);
		eat(dog);
	}
} 
运行结果:
元宝吃小鱼干
元宝正在睡觉
小七吃狼骨头
小七正在睡觉

2.3重写(发生再继承)

2.3.1重写的概念

  • 概念
    重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程
    进行重新编写, 返回值和形参都不能改变。即**外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。

2.3.2重写的规则

  • 子类在重写父类的方法时,一般必须与父类方法原型一致: 返回值类型 方法名 (参数列表) 要完全一致
  • 被重写的方法返回值类型可以不同,但是必须是具有父子关系的。即继承关系
  • 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被public修饰,则子类中重写该方法就不能声明为 protected
  • 父类被static、private修饰的方法、构造方法都不能被重写。
    -重写的方法,可以使用@Override 注解来显式指定,有了这个注解能帮我们进行一些合法性校验。 例如不小心将方法名字拼写错了 (比如写成 aet),那么此时编译器就会发现父类中没有 aet方法,就会编译报错,提示无法构成重写。

2.3.3重写和重载的区别

重写:
1:方法名称相同
2:返回值相同//具有父子关系的可以不相同
3:参数列表相同(数据类型、个数、顺序)

  • 子类的访问权限要>=父类的权限
  • private修饰的方法、final修饰的方法、static修饰的方法 不可以重写
  • 当在父类中调用子类重写方法的时候 此时会调用子类重写的方法

重载:
1:方法名称相同
2:参数列表不同数据类型、个数、顺序)
3:返回值没有要求

重写和重载的区别

区别点重写重载
参数列表不能修改必须修改
返回类型不能修改(除非父子关系)可以修改
访问修饰符可以降低限制可以修改

2.4向上、向下转型

2.4.1向上转型

  • 概念
    创建一个子类对象,将其当成父类对象来使用。
  • 语法格式
    父类类型 对象名= new 子类类型();
    例:Animal animal = new Cat();
class Animal {
	String name;
	int age;
	public void eat(){
	System.out.println(name + "吃饭");
	}
}
class Cat extends Animal{
	public void eat(){
	System.out.println(name + "吃小鱼干");
	}
}
class Dog extends Animal{
	public void eat(){
	System.out.println(name + "吃狗粮");
	}
}
public class Test {
	// 2. 方法传参:形参为父类型引用,可以接收任意子类的对象
	public static void eatFood(Animal a){
		a.eat();
	} 
	// 3. 作返回值:返回任意子类对象
	public static Animal buyAnimal(String var){
		if("狗".equals(var) ){
			return new Dog("狗狗",1);
		}else if("猫" .equals(var)){
			return new Cat("猫猫", 1);
		}else{
			return null;
		}
	}
	public static void main(String[] args) {
		Animal cat = new Cat("元宝",2); // 1. 直接赋值:子类对象赋值给父类对象
		Dog dog = new Dog("小七", 1);
		eatFood(cat);
		eatFood(dog);
		Animal animal = buyAnimal("狗");
		animal.eat();
		animal = buyAnimal("猫");
		animal.eat();
	}
}

优缺点:
向上转型的优点:让代码实现更简单灵活。
向上转型的缺陷:不能调用到子类特有的方法。

2.4.2向下转型

  • 概念
    为了重新获得因为向上转型而丢失的子类独有的特性
  • 格式
    父类类型 父类对象 = 子类实例;
    子类类型 子类对象 = (子类)父类对象;
    例 Animal a = new Dog();
    Dog b = (Dog) a;

释:
向下转型不安全,用的比较少,一般情况用instanceof关键字来提高向下转型的安全

2.4.3 instanceof

  • 功能
    判断一个对象是否是某个类(或接口)的实例
    例 对象 instanceof 类(接口)
	//如果成立返回 true   否则 flase
if(对象 instanceof 类(接口)){
	//运行这里的代码
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值