zoukankan      html  css  js  c++  java
  • Java 抽象类详解

      在《Java中的抽象方法和接口》中,介绍了抽象方法与接口,以及做了简单的比较。

      这里我想详细探讨下抽象类。

      

      一、抽象类的定义

      被关键字“abstract”修饰的类,为抽象类。(而且,abxtract只能修饰类和方法)

      下面显示了一个最简单的空抽象类

    public abstract class AbstractClass {
        public static void main(String[] args) {
            AbstractClass abstractClass=new AbstractClass();
        }
    }

      当对这个空的抽象类进行实例化时,编译器会报错:

      'AbstractClass' is abstract; cannot be instantiated'

       现在对这个抽象类进行扩展,添加属性和方法:

    public abstract class AbstractClass {
        public int constInt = 5;
    
        /*重载method()*/
        public void method() { }
    
        //没有编译错误
        public abstract void method(int a);
    
        public static void main(String[] args) {
            AbstractClass abstractClass=new AbstractClass() {
                @Override
                public void method(int a) {
                    System.out.println("实例化抽象类");
                }
            };
    System.out.println(abstractClass.constInt); abstractClass.method(
    5); } } //运行结果 /*   5   实例化抽象类 */

       在这个抽象类中我添加了一个实例属性,一个抽象方法,以及该抽象方法的重载实例方法,这些都是合法的。

      在main方法中,直接对抽象类通过new操作符进行实例化,出乎意料的是,IDE直接提示了这种操作——这里生成了一个匿名内部类(Anonymous Inner)。

      下面是关于匿名内部类的知识点:

    • 一种特殊的局部内部类,没有类名,没有class关键字,也不能使用extends和implements等关键字修饰。
    • 匿名内部类不能是抽象类(即不能拥有抽象方法),必须实现它的抽象父类或者接口的所有抽象方法。 
    • 因为没有类名,所以匿名内部类只能被使用一次,通常用来简化代码编写。
    • 使用匿名内部类的前提条件:继承一个父类或者实现一个接口。

      第四点和第一点略有矛盾,其实就是使用的时候通过new操作符指定要继承的父类或要实现的接口(事实上,new是直接调用某个构造函数。new真的是个牛逼的操作符啊),然后再通过直接定义类体(类体中实现某些方法),构建新类的实例。

      所以,在我上面的代码中,生成了一个继承AbstractClass的新匿名内部类的实例,这个类中实现了父类的抽象method方法,获得的实例我们赋给了abstractClass,并通过实例调用了新的method(int 5)方法。

       二、抽象类与抽象方法

      抽象方法只有方法声明,没有方法体的方法。它将由子类(可能是抽象类,也可能是非抽象类)进行实现。

      通过上面空的抽象类的方法可知,拥有一个抽象方法并不是构建一个抽象类充分必要条件。

      那么,一个有抽象方法的普通类是合法的吗?大概率是不合法的,因为如果这样的设计是合法的又有什么意义呢?

      实际上,如果我们在一个非抽象类中定义一个抽象方法,IDE会提示:

      “Abstract method in non-abstract class”

      而如果我们一定要运行如下所示的一段错误代码:

    public class AbstractTest {
    
        public abstract void test();
    
        public static void main(String[] args) {
            
        }
    }

      编译器的报错信息为:

      Error:java: AbstractTest不是抽象的, 并且未覆盖biguo.classConstruct.AbstractTest中的抽象方法test()

      所以抽象方法只能存在于抽象类中。

      三、抽象方法可以是static的吗?

      在进行下面的测试时,突然想到一个很有意思的问题,抽象类中的抽象方法可以是静态的吗?

      先下结论:NO,这是的修饰符组合是不合法的——Error: java: 非法的修饰符组合: abstract和static

    public abstract class AbstractTest {
     //非法的修饰符组合
        public static abstract void test();
    
        public static void main(String[] args) {
            
        }
    }

      static成员方法意味着,不需要实例化可以使用(在类的内部或者通过类访问)。但是呢,也可以通过实例进行访问,这样做不会报错,但会得到IDE的警告,因为违反了static的设计语义;

      abstract方法意味着没有方法体(区别下“有方法体,但方法体为空”),即只是一个方法声明,需要被子类去实现。

      我们先要清楚,抽象类中是可以拥有static方法的,比如,我们把main方法放在一个抽象类中,程序是可以由此运行的。

      既然这样,一个“static abstract”组合的方法,对这个抽象类完全没有存在的意义了!!因为它没有方法体,无法通过类来使用。

      在StackOverFlow上有探讨,说完全可以允许这样的“static abstract”方法,因为在非抽象子类中,实现这个抽象方法后的子方法仍然是static,是子类的类方法。

      这样的说法有一点点意义,但是它仍然无法解决的是,“static abstract”方法对父类中“static”语义的违背

      static方法可以被子类重写吗?

      答案是:static方法不能被子类重写。(涉及到重写的定义)

      但是!我们确实又可以在子类中重新定义一个与父类static方法一模一样的方法,如下的test()方法。

    package biguo.classConstruct;
    
    public class AbstractTest {
    
        public static  void test(){
            System.out.println("This is AbstractTest's static test!");
        }
    
        public static  void print(){
            System.out.println("This is AbstractTest's static print!");
        }
    
        public static void main(String[] args) {
            AbstractTest.test();
            AbstractTestSon.test();
            AbstractTestSon.print();
            System.out.println();
    
            AbstractTestSon abstractTestSon=new AbstractTestSon();
            abstractTestSon.print();
            abstractTestSon.test();
        }
    }
    
    class AbstractTestSon extends AbstractTest{
        public static void test(){
            System.out.println("This is AbstractTest-Son's static test!");
        }
    }
    
    
    //输出
    /*
    This is AbstractTest's static test!
    This is AbstractTest-Son's static test!
    This is AbstractTest's static print!
    
    This is AbstractTest's static print!
    This is AbstractTest-Son's static test!
    */ 

      通过输出结果可以看到:父类中未被子类重写的static方法是可以被子类及其对象访问到的,但是被子类重写过的方法,则子类及其对象只能调用自己的方法了。

      为什么这种情况不能叫做子类“重写”了父类的方法呢,而是叫”方法隐藏(method hidding)“?

      在www.geeksforgeeks.org的这篇文章中,对此做了解释:

      因为,方法重写(Overriding)是OOP语言的一种特性,在Java中,它是实现“运行时多态”一种方式!!而父类子类的相同签名的同名方法的情况,there won’t be any run-time polymorphism。

      还篇文章(”5 Java concepts explained: Overloading, overriding, shadowing, hiding, and obscuring“),解释了这些概念的差别,但是却没有提到上述的method hidding情况。(所以我以为隐藏只是继承关系的类中变量之间的行为)。

      四、抽象类的继承

      从抽象父类派生的子类如果不能实现所有的抽象方法,它也必须声明为抽象的。

      抽象类可以定义构造方法,且能被子类在构造方法中调用。

      一个非抽象类的子类,可以声明为抽象类。

      五、final与abstract的矛盾

      final关键字可以修饰类、方法、变量。

      final修饰的类不能被派生;final修饰的方法,禁止子类重写。

      所以我们可以看出,final和abstract就是冰火不容的~

  • 相关阅读:
    星辰小队针对于软件“星遇”的10天冲刺——第2天
    石家庄地铁路线安排网站的最终版本——博客登记
    星辰小队针对于软件“星遇”的10天冲刺——第1天
    单词统计——基本Java实现(无特殊设置)
    周周总结——时时更新(第4学期,第10周)
    构建之法阅读笔记04
    用户模板和用户场景(星遇)
    周周总结——时时更新(第4学期,第9周)
    ios创建bundle的图片资源文件(转)
    GCD的多线程实现方式,线程和定时器混合使用
  • 原文地址:https://www.cnblogs.com/bigbigbigo/p/10506790.html
Copyright © 2011-2022 走看看