Java继承和多态

继承

成员继承
class Animal{
    public String name;
    public int age;

    public int a=10;
    public void doSomething(){
        System.out.println("Animal do something!");
    }
    public void eat(){
        System.out.println("Animal eat!");
    }
}
//java中只能是单继承,也就是说只能继承一个类
class Dog extends Animal{//继承关键字

    int a=20;
    public void func(){
        System.out.println("a:"+a);//当子类和父类的成员变量冲突时,优先访问自己的
        System.out.println("base a:"+super.a);//就是想访问基类的,用Super
        //super只是一个关键字,并不是父类的引用,因为并没有实例化对象
        //只是提供一种功能,就是在子类中访问父类的成员变量/方法
        //只能指代父类,不能指代爷爷类
    }
    //this 和super都是和对象相关的,所以无法在静态方法中使用
//    public static void func2(){
//        System.out.println(super.a);//error
//        System.out.println(this.a);//error
//    }
    @Override
    public void doSomething(){//子类对父类函数发生重写
        System.out.println("狗,汪汪叫!");
    }
    public void eat(){//构成对父类的隐藏:其实就是优先访问自己的
        System.out.println("Dog eat!");
    }
    public void eat(int x){//和eat参数列表不同构成重载.既和子类的构成重载,和父类的也构成重载.继承下来的也认为是一个作用域
        System.out.println(x+ "Dog eat!");
    }
}
class Cat extends Animal{

}
public class inherit {//继承

    public static void main(String[] args) {
        Dog dog =new Dog();
        dog.name="狗";
        dog.age=22;
        Cat cat=new Cat();
        cat.age=10;
        cat.name="猫";

        dog.eat();
        dog.eat(2);

        Animal a1= new Animal();
        a1.doSomething();
        //父类的引用调用子类发生重写的函数,构成多态
        Animal a2 =new Dog();
        a2.doSomething();

        dog.func();
    }
}

构造方法
class Base{
    public String name;
    public int age;
    public Base(){//提供默认构造函数之后才行
    }
    public Base(String name,int a ) {//当只提供这个构造函数时,子类无法创建
        this.name = name;
        this.age=a;
    }
}
class Derive extends Base{
}
class C{
    public String name;
    public int age;
    public C(String name,int a ) {//当只提供这个构造函数时,子类无法创建
        this.name = name;
        this.age=a;
    }
}
class D extends C{
    public D(String name,int age){
        super(name,age);//并没有在这里构造一个父类对象,只是将继承下来的成员变量进行初始化
//        super("yuanwei",22);//显示的调用父类构造函数,帮助父类成员完成初始化
    }
}

class E extends C{
    int e=0;
    public E(String name,int age){
        super(name,age);//一定要先帮助父类初始化
        e=20;
    }
    public E(String name,int age,int e){
        super(name,age);
//        this(name,age,e);//this 和super顺序无法确定,所以在调用构造方法时不能同时出现
    }
}
class G{
    
}
class H extends G{
    //什么都不写,编译器默认生成如下代码
    public H(){
        super();//调用父类自动生成的构造函数
    }
}
super和this的区别

super和this都可以在成员方法中用来访问,

成员变量和调用其他的成员函数都可以作为构造方法的第一条语句

  • 那他们之间有什么区别呢?

[相同点]

1.都是]ava中的关键字
2.只能在类的非静态方法中使用,用来访问非静态成员方法和字段
3.在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在

[不同点]

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

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

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

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

代码块构造顺序
class Y{
    static{
        System.out.println("Y : static 静态代码块");
    }
    {
        System.out.println("Y: 非静态代码块");
    }
    Y() {
        System.out.println("Y : 不带参数的构造函数");
    }
}
class W extends Y{
    static{
        System.out.println("W : static 静态代码块");
    }
    {
        System.out.println("W: 非静态代码块");
    }
    W() {
        System.out.println("W : 不带参数的构造函数");
    }

    public static void main(String[] args) {
        W w=new W();
        //Y : static 静态代码块
        //W : static 静态代码块
        //Y: 非静态代码块
        //Y : 不带参数的构造函数
        //W: 非静态代码块
        //W : 不带参数的构造函数
        System.out.println("==============");
        W w1=new W();
        //Y: 非静态代码块
        //Y : 不带参数的构造函数
        //W: 非静态代码块
        //W : 不带参数的构造函数
    }
}
  • 先静态,先父类静态再子类静态.再父类实例和构造函数,子类实例和构造函数
  • 静态每一个类,只会执行一次
