Java面向对象笔记 • 【第4章 抽象类和接口】

全部章节   >>>>


本章目录

4.1 抽象类

4.1.1 抽象方法和抽象类

4.1.2 抽象类的作用

4.1.3 实践练习

4.2 final修饰符

4.2.1 final成员变量

4.2.2 final局部变量

4.2.3 final方法

4.2.4 final类

4.2.5 实践练习

4.3 接口

4.3.1 接口的定义

4.3.2 接口的实现

4.3.3 抽象类和接口的区别

4.3.4 实践练习

4.4 接口编程案例

4.4.1 接口编程实例

4.4.2 实践练习

总结:


4.1 抽象类

如何定义图形类计算周长的通用方法?

  • 不同的图形类图形计算周长的方式大相径庭所以导致Shape类的calcPerimeter()方法无法运用某个固定的计算图形周长的公式
  • 可以将calcPerimeter()方法定义为抽象方法,抽象方法没有具体的方法实现,该方法必须由其继承的子类重写,这样该方法就起到了约束规范的作用,又不影响类最初的设计思路。

4.1.1 抽象方法和抽象类

定义抽象类语法

[访问修饰符]   abstract  class  类名

定义抽象方法语法

[访问修饰符]   abstract  返回类型  方法名([参数列表])

抽象类和抽象方法规则:

个抽象类中可以不定义抽象方法,但是只要类中有一个抽象方法,则该类一定是抽象类。

抽象类不能被实例化,即不能被new创建一个实例对象。

如果一个子类继承一个抽象类,则子类需要通过覆盖的方式来重写该抽象类中的所有抽象方法。如果子类没有完全重写抽象父类中所有的抽象方法,则子类仍是抽象的。

抽象方法可以与publicprotected复合使用,但不能与finalprivatestatic复合使用。

abstract 不能用于修饰属性,不能用于修饰局部变量,也不能用于修饰构造

示例:图形继承关系中使用抽象类和抽象方法

// 抽象类 图形Shape :

public abstract class Shape { // 抽象类 图形
	private  String color;
 
	// 定义一个计算周长的抽象方法
	public abstract double calcPerimeter();
	// 定一个返回图形子类型的方法
	public abstract String  getType();
	// 有参构造方法
	public Shape(String color){
		this.color=color;
		System.out.println("---执行了Shape类的构造方法---");
	}
	public String getColor() {
		return color;
	}
	public void setColor(String color) {
		this.color = color;
	}
}

// 子类 三角形Triangle :

public class Triangle  extends Shape{ //子类 三角形
	private  double  x,y,z; //定义三角形边
	 
	public Triangle(String color,double x,double y,double z) {
		super(color);	//调用父类构造器	
		this.setSide(x, y, z);
	}
    	public  void setSide(double x,double y,double z){//设置三角形三边
		if(x>y+z || y>x+z || z>x+y){
		   System.out.println("三角形两边之和必须大于第三边");
		   return ;
		}
		this.x=x, this.y=y,this.z=z;
	}
	public double calcPerimeter() {//重写父类计算周长方法
		return x+y+z;
	}
	public String getType() {//重写父类获取图形类型方法
		return "三角形";
	}
}

//子类 圆形Circle :

public class Circle extends Shape { //子类 圆形
	private  double r;//半径
 
	public Circle(String color,double r) {
		super(color);
		this.r=r;
	}
	public double calcPerimeter() {//重写父类计算周长方法
		return  2*Math.PI*r;
	}
	//重写父类获取图形类型方法
	public String getType() {
		return "圆形";
	}
	public double getR() {
		return r;
	}
	public void setR(double r) {
		this.r = r;
	}

main方法

	public static void main(String[] args) { // 测试
		Shape triangle=new Triangle("红色", 3, 4, 5);
		Shape circle=new Circle("红色", 4);		
		System.out.println(triangle.getType()+" 周长="+triangle.calcPerimeter());
		System.out.println(circle.getType()+" 周长="+circle.calcPerimeter());
	}

4.1.2 抽象类的作用

避免子类设计的随意性:

