• Java之抽象类,多态


    抽象类

    • 抽象方法 : 没有方法体的方法。

    • 抽象类:被abstract所修饰的抽的类。

    抽象类的语法格式

    【权限修饰符】 abstract class 类名{
        
    }
    【权限修饰符】 abstract class 类名 extends 父类{
        
    } 

    抽象方法的语法格式

    【其他修饰符】 abstract 返回值类型  方法名(【形参列表】);
    

    注意:抽象方法没有方法体 

    抽象的使用

     

    继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该父类的抽象方法,否则,从最初的父类到最终的子类都不能创建对象,失去意义。

    public abstract class Animal {
        public abstract void run();
    }
    
    public class Cat extends Animal {
        public void run (){
          	System.out.println("小猫在墙头走~~~"); 	 
        }
    } 

    注意事项

    1. 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。

      理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。

    2. 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。

      理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。

    3. 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

      理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。

    4. 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。

      理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。

    模板设计模式

    1、当解决某个问题,或者完成某个功能时,主体的算法结构(步骤)是确定的,只是其中的一个或者几个小的步骤不确定,要有使用者(子类)来确定时,就可以使用模板设计模式

    2、示例代码:计算任意一段代码的运行时间

    //模板类
    public abstract class CalTime{
        public long getTime(){
            //1、获取开始时间
            long start =  System.currentTimeMills();
            
            //2、运行xx代码:这个是不确定的
            doWork();
            
            //3、获取结束时间
            long end =  System.currentTimeMills();
            
            //4、计算时间差
            return end - start;
        }
        
        protected abstract void doWork();
    }
    
    
    
    public class MyCalTime extends CalTime{
        protected void doWork(){
            //....需要计算运行时间的代码
        }
    }
    View Code

    多态

    同一行为,通过不同的事物,可以体现出来的不同的形态。多态,描述的就是这样的状态。

    前提

    1. 继承父类或者实现接口【二选一】

    2. 方法的重写【意义体现:不重写,无意义】

    3. 父类引用指向子类对象【格式体现】

    多态的体现

    多态体现出来的现象:
    
    编译时,看“父类”,只能调用父类声明的方法,不能调用子类扩展的方法;
    
    运行时,看“子类”,一定是执行子类重写的方法体;
    

      

    多态体现的格式:

    父类类型 变量名 = new 子类对象;
    变量名.方法名();//这个方法是父类中声明,子类中重写的方法
    

    父类类型:指子类对象继承的父类类型,或者实现的父接口类型。  

    public abstract class Animal {  
        public abstract void eat();  
    }  
    
    class Cat extends Animal {  
        public void eat() {  
            System.out.println("吃鱼");  
        }  
        public void catchMouse(){
            System.out.println("抓老鼠"); 
        }
    }  
    
    class Dog extends Animal {  
        public void eat() {  
            System.out.println("吃骨头");  
        }  
    }
    
    public class Test {
        public static void main(String[] args) {
            // 多态形式,创建对象
            Animal a1 = new Cat();  
            // 调用的是 Cat 的 eat
            a1.eat();    
            //a1.catchMouse();//错误,catchMouse()是子类扩展的方法,父类中没有
            /*
            多态引用,编译时,看“父类”,只能调用父类声明的方法;
                    运行时,看“子类”,一定是执行子类重写的方法体;
            */
    
            // 多态形式,创建对象
            Animal a2 = new Dog(); 
            // 调用的是 Dog 的 eat
            a2.eat();               
        }  
    }
    View Code

    多态方法和属性分别重名时候的调用方式

    //方法看对象(实例) 属性看类型
    public class test {
        public static void main(String[] args) {
    
            // TODO 方法的重写
            // 子类重写父类的方法
            // 如何区分父类,子类中相同的方法,需要采用TODO 动态绑定机制
            // 在调用对象的成员方法过程中,将方法和对象的实际内存进行绑定,然后调用
            A1 a1 = new A1();
            System.out.println(a1.sum()); // 20
    
            B1 b1 = new B1();
            System.out.println(b1.sum()); // 40
    
            A1 a2 = new B1();
            System.out.println(a2.sum()); // 40
    
            A1 a3 = new B1();
            // 属性是不遵循动态绑定机制,所以在哪里声明,在哪里使用
            System.out.println(a3.sum()); // 20
    
        }
    }
    class A1 {
        public int i = 10;
        public int sum() {
            return getI() + 10; //父类中使用的属性居然在子类中声明?
        }
        public int getI() {
            return i;
        }
    }
    class B1 extends A1 {
        public int i = 20;
    //    public int sum() {
    //        return i + 20;
    //    }
    //   public int getI() {
    //        return i;
    //    }
    }
    

      

    多态的好处

    1.多态参数

    public class Test {
        public static void main(String[] args) {
            // 多态形式,创建对象
            Cat c = new Cat();  
            Dog d = new Dog(); 
    
            // 调用showCatEat 
            showCatEat(c);
            // 调用showDogEat 
            showDogEat(d); 
    
            /*
            以上两个方法, 均可以被showAnimalEat(Animal a)方法所替代
            而执行效果一致
            */
            showAnimalEat(c);
            showAnimalEat(d); 
        }
    
        public static void showCatEat (Cat c){
            c.eat(); 
        }
    
        public static void showDogEat (Dog d){
            d.eat();
        }
    
        public static void showAnimalEat (Animal a){
            a.eat();
        }
    }
    View Code

    由于多态特性的支持,showAnimalEat方法的Animal类型,是Cat和Dog的父类类型,父类类型接收子类对象,当然可以把Cat对象和Dog对象,传递给方法。

    当eat方法执行时,多态规定,执行的是子类重写的方法,那么效果自然与showCatEat、showDogEat方法一致,所以showAnimalEat完全可以替代以上两方法。

    不仅仅是替代,在扩展性方面,无论之后再多的子类出现,我们都不需要编写showXxxEat方法了,直接使用showAnimalEat都可以完成。

    2.多态数组

    public class TestAnimal {
    	public static void main(String[] args) {
    		Animal[] all = new Animal[4];//可以存储各种Animal子类的对象
    		all[0] = new Cat();
    		all[1] = new Cat();
    		all[2] = new Dog();
    		all[3] = new Dog();
    		
    		for (int i = 0; i < all.length; i++) {
    			all[i].eat();//all[i]编译时是Animal类型,运行时看存储的是什么对象
    		}
    	}
    } 

    多态:父子类之间的类型转行

    多态的转型分为向上转型与向下转型两种:

    向上转型

    • 向上转型:多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。

    当父类引用指向一个子类对象时,便是向上转型。

     使用格式:

    父类类型  变量名 = new 子类类型();
    如:Animal a = new Cat();
    (大--小)
    

    向下转型

    • 向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。

    一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。

    使用格式:

    子类类型 变量名 = (子类类型) 父类变量名;
    如:Cat c =(Cat) a;  
    

    为什么要转型

    当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做向下转型。

    abstract class Animal {  
        abstract void eat();  
    }  
    
    class Cat extends Animal {  
        public void eat() {  
            System.out.println("吃鱼");  
        }  
        public void catchMouse() {  
            System.out.println("抓老鼠");  
        }  
    }  
    
    class Dog extends Animal {  
        public void eat() {  
            System.out.println("吃骨头");  
        }  
        public void watchHouse() {  
            System.out.println("看家");  
        }  
    }
    
    public class Test {
        public static void main(String[] args) {
            // 向上转型  
            Animal a = new Cat();  
            a.eat();                 // 调用的是 Cat 的 eat
    
            // 向下转型  
            Cat c = (Cat)a;       
            c.catchMouse();         // 调用的是 Cat 的 catchMouse
        }  
    }
    View Code

    转型的异常

    public class Test {
        public static void main(String[] args) {
            // 向上转型  
            Animal a = new Cat();  
            a.eat();               // 调用的是 Cat 的 eat
    
            // 向下转型  
            Dog d = (Dog)a;       
            d.watchHouse();        // 调用的是 Dog 的 watchHouse 【运行报错】
        }  
    }
    

    这段代码可以通过编译,但是运行时,却报出了 ClassCastException ,类型转换异常!这是因为,明明创建了Cat类型对象,运行时,当然不能转换成Dog对象的。这两个类型并没有任何继承关系,不符合类型转换的定义。  

    instanceof运算符

    为了避免ClassCastException的发生,Java提供了 instanceof 关键字,给引用变量做类型的校验,只要用instanceof判断返回true的,那么强转为该类型就一定是安全的,不会报ClassCastException异常。 

    变量名/对象 instanceof 数据类型 
    
    如果变量/对象属于该数据类型,返回true。
    如果变量/对象不属于该数据类型,返回false。
    

    属性、静态方法没有多态性

    小贴士:

    静态方法不能被重写

    调用静态方法最好使用“类名.”

     外部类成员变量代码块构造器方法局部变量
    public × ×
    protected × × ×
    private × × ×
    static × × ×
    final × ×
    abstract × × × ×
    native × × × × ×

    不能和abstract一起使用的修饰符?

    (1)final:和final不能一起修饰方法和类

    (2)static:和static不能一起修饰方法

    (3)native:和native不能一起修饰方法

    (4)private:和private不能一起修饰方法

     

    static和final一起使用:

    (1)修饰方法:可以,因为都不能被重写

    (2)修饰成员变量:可以,表示静态常量

    (3)修饰局部变量:不可以,static不能修饰局部变量

    (4)修饰代码块:不可以,final不能修改代码块

    (5)修饰内部类:可以一起修饰成员内部类,不能一起修饰局部内部类(后面讲)

    Object根父类

    java.lang.Object是类层次结构的根类,即所有类的父类。每个类都使用 Object 作为超类。所有对象(包括数组)都实现这个类的方法。

    public class MyClass /*extends Object*/ {
      	// ...
    }
    

      

      

    本文为原创文章,转载请标明出处
  • 相关阅读:
    递增一个指针
    ubuntu 系统 sudo apt-get update遇到问题sub-process returned an error code
    熟悉HDFS过程中遇到的问题
    大二暑假第八周进度报告
    大二暑假第七周进度报告
    oracle“ORA-00904”错误:标识符无效
    大学暑假第六周进度报告
    大二暑假第五周进度报告
    使用Navicat for Oracle新建表空间、用户及权限赋予
    大学暑假第四周进度报告
  • 原文地址:https://www.cnblogs.com/harden13/p/14530884.html
Copyright © 2020-2023  润新知