目录
Java — 继承
一.为什么要用继承?
二.继承的详细概念
三.继承的使用
四.父类成员访问
1.子类访问父类的成员变量
2.子类访问父类的成员方法
3.super关键字
五.子类的构造方法
子类如何使用构造方法
六.子类与父类中代码的执行顺序
Java —多态
一.多态的概念
二.多态的使用
1.实现多态需要的条件
2.向上转型和向下转型
3.重写
4.重写和重载的区别
这里我们举一个例子:猫和狗,他们都是动物
首先定义一个小狗类和一个小猫类
//小狗
class Dog {
public String name;
public int age;
public void eat() {
System.out.println(name+"正在吃饭!");
}
public void wangwnag() {
System.out.println(name + "正在汪汪叫");
}
}
//猫猫
class Cat {
public String name;
public int age;
public void eat() {
System.out.println(name+"正在吃饭!");
}
public void miaomaio() {
System.out.println(name + "正在喵喵叫");
}
}
//实例化对象
public class Test {
public static void main(String[] args) {
//实例化一个狗的对象
Dog dog = new Dog();
dog.name = "坦克";
dog.eat();
dog.wangwnag();
//实例化一个猫的对象
Cat cat = new Cat();
cat.name = "咪咪";
cat.eat();
cat.miaomaio();
}
}
写到这里,我们会发现这个代码会有很多相似重复的属性,比如小狗和小猫有 name,age,eat(),写起来很繁琐,这时我们就可以将这些共性给抽取出来,放在同一个类当中,即用继承的思想进行共性抽取,实现代码复用。
继承是一个对象获取父对象的所有属性和行为的机制。
继承作用:共性的抽取,实现代码复用
我们将抽取的共性放到一个类中,即父类,一脚基类或者超类,然后由它的子类,也叫派生类,来继承父类的成员变量和方法,如下图:
继承以后子类会复用父类中的成员,从逻辑和业务上来说子类必须在自己的类中可以新加成员,体现出与父类的不同,虽然代码不会报错,但是体现不出写继承的意义。
首先我们将这些共性放在Animal中,定义一个Animal类
class Animal {
public String name;
public int age;
public void eat() {
System.out.println(name+"正在吃饭!");
}
}
这里小狗和小猫要继承Animal类中的变量和方法,我们需要采用以下的格式:
修饰符 子类类名 extends 父类 {
}
class Dog extends Animal {
public void wangwnag() {//子类中新加的成员
System.out.println(name + "正在汪汪叫");
}
}
class Cat extends Animal {
public void miaomaio() {//子类中新加的成员
System.out.println(name + "正在喵喵叫");
}
}
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "坦克";
dog.eat();
dog.wangwang();
Cat cat = new Cat();
cat.name = "咪咪";
cat.eat();
cat.miaomaio();
}
}
注意:子类下面也可以有子类继承,也就是孙子类,比如中华田园犬可以继承狗类,但是我们希望继承一般不会超过三层。
1).当子类与父类的成员变量不同时,子类继承父类的成员变量
class Base {
public int a;
public int b;
}
class Derived extends Base {
public int c = 9;
public void func() {
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
}
public class Test2 {
public static void main(String[] args) {
Derived derived = new Derived();
derived.func();
}
2).当子类与父类的成员变量相同时,优先访问子类,就近原则
class Base {
public int c = 199;
}
class Derived extends Base {
public int c = 9;
public void func() {
System.out.println(c);
}
}
public class Test2 {
public static void main(String[] args) {
Derived derived = new Derived();
derived.func();
}
1).当 子类的方法名与父类的方法名相同时,同成员变量,子类可以使用父类的成员方法。
class Base {
public void methodBase() {
System.out.println("base");
}
}
class Derived extends Base {
public void methodDerived() {
System.out.println("Derived");
}
public void func() {
//子类有拿子类的,子类没有拿父类
methodDerived();
methodBase();
}
}
public class Test2 {
public static void main(String[] args) {
Derived derived = new Derived();
derived.func();
}
}
2).当子类与父类的成员方法同名时,这时候和成员变量一样,都是用就近原则
class Base {
public void methodBase() {
System.out.println("base");
}
}
class Derived extends Base {
public void methodBase() {
System.out.println("Derived");
}
public void func() {
//子类有拿子类的,子类没有拿父类
methodBase();
}
}
public class Test2 {
public static void main(String[] args) {
Derived derived = new Derived();
derived.func();
}
}
如果方法名和变量名相同,子类会默认放任自己的成员变量和成员方法。
那么,如果我们想要访问父类中同名的成员方法和成员变量,这是只要我们加上super关键字。
我们将前面代码中提到过的同名的成员变量c和成员方法methodBase()前面加上super关键字,
public void func() {
super.methodBase();
System.out.println(super.c);
}
这时输出访问的是父类的成员,结果如下:
注意:super/不能再静态方法当中使用。
子类对象构造时,需要先调用基类构造方法,然后执行子类的构造方法。
class Animal {
public String name;
public int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
System.out.println("带有两个参数的构造方法");
}
}
class Dog extends Animal {
public Dog(String name, int age) {
//显示调用父类的构造方法,帮助父类的成员变量初始化
super(name,age);
}
public void wangwang() {
System.out.println(name + "正在汪汪叫");
}
}
public class Test {
public static void main(String[] args) {
Dog dog = new Dog("小名",1);
dog.wangwang();
}
}
注意:
1)因为先有父再有子,所以我们要先,造父类,在狗早子类对象的时候,我们要先调用基类的构造方法,将从基类继承下来的成员构造完整 ,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整 。
2)若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用基类构造方法
3) 如果父类构造方法是带有参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败。
5)在子类构造方法中,super(...)调用父类构造时,必须是子类构造函数中第一条语句。
5) super(...)只能在子类构造方法中出现一次,并且不能和this同时出现
我们先编写几个代码块
class Animal {
static {//父类静态方法
System.out.println("Animal static{}");
}
{
System.out.println("Animal{}");
}
public Animal() {父类的构造方法
System.out.println("Animal()");
}
}
class Dog extends Animal {
static {//子类的静态方法
System.out.println("Dog static()");
}
{
System.out.println("Dog{}");
}
public Dog() {//子类的构造方法
System.out.println("Dog()");
}
}
public class Test {
public static void main(String[] args) {
Dog dog1 = new Dog();
}
}
执行结果:
由此可见,代码的优先级:父类 > 子类,静态 >实例代码 > 构造 ,当时静态的只执行一次,如果实例化一个dog2,那么静态方法就不会再执行
通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同 的状态,也就是,当父类引用 引用的对象不一样的时候,表现出的行为是不一样的
1)必须在继承体系下;
2)子类必须要对父类的方法进行重写;
3)通过父类的引用调用重写的方法。
向上转型:创建一个子类对象,将其当成父类对象来使用。
书写格式1.
父类类型 对象名 = new 子类类型()
//示例
public static void main(String[] args) {
Animal animal1 = new Dog();
}
书写格式2
public static void 方法名(父类类型 对象名){} //方法的传参
//示例
public static void func(Animal animal){
}
public static 父类类型 方法名(){ return new 子类类型()} //方法返回
//示例
public static Animal func2() {
return new Dog();
}
优点:让代码实现更简单灵活
缺点:不能调用到子类特有的方法
重写是指子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程进行重新编写,子类可以根据需求重写父类中有的方法,比如狗和猫吃东西,狗吃狗粮,猫吃猫粮,我们就可以在继承中重写父类中的eat()方法,返回值和形参都不能改变。即外壳不变,核心重写。
在运行时main方法中会自动调用子类重写的方法,这种称为动态绑定。
class Animal {
public String name;
public int age;
public void eat() {
System.out.println(name + "正在吃饭");
}
}
class Dog extends Animal {
public void wangawang(){
System.out.println(name + "正在汪汪叫");
}
@override //重写方法的注释
public void eat() { //重写
System.out.println(name + "正在吃狗粮!");
}
}
class Cat extends Animal {
public void mew() {
System.out.println(name + "喵喵叫");
}
@override //重写方法的注释
public void eat() {
System.out.println(name + "正在吃猫粮");
}
}
public class Test2 {
public static void main(String[] args) {
//向上转型
Animal animal1 = new Dog();
animal1.name = "旺财";
animal1.eat();
Animal animal2 = new Cat();
animal2.name = "咪咪";
animal2.eat();
}
}
在这里我们用快捷方法生成重写:
注意:
1)private修饰的方法不能被重写
2)static修饰的方法是不能重写的
3)子类的访问修饰限定权限要大于等于父类的权限(如果父类是private封装除外)
默认 < protected < public
4)被final修饰的方法是不能被重写的,这个方法是一个,密封方法,故而不能被重写