目录
在 Java 中,接口(Interface) 是一种非常重要的结构,它定义了一组方法的集合,但没有提供这些方法的具体实现。接口主要用于定义行为规范或契约,而不关心如何实现这些行为。通过使用接口,可以实现不同类之间的低耦合性、高内聚性以及代码的重用。
接口到底是什么?
你可以把接口(Interface)想象成一份 “功能清单” 或者说是 “行为协议”。
它不是具体怎么做的,而是说:
“你要是想当这个角色,就必须会做这些事。”
🧑🏫 举个生活中的例子:手机充电线
假设你现在有一根充电线,它是 Type-C 的。你拿它给不同的手机充电:
- 小米手机
- 华为手机
- 蓝牙耳机
- 平板电脑
它们内部实现不一样,但只要支持 Type-C 接口,就能充电。
👉 这个 Type-C 接口就是一个“标准”,谁想插进来,就得按照这个标准来。
💡 在 Java 中的类比:
public interface Animal {
void makeSound(); // 所有动物都会叫
}
这是一个“动物”的接口,表示只要是“动物”,都必须能“叫”。
然后不同的动物去实现它:
public class Dog implements Animal {
public void makeSound() {
System.out.println("汪汪!");
}
}
public class Cat implements Animal {
public void makeSound() {
System.out.println("喵~");
}
}
虽然狗和猫都会叫,但叫声不一样。这就是:
统一接口,不同实现
✅ 接口有什么用?一句话总结:
接口就像一个“能力说明书”,规定了你能做什么,但不关心你是怎么做到的。
🔁 接口的好处(再通俗点)
| 好处 | 通俗解释 |
|---|---|
| 统一规范 | 大家都按同一个规则做事,不会乱套 |
| 多态 | 同一个动作,不同对象有不同的表现(比如猫叫、狗叫) |
| 解耦 | 类和类之间不直接依赖,只依赖接口,方便替换 |
| 易扩展 | 想加新功能?写一个新类实现接口就行,不用改老代码 |
🛠 实际开发中接口常用于:
- 定义数据访问层(DAO)的方法(如
save(),delete()) - 定义服务逻辑接口(Service)
- 定义回调函数(监听器)
- Spring 中大量使用接口来做依赖注入(DI)和面向接口编程
总结一句话:
接口就像是一个“合同”,规定了你要完成哪些任务,但不管你是怎么完成的。这样大家都能合作,又互不影响
几个通俗易懂的生活场景和开发场景的例子
🎯 场景一:支付系统(支付宝 和 微信)
🧩 问题背景
我们想做一个电商系统,支持多种支付方式,比如:
- 支付宝支付
- 微信支付
- 银行卡支付
这些支付方式内部逻辑不一样,但对外暴露的功能是一样的:支付、退款
使用接口的好处
我们可以定义一个 Payment 接口:
public interface Payment {
void pay(double amount);
void refund(double amount);
}
然后让不同的支付方式去实现它:
public class Alipay implements Payment {
public void pay(double amount) {
System.out.println("支付宝支付了:" + amount + "元");
}
public void refund(double amount) {
System.out.println("支付宝退款了:" + amount + "元");
}
}
public class WeChatPay implements Payment {
public void pay(double amount) {
System.out.println("微信支付了:" + amount + "元");
}
public void refund(double amount) {
System.out.println("微信退款了:" + amount + "元");
}
}
使用示例
public class OrderService {
public void checkout(Payment payment, double amount) {
payment.pay(amount);
}
}
// 主程序中
OrderService service = new OrderService();
service.checkout(new Alipay(), 199.0); // 支付宝付款
service.checkout(new WeChatPay(), 299.0); // 微信付款
总结
我们通过一个统一的
Payment接口,实现了不同支付方式的插拔式使用,代码灵活又解耦。
🎯 场景二:日志记录器(Log4j 和 Logback)
🧩 问题背景
在项目中,你可能一开始用的是 Log4j 做日志记录,后来换成 Logback。如果你直接写死调用 Log4j 的方法,那么更换日志框架就要改很多代码。
✅使用接口的好处
我们可以定义一个通用的日志接口:
public interface Logger {
void info(String message);
void error(String message);
}
然后分别实现两个日志工具:
public class Log4jLogger implements Logger {
public void info(String message) {
// 调用 Log4j 的 info 方法
System.out.println("[Log4j] INFO: " + message);
}
public void error(String message) {
// 调用 Log4j 的 error 方法
System.out.println("[Log4j] ERROR: " + message);
}
}
public class LogbackLogger implements Logger {
public void info(String message) {
System.out.println("[Logback] INFO: " + message);
}
public void error(String message) {
System.out.println("[Logback] ERROR: " + message);
}
}
这样你的业务代码就可以依赖这个接口:
public class App {
private Logger logger;
public App(Logger logger) {
this.logger = logger;
}
public void doSomething() {
logger.info("开始执行任务");
// ...
logger.error("发生错误");
}
}
运行时切换日志工具:
App app1 = new App(new Log4jLogger());
app1.doSomething();
App app2 = new App(new LogbackLogger());
app2.doSomething();
总结
使用接口后,你可以随时替换底层实现,而不需要修改业务代码,这就是“面向接口编程”
🎯 场景三:登录系统(账号密码登录 和 第三方登录)
🧩 问题背景
现在大多数网站都支持多种登录方式:
- 用户名+密码登录
- 微信扫码登录
- QQ 登录
- GitHub 登录
每种方式验证逻辑不同,但最终目标都是“用户登录成功”。
定义接口
public interface LoginHandler {
boolean login(String tokenOrUsername, String password);
}
实现类
public class UsernamePasswordLogin implements LoginHandler {
public boolean login(String username, String password) {
// 模拟数据库校验用户名密码
return "admin".equals(username) && "123456".equals(password);
}
}
public class WeChatLogin implements LoginHandler {
public boolean login(String openId, String password) {
// password 参数其实没用,这里只是为了兼容接口
return validateWeChatToken(openId);
}
private boolean validateWeChatToken(String openId) {
// 模拟微信 token 校验
return openId != null && openId.startsWith("wx_");
}
}
使用示例
public class LoginController {
public void handleLogin(LoginHandler handler, String input1, String input2) {
if (handler.login(input1, input2)) {
System.out.println("登录成功!");
} else {
System.out.println("登录失败!");
}
}
}
// 测试
LoginController controller = new LoginController();
controller.handleLogin(new UsernamePasswordLogin(), "admin", "123456"); // 成功
controller.handleLogin(new WeChatLogin(), "wx_12345", ""); // 成功
总结一下接口的作用
| 场景 | 接口作用 |
|---|---|
| 支付系统 | 统一支付行为,便于扩展新支付方式 |
| 日志系统 | 解耦具体日志实现,方便替换 |
| 登录系统 | 抽象出登录行为,适配多种登录方式 |
什么时候应该用接口?
当你发现以下情况时,就应该考虑使用接口:
- 多个类有相同的行为,但实现方式不同
- 你希望隐藏实现细节,只暴露功能
- 你想让系统更容易扩展、维护和测试
- 你想实现多态、依赖注入或策略模式等设计思想
🎯 在spring框架中的应用
在 Spring 框架中,接口的应用非常广泛,尤其是在数据访问层(DAO)、服务层(Service)等方面。使用接口可以帮助我们实现代码的解耦、提高可测试性以及增强灵活性。下面我将通过一些具体的例子来说明这些概念。
🎯 DAO 接口的应用场景
DAO(Data Access Object) 是一种设计模式,用于将底层的数据访问逻辑与业务逻辑分离。通过定义 DAO 接口,我们可以隐藏数据访问的具体实现细节,并允许不同的实现(如 JDBC、MyBatis、Hibernate 等)。
示例:用户管理系统的 DAO 层
假设我们正在开发一个用户管理系统,需要对用户的增删改查操作。
- 定义接口
public interface UserDao {
void save(User user);
User findById(Long id);
List<User> findAll();
void delete(Long id);
}
- 实现接口
可以有多种实现方式,比如使用 JdbcTemplate 或者 MyBatis。
JdbcTemplate 实现
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void save(User user) {
String sql = "INSERT INTO users(name, email) VALUES(?, ?)";
jdbcTemplate.update(sql, user.getName(), user.getEmail());
}
@Override
public User findById(Long id) {
String sql = "SELECT * FROM users WHERE id = ?";
return jdbcTemplate.queryForObject(sql, new Object[]{id}, new BeanPropertyRowMapper<>(User.class));
}
// 其他方法略...
}
MyBatis 实现
<!-- UserMapper.xml -->
<mapper namespace="com.example.UserDao">
<insert id="save" parameterType="User">
INSERT INTO users(name, email) VALUES(#{name}, #{email})
</insert>
<select id="findById" resultType="User" parameterType="long">
SELECT * FROM users WHERE id = #{id}
</select>
</mapper>
@Mapper
public interface UserMapper extends UserDao {
// 这里不需要写方法实现,MyBatis 会根据 XML 文件自动生成实现
}
服务层调用
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void createUser(User user) {
userDao.save(user);
}
public User getUserById(Long id) {
return userDao.findById(id);
}
// 其他方法略...
}
🛠 Service 接口的应用场景
Service 层 主要负责处理业务逻辑。通过定义 Service 接口,可以使得业务逻辑更加清晰,并且便于进行单元测试和依赖注入。
示例:订单处理系统的服务层
定义接口
public interface OrderService {
void placeOrder(Order order);
List<Order> getOrdersByUserId(Long userId);
}
- 实现接口
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderRepository orderRepository;
@Override
public void placeOrder(Order order) {
// 可能包括库存检查、支付处理等复杂的业务逻辑
orderRepository.save(order);
}
@Override
public List<Order> getOrdersByUserId(Long userId) {
return orderRepository.findByUserId(userId);
}
}
- 控制器层调用
@RestController
@RequestMapping("/orders")
public class OrderController {
@Autowired
private OrderService orderService;
@PostMapping
public ResponseEntity<Void> createOrder(@RequestBody Order order) {
orderService.placeOrder(order);
return ResponseEntity.ok().build();
}
@GetMapping("/{userId}")
public ResponseEntity<List<Order>> getOrdersByUserId(@PathVariable Long userId) {
List<Order> orders = orderService.getOrdersByUserId(userId);
return ResponseEntity.ok(orders);
}
}
总结
-
DAO 接口:通过定义数据访问接口,实现了数据访问逻辑与业务逻辑的分离。这不仅提高了代码的可维护性和可扩展性,还使得单元测试变得更加容易。
-
Service 接口:服务层接口有助于封装复杂的业务逻辑,使代码结构更加清晰。同时,它也为依赖注入提供了便利,增强了代码的灵活性和可测试性。
2万+

被折叠的 条评论
为什么被折叠?



