第一部分:继承(Inheritance)
1. 继承的概念与作用
Q: 什么是继承?为什么要使用继承?
A: 继承是面向对象三大特征之一,指子类继承父类的非私有成员(属性和方法),实现代码复用。
核心思想:
- 共性抽取:将多个子类共同的属性和方法抽取到父类中
- 代码复用:子类自动拥有父类非私有成员
- is a关系:子类是父类的一种(如:老师是人物的一种)
// 未继承:Teacher和Student都有name、age,代码重复
public class TeacherUnsafe {
private String name; // 重复
private int age; // 重复
private String job; // 特有
// ... getter/setter 重复写
}
public class StudentUnsafe {
private String name; // 重复
private int age; // 重复
private String className; // 特有
// ... getter/setter 重复写
}
// 已继承:共性抽取到父类,减少重复代码
public class Person {
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 class Teacher extends Person {
private String job; // 只需定义特有属性
public String getJob() { return job; }
public void setJob(String job) { this.job = job; }
}
public class Student extends Person {
private String className; // 只需定义特有属性
public String getClassName() { return className; }
public void setClassName(String className) { this.className = className; }
}
// 使用
public class Demo {
public static void main(String[] args) {
Teacher t = new Teacher();
t.setName("阳哥"); // 继承自Person
t.setAge(24); // 继承自Person
t.setJob("讲师"); // Teacher特有
System.out.println(t.getName() + "," + t.getAge() + "," + t.getJob());
// 输出:阳哥,24,讲师
Student s = new Student();
s.setName("齐泽宁"); // 继承自Person
s.setAge(34); // 继承自Person
s.setClassName("Java208期"); // Student特有
System.out.println(s.getName() + "," + s.getAge() + "," + s.getClassName());
// 输出:齐泽宁,34,Java208期
}
}
2. 继承的语法与is a关系
Q: 继承的语法是什么?什么时候使用继承?
A:
- 语法:
class 子类 extends 父类 { } - 使用时机:子类和父类具备 "is a" 关系时
| 关系判断 | 是否继承 | 代码 |
|---|---|---|
| 苹果 is a 水果 | ✅ 是 | class Apple extends Fruit { } |
| 猫 is a 动物 | ✅ 是 | class Cat extends Animal { } |
| 猫 is a 狗 | ❌ 否 | 不能继承,无is a关系 |
// 正确:狗是动物的一种
class Animal {
public void eat() {
System.out.println("动物吃饭");
}
}
class Dog extends Animal { // 正确,狗是动物
@Override
public void eat() {
System.out.println("狗吃骨头");
}
}
class Cat extends Animal { // 正确,猫是动物
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
// 错误:猫不是狗的一种,不能继承
// class Cat extends Dog { } // 逻辑错误!
public class Demo {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat(); // 狗吃骨头
Cat cat = new Cat();
cat.eat(); // 猫吃鱼
}
}
3. 权限修饰符
Q: Java四种权限修饰符的区别是什么?
A:
| 修饰符 | 本类 | 同包其他类 | 不同包子类 | 任意位置 |
|---|---|---|---|---|
private |
✅ | ❌ | ❌ | ❌ |
| 默认(缺省) | ✅ | ✅ | ❌ | ❌ |
protected |
✅ | ✅ | ✅ | ❌ |
public |
✅ | ✅ | ✅ | ✅ |
总结:private和 public最常用。private本类使用,public任意位置使用。
// com.itheima.demo1 包下
package com.itheima.demo1;
public class Fu {
private String one = "one"; // 只能本类访问
String two = "two"; // 同包可访问(缺省)
protected String three = "three"; // 同包+不同包子类内部可访问
public String four = "four"; // 任意位置可访问
public void demo() {
System.out.println(one); // ✅ 本类可以
System.out.println(two); // ✅ 本类可以
System.out.println(three); // ✅ 本类可以
System.out.println(four); // ✅ 本类可以
}
}
// 同包其他类
package com.itheima.demo1;
public class Demo021 {
public static void main(String[] args) {
Fu fu = new Fu();
// System.out.println(fu.one); // ❌ 编译错误!private
System.out.println(fu.two); // ✅ 同包可访问缺省
System.out.println(fu.three); // ✅ 同包可访问protected
System.out.println(fu.four); // ✅ public任意位置可访问
}
}
// 不同包子类
package com.itheima.demo2;
import com.itheima.demo1.Fu;
public class Son extends Fu {
public void test() {
// System.out.println(one); // ❌ private无法访问
// System.out.println(two); // ❌ 不同包无法访问缺省
System.out.println(three); // ✅ 子类内部可访问protected
System.out.println(four); // ✅ public任意位置可访问
}
}
// 不同包其他类
package com.itheima.demo2;
import com.itheima.demo1.Fu;
public class Demo031 {
public static void main(String[] args) {
Fu fu = new Fu();
// System.out.println(fu.one); // ❌
// System.out.println(fu.two); // ❌
// System.out.println(fu.three); // ❌ 不是子类,无法访问protected
System.out.println(fu.four); // ✅ 只有public可以
}
}
4. 单继承与多层继承
Q: Java支持多继承吗?支持多层继承吗?Object类是什么?
A:
- 单继承:一个类只能直接继承一个父类(
extends后只能写一个类) - 多层继承:支持A→B→C的继承链,A间接继承C
- Object类:所有类的根类(祖宗类),任何类都直接或间接继承Object
// 单继承:只能继承一个直接父类
class A { }
class B { }
// class C extends A, B { } // ❌ 错误!Java不支持多继承
// 多层继承:支持继承链
class GrandParent { // 隐式继承Object
String grandName = "爷爷";
}
class Parent extends GrandParent { // 继承GrandParent,间接继承Object
String parentName = "爸爸";
}
class Child extends Parent { // 继承Parent,间接继承GrandParent和Object
String childName = "儿子";
}
public class Demo041 {
public static void main(String[] args) {
Child child = new Child();
System.out.println(child.grandName); // 爷爷(可以访问爷爷的属性)
System.out.println(child.parentName); // 爸爸(可以访问爸爸的属性)
System.out.println(child.childName); // 儿子(自己的属性)
// 从Object继承的方法
child.toString(); // 可以调用Object的方法
child.hashCode(); // 可以调用Object的方法
child.equals(null); // 可以调用Object的方法
// 验证:任何类都继承Object
new GrandParent().toString(); // GrandParent也继承了Object
new Parent().toString(); // Parent也继承了Object
}
}
5. 继承后的成员访问特点(就近原则)
Q: 子类中如何访问成员变量和成员方法?this和super的作用?
A:
- 就近原则:局部变量 → 子类成员 → 父类成员
this.成员:访问**本类(子类)**的成员super.成员:访问父类的成员
class Fu {
public String name = "Fu类成员name";
public int age = 42;
public String nationality = "中国"; // 只有父类有
public void drive() {
System.out.println("Fu驾驶AE86在秋名山上连续过弯!");
}
}
class Zi extends Fu {
public String name = "Zi类成员name"; // 与父类重名
public int age = 20; // 与父类重名
public void drive() { // 重写父类方法
System.out.println("Zi驾驶兰博基尼在城市的灯红酒绿间穿梭!");
}
public void print() {
String name = "Zi类局部name"; // 局部变量
// 1. 默认就近原则
System.out.println(name); // Zi类局部name(局部)
System.out.println(age); // 20(子类成员)
System.out.println(nationality); // 中国(父类成员,子类没有)
drive(); // Zi驾驶...(子类方法)
System.out.println("-------------------");
// 2. 使用this和super明确指定
System.out.println(this.name); // Zi类成员name(子类成员)
System.out.println(super.name); // Fu类成员name(父类成员)
System.out.println(super.age); // 42(父类成员)
super.drive(); // Fu驾驶...(父类方法)
}
}
public class Demo051 {
public static void main(String[] args) {
Zi zi = new Zi();
zi.print();
}
}
6. 方法重写(Override)
Q: 什么是方法重写?有哪些注意事项?
A: 子类重新定义父类中已有的方法,方法名和参数列表相同,方法体不同。
注意事项:
- 使用
@Override注解进行语法检查(推荐) - 方法名、参数列表必须完全相同
- 私有方法和静态方法不能被重写
- 子类重写方法的访问权限必须大于等于父类(
private < 缺省 < protected < public)
class Fu {
public void show() {
System.out.println("Fu...show...");
}
private void show2() { } // 私有,不能重写
public static void show3() { } // 静态,不能重写
protected void show4() { }
protected void show5() { }
}
class A extends Fu {
@Override // ✅ 注解检查重写语法
public void show() {
System.out.println("A...show...");
}
// @Override
// private void show2() { } // ❌ 不能重写父类私有方法
// @Override
// public static void show3() { } // ❌ 不能重写父类静态方法
@Override
public void show4() { // ✅ 正确:public > protected
System.out.println("A...show4...");
}
@Override
protected void show5() { // ✅ 正确:protected = protected
// 快捷键:Alt+Insert → Override Methods
}
}
class B extends Fu {
// 不重写show(),使用继承自父类的版本
}
public class Demo061 {
public static void main(String[] args) {
A a = new A();
a.show(); // A...show...(调用子类重写的方法)
B b = new B();
b.show(); // Fu...show...(调用继承自父类的方法)
}
}
7. 方法重写的应用场景
Q: 方法重写在实际开发中有哪些常见应用?
A: 常见应用:重写 Object类的 toString()和 equals()方法,使对象信息更可读、比较更准确。
class Student {
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
// 没有重写toString()
}
class Teacher {
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
// 重写Object的toString方法
@Override
public String toString() {
return String.format("Teacher{name='%s'}", name);
}
}
public class Demo062 {
public static void main(String[] args) {
// 1. toString()应用
Student student = new Student();
student.setName("播仔");
System.out.println(student);
// 没有重写:输出 com.itheima.Student@6d06d69c(内存地址)
Teacher teacher = new Teacher();
teacher.setName("黑马");
System.out.println(teacher);
// 重写后:输出 Teacher{name='黑马'}(可读信息)
// 2. equals()应用(String类重写了equals)
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(str1 == str2); // false(比较内存地址)
System.out.println(str1.equals(str2)); // true(比较内容值,因为String重写了equals)
}
}
8. 继承后的构造器执行顺序
Q: 创建子类对象时,构造器的执行顺序是什么?super()如何使用?
A:
- 执行顺序:父类构造器 → 子类构造器(先初始化父类公共部分,再初始化子类特有部分)
- super():调用父类构造器,必须放在子类构造器第一行
- 默认行为:子类构造器默认第一行有
super()(调用父类无参构造)
class Person {
private String name;
private int age;
public Person() {
System.out.println("Person...无参构造器");
}
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Person...全参构造器");
}
public String getName() { return name; }
public int getAge() { return age; }
}
class Teacher extends Person {
private String job;
public Teacher() {
// 默认有 super(); 调用父类无参构造
System.out.println("Teacher...无参构造器");
}
public Teacher(String name, int age, String job) {
super(name, age); // ✅ 必须第一行!调用父类全参构造
this.job = job; // 再初始化子类特有成员
System.out.println("Teacher...全参构造器");
}
@Override
public String toString() {
return "Teacher{job='" + job + "',name='" + getName() + "',age=" + getAge() + "}";
}
}
public class Demo071 {
public static void main(String[] args) {
System.out.println("=== 调用无参构造 ===");
Teacher t1 = new Teacher();
// 输出:Person...无参构造器
// Teacher...无参构造器
System.out.println("=== 调用全参构造 ===");
Teacher t2 = new Teacher("播仔", 20, "讲师");
// 输出:Person...全参构造器
// Teacher...全参构造器
System.out.println(t2); // Teacher{job='讲师',name='播仔',age=20}
}
}
9. this()和super()的区别
Q: this(...)和super(...)有什么区别?使用时注意什么?
A:
| 关键字 | 作用 | 位置要求 |
|---|---|---|
this(...) |
调用本类其他构造器 | 必须是构造器第一行 |
super(...) |
调用父类构造器 | 必须是构造器第一行 |
⚠️ 注意:两者不能同时出现在一个构造器中(都要求是第一条语句)
class Teacher extends Person {
private String job;
public Teacher() {
// super(); // 默认调用父类无参
System.out.println("Teacher无参构造");
}
// 全参构造
public Teacher(String name, int age, String job) {
super(name, age); // 调用父类构造器初始化公共属性
this.job = job;
System.out.println("Teacher全参构造");
}
// 2个参数构造器:重用全参构造器
public Teacher(String name, int age) {
// setName(name);
// setAge(age); // 这样代码较多
this(name, age, "班主任"); // ✅ 调用本类三参构造器,代码简洁
// super(name, age); // ❌ 错误!不能放在第二行
// 注意:this()和super()都必须放在第一行,有this()就不能有super()
}
@Override
public String toString() {
return "Teacher{job='" + job + "',name='" + getName() + "',age=" + getAge() + "}";
}
}
第二部分:多态(Polymorphism)
1. 多态的概念与前提条件
Q: 什么是多态?多态的前提条件是什么?
A: 多态:同一种方法在不同对象身上表现出不同的行为(如:猫和狗都是动物,但猫吃鱼,狗吃骨头)。
前提条件:
- 有继承/实现关系
- 父类类型的变量指向子类对象(
Animal a = new Cat()) - 有方法重写
class Animal {
public String name = "动物"; // 属性不存在多态
public void eat() {
System.out.println("动物在吃饭");
}
}
class Cat extends Animal {
public String name = "咪咪"; // 与父类同名属性
@Override
public void eat() {
System.out.println("猫吃鱼。。。");
}
}
class Dog extends Animal {
public String name = "旺财"; // 与父类同名属性
@Override
public void eat() {
System.out.println("狗吃骨头。。。");
}
}
public class Demo081 {
public static void main(String[] args) {
// 多态:父类引用指向子类对象
Animal a1 = new Cat();
System.out.println(a1.name); // 动物(属性没有多态,看左边)
a1.eat(); // 猫吃鱼。。。(方法有多态,看右边)
Animal a2 = new Dog();
System.out.println(a2.name); // 动物(属性看左边)
a2.eat(); // 狗吃骨头。。。(方法看右边)
// a1, a2类型相同(都是Animal), 执行的行为不同
// 特点:父类类型引用可以接收不同子类对象
}
}
2. 多态下方法调用规则
Q: 多态下方法调用的规则是什么?属性有多态吗?
A:
- 方法调用:编译看左边,运行看右边
- 属性访问:编译和运行都看左边(属性没有多态)
class Animal {
public String name = "父类name";
public void eat() {
System.out.println("父类eat方法");
}
}
class Dog extends Animal {
public String name = "Dog类name";
@Override
public void eat() {
System.out.println("狗吃骨头");
}
}
class Cat extends Animal {
public String name = "Cat类name";
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
public class Demo {
public static void main(String[] args) {
Animal a = new Cat();
// 方法调用:编译看左边,运行看右边
a.eat(); // 编译时检查Animal有eat(),运行时执行Cat的eat()
// 输出:猫吃鱼
// 属性访问:编译和运行都看左边
System.out.println(a.name); // 父类name(Animal的name)
// 重要结论:多态是行为的多态,Java中的属性(成员变量)不存在多态
}
}
3. 多态的好处与弊端
Q: 多态的好处和弊端分别是什么?
A:
- 好处:定义方法时使用父类类型形参,可以接收一切子类对象,扩展性更强、代码更简洁
- 弊端:多态下不能直接调用子类的独有(特有)功能
class Animal {
public void eat() {
System.out.println("父类eat方法");
}
}
class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
// 特有方法
public void sleep() {
System.out.println("猫在睡觉...");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨头");
}
// 特有方法
public void play() {
System.out.println("狗在玩飞盘...");
}
}
public class Demo081 {
public static void main(String[] args) {
Cat cat = new Cat();
Dog dog = new Dog();
System.out.println("=== 冗余方式(不用多态)===");
feed(cat); // 需要写多个重载方法
feed(dog);
System.out.println("=== 多态方式 ===");
feed2(cat); // 一个方法接收所有子类对象
feed2(dog);
}
// ❌ 不用多态:代码冗余,有多少个动物就要写多少个方法
public static void feed(Cat cat) {
cat.eat();
}
public static void feed(Dog dog) {
dog.eat();
}
// ✅ 使用多态:参数使用父类类型,节省代码
public static void feed2(Animal animal) {
animal.eat(); // 可以调用共有的eat()
// ❌ 多态弊端:不能直接调用子类独有成员
// animal.sleep(); // 编译报错!Animal没有sleep()方法
// animal.play(); // 编译报错!Animal没有play()方法
}
}
4. 多态下的类型转换
Q: 如何解决多态不能调用子类独有功能的问题?如何进行类型转换?
A: 通过类型转换解决:先使用 instanceof判断真实类型,再进行强制类型转换。
类型转换:
- 自动类型转换:
父类 变量名 = new 子类();(向上转型) - 强制类型转换:
子类 变量名 = (子类) 父类变量;(向下转型)
class Animal {
public void eat() {
System.out.println("动物吃饭");
}
}
class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
public void sleep() { // 特有方法
System.out.println("猫在睡觉...");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨头");
}
public void play() { // 特有方法
System.out.println("狗在玩飞盘...");
}
}
public class Demo081 {
public static void main(String[] args) {
feed2(new Cat());
feed2(new Dog());
}
public static void feed2(Animal animal) {
animal.eat(); // 调用共有的eat()
// 解决多态弊端:类型转换调用特有方法
// 方式1:JDK16之前,先判断再转换
/*
if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.sleep();
} else if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.play();
}
*/
// 方式2:JDK17+,判断+转换同时进行(推荐)
if (animal instanceof Cat cat) { // 判断是Cat并直接转换
cat.sleep();
} else if (animal instanceof Dog dog) { // 判断是Dog并直接转换
dog.play();
}
// ⚠️ 注意:强制类型转换前必须判断,否则会报ClassCastException
// Dog dog = (Dog) animal; // 如果animal实际是Cat,运行时报错!
}
}
第三部分:final关键字
1. final修饰类、方法、变量
Q: final关键字可以修饰什么?分别有什么作用?
A: final是最终的意思,可以修饰类、方法、变量:
| 修饰目标 | 作用 | 代码 |
|---|---|---|
| 类 | 最终类,不能被继承 | final class Fu { } |
| 方法 | 最终方法,不能被重写 | public final void show() { } |
| 变量 | 常量,只能赋值一次 | final int MAX = 100; |
// 1. final修饰类:不能被继承
final class Fu1 { }
// class Son1 extends Fu1 { } // ❌ 报错!Fu1是final类,不能有后代
// 2. final修饰方法:不能被重写
class Fu2 {
public final void demo() { // final方法
System.out.println("This is a final method.");
}
}
class Son2 extends Fu2 {
// public void demo() { } // ❌ 报错!不能重写父类final方法
private int age;
public void testFinal() {
// 3. final修饰变量:只能赋值一次
final int a = 0;
// a = 2; // ❌ 报错!final变量不能改值
// 4. final修饰引用类型:地址不能改,内容可以改
final Son2 son2 = new Son2(); // new对象就是赋值内存地址
son2.age = 100; // ✅ 可以修改对象内部数据
son2.age = 200; // ✅ 可以再次修改
// son2 = new Son2(); // ❌ 报错!不能改变引用地址
}
}
public class Demo091 { }
2. 常量(static final)
Q: 什么是常量?如何定义?有什么优势?
A: 常量:使用 static final修饰的成员变量,值在程序运行期间不会改变。
命名规范:大写英文单词,多个单词用下划线连接(如 SCHOOL_NAME)
优势:
- 代码可读性、可维护性更好
- 编译后常量会被替换成字面量,性能与直接使用字面量相同
class Payment {
// 常量定义:static final
public static final String TYPE_ALIPAY = "支付宝";
public static final String TYPE_WECHAT = "微信";
public static final String TYPE_OTHERS = "其他";
// 引用类型常量:地址不能改,内容可以改
public static final Demo CONSTANT_DEMO = new Demo();
}
class Demo {
String name;
}
public class Demo092 {
public static void main(String[] args) {
// 访问语法:类名.常量名(无需创建对象)
System.out.println(Payment.TYPE_ALIPAY); // 支付宝
// ❌ 常量值不能修改
// Payment.TYPE_ALIPAY = "支付宝2"; // 编译报错!
// 引用类型常量:可以修改对象内部数据
Payment.CONSTANT_DEMO.name = "xxdx"; // ✅ 可以
Payment.CONSTANT_DEMO.name = "xxdx2"; // ✅ 可以再次修改
// ❌ 但不能改变引用地址
// Payment.CONSTANT_DEMO = new Demo(); // 编译报错!
}
}
第四部分:综合案例(加油站会员卡系统)
1. 继承与多态的综合应用
Q: 如何使用继承+多态设计加油站会员卡系统?
A: 需求分析:
- 金卡:办卡≥5000元,支付8折,满200元送洗车服务
- 银卡:办卡≥2000元,支付9折
- 共同属性:车牌号、车主姓名、电话、余额
- 共同功能:充值、消费
设计思路:
- 父类
Card:定义共同属性和方法 - 子类
GoldCard/SilverCard:重写consume()方法实现不同折扣逻辑
// 父类:定义共同特征
public class Card {
private String carNumber; // 车牌号码
private String name; // 车主姓名
private String phone; // 电话号码
private double balance; // 余额
// 构造器、getter/setter、toString...
public Card() { }
public Card(String carNumber, String name, String phone, double balance) {
this.carNumber = carNumber;
this.name = name;
this.phone = phone;
this.balance = balance;
}
// getter和setter方法...
public String getCarNumber() { return carNumber; }
public void setCarNumber(String carNumber) { this.carNumber = carNumber; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
public double getBalance() { return balance; }
public void setBalance(double balance) { this.balance = balance; }
// 充值方法(通用)
public void recharge(double money) {
balance += money;
System.out.printf("成功充值 %.2f 元,最新余额 %.2f 元!\n", money, balance);
}
// 消费方法(子类重写)
public void consume(double money) {
if (money > balance) {
System.err.println("消费失败,余额不足!");
} else {
balance -= money;
System.out.printf("成功消费 %.2f 元,最新余额 %.2f 元!\n", money, balance);
}
}
}
// 金卡:8折优惠,满200送洗车
public class GoldCard extends Card {
public GoldCard(String carNumber, String name, String phone, double balance) {
super(carNumber, name, phone, balance);
}
@Override
public void consume(double money) {
if (money > getBalance()) {
System.err.println("消费失败,余额不足!");
} else {
// 金卡打8折
money *= 0.8;
setBalance(getBalance() - money);
System.out.printf("成功消费8折后金额为 %.2f 元,最新余额 %.2f 元!\n",
money, getBalance());
// 满200送洗车
if (money >= 200) {
System.out.println("恭喜获取免费洗车服务,打印洗车票!");
}
}
}
}
// 银卡:9折优惠
public class SilverCard extends Card {
public SilverCard(String carNumber, String name, String phone, double balance) {
super(carNumber, name, phone, balance);
}
@Override
public void consume(double money) {
if (money > getBalance()) {
System.err.println("消费失败,余额不足!");
} else {
// 银卡打9折
money *= 0.9;
setBalance(getBalance() - money);
System.out.printf("成功消费9折后金额为 %.2f 元,最新余额 %.2f 元!\n",
money, getBalance());
}
}
}
// 测试类:使用多态
public class Demo101 {
public static void main(String[] args) {
// 多态:父类引用指向子类对象
Card c1 = new GoldCard("GOLD001", "播仔", "12345678912", 5000);
if (c1.getBalance() < 5000) {
System.err.println("金卡办卡失败,存入金额必须≥5000!");
return;
}
System.out.println("金卡开卡成功!");
c1.recharge(1000); // 充值
c1.consume(300); // 消费(8折,满200送洗车)
System.out.println("----------------------------------------");
Card c2 = new SilverCard("SILVER001", "播妞", "12345678912", 2000);
if (c2.getBalance() < 2000) {
System.err.println("银卡办卡失败,存入金额必须≥2000!");
return;
}
System.out.println("银卡开卡成功!");
c2.recharge(1000); // 充值
c2.consume(300); // 消费(9折)
}
}
2. Lombok优化JavaBean
Q: 如何使用Lombok优化JavaBean代码?
A: Lombok是第三方类库,通过注解自动生成构造器、getter/setter、toString等方法,简化JavaBean开发。
常用注解:
@Data:生成getter、setter、toString、equals、hashCode@NoArgsConstructor:生成无参构造器@AllArgsConstructor:生成全参构造器
// 使用前:冗长的JavaBean
/*
public class Card {
private String carNumber;
private String name;
private String phone;
private double balance;
// 无参构造
public Card() { }
// 全参构造
public Card(String carNumber, String name, String phone, double balance) {
this.carNumber = carNumber;
this.name = name;
this.phone = phone;
this.balance = balance;
}
// getter和setter...
// toString...
}
*/
// 使用后:简洁的JavaBean
package com.itheima._11使用lombok优化实体类;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data // 生成getter、setter、toString、equals、hashCode
@NoArgsConstructor // 生成无参构造器
@AllArgsConstructor // 生成全参构造器
public class Card {
private String carNumber; // 车牌号码
private String name; // 车主姓名
private String phone; // 电话号码
private double balance; // 余额
// 只需写业务方法
public void recharge(double money) {
balance += money;
System.out.printf("成功充值 %.2f 元,最新余额 %.2f 元!\n", money, balance);
}
public void consume(double money) {
if (money > balance) {
System.err.println("消费失败,余额不足!");
} else {
balance -= money;
System.out.printf("成功消费 %.2f 元,最新余额 %.2f 元!\n", money, balance);
}
}
}
// 子类使用Lombok注意事项
package com.itheima._11使用lombok优化实体类;
import lombok.NoArgsConstructor;
@NoArgsConstructor
// @AllArgsConstructor // ❌ 不能加!会多生成一个无参构造器(因为子类没有独有成员)
public class GoldCard extends Card {
// 构造器必须手写,因为需要调用父类构造器
public GoldCard(String carNumber, String name, String phone, double balance) {
super(carNumber, name, phone, balance);
}
@Override
public void consume(double money) {
if (money > getBalance()) {
System.err.println("消费失败,余额不足!");
} else {
money *= 0.8; // 8折
setBalance(getBalance() - money);
System.out.printf("成功消费8折后金额为 %.2f 元,最新余额 %.2f 元!\n",
money, getBalance());
if (money >= 200) {
System.out.println("恭喜获取免费洗车服务,打印洗车票!");
}
}
}
}
使用步骤:
- IDEA 2021+自带Lombok插件,旧版本需手动安装
- 添加Lombok依赖(Maven/Gradle或手动导入jar)
- 类上使用注解
- 首次使用需启用注解处理:
Settings → Build → Compiler → Annotation Processing → Enable annotation processing