GP_05_接口、匿名内部类、泛型、异常

本文详细介绍了Java中的final修饰符如何作用于引用数据类型,接口的特性,包括多继承和默认方法,以及多态的概念和应用。此外,还探讨了匿名内部类简化开发的方式,泛型的增强方法功能,以及异常处理的基本结构和方法,如try-catch和throws关键字的使用。
摘要由CSDN通过智能技术生成

零.昨日复习

零.晨考:

  1. final 修饰引用数据类型变量有什么特征?
  • final 修饰引用数据类型变量,存储地址不可变[指向不可变]
  • 引用数据类型指向的空间内容可以修改[指向内容可变]
  1. static 修饰静态代码块使用要求

  2. 类文件加载阶段,static 修饰静态代码块内容一定执行,并且有且只执行一次。

  3. 静态代码块可以使用类内的静态资源

  4. 静态代码块不可以使用类内的非静态资源

  5. 静态代码块常用于项目启动初始化操作,配置文件读取,日志文件加载,相关配套程序准备。。。

  6. 常用运算符

算术运算符:

±*/ % () =
+= -= *= /= %=
关系运算符:

< >= <= != ==
逻辑运算符:
&& 同真为真,有假【即】假
|| 有真【即】真,同假为假
! 取反
条件运算符:
condition ? true result : false result;
自增自减运算符:

++

  1. abstract 修饰成员方法有什么特征

  2. 方法没有方法体

  3. 方法有且只能定义在 abstract 修饰的类内或者 interface 接口中

  4. 一个非 abstract 修饰类继承 abstract 或者 遵从 interface 接口,必须实现 abstract 修饰的方法。

  5. public protected private 关键字修饰特征

public
公开的~~~
protected
1. 类内使用
2. 同包内使用
3. 子孙类可以使用
private
有且只能类内使用

  1. 数组容量的范围

​ 0 ~ Integer.MAX_VALUE - 8

一.interface接口【重点】

1.1 interface接口语法特征

关键字:
	interface
类【遵从】关键字:
	implements

接口的定义格式:


interface 接口名{
	成员变量;//缺省属性 public static final 公开的带有名称的常量,仅提供数据操作
	成员方法;// 缺省属性 public abstract
    默认成员方法;
}

遵从接口的格式


class TypaA implements 接口名 {
	// TypeA 类必须实现接口中所有缺省属性为public abstract 的修饰方法
}

基本语法代码演示

package com.qfedu.a_interface;

// 自定义接口
interface A {
    int NUM = 10; // 缺省属性 public static final

    void test(); // 缺省属性 public abstract
}

class TypeA implements A {
    @Override
    public void test() {
        System.out.println("Type A 类遵从接口 A 实现 test 方法");
    }
}

/**
 * @author Anonymous 2023/2/27 9:37
 */
public class Demo1 {
    public static void main(String[] args) {
        new TypeA().test();
    }
}

1.2interface语法特殊用法

一个类可以遵从多个接口

interface B {
    void testB();

    void test();
}

interface C {
    void testC();

    void test();
}

/**
 * 一个类可以同时遵从多个接口,不同的接口使用 , 隔开
 */
class TypeB implements B, C {
    @Override
    public void testB() {
        System.out.println(111);
    }

    @Override
    public void testC() {
        System.out.println(222);
    }

    @Override
    public void test() {
        System.out.println("遵从多接口,接口中有相同声明的方法,实现类有且只实现一次即可。");
    }
}

接口可以继承,且可以多继承其他接口

package com.qf.poly;

public interface USB1_0 {
    void usb1_0Connected();
}
interface USB2_0 {
    void usb2_0Connected();
}
interface USB3_0 {
    void usb3_0Connected();
}

/*
USB3.1同时继承 USB1-0,USB2_0,USB3_0接口
表示USB3.1接口向下兼容
 */

interface USB3_1 extends USB1_0,USB2_0,USB3_0 {
    void usb3_1Connected();
}

/*
实现类遵从接口,同时需要实现当前接口对应父类接口中的缺省属性为 public abstract 修饰方法
 */
class LogiUDisk implements USB3_1 {
    @Override
    public void usb1_0Connected(){
        System.out.println("USB 1_0支持");
    }

