第十七章_抽象类与接口

第十七章 抽象类与接口


提纲

  • 17.1 抽象类
    • 17.1.1 为什么使用抽象类
    • 17.1.2 定义抽象类语法
    • 17.1.3 抽象方法
    • 17.1.4 举例
  • 17.2 接口
    • 17.2.1 什么是接口
    • 17.2.2 为什么使用接口
    • 17.2.3 定义一个接口的语法
    • 17.2.4 接口的特点
    • 17.2.5 举例
    • 17.2.6 具体问题举例
      • 17.2.6.1 例1:接口中能不能定义属性?定义的属性是什么状态的?
      • 17.2.6.2 例2:抽象类能不能实现接口?
      • 17.2.6.3 例3:接口能否继承接口?
  • 17.3 抽象类与接口的区别
    • 17.3.1 语法层面上的区别
    • 17.3.2 设计层面上的区别
    • 17.3.3 什么时候使用抽象类和接口
  • 17.4 作业

17.1 抽象类

  • 17.1.1 为什么使用抽象类:我们一般会将父类定义为抽象类,比如有汽车类,汽车中有开车方法,但是具体开什么车是不知道的,即在汽车类中这个方法是没有什么具体意义的。所以这个汽车类就是一个抽象的类。又比如让你去买电脑,肯定不会让你去买电脑类,因为电脑种类有很多。

  • 17.1.2 定义抽象类语法:public abstract class 类名{}

  • 17.1.3 抽象方法

    • 使用abstract定义的类称为抽象类,而使用这个关键字定义的方法称为抽象方法。抽象方法没有方法体,这个方法本身没有任何意义,除非它被重写,而承载这个抽象方法的抽象类必须被继承,实际上抽象类除了被继承外没有任何意义。

    • 抽象方法的特点:

      1. 没有方法的实体,必须要用子类来重写的方法。
      2. 如果类中有一个方法为抽象方法,那么这个类就是抽象类。
      3. 抽象方法和抽象类必须要加上abstract。
      4. 抽象类不能被实例化,即不能被new出来,只能被继承。
      5. 抽象类中可以写实体方法。
    • 举例:

        //定义一个抽象类
        public abstract class Person {
        	//定义一个抽象方法,抽象方法必须加入abstract关键字
        	public abstract void run();
        	//定义一个实体方法
        	public void eat(){
        		System.out.println("人吃饭");
        	}
        }
        //测试类
        public class Test {
        	public static void main(String[] args) {
        		//这是错误的,抽象方法不能被实例化
        		Person person = new Person();
        	}
        }
      
  • 17.1.4 举例:汽车开车的例子。

      	//定义一个汽车抽象类
      	public abstract class Car {
      		//开车的抽象方法
      		public abstract void drive();
      		//定义一个实体加速的方法
      		public void speedUp(){
      			System.out.println("汽车在加速");
      		}
      	}
      	//本田车类
      	public class Honda extends Car{
      		@Override
      		public void drive() {
      			//super.drive();//调用父类的开车方法
      			System.out.println("开本田牌汽车");
      		}
      		@Override
      		public void speedUp() {
      			System.out.println("本田车在加速");
      		}
      	}
      	//宝马车类
      	public class BMW extends Car{
      		@Override
      		public void drive() {
      			System.out.println("开宝马牌汽车");
      		}
      	}
      	//保时捷车类
      	public class Porsche extends Car{
      		@Override
      		public void drive() {
      			System.out.println("开保时捷牌汽车");
      		}
      	}
      	public class Test {
      		public static void main(String[] args) {
      			//创建一个人类对象
      			Person person = new Person();
      			//实现多态,父类的引用指向子类的对象
      			Car bmw = new BMW();
      			Car honda = new Honda();
      			Car porsche = new Porsche();
      			//调用各子类的开车方法
      			person.drive(bmw);
      			person.drive(honda);
      			person.drive(porsche);
      			//调用加速的方法
      			bmw.speedUp();
      			honda.speedUp();
      			porsche.speedUp();
      		}
      	}
      	//执行结果
      	开宝马牌汽车
      	开本田牌汽车
      	开保时捷牌汽车
      	汽车在加速
      	本田车在加速
      	汽车在加速
    

