Java面向对象编程

Java面向对象编程

参加拉勾教育大数据训练营课程笔记

类和对象

对象 - 现实生活中的具体的客观实体,Java语言中体现为内存中的一块区域

类 - 抽象出具有相同特征和行为的多个对象的抽象描述。是蓝图,设计图

类定义

类的声明

  • 类名遵循Pascal命名 - 所有单词首字母大写,比如BigData,代码文件名与类名相同,比如BigData.java

    public class ClassName {
        
    }
    
  • 构造方法

    • 初始化类的成员变量(特征变量)
    • 与类名相同,无返回类型
    • 可以重载
    • 提供构造方法后,编译器不再提供默认的无参数构造方法
    public class ClassName {
        public ClassName() {// 无参构造函数
        }
        public ClassName(int a) {// 有参构造函数
        }
    }
    
  • 成员变量 - Camel命名规范,常量则是全部大写,单词间用下划线连接(_),如private final int THIS_IS_A_CONSTANT = 0

    public class ClassName {
        int thisIsAnInteger = 0;
        public ClassName() {// 无参构造函数
        }
        public ClassName(int a) {// 有参构造函数
        }
    }
    

    final成员变量初始化的三种方式:

    1. 直接赋值语句初始化
    2. 代码块初始化
    3. 构造函数初始化
    // 1.
    public class ClassName {
        int final thisIsAnInteger = 0;
    }
    // 2.
    public class ClassName {
        int final thisIsAnInteger;
        {
            thisIsAnInteger = 0;
        }
    }
    // 3.
    public class ClassName {
        int final thisIsAnInteger;
        public ClassName() {
            thisIsAnInteger = 0;
        }
    }
    
  • 成员方法 - Camel命名规范

    public class ClassName {
        int thisIsMethod() {} // 有返回值,无参数成员方法
        void noReturnNoParametersMethod() {} // 无返回值,无参数成员方法
        int returnWithParametersMethod(int a, int b) {} // 有返回值,有参数成员方法
        int intReturnVariableParametersMethod(int a, String...args) {} // 有返回值,有参数,有变长参数成员方法,变长参数必须放最后
    }
    

    传参注意事项:

    1. 修改基本数据类型的形参,不会影响实参的值(传值)
    2. 修改引用类型的形参,会影响实参(传址)
    3. 调用一个方法时,会为方法创建一个栈帧的存储区域,用来存储方法的参数、局部变量等,调用结束栈帧空间就被释放(数据被清除)

    方法重载,满足其一:

    • 参数个数不同
    • 参数类型不同
    • 参数顺序不同

    递推和递归求阶乘和斐波拉契数列(Fibonacci sequence):

    • 递推 - 通过循环实现
    • 递归 - 方法直接或间接调用自身

    Notes:

    递归出现性能瓶颈时,改用递推实现

  • this关键字

    • 消除歧义 - 成员方法会优先使用局部变量,当局部变量名和成员变量名相同时,用this关键字明确指定变量或方法是成员变量或方法
    • 调用成员变量、方法,作为返回值
    • 构造方法第一行调用其他形式构造方法:this([parameters...]);
    • 构造方法中指向正在构造的对象
    • 成员方法中指向当前对象

类的实例化(创建对象)

  • 在堆区申请一块存储区域,并把地址保存到引用变量中

    // ClassName.java
    class ClassName {}
    
    // ClassTest.java
    ClassName className = new ClassName([constructor parameters,...]);
    
  • className变量是一个引用,指向新的ClassName对一个对象(即保存指向保存对象的堆区的地址)。引用数据的默认初始值是null

  • 匿名对象 - new ClassName([constructor parameters,...]);

  • 构造块 - 类定义中由大括号括起来的代码块,每次实例化都会调用一次

    class ClassName {
        { // 构造代码块
            a = 1;
            b = new Connection();
        }
    }
    
  • 静态代码块,由static修饰的构造块,随着类加载时执行一次

    class ClassName {
        static { // 静态代码块
            a = 1;
            b = new Connection();
        }
    }
    

单个class中,构造块,静态代码块和构造方法的执行顺序:

  1. 静态代码块(执行一次),类加载时
  2. 构造块,实例化时
  3. 构造方法,实例化时

