大数据之java基础增强

一、集合框架

(一)集合框架五大类体系:

    1.Collection(常用的List和Set,和不常用的Queue和Vector和Stack),单元素集合
    2.Map(常用的HashMap和TreeMap,不常用的HashTable),Key-Value映射
    3.Iterator(迭代器)
    4.工具类(Collections和Arrays)
    5.Comparable和Comparator比较器

(二)集合和数组的区别:

    1.数组长度在初始化时指定,意味着只能保存定长的数据。而集合可以保存数量不确定的数据,
        同时可以保存具有映射关系的数据(键值对key-value);
    2.数组元素既可以是基本类型的值,也可以是对象。而集合里只能保存对象(实际上是保存对象的引用变量),
        基本类型的变量必须包装成相应的类才能放入集合中。

(三)List:

    1.List特点:元素有放入顺序,元素可重复
    2.List接口有三个实现类:ArrayList,LinkedList,vector
    3.LinkedList:底层基于链表实现,链表内存是散列的,每一个元素存储本身内存地址的同时还要存储下一个元素的地址。链表增删快,查询慢。
    4.ArrayList和Vector都是基于数组实现的。数组查询快,增删慢。
        区别是ArrayList是线程非安全的,效率高;Vector是线程安全的,效率低。
    5.ArrayList的初始容量大小为10,扩容策略是1.5倍原元素数量的大小

(四)Set:

    1.Set特点:元素无放入顺序,元素不可重复
    2.Set接口有三个实现类:HashSet,TreeSet,LinkedHashSet
    3.HashSet:底层由HashMap实现,底层通过equals()和hashcode()去重
    4.HashSet判断两个元素的标准:两个对象通过equals()方法比较相等,并且两个对象的hashcode()方法返回值也相等
    5.LinkedHashSet:是HashSet的一个子类,在插入元素的时候,同时使用链表维持插入元素的顺序
    6.SortedSet接口有一个实现类:TreeSet(底层是由平衡二叉树实现)确保集合中的元素处于有序状态
    7.LinkedHashSet和SortedSet区别:前者存储插入顺序,后者存储插入之后的顺序

(五)Map:

    1.Map特点:存储键值对Key-Value
    2.Map接口有五个实现类:HashMap,TreeMap,LinkedHashMap,HashTable,ConcurrentHashMap
    3.HashMap:线程非安全,高效,支持null的key和value,底层由数组和链表实现,通过hashcode和equals保证键的唯一性
        HashMap的初始化大小为16,扩展因子为0.75,扩容策略是2倍原容量大小
    4.HashTable:线程安全,低效,不支持null的key和value
    5.ConcurrentHashMap:是jdk1.5之后HashTable的一个替代实现,采用分断锁机制,底层采用数组+链表+红黑树的数据结果
    6.SortedMap有一个实现类:TreeMap会存储放入元素的顺序
    7.TreeMap:基于红黑树数据结构的实现

二、反射

(一)Java的反射机制主要提供了以下:

    1.在运行时判断任意一个对象所属的类Class
    2.在运行时判断构造任意一个类的对象Construtor
    3.在运行时判断任意一个类所具有的成员变量Field和方法Method
    4.在运行时调用任意一个对象的方法。method.invoke(object,args)

(二)Class类:

        Java中的类用于描述一类事物的共性,该类的属性是由该类的实例对象来决定的,不同的实例对象具备不同的属性。
    Java程序中的各个不同类型的Java类也属于同一类事物,而Class类就是用来描述这类事物的。Class类描述了类的属性信息,
    如类名,权限,包名,字段名称列表,方法名称列表。
        获取Class对象的三种方式:
    1.Class.forName("类名字符串")(类名字符串必须是全称:包名+类名)
    2.类名.class
    3.对象名.getClass()

三、设计模式

(一)概念:软件开发人员在软件开发过程中面临的一般问题的解决方案

(二)总原则:开闭原则(对扩展开放,对修改关闭)

(三)六大原则:

    1.单一职责原则:每个类应该实现单一的职责
    2.里氏替换原则:子类对父类的方法尽量不要重写和重载
    3.依赖倒转原则:面向接口编程,依赖于抽象而不依赖于具体
    4.接口隔离原则:每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分
    5.迪米特法则(最少知道原则):一个类对自己依赖的类知道的越少越好
    6.合成复用原则:尽量首先使用合成/聚合的方式,而不是使用继承