    @Override
    public void usb2_0Connected() {
        System.out.println("Usb 2_0支持");
    }

    @Override
    public void usb3_0Connected() {
        System.out.println("usb 3_0支持");
    }

    @Override
    public void usb3_1Connected() {
        System.out.println("usb 3_1支持");
    }
}

default 关键字特征

default 关键字在接口中有什么作用
修饰方法,对应方法可以有方法体
JDK1.8 以上版本支持。

interface D {
    // 缺省属性为 public abstract 修饰方法
    void test();

    default void defaultMethod() {
        System.out.println("JDK 1.8 以上版本支持的 default 接口方法修饰默认方法");
    }
}

class TypeC implements D {

    @Override
    public void test() {
        System.out.println("遵从接口要求必须实现的方法");
    }

    @Override
    public void defaultMethod() {
        System.out.println("接口中默认方法,实现类可以【重写】");
    }
}

二.多态

编译看左,运行看右

白话:

​ 父类的引用数据类型变量,指向子类或者间接子类的对象(向上转型)

​ 接口的引用数据类型变量,指向遵从接口的实现类对象。

2.1多态举例

package com.qf.poly;

// 父类
class Animal {}

// Animal子类
class Panda extends Animal {}
class Monkey extends Animal {}
class Snake extends Animal {}

// Animal 孙类
class JSMonkey extends Animal {}


public class Demo1 {
    public static void main(String[] args) {

        // 创建父类和子类对象
        Animal animal = new Animal();
        Animal panda = new Panda();
        Animal monkey = new Monkey();
        Animal snake = new Snake();

        /*
        面向对象编程过程中:
        要什么给什么,用什么拿什么 【数据类型一致化】
         */
        // 当前方法所需数据类型为 Animal 类型
        feedAnimal(animal);
        // 当前方法参数支持 Animal 类型的子类。
        feedAnimal(panda);
        feedAnimal(monkey);
        feedAnimal(snake);
        feedAnimal(new JSMonkey());


        System.out.println("--------------------------");

        /*
        利用多态
         */
        //数据类型向上转型
        //Panda 类型对象 ==> Animal 类型
        Animal a1 = new Panda();
        System.out.println(a1.getClass().getName() + "来吃饭啦~~");
    }

    public static void feedAnimal(Animal animal) {
        /*
        animal.getClass().getName()
        获取当前参数对象,对应的具体的数据类型名称
         */
        System.out.println(animal.getClass().getName() + "来吃饭啦~");
    }

}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-93C0ULIX-1678062678771)(GPday5.assets/image-20230227192706435.png)]

2.2USB接口

后期项目中框架,模块,功能,基本上都是通过接口衔接,降低代码的耦合度,提高代码的复用度和可移植性。

package com.qf.poly;

import java.awt.peer.MouseInfoPeer;

interface USB {
    void connect();
}
class Mouse implements USB {
    @Override
    public void connect() {
        System.out.println("鼠标遵从USB接口");
    }
}
class LogiMouse extends Mouse {
    @Override
    public void connect() {
        System.out.println("罗技鼠标遵从鼠标");

    }
}
class Keyboard implements USB {
    @Override
    public void connect() {
        System.out.println("键盘遵从USB接口");
    }
}
class IKBCKeyboard extends Keyboard {
    @Override
    public void connect() {

        System.out.println("IKBC键盘遵从键盘");
    }
}
// 电脑类
class PC{
    // 电脑声明方法,需要连接USB设备
    public void useUSBInterface(USB usb) {
        // 通过USB接口调用connect方法,该方法具体实现内容是由USB实现类决定
        usb.connect();
    }
}

public class Demo2 {
    public static void main(String[] args) {
        Mouse mouse = new Mouse();
        Keyboard keyboard = new Keyboard();

        PC pc = new PC();

        /*
        方法所需参数为接口类型,实际所需的类型是接口的实现类对象。【开发中常用模式】
        interface 接口同时也是一个特殊的类。
         */
        pc.useUSBInterface(mouse);
        pc.useUSBInterface(keyboard);
        pc.useUSBInterface(new LogiMouse());
        pc.useUSBInterface(new IKBCKeyboard());


    }


}

