Loading...

文章背景图

Day 006-知识点汇总

2026-02-11
0
-
- 分钟

第一部分:继承(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

总结privatepublic最常用。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: 子类重新定义父类中已有的方法,方法名和参数列表相同,方法体不同。

注意事项

  1. 使用 @Override注解进行语法检查(推荐)
  2. 方法名、参数列表必须完全相同
  3. 私有方法静态方法不能被重写
  4. 子类重写方法的访问权限必须大于等于父类(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: 多态:同一种方法在不同对象身上表现出不同的行为(如:猫和狗都是动物,但猫吃鱼,狗吃骨头)。

前提条件

  1. 有继承/实现关系
  2. 父类类型的变量指向子类对象(Animal a = new Cat()
  3. 有方法重写
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

优势

  1. 代码可读性、可维护性更好
  2. 编译后常量会被替换成字面量,性能与直接使用字面量相同
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("恭喜获取免费洗车服务,打印洗车票!");
            }
        }
    }
}

使用步骤

  1. IDEA 2021+自带Lombok插件,旧版本需手动安装
  2. 添加Lombok依赖(Maven/Gradle或手动导入jar)
  3. 类上使用注解
  4. 首次使用需启用注解处理:Settings → Build → Compiler → Annotation Processing → Enable annotation processing
评论交流

文章目录