面向对象——多态

简单认识多态-对象多态与行为多态 

多态性,是子类的对象赋给了父类的引用,使用的前提是有继承关系、有方法的重写

多态是什么?

  • 多态是同一个行为具有不同的表现形式或形态的能力
  • 同一方法可以根据发送对象的不同而采用不同的行为方式

多态存在的三个必要条件

  1. 继承或实现:在多态中必须存在有继承或实现关系的子类和父类
  2. 方法的重写:子类对父类中的某些方法进行重新定义(重写,使用@Override注解进行重写)
  3. 基类引用指向派生类对象,即父类引用指向子类对象,父类类型:指子类对象继承的父类类型,或实现的父接口

多态的格式

  • 父类类型  变量名 = new 子类类型();
  • 然后通过 变量名.方法名()调用在子类中重写的方法
  • 多态体现为父类引用变量可以指向子类对象:定义了一个父类类型的引用,指向新建的子类类型的对象,由于子类是继承他的父类的,所以父类类型的引用是可以指向子类类型的对象的

多态中的成员特点
多态成员变量:编译运行看左边
此处举例Animal是父类,Dog是子类

Animal dog = new Dog();   //Animal是引用类型,Dog是实际类型
System.out.println(dog.age) //dog的引用类型是Animal,所以取到的是父类Animal中的值,说白了dog是属于Animal类,Animal中变量的值是多少就通过对象就取得多少

多态中的特点

1.多态成员变量:编译运行看左边
 

此处举例Animal是父类,Dog是子类

Animal dog = new Dog();   //Animal是引用类型,Dog是实际类型
System.out.println(dog.age) //dog的引用类型是Animal,所以取到的是父类Animal中的值,说白了dog是属于Animal类,Animal中变量的值是多少就通过对象就取得多少
 

父类Animal: 

//父类

//父类
public class Animal {
 
    public int age = 11;
    
}


子类Dog:

//子类
public class Dog extends Animal {
 
    public int age = 33;
    
}


启动项:

public class DemoApplication {
 
    public static void main(String[] args) {
 
        //父类类型 对象 = new 子类类型()
        Animal dog = new Dog();
        System.out.println(dog.age);
    }
}


控制台打印输出:父类中定义的age 

11

2.多态成员方法:编译看左边,运行看右边 


此处举例Animal是父类,Dog是子类

Animal  dog  =  new Dog();  //Animal是引用类型,Dog是实际类型
dog.eat();    

//变量dog的实际类型是Dog,即是由Dog 这个实际类型new出来的,因此dog.eat() 调用的应该是子类Dog中重写的方法
 

父类Animal 

//父类
public class Animal {
 
 
 
    public void eat() {
        System.out.println("午餐吃狗粮");
    }
 
}


子类Dog:

//子类
public class Dog extends Animal {
    
    @Override
    public void eat() {
        System.out.println("晚餐吃狗粮");
    }
}


启动项:


控制台打印输出:调用的是子类中重写的方法

晚餐吃狗粮

 技巧:编译看左边,运行看右边 

 变量不多态都为people

在代码中p1.name与p2.name都为peo

public class DemoApplication {
 
    public static void main(String[] args) {
 
        //父类类型 对象 = new 子类类型()
        Animal dog = new Dog();
 
        dog.eat();
    }
}

注意点

 

  1. 多态情况下,子类和父类存在同名的成员变量时,访问的时父类的成员变量
  2. 多态情况下,子父类存在同名的非静态成员方法时,访问的是子类中重写的方法
  3. 多态情况下,子父类存在同名的静态成员变量成员方法时,访问的是父类的成员函数
  4. 多态情况下,不能访问子类独由的方法

对于子类独有的方法,父类无法访问,

父类Animal保持不变:

//父类
public class Animal {
    
    public void eat() {
        System.out.println("午餐吃狗粮");
    }
 
}


子类Dog:增加子类读有的方法walk()

//子类
public class Dog extends Animal {
 
    
    public void walk(){
        System.out.println("子类独有的方法");
    }
    
    
    @Override
    public void eat() {
        System.out.println("晚餐吃狗粮");
    }
}


启动项:walk()方法爆红,即编译报错,

根据多态成员方法中编译看左边,运行看右边的原理

Animal  dog  =  new Dog();