2.3 多态总结

  1. 拓宽方法参数支持数据类型范围,满足数据类型一致化,支持数据类型多样化
  2. 拓宽方法返回值类型,可以支持类型统一。

三.匿名内部类【重点】

JDK 1.7 版本及其以下版本常用,JDK 1.8 修改为 lambda 但是有语法限制

package com.qf.poly;

interface A {
    void test();
}

/*
【类名没用】
    1. 有对象
    2. 方法实现
 */
class TypeA implements A {
    @Override
    public void test() {
        System.out.println("TypeA 遵从接口 A 实现方法");
    }
}

/**
 * @author Anonymous 2023/2/27 11:47
 */
public class Demo3 {
    public static void main(String[] args) {
        TypeA typeA = new TypeA();
        typeA.test();// TypeA 遵从接口 A 实现方法

        /*
        问题一:
            请问这里实例化的是 A 接口对象还是 A 接口实现类对象???
                A 接口实现类对象
        问题二:
            大括号内容是否可以看做是一个接口 A 实现类【本体】???
                是的!!!
        问题三:
            A() 接口 A 的构造方法,该方法做了什么事情???
                A 在这里提供数据类型,并且隐含遵从关系,告知编译器当前实例化对象
                是遵从接口 A 的实现类对象。
                    A ==> XXX implements A
                () 借用接口 A 的构造方法,实际上实例化对象是接口 A 的实现类对象
                但是没有类名。
                【匿名内部类】
         问题四:
            实例化实现类对象赋值给接口 A 的引用数据类型变量
            【多态】
                接口的引用数据类型变量,指向遵从接口的实现类对象

         */
        A a = new A() {
            @Override
            public void test() {
                System.out.println("不报错");
            }

        };

        a.test();


        // 匿名内部类的匿名对象,直接调用方法。
        new A() {
            @Override
            public void test() {
                System.out.println("常用写法");
            }
        }.test();
    }
}

补充:自己的内容

1基于接口

需求:想使用A接口,并创建多个对象
传统方式:需要几个就写个类,并且遵从该接口,然后创建对象

public class AnonymousInnerClass {
    public static void main(String[] args) {

        A tiger = new Tiger();// 多态
        tiger.cry();
        A dog = new Dog();
        dog.cry();
    }
}

interface A {// 接口
    public void cry();
}

class Tiger implements A {
    @Override
    public void cry() {
        System.out.println("老虎叫唤~~");
    }
}
class Dog implements A {
    @Override
    public void cry() {
        System.out.println("狗嗷嗷叫~~");
    }
}

但是这些类我如果只是使用一次,后面不再使用。就非常占用空间
此时可以使用匿名内部类来简化开发

public class AnonymousInnerClass {// 外部类
    public static void main(String[] args) {

         /*
        接口本不能new,这里的本质是系统在底层给我们分配了一个临时的、名字为外部类 + $ + 序号的类(即匿名内部类):
        class AnonymousInnerClass$1 implements A {
            @Override
            public void cry() {
                System.out.println("老虎嗷嗷叫~");
            }
        }
         */
        A Tiger = new A() {
            @Override
            public void cry() {
                System.out.println("老虎嗷嗷叫~");
            }
        };
        Tiger.cry();
		System.out.println("Tiger的运行类型是" + Tiger.getClass());// AnonymousInnerClass$1 (即:外部类 + $ + 匿名内部类的序号)
        
//        // 或者也可以直接调用,匿名内部类本身返回的也是对象!
//        new A() {
//            @Override
//            public void cry() {
//                System.out.println("老虎嗷嗷叫~");
//            }
//        }.cry();
        

    }
}

interface A {// 接口
    public void cry();
}

2基于类的

总结(个人理解):其实就是在创建对象的过程的那一步加了一个{ },而new的谁,{ }内就重写谁的方法,相当于系统在底层创建了一个临时的类,且这个类继承了父类。而这个类就叫做匿名内部类

