抽象类
一句话 :抽象类是不能被实例化的类,它专门用来当"模板",让子类去继承。
类比:
-
你设计一张"动物"的蓝图,上面写着"所有动物都要会叫",但你不具体写怎么叫。
-
这张蓝图就是抽象类,你没法直接拿它造一只动物,只能用它来造具体的动物(狗、猫).相当于我只是给了我的子类一个规定,如果我的子类要做一个什么样的动作,我在父类里面规定,他做这个动作,需要用这样的方法来去写。
java
// 抽象类
abstract class Order {
protected String orderId; // 公共字段
protected double amount;
// 公共方法(已实现)
public void setOrderId(String orderId) {
this.orderId = orderId;
}
// 抽象方法:必须由子类实现
public abstract void pay();
}
// 子类
class AlipayOrder extends Order {
@Override
public void pay() {
System.out.println("使用支付宝支付:" + amount);
}
}
class WechatOrder extends Order {
@Override
public void pay() {
System.out.println("使用微信支付:" + amount);
}
}
// 使用
Order order1 = new AlipayOrder();
order1.pay(); // 输出:使用支付宝支付
接口
接口是Java中的一种引用类型,它定义了一组方法规范(方法名、参数、返回类型),但不提供具体实现。接口表示"能做什么"(能力),而不是"是什么"。
-
接口中的方法默认是
public abstract -
接口中的变量默认是
public static final(常量)】java// 定义接口 public interface Payment { boolean pay(double amount); // 方法名、参数、返回类型,没有方法体 boolean refund(String orderId); }
接口.Java文件的位置
java
src/
└── com/example/order/
├── controller/
│ └── OrderController.java
├── service/
│ ├── OrderService.java // 接口
│ └── impl/
│ └── OrderServiceImpl.java // 实现类
├── repository/
│ └── OrderRepository.java // 接口(Spring Data JPA)
└── entity/
└── Order.java
接口的具体实现
1. 实现接口的关键字:implements
一个类通过 implements 关键字来实现接口,并且必须重写接口中定义的所有抽象方法(除非该类本身是抽象类)。
java
public class Alipay implements Payment {
@Override
public boolean pay(double amount) {
// 具体的支付宝支付逻辑
System.out.println("支付宝支付:" + amount + "元");
return true;
}
@Override
public boolean refund(String orderId) {
System.out.println("支付宝退款,订单号:" + orderId);
return true;
}
}
2. 使用接口(面向接口编程)
java
public class OrderService {
private Payment payment; // 用接口类型声明变量
// 通过构造方法注入具体实现(Spring中会用@Autowired自动注入)
public OrderService(Payment payment) {
this.payment = payment;
}
public void checkout(double amount) {
payment.pay(amount); // 调用接口方法,实际执行的是实现类的代码
}
}
3. 多态效果
java
Payment p1 = new Alipay();
Payment p2 = new WechatPay();
p1.pay(100); // 执行支付宝支付
p2.pay(100); // 执行微信支付
同一个接口类型,不同的实现类,调用同一个方法产生不同的行为------这就是多态在接口中的体现。
内部类(Inner Class)
内部类就是定义在另一个类内部的类。它和普通类最大的区别是:它可以访问外部类的所有成员(包括私有成员)。
一、为什么需要内部类
-
逻辑上的归属:有些类天生就是为另一个类服务的,比如"汽车"的"发动机"类,放在外部显得多余,放在内部更合理。
-
增强封装:内部类可以访问外部类的私有成员,但不对外暴露。
-
代码组织:让结构更清晰,避免一个包下堆满几十个类文件。
二、内部类的四种类型
| 类型 | 关键字 | 特点 |
|---|---|---|
| 成员内部类 | 无 static | 作为外部类的成员,可以访问外部类的所有成员 |
| 静态内部类 | static | 不持有外部类的引用,只能访问外部类的静态成员 |
| 局部内部类 | 无 | 定义在方法内部,只能在方法内使用 |
| 匿名内部类 | 无 | 没有类名,通常用于临时实现接口或抽象类 |
三、代码示例
1. 成员内部类
java
public class Outer {
private String name = "外部类";
// 成员内部类
class Inner {
public void show() {
System.out.println("访问外部类的私有属性:" + name);
}
}
public void test() {
Inner inner = new Inner();
inner.show();
}
}
// 使用
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner(); // 需要通过外部类实例创建
2. 静态内部类
java
public class Outer {
private static String staticName = "静态属性";
// 静态内部类
static class StaticInner {
public void show() {
System.out.println("访问外部类的静态属性:" + staticName);
// 不能访问外部类的非静态成员
}
}
}
// 使用
Outer.StaticInner inner = new Outer.StaticInner(); // 不需要外部类实例
3. 局部内部类
java
public class Outer {
public void method() {
int localVar = 10;
// 局部内部类(定义在方法里)
class LocalInner {
public void show() {
System.out.println("局部内部类,localVar = " + localVar);
}
}
LocalInner inner = new LocalInner();
inner.show();
}
}
4. 匿名内部类(最常用,尤其在框架中)
java
// 场景:临时实现一个接口
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类实现的线程任务");
}
};
// 或者使用 Lambda(Java 8+,匿名内部类的简化版)
Runnable task2 = () -> System.out.println("Lambda 表达式");
四、你在框架中会看到的内部类
java
// 写测试类时经常这么用
@Test
public void test() {
// 匿名内部类实现回调
restTemplate.execute("http://api.example.com", HttpMethod.GET,
request -> {
// 匿名内部类:设置请求头
request.getHeaders().set("Authorization", "token");
},
response -> {
// 匿名内部类:处理响应
return response.getBody();
}
);
}
你不需要刻意去写内部类,但需要能看懂这种写法,尤其是在看框架源码或别人写的代码时。
| 类型 | 是否需要外部类实例 | 能否访问外部类非静态成员 |
|---|---|---|
| 成员内部类 | ✅ 需要 | ✅ 能 |
| 静态内部类 | ❌ 不需要 | ❌ 不能 |
| 局部内部类 | 看情况 | ✅ 能 |
| 匿名内部类 | 看情况 | ✅ 能 |
实用建议:
-
日常开发中最常用的是匿名内部类(尤其是写监听器、回调时)
-
静态内部类比较常用(因为不持有外部类引用,不会造成内存泄漏)
-
成员内部类和局部内部类用得较少,能看懂即可