  • 抽象类不能被实例化,只能作为父类继承。
  • 从多个具有相同特征的类中抽象出一个抽象类,以该抽象类作为其子类的模板

体现了模板模式的设计理念:

  • 类作为多个子类的通用模板
  • 子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式
  • 编写一个抽象父类,父类提供多个子类的通用方法,并且将一个或多个方法留给其子类实现,这就是一种模板模式

示例:在速度表应用上使用模板设计模式

// 抽象父类速度表 SpeedMeter ,该抽象类为模板类

public abstract  class SpeedMeter { //抽象父类速度表,该抽象类为模板类
	private  double trunRate; //转速
	//将返回车轮半径的方法定义为抽象方法
	public abstract double getRedius();
	
   	 //定义计算速度的通用算法
	public  double  getSpeed(){
	             //速度=车轮半径*2*π*转速
	            return  2*3.14*getRedius()*getTrunRate();
	}
	public double getTrunRate() {
		return trunRate;
	}
	public void setTrunRate(double trunRate) {
		this.trunRate = trunRate;
	}
}

// 子类汽车速度表 CarSpeedMeter

public class CarSpeedMeter extends SpeedMeter{ //子类汽车速度表
	//重写父类的获取半径的方法
	public double getRedius() {
		return 2.0;
	}
	public static void main(String[] args) {
	            //创建CarSpeedMeter对象
	           CarSpeedMeter csm=new CarSpeedMeter();
	           csm.setTrunRate(15);//设置转速
	           System.out.println("当前汽车时速="+csm.getSpeed()+"公里/小时");
	}
}

SpeedMeter类中提供了速度表的通用算法,但一些具体的实现细节则推迟到其子类CerSpeedMeter类中实现。这是一种典型的模板模式

模板模式基本规则:

抽象父类仅定义使用的某些方法,将不能实现的部分抽象成抽象方法,留给其子类实现。

类中包含需要调用其他依赖的方法,这些被调方法既可以由父类实现,也可以由其子类实现。父类中提供的方法仅定义了一个通用算法,需要具体子类辅助实现。

4.1.3 实践练习

 

4.2 final修饰符

  • final关键字可以用于修饰类、变量和方法
  • final修饰变量时,表示该变量一旦获得了初始值就不可能被改变,final修饰的类不能被继承,final修饰的方法可以被继承但不能被重写
  • final意味着终极。

4.2.1 final成员变量

  • 对于final修饰的成员变量而言,一旦赋初始值就不能被重新赋值
  • final修饰的类属性只能静态初始化块或声明该属性时指定初始值
  • final修饰的实例属性只能构造方法声明该属性时指定初始值。

示例:演示final成员变量使用

4.2.2 final局部变量

  • 系统不会对局部变量进行初始化,局部变量必须由程序员显式初始化。因此,使用final修饰局部变量时,既可以在定义时指定默认值,也可以不指定默认值
  • final修饰的局部变量在定义时没有指定默认值,则应在后面的代码中对该final变量赋初始值,但只能赋值一次,不能重复赋值。
  • 如果final修饰的局部变量在定义时已经指定默认值,则后面代码中不能对该变量赋值。

示例:演示final局部变量使用

4.2.3 final方法

  • final修饰的方法不可以被重写
  • 如果不允许子类重写父类的某个方法,则可以使用final修饰该方法

示例:演示final方法使用

public class Bank { 
                //将该方法定义为final,不允许子类重写该方法
                 public final void LowerInterestRates(){ 
	         System.out.println(“央行按照宏观经济情况进行统一降息,各银行按照国家标准统一执行");
	}
}

public class ChinaBank extends Bank{	 
   	//如果子类重写父类的fianl()方法,程序在编译时将报错
   	public final void LowerInterestRates(){
   }
}

4.2.4 final类

