9.正则表达式-异常--黑马程序员

正则表达式-异常

D:\code\黑马\code\expression\src\com\lmh

正则表达式

由特定的字符组成,代表某种规则,可以简化代码

书写规则

  • String提供了一个方法用于判断是否匹配正则表达式
  • public boolean matches(String regex) 参数为正则表达式
1. 字符类(只匹配单个字符)
符号含义
[ ]里面的内容只出现一次,只能是[ ]中的内容之一
^取反
-从哪-哪
&&交集
|并集,不写默认为并集

例子

//带[]的只能匹配单个字符,如果超过一个字符都是false
public class Test3 {
    public static void main(String[] args) {
        System.out.println("a".matches("[abc]"));// [abc]只能匹配a、b、c,所以返回true
        //等价于
        System.out.println("a".matches("[a-c]"));// [a-c]只能匹配a、b、c,所以返回true
        //等价于
        System.out.println("a".matches("[a|b|c]"));
        // [a|b|c]只能匹配a、b、c,所以返回true,默认|可以省略

        System.out.println("e".matches("[abcd]")); 
        // [abcd]只能匹配a、b、c、d, 不能是e,所以返回false

        "============================================================================="
        
         //^只要出现,就表示后面的整体为非,即不能是后面的整体
        System.out.println("d".matches("[^abc]"));
        // [^abc]表示不能是abc,可以是其他任意字符,所以返回true
        //等价于
        System.out.println("d".matches("[^a-c]"));
        // [^a-c]表示不能是a-c,可以是其他任意字符,所以返回true
        //等价于
        System.out.println("d".matches("[^a|b|c]"));
        // [^a|b|c]表示不能是a|b|c,可以是其他任意字符,所以返回true

        System.out.println("a".matches("[^abc]"));  // false

       "============================================================================="
        
         System.out.println("b".matches("[a-zA-Z]")); 
        // [a-zA-Z]表示只能是a-z A-Z的字符,所以返回true
        //等价于
        System.out.println("b".matches("[a-z]|[A-Z]")); //true
        //等价于
        System.out.println("b".matches("[a-z[A-Z]]")); // true
        
        System.out.println("2".matches("[a-zA-Z]")); // false
        
 "============================================================================="
     
	    System.out.println("k".matches("[a-z&&[^bc]]")); //a到z,除了b和c,所以返回true
        //等价于
        System.out.println("b".matches("[a[d-z]]")); //false
        
        System.out.println("b".matches("[a-z&&[^bc]]")); // false

        System.out.println("ab".matches("[a-zA-Z0-9]"));
        //表示只能是a-z A-Z 0-9的单个字符,两个字符返回false
    }
}
2. 预定义字符(只匹配单个字符)
符号含义
.表示除\n外的任意字符
\表示转义
\d表示一个[0-9]之间的数字,即数字字符
\D表示一个非数字[^0-9]的字符
\s表示空白字符
\S表示非空白字符
\w表示数字、字母或下划线:[a-zA-Z_0-9]
\W表示非数字、字母和下划线

注意:下面的代码中的\\w就表示\w,因为\ \w中的第一个\是转义字符,告诉计算机\\w就是\w,否则如果直接输入\w,计算机会默认将第一个\理解为转义,能看到的也就只有w