17.2 接口

  • 17.2.1 什么是接口:接口是抽象类的延伸,可以将它看作是存粹的抽象类,接口中所有的方法都没有实体,但是可以写静态方法**(JDK1.8以后可以)**,但是静态方法必须有实体。

  • 17.2.2 为什么使用接口:在继承中可以发现一个问题,就是单继承。一个子类只能继承一个父类,但是有这么个情况:比如一个人,他既是学生,又是党员,又是一个儿子,这么多身份要继承,那要怎么办呢?那就要用到接口。

  • 17.2.3 定义一个接口的语法:public interface 类名{}

  • 17.2.4 接口的特点:

    1. 接口中所有的方法都是抽象方法,所以可以省略abstract关键字。但是可以写静态方法**(JDK1.8以后可以)**,但是静态方法必须有实体。
    2. 子类与父类的关系叫:继承,接口与继承接口的关系叫:实现。实现接口的关键字为:implememnts。
    3. 接口中的定义的属性都是public static final类型的。一般接口中不会定义属性。
    4. 一个类可以实现多个接口,语法为:public class 类名 implememnts 接口1,接口2…{}
    5. 一个类继承父类与实现接口之间不冲突,可同时进行,语法为:public class 类名 extends 父类 implememnts 接口1,接口2…{},必须先继承,再实现接口。
    6. 接口中没有构造方法,与抽象类类似,无法实例化。
  • 17.2.5 举例:有一个人类person,他既是慈善家cherisher,捐款500万才能称为慈善家,又是国家干部nationaler,先入党才能称为国家干部,又是土豪richer,成为土豪必须拥有一个亿。

      //定义慈善家接口
      public interface Cherisher {
      	//捐款方法,abstract可不写,接口中的方法默认加上了这个关键字
      	public abstract void contribute();
      
      }
      //定义国家干部接口
      public interface Nationaler {
      	//入党的方法
      	public void becomePartyM();
      }
      //定义一个土豪接口
      public interface Richer {
      	public void haveMoney();
      }
    
      //实现多个接口 
      public class Person implements Cherisher,Nationaler,Richer{
      	//实现接口关键字implements
      	public void drive(Car car){
      		car.drive();
      	}
      	
      	public void contribute() {
      		System.out.println("已经捐款500W");	
      	}
      	
      	public void becomePartyM(){
      		System.out.println("加入国民党");
      	}
      	
      	public void haveMoney(){
      		System.out.println("至少1个亿");
      	}
    


    public static void main(String[] args) {
    //接口的引用指向实现类的对象
    Nationaler n = new Person();
    //成为党员方法
    n.becomePartyM();
    //慈善家接口指向人类的对象
    Cherisher c = new Person();
    //调用捐款的方法
    c.contribute();
    }
    }
    //执行结果
    加入国民党
    已经捐款500W

  • 17.2.6 具体问题举例:

    • 17.2.6.1 例1:接口中能不能定义属性?定义的属性是什么状态的?

        public interface Animal {
        	//接口中可以定义属性,属性都是public static final类型的,会省略。一般接口中不会定义属性。
        	public static final String NAME = "老虎";
        }
      
    • 17.2.6.2 例2:抽象类能不能实现接口?

        //定义一个动物接口
        public interface Animal {
        	public static final String NAME = "老虎";
        	//定义一个睡觉的方法
        	public void sleep();
        }
        //定义一个抽象的猫科类
        public abstract class AllCats implements Animal{
        	//如果猫科类实现了接口Animal中的方法,其子类就不必再实现Animal中的方法
        	@Override
        	public void sleep() {
        		System.out.println("猫科动物睡觉");
        	}
        }
        //定义一个老虎类
        public class Tiger extends AllCats{
        	//如果父类AllCats没有实现Animal接口中的方法,Tiger就必须实现
        	@Override
        	public void sleep() {
        		System.out.println("老虎在睡觉");
        	}
        }
      

      结论:抽象类可以实现接口。但是如果抽象类没有实现接口中的方法,那么抽象类的子类就必须一起实现接口的方法。

    • 17.2.6.3 例3:接口能否继承接口?

        //定义一个动物接口
        public interface Animal {
        	//定义一个睡觉的方法
        	public void sleep();
        }
        //定义一个灵长类接口
        public interface Primates extends Animal{
        	public void eat();
        }
        //定义一个狒狒类
        public class Baboon implements Primates{
        	@Override
        	public void sleep() {
        		System.out.println("狒狒在睡觉");
        	}
        	@Override
        	public void eat() {
        		System.out.println("狒狒在吃饭");
        	}
        }
      

      结论:如果一个类实现了子接口,则需要将父接口和子接口中的所有方法实现。如果只是实现了父接口,则只需要实现父接口中的方法就可以。