继承中构造块,静态代码块和构造方法的执行顺序:

  1. 先执行父类的静态代码块,再执行子类的静态代码块。

  2. 执行父类的构造块,执行父类的构造方法体。

  3. 执行子类的构造块,执行子类的构造方法体。

封装

控制成员变量合法性、合理性

对成员变量进行密封包装处理,隐藏成员变量的细节以及保证成员变量的合理性,这样的机制叫做封装

实现封装

  1. 私有化成员变量: private int anInt;

  2. 为每个私有变量编写getterssetters方法

  3. 构造方法中也要调用setters保证成员变量的合法性

JavaBean - 可重用组件,其它Java类可以通过反射机制发现和操作这些JavaBean的属性

  • 类是公共的
  • 有一个无参的公共构造方法
  • 有属性,且都有对应的getset方法

Notes:

JavaBeen常用来定义数据实体(data entity)

static

  1. 定义静态变量、方法
  2. 与创建的对象无关,存放在内存方法区
  3. 静态变量、方法被所以方法共享
  4. 静态方法中不能使用非静态变量
  5. 静态方法中没有this关键字
  6. 可以用于getters、setters

应用 - 单例模式

  • 饿汉模式

    public class Singleton {
        private static Singleton instance = new Singleton();
        private Singleton() {}
        public getInstance() {
    		return instance;
    	}
    }
    
  • 懒汉模式

    public class Singleton {
        private static Singleton instance = null;
        private Singleton() {}
        public getInstance() {
          if(null == instance) {
            instance = new Singleton();
          }
          return instance;
        }
    }
    

Notes:

推荐使用饿汉模式,防止多线程资源抢占

继承

目标 - 提高代码的复用性,可维护性及扩展性,也是多态的前提条件

超类、父类、基类 → 子类、派生类、孩子类

类访问权限:

修饰符本类同包类子类其他类
publicYYYY
protectedYYYN
defaultYYNN
privateYNNN

继承特点

  • 当多个类之间有相同的特征和行为时,可以将相同的内容提取出来组成一个公共类,让多个类吸收公共类中已有特征和行为而在多个类型只需要编写自己独有特征和行为的机制,叫做继承。
  • 私有方法、构造方法不能继承;私有变量可以继承但不能直接访问
  • 满足逻辑关系:子类is a父类
  • 单继承,不能继承多个父类,一个父类可以由多个子类

继承的方法不能满足子类需求时,需要重写方法(override)

  • 方法名相同、参数列表相同、返回值类型相同,Java 5开始允许返回子类类型
  • 访问权限不能变小,可以不变或变大
  • 不能抛出更大的异常(异常机制)

使用super()调用父类构造方法

package

  • 相当于命名空间,防止命名冲突
  • 例子:top.datageek.project.module
  • 使用import导入包
  • Java 5后支持导入静态成员

final

  • final class不能被继承,可以防止滥用继承
  • final方法不能被重写(override),可以被继承
  • final成员变量必须初始化,不能更改
  • 很少单独使用final关键字来修饰成员变量,通常使用public static final关键字共同修饰成员变量来表达常量的含义,常量的命名规范要求是所有字母都要大写,不同的单词之间采用下划线连(A_CONSTANT)。

多态

父类引用指向子类实例(子类到父类/小到大的自动转换)

  • 父类独有的方法,子类可以访问
  • 父类子类共有的方法,子类可以访问
  • 子类独有的方法,父类引用不能访问。如果要访问需要做强制类型转换,将父类引用转为子类引用
  • 强制类型转换之前使用instanceof判断:
    if(rectangle instance of Rectangle) 
    
  • 用父类作为参数的类型,当传入不同的子类时,其行为就会是传入的子类行为(方法重写)
// Shape.java
package phase01.module2.code;

public class Shape {
    public void show() {
        System.out.println("Shape");
    }
}

// Rectangle.java
package phase01.module2.code;

public class Rectangle extends Shape {
    @Override
    public void show() {
        super.show(); // call parent's method
        System.out.println("Rectangle");
    }
}

// Circle.java
package phase01.module2.code;

public class Circle extends Shape{
    @Override
    public void show() {
        super.show();
        System.out.println("Circle");
    }
}


