JAVA基础总结(七)抽象类 与 接口



一、抽象类(abstract)


1、抽象类的基本概念

        在Java中,普通的类是一个完善的功能类,使用class关键字修饰,可以直接通过 new 关键字去实例化类的对象,并且在普通类中可以包含有构造方法、普通方法、static方法、常量和变量等内容。
        而抽象类是指在普通类原有的基础上添加 abstract 关键字进行声明,在普通类原本的结构里面增加抽象方法的组成部分,而拥有抽象方法的类就是抽象类。
        那么什么叫抽象方法呢?在以往的普通类中定义的方法通常都有"{}",这个大括号便是方法的方法体,通常会在方法体中去定义一些代码来实现一些特定的功能。而在实例化对象之后,对象便可以直接使用类中的方法。而抽象方法并没有"{}"方法体,而是直接使用 " ; "结尾,所以说抽象方法便是没有方法体的方法。

抽象类Demo代码展示:

public abstract class A{//定义一个抽象类
	
	public void fun(){//普通方法
		System.out.println("存在方法体的方法");
	}
	
	public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰
	
}



2、抽象类的使用


1、抽象类是否可以实例化?

abstract class A {

   public void fun(){//普通方法
       System.out.println("存在方法体的方法");
   }

   public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰


}

class B{

   public static void main(String[] args) {
       A a = new A();//此时已进入编译器异常
   }
}
        通过实例化抽象类可知,抽象类是无法实例化的。因为在实例化之后,类对象是可以调用类中的属性和方法的,但是抽象类中存在这抽象方法,抽象方法并没有方法体,而没有方法体便无法调用该方法,也就无法产生实例化对象。



2、抽象类的使用原则

  • 抽象类的使用原则如下:
    • 抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法,就失去了定义抽象方法的意义),缺省情况下默认为public;
    • 抽象类不可以使用final关键字修饰,因为抽象类必须要有子类,但是final不能有子类,两者相互冲突。
    • 抽象类不能直接实例化,需要依靠子类采用向上转型的方式处理;
    • 抽象类必须有子类,使用extends继承,一个子类只能继承一个抽象类;
    • 在继承抽象类时,子类是普通类的情况下,必须覆写抽象类中所有的抽象方法。如果子类是抽象类便不需要全部覆写

Demo代码示例:

abstract class A{//定义一个抽象类
	
	public void fun(){//普通方法
		System.out.println("存在方法体的方法");
	}
	
	public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰
	
}
//单继承
class B extends A{//B类是抽象类的子类,是一个普通类

	@Override
	public void print() {//强制要求覆写
		System.out.println("Hello World !");
	}
	
}
public class TestDemo {

	public static void main(String[] args) {
		A a = new B();//向上转型
		
		a.print();//被子类所覆写的过的方法
	}
}

运行结果:
在这里插入图片描述

  • 现在就可以清楚的发现:
    • (1)抽象类继承子类里面有明确的方法覆写要求,而普通类之间的继承关系中可以有选择性覆写需要的方法。
    • (2)抽象类实际上就比普通类多了一些抽象方法而已,其他组成部分和普通类完全一样;
    • (3)普通类对象可以直接实例化,但抽象类的对象必须经过向上转型之后才可以得到。
        虽然一个类的子类可以去继承任意的一个普通类,可是从开发的实际要求来讲,普通类尽量不要去继承另外一个普通类,而是去继承抽象类。



3、抽象类中是否存在构造方法?

Demo代码示例:

abstract class A{//定义一个抽象类

    public A(){
        System.out.println("*****A类构造方法*****");
    }

    public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰

}
//单继承
class B extends A{//B类是抽象类的子类,是一个普通类
    public B(){
        System.out.println("*****B类构造方法*****");
    }

    @Override
    public void print() {//强制要求覆写
        System.out.println("Hello World !");
    }
}

class TestDemo {
    public static void main(String[] args) {
        A a = new B();//向上转型    
    }
}