public class AnonymousInnerClass {// 外部类
    public static void main(String[] args) {

        /*
        相当于在底层进行了
        class AnonymousInnerClass$2 extends Father {
            @Override
            public void test() {
                System.out.println("匿名内部类重写了Father的方法`");
            }
        }
         */
        Father father = new Father("Tom") {
            @Override
            public void test() {
                System.out.println("匿名内部类重写了Father的方法`");
            }
        };
        father.test();
        System.out.println(father.getClass());// AnonymousInnerClass$2

//        // 或者也可以直接调用,匿名内部类本身返回的也是对象!
//        new Father("Tom") {
//            @Override
//            public void test() {
//                System.out.println("匿名内部类重写了Father的方法`");
//            }
//        }.test();

    }
}

class Father {
    public Father(String name) {// 构造器

    }

    public void test() {

    }
}

3匿名内部类的细节

1.可以直接访问外部类的所有成员,包含私有的

2.不能添加访问修饰符,因为他的地位就是一个局部变量

3.作用域:仅仅在定义它的方法或代码块中

4.匿名内部类---->访问---->外部类成员【访问方式:直接访问】

5.外部其它类---->不能访问---->匿名内部类(因为匿名内部类地位是一个局部变量)

8.如果外部类和内部类的成员重名时,内部类访问的胡,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问

四.泛型

4.1泛型概述

1.泛型满足数据类型支持多样化

2.严格遵守数据类型一致化要求

3.核心目标是增强方法

4.2泛型的格式

<英文单个大写字母无意义占位符>
一般情况下会选择以下占位符
    T	Type	类型
    E	Element	元素
    K	KeyV	ValueR	Return	返回值
语法特征:
    1.单一方法增强
    2.整体方法增强
    	a.类声明泛型,类内的成员方法使用
    	b.接口声明泛型,接口内成员方法使用

4.3泛型增强方法

4.3.1单一方法增强

语法格式:

权限修饰符 [static] <自定义泛型占位符> 返回值 方法名(形式参数列表) {
方法体;
}

[严格要求:]形式参数列表中必须有一个参数对应自定义泛型,用于明确约束泛型对应的具体数据类型

其他:

​ 返回值类型可以是自定义泛型T

​ 方法内可以使用泛型定义的局部变量

package com.qf.fx;

import java.io.Serializable;

public class Demo1 {
    public static void main(String[] args) {
        Integer a = getType(100);
        String b = getType("党浩");
        Demo1 d = new Demo1();
        System.out.println(a);// 100
        System.out.println(b);// 党浩
        System.out.println(d);// com.qf.fx.Demo1@14ae5a5

        System.out.println("-----------------------");


        /*
         *  ??? ? = test(1, "字符串");
         *  test(T, T)
         *  按照开发规则,实际参数的数据类型应该是一致的,如果不一致,为了满足
         *  代码的正常运行,会选择变成 Object 类型,失去方法增强含义。
         */
        Object e = test(1, "党浩");
        System.out.println(e);// 1

    }


    // 声明一个带有自定义泛型的方法
    // 泛型占位符为 T ,方法参数数据类型对应自定义泛型类型
    // t 自定义泛型参数
    // <T> 自定义泛型占位符
    // return 自定义泛型类型,具体数据类型需要通过用户传递实际参数来明确
    public static <T> T getType(T t) {
        return t;
    }
    public static <T> T test(T t1,T t2) {
        return t1;
    }
}

package com.qfedu.d_genericity;

import java.util.Arrays;

/**
 * @author Anonymous 2023/2/27 14:59
 */
public class MyArrays {
    /*
    以下三个 public 修饰的公开方法,提供给用户使用,满足用户使用的多样性,数据类型
    处理的单一性。
     */
    public static void printIntArray(Integer[] arr) {
        printArray(arr);
    }

    public static void printDoubleArray(Double[] arr) {
        printArray(arr);
    }

    public static void printFloatArray(Float[] arr) {
        printArray(arr);
    }

