Java基础快速入门: 面向对象高级之继承、抽象类、代码块与final关键字

本文纲要

  1. 继承入门
  2. 继承的好处与弊端
  3. 继承的特点
  4. 继承中成员变量的访问特点
  5. this 和 super 访问成员的格式
  6. 继承中成员方法的访问特点
  7. 方法重写概述与应用场景
  8. 方法重写的注意事项
  9. 权限修饰符
  10. 继承中构造方法的访问特点
  11. 构造方法的访问特点 - 父类没有无参构造
  12. 代码优化与内存图解
  13. 信息管理系统- 集成改进
  14. 抽象类入门
  15. 抽象类的注意事项
  16. 模板设计模式
  17. final 关键字
  18. 信息管理系统- 抽象类改进
  19. 代码块
  20. 信息管理系统- 代码块改进
  21. 总结

本文代码结构

project-root/
├── test-abstract/src/com/wb                          (代码块)
│     ├── mfinal/                                     (final关键字)
│     ├── test1/                                      (抽象类)
│     └── test2/                                      (模板设计模式)
├── test-extends/src/com/wb                           (继承)
│     ├── constructor/                                (继承-构造方法)
│     ├── override/                                   (方法重写)
│     ├── override2/                                  (方法重写注意事项)
│     ├── test1/                                      (继承-基础)
│     ├── test2/                                      (继承-优化)
│     ├── test3/                                      (继承-多层继承)
│     ├── test4/                                      (继承-成员变量访问)
│     └──  test5/                                     (继承-成员方法访问)
├── test-permission/src/com/wb/                       (代码块)
│     ├── test1/                                      (权限修饰符)
│     └── test2/                                      (权限修饰符-不同包)
├── test-block/src/com/wb/block                       (代码块)
│     ├── construction/                               (构造代码块)
│     ├── local/                                      (局部代码块)
│     └── mstatic/                                    (静态代码块)
└── wb-edu-info-manager/com/wb/edu/info/manager/      (信息管理系统)
    ├── controller/                                   (控制层)
    ├── dao/                                          (数据访问层)
    ├── domain/                                       (实体类)
    ├── entry/                                        (程序入口)
    ├── factory/                                      (工厂类)
    └── service/                                      (业务层)

继承入门

概念: 继承是面向对象编程的第二大特征(封装、继承、多态)。它允许类与类之间产生子父类关系。子类可以直接使用父类中非私有的成员变量和成员方法。

场景: 在之前的黑马信息管理系统中,Student类和Teacher类中存在大量共性的代码(如 id, name, age 等属性),这降低了代码的复用性。为了解决这个问题,可以将这些共性的内容向上抽取,形成一个父类 Person,然后让 Student 和 Teacher 继承它。

格式: 在类名后使用 extends 关键字,后跟父类名
public class 子类名 extends 父类名 { ... }

代码演示:

// 父类 Person,抽取了 Student 和 Teacher 的共性内容 
public class Person {
    private String id;
    private String name;
    private String age;
    private String birthday;
 
    public Person() {
    }
 
    public Person(String id, String name, String age, String birthday) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }
 
    // 提供非私有的 get/set 方法供子类访问 
    public String getId() {
        return id;
    }
 
    public void setId(String id) {
        this.id = id;
    }
 
    // 其他 get/set 方法...
}
 
// 子类 Student,通过 extends 关键字继承 Person 
public class Student extends Person {
    // 子类可以编写自己特有的成员,没有编写任何代码,也可以使用父类非私有成员 
}
 
// 子类 Teacher 
public class Teacher extends Person {
    // ...
}

结论: 虽然父类中的成员变量是 private 的,子类不能直接访问,但父类提供了非私有的 getter/setter 方法。子类通过继承这些方法,间接地访问了父类的私有数据。

继承的好处与弊端

好处:

  1. 提高代码的复用性: 这是继承最直观的好处,子类无需重复编写代码,可以直接使用父类的非私有成员。

  2. 提高代码的维护性: 当需求变更(如增加一个属性),只需在父类中修改一次,所有子类都会自动更新。

    // 没有继承时,修改需求需在多个类中重复添加属性 
    public class Student { / ... / String address; }
    public class Teacher { / ... / String address; }
    
    // 有继承时,只在父类修改一次 
    public class Person { / ... / String address; }
    // Student 和 Teacher 无需任何修改,即可使用 address 属性(通过get/set)
    
  3. 是多态的前提: 代码之间的“是”(is-a)关系(学生是人的一种)是建立继承关系的基础,这也是多态技术实现的前提。

