java基础之抽象类、接口

java基础之抽象类、接口

学习过抽象类和接口后,这两个概念一直傻傻分不清楚,抽象类和接口声明的方法都不实现,有时候对它们的用法会混淆,所以到底该什么时候用抽象类,什么时候用接口呢?接下来做一个详细的区别说明

抽象类

abstract关键字与抽象类

abstract(抽象)可以用于修饰一个类,方法;被abstract修饰类成为抽象类,被abstract修饰方法,称之为抽象方法。抽象方法只能出现在抽象类或者接口中。

抽象类Animal

public abstract class Animal {
	
	private String name;
	private int age;
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

    //抽象方法(只有方法的声明,没有具体实现)
	public abstract void bark();
	
	public abstract void eat();
	
	public abstract void sleep();

}

抽象类Cat可以继承抽象类Animal ,也可以声明抽象方法

public abstract  class Cat extends Animal{

	
	public abstract void catchMouse();

}

类PetCat继承抽象类Cat ,里面的方法需要重写才可以实现

public class PetCat extends Cat {

	@Override
	public void bark() {
		
	}

	@Override
	public void eat() {
		
	}

	@Override
	public void sleep() {
		
	}

	@Override
	public void catchMouse() {
		
	}

}

为什么需要抽象类

在实际开发中,对于某些需求具体的实现不确定时,可以将这些需求对应的功能定义为抽象方法,等待后期子类进行实现,从而提高程序的扩展性。

抽象类特点

  1. 抽象类中可以包含普通类的一些特性(属性,方法,构造器)
  2. 抽象类中除了包含一些已实现的方法外还包含抽象方法(未实现)
  3. 抽象类存在构造器,但是不允许被实例化
  4. 抽象类一般由子类继承,但是子类继承了抽象类之后就必须实现抽象方法(除非子类也是抽象类)
  5. 抽象类也可以继承其他抽象类
  6. 如果子类继承了多个抽象类(多重继承) 则子类必须实现所有抽象类中未实现的方法

例题

有一个抽象类里面包含两个方法,分别用于计算图形的面积和图形的周长,请实现对于不同图形的面积和周长计算(三角形,矩形,圆形)?

Shape类(抽象类)

public abstract class Shape {
	/**
	 * 周长计算
	 */
	public abstract void around();
	
	/**
	 * 面积计算
	 */
	public abstract void area();

}

三角形 Tran类

/**
 * 三角形
 * @author 12483
 */
public class Tran extends Shape {
	
	private double a;
	private double b;
	private double c;
	
	public Tran(double a, double b, double c) {
		super();
		this.a = a;
		this.b = b;
		this.c = c;
	}

	@Override
	public void around() {
		System.out.println(a + b + c);
	}

	@Override
	public void area() {
		double t = (a + b + c)/2;
		System.out.println(Math.sqrt(t*(t-a)*(t-b)*(t-c)));
	}

}

矩形 Rect类

/**
 * 矩形
 * @author 12483
 *
 */
public class Rect extends Shape{
	
	private double width;
	private double height;
	
	public Rect(double width, double height) {
		super();
		this.width = width;
		this.height = height;
	}

	@Override
	public void around() {
		System.out.println(2*(width+height));
	}

	@Override
	public void area() {
		System.out.println(width*height);
	}

}

圆形 Round类

/**
 * 圆形
 * @author 12483
 *
 */
public class Round  extends Shape{
	
	private double r;
	
	public Round(double r) {
		super();
		this.r = r;
	}

	@Override
	public void around() {
		System.out.println(2*Math.PI*r);
	}

	@Override
	public void area() {
		System.out.println(Math.PI*r*r);
	}

}

测试类Test

public class Test {
	public static void main(String[] args) {
		
		Rect r = new Rect(10,5);
		r.area();
		r.around();
		
		Round ro = new Round(10);
		ro.area();
		ro.around();
		
		Tran t = new Tran(3,4,5);
		t.area();
		t.around();
	}

}

设计模式之——模板方法

思考:

在银行的业务中,对于利息的计算有不同的计算规则:

对于定期账户,基准年利率为4.0%

对于活期账户,基准年利率为2.0%

利率的计算有不同的计算方式

利息 = 本金 * 利率;

根据以上信息,请问如何设计对应的类完成利息的计算,要求利率需要通过方法获取?

定义算法的实现骨架,但是不具体实现,通过不同的子类实现不同的计算方法,从而获取模板方法的不同结果,比如银行业务:银行计算利息,都是利率乘以本金和存款时间,但各种存款方式计算利率的方式不同,所以,在账户这个类的相关方法里,只搭出算法的骨架,但不具体实现。具体实现由各个子类来完成。

例如:

Account类(抽象类)

public abstract class Account {
​
    //本金
    private double cash;
    //基准利率
    private double baseInterest;
    
    public Account(double cash, double baseInterest) {
        super();
        this.cash = cash;
        this.baseInterest = baseInterest;
    }
    
    public double getBaseInterest() {
        return baseInterest;
    }
​
    /**返回总利息  定义算法的计算骨架*/
    public double getTotalInterest(){
        return cash * getInterest();
    }
    
    /**计算获取利率*/
    public abstract double getInterest();
}
 

FixedAccount(定期账户)

public class FixedAccount extends Account{
​
    private int year;
    
    public FixedAccount(double cash, double baseInterest) {
        super(cash, baseInterest);
    }
​
    public int getYear() {
        return year;
    }
​
    public void setYear(int year) {
        this.year = year;
    }
​
    //实现利率的计算方法
    @Override
    public double getInterest() {
        return year * getBaseInterest() / 1.1;
    }
​
}
​

ActiveAccount(活期账户)

public class ActiveAccount  extends Account{
​
    private int month;
    