//只能匹配单个字符,如果超过一个字符都是false
public class Test4 {
    public static void main(String[] args) {
        //.代表除了换行符以外的任意字符
        System.out.println("徐".matches(".")); // .可以匹配任意字符,所以返回true
        System.out.println("\n".matches(".")); // 不能匹配换行符,所以返回false
        System.out.println("徐徐".matches(".")); // 因为是两个字符,所以为false

        System.out.println("==============================================");
        // \d: 表示一个数字字符,等价于[0-9],因为\为转义字符,所以\d要写成\\d
        System.out.println("3".matches("\\d"));  // \d: 0-9的数字,所以返回true
        System.out.println("a".matches("\\d"));  //false
        // \D: 表示一个非数字字符,等价于[^0-9]
        System.out.println("a".matches("\\D"));  // \D: 不能是0-9的数字,所以返回true
        System.out.println("3".matches("\\D"));  // false
        System.out.println("==============================================");

        // \s: 代表一个空白字符,包括空格、制表符、换页符、换行符
        System.out.println(" ".matches("\\s"));   //true
        System.out.println("\t".matches("\\s"));  //true
        System.out.println("\n".matches("\\s"));  //true
        System.out.println("a".matches("\\s")); // false
        // \S: 代表一个非空白字符,除了空格、制表符、换页符、换行符
        System.out.println("a".matches("\\S"));  // true
        System.out.println(" ".matches("\\S")); // false
        System.out.println("==============================================");

        // \w: 代表一个数字、字母、下划线字符,等价于[a-zA-Z_0-9]
        System.out.println("a".matches("\\w"));  // true
        System.out.println("_".matches("\\w")); // true
        System.out.println("徐".matches("\\w")); // false
        System.out.println("__".matches("\\w"));// false,因为只能匹配单个字符
        // \W: 代表一个非数字、字母、下划线字符,等价于[^a-zA-Z_0-9]
        System.out.println("徐".matches("\\W"));  // true
        System.out.println("a".matches("\\W"));  // false
    }
}
3. 数量词(匹配多个字符)

下表中的X代表单个字符,即上文的字符类或预定义字符

符号含义
X ?X出现0次或一次,并且不能出现X以外的字符
X *X出现0次或多次,并且不能出现X以外的字符
X +X出现1次或多次,并且不能出现X以外的字符
X {n}X正好出现n次,并且不能出现X以外的字符
X {n, }X至少出现n次,并且不能出现X以外的字符
X{n, m}X至少出现n次但是不能超过m次,并且不能出现X以外的字符

注意:数量词中只能出现指定字符,也就是说如果X为666,那么出现66也是错的,因为不是666,详见下文中的小案例的需求二

public class Test2 {
    public static void main(String[] args) {
        //?表示出现0次或1次
        System.out.println("a".matches("\\w?"));   //表示\w出现0次或1次,所以返回true
        System.out.println("".matches("\\w?"));    // 出现0次也可以,所以返回true
        System.out.println("abc".matches("\\w?")); // false,因为出现了3次

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

        // * 代表0次或多次
        System.out.println("abc12".matches("\\w*"));   //出现5次,所以返回true
        System.out.println("".matches("\\w*"));        // 出现0次也可以,所以返回true
        System.out.println("abc12张".matches("\\w*")); //出现了张,所以返回false

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

        // + 代表1次或多次
        System.out.println("abc12".matches("\\w+"));   //出现5次,所以返回true
        System.out.println("".matches("\\w+"));       // 出现0次不可以,所以返回false
        System.out.println("abc12张".matches("\\w+")); // 出现了张,所以返回false

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

        // {n} 代表正好出现n次
        System.out.println("a3c".matches("\\w{3}"));   //出现了3次,所以返回true
        System.out.println("abcd".matches("\\w{3}"));  //出现了4次,所以返回false
        // {n,} 代表至少出现n次
        System.out.println("abcd".matches("\\w{3,}")); // 出现了4次,所以返回true
        System.out.println("ab".matches("\\w{3,}"));   // 出现了2次,所以返回false
        System.out.println("abcde徐".matches("\\w{3,}"));
        //出现了5次,但是有汉字,所以返回false
    
        // {n,m} 代表出现n到m次
        System.out.println("abc232d".matches("\\w{3,9}"));//出现了7次,所以返回true
    }
}
4. 补充符号
符号含义
(?!)忽略大小写
()分组
public class Test2 {
    public static void main(String[] args) {
        //其他几个常用的符号:(?i)忽略大小写 、分组:()
        System.out.println("abc".matches("(?i)abc")); //表示abc忽略大小写,所以返回true
        System.out.println("ABC".matches("(?i)abc")); //表示abc忽略大小写,所以返回true
        System.out.println("aBc".matches("a((?i)b)c")); //表示b忽略大小写,所以返回true
        System.out.println("ABc".matches("a((?i)b)c")); 
        //表示b忽略大小写,a不忽略大小写,所以返回false
    }
}
小案例
public class Test2 {
    public static void main(String[] args) {
        // 需求1:要求要么是3个小写字母,要么是3个数字。
        System.out.println("abc".matches("[a-z]{3}|\\d{3}")); // true
        System.out.println("ABC".matches("[a-z]{3}|\\d{3}")); // false
        System.out.println("123".matches("[a-z]{3}|\\d{3}")); // true
        System.out.println("A12".matches("[a-z]{3}|\\d{3}")); // false

        // 需求2:必须是”我爱“开头,中间可以是至少一个”编程“,最后至少是1个”666“
	    System.out.println("我爱编程编程666666".matches("我爱(编程)+(666)+"));//true
        System.out.println("我爱编程编程66666".matches("我爱(编程)+(666)+"));
        //false,因为出现了不该出现的66
        System.out.println("我爱编程编666666".matches("我爱(编程)+(666)+"));
        //false,因为出现了不该出现的编
    }
}