    /*
    工具类私有化方法,使用泛型约束方法的参数类型,增强方法功能,用于处理
    数组类型数据展示打印功能,提供给以上三个 public 修饰公开方法使用。

    可以同时满足用户操作使用工具的多样性,同时满足数据处理一致性,节约代码资源。
     */
    private static <T> void printArray(T[] arr) {
        Arrays.sort(arr);
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
}

4.3.2类声明泛型

定义类的格式,在类声明位置告知编译,当前类内有自定义泛型,主要用于方法增强

class 类名<自定义泛型占位符> {
	成员方法可以使用自定义泛型.
}

类名带有自定义泛型,需要通过【实例化】对象过程进行泛型对应具体数据类型确定。

// IDEA 写法
类名<具体数据类型> 类对象 = new 类名<>();

4.3.3接口声明泛型

接口声明泛型

interface 接口名<自定义泛型占位符> {
	接口中的成员变量无法使用自定义泛型,因为缺省属性为 public static final ,定义时需要初始化。无法对于泛型类型进行初始化操作。
 
因此接口声明泛型有且只用于成员方法。
         1. 缺省属性为 public abstract 的方法可以使用自定义泛型
        2. default 默认方法也可以使用自定义泛型
}

接口声明泛型影响的是实现类操作,实现类遵从带有自定义泛型的接口有两种形式

  1. 妻管严 【约束模式】
  2. 媳妇回娘家 【自由模式】

接口实现

interface A<T> {
    /**
     * 缺省属性为 public abstract 修饰方法,带有自定义泛型
     * return 接口约束的自定义泛型,需要实现类明确泛型对应的具体类型
     */
    T getType(T t);

    /**
     * default 约束的默认方法,可以有方法体,这里使用的自定义泛型。
     * return 接口约束的自定义泛型,需要实现类明确泛型对应的具体类型
     */
    default T testDefault(T t) {
        return t;
    }
}

妻管严模式

/*
【妻管严模式】
    类遵从接口,直接明确泛型对应的具体数据类型

    A<String> 告知编译器,当前接口中泛型对应的具体数据类型为 String 类型
    要求 TypeB 中实现的方法,所有泛型对应的位置都是 String 类型。
 */
class TypeB implements A<String> {

    /*
    所有泛型对应的位置都是 String 类型
     */
    @Override
    public String getType(String s) {
        return s;
    }

    @Override
    public String testDefault(String s) {
        return s;
    }
}

媳妇回娘家,自由模式

/*
媳妇回娘家,自由模式
    TypeC 类遵从接口 A 并且 TypeC 类声明和接口一致的泛型占位符。
    泛型对应的具体数据类型,是通过实例化对象操作约束。
 */
class TypeC<T> implements A<T> {

    @Override
    public T getType(T t) {
        return t;
    }

    @Override
    public T testDefault(T t) {
        return t;
    }
}

五.异常

5.1异常的结构

Java 中所有异常和错误的基类:
Throwable

Throwable 有两个子类
--| Exception 异常 可以处理【捕获,抛出】
--| Error     错误 只能不写 BUG

涉及到的方法:
	构造方法 Constructor
		Throwable();
			无参数构造方法,用于初始化异常/错误信息为 null 的构造方法,很少使用。
		Throwable(String message);	
        	使用 message 描述当前的异常/错误信息
     