弊端:

  1. 降低了代码的灵活性: 继承是侵入性的。子类必须拥有父类非私有的属性和方法,这给予了子类一些不想要的约束。
  2. 增强了代码的耦合性: 父类和子类之间的关联非常紧密(强耦合)。如果父类的某个成员被修改或删除,所有使用该成员的子类代码都可能需要修改,否则会报错,这就是“牵一发而动全身”。

应用场景: 当类与类之间存在共性内容,并且满足"is-a"关系时(如:学生和老师都是人),就可以考虑使用继承来优化代码结构。

继承的特点

  1. 单继承: Java只支持单继承,一个子类只能有一个直接父类。

  2. 不支持多继承: 一个子类不能同时继承多个父类。

    // 错误写法!Java不支持多继承 
    public class C extends A, B { }
    

    原因:如果父类A和B有同名方法但实现不同,子类调用时会产生逻辑冲突,Java为避免这种二义性,不支持多继承。

  3. 多层继承: 支持多层继承体系(子->父->爷)。

    public class A { public void methodA(){} }
    public class B extends A { public void methodB(){} }
    // C 是最底层的子类,可以访问 A 和 B 中所有非私有成员 
    public class C extends B { }
    
    public class Test {
        public static void main(String[] args) {
            C c = new C();
            c.methodA(); // 访问爷爷类的方法 
            c.methodB(); // 访问父亲类的方法 
        }
    }
    

继承中成员变量的访问特点

访问一个变量时,遵循就近原则:

  1. 先在方法内部查找(局部变量)。
  2. 没有则去子类的成员范围查找(本类成员变量)。
  3. 还没有则去父类的成员范围查找(父类成员变量)。
  4. 如果都找不到,则编译报错。

子父类成员变量重名:

如果子类和父类出现了重名的成员变量,默认访问的是子类的。如果想在子类中访问父类的同名变量,需要使用 super 关键字。

public class Fu {
    int a = 10;
}
 
public class Zi extends Fu {
    int a = 20; // 与父类重名 
 
    public void method() {
        int a = 30; // 局部变量 
        System.out.println(a);          // 30,就近使用局部变量 
        System.out.println(this.a);     // 20,使用this区分,访问本类成员变量 
        System.out.println(super.a);    // 10,使用super关键字访问父类成员变量 
    }
}

this 和 super 访问成员的格式

this: 代表本类对象的引用。用于区分局部变量和成员变量,或调用本类的其他成员。
super: 代表父类存储空间的标识(可以理解为父类对象的引用)。用于在子类中访问父类的成员。

两者的格式非常相似,可以调用成员变量、成员方法和构造方法。

关键字访问成员变量访问成员方法访问构造方法
thisthis.成员变量this.成员方法(参数)this(参数)
supersuper.成员变量super.成员方法(参数)super(参数)
this(…) 和 super(…) 是用来调用构造方法的,且都必须放在构造方法的第一行。

继承中成员方法的访问特点

通过子类对象访问方法时,查找顺序:

  1. 在子类的成员范围查找。
  2. 如果子类有,则执行子类的方法。
  3. 如果子类没有,则去父类的成员范围查找。
  4. 如果父类也没有,则报错。

代码演示:

public class Fu {
    public void show() { System.out.println("父类show方法"); }
}
public class Zi extends Fu {
    // 子类有 show 方法,则调用时执行这个 
    public void show() { System.out.println("子类show方法"); }
 
    public void method() {
        this.show();   // 调用本类的show方法 
        super.show();  // 通过super调用父类的show方法 
    }
}

方法重写概述与应用场景

概念: 在继承体系中,子类出现了和父类一模一样的方法声明(方法名、参数列表、返回值类型都相同)的行为,称为方法重写 (Override)。

应用场景: 当子类需要父类的功能,但功能主体子类又有自己特有的实现时。通过重写,既能沿袭父类的功能,又能定义子类特有的内容。

案例: 手机系统升级。v1.0 手机的语音助手 smallBlack 只说英文,v2.0 要求既能说英文又能说中文。