作用(常见用途)

1. 校验数据格式是否合法

使用String类的matches(“正则表达式”)实例方法

方法说明
public boolean matches( String regex )是String类的实例方法,根据入参的正则表达式判断是否与当前的String对象匹配,如果匹配返回true
案例:校验用户输入的电话、邮箱、时间是否合法
public class Test5 {
    public static void main(String[] args) {
        while (true) {
            System.out.println("判断手机号或座机号请输入1:");
            System.out.println("判断邮箱请输入2:");
            System.out.println("判断时间请输入3:");
            System.out.println("退出请输入其他数字:");
            Scanner scanner = new Scanner(System.in);
            int i = scanner.nextInt();
            switch (i){
                case 1:
                    checkPhone();
                    break;
                case 2:
                    checkEmail();
                    break;
                case 3:
                    checkTime();
                    break;
                default:
                    System.out.println("退出程序");
                    return;
            }
        }
    }

    //ctrl+alt+t 快速生成try catch等
    public static void checkPhone(){
        //手机号常见格式:18676769999  第一位为1,第二位为3-9,后面9位为0-9
        // 座机号常见格式:010-8424242424  0104644535
        // 第一位为0,第二到4位为0-9,-可有可无,-后第一位必须为1-9,后面7到8位为0-9
        while (true) {
            System.out.println("请输入手机号码或座机号:");
            Scanner scanner = new Scanner(System.in);
            String s = scanner.next();
            String regex = "(1[3-9]\\d{9})|(0\\d{2,3}-?[1-9]\\d{7,8})";
            if (s.matches(regex)) {
                System.out.println("是手机号码或座机号");
                System.out.println("============================");
                break;
            }
            else {
                System.out.println("不是手机号码或座机号");
            }
        }
    }

    public static void checkEmail(){
    /*
    邮箱常见格式:
    d1ei0009@163.com
    25143242@qq.com
    itheimad@itcast.com.cn
    2位以上的字母、数字、下划线 + @ + 2位以上的字母、数字、下划线 + 一次或两次 . + 2-10位字母
         */
        while (true) {
            System.out.println("请输入邮箱:");
            Scanner scanner = new Scanner(System.in);
            String s = scanner.next();
            String regex = "\\w{2,}@\\w{2,}(\\.\\w{2,10}){1,2}";
            if (s.matches(regex)) {
                System.out.println("是邮箱");
                System.out.println("============================");
                break;
            }
            else {
                System.out.println("不是邮箱");
            }
        }
    }

    public static void checkTime(){
        //时间常见格式:2020-10-10 10:10:10
        while (true) {
            System.out.println("请输入时间:");
            Scanner scanner = new Scanner(System.in);
            String s = scanner.next();
            String regex = "\\d{4}[-年][01]?\\d[-月][0123]?\\d[-日]\\s*" +
                    "[012]?\\d[时:][0-5]?\\d[:分][0-5]?\\d秒?";
            if (s.matches(regex)) {
                System.out.println("是时间");
                System.out.println("============================");
                break;
            }
            else {
                System.out.println("不是时间");
            }
        }
    }
}
2. 在一段文本中查找满足要求的内容

使用Pattern类的实例和Matcher类的实例

方法说明
public static Pattern compile( String regex )将正则表达式编译成Pattern对象
public java.util.regex.Matcher matcher( CharSequence input )入参为等待爬取的原数据,将字符串与Pattern对象进行匹配,返回Matcher对象
public boolean find()Matcher对象的方法,如果找到匹配项,返回true
public String group()Matcher对象的方法,将找到的匹配项返回
案例:将下面文本中的电话、邮箱、座机号码、热线电话爬取出来