    public ActiveAccount(double cash, double baseInterest) {
        super(cash, baseInterest);
    }
    
    public int getMonth() {
        return month;
    }
​
    public void setMonth(int month) {
        this.month = month;
    }
​
    @Override
    public double getInterest() {
        return Math.sqrt(month) * getBaseInterest();
    }
​
}
​

TestAccount(测试类)

public class TestAccount {
​
    public static void main(String[] args) {
        
        FixedAccount fa = new FixedAccount(10000, 0.04);
        fa.setYear(5);
        System.out.println(fa.getTotalInterest());
        
        
        ActiveAccount aa = new ActiveAccount(10000, 0.02);
        aa.setMonth(60);
        System.out.println(aa.getTotalInterest());
    }
​
}

通过以上描述大家应该可以了解抽象类到底是什么了吧

接下来介绍接口

接口

接口(interface)是一种特殊的抽象类,本质上不是一个类,里面只能包含常量和方法的定义(不允许实现),接口就是一套未实现方法的集合。

声明语法

[修饰符] <interface> 接口名称{

​               [常量定义]

​               [抽象方法的定义]

}

例如

public interface Flyable{
    //public static final String NAME = "bird";
    String NAME = "bird";
    
    //public abstract void fly();
    void fly();
}

从以上表现来看,接口中不存在像普通类一样的属性,以及普通方法,因此接口实际上就是一套标准,既然是标准,那就必然存在实现,java中的类可以实现多个接口,从而弥补了类只能单继承的缺点。

实现类

public class Bird implements Flyable{
    
    public void fly(){
        System.out.println("自由自在的飞翔");
    }
    
}

并且接口也允许有多个子类实现

实现类2

public class plane implements Flayble{
    public void fly(){
        System.out.println("从北京飞武汉");
    }
}

同时java中的类可以实现多个接口

接口2

public interface Singable{
    void sing();
}

实现类3

public class Bird implements Flyable,Sinable{
    public void fly(){
        
    }
    public void sing(){
        
    }
}

如果一个子类实现了多个接口,那么这个子类就必须实现所有的抽象方法(除非这个子类是抽象类).

注意事项:

  1. 接口中只能存在常量和抽象方法(JDK1.8以前)
  2. 接口允许继承接口,并且能多继承
  3. 接口不允许继承任何类(包括抽象类)
  4. 接口允许有多个子类实现,并且子类可以同时实现多个接口
  5. 接口不属于类,因此不存在构造器,所以也无法实例化

接口(标准)和抽象类(类)的区别

  1. 接口通常表示对行为(动词,或形容词)的抽象;抽象类一般为对具备相同的属性的类(名词)的抽象
  2. 接口中只允许常量和抽象方法;抽象类中具备普通的类所有特征,同时也支持抽象方法
  3. 接口允许多实现,接口允许继承多个接口;抽象类只能单继承
  4. 接口不存在构造器,无法实例化;抽象类虽然存在构造器,但是也无法实例

JDK8基于接口的新特性

JDK版本更新迭代后新版本的JDK中对于接口新增很多新特性,比如:接口默认方法,静态方法,函数式接口等。

默认方法

某些情况下,可能实现类只需要对于接口中的部分方法实现而非所有方法,因此在这种需求下JDK新增了默认方法的设定,将一些实现类只需选择性实现的方法定义为default方法,这样一来,这些子类(实现类)就可以不一定实现,例如:

public interface CategoryManage {
​
    boolean add(Category c);
    
    //JDK8默认方法
    default boolean updateCname(Category c){
        return false;
    }
    
    //JDK8默认方法
    default boolean delete(int cid){
        return false;
    }
    
    ArrayList<Category> findAll();
    
    Category findById(int cid);
}
​
​

以上接口中updateCname和delete方法为默认方法,因此,子类可以无需实现这两个方法,如下:

public class CategoryManageImpl implements CategoryManage{
​
    @Override
    public boolean add(Category c) {
        // TODO Auto-generated method stub
        return false;
    }
​
    @Override
    public ArrayList<Category> findAll() {
        // TODO Auto-generated method stub
        return null;
    }
​
    @Override
    public Category findById(int cid) {
        // TODO Auto-generated method stub
        return null;
    }
​
}
 

这种操作,是JDK中一种兼容性的解决方案。

接口静态方法

JDK中除了对接口新增默认方法外,还新增接口的静态方法,即在接口直接将方法定义为静态的已实现方法,这样一来,就无需创建接口的实现类来调用,直接通过接口名.方法()即可调用,大大提高方法调用的便利性.

public interface CategoryManage {
​
    boolean add(Category c);
    
    //JDK8接口静态方法:该方法可以直接通过接口名调用(无需创建对象)
    static String showMsg(){
        return "HelloInterface";
    }
    
    ArrayList<Category> findAll();
    
    Category findById(int cid);
}

 

函数式接口(@FunctionalInterface)与lambda表达式

 

所谓函数式接口是JDK8中新增的一种概念,表名一个接口中只存在一个未实现方法(可以包含默认方法,静态方法和从Object中继承的方法)

@FunctionalInterface
public interface Flyable {
​
    void fly();
    
}
​

@FunctionalInterface是注解,用于检查接口是否只包含一个未实现的方法。

lambda表达式

public class Test{
    public static void main(String[] args){
        Flyable f = ()->{};
    }
}

实际应用

public class TestLambda {
    public static void main(String[] args) {
        //ArrayList
        List<String> list = Arrays.asList("Tom","Jarray","Lily","Lilei","Hanmeimei"); 
        //lambda表达式实际使用
        list.forEach(n->System.out.println(n)); 
    }
}

注意事项:

lambda表达式只能适用函数式接口

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值