限定修饰符

protected: 不同包的子类可以使用

default:同一包中不同的类可以使用

private: 修饰只能在同一个类中才能访问

image-20231020155538822

class V{
    private String name;
    public void func(){
        System.out.println(name);
    }
}
class N extends V{
    public static void main(String[] args) {
//        System.out.println(name);//error
        //private修饰的name已经被继承了,但是不可见. 因为private修饰只能在同一个类中才能访问
    }
}
final
//继承的深度可能很深,为了避免深度过深,当不想某一个类被继承时 ,使用final关键字
class a{
    public final int b=20;//成员变量定义为final一定要给定初始值
}
class b extends a{
    public static void main(String[] args) {
        final int[] array={1,2,3,4,5};
        
        array[0]=10;
//        array =new int[4]{2,3,4,5};//Cannot assign a value to final variable 'array'
        //array指向的对象是不能改变的
    }
}
final class c extends b{//不想让C被继承

    public static void main(String[] args) {
        final int A=10;
//        A=20;//error不想让A 被修改,那就是常量,建议大写变量名
        System.out.println(A);
    }
}
//class d extends c{//error
//
//}
继承和组合

是一种代码实现方式,设计思想. a part of ; has a 的关系.

降低不同类耦合度.

奔驰是一辆车

奔驰有轮胎,座椅,车架子这些类.

多态

同一件事情,发生在不同对象的身上,呈现出不同的状态.

  • 子类对父类对象中方法重写
  • 父类引用调用子类对象
向上转型
  • 子类对象给父类,使得父类引用指向了子类对象.
  • 因为父子类继承关系,所以可以实现不同类型对象之间的赋值.
  • 当发生向上转型之后,父类的引用,只能,访问父类自己的成员,不能访问子类所特有的成员.

class Animal {
    public String name;

    public void eat(){
        System.out.println(name+" 吃!");
    }
}
class Bird extends Animal {
    public String swing="翅膀";
    public void fly(){
        System.out.println(name+" 用 "+swing+" 飞 ");
    }
}
class Pig extends Animal{
    public int iq=1000;
    public void poll(){
        System.out.println(name + " 的iq是 "+iq);
    }
}

public class PolyCode {
    //返回值统一
        public static Animal func2(){
        Bird bird = new Bird();
        bird.name="鸟";
        Pig pig=new Pig();
        pig.name="猪";
        
        return pig;
    }
    //函数传参中的向上转型
    public static void  func(Animal animal){//使用父类引用进行接收子类对象,也是一种向上转型
        System.out.println(animal.name);
    }
    public static void main(String[] args) {
        Bird bird = new Bird();
        bird.name="鸟";
        func(bird);
        Pig pig=new Pig();
        pig.name="猪";
        func(pig);
        //鸟
        //猪
    }
    //直接接收
    public static void main1(String[] args) {
        Animal a=new Animal();
        a.name="动物基类";
        a.eat();
        System.out.println("=================");
        Animal a2=new Pig();
        a2.name="猪";
        a2.eat();

        ((Pig) a2).poll();//这么写可以,但是不建议
//        a2.poll();//error,子类中包含父类中没有的方法和属性
        
        System.out.println("=================");
        Animal a3 = new Bird();
        a3.name="鸟";
        a3.eat();

        ((Bird) a3).fly();
        //动物基类 吃!
        //=================
        //猪 吃!
        //猪 的iq是 1000
        //=================
        //鸟 吃!
        //鸟 用 翅膀 飞
    }
}

重写

需要满足三同: 函数名相同,参数列表相同,返回值相同.

class Animal {
    public String name;

    public void eat(){
        System.out.println(name+" 吃!");
    }
}
class Bird extends Animal {
    public String swing="翅膀";

    @Override
    public void eat(){
        System.out.println(name+" 吃!");
    }
}
class Pig extends Animal{
    public int iq=1000;
    @Override
    public void eat(){
        System.out.println(name+" 吃!");
    }
}
public class PolyCode {

    public static void main(String[] args) {
        Animal a=new Animal();
        a.name="动物基类";
        a.eat();
        System.out.println("=================");
        Animal a2=new Pig();
        a2.name="猪";
        a2.eat();

        System.out.println("=================");
        Animal a3 = new Bird();
        a3.name="鸟";
        a3.eat();
        
        //动物基类 吃!
        //=================
        //猪 吃!
        //=================
        //鸟 吃!
    }
}
动态绑定