可知 左边的Animal引用类型中没有walk()这个方法,故编译不通过,编译爆红

 

public class DemoApplication {
 
    public static void main(String[] args) {
 
        //父类类型 对象 = new 子类类型()
        Animal dog = new Dog();
 
        dog.eat();  //访问的是子类中重写的方法
 
 
        dog.walk();  
        //walk方法爆红,即编译报错,编译看左边

        //dog类的实例对象Animal没有walk()这个方法,所以编译报错
 
    }
}


那么想要直接访问子类独有的方法,该如何解决呢,由此引出了引用类型转换

 

 下面代码中的p1.name与p2.name都为people意思就是说变量不多态

public class Test {
    public static void main(String[] args) {
                People p1 =new Teacher();
                p1.run();
        System.out.println(p1.name);
        People p2 =new Studnet();
                p2.run();
        System.out.println(p2.name);
    }
}
//父类
public class People {
    String name="people";
    public  void  run()
    {

        System.out.println("人可以跑");
    }
}
public class Studnet  extends People {
    String name="student";
    @Override
    public  void  run()
    {
        System.out.println("学生跑的贼快");
    }
}
public class Teacher extends People {
    String name="student";
    @Override
    public  void  run()
    {
        System.out.println("老师跑的贼快");
    }
}

多态的好处

 

 

 

 不能掉子类的例子

public class Test {
    public static void main(String[] args) {
                People p1 =new Teacher();
                p1.run();
                //p1.test编译看左边people中无text方法虽然学生类中有无法调用
        System.out.println(p1.name);
        People p2 =new Studnet();
                p2.run();
        System.out.println(p2.name);
    }
}
//父类
public class People {
    String name="people";
    public  void  run()
    {

        System.out.println("人可以跑");
    }
}
public class Studnet  extends People {
    String name="student";

    public  void text()
    {
        System.out.println("学生要考试");
    }
    @Override
    public  void  run()
    {
        System.out.println("学生跑的贼快");
    }
}

 

强制类型的转换可能存在的问题,编译阶段没有继续或者实现关系就可以强制转换,但运行就会报错 

 //强制类型的转换可能存在的问题,编译阶段没有继续或者实现关系就可以强制转换,但运行就会报错
        Teacher  t1= (Teacher)  p2;

 

 多态下的类型转化

1.1为什么需要引用类型转换 

父类无法调用子类独有的方法 

因此如果我们想要调用子类的方法,必须做到向下转型 

向上转型

父类类型  变量名  = new 子类类型();

Animal       dog     = new  Dog()

向下转型

子类类型 子类变量名 = (子类类型) 父类变量名

Dog dog1 = (Dog) dog;

dog1.walk;

父类Animal:

//父类
public class Animal {
 
    public void eat() {
        System.out.println("午餐吃狗粮");
    }
 }



子类Dog:包含有子类独有的方法walk()

//子类
public class Dog extends Animal {
 
 
    public void walk(){
        System.out.println("子类独有的方法");
    }
 
 
    @Override
    public void eat() {
        System.out.println("晚餐吃狗粮");
    }
}


启动项:

通过

Dog dog1 = (Dog) dog;完成向下转型
再利用向下转型成功的子类对象dog1调用子类中独有的方法walk()

 

public class DemoApplication {
 
    public static void main(String[] args) {
 
        //父类类型 对象 = new 子类类型()
        Animal dog = new Dog();
        //向下转型
        //子类类型 子类变量名 = (子类类型) 父类变量名
        Dog dog1 = (Dog) dog;
 
        dog.eat();  //访问的是子类中重写的方法
 
        //通过向下转型的子类对象调用子类独有的方法
        dog1.walk();  //walk方法爆红,即编译报错,编译看左边
        //dog类的实例对象Animal没有walk()这个方法,所以编译报错
 
 
    }
}


控制台打印输出:

晚餐吃狗粮
子类独有的方法


所以对于多态中,无法使用子类特有的方法也通过向下转型,将父类类型强制转换为某个子类类型后,再进行方法的调用

1.2向下转型的问题


虽然可以通过向下转型可以调用子类独有的方法,但也会产生下面的问题

增加一个子类Cat类,

Cat类中有其独有的方法sleep()

 