(四)分类:

    1.创新型模式:工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式
    2.结构型模式:适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式
    3.行为型模式:策略模式,模板方法模式,观察者模式,迭代子模式,责任链模式,命令模式,
                备忘录模式,状态模式,访问者模式,中介者模式,解释器模式

(五)单例模式:

    1.单例类只能有一个实例;
    2.单例类必须自己创建自己的唯一实例;
    3.单例类必须给所有其他对象提供这一实例。
    重点:双检锁/双重校验锁

(六)装饰器模式:

        允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,
    它是作为现有的类的一个包装。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,
    提供了额外的功能。

(七)代理模式:

    1.静态代理:代理类和委托类在代码运行前关系就确定了,也就是说在代理类的代码一开始就已经存在了
    2.动态代理:动态代理类的字节码在程序运行时的时候生成,根据传进来的业务实现类和方法名进行具体调用。
        第一种:JDK动态代理实现
            JDK动态代理实现所用的代理类在程序调用到代理类对象时才由JVM真正创建,JVM根据传进来的业务实现类对象以及方法名,
        动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。我们需要做的,
        只需指定代理类的预处理、调用后操作即可。
        第二种:CGUB动态代理实现:
            CGLIB是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,
        所以不能对final修饰的类进行代理,final的方法也不能。
        两种最重要区别在于JDK的动态代理的委托类,必须要实现一个接口
    3.静态代理和动态代理的区别:
        静态代理:自己编写创建代理类,然后再进行编译,在程序运行前,代理类的.class文件就已经存在了
        动态代理:在实现阶段不用关心代理谁,而在运行阶段(通过反射机制)才能指定代理哪一个对象。

四、并发编程

(一)进程:

        它是内存中的一段独立的空间,可以负责当前应用程序的运行。当前这个进程负责调度当前程序中所有运行细节。
        我们把每个独立应用程序在内存的独立空间称为当前应用程序运行的一个进程。

(二)线程:

        它是位于进程中,负责当前进程的某个具备独立运行资格的空间。
        在一个进程中,每个独立的功能都需要独立的去运行,这时又需要把当前这个进程划分成多个运行区域,每个独立的小区域称为一个线程。
        进程是负责整个程序的运行,而线程是程序中具体的某个独立功能的运行。
        一个进程中至少应该有一个线程,可以并发运行多个进程。

(三)多线程运行的原理:
        CPU在线程中做时间片的切换

(四)实现线程的两种方式:

    1.继承 Thread 的原理
    2.声明实现 Runnable 接口的类
    3.Thread 和 Runnable 的区别
        实现 Runnable 接口比继承 Thread 类所具有的优势:
        (1)适合多个相同的程序代码的线程去处理同一个资源
        (2)可以避免 java 中的单继承的限制
        (3)增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
        (4)线程池只能放入实现 Runable 或 callable 类线程,不能直接放入继承 Thread 的类

(五)线程的五种状态

    1.新建状态(New):新创建了一个线程对象
    2.就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行池中,
                        变得可运行,等待获取CPU的使用权。
    3.运行状态(Running):就绪状态的线程获取了CPU,执行程序代码
    4.阻塞状态(Blocked):线程因为某种原因放弃了CPU的使用权,暂时停止运行。直到线程进入就绪状态,才能转到运行状态。
        阻塞的情况分三种:
        (1)等待阻塞:
            运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)                                
        (2)同步阻塞:
            运行的线程在获取对象的同步锁时,若该同步锁被其他的线程占用,JVM会把该线程放入锁池中
        (3)其他阻塞:
            运行的线程执行sleep()或join()方法,或者发出来I/O请求时,JVM会把该线程设置为阻塞状态。
        当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(sleep是不会释放持有的锁)
    5.死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期

(六)几个重要方法的区别:

    1.sleep(timeout):当前线程进入阻塞状态,暂停执行一段时间,不会释放锁标记
    2.join():join()方法会使当前线程等待调用join()方法的线程结束后才能继续执行
    3.yield():调用该方法的线程重回可执行状态,不会释放锁标记,可以理解为交出CPU时间片,但是不一定有效果,
                因为有可能马上又被马上执行。该方法的真正作用是使具有相同或者更高优先级的方法得到执行机会。
    4.wait(timeout):wait方法通常和notify()/notifyAll()搭配使用,当前线程暂停执行,会释放锁标记。进入对象等待池。
                    直到调用notify()方法之后,线程被移动到锁标记等待池。只有锁标记等待池的线程才能获得锁。