来黑马程序员学习Java,
电话:18669668888,18699997777,
或者联系邮箱:boniu@itcast.cn,
座机电话:01036517895,010-98951256,
邮箱:bozai@itcast.cn,
邮箱:dlei0009@163.com,
热线电话:400-618-9090 ,400-618-4000,4006184000,4006189090

public class Test6 {
    public static void main(String[] args) {
        String data = " 来黑马程序员学习Java,\n" +
                "        电话:18669668888,18699997777\n" +
                "        或者联系邮箱:boniu@itcast.cn,\n" +
                "        座机电话:01036517895,010-98951256\n" +
                "        邮箱:bozai@itcast.cn,\n" +
                "        邮箱:dlei0009@163.com,\n" +
                "        热线电话:400-618-9090 ,400-618-4000,4006184000,4006189090";

        //定义正则表达式即爬取的规则
        String phoneRegex = "(1[3-9]\\d{9})|(0\\d{2,3}-?[1-9]\\d{7,8})";
        String emailRegex = "(\\w{2,}@\\w{2,}(\\.\\w{2,10}){1,2})";
        String reXianPhoneRegex = "(400-?\\d{3,7}-?\\d{3,7})";
        String regex = phoneRegex + "|" + emailRegex + "|" + reXianPhoneRegex;

        //定义Pattern对象,将正则表达式编译成Pattern对象
        Pattern pattern = Pattern.compile(regex);
        
        //定义Matcher对象,将字符串与Pattern对象进行匹配
        Matcher matcher = pattern.matcher(data);
        
        //调用Matcher对象的find方法进行匹配
        while (matcher.find()) {
            System.out.println(matcher.group());
        }
    }
}
/*
result:
        18669668888
        18699997777
        boniu@itcast.cn
        01036517895
        010-98951256
        bozai@itcast.cn
        dlei0009@163.com
        400-618-9090
        400-618-4000
        4006184000
        4006189090
*/
3. 用于搜索替换

使用String类的实例方法replaceAll

方法说明
public String replaceAll(@NotNull String regex, @NotNull String replacement )根据第一个入参的正则表达式寻找当前String对象中的匹配项,替换成第二个入参,返回替换后的String对象
案例一:

请把“古力娜扎ai8888迪丽热巴999aa5566马尔扎哈fbbfsfs42425卡尔扎巴”中间的非中文字符替换成 “-”

public class Test7 {
    public static void main(String[] args) {
        String s = "古力娜扎ai8888迪丽热巴999aa5566马尔扎哈fbbfsfs42425卡尔扎巴";
        String regex = "\\w+";
        String s1 = s.replaceAll(regex, "-");
        System.out.println(s1);
    }
}
//古力娜扎-迪丽热巴-马尔扎哈-卡尔扎巴
案例二(扩展):

某语音系统,收到一个口吃的人说的“我我我喜欢编编编编编编编编编编编编程程程!”需要优化成“我喜欢编程!”。

public class Test8 {
    public static void main(String[] args) {
        String s = "我我我喜欢编编编编编编编编编编编编程程程!";
        /*
         * (.)表示一组:.匹配任意字符的。
         * \\1 :为(.)这一组声明一个组号:1号
         * +:声明必须是重复的字
         * $1可以取到第1组代表的那个重复的字
         */
        String regex = "(.)\\1+";
        String s1 = s.replaceAll(regex, "$1");
        System.out.println(s1);
    }
}
//我喜欢编程!
4. 分割内容

使用String类的实例方法split

方法说明
public String[ ] split( @NotNull String regex )根据输入的正则表达式将字符串分割,返回分割后的String数组
案例:

请把“古力娜扎ai8888迪丽热巴999aa5566马尔扎哈fbbfsfs42425卡尔扎巴”中的人名获取出来。

public class Test9 {
    public static void main(String[] args) {
        String s = "古力娜扎ai8888迪丽热巴999aa5566马尔扎哈fbbfsfs42425卡尔扎巴";
        String regex = "\\w+";
        String[] ss = s.split(regex);
        System.out.println(Arrays.toString(ss));
    }
}
//[古力娜扎, 迪丽热巴, 马尔扎哈, 卡尔扎巴]