// 父类 iPearV1 
public class iPearV1 {
    public void smallBlack() {
        System.out.println("speak english...");
    }
}
// 子类 iPearV2 
public class iPearV2 extends iPearV1 {
    // 重写父类方法 
    @Override 
    public void smallBlack() {
        // 调用父类的功能,沿袭了说英文的能力 
        super.smallBlack();
        // 添加子类特有的功能 
        System.out.println("说中文");
    }
}

区分方法重载 (Overload):

  • 重写 (Override): 在继承体系的子父类中,方法声明一模一样。
  • 重载 (Overload): 在同一个类中,方法名相同,参数列表不同,与返回值无关。

方法重写的注意事项

  1. 权限:子类重写方法时,访问权限必须大于等于父类方法的权限。
  2. 静态:父类中静态方法,不能被子类以非静态方法重写,反之亦然。静态方法本身不能被重写(只能被隐藏),但可以用 @Override 注解来检查。
  3. 私有:父类中私有方法不能被重写。
public class Fu {
    void method() { ... } // 默认权限 
    // public static void show() { ... }
}
 
public class Zi extends Fu {
    @Override 
    void method() { ... } // OK, 权限 >= 默认权限 
    // @Override 
    // public void show() { ... } // 报错,父类静态,子类未用 static 
}

权限修饰符

Java中有四种权限修饰符,用来控制成员的访问范围,从小到大的等级为:
private < 默认 (什么都不写) < protected < public

修饰符同一个类同一个包中的子类/无关类不同包下的子类不同包下的无关类
private×××
默认 (default)××
protected×
public

我们日常开发中最常用的是 private(用于封装)和 public(对外提供接口)。

继承中构造方法的访问特点

规则:子类中所有的构造方法,默认都会先访问父类中的无参构造方法,再执行自己的方法体。

为什么?: 因为子类在初始化时,可能会使用到父类的数据。如果父类数据没有完成初始化,子类就无法安全地使用它。所以,子类初始化前,必须先完成父类的初始化。

**怎么做? **:super() 语句。每一个子类构造方法的第一行,都隐式地有一条 super() 语句,用于调用父类的无参构造。

public class Person {
    public Person() {
        System.out.println("父类无参构造");
    }
}
 
public class Student extends Person {
    public Student() {
        // 系统默认隐藏了 super();
        System.out.println("子类无参构造");
    }
}
 
// main中执行 new Student();
// 控制台输出顺序:
// "父类无参构造"
// "子类无参构造"

Object类:每一个类都直接或间接地继承了 Object 类。Object 是 Java 类层次结构的最顶层。父类 Person 如果不显式继承其他类,那么它也默认继承 Object。Person 构造中的 super() 调用的就是 Object 的无参构造。

构造方法的访问特点 - 父类没有无参构造

如果父类没有提供无参构造方法,只提供了有参构造方法,子类构造方法就不会再默认生成 super(),而是会报错。

解决方案(推荐):
在子类构造方法中,手工调用父类的有参构造方法 super(参数),并传入相应的参数。

public class Fu {
    private int age;
    // 父类没有无参构造 
    public Fu(int age) {
        this.age = age;
    }
}
 
public class Zi extends Fu {
    public Zi(int age) {
        super(age); // 必须手写,显式调用父类的有参构造 
    }
}

注意事项:
this(...)super(...) 调用构造方法的语句,都必须放在构造方法的第一行有效语句。因此,它们在同一个构造方法中不能共存。

代码优化与内存图解

在编写继承代码时,子类的构造方法可以通过快捷键 Alt + Insert (或右键 -> Generate…) 快速生成。如果需要初始化完整的属性,子类的有参构造应接收所有数据(包括父类的),并通过 super 将父类的数据传递给父类去初始化。

// 父类 
public class Person {
    private String name;
    private int age;
    public Person(String name, int age) { this.name = name; this.age = age; }
    // ...
}
 
// 子类 
public class Student extends Person {
    private int score;
    public Student(String name, int age, int score) {
        super(name, age); // 将 name 和 age 传递给父类初始化 
        this.score = score; // 子类自己初始化独有属性 
    }
}

内存图解(堆内存):
当创建 new Student(“张三”, 23, 100) 时,堆内存的结构分析。

执行流程