// ShapeTest.java
package phase01.module2.code;

public class ShapeTest {
    public static void ShapeShow(Shape shape) {
        shape.show();
    }

    public static void main(String[] args) {
        Shape rect = new Rectangle(); // parent reference points to a child's instance
        Shape circle = new Circle(); // parent reference points to a child's instance
        ShapeShow(rect); // rectangle behavior
        ShapeShow(circle); // circle behavior
    }
}

Notes:

为了代码可维护性和课扩展性,声明父类引用指向子类实例,缺点是不能直接访问子类独有方法

抽象类

抽象方法:public abstract void aMethod([parameterList...]);

抽象类:public abstract aClass {}

  • 不能实例化 - 抽象类可能包含有抽象方法,实例化后可能调用没有方法体的抽象方法

  • 可以有特征变量,可以有方法,可以有构造方法

  • 可以包含抽象方法,也可以没有 - 但最好有,否则失去了创建抽象类的意义

  • 意义

    1. 用于被继承,实现多态

    2. 继承类必须实现抽象方法,否则也必须声明为抽象类 - 模板设计模式。

Notes:

抽象方法不能是private的

接口

public interface MyInterface {
  public final int A = 1; // 必须是常量
  private void concreteMethod(){} // since Java 9
  public abstract void interfaceMethod();
}

接口声明不允许使用protected,public可以省略
在这里插入图片描述
实现声明:class A implements MyInterface

  • 一个class可以实现多个接口
  • 不能有构造方法
  • 只能包含非私有的没有方法体的抽象方法,1.9以后可以有有方法体的私有方法
  • 只能继承接口
  • 可以弥补不能多继承的不足
  • 从Java8开始增加新特性,接口中允许出现非抽象方法静态方法,但非抽象方法需要使用default关键字修饰。意味着可以增加非抽象方法,而其实现类可以自由选择要不要实现这个新方法,增强了接口的可扩展性和可维护性

特殊类

内部类 - 仅为一个类服务

普通内部类
  • 访问外部类与本类同名成员时 - 外部类.this.同名变量/方法

  • 可以有成员变量、方法、构造方法

  • 可以使用finalabstract修饰

  • 可以使用private, protected修饰

  • 需要使用外部类对象来创建对象

  • 内部类编译产生的class文件

    NormalInnerClass$A.class
    NormalInnerClass$B.class
    NormalInnerClass$C.class
    NormalInnerClass$D.class
    NormalInnerClass$E.class
    NormalInnerClass$F.class
    

普通内部类测试代码:

// NormalInnerClass.java
package phase01.module2.code.innerclass;

public class NormalInnerClass {
    private int c = 0;

    public class A {
        private int a = 0;
        private int c = 1;

        public void show() {
            System.out.println(NormalInnerClass.this.c); // print NormalInnerClass's c - 0
            System.out.println(this.c); // print A's c - 1
        }
    }

    private class B {
        private int b = 0;
        private int c = 2;
    }

    protected class C {
        private int d = 0;
        private int c = 3;

        public void show() {
            System.out.println(NormalInnerClass.this.c); // print NormalInnerClass's c - 0
            System.out.println(this.c); // print A's c - 3
        }
    }

    private static class D {
        private int e = 0;

        private void show() {
        }
    }

    private final class E {
        private int f = 0;
    }

    private abstract class F {
        abstract void display();
    }

    public void show() {
        A a = new A();
        System.out.println(c);
        a.show();
    }
}

// NormalInnerClassTest.java
package phase01.module2.code.innerclass;

public class NormalInnerClassTest {
    public static void main(String[] args) {
        NormalInnerClass normalInnerClass = new NormalInnerClass();
        NormalInnerClass.A a = normalInnerClass.new A(); // public inner class A
        a.show();
        NormalInnerClass.C c = normalInnerClass.new C(); // protected inner class C
        c.show();
    }
}
静态内部类
  • 不能访问外部类的非静态成员
  • 可以直接通过类创建对象(不需要外部类的实例)
  • 访问和外部类同名成员,使用className.memberName

静态内部类测试代码:

// StaticInnerClass.java
package module2.code.innerclass;

public class StaticInnerClass {
    private int cnt = 0;
    private static int snt = 2;