异常

异常

程序出现的问题

异常的体系

在这里插入图片描述

异常的分类
1.Error(开发人员无需关注)

代表的系统级别错误(属于严重问题),也就是说系统一旦出现问题,Sun公司会把这些问题封装成Error对象给出。

Error并不是开发人员所需要关注的,而是sun公司自己使用的。

2.Exception(开发人员需要关注的)

代表开发人员开发的程序可能出现的问题,通常用Exception以及它的孩子来封装程序出现的问题。

可以分为运行时异常和编译时异常

运行时异常(体系中的RuntimeException)

RuntimeException及其子类,编译阶段不会出现错误提醒,运行时出现的异常。即在程序编写阶段不会报错,不影响程序的启动,只有当程序运行起来了才会报错

如数组越界异常

public class Test1 {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3};
        System.out.println(arr[3]);
    }
}

在编译阶段不会报错,在程序运行后在控制台会出现下面的异常

在这里插入图片描述

编译时异常(体系中的其他异常)

编译阶段就会出现的错误,如果不解决无法编译通过,即无法启动程序

如日期解析异常

在这里插入图片描述

在编译阶段就会报错,如果不处理,编译无法通过,也就无法运行程序

异常的作用
  • 可以用来查询系统Bug的关键信息,因为会在控制台显示
  • 异常可以作为方法内部的一种特殊返回值,将异常抛给调用该方法的上层调用者,以便通知上层调用者底层的执行情况

自定义异常

Java无法为这个世界上全部的问题都提供异常类来代表,如果企业自己的某种问题,想通过异常来表示,
以便用异常来管理该问题,那就需要自己来定义异常类。

自定义异常种类
1.自定义运行时异常RuntimeException
  • 当错误不是很严重时,不需要强烈提醒时,使用自定义运行时异常

  • 该异常编译阶段不报错,运行时出现该异常问题才可能报错

写法

  • 定义一个异常类继承RuntimeException类
  • 重写构造器
  • 通过throw new异常类(Xxx)来创建异常对象并抛出
/*
    1.定义一个异常类继承RuntimeException类,定义运行时异常
*/
public class AgeIllegalRuntimeException extends RuntimeException{
    /*
    2.重写构造器
    */
    public AgeIllegalRuntimeException() {
    }

    //message为自定义的异常信息
    public AgeIllegalRuntimeException(String message) {
        super(message);
    }
}
public class Test1 {
    public static void main(String[] args) {
        //年龄不能为负数,会在运行时报异常,但是不会在编译阶段检查并报错,不强制要求处理该异常
        getAge(-1);
    }

    public static void getAge(int age){
        if (age >= 0){
            System.out.println("年龄为:" + age);
        }else {
            /*
                3.通过throw new异常类(Xxx)来创建异常对象并抛出
            */
            throw new AgeIllegalRuntimeException("年龄不能为负数");//自定义的异常信息
        }
    }
}

控制台的报错信息:

在这里插入图片描述

2.自定义编译时异常Exception
  • 当错误特别严重,需要强烈提醒时,使用自定义编译时异常
  • 编译阶段不处理就会报错

写法

  • 定义一个异常类继承Exception类
  • 重写构造器
  • 通过throw new异常类(Xxx)来创建异常对象并抛出,使调用者可以获得异常信息
  • 在当前方法中还要使用throws抛出异常给调用者,要求调用者必须处理
/*
 * 1.定义一个异常类继承Exception类,定义编译时异常
*/
public class AgeIllegalException extends Exception{
    /*
     * 2.重写构造器
    */

    public AgeIllegalException() {
    }

    public AgeIllegalException(String message) {
        super(message);
    }
}
public class Test2 {
    public static void main(String[] args) {
        //年龄不能为负数,会在编译阶段就报错,如果不处理,无法运行程序
         getAge(-1);//会标红,即编译错误
    }

    /*
        4.在当前方法中还要使用throws抛出异常给调用者,要求调用者必须处理这个异常
    */
    public static void getAge(int age) throws AgeIllegalException {
        if (age >= 0){
            System.out.println("年龄为:" + age);
        }else {
            /*
                3.通过throw new异常类(Xxx)来创建异常对象并抛出,使调用者可以获得异常信息
            */
            throw new AgeIllegalException("年龄不能为负数");//自定义的异常信息
        }
    }
}