堆内存_Heap

主程序

Student stu = new Student('张三', 23, 100)

创建对象

Student对象空间
含地址值 @15db9742

super区域 Person成员

name = null
age = 0

Student特有成员

score = 0

1. new Student时
实参 张三,23,100 传给有参构造

2. super 张三,23 语句执行

3. Person有参构造完成赋值

4. super区域更新
name=张三, age=23

5. this.score = 100 执行

6. Student对象空间完整

结论:
父类中private的成员也会在子类对象的super区域内有一份存储空间。子类只是没有直接访问的权限。
初始化是分层进行的:先由父类完成其成员的初始化,再由于子类完成其自身特有成员的初始化。

信息管理系统 - 集成改进

利用继承思想对信息管理系统进行优化。

1 ) 实体类优化:将 Student 和 Teacher 的共性属性抽取到 Person 父类。

2 ) 控制器优化:

  • 起初,StudentController 使用 set方法给属性赋值,代码冗长。
  • 新业务要求使用构造方法赋值,但不希望修改原有代码。这体现了开闭原则(对修改关闭,对扩展开放)。
  • 解决方案:创建一个新控制器 OtherStudentController,复写 inputStudentInfo 方法,在其内部使用有参构造来封装 Student 对象。
  • 进一步优化:发现 StudentController 和 OtherStudentController 中有大量共性代码。于是向上抽取,创建一个抽象的 BaseStudentController 父类,将公共方法(如 start, addStudent, deleteStudentById 等)放入其中。子类只需专注于重写 inputStudentInfo 这个核心差异方法。

优化后的代码结构:

// 抽象的父类控制器 
public abstract class BaseStudentController {
    private StudentService studentService = new StudentService();
    private Scanner sc = new Scanner(System.in);
 
    public final void start() { / 复杂的控制逻辑, 不允许子类修改 / }
    public final void deleteStudentById() { / ... / }
    public final void addStudent() {
        // ... 接收id等公共逻辑 ...
        Student stu = inputStudentInfo(id); // 调用抽象方法,具体怎么输入由子类决定 
        // ...
    }
    // 将录入学生信息的方法定义为抽象,强制子类实现,体现了多态思想 
    public abstract Student inputStudentInfo(String id);
}
 
// 子类实现1: 使用set方法赋值 
public class StudentController extends BaseStudentController {
    @Override 
    public Student inputStudentInfo(String id) {
        // ... 键盘录入 ...
        Student stu = new Student();
        stu.setId(id);
        stu.setName(name);
        // ...
        return stu;
    }
}
 
// 子类实现2: 使用构造方法赋值 
public class OtherStudentController extends BaseStudentController {
    @Override 
    public Student inputStudentInfo(String id) {
        // ... 键盘录入 ...
        Student stu = new Student(id, name, age, birthday);
        return stu;
    }
}

抽象类入门

1 ) 概念由来: 当我们将共性方法(如 eat)向上抽取到父类 Animal 时,发现该方法在父类中无法给出具体的实现(因为猫吃鱼,狗吃肉,方法体不同)。这种只有方法声明而没有方法体的,就是抽象方法。有抽象方法的类,必须是抽象类。

2 ) 格式:
抽象方法:public abstract void 方法名(参数列表);
抽象类:public abstract class 类名 { ... }

3 ) 案例:

// 父类是抽象类 
public abstract class Animal {
    // 普通方法,可以直接有实现 
    public void drink() { System.out.println("喝水"); }
    // 抽象方法,没有方法体 
    public abstract void eat();
}
 
// 子类继承抽象类后,必须重写所有抽象方法 
public class Cat extends Animal {
    @Override 
    public void eat() { System.out.println("猫吃鱼"); }
}
public class Dog extends Animal {
    @Override 
    public void eat() { System.out.println("狗吃肉"); }
}

抽象类的注意事项

1 ) 不能实例化:抽象类不能创建对象(new Animal() 是错误的)。因为如果允许创建,就可以调用没有方法体的抽象方法,这是没有意义的。

2 ) 有构造方法:抽象类中有构造方法。这是为了让子类通过 super() 调用,以完成父类成员的初始化。

3 ) 子类的职责:
方案A(推荐):子类必须重写抽象父类中的所有抽象方法。
方案B(不推荐):把自己也变成一个抽象类。但这会导致功能延期,最终必须由另一个子类来实现抽象方法,增加了复杂度。