//Cat类通过extends关键字继承父类Animal
public class Cat extends Animal {
 
 
 
    public void sleep(){
        System.out.println("Cat类独有的方法");
    }
 
    @Override
    public void eat() {
        System.out.println("晚餐吃猫粮");
    }
}


        

父类Animal:

//父类
public class Animal {
 
    public void eat() {
        System.out.println("午餐吃狗粮");
    }
 
}


子类Dog类:

//子类
public class Dog extends Animal {
 
 
    public void walk(){
        System.out.println("Dog类独有的方法");
    }
 
 
    @Override
    public void eat() {
        System.out.println("晚餐吃狗粮");
    }
}


启动项:

public class DemoApplication {
 
    public static void main(String[] args) {
 
        //向上转型
        //父类类型 对象 = new 子类类型()
        Animal cat = new Cat();
        //向下转型
        //子类类型 子类变量名 = (子类类型) 父类变量名
        Dog dog1 = (Dog) cat;
 
 
        //通过向下转型的子类对象调用子类独有的方法
        dog1.walk();  //walk方法爆红,即编译报错,编译看左边,
        //dog类的实例对象Animal没有walk()这个方法,所以编译报错
 
    }
}


控制台打印输出:爆出异常ClassCastException,即类型转换异常

 分析:为什么会爆出类型转换异常

因为 在启动项中,向上转型的过程,Animal cat = new Cat(); cat对象是由子类Cat构造出来的
而向下转型的过程中 Dog dog1 = (Dog) cat; 却将其变成了Dog 类的对象,
Dog类和Cat类都是Animal类的儿子类 ,
上面的步骤中的第二步将子类Cat的对象cat变成了兄弟类的对象dog,这就不是向下转型了,因此会报类型转换异常

那么如何避免这种异常呢 ,就需要使用instanceof关键字 

 

 

 

 

1.3instanceof关键字详解

 
Java为我们提供了一个关键字instanceof,它可以帮助我们避免出现ClassCastException这样的异常,

格式:

变量名  instanceof   数据类型

解释:

如果变量属于该数据类型或者其子类型,返回true
如果变量不属于该数据类或者其子类型,返回false

 
public class DemoApplication {
 
    public static void main(String[] args) {
 
        //向上转型
        //父类类型 对象 = new 子类类型()
        Animal animal = new Cat();
 
 
        //向下转型
        //子类类型 子类变量名 = (子类类型) 父类变量名
        if ( animal instanceof Cat){
            Cat cat = (Cat) animal;
            cat.sleep();
        }else if(animal instanceof Dog){
            Dog dog = (Dog) animal;
            dog.walk();
        }
 
    }
}


final 

使用final关键字通常意味着对变量必须进行一次赋值,即变量被赋值后就不能再改变其值。

public class Test {
    public static void main(String[] args) {
        //final修饰变量有且只能赋值一次
//              变量
//                      1,局部变量
        final int a;
        a = 12;
        // a=13;//只能赋值一次
        Test t =new Test();
      //  t.name="孙悟空";//报错实例变量
            //用final修饰引用类型变量
          final  int arr[] ={1,2,3};
          // arr=null;//第二次赋值,地址
           arr[0]=222;//合法
    }

    public static void buy(final int a) {
        //  a=2;第二次赋值报错
    }

    //                2,成员变量
//                (1)静态变量
    //常量 :public static final 修饰的成员变量,建议这些名称大写,多个单词用下划线链接
    public static final String SHOOL_NAME = "黑马";//这也称为常量

    //                (2)实例变量(没有意义)对于对象用但是只能赋值一次
    public final String name = "猪八戒";

}




      //1,final 修饰类,类不能被继承
   final class  A
      {//一般用在工具类中

      }
     // class  B extends  A{}//会报错
//2,final修饰方法,方法不能被重写
    class  C
     {
         public  final  void  test()
         {

         }
     }
//     class  D extends  C
//     {
//         @Override
//         public  void  test()
//         {
//
//         }
//     }//不能实现

常量

常量 :public static final 修饰的成员变量,建议这些名称大写,多个单词用下划线链接

public class Text2 {
      public  static  final  String SHOOL_NAME ="黑马程序员";

    public static void main(String[] args) {
        System.out.println(SHOOL_NAME);
        System.out.println(SHOOL_NAME);
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值