    public static class StaticInner {
        private int ia = 3;
        private static int snt = 4;

        public StaticInner() {
            System.out.println("StaticInner");
        }

        public void show() {
            System.out.println(ia); // 3
//            System.out.println(cnt); // Non-static field 'cnt' cannot be referenced from a static context
            System.out.println(snt); // 4
        }

        public void show2(int snt) {
            System.out.println(snt); // parameter snt
            System.out.println(StaticInner.snt); // 4
            System.out.println(StaticInnerClass.snt); // 2
        }
    }
}

// StaticInnerClassTest.java
package module2.code.innerclass;

public class StaticInnerClassTest {
    public static void main(String[] args) {
        StaticInnerClass.StaticInner staticInner = new StaticInnerClass.StaticInner();
        staticInner.show();
        staticInner.show2(5);
    }
}

局部内部类
  • 声明在方法体内只在方法体内可用

  • 在方法体内直接创建对象

  • 不能使用权限访问控制(private, public, protected, static)

  • 可以使用方法的局部变量,但不允许修改,所以通常方法给内部类使用的的局部变量会声明为常量final变量,原因有两个:

    1. 内部类会复制一份方法的局部变量,如果方法体中修改了局部变量的值,内部类还会继续使用旧的值,造成潜在的难以发现的漏洞
    2. 防止在方法内部类意外尝试修改方法的局部变量,引起错误
      在这里插入图片描述
// MethodInnerClass.java
package module2.code.innerclass;

public class MethodInnerClass {
    private int cnt = 1;

    public void show() {
        final int shw = 0;
        class A {
            private int ia = 2;

            public A() {
            }

            public void test() {
                System.out.println(ia); // 2
                System.out.println(cnt); // 1
                System.out.println(shw); // 0
            }
        }
        A a = new A();
        a.test();
    }
}
// MethodInnerClassTest.java
package module2.code.innerclass;

public class MethodInnerClassTest {
    public static void main(String[] args) {
        MethodInnerClass methodInnerClass = new MethodInnerClass();
        methodInnerClass.show();
    }
}
匿名内部类

回调模式的概念

如果一个方法的参数是接口类型,则在调用该方法时,需要创建并传递一个实现此接口类型的对象;而该方法在运行时会调用到参数对象中所实现的方法(接口中定义的)。

// AnonymousInterface.java
package module2.code.anonymous;

public interface AnonymousInterface {
    public abstract void show();
}

// AnonymousInterfaceImp.java
package module2.code.anonymous;

public class AnonymousInterfaceImp implements AnonymousInterface {
    @Override
    public void show() {
        System.out.println("AnonymousInterface的一个实现");
    }
}

// AnonymousInterfaceTest.java
package module2.code.anonymous;

public class AnonymousInterfaceTest {
    public static void test(AnonymousInterface anonymousInterface) {
        anonymousInterface.show();
    }

    public static void main(String[] args) {
        test(new AnonymousInterfaceImp());
    }
}

使用匿名内部类或者lambda(Java 8以后)表达式

// AnonymousInterface.java
package module2.code.anonymous;

public interface AnonymousInterface {
    public abstract void show();
}

// AnonymousInterfaceTest.java
package module2.code.anonymous;

public class AnonymousInterfaceTest {
    public static void test(AnonymousInterface anonymousInterface) {
        anonymousInterface.show();
    }

    public static void main(String[] args) {
        test(() -> System.out.println("lambda")); // lambda
        test(new AnonymousInterface() {
            @Override
            public void show() {
                System.out.println("匿名内部类");
            }
        });
    }
}

枚举类(特殊类)

定义:

  • 从Java 5开始支持
  • 枚举值就是当前类的类型,也就是指向本类的对象,默认使用public static final关键字共同修饰,因此采用枚举类型.的方式调用。
  • 枚举类可以自定义构造方法,但是构造方法的修饰符必须是private,默 认也是私有的。
// EnumDirections.java
package module2.code.direction;

public enum EnumDirections {
    UP(1), DOWN(2), LEFT(3), RIGHT(4);

    EnumDirections(int direction) {
    }
}

// EnumDirectionsTest.java
package module2.code.direction;

