抽象类的基本概念
课前小叮嘱:
在以后的的开发过程中绝对不要出现一个类去继承一个已经实现好的类,而只能继承抽象类与接口。
对象多态性的核心本质在于方法的覆写,如果子类没有进行指定方法的覆写就有些不合要求。
所以如果要对子类的方法进行一些强制的要求就必须采用抽象类来解决。
抽象类的基本概念:
抽象类只是在普通类的基础上扩充了一些抽象方法而已
所谓的抽象的方法只声明而没实现的方法,没有方法体,所有抽象方法要求用abstract关键字来定义,并且抽象方法所在的类也一定要使用abstract定义类表示抽象类
例子:定义一个抽象类
class A{
private String msg = "hello";//属性
public void print(){//普通方法
System.put.print(msg);
}
//{}大括号是方法体,抽象没有{}
public abstract void fun();//抽象方法
}
public class TestDemo{
public static void main(String args[]){
}
}
}
现在可以发现抽象类就是比普通类多了一些抽象方法而已
抽象类包含有抽象方法,而抽象方法与普通方法其没有方法体,不知道具体的实现,而如果现在产生了实例化对象意味着可以调用类中的所有的操作。
那么就无法调用抽象类
抽象类的使用原则
所有的抽象类必须要有子类
抽象类的子类(不是抽象类)必须覆写抽象类中的所有抽象方法
——方法的覆写一定考虑到权限问题,抽象方法可以使用任意权限,但是要求注意一点:权限尽量都用public
抽象类的对象可以通过对象多态性,利用子类为其实例化
例子:使用抽象类
abstract class A{
private String msg = "hello";//属性
public void print(){//普通方法
System.out.print(msg);
}
//{}大括号是方法体,抽象没有{}
public abstract void fun();//抽象方法
}
//定义抽象类的子类。一个子类只能利用extends继承抽象类,所以有单继承局限
class B extends A{
public void fun(){
System.out.println("world");
}
}
public class TestDemo{
public static void main(String args[]){
A a = new B();//实例化子类对象
a.fun();//被子类所覆写过的方法
}
}
从正常的一个开发角度来看,以上的操作没有问题也是使用最多的形式,但是对抽象类必须要说明一点,您以后可能见到如下一种使用形式。
例子(内部抽象类)
static方法取得实例化对象,内部已经设置好了一个子类
abstract class A{
private String msg = "hello";//属性
public void print(){//普通方法
System.out.print(msg);
}
public static A getInstance(){//取得A类对象
class B extends A{
public void fun(){
System.out.println("world");
}
}
return new B();
}
//{}大括号是方法体,抽象没有{}
public abstract void fun();//抽象方法
}
public class TestDemo{
public static void main(String args[]){
A a = A.getInstance();
a.fun();//被子类所覆写过的方法
}
}
抽象类的相关规定
抽象类只是比普通类多了些抽象方法的定义,所以在抽象类之中依然允许有抽象方法,并且我们的子类也遵守子类对象的实例化流程,实例化子类对象前一定先去调用父类的构造方法。
范例:在抽象类中定义构造方法:
abstract class Person{
private String name;
private int age;
public Person(){
System.out.println("************");
}
public abstract String getInfo();//抽象方法
}
class Student extends Person{
private String school;
public Student(){
System.out.println("$$$$$$$$$$");
}
public String getInfo(){
return null;
}
}
public class TestDemo{
public static void main(String args[]){
new Student();
}
}
如果父类中没有无参构造,那么子类就必须通过super()指明父类中的构造方法
abstract class Person{
private String name;
private int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
public String getName(){
return this.name;
}
public int getAge(){
return this.age;
}
public abstract String getInfo();//抽象方法
}
class Student extends Person{
private String school;
public Student(String name,int age,String school){
super(name,age);//子类构造必须明确调用父类构造
this.school = school;
System.out.println("$$$$$$$$$$");
}
public String getInfo(){
return "【student】name = " + super.getName() +
",age = " + super.getAge() + ",school = " +this.school;
}
}
public class TestDemo{
public static void main(String args[]){
Person per = new Student("TOM",20,"新加坡国立大学");
System.out.println(per.getInfo());
}
}
其实抽象类存在构造方法也很好理解,毕竟抽象中还有属性,所有属性一定要在对象实例化的时候进行空间的一个开辟,对象实例化必须要使用构造方法。
额外话题:关于对象实例化
对象的实例化操作实际上需要以下几个核心步骤:
1 进行类的加载
2 进行类对象的空间开辟
3 进行类对象中的属性初始化(构造方法)
abstract class A {
public A(){//3调用构造方法
this.print();//4调用被子类覆写过的方法
}
public abstract void print();//抽象方法
}
class B extends A{
private int num = 100;
public B(int num){//2调用子类实例化对象
super();// 3隐含一行语句,实际上想要调用父类构造
this.num = num;//7为类中发属性初始化
}
public void print(){ //5 此时子类对象的属性还没有被初始化
System.out.println(this.num);//6内容其实是对应数据类型的默认值
}
}
public class TestDemo{
public static void main(String args[]){
new B(30);//1实例化子类对象
}
}
结论
1 如果构造方法没有执行,那么对象中的属性一定都是其对应数据类型的默认值
2 抽象类中允许不定义任何的抽象方法,但是此时抽象方法依然无法进行直接实例化处理
3 抽象类一定不能使用final来声明,因为使用final定义的类不能够有子类,而我们抽象类必须有子类,而且抽象方法不能够使用private来定义因为抽象方法必须被覆写!
4 抽象类分为外部抽象类与内部抽象类,内部抽象类可以使用static定义描述为外部抽象类
abstract class A {//此类结构出现几率低
public abstract void printA();
abstract class B{
public abstract void printB();
}
}
class X extends A{
public void printA(){}
class Y extends B{
public void printB(){}
}
}
public class TestDemo{
public static void main(String args[]){
}
}
而我们要知道外部抽象类上使用了static那么就是语法错误,可内部抽象类允许使用static。
abstract class A {//此类结构出现几率低
public abstract void printA();
static abstract class B{
public abstract void printB();
}
}
class X extends A.B{
public void printB(){}
}
public class TestDemo{
public static void main(String args[]){
}
}
从一般的设计角度来讲,以上的问题很多时候一般不会出现
模板设计模式(实际应用)
抽象类最大的特点在于强制规定了子类的实现结构,那么除了这一特点外抽象类更多情况可以起到模板的作用。下面做一个简单的分析:
人 = 吃饭+睡觉+工作;
猪 = 吃饭+睡觉
机器人=吃饭+工作
现在假设有一个按钮控制(方法),一旦传入了某些指令后就可以进行相应的处理。
例子:定义抽象的行为:
abstract class Action {//描述的是抽象的行为
public static final int EAT = 1;
public static final int SLEEP = 5;
public static final int WORK = 10;
public void command(int cmd){
switch(cmd){
case EAT:
this.eat();
break;
case SLEEP:
this.sleep();
break;
case WORK:
this.work();
break;
case EAT + SLEEP + WORK:
this.eat();
this.sleep();
this.work();
break;
}
}
//不确定具体的实现,但是行为应该定义好
public abstract void eat();
public abstract void sleep();
public abstract void work();
}
public class TestDemo{
public static void main(String args[]){
}
}
例子:定义各个行为的子类:
abstract class Action {//描述的是抽象的行为
public static final int EAT = 1;
public static final int SLEEP = 5;
public static final int WORK = 10;
public void command(int cmd){
switch(cmd){
case EAT:
this.eat();
break;
case SLEEP:
this.sleep();
break;
case WORK:
this.work();
break;
case EAT + SLEEP + WORK:
this.eat();
this.sleep();
this.work();
break;
}
}
//不确定具体的实现,但是行为应该定义好
public abstract void eat();
public abstract void sleep();
public abstract void work();
}
class Human extends Action{
public void eat(){
System.out.println("delicious");
}
public void sleep(){
System.out.println("bed");
}
public void work(){
System.out.println("cheer up");
}
}
class Pig extends Action{
public void eat(){
System.out.println("garbage");
}
public void sleep(){
System.out.println("floor");
}
public void work(){}
}
class Robot extends Action{
public void eat(){
System.out.println("energy");
}
public void sleep(){}
public void work(){
System.out.println("continuing");
}
}
public class TestDemo{
public static void main(String args[]){
}
}
例子:调用各自的行为操作:
abstract class Action {//描述的是抽象的行为
public static final int EAT = 1;
public static final int SLEEP = 5;
public static final int WORK = 10;
public void command(int cmd){
switch(cmd){
case EAT:
this.eat();
break;
case SLEEP:
this.sleep();
break;
case WORK:
this.work();
break;
case EAT + SLEEP + WORK:
this.eat();
this.sleep();
this.work();
break;
}
}
//不确定具体的实现,但是行为应该定义好
public abstract void eat();
public abstract void sleep();
public abstract void work();
}
class Human extends Action{
public void eat(){
System.out.println("delicious");
}
public void sleep(){
System.out.println("bed");
}
public void work(){
System.out.println("cheer up");
}
}
class Pig extends Action{
public void eat(){
System.out.println("garbage");
}
public void sleep(){
System.out.println("floor");
}
public void work(){}
}
class Robot extends Action{
public void eat(){
System.out.println("energy");
}
public void sleep(){}
public void work(){
System.out.println("continuing");
}
}
public class TestDemo{
public static void main(String args[]){
fun(new Human());
fun(new Pig());
fun(new Robot());
}
public static void fun(Action action){
action.command(Action.EAT + Action.SLEEP + Action.WORK);
}
}
通过此程序的定义结构:清楚发现,如果抽象类在实际使用过程中会定义一些固化模式,只能接收特定指令,但是每种指令的具体实现由子类负责完成,父类只负责方法的约定
最具有代表性就是后面要学习的servlet程序
总结
1 抽象类虽然定义了子类必须要做的事情,但是抽象类依然会存在有单继承局限。
2 抽象类的使用必须通过子类进行对象实例化处理