4 ) 抽象类中可以有普通方法:抽象类中可以没有抽象方法。但有抽象方法的类,必须是抽象类。

模板设计模式

1 ) 设计模式: 一套被反复使用、多数人知晓、经过分类编目的代码设计经验的总结。简单说,就是一种优秀的、固定的编码风格,而不是新技术点。

2 ) 模板设计模式: 抽象类最常见的应用。它将抽象类看成一个模板,用final修饰的普通方法作为模板骨架(定义好流程),而将流程中不能确定的部分定义为抽象方法,让子类来重写实现。

3 ) 案例: 作文模板。所有作文的标题和结尾是固定的(模板),但正文内容由不同学生自己决定

// 作文模板抽象类 
public abstract class CompositionTemplate {
    // final 修饰的模板方法,定义了写作文的整体骨架,不允许子类修改 
    public final void write() {
        System.out.println("<<我的爸爸>>"); // 固定部分 
        body(); // 不确定部分,由子类实现 
        System.out.println("啊~ 这就是我的爸爸"); // 固定部分 
    }
    // 抽象方法,将变化的正文内容留给子类实现 
    public abstract void body();
}
 
// Tom 同学的作文 
public class Tom extends CompositionTemplate {
    @Override 
    public void body() {
        System.out.println("那是一个秋天, 风儿那么缠绵...");
    }
}
// 测试 
public class Test { public static void main(String[] args) { new Tom().write(); } }

final 关键字

final 是最终的意思,可用于修饰类、方法和变量。

1 ) 修饰类:该类是最终类,不能被继承。例如,当希望一个工具类的所有方法都不能被重写时,可以直接将类声明为 final
java public final class Fu { } // public class Zi extends Fu { } // 报错

2 ) 修饰方法:该方法是最终方法,不能被重写。常用于模板方法(如上文的 write())。

public class Fu { public final void show(){} }
// public class Zi extends Fu { public void show(){} } // 报错 

3 ) 修饰变量:变量变为常量,只能被赋值一次。

  • 基本数据类型:数据值不可变。

  • 引用数据类型:对象的内存地址不可变,但对象内部属性可以修改。

    // 常量命名规范: 单词全大写,多个单词用_分隔 
    public static final int MAX_VALUE = 100;
    
    final Student stu = new Student("张三"); // 地址值被绑定,不能修改 
    stu.setName("李四"); // OK, 对象内容可以修改 
    // stu = new Student("李四"); // Error, 不能新指向其他对象 
    
  • 修饰成员变量时:必须在对象构造完成前完成初始化。有两种方式:

    1. 在定义时直接赋值:final int a = 10;
    2. 在构造方法中完成赋值。

信息管理系统 - 抽象类改进

运用抽象类思想,对 BaseStudentController 继续优化

  1. inputStudentInfo 设计为抽象方法,因为它就是模板中无法确定的部分
  2. 将类本身和内部不希望子类修改的核心方法(如 start, addStudent 等)用 abstract 和 final 进行修饰,强化模板设计的规范性和安全性
public abstract class BaseStudentController {
    // ... 其他final方法 ...
    public final void start() { ... }
    public abstract Student inputStudentInfo(String id);
}

代码块

Java中用 {} 括起来的代码段就是代码块,根据位置和修饰符不同,分为三种

1 ) 局部代码块

位置:方法内部

作用:限定变量的生命周期,让变量尽早释放,提高内存利用率

public void method() {
    {
        int a = 10; // a 的作用域只在这个大括号内 
        System.out.println(a);
    }
    // System.out.println(a); // 无法访问,a已从内存释放 
}

2 ) 构造代码块

位置:类中方法外,无任何修饰符。

特点:每次构造方法执行时,都会在构造方法体之前执行。

作用:将多个构造方法中相同的代码抽取出来,提高代码复用性。

class Student {
    { System.out.println("好好学习"); } // 构造代码块 

    public Student() { System.out.println("空参构造"); }
    public Student(int a) { System.out.println("带参构造"); }
}
// 每次 new Student(),都会先打印“好好学习”,再打印各自的构造方法内容 

3 ) 静态代码块

位置:类中方法外,用 static 修饰。