public class EnumDirectionsTest {
    public static void test(EnumDirections direction) {
        switch (direction) {
            case UP:
                System.out.println("UP");
                break;
            case DOWN:
                System.out.println("DOWN");
                break;
            case LEFT:
                System.out.println("LEFT");
                break;
            case RIGHT:
                System.out.println("RIGHT");
                break;
            default:
                System.out.println("DEFAULT");
        }
    }

    public static void main(String[] args) {
        test(EnumDirections.UP);
    }
}

所有枚举类继承自java.lang.Enum,继承的方法有:

方法定义解释
static T[] values()返回当前枚举类中的所有对象
String toString()返回当前枚举类对象的名称
int ordinal()获取枚举对象在枚举类中的索引位置
static T valueOf(String str)将参数指定的字符串名转为当前枚举类的对象
int compareTo(E o)Compares this enum with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object. Enum constants are only comparable to other enum constants of the same enum type. The natural order implemented by this method is the order in which the constants are declared.

枚举类实现接口

  1. 整个每局类型重写一次接口的方法

    // DirectionInterface.java
    package module2.code.direction;
    
    public interface DirectionInterface {
        void show();
    }
    
    // EnumDirections.java
    package module2.code.direction;
    
    public enum EnumDirections implements DirectionInterface {
        UP(1), DOWN(2), LEFT(3), RIGHT(4);
    
        EnumDirections(int direction) {
        }
    
        @Override
        public void show() {
            System.out.println("show method called");
        }
    }
    
    // EnumDirectionsTest.java
    package module2.code.direction;
    
    public class EnumDirectionsTest {
        public static void test(EnumDirections direction) {
            switch (direction) {
                case UP:
                    System.out.println("UP");
                    break;
                case DOWN:
                    System.out.println("DOWN");
                    break;
                case LEFT:
                    System.out.println("LEFT");
                    break;
                case RIGHT:
                    System.out.println("RIGHT");
                    break;
                default:
                    System.out.println("DEFAULT");
            }
        }
    
        public static void main(String[] args) {
            EnumDirections[] values = EnumDirections.values();
            for (EnumDirections value : values) {
                value.show(); // call 4 times
            }
            test(EnumDirections.UP);
        }
    }
    
  2. 每个每局类对象重写一次接口方法,4个不同的show()方法重写。每局的每个值实际就是类的一个由public static final修饰的实例,由于类实现了接口,所以作为实例,必须实现接口,这里有匿名内部类实现。
    在这里插入图片描述

    // DirectionInterface.java
    package module2.code.direction;
    
    public interface DirectionInterface {
        void show();
    }
    
    // EnumDirections.java
    package module2.code.direction;
    
    public enum EnumDirections implements DirectionInterface {
    
        UP(1){
            @Override
            public void show() {
                System.out.println("UP");
            }
        }, DOWN(2) {
            @Override
            public void show() {
                System.out.println("DOWN");
            }
        }, LEFT(3) {
            @Override
            public void show() {
                System.out.println("LEFT");
            }
        }, RIGHT(4) {
            @Override
            public void show() {
                System.out.println("RIGHT");
            }
        };
    
        EnumDirections(int direction) {
        }
    }
    
    // EnumDirectionsTest.java
    package module2.code.direction;
    
    public class EnumDirectionsTest {
        public static void test(EnumDirections direction) {
            switch (direction) {
                case UP:
                    System.out.println("UP");
                    break;
                case DOWN:
                    System.out.println("DOWN");
                    break;
                case LEFT:
                    System.out.println("LEFT");
                    break;
                case RIGHT:
                    System.out.println("RIGHT");
                    break;
                default:
                    System.out.println("DEFAULT");
            }
        }
    
        public static void main(String[] args) {
            EnumDirections[] values = EnumDirections.values();
            for (EnumDirections value : values) {
                value.show(); // 4 different overrides of show method
            }
            test(EnumDirections.UP);
        }
    }
    

注解(特殊的接口)

  • 注解(Annotation)又叫标注,是从Java5开始增加的一种引用数据类型。

  • 注解本质上就是代码中的特殊标记,通过这些标记可以在编译、类加载、 以及运行时执行指定的处理。

  • 通过@注解名称的方式可以修饰包、类、 成员方法、成员变量、构造方法、参数、局部变量的声明等。