17.3 抽象类与接口的区别

  • 17.3.1 语法层面上的区别
    1. 抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法和static静态方法**(JDK1.8以后可以)**,这个静态必须写实体;
    2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;
    3. 接口中不能含有静态代码块,可以写静态方法**(JDK1.8以后可以)**,但是静态方法必须有实体,只能用接口名.方法名调用。而抽象类可以有静态代码块;
    4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
  • 17.3.2 设计层面上的区别
    1. 抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。举个简单的例子,飞机和鸟是不同类的事物,但是它们都有一个共性,就是都会飞。那么在设计的时候,可以将飞机设计为一个类Airplane,将鸟设计为一个类Bird,但是不能将 飞行 这个特性也设计为类,因此它只是一个行为特性,并不是对一类事物的抽象描述。此时可以将 飞行 设计为一个接口Fly,包含方法fly( ),然后Airplane和Bird分别根据自己的需要实现Fly这个接口。然后至于有不同种类的飞机,比如战斗机、民用飞机等直接继承Airplane即可,对于鸟也是类似的,不同种类的鸟直接继承Bird类即可。从这里可以看出,继承是一个 "是不是"的关系,而 接口 实现则是 "有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如鸟是否能飞(或者是否具备飞行这个特点),能飞行则可以实现这个接口,不能飞行就不实现这个接口。
    2. 设计层面不同,抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。什么是模板式设计?最简单例子,大家都用过ppt里面的模板,如果用模板A设计了ppt B和ppt C,ppt B和ppt C公共的部分就是模板A了,如果它们的公共部分需要改动,则只需要改动模板A就可以了,不需要重新对ppt B和ppt C进行改动。而辐射式设计,比如某个电梯都装了某种报警器,一旦要更新报警器,就必须全部更新。也就是说对于抽象类,如果需要添加新的方法,可以直接在抽象类中添加具体的实现,子类可以不进行变更;而对于接口则不行,如果接口进行了变更,则所有实现这个接口的类都必须进行相应的改动。
    3. 举例:
      • 门和警报的例子:门都有open( )和close( )两个动作,此时我们可以定义通过抽象类和接口来定义这个抽象概念:

          //定义抽象类
          abstract class Door {
              public abstract void open();
              public abstract void close();
          }
          //或者
          interface Door {
              public abstract void open();
              public abstract void close();
          }
        
      • 但是现在如果我们需要门具有报警alarm( )的功能,那么该如何实现?下面提供两种思路:

        1. 将这三个功能都放在抽象类里面,但是这样一来所有继承于这个抽象类的子类都具备了报警功能,但是有的门并不一定具备报警功能;
        2. 将这三个功能都放在接口里面,需要用到报警功能的类就需要实现这个接口中的open( )和close( ),也许这个类根本就不具备open( )和close( )这两个功能,比如火灾报警器。
      • 从这里可以看出, Door的open() 、close()和alarm()根本就属于两个不同范畴内的行为,open()和close()属于门本身固有的行为特性,而alarm()属于延伸的附加行为。因此最好的解决办法是单独将报警设计为一个接口,包含alarm()行为,Door设计为单独的一个抽象类,包含open和close两种行为。再设计一个报警门继承Door类和实现Alarm接口。

          interface Alram {
              void alarm();
          }
           
          abstract class Door {
              void open();
              void close();
          }
           
          class AlarmDoor extends Door implements Alarm {
              void oepn() {
                //....
              }
              void close() {
                //....
              }
              void alarm() {
                //....
              }
          }
        
  • 17.3.3 什么时候使用抽象类和接口
    1. 如果你拥有一些方法并且想让它们中的一些有默认实现,那么使用抽象类吧。
    2. 如果你想实现多重继承,那么你必须使用接口。由于Java不支持多继承,子类不能够继承多个类,但可以实现多个接口。因此你就可以使用接口来解决它。
    3. 如果基本功能在不断改变,那么就需要使用抽象类。如果不断改变基本功能并且使用接口,那么就需要改变所有实现了该接口的类。