image-20231021201204625

在编译时发现是没有变化的,每个类的函数都会被确定执行地址.只有在运行时才会根据类型的不同,实现函数地址覆盖,然后调用.

import javafx.scene.image.PixelFormat;
class Animal {
    public String name;

    public void eat(){
        System.out.println(name+" 吃!");
    }
}
class Bird extends Animal {
    public String swing="翅膀";

    @Override
    public void eat(){
        System.out.println(name+" 吃!");
    }
}
class Pig extends Animal{
    public int iq=1000;
    @Override
    public void eat(){
        System.out.println(name+" 吃!");
    }
}
public class PolyCode {
    //多态: 当父类引用的对象不同时,表现出来的行为是不一样的
    public static void function(Animal animal){
        animal.eat();
    }

    public static void main(String[] args) {
        Animal a=new Animal();
        a.name="动物基类";
        function(a);

        System.out.println("=================");
        Animal a2=new Pig();
        a2.name="猪";
        function(a2);

        System.out.println("=================");
        Animal a3 = new Bird();
        a3.name="鸟";
        function(a3);
    }
重写注意事项
  1. private修饰的函数是不能被重写的

  2. static修饰的不能被重写.这个方法属于这个类不属于某个对象

  3. 子类的访问修饰限定权限要>=父类的.

    private<default<protected<public

  4. 被final修饰的方法是不能被重写的.此时这个方法被称为密封方法.

向下转型

子类引用指向父类对象.

非常的不安全,只能骗过编译器,但是运行时会出现问题

import javafx.scene.image.PixelFormat;
class Animal {
    public String name;

    public void eat(){
        System.out.println(name+" 吃!");
    }
}
class Bird extends Animal {
    public String swing="翅膀";

    @Override
    public void eat(){
        System.out.println(name+" 吃!");
    }
}
class Pig extends Animal{
    public int iq=1000;
    @Override
    public void eat(){
        System.out.println(name+" 吃!");
    }

    public void poll(){
        System.out.println(name + " 的iq是 "+iq);
    }
}
public class PolyCode {
    //向下转型: 所有的动物一定都是猪吗?
    public static void main(String[] args) {
        Animal animal =new Bird();
        //1.
        Pig pig =(Pig) animal;
        pig.poll();//terminal animal最开始是Bird的引用,又强转为pig,Bird中没有poll方法
        //2.
        //需要通过instanceof关键字判断animal是否真的引用一个pig对象
        if(animal instanceof Pig){
            Pig pig2 = (Pig)animal;
            pig2.poll();
        }
    }
}
多态优点
  1. 降低圈复杂度,避免使用大量的if-else判断语句.
  2. 父类类型数组可以存放子类的对象.
import javafx.scene.image.PixelFormat;
class Shape{
    public String name;
    public void drawShape(){
        System.out.println("画");
    }
}
class Circle extends Shape{
    public void drawShape(){
        System.out.println("画圆");
    }
}
class Rect extends Shape{
    public void drawShape(){
        System.out.println("画矩形");
    }
}
class Flower extends Shape{
    public void drawShape(){
        System.out.println("画花");
    }
}
public class PolyCode {
    public static void draw(){
        Rect rect=new Rect();
        Circle circle =new Circle();
        Flower flower =new Flower();
        
        //此处发生向上转型
        Shape[] shape = new Shape[]{rect,circle,flower};
        for(Shape e: shape){
            e.drawShape();
        }
        //画矩形
        //画圆
        //画花
    }
    public static void main(String[] args) {
        draw();
    }
}
注意
  1. 避免在构造方法中调用重写方法

当在父类中的构造方法中,调用父类和子类发生重写的 方法时,会调用子类的.

导致父类构造函数都没走完,父类对象都没有初始化完成,自然子类对象也没有初始化完成,所有的成员都是默认值.

import javafx.scene.image.PixelFormat;
//-------------避免在构造函数中调用重写函数
class B{
    public B(){
        func();
    }
    public void func(){
        System.out.println("B::func()");
    }
}
class D extends B{
    private int num=1;
    //默认调用B类的构造函数
//    D(){
//        super();
//    }
    
    public void func(){
        System.out.println("D::func()");
        System.out.println("B.num = "+num);
    }
}

public class PolyCode{
    public static void main(String[] args) {
        D d=new D();
        //D::func()
        //B.num = 0
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值