(七)同步关键字解释

    1.synchronized:
        (1)属于JVM级别加锁,底层实现是:在编译过程中,在指令级别加入一些标识来实现的。
        (2)在使用synchronized时,用到的方法是wait和notify(或notifyAll)
        (3)synchronized无法中断正在阻塞队列或者等待队列的线程
        (4)synchronized提供了已下几种类型的锁:偏向锁、轻量级锁、重量级锁
        获取锁的线程释放释放锁只会有两种情况:
            (1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;
            (2)线程执行发生异常,此时JVM会让线程自动释放锁。
    2.lock:
            Lock锁是Java代码级别来实现的,相对于 synchronized 在功能性上,有所加强,主要是,公平锁,轮询锁,定时锁,
        可中断锁等,还增加了多路通知机制(Condition),可以用一个锁来管理多个同步块。另外在使用的时候,
        必须手动的释放锁。Lock 锁的实现,主要是借助于队列同步器(我们常常见到的 AQS)来实现。它包括一个 int 变量来表示状态;
        一个 FIFO 队列,来存储获取资源的排队线程。
            四个获取锁方法的区别:
        (1)lock(),阻塞方法,用来获取锁;unlock(),释放锁;
        (2)tryLock(),非阻塞方法,用来尝试获取锁,有返回值,立即返回,不会等待;
        (3)tryLock(long time,TimeUnit unit),阻塞方法,阻塞给定时长,会等待一段时间;
        (4)lockInterruptibly(),中断线程的等待状态;当一个线程获得了锁之后,不会被中断,只能中断等待状态。
        ReentrantLock(可重入锁)类:唯一实现了Lock接口的类
        ReadWriteLock类:接口
        ReentrantReadWriteLock类:
            两个重要方法:readLock()和 writeLock()用来获取读锁和写锁
        注意:如果有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁。
            如果有一个线程已经占用了写锁,则此时其他线程如果申请写锁或者读锁,则申请的线程会一直等待释放写锁
    3.lock 和 synchronized 的区别:
        (1)Lock 是一个接口,而 synchronized 是 Java 中的关键字,synchronized 是内置的语言实现;
        (2)synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而 Lock 在发生异常时,
            如果没有主动通过 unLock() 去释放锁,则很可能造成死锁现象,因此使用 Lock 时需要在 finally 块中释放锁;
        (3)Lock 可以让等待锁的线程响应中断,而 synchronized 却不行,使用synchronized时,等待的线程会一直等待下去,
            不能够响应中断;
        (4)通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。
        (5)Lock 可以提高多个线程进行读操作的效率。
    4.死锁发生的四个条件:
        (1)互斥条件:一个资源每次只能被一个进程使用。
        (2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
        (3)不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
        (4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
            避免死锁最简单的方法就是阻止循环等待条件,将系统中所有的资源设置标志位、排序,
        规定所有的进程申请资源必须以一定的顺序(升序或降序)做操作来避免死锁。

五、数据结构
(一)概述
(二)线性表
(三)数组Array
(四)链表Link
(五)散列表/Hash表
(六)栈Stack
(七)队列Queue
(八)树Tree
(九)堆Heap
(十)图Graph
(十一)森林Forest

六、算法基础

(一)算法概述

    1.概念

        算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法
    描述解决问题的策略机制。也就是说,能够对一定规范的输入,在有限时间内获得所要求规范的输出。
        一个算法的优劣可以用空间复杂度与时间复杂度来衡量。

    2.算法特征

        (1)有穷性(Finiteness):算法的有穷性是指算法必须能在执行有限个步骤之后终止;
        (2)确切性(Definiteness):算法的每一步骤必须有确切的定义;
        (3)输入项(Input):一个算法有 0 个或多个输入,以刻画运算对象的初始情况,所谓 0 个输入是指算法本身定出了初始条件;
        (4)输出项(Output):一个算法有一个或多个输出,以反映对输入数据加工后的结果。没有输出的算法是毫无意义的;
        (5)可行性(Effectiveness):算法中执行的任何计算步骤都是可以被分解为基本的可执行的操作步,
            即每个计算步都可以在有限时间内完成(也称之为有效性)。

(二)算法复杂度分析

    1.为什么要进行算法分析

        (1)预测算法所需要的资源
            计算时间(CPU 消耗)
            内存空间(RAM 消耗)
            通信时间(带宽消耗)
        (2)预测算法所需要的运行时间
            在给定输入规模时,所执行的基本操作数量。或者称为算法复杂(Algorithm Complexity)

    2.如何衡量算法的复杂度

        内存(Memory)
        时间(Time)
        指令的数量(Number of Steps)
        特定操作的数量
        磁盘访问数量
        网络包数量
        渐进复杂度(Asymptotic Complexity)

    3.算法的运行时间与什么有关

        取决于输入的数据形态。(例如:如果数据已经是排好序的,时间消耗可能会减少。)
        取决于输入数据的规模。(例如:6 和 6 * 109)
        取决于运行时间的上限。(因为运行时间的上限是对使用者的承诺。)

    4.算法分析

        算法分析的目的在于选择合适算法和改进算法。一个算法的评价主要从时间复杂度和空间复杂度来考虑。
        算法的空间复杂度是指算法需要消耗的内存空间。其计算和表示方法与时间复杂度类似,一般都用复杂度的渐近性来表示。
        算法的时间复杂度是指执行算法所需要的计算工作量。
        问题的规模 n 越大,算法执行的时间的增长率与 f(n)的增长率正相关,称作渐进时间复杂度(Asymptotic TimeComplexity)
        (1)最坏情况(Worst Case):任意输入规模的最大运行时间。(Usually)
        (2)平均情况(Average Case):任意输入规模的期待运行时间。(Sometimes)
        (3)最佳情况(Best Case):通常最佳情况不会出现。(Bogus)
        实际中,我们一般仅考量算法在最坏情况下的运行情况,也就是对于规模为 n 的任何输入,算法的最长运行时间。
        这样做的理由是:
        1、一个算法的最坏情况运行时间是在任何输入下运行时间的一个上界(Upper Bound)
        2、对于某些算法,最坏情况出现的较为频繁。
        3、大体上看,平均情况通常与最坏情况一样差。
        算法分析要保持大局观(Big Idea),其基本思路:
        1、忽略掉那些依赖于机器的常量。
        2、关注运行时间的增长趋势。

    5.算法的描述方式           

        自然语言、结构化流程图、伪代码和 PAD 图

(三)算法分类

    1.递推算法

        递推是按照一定的规律来计算序列中的每个项,通常是通过计算机前面的一些项来得出序列中的指定项的值。
        递推算法分为顺推和逆推两种。
        所谓顺推法是从已知条件出发,逐步推算出要解决的问题的方法叫顺推。
        所谓逆推法从已知问题的结果出发,用迭代表达式逐步推算出问题的开始的条件,即顺推法的逆过程,称为逆推。
        相对于递归算法,递推算法免除了数据进出栈的过程,也就是说,不需要函数不断的向边界值靠拢,而直接从边界出发,直到求出函数值。
        应用场景:
        顺推:斐波拉契数列
        逆推:整存零取

    2.递归算法

        递归算法(英语:Recursion Algorithm)在计算机科学中是指一种通过重复将问题分解为同类的子问题而解决问题的方法。
        程序调用自身的编程技巧称为递归(recursion)。
        当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
        注意:
        1、递归就是在过程或函数里调用自身;
        2、在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口。
        应用场景:
        1、数据的定义是按递归定义的。如 Fibonacci(斐波拉契数列)函数和阶乘函数。
        2、问题解法按递归算法实现。如 Hanoi(汉诺塔)问题。
        3、数据的结构形式是按递归定义的。如二叉树、广义表等。

    3.穷举算法

        穷举法,或称为暴力破解法,其基本思路是:对于要解决的问题,列举出它的所有可能的情况,
    逐个判断有哪些是符合问题所要求的条件,从而得到问题的解。
        1、顺序列举
            是指答案范围内的各种情况很容易与自然数对应甚至就是自然数,可以按自然数的变化顺序去列举。
        2、排列列举
            有时答案的数据形式是一组数的排列,列举出所有答案所在范围内的排列,为排列列举。
        3、组合列举
            当答案的数据形式为一些元素的组合时,往往需要用组合列举。组合是无序的。
        应用场景:
        1、百钱买百鸡问题
        2、硬币兑换
        3、密码破译
        4、拨钟问题
        5、求 2-1000 内的所有素数

    4.贪心算法

        贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,
    他所做出的是在某种意义上的局部最优解。
        能够得到某种量度意义下最优解的分级处理方法称为贪婪算法。
        通过每一步贪心选择,可得到问题的一个最优解,虽然每一步上都要保证能获得局部最优解,
    但由此产生的全局解有时不一定是最优的,所以贪婪法不要回溯。
        贪心算法保证了局部最优,但并不能保证得到最优解。
        贪心策略适用的前提是:局部最优策略能导致产生全局最优解。
        应用场景:
        1、背包问题
        2、钱币找零问题

    5.分治算法

        分治法是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子题……
    直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并
        分治法所能解决的问题一般具有以下几个特征:
            1、该问题的规模缩小到一定的程度就可以容易地解决;
            2、该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质;
            3、利用该问题分解出的子问题的解可以合并为该问题的解;
        分治法的设计思想是:将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
        分治策略是:对于一个规模为 n 的问题,若该问题可以容易地解决(比如说规模 n 较小)则直接解决,
        否则将其分解为 k 个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,
        然后将各子问题的解合并得到原问题的解。
        应用场景:
        1、原问题可以分解为多个子问题
        2、原问题在分解过程中,递归地求解子问题
        3、在求解并得到各个子问题的解后应能够采用某种方式、方法合并或构造出原问题的解。

    6.动态规划算法

        动态规划是一种在数学和计算机科学中使用的,用于求解包含重叠子问题的最优化问题的方法。其基本思想是,
    将原问题分解为相似的子问题,在求解的过程中通过子问题的解求出原问题的解。
        动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,
    所以,这种多阶段最优化决策解决问题的过程就称为动态规划。
        与分治法最大的差别是:适合于用动态规划法求解的问题,经分解后得到的子问题往往不是互相独立的
    (即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解)。而分治法中,切分出来的多个子问题都是相互独立的。
        适用条件:
        1、最优化原理(最优子结构性质)
        2、无后效性
        3、子问题的重叠性
        分类:线性动规,区域动规,树形动规,背包动规
        解题思路:
        1、将原问题分解为子问题
        2、确定状态和状态变量
        3、确定一些初始状态(边界状态)的值
        4、确定状态转移方程

    7.迭代算法

        迭代法也称辗转法,是一种不断用变量的旧值递推新值的过程,跟迭代法相对应的是直接法(或者称为一次解法),
    即一次性解决问题。迭代法又分为精确迭代和近似迭代。
        算法思想:
        1、确定迭代变量
        2、建立迭代关系式
        3、对迭代过程进行控制

    8.分支限界算法

        分支定界法的基本思想是对有约束条件的最优化问题的所有可行解(数目有限)空间进行搜索。该算法在具体执行时,
    把全部可行的解空间不断分割为越来越小的子集(称为分支),并为每个子集内的解的值计算一个下界或上界(称为定界)。
    在每次分支后,对凡是界限超出已知可行解值那些子集不再做进一步分支,这样,解的许多子集(即搜索树上的许多结点),
    因此这种算法一般可以求得最优解。
        回溯法和分支限界法的一些区别:
        1、 方法对解空间树的搜索方式
        2、 存储结点的常用数据结构
        3、 结点存储特性常用应用

    9.回溯算法

        回溯法(探索与回溯法)是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,
    发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,
    而满足回溯条件的某个状态的点称为“回溯点”。
        解题步骤:
        1、针对所给问题,确定问题的解空间:
        首先应明确定义问题的解空间,问题的解空间应至少包含问题的一个(最优)解。
        2、确定结点的扩展搜索规则
        3、以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。

    10.排序算法

        排序是计算机内经常进行的一种操作,其目的是将一组“无序”的记录序列调整为“有序”的记录序列。分内部排序和外部排序
        若整个排序过程不需要访问外存便能完成,则称此类排序问题为内部排序。
        内部排序的过程是一个逐步扩大记录的有序序列长度的过程。
        若参加排序的记录数量很大,整个序列的排序过程不可能在内存中完成,则称此类排序问题为外部排序。
        1、冒泡排序
        2、鸡尾酒排序
        3、快速排序
        4、直接插入排序
        5、二分插入排序
        6、希尔排序
        7、简单选择排序
        8、堆排序
        9、归并排序
        10、基数排序
        11、计数排序
        12、桶排序
        13、梳排序
        14、奇偶排序

    11.查找算法

        1、顺序查找
            顺序查找是在一个已知无(或有序)序队列中找出与给定关键字相同的数的具体位置。
            原理是让关键字与队列中的数从最后一个开始逐个比较,直到找出与给定关键字相同的数为止,它的缺点是效率低下。
        2、折半查找
            算法要求:
            1、必须采用顺序存储结构。
            2、必须按关键字大小有序排列。
            优点是比较次数少,查找速度快,平均性能好;
            缺点是要求待查表为有序表,且插入删除困难。
            因此,折半查找方法适用于不经常变动而查找频繁的有序列表。
        3、分块查找
            分块查找是折半查找和顺序查找的一种改进方法,分块查找由于只要求索引表是有序的,对块内节点没有排序要求,
        因此特别适合于节点动态变化的情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值