一、抽象类的定义
被关键字“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类。
下面是关于匿名内部类需要了解的一些事情:
一个特殊的局部内部类,没有类名,没有类关键字,也没有extends和implements关键字的修改。
匿名内部类不能是抽象的(也就是说,不能有抽象方法),必须实现其抽象超类或接口的所有抽象方法。
因为没有类名,匿名内部类只能使用一次,通常是为了简化代码编写。
使用匿名内部类的先决条件是继承父类或实现接口。
new操作符用于指定继承哪个父类或实现哪个接口。(事实上,new直接调用构造函数。New是一个非常酷的操作符),然后通过直接定义类主体(实现某些方法)来构建新类的实例。
因此,在上面的代码中,我生成了一个新的匿名内部类的实例,它继承了AbstractClass,实现了父类的抽象方法方法,将实例赋值给AbstractClass,并从实例调用新方法(int 5)方法。
二、抽象类与抽象方法
抽象方法只有方法声明,没有方法体的方法。它将由子类(可能是抽象类,也可能是非抽象类)进行实现。
通过上面空的抽象类的方法可知,拥有一个抽象方法并不是构建一个抽象类充分必要条件。
那么,一个有抽象方法的普通类是合法的吗?大概率是不合法的,因为如果这样的设计是合法的又有什么意义呢?
实际上,如果我们在一个非抽象类中定义一个抽象方法,IDE会提示:
“Abstract method in non-abstract class”
而如果我们一定要运行如下所示的一段错误代码:
public class AbstractTest { public abstract void test(); public static void main(String[] args) { } }
编译器的错误信息是:
错误:Java: AbstractTest不是抽象的,并且没有覆盖biguo classConstruct。abstract test()中的抽象方法
所以抽象方法只能存在于抽象类中。
抽象方法可以是静态的吗?
在接下来的测试中,我想到了一个有趣的问题。抽象类中的抽象方法可以是静态的吗?
错误:Java:无效的修饰符组合:抽象和静态。错误:Java:无效的修饰符组合:abstract和static
public abstract class AbstractTest { //非法的修饰符组合 public static abstract void test(); public static void main(String[] args) { } }
静态成员方法意味着可以(在类内部或通过类)使用它们,而不需要实例化。但是,也可以从实例中访问它,并且没有错误,但是IDE会发出警告,因为它违反了静态的设计语义;
抽象方法意味着没有方法主体,也就是说,只有一个需要由子类实现的方法声明。
我们需要意识到我们可以在抽象类中拥有静态方法。例如,如果我们把main方法放在一个抽象类中,我们的程序就可以从它运行。
在这种情况下,静态抽象方法的组合对这个抽象类没有任何意义。因为它没有方法主体,所以不能被类使用。
允许这样一个“静态抽象”方法是完全可以接受的,因为在一个非抽象子类中,实现抽象方法的子方法仍然是静态的,即子类的类方法。
这个语句有一点意义,但它仍然没有解决的是“静态抽象”方法对父类的“静态”语义的违背
静态方法可以被子类覆盖吗?
答案是:静态方法不能被子类覆盖。(包括重写定义)
但是!实际上,我们可以在子类中重新定义与它的静态父方法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)“?
因为,方法重写(Overriding)是OOP语言的一种特性,在Java中,它是实现“运行时多态”一种方式!!而父类子类的相同签名的同名方法的情况,there won’t be any run-time polymorphism。
4. 抽象类的继承
如果抽象父类不能实现所有的抽象方法,则派生自抽象父类的子类也必须声明为抽象。
抽象类可以定义可以被构造函数中的子类调用的构造函数。
可声明为抽象的非抽象类的子类。
五、定与抽象的矛盾
最后一个关键字可以修改类、方法和变量。
最终修改的类不能派生;最后一个修饰符方法禁止重写子类。
由此可见,定义性和抽象性是不相容的