继承 和 多态

1.继承

        ⑴为什么需要继承:专门为了用来进行共性抽取,实现代码复用

        ⑵继承的概念:继承机制 是面向对象程序设计代码可以复用的重要手段,它允许程序员在保持原有类特性的基础上进行扩展,增加新功能,这样产生新的类,称为派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到负复杂的认知过程。主要解决的问题是:共性的抽取,实现代码复用

         ⑶继承的语法:在java中如果要表示类之间的继承关系,需要借助extends关键字,具体如下

class Animal{
    String name;
    int age;

    public void eat() {
        System.out.println(name+"正在吃饭");
    }
    public void sleep() {
        System.out.println(name+"正在睡觉");
    }
}
class Dogg extends Animal {
    void bark() {
        System.out.println(name+"汪汪汪 ~~~");
    }
}
class Catt extends Animal {

}
public class HolidayDemo {
    public static void main(String[] args) {
        Dogg dog = new Dogg();
        //dog类中没有定义任何成员变量,name和age属性肯定是从父类Animal中继承下来的
        System.out.println(dog.name);
        System.out.println(dog.age);
        //dog访问的eat()和sleep也是从Animal中继承下来的
        dog.eat();
        dog.sleep();
        dog.bark();
    }
}

注意:

        子类会将父类中的成员变量或者成员方法继承到子类中了

        ☯子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同,否则就没必要继承了

        ⑷子类访问父类的成员方法

        

 总结:成员方法没有同名时,在子类方法中或者通过子类对象访问方法时,则优先访问自己的,自己没有时再到父类中找,如果父类中也没有则报错

2.super关键字:该关键字的主要作用是在子类方法中访问父类的成员

注意看代码注释就简单啦~

class Base{
    int a;
    int b;

    public void methodA() {
        System.out.println("Base中的method()");
    }
    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()方法");
     }
     public void methodB() {
         System.out.println("Derived中的methodB ()方法");
     }
     public void methtodC() {
         //对于同名的成员变量直访问时,访问的都是子类的
         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()

     }
 }



public class HolidayDemo24 {
    public static void main(String[] args) {
        Base base = new Base();
        base.methodA();
        base.methodB();
        System.out.println("==============================");
        Derived de = new Derived();
        de.methodA();
        de.methodB();
        System.out.println("====================");
        de.methtodC();

    }
}

注意:

        super只能在静态方法中使用

        ☯在子类方法中,访问父类的成员变量和方法

3.子类构造方法:即 子类对象构造时,需要先调用基类构造方法,然后执行子类的构造方法

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

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

注意:

        若父类显示定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用基类构造方法

        ☯如果父类构造方法是带有参数的,此时编译器不会再给子类生成默认的构造方法,此时需要用户为子类显示定义构造方法,并在子类构造方法中选择适合的父类构造方法调用,否则编译失败

        ☯在子类构造方法中,super(...)调用父类构造时,必须是字了构造函数中第一条语句

        ☯super(...)只能在子类构造方法中出现一次,并且不能和this同时出现

4.super和this

super和this都可以在成员方法中用来访问:成员变量和调用其他的成员函数,都可以作为构造方法的第一条语句,让我们来看看他们有什么区别呢?

【相同点】

        ♨都是java中的关键字

        ♨只能在类的非静态方法中使用,用来访问非静态成员方法和字段

        ♨在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在

【不同点】

        ♨this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承下来部分成员的引用

        ♨在非静态成员方法中,this用来访问本类的方法和属性,super 用来访问父类继承下来的方法和属性

        ♨this是非静态成员方法的一个隐藏参数,super不是隐藏的参数

        ♨成员方法中直接访问本类成员时,编译之后会将this还原,即本类非静态成员都是通过this阿里访问的;在子类中如果super访问父类成员,编译之后在字节码层面super实际是不存在的(通过字节码文件可以验证)

        ♨在构造方法中:this(...)用于调用本类构造方法,super(...)用于调用父类构造方法,两种调用不能同时在构造方法中出现

        ♨构造方法中一定会存在super(...)的调用,用户没有写编译器也会增加,但是this(...)用户不写则没有

5.protected关键字

访问权限
No范围privatedefaultpublic
1同一包中的同一类
2同一包中的不同类
3不同包中的子类
4不同包中的非子类

基类:B在demo1包中

/**
 * @author HP
 */
// 为了掩饰基类中不同访问权限在子类中的可见性,为了简单类B中就不设置成员方法了

// demo1包中

public class B {
    private int a;
    protected int b;
    public int c;
    int d;

}

 ⑴同一包中的子类

/**
 * @author HP
 */ //demo1包中的