运行结果:
在这里插入图片描述
通过以上运行结果可以看出抽象类是存在构造方法的。

        因为抽象类中也可以定义一些普通的方法和属性,那么抽象类中便一定存在构造方法,其存在目的是为了属性的初始化以及方法的调用。
        并且子类对象实例化的时候,依然满足先执行父类构造,再执行子类构造的顺序。



4、抽象类使用static 声明的场景

  • 抽象外部类使用static声明场景
        抽象外部类使用static声明时,直接进入编译期异常,所以抽象外部类不可以使用static关键字声明。
  • 抽象内部类使用static声明场景

Demo代码示例:

abstract class A{//定义一个抽象类
	
	static abstract class B{//static定义的内部类属于外部类
		public abstract void print();
	}	
}

class C extends A.B{
	
	public void print(){
		System.out.println("**********");
	}
}
public class TestDemo {

	public static void main(String[] args) {
		A.B ab = new C();//向上转型
		ab.print();
	}
}

在这里插入图片描述

        由此可见,外部抽象类不允许使用static声明,而内部的抽象类运行使用static声明。使用static声明的内部抽象类相当于一个外部抽象类,继承的时候使用“外部类.内部类”的形式表示类名称。

注:任何时候,如果要执行类中的static方法的时候,都可以在没有对象的情况下直接用类名.方法的形式调用,对于抽象类也一样。

Demo代码示例:

abstract class A{//定义一个抽象类

    static abstract class B{//static定义的内部类属于外部类
        public abstract void print();
        public static void show(){
            System.out.println("show");
        }
    }
}

class C extends A.B{


    @Override
    public void print() {
        System.out.println("覆写了B类的抽象方法");
    }
}
class TestDemo {

    public static void main(String[] args) {
        /*A.B ab = new C();//向上转型
        ab.print();*/

        A.B.show();
    }
}



3、 抽象类的应用—模板设计模式

现要求实现一个程序,可以实现三种不同事物的行为。

  • 例如,现在有三类事物:
    • (1)机器人:充电,工作;
    • (2)人:吃饭,工作,睡觉;
    • (3)猪:进食,睡觉。

1、首先先定义一个抽象行为类

public abstract class Action{

    //定义全局变量	
	public static final int EAT = 1 ;
	public static final int SLEEP = 3 ;
	public static final int WORK = 5 ;
	
	public abstract void eat();
	public abstract void sleep();
	public abstract void work();
	
	public void commond(int flags){
	  switch(flags){
		case EAT:
			this.eat();
			break;
		case SLEEP:
			this.sleep();
			break;
		case WORK:
			this.work();
			break;
		default:
			break;
		}
	}
}

2、定义一个机器人的类:

class Robot extends Action{

    @Override
    public void eat() {
        System.out.println("机器人充电");
    }

    @Override
    public void sleep() {
    }

    @Override
    public void work() {
        System.out.println("机器人工作");
    }
    
}

3、定义一个人的类:

class Human extends Action{

    @Override
    public void eat() {
        System.out.println("人吃饭");
    }

    @Override
    public void sleep() {
        System.out.println("人睡觉");
    }

    @Override
    public void work() {
        System.out.println("人工作");
    }
    
}

4、定义一个猪的类:

class Pig extends Action{

    @Override
    public void eat() {
        System.out.println("猪进食");
    }

    @Override
    public void sleep() {
        System.out.println("猪睡觉");
    }

    @Override
    public void work() {
    }

}

5、定义测试实现类:


public class TestDemo {


    public static void main(String[] args) {

        fun(new Robot());
        fun(new Human());
        fun(new Pig());

    }

    //创建fun() ,方法的参数类型是Action抽象类,参数是Action抽象类对象
    public static void fun(Action action){
        action.commond(Action.EAT);
        action.commond(Action.SLEEP);
        action.commond(Action.WORK);
    }

}

在这里插入图片描述

        所有继承抽象类的子类如果要想正常的完成操作,必须按照指定的方法进行覆写才可以,而这个时候抽象类所起的功能就是一个类定义模板的功能。




二、接口(interface)