  • final修饰符修饰的类称为最终类,最终类不能有子类
  • final类通常固定作用用于完成某种标准功能
  • final不能被继承以达到不能被修改的目的

示例:演示final类使用

public final class Bank {  // final类
	public void LowerInterestRates(){ 
	         System.out.println(“央行按照宏观经济情况进行统一降息,各银行按照国家标准统一执行");
	}
}

public class ChinaBank extends Bank{ //报错,final类不能有子类 
   		
   }
}

4.2.5 实践练习

 

4.3 接口

举例:生活中的接口

Java中的接口同生活中的接口一样,体现的是一种能力。

4.3.1 接口的定义

语法:

[访问修饰符] interface  接口名  extends  父接口1,[父接口2,...]

说明:

接口的访问修饰符可以public和缺省访问修饰符,如果省略public修饰符,系统默认使用缺省访问修饰符

接口中只能定义公有的、静态的常量,并且这些常量默认都是公有的、静态的。

接口中的方法只能是公有的抽象方法,并且这些方法默认都是公有的、抽象的。

接口只能继承多个接口,接口不能继承类,也不能实现其他接口。

4.3.2 接口的实现

语法:

[访问修饰符]  class  类名  implements  接口1,[接口2,...]

说明:

接口是一种标准的体现

接口不能用于创建实例,接口的主要作用是在设计程序时对其实现类进行规范和约束

接口的主要用途就是被实现类实现。

一个类可以实现多个接口,从而实现多继承。

示例:设计一个具有输入功能,并且防水、防尘功能的键盘,以及一个防水、防尘和防盗功能的防盗门

public interface  Input { //输入接口
    	//定义输入的标准,由其实现类实现具体的实现细节
	public abstract void input();  // abstract 可省略
}

public interface Function { //功能接口
	//接口只能定义常量, 而且必须是静态常量
	public static final String DEEP="30米";//防水深度
	//防尘指数
   	 int INDEX=5;  //接口中的常量默认是公有的、静态、终极的
	//防水功能
	public abstract void waterproof();
	//防尘功能
   	 void dust(); //接口中的方法默认是公有的、抽象的
}

 

public interface ExtendsFunction extends Function { //扩展功能
             	void  antiTheft(); //防盗
}
public class AntitheftDoor implements ExtendsFunction { //防盗门实现扩展功能接口
                   //实现防水功能
	public void waterproof() {
		System.out.println("我是防盗门采用高科技的防水技术,防水深度:"+DEEP);
	}
                    //实现防尘功能
	public void dust() {
		System.out.println("我是防盗门采用纳米防尘技术防尘,防尘指数:"+INDEX+"颗星");
	}
                     //实现防盗功能
	public void antiTheft() {
		System.out.println("我是防盗门采用密码匹配防盗技术");
	}
}

 

public class Keyboard implements Function,Input{ //键盘类实现功能接口和输入接口
   	//实现防水功能
	public void waterproof() {
		System.out.println("我是键盘采用特殊的密封屏蔽技术实现防水,防水深度:"+DEEP);
	}
	 //实现防尘功能
	public void dust() {
		System.out.println("我是键盘采用全硅胶材料实现防尘功能,防尘指数:"+INDEX+"颗星 ");		
	}
    	//实现Input接口中的输入功能
	public void input() {
		System.out.println("我是键盘可以将敲击键盘上的数据输入到计算机中");
	}
}

 

public class Tset{
            public static void main(String[] args) {
		   //创建键盘对象
		   Keyboard keyboard=new Keyboard();
		   keyboard.dust();//调用键盘的防尘方法
		   keyboard.waterproof();//调用键盘的防水方法
		   keyboard.input();//调用输入方法
		   System.out.println("------------------------------------");
		   //创建防盗门对象
		   AntitheftDoor  antitheftDoor=new AntitheftDoor();
		   antitheftDoor.antiTheft();//调用防盗门的防盗方法
		   antitheftDoor.dust();//调用防盗门的防尘方法
		   antitheftDoor.waterproof();//调用防盗门的防水方法
	}
}

4.3.3 抽象类和接口的区别

相同点:

接口和抽象类都不能被实例化,它们都位于继承树的顶端,用于被其他类实现和继承。

接口和抽象类都可以包含抽像方法,实现接口或继承抽象类的普通子类都必须实现这些抽象方法

不同点:

接口中只能包含抽象方法,而抽象类则完全可以包含普通方法。

接口中不能定义静态方法,而抽象类中可以定义静态方法。

接口中只能定义静态常量,不能定义普通变量,或非静态的常量,而抽象类中则可以定义不同的属性,也可以定义静态的属性。

接口中不包含构造器,而抽象类中可以包含构造器,抽象类中的构造器并不用于创建对象,而是让其子类调用这些构造器来完成抽象类的初始化操作。

个类最多只能有一个直接父类,包括抽象类,而一个类可以实现多个接口。通过实现多个接口可以弥补Java单继承的不足。

4.3.4 实践练习

 

4.4 接口编程案例

什么是面向接口编程?

面向接口编程就是先把客户的业务逻辑线提取出来作为接口,接口业务的具体实现通过该接口的实现类来完成。

当客户需求变化时,只需编写该业务逻辑的新的实现类,通过更改配置文件中接口的实现类就可以完成需求,而不需要改写现有代码,从而减少对系统的影响。

面向接口编程的优点:

降低程序的耦合性。在程序中紧密的联系并不是一件好的事情,因为两种事物之间联系越紧密,更换其中之一的难度就越大。

易于系统的扩展。

易于系统的维护。

4.4.1 接口编程实例

使用面向接口编程实现一个简易的用户管理系统

要求用户按照系统在控制台中的提示信息,做出相应的选择

系统接收到用户输入的指令后,给出相应的提示信息

创建dto包,在该包中定义类UserInfo用于封装用户信息。

public class UserInfo { //用户实体类
      private String name;
      private int age;
      private String birthday;
      private String address;
	  