//同一包中的子类

 class D extends B{
 public void method(){
// super.a = 10;  // 编译报错,父类private成员在相同包子类中不可见

 super.b = 20; // 父类中protected成员在相同包子类中可以===直接访问==

 super.c = 30;  // 父类中public成员在相同包子类中可以===直接访问==

 super.d = 40;  // 父类中默认访问权限修饰的成员在相同包子类中可以===直接访问==

  }
}
public class Test {
    public static void main(String[] args) {
        D d = new D();
        d.method();

    }
}

 ⑵不同包中的子类

//demo2包中
//不同包中的子类
class C extends B {
public void method(){
 // super.a = 10;  // 编译报错,父类中private成员在不同包子类中不可见

 super.b = 20; // 父类中protected修饰的成员在不同包子类中可以直接访问

 super.c = 30; // 父类中public修饰的成员在不同包子类中可以直接访问

 //super.d = 40;  // 父类中默认访问权限修饰的成员在不同包子类中不能直接访问

 }
}
public class TestDemo {
}

 ⑶不同包中的类

// extend02包中

// 不同包中的类

public class TestC {
    public static void main(String[] args) {
        C c = new C();
         c.method();
// System.out.println(c.a); // 编译报错,父类中private成员在不同包其他类中不可见

 // System.out.println(c.b);  // 父类中protected成员在不同包其他类中不能直接访问

 System.out.println(c.c); // 父类中public成员在不同包其他类中可以直接访问

 // System.out.println(c.d);  // 父类中默认访问权限修饰的成员在不同包其他类中不能直接访问

 }
}

6.继承方式

 7.final关键字:final关键字可以用来修饰变量,成员方法以及类

        ⑴修饰变量或字段,表示常量(即不能修改)

        ⑵修饰类:表示此类不能被继承

        ⑶修饰方法:表示该方法不能被重写

8.继承与组合:继承表示对象之间是is-a的关系;;组合表示对象之间是has-a的关系

/**
 * @author HP
 */
// 轮胎类

class Tire{
// ...

}


// 发动机类

class Engine{
 // ...

}


// 车载系统类

class VehicleSystem{
 // ...

}


class Car{
 private Tire tire; // 可以复用轮胎中的属性和方法

            private Engine engine; // 可以复用发动机中的属性和方法

            private VehicleSystem vs;  // 可以复用车载系统中的属性和方法


            // ...

}


// 奔驰是汽车

class Benz extends Car{
 // 将汽车中包含的:轮胎、发送机、车载系统全部继承下来

}
public class HolidayDemo27 {
}

        组合和继承都可以实现代码复用,应该使用继承还是组合,需根据应用场景来选择,一般建议:能用组合尽量用组合

9.多态

        ⑴多态的概念:通俗来说就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态

        ⑵多态实现条件

                ☀必须在继承体系下

                ☀子类必须要对父类中方法进行重写

                ☀通过父类的引用调用重写的方法

♦多态实现:在代码运行时,当传递不同类对象时,会调用对应类中的方法

/**
 * @author HP
 */
 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);
  }

 @Override

 public void eat(){
 System.out.println(name+"吃鱼~~~");
  }
}


 class Dog extends Animal {
 public Dog(String name, int age){
 super(name, age);
 }

 @Override

 public void eat(){
 System.out.println(name+"吃骨头~~~");
 }
}
public class TestAnimal {
     // 编译器在编译代码时,并不知道要调用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);
  }

}

10.重写:返回值和形参都不能改变。即外壳不变,核心重写!

【方法重写的规则】

        子类在重写父类的方法时,一般必须与父类方法原型一致:修饰符 返回值类型 方法名(参数列表) 要完全一致

        JDK7以后,被重写的方法返回值类型可以不同,但是必须是具有父子关系的

        访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被public修饰,则子类中重写该方 法就不能声明为 protected

        父类被static、private修饰的方法、构造方法都不能被重写。

        子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。

        子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。

        重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验. 例如不小心 将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法, 就会编译报错, 提示无法 构成重写.

【重写和重载的区别】

区别点重载重写
参数列表必须修改一定不能修改
返回类型可以修改一定不能修改
访问限定符可以修改一定不能做更严格的限制(可以降低限制)
异常可以修改可以减少删除,一定不能抛新的或者更广的异常

所以:方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现

静态绑定:也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代 表函数重载

动态绑定:也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体 调用那个类的方法。

11.向上转型和向下转型

向上转型:实际就是创建一个子类对象,将其当成父类对象来使用。

语法格式:父类类型 对象名 = new 子类类型()

public class TestAnimall {
    // 2. 方法传参:形参为父类型引用,可以接收任意子类的对象

            public static void eatFood(Animal a){
 a.eat();
  }

 // 3. 作返回值:返回任意子类对象

         public static Animal buyAnimal(String var){
 if("狗" == var){
 return new Dog("狗狗",1);
  }else if("猫" == 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();
  }
    
}

        向上转型的优点:让代码实现更简单灵活。

        向上转型的缺陷:不能调用到子类特有的方法

  向下转型:

笔记:

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

银海富月

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

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

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

打赏作者

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

抵扣说明:

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

余额充值