1、接口的基本概念


  • 接口由关键字 interface 修饰

    • 格式:public interface 接口名 {}

  • 类与类之间的关系是继承,类与接口之间的关系是实现。实现接口时使用的是 implements 关键字

    • 格式:class 类名 implements 接口名 {}

  • 想要使用接口,便需要将接口实现,并且接口本身是无法实例化的,但是可以实例化接口的实现类

  • 接口中有且只有抽象方法,没有其他方法。

    • 并且其中的抽象方法都有默认的修饰符:public abstract(虽然不写也没问题,但还是建议手动将修饰符补全,不然看着别扭…)

  • 因为接口主要作用是对功能进行扩展,其中只有抽象方法,没有具体的实现,所有接口中也没有构造方法。

接口实现,Demo代码示例:

定义有增删改查方法的接口

public interface UserService {
  
    //错误: 需要<标识符>,接口没有构造方法
    //public UserService() {}

    //接口方法不能带有方法体
    //public void show() {}


    void add();//添加
    void del();//删除
    void up();//修改
    void query();//查询
}

定义接口的实现类

//实现  UserService  接口 
public class UserServiceImpl implements UserService{

//重写UserService 接口中所有的抽象方法

    @Override
    public void add() {
        System.out.println("add");
    }

    @Override
    public void del() {
    }

    @Override
    public void up() {
    }

    @Override
    public void query() {
    }
}

定义测试类

public class Demo01 {

    public static void main(String[] args) {
        UserService user = new UserServiceImpl();
        user.add();
    }
}

运行结果:

在这里插入图片描述


接口中定义常量

  • 接口中的成员变量只能是常量,且都为静态,并且必须赋值。
    • 默认修饰符:public static final(可以不写,但是建议补全…)
    • 接口中常量名称,使用完全大写的字母,下划线分割。(推荐)

Demo代码示例:

public interface MyInterfaceConst {
    //就是一个常量,一旦赋值,不可以修改
    public static final int NUM_OF_MY_CLASS=10;
}

--------------------------------------------------
public class DemoInterface {
    public static void main(String[] args) {
        
        //只能使用不可修改
        System.out.println(  MyInterfaceConst.NUM_OF_MY_CLASS);
    }
}

注:接口中定义的常量应为所有类频繁使用的常量,但并不是每个类都使用了接口中定义的所有常量,因而导入了不必要的常量到这个类中,并且导入后这个类的子类也会基础导入的常量,这样会导致混乱,应当避免此种用法。

2、接口与类,接口与接口之间的关系


  • 类与接口是实现关系
    • 一个类可以实现一个接口,也可以同时实现多个接口
    • 一个类在继承一个类的同时,还可以实现单个或者多个接口

  • 接口与接口之间是继承关系,可以单继承,也可以多继承
    • 但与抽象类实现接口一样无需重写接口中的抽象方法,意义不大

Demo代码示例:


		//接口一
		interface Father{
		    public abstract void show();
		}
		
		----------------------------------------------------
		//接口二
		interface Mother {
		    public abstract void show2();
		}
		
		-------------------------------------------------------
		//接口三
		interface Sister extends Father,Mother {
		
		}
		
		-------------------------------------------------------
		
		 //创建 Son 类 ,继承 Object ,同时实现多个接口
		public  class Son extends Object implements Father,Mother {

			//重写Father接口中的抽象方法
		    public void show() {
		        System.out.println("show son");
		    }
		
			//重写Mother 接口中的抽象方法
		    public void show2() {
		        System.out.println("show2 son");
		    }
		}
		
		public class TestInterfaceDemo {
		
		    public static void main(String[] args) {
		                //创建对象
		                Father f = new Son();
		                f.show();
		                //f.show2(); //报错
		
		                Mother m = new Son();
		                //m.show(); //报错
		                m.show2();
		
		    }
		}

输出结果:

		show2 son
		show son




三、抽象类和接口的区别

  • 成员区别

    • 抽象类:
      • 成员变量:可以变量,也可以常量
      • 构造方法:有
      • 成员方法:可以有抽象方法,也可以有普通方法
    • 接口:
      • 成员变量:只可以常量,且不可修改
      • 成员方法:只有抽象方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值