什么是抽象类和接口?区别在哪里?
下面代码是一个比较典型的抽象类的使用场景(模板设计模式).
Logger是一个记录日志的抽象类,FileLogger和MessageQueueLogger继承Logger,分别实现两种不同的日志记录方式:记录日志到文件中和记录日志到消息队列中.FileLogger和MessageQueueLogger两个子类复用父类的Logger中的name、enabled、minPermittedLevel属性和log()方法,但因为这两个子类写日志的方式不同,它们又各自重写了父类中的doLog()方法.
点击查看代码
// 抽象类
public abstract class Logger {
private String name;
private boolean enabled;
private Level minPermittedLevel;
public Logger(String name, boolean enabled, Level minPermittedLevel) {
this.name = name;
this.enabled = enabled;
this.minPermittedLevel = minPermittedLevel;
}
public void log(Level level, String message) {
boolean loggable = enabled && (minPermittedLevel.intValue() <= level.intValue());
if (!loggable) return;
doLog(level, message);
}
protected abstract void doLog(Level level, String message);
}
// 抽象类的子类:输出日志到文件
public class FileLogger extends Logger {
private Writer fileWriter;
public FileLogger(String name, boolean enabled,
Level minPermittedLevel, String filepath) {
super(name, enabled, minPermittedLevel);
this.fileWriter = new FileWriter(filepath);
}
@Override
public void doLog(Level level, String mesage) {
// 格式化level和message,输出到日志文件
fileWriter.write(...);
}
}
// 抽象类的子类: 输出日志到消息中间件(比如kafka)
public class MessageQueueLogger extends Logger {
private MessageQueueClient msgQueueClient;
public MessageQueueLogger(String name, boolean enabled,
Level minPermittedLevel, MessageQueueClient msgQueueClient) {
super(name, enabled, minPermittedLevel);
this.msgQueueClient = msgQueueClient;
}
@Override
protected void doLog(Level level, String mesage) {
// 格式化level和message,输出到消息中间件
msgQueueClient.send(...);
}
}
通过上面的例子,可以得到抽象类的以下结论:
- 抽象类不允许实例化,只能被继承,也就是说,你不能new一个抽象类的对象出来(Logger logger = new Logger(...),会报编译错误).
- 抽象类可以包含属性和方法,方法既可以包含代码实现,也可以不包含代码实现.不包含代码实现的方法叫做抽象方法.
- 子类继承抽象类,必须实现抽象类中的所有抽象方法,对应到上面的代码就是,所有继承Logger抽象类的子类,都必须重写doLog()方法.
下面代码是一个比较典型的接口的使用场景.
点击查看代码
// 接口
public interface Filter {
void doFilter(RpcRequest req) throws RpcException;
}
// 接口实现类:鉴权过滤器
public class AuthencationFilter implements Filter {
@Override
public void doFilter(RpcRequest req) throws RpcException {
//...鉴权逻辑..
}
}
// 接口实现类:限流过滤器
public class RateLimitFilter implements Filter {
@Override
public void doFilter(RpcRequest req) throws RpcException {
//...限流逻辑...
}
}
// 过滤器使用Demo
public class Application {
// filters.add(new AuthencationFilter());
// filters.add(new RateLimitFilter());
private List<Filter> filters = new ArrayList<>();
public void handleRpcRequest(RpcRequest req) {
try {
for (Filter filter : filters) {
filter.doFilter(req);
}
} catch(RpcException e) {
// ...处理过滤结果...
}
// ...省略其他处理逻辑...
}
}
我们通过Java中的interface关键字定义了一个Filter接口.AuthencationFilter和RateLimitFilter是接口的两个实现类.分别实现了对RPC请求鉴权和限流的过滤功能.
接口的特性:
1. 接口不能包含属性(也就是成员变量)
2. 接口只能声明方法,方法不能包含代码实现
3. 类实现接口的时候,必须实现接口中声明的所有方法.
抽象类和接口的应用场景
实际上,判断的标准很简单.如果要表示一种is-a的关系,并且是为了解决代码的复用问题,我们就用抽象类;如果要表示_x0008_一种has-a关系,并且是为了解决抽象而非代码复用问题,那就用接口.