    成员方法 Method
    	printStackTrace();
    		在控制台展示错误/异常导致的方法调用流程,和错误信息,红色
    	String toString();
    		获取当前异常/错误简要信息描述,异常/错误的类型
    	String getMessage();
    		获取当前异常/错误的信息

5.2异常的分类

运行时异常
	Java 编译器不会提示当前异常的处理要求,JVM 针对于这些异常是有对应的处理手段,通常是
	printStackTrace 方法。
	
编译时异常
	【提示】告知程序员当前代码出现异常的情况非常紧急,极易出现.
	代码编写过程中,针对于当前异常进行预处理【捕获,抛出】
	

后期项目中 【Servlet】
	可以完成规范的异常处理器,针对于不同的异常类型,不同的异常情况,不同的异常等级,进行分别处理
	INFO    信息
	WARNING 警告
	DANGER  危险
	ERROR   错误

5.3异常处理方式——捕获

package com.qfedu.e_throwable;

import java.lang.reflect.Method;

/**
 * 捕获异常
 *  1. 如果代码通过 try - catch 捕获异常,JVM 会认为当前代码中没有异常情况,可以正常运行。
 *  2. 支持多个 catch 处理不同的异常
 *  3. 目前没有其他方式可以处理异常,后期可以记录错误日志。
 *  4. try - catch - finally 后期用于管理资源
 * @author Anonymous 2023/2/27 20:49
 */
public class Demo1 {
    public static void main(String[] args) {
        /*
        try 大括号中是有可能出现异常代码
         */
        try {
             /*
            用户指定对应类型的完整的【包名.类名】获取对应类型的 Class 对象【反射重点 Reflect】
             */
            Class<?> aClass = Class.forName("com.qfedu.a_interface.D1");

            Method test = aClass.getMethod("test");
        } catch (ClassNotFoundException e) {
            /*
             catch 之后是捕获的异常类型,同时将当前异常对象临时存储到引用数据变量 e 中
             之后是针对于当前异常的处理形式,处理方法,Java 默认是 控制台展示。
             */
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            // try - catch 允许多个异常情况,可以分门别类处理不同的异常信息。
            e.printStackTrace();
        }

        System.out.println("-------------代码正常运行---------------");
    }
}

5.4常处理方式——抛出

使用的关键字:

throws在方法的声明位置告知调用者当前方法有哪些异常抛出

throw在方法中,出现对应的异常情况,异常数据,抛出对应的异常对象,并且带有异常信息。

package com.qfedu.a_throwable;

import java.io.FileNotFoundException;

/**
 * @author Anonymous 2023/2/28 9:34
 */
public class Demo1 {
    public static void main(String[] args)
            throws FileNotFoundException {
        /*
        调用一个带有异常抛出的方法,需要考虑当前异常的处理方式
            方式一: 捕获处理
            方式一: 抛出处理
         */

        try {
            // 捕获处理
            searchFile("1.txt");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        // 抛出处理 当前方法声明位置补充 throws FileNotFoundException
        searchFile("2.txt");
    }

    /**
     * 搜索文件方法
     *
     * @param fileName 用户指定的方法名
     * @throws FileNotFoundException 用户提供的文件路径/名称不符合要求
     */
    public static void searchFile(String fileName)
            throws FileNotFoundException {
        /*
         判断用户同的文件路径是否有问题
            情况一: 用户提供的 fileName 是 null
            情况二: 用户提供的 fileName 字符串为 空字符串
            一定会导致文件找不到!!!
         */
        if (null == fileName || "".equals(fileName)) {
            /*
            throw 抛出异常的关键字
                new FileNotFoundException() 实例化异常对象,同时提供对应的异常信息

            IDEA错误提示: Unhandled exception: java.io.FileNotFoundException
            【注意】
                方法运行到 throw 抛出异常操作,当前代码终止运行。
             */
            throw new FileNotFoundException("文件路径不合法");
        }

        // 每一个错误条件有且只能抛出一个异常对象,如果存在多种异常情况,需要不同的 if 判断执行。
        if (fileName.length() > 10) {
            throw new IllegalArgumentException("非法参数异常");
        }

        System.out.println("已找到" + fileName);
    }
}

5.5抛出和捕获的对比

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i9wrvUGc-1678062678773)(GP_05.assets/image-20230228141927536.png)]
* @throws FileNotFoundException 用户提供的文件路径/名称不符合要求
/
public static void searchFile(String fileName)
throws FileNotFoundException {
/

判断用户同的文件路径是否有问题
情况一: 用户提供的 fileName 是 null
情况二: 用户提供的 fileName 字符串为 空字符串
一定会导致文件找不到!!!
/
if (null == fileName || “”.equals(fileName)) {
/

throw 抛出异常的关键字
new FileNotFoundException() 实例化异常对象,同时提供对应的异常信息

        IDEA错误提示: Unhandled exception: java.io.FileNotFoundException
        【注意】
            方法运行到 throw 抛出异常操作,当前代码终止运行。
         */
        throw new FileNotFoundException("文件路径不合法");
    }

    // 每一个错误条件有且只能抛出一个异常对象,如果存在多种异常情况,需要不同的 if 判断执行。
    if (fileName.length() > 10) {
        throw new IllegalArgumentException("非法参数异常");
    }

    System.out.println("已找到" + fileName);
}

}


## 5.5抛出和捕获的对比

![\[外链图片转存中...(img-i9wrvUGc-1678062678773)\]](https://img-blog.csdnimg.cn/271afe6ba15446bca520330254dc4daf.png)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值