       //省略属性的setter和getter方法
}

创建dao,在该包中定义一个维护用户信息的接口UserInfoDao,在该接口中定义3个抽象方法,它们的功能分别是删除用户、更新用户信息和保存用户。

public interface UserInfoDao { //维护用户信息接口
            public abstract void deleteUser(UserInfo user);
            public abstract void updateUser(UserInfo user);
            public abstract void saveUser(UserInfo user);
}

在该dao定义一个实现UserInfoDao的用户信息维护UserInfoDaoImpl,该类分别实现接口中所有的抽象方法。

public class UserInfoDaoImpl implements UserInfoDao { //实现用户信息维护接口
	public void saveUser(UserInfo user) {
		System.out.println("执行新增方法");
		System.out.println("姓名:"+ user.getName() +"  年龄:"+ 			                    user.getAge() + "  生日:"+ user.getBirthday() + "  家庭地址:"+ user.getAddress());
	}
	public void updateUser(UserInfo user) {
		System.out.println("执行更新方法");
	}
	public void deleteUser(UserInfo user) {
		System.out.println("执行删除操作");
                   }
}

创建business包,在该包中定义UserInfoAccess类,在该类定义UserInfoDao对象属性,值为实现UserInfoDao接口的具体对象

在UserInfoAccess类定义服务方法service()实现主体业务。

public class UserInfoAccess {
	private UserInfoDao userInfoDao=new UserInfoDaoImpl();
	
	public void service(){
		System.out.println("请选择操作【1】添加【2】修改【3】删除");
		Scanner input=new Scanner(System.in);
		String state=input.next();
		if("1".equals(state)){
			userInfoDao.saveUser();
		}else if("2".equals(state)){
			userInfoDao.updateUser();
		}else if("3".equals(state)){
			userInfoDao.deleteUser();
		}
	}
}

public class UserMain{
	 public static void main(String[] args) {
	       new UserInfoAccess().service();
	}
}

4.4.2 实践练习

 

总结:

个抽象类中可以不定义抽象方法,但是只要类中有一个抽象方法,则该类一定是抽象类

抽象类不能被实例化,只能作为父类继承

final关键字可以用于修饰类、变量和方法。final修饰变量时,表示该变量一旦获得了初始值就不可能被改变final修饰的类不能被继承,final修饰的方法可以被继承但不能被重写。

接口中只能定义公有的、静态的常量,并且这些常量默认都是公有的、静态的;接口中的方法只能是公有的抽象方法,并且这些方法默认都是公有的、抽象的。

接口只能继承多个接口,接口不能继承类,也不能实现其他接口。

  • 22
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 15
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明金同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值