17.4 作业

  1. 编写程序表示职员的基本信息与薪酬计算(一下1234为一个题目):

    1. 定义抽象类 Employee 表示职员。
      • 包含私有的属性:姓名,基本工资,家庭住址,该月休假数。
      • 为Employee类提供构造函数,为每个属性赋值。
      • 编写show()方法显示所有信息
      • 编写方法:calculateLessPay() 计算扣除。规则为无休假不扣除,5天以内(包含5天)扣除基本工资的4分之1,超过5天扣除基本工资的一半。
      • 编写抽象方法calculateTotal()计算工资总额。
    2. 编写子类Director 表示董事长。
      • 包含私有属性transportAllowance 表示交通补助。
      • 给该类提供构造函数,给每个属性赋值,注意使用super。
      • 重写calculateTotal()方法计算董事长的工资总额,计算标准为:基本工资+岗位津贴5000+交通补助。
      • 重写show()方法显示基本信息,并显示董事长的总工资和实际工资(总工资-应扣工资)。
    3. 编写子类 Manager 表示部门经理。
      • 类中包含私有属性:Department 表示部门。
      • 给该类提供构造函数,给每个属性赋值,注意使用super。
      • 重写calculateTotal()方法计算部门经理的工资总额,计算标准为:基本工资+岗位津贴1500+绩效工资:基本工资的20%。
      • 重写show()方法,显示基本信息,所在部门名称,以及总工资与实际工资(总工资-应扣工资)
    4. 在测试类中分别创建董事长与部门经理的实例,分别调用show()方法显示信息。
  2. 使用抽象概念定义电话的继承体系:

    • 定义抽象类电话:包含抽象方法:void sendCall(String name) ; 打电话。void getCall(String name); 接电话。
    • 定义上网设备接口Net:包含抽象方法 getInterNet();
    • 定义 用电设备接口 Elec。包含抽象方法 on(); off();
    • 定义手机类,能打电话接电话 发短信还能上网以及能开机关机。
    • 定义电脑类,能开机关机,能上网在测试类中测试手机与电脑的功能。
  3. 改写下面的类:

    • 第一题:
      1. 门类:具有开、关方法:

         /**
          * 门类:具有开、关方法
          */
         public class Door {
         	public void open() {
         		System.out.println("门开了");
         	}
         	
        
         	public void close() {
         		System.out.println("门关了");
         	}
         }
        
      2. 电脑类:具有开、关方法:

         /**
          * 电脑类:具有开、关方法
          */
         public class Computer {
         	public void open() {
         		System.out.println("电脑开了");
         	}
         	
        
         	public void close() {
         		System.out.println("电脑关了");
         	}
         }
        
      3. 用OOP(面向对象)的思想,改造上述的类。要求注意代码的重性与可扩展性。注意:是使用继承还是使用接口实现?(使用接口实现,门与电脑并不属于同一种类型,但都有开,关的空能,所以写一个带可开关设备的接口,门与电脑来实现这个接口后续题目依照此思路自己思考)。

    • 第二题:
      1. 兔子类:有吃和睡的方法:

         /**
          * 兔子类:有吃和睡的方法
          */
         public class Rabbit {
         	public void eat() {
         		System.out.println("兔子吃草");
         	}
         	
         	public void sleep() {
         		System.out.println("兔子睡了");
         	}
         }
        
      2. 老虎类:有吃和睡的方法:

         /**
          * 老虎类:有吃和睡的方法
          */
         public class Tiger {
         	public void eat() {
         		System.out.println("老虎吃草");
         	}
         	
         	public void sleep() {
         		System.out.println("老虎睡了");
         	}
         }
        
      3. 请OOP的思想,修改上面类。要求注意代码的重性与可扩展性。注意:使用接口还是抽象类。

    • 第三题:
      1. 汽车类:有启动和停止方法:

         /**
          * 汽车类:有启动和停止方法
          */
         public class Car {
         	public void strat() {
         		System.out.println("汽车启动了");
         	}
         	
         	public void stop() {
         		System.out.println("汽车停止了");
         	}
         }
        
      2. 项目类:有启动和停止方法:

         /**
          * 项目类:有启动和停止方法
          */
         public class Project {
         	public void start() {
         		System.out.println("项目启动了");
         	}
         	
         	public void stop() {
         		System.out.println("项目停止了");
         	}
         }
        
        1. 请OOP的思想,修改上面类。要求注意代码的重性与可扩展性。注意:使用接口还是抽象类。
  4. 观察以下代码,说明属性有多态吗?

     class C{
     	int a=10;
     }
     
     class D extends C{
     	int a=5;
     }
     
     class Test{
     	public static void main(String[] args){
     		C c=new D();
     		System.out.println(c.a);//10
     		
     		D d=(D)c;
     		System.out.println(d.a);//5
     	}
     	
     }
    
  5. (封装,构造。注意:所有类、属性、方法命名全部要用英文或拼音,不能使用中文!)写一个职员类,类中有属性:姓名,年龄,工资。

    1. 为了保护数据,所以将所有属性设置为私有的。
    2. 使用一个带参的构造方法将这些属性初始化,要求姓名长度必须大于等于2(默认值:未知),年龄必须大于等于18(默认值:18),工资大于等于0。
    3. 一个显示所有信息的方法,要求在此方法中输出“我的姓名是:XX,我的年龄是:XX,我的工资有:XX”。
  6. (继承)再写一个经理类,继承职员类,(因为一个经理也是一个职员)。

    1. 类中增加属性:奖金。然后为此类写一个带参的构造方法,要求传入:姓名,年龄,工资及奖金。
    2. 在构造方法中使用super关键字调用父类的带参构造方法初始化:姓名,年龄,工资字段,再使用this关键字初始化奖金字段,要求奖金至少1000。
    3. 类中重写职员类中的显示信息的方法,要求输出“我的姓名是:XX,我的年龄是:XX,我的工资有:XX,我的奖金有:XX”。
    4. 测试:职员 a=new 经理(“小样”,19,1200,2000);a.显示信息();
  7. (抽象)写一个人类,类中有属性:姓名,年龄,性别。

    1. 一个构造方法属性初始化,一个出行的方法,但是这个方法描绘不清楚(因为每个人的出行方式是不一样),所以抽象此方法。
    2. 再写一个学生类继承人类型,类中有属性:学生的学号,一个构造方法给属性赋值(父类的属性super()赋值),重写父类的出行方法:描绘“学生骑自行车出行”。
    3. 再写一个工人类继承人类型,类中有属性:工人的工作证号,一个构造方法给属性赋值(父类的属性super()赋值),重写父类的出行方法:描绘“工人坐公交车出行”。
    4. 再写一个老板类继承人类型,类中有属性:老板的私家车号,一个构造方法给属性赋值(父类的属性super()赋值),重写父类的出行方法:描绘“老板开车出行”。
    5. 写一个测试类,定义一个人类数组,长度为3,分别生成学生对性,工人对象,老板对象,调用对象的出行方法实现多态。
  8. (接口)接口和多态:

    1. 编写USB接口,USB接口有启动和停止二个方法

      	public interface USB{
      		public void start();
      		public void end();
      }
      
    2. 编写U盘类,实现USB接口

      public class U implements USB{
      
      }
      
    3. 编写移动硬盘类,实现USB接口

    4. 编写电脑类,电脑提供一个test方法,在test方法中调用启动和停止方法

  9. (接口)文员类。

    1. 类中有行为:public void 写文章()此行为将内容写在本子上。
    2. 刻字机类,类中也有行为:public void 写文章()此行为将内容刻在铁板上。
    3. 再写一个公司类,类中有方法:public void 记录公司会议内容(文员 a)。
    4. 使用文员来记录内容,类中还有一个方法:public void 记录公司会议内容(刻字机 a);使用刻字机来记录内容。
    5. 以上设计使用了方法重载实现“记录公司会议内容”,但是如果再来一个电脑,他也有行为:public void 写文章();此行为将内容写到电脑中,是不是还要在公司类中再写一个方法来使用电脑操作员呢?请使用接口编程技术重新设计此题。
  • 18
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值