定义:

访问修饰符 @interface 注解名 {
    注解成员;
}
  • 注解体中只有成员变量没有成员方法,而注解的成员变量以“无形参的方 法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该 成员变量的类型。

  • 如果注解只有一个参数成员,建议使用参数名为value,而类型只能是八 种基本数据类型、String类型、Class类型、enum类型及Annotation类型。

Notes:

  1. 所有注解继承自java.lang.annotation.Annotation接口

  2. 空标记定义称为标记注解或标识注解

// MyAnnotation.java
package module2.code.annotation;

public @interface MyAnnotation {
    public String value() default "default"; // 声明一个String类型的成员变量,变量名是value,public修饰符可以省略
    public String value2(); // 声明一个String类型的成员变量,变量名是value,public修饰符可以省略
    public String value3() default "value 3"; // 声明一个String类型的成员变量,变量名是value,public修饰符可以省略
}

// Person.java
package module2.code.annotation;

@MyAnnotation(value = "A person", value2 = "value 2")
public class Person {
}

元注解 - 作用于注解的注解

  • @Retention - 有效范围,声明周期,取值如下:

    1. RetentionPolicy.SOURCE注解只在源码阶段保留,在编译器进行编译时 它将被丢弃忽视
    2. RetentionPolicy.CLASS注解只被保留到编译进行的时候,它并不会被加 载到 JVM 中,默认方式
    3. RetentionPolicy.RUNTIME注解可以保留到程序运行的时候,它会被加载 进入到 JVM 中,所以在程序运行时可以获取到它们
  • @Documented - 是否在文档注释中。使用javadoc工具可以从程序源代码中抽取类、方法、成员等注释形成一 个和源代码配套的API帮助文档,而该工具抽取时默认不包括注解@Documented用于指定被该注解将被javadoc工具提取成文档。定义为@Documented的注解必须设置Retention值为RUNTIME

    Notes:

    使用ItelliJ生成文档,Tools → \to Generate JavaDoc,给自定义参数–encoding utf-8

  • @Target - 可以修饰哪些元素。@Target用于指定被修饰的注解能用于哪些元素的修饰,取值如下:

    解释
    ElementType.ANNOTATION_TYPE可以给一个注解进行注解
    ElementType.CONSTRUCTOR可以给构造方法进行注解
    ElementType.FIELD可以给属性进行注解
    ElementType.LOCAL_VARIABLE可以给局部变量进行注解
    ElementType.METHOD可以给方法进行注解
    ElementType.PACKAGE可以给一个包进行注解
    ElementType.PARAMETER可以给一个方法内的参数进行注解
    ElementType.TYPE可以给类型进行注解,比如类、接口、枚举

    Java 8开始对元注解@Target的参数类型ElementType枚举值增加了两个:

    • ElementType.TYPE_PARAMETER表示该注解能写在类型变量的声明语句中,如:泛型。

    • ElementType.TYPE_USE表示该注解能写在使用类型的任何语句中。

  • @Inherited - 目标类的子类是否继承注解。并不是说注解本身可以继承,而是说如果一个超类被该注解标记过的注解进行注解时,如果子类没有被任何注解应用时,则子类就继承超类的注解

  • @Repeatable - 是否可以重复。@Repeatable表示注解可重复使用的含义,从Java 8开始增加的新特性。

    // ManTypes.java
    public @interface ManTypes {
        ManType[] value();
    }
    
    // ManType.java
    @Repeatable(value=ManTypes.class)
    public @interface ManType {}
    
    // Man.java
    @ManType("超人")
    @ManType("职工")
    //@ManTypes({@Mantype(value="职工"), @ManType(value="超人")}) // java 8以前处理多个注解的方式
    public class Man {}
    

常见的预制注解

注解名说明
@version标明该类模块的版本
@see参考转向,也就是相关主题
@since从哪个版本开始增加的
@param对方法中某参数的说明,如果没有参数就不能写
@return对方法返回值的说明,如果方法的返回值类型是void就 不能写
@exception对方法可能抛出的异常进行说明
@Override限定重写父类方法, 该注解只能用于方法
@Deprecated用于表示所修饰的元素(类, 方法等)已过时
@SuppressWarnings抑制编译器警告
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值