会强制要求处理异常,如果不处理编译不通过,见下图

在这里插入图片描述

异常的处理

ctrl + alt + t

异常处理的基础语法
1.抛出异常(throws)

在方法上使用throws关键字抛出异常,将方法内部出现的异常抛给上一层调用者

基础语法

修饰符 返回值 方法名 (参数列表) throws 异常1,异常2 ...{
	方法体
}

建议使用的语法

修饰符 返回值 方法名 (参数列表) throws Exception{//不论多少异常只写一个throws Exception
	方法体
}

因为所有的异常均继承自Exception,所以Exception可以代表一切异常

2.捕获异常

捕获程序出现的异常或者调用的方法抛给当前程序的异常

基础语法

try{
    //可能发生异常的代码
}catch(异常变量1 变量2){
    处理异常1
}catch(异常变量2 变量2){
    处理异常2
}...

建议使用的语法

try{
    //可能发生异常的代码
}catch(Exception e){
    e.printStackTrace();
}//与上文类似,只要Exception e就可以捕获一切异常
常见处理方式
异常处理流程

在这里插入图片描述

异常捕获后的处理方法

经过上述的异常处理流程后,此时已经捕获到了异常,需要处理捕获后的异常。

捕获后的异常的处理方式分为以下两种:

1. 捕获异常后记录异常并响应合适的信息给用户

普通写法

public class Test3 {
    public static void main(String[] args) {
        //这种处理异常捕获异常的方法是记录异常并响应合适的信息给用户
        try {
            B();
        } catch (ParseException e) {
            //如果执行时时间格式与SimpleDateFormat传入的参数不一致,就会执行下面语句
            System.out.println("你解析的时间格式有问题");//响应合适的信息给用户
            e.printStackTrace();//记录异常信息
        } catch (FileNotFoundException e) {
            //如果执行时找不到文件,就会执行下面语句
            System.out.println("你要找的文件不存在");//响应合适的信息给用户
            e.printStackTrace();//记录异常信息
        }
    }

    public static void B() throws ParseException, FileNotFoundException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date parse = sdf.parse("1999-0909");//ParseException也是编译时异常
        C();
    }

    public static void C() throws FileNotFoundException {//抛出异常给方法B
        //读取文件异常是编译时异常
        FileReader fr = new FileReader("d:/a.txt");
    }
}

控制台输出:

在这里插入图片描述

没有出现文件找不到的提示信息是因为代码在B方法中已经发现异常并报错,不会再执行C

如果B中没有出现异常,则会运行C方法,如果C中出现错误,会输出下面的结果

在这里插入图片描述

如果都没有问题,则两个错误信息都不会出现

优化写法

//上面代码中的B和C方法没有变化,不在赘述,仅仅优化处理异常的方法
public class Test3 {
    public static void main(String[] args) {
        try {
            B();
        } catch (Exception e) {//用Exception e代表所有的异常
            System.out.println("你当前错误有问题");//响应合适的信息给用户
            e.printStackTrace();//记录异常信息
        }
    }
}
2. 捕获异常并尝试重新修复

需求:调用一个方法,让用户输入一个合适的价格后返回并输出

未捕获异常修复前:

public class Test4 {
    public static void main(String[] args) {
        System.out.println(getMoney());
    }
    
    public static double getMoney() {
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请输入金额:");
            double money = sc.nextDouble();
            if (money < 0) {
                System.out.println("金额不能为负数");
            }else {
                return money;
            }
        }
    }
}

当输入的金额不是duoble类型时会报以下异常,这是由于sc.nextDouble(),所以可以尝试修复

在这里插入图片描述

修复后:

//只是修改了主函数达到修复的效果,getMoney()方法没有改变,不再赘述
public static void main(String[] args) {
    //使用循环来解决异常
    /*
    捕获异常后,如果发生异常会给用户提示并通过while循环重新执行程序,直到用户输入正确的数据,
    然后通过break跳出循环并终止程序
    */
    while (true) {
        try {
            System.out.println(getMoney());
            break;
        } catch (Exception e) {
            System.out.println("输入的数据有误,请重新输入");
        }
    }
}

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值