特点:随着类的加载而加载,且只执行一次,优先于对象的创建。

作用:在类加载时,做一些数据初始化操作

class Person {
    static { System.out.println("我是静态代码块, 类一加载我就执行"); }
    public Person() { System.out.println("构造方法执行"); }
}
// 执行 new Person() 两次,静态代码块只在第一次加载类时执行 

信息管理系统 - 代码块改进

为了在系统启动后,自动拥有初始学生数据,方便功能测试,我们可以利用静态代码块来初始化 StudentDao 中的学生数组/集合。

public class StudentDao implements BaseStudentDao {
    // 创建学生对象数组 
    private static Student[] stus = new Student[5];
 
    // 静态代码块: 初始化数组数据 
    static {
        Student stu1 = new Student("heima001", "张三", "23", "1999-11-11");
        Student stu2 = new Student("heima002", "李四", "24", "2000-11-11");
        stus[0] = stu1;
        stus[1] = stu2;
    }
    // ...
}

这样,当 StudentDao 类被加载到JVM时,静态代码块执行,学生数组就自动添加了初始数据,大大方便了后续的开发和测试

总结

1 ) 继承 — 代码复用的基石

  • 概念:子类通过 extends 关键字获取父类的非私有成员。
  • 优点:提高代码复用性、维护性,是多态的前提。
  • 弊端:侵入性强,耦合性高,使用需谨慎。
  • 特点:Java 单继承、多层继承,不支持多继承(避免方法冲突)。
  • 成员访问:
    • 变量:就近原则(局部 → 本类成员 → 父类成员),同名时用 super. 访问父类变量。
    • 方法:子类优先,用 super. 可显式调用父类方法。
    • 构造方法:子类构造默认 super() 调用父类无参构造,用于完成父类数据初始化。
  • 方法重写:子类定义与父类方法签名完全相同的方法,用于扩展或改变父类行为。
    • 要求:权限不降低,静态只能重写静态,私有不可重写。
    • 与重载的区别:重载在同类的同名不同参。

2 ) 抽象类 — 模板与约束

  • 抽象方法:只有声明,没有方法体,用 abstract 修饰。
  • 抽象类:包含抽象方法的类必须声明为抽象类,也可包含普通方法。
  • 规则:
    • 不能实例化,但有构造方法(供子类 super() 用)。
    • 子类必须实现所有抽象方法,或自己也声明为抽象类。
  • 设计模式 — 模板模式:抽象类作为模板,定义骨架流程(final 方法),将可变部分声明为抽象方法,交由子类实现。保证整体结构统一,管理灵活。

3 ) final 关键字 — 不可变性

  • 修饰类:类不能被继承。
  • 修饰方法:方法不能被重写。
  • 修饰变量:
    • 基本类型值不可变,引用类型地址不可变(属性可变)。
    • 成员变量须在构造完成前初始化(直接赋值或构造方法中赋值)。
    • 常量命名全大写,多单词用下划线分隔。

4 )代码块 — 初始化利器

  • 局部代码块(方法内):控制变量生命周期,优化内存。
  • 构造代码块(类中方法外):每次构造方法执行前调用,提取公共初始化代码。
  • 静态代码块(static 修饰):类加载时执行一次,适合静态数据初始化。

5 ) 权限修饰符 — 访问控制
从紧到宽:private → 默认 → protected → public。合理使用保护封装和继承可见性。

6 ) 实战:信息管理系统的优化

  • 实体类:抽取 Person 父类,Student、Teacher 继承。
  • 控制器层:
    • 抽取 BaseStudentController 抽象父类,定义模板方法 start()、addStudent() 等并声明为 final。
    • 将差异方法 inputStudentInfo() 声明为抽象方法,由子类 StudentController、OtherStudentController 分别实现。
    • 符合开闭原则,新增子类时无需修改父类代码。
    • 数据访问层:在 StudentDao 中使用静态代码块初始化初始学生数据,方便测试。

要领:面向对象编程中,通过继承提升复用,通过抽象规范行为,通过 final 锁定核心,通过代码块巧设初始化,最终构建出灵活、可维护的应用程序架构。

如果需要更简短的结论,一句话:以继承和抽象为核心的模板设计模式,结合 final 约束和各类代码块,能够有效提升 Java 项目代码的结构化、复用性和可维护性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值