Gradle最详细教程,关于Gradle,学习这些就够了.......

目录

Gradle的简介

Groovy语言的学习

Groovy环境变量的配置

第一个Groovy项目

变量的定义

字符串的定义

字符串的常用方法

流程控制

switch

for

闭包

基本技能

闭包的使用场景

闭包中的变量

列表

定义方式

添加和删除

排序和查找

映射map

map的操作

查找

遍历

范围

面向对象

对象的定义

方法的定义调用

接口

Trait

方法的拦截与调用

MetaClass

Groovy对Json的操作

对象与json的转换

Gson处理json

Groovy解析xml

Groovy生成xml

Groovy对文件的操作

Groovy把对象写入文件、读取文件中对象

Gradle的学习和使用

下载和安装

环境变量配置

创建第一个gradle项目

构建脚本介绍

Project

Task

插件

版本冲突的解决方案

多项目构建案例


Gradle的简介

        学习一个东西的时候,要了解其概念,下面先了解一下gradle的相关概念:

        Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建开源工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,也增加了基于Kotlin语言的kotlin-based DSL,抛弃了基于XML的各种繁琐配置,面向Java应用为主。当前其支持的语言C++、Java、GroovyKotlinScala和Swift,计划未来将支持更多的语言。----摘自百度百科

        通过对groovy以及gradle的使用和练习后,自我理解与总结如下:

        1. gradle类似于maven,是一个集项目jar包管理、依赖管理、项目打包等操作为一体的工具。

        2. gradle不同于maven的地方在于,取消maven笨重的xml配置,以独特简便的groovy语言代替大量繁琐的xml配置项。

        3. 拥有自己脚本语言的gradle更加灵活,我们可以在项目构建的时候通过groovy语言,去灵活的创建任务,依据自己的需求对项目进行构建,相比于maven来说,使用groovy进行构建的gradle,扩展性和灵活性更高。

Groovy语言的学习

为什么学习gradle之前需要学习groovy?

        gradle中采用groovy语言作为项目的build脚本,需要我们先大致学习一下groovy,避免在使用gradle构建项目时,看不懂groovy源码,不知道从何下手,比如下面的build.gradle文件,大家可以先试试看是否能看懂和定义呢?

本文章中groovy源码下载地址:

链接:https://pan.baidu.com/s/1MX4_Lk6ynF9waD7XPcG4FQ?pwd=pno3 
提取码:pno3 
--来自百度网盘超级会员V3的分享

Groovy环境变量的配置

1. 第一步,下载Groovy,下载地址:https://groovy.apache.org/download.html

2. 下面是小编整理的版本搭配方案:

Groovy

JVM Required (non-indy)

JVM Required (indy) *

4.0 - current

N/A

1.8+

3.x

1.8+

1.8+

2.5 - 2.6

1.7+

1.7+

2.3 - 2.4

1.6+

1.7+

2.0 - 2.2

1.5+

1.7+

1.6-1.8

1.5+

N/A

1.5

1.4+

N/A

1.0

1.4-1.7

N/A

3. 下载完毕后,将zip包解压到本地目录。

4. 下面我们来配置环境变量,点击此电脑鼠标右键,显示出属性,点击高级系统设置,配置GROOVY_HOME

5. 之后在Path变量中配置bin路径:

6. 验证groovy的环境变量是否配置成功,打开黑窗口,输入groovy -version:

第一个Groovy项目

下面我们来创建第一个grovvy项目:

1. 在idea中点击new-project-Groovy-GroovyLibrary,选择groovy的安装目录,点击下一步。

2. 输入项目名称,点击finish

3. 选择项目,点击鼠标右键,new-groovy script,开始第一个groovy程序:

4. 好了,让我们以一句hello world开启groovy的学习之旅吧:

5. 鼠标右键,run,输出:

变量的定义

        变量可以通过java中的方式定义,也可以使用def进行定义,需要注意的是:变量在groovy中都是对象类型,没有基本数据类型,就算是定义了基本数据类型,也会自动转换为对象类型,变量的定义方式如下:

//强定义
int a=15
double b=12.3
println "a: "+a
println "a class: "+a.class
println "b: "+b
println "b class: "+b.class
//弱定义
def c=15
def d=11.2
def e="Hello World!"
println "c: "+c
println "d: "+d
println "e: "+e
println "c class: "+c.class
println "d class: "+d.class
println "e class: "+e.class
//弱定义可以修改变量的值
c=10.2
println "c: "+c
println "c class: "+c.class

执行结果如下:

字符串的定义

groovy中的字符串有三种定义方式,分别是:

        1. 单引号定义:字符串的格式需要自己控制,比如加转义字符

        2. 双引号定义:可扩展字符串

        3. 三引号定义:直接在字符串中定义 比如换行直接输入回车即可

代码结构如下:

//字符串的定义方式
//单引号定义
def str1='abc'
//双引号定义
def str2="def"
//双引号可以以这种形式被引用
def str4="ghi${str2}"
//三引号定义
//三引号是带格式的定义,比如输入回车就是换行
def str3='''a
b
c
'''
println "str1: "+str1
println "str2: "+str2
println "str4: "+str4
println "str3: "+str3

执行结果如下:

字符串的常用方法

1. groovy可以调用java中的类库,在java中String拥有哪些方法,groovy中也可以使用哪些方法:

def str1="abc"
println "长度:"+str1.length()
println "是否为空:"+str1.isEmpty()
println "下标对应的字符:"+str1.charAt(0)

def str2="d-e-f"
println "两个字符串是否相等:"+str2.equals(str1)
println "从固定位置截取:"+str2.substring(1)
println "从区间截取:"+str2.substring(0,2)
println "替换字符串:"+str2.replace("-","*")

def str3="g-h-i"
println "str3split : "+str3.split("-")

def str4="jkl"
println "转大写: "+str4.toUpperCase()

def str5="MNO"
println "转小写: "+str5.toUpperCase()

def str6=" pqr "
println "去首尾空格: "+str6.trim()

def str7="a"
def str8="b"
println "比较ASCII码: "+str7.compareTo(str8)

执行结果如下:

流程控制

switch

//a可以是任何类型
def a=1.2
switch (a){
    case '123':
        println("123")
        break;
    case [4,7,9]:
        println("456")
        break;
    case 1..20:
        println("1..20")
        break;
    case Integer:
        println("Integer")
        break;
    case  BigDecimal:
        println("BigDecimal")
        break;
    default:
        println("默认方法")
        break;
}

运行结果:

For

//普通for循环
for (int i = 0; i < 3; i++) {
    println "普通for循环: "+i
}

//循环list
def list=[1,2,3]
for (i in list){
    println "list: "+i
}

//循环范围
for (i in 1..10){
    println "循环范围: "+i
}

//循环map
for(i in [1001:"zs",1002:"ls"]){
    println "循环map: "+i.key+"----"+i.value
}

执行结果如下:

闭包

        groovy中的闭包类似于java8中的lambda表达式,也是对方法的简写。

基本技能

        闭包的本质就是一个使用花括号{}包裹起来的代码块。

//定义闭包
def mth1={println "Hello World!"}

//调用闭包
mth1.call()

//在闭包中传入参数
def mth2={str,num->
    println "str: "+str+" num: "+num
}
//调用闭包 传参
mth2.call("hello",123)

//闭包定义之省略参数 省略参数时必须使用it作为变量
def mth3={println "hello ${it}"}
//调用闭包 传参
mth3.call(123)

//闭包中定义返回值
def mth4={return "hello ${it}"}

println "闭包中定义返回值: "+mth4.call(123)

 执行结果如下:

闭包的使用场景

与基本数据类型结合:

//与基本数据类型配合使用闭包
//定义一个从2递增到10递增的范围
2.upto(10){
    println "upto: "+it
}

//使用闭包实现从1加到100
def result=0
1.upto(100){
    return result+=it
}
println "result: "+result

//创建递减的范围
10.downto(1){
    println "downto: "+it
}

//创建一个执行多少次的闭包
3.times {
    println "times: "+it
}

 执行结果如下:

与String结合使用:

//配合String使用闭包
def str1="a1b2c3"

//遍历字符串中的每一个字符 返回值是原来的字符串
def str2=str1.each {
    println "遍历字符串中的每一个字符: "+it
}
println "返回值是原来的字符串"+str2

//查找符合条件的第一个是数字的值
def str3=str1.find{it->it.isNumber()}
println "查找符合条件的第一个是数字的值: "+str3

//查找符合条件的所有的是数字的值
def str4=str1.findAll{it->it.isNumber()}
println "查找所有的是数字的值: "+str4

//判断任意一个值是否满足条件
def str5=str1.any{it->it.isNumber()}
println "判断任意一个值是否满足条件: "+str5

//判断每一个值是否满足条件
def str6=str1.every {it->it.isNumber()}
println "判断每一个值是否满足条件: "+str6

//对值进行收集 收集后返回值是list
def list=str1.collect {
    it->it.isNumber()
}
println "对值进行收集 收集后返回值是list: "+list.toListString()

执行结果如下:

闭包中的变量

This

代表类的实例对象或类本身

Owner

       闭包中嵌套闭包时,指向定义它的闭包对象

Delegate

       大部分时间和owner一样

一般情况下以上三个变量都是指向当前类对象

//输出闭包中的变量
def mth1={
    println "this: "+this
    println "owner: "+owner
    println "delegate: "+delegate
}

mth1.call()

执行结果: 

列表

        groovy中的列表默认为linkedlist,定义方式类似于数组。

定义方式

//定义列表
def list=['1','a','b']
println "list: "+list
println "list: "+list.class

//定义数组
def arr1=[1,2,3] as int[]
int[] arr2=[4,5,6]
println "arr1: "+arr1
println "arr2: "+arr2

执行结果:

添加和删除

//操作列表元素
def list1=['1','a','b','c','d','e']
//添加
list1.add(2)
list1.leftShift(3)
list1<<4
println "添加后的list: "+list1

//移除下标为0的元素
list1.remove(0)
//移除元素是d的
list1.removeElement("e")
//按照条件移除 移除下标是偶数的元素
list.remove{it%2==0}

//获取两个list的差
def list2=['1','2','3','4','5']
def list3=['1','2','3']
def list4=list2-list3
println "获取两个list的差: "+list4

执行结果如下:

  

排序和查找

//排序
def list5=[3,2,5,6,7,4,1,9]
//升序排列
list5.sort()
println "list5.sort: "+list5.toListString()
//按照指定条件排序 降序排序
list5.sort{num1,num2-> num1-num2>0 ? -1:1}
println "list5.sort by rule : "+list5.toListString()
//按照字符串长度排序
def list6=['a','bbb','cc','dddd']
list6.sort{return it.size()}
println "按照字符串长度排序 : "+list6.toListString()

//查找
def list7=[1,2,3,4,5,6]
//查找是偶数的第一个数
println "查找是偶数的第一个数: "+list7.find {it%2==0}
//查找是偶数的所有的数
println "查找是偶数的所有的数: "+list7.findAll {it%2==0}
//查找是偶数的任意数
println "查找是偶数的任意数: "+list7.any {it%2==0}
//判断列表是否全部是偶数
println "判断列表是否全部是偶数: "+list7.every{it%2==0}
//最大值、最小值
println "最大值: "+list7.max()
println "最小值: "+list7.min()
//统计符合条件的偶素有多少个
println "符合条件的值有几个: "+list7.count{it%2==0}

执行结果:

映射map

map的定义和添加

//弱定义map
def map1=[1001:"zs",1002:"ls",1003:"ww",1004:"zl"]
//定义时可省略引号 前提是字符串是key
def map4=[zs:1001,ls:1002,ww:1003,zl:1004]
println "定义map: "+ map1.toMapString()
println "字符串为key时,定义时可省略引号map4: "+ map4.toMapString()

//将map强指引为其他类型的map
HashMap map2=[1001:"zs",1002:"ls",1003:"ww",1004:"zl"]
Hashtable map3=[1001:"zs",1002:"ls",1003:"ww",1004:"zl"]

//添加元素
map1[1005]="迪迦"
println "添加迪迦后: "+ map1.toMapString()

//在map中新增map
map1.'newMap'=[1006:"泰罗",1007:"佐菲"]

执行结果如下:

查找

新建groovy类student:

class Student {
    Integer score
    String sex
}

在map中新增student对象,并进行查找:

//查找
def map5=[
        "张三":new Student(score:70,sex:'男'),
        "莉莉":new Student(score:60,sex:'女'),
        "小红":new Student(score:59,sex:'女'),
        "老王":new Student(score:34,sex:'男')]

//查找map中及格的第一个人
println "查找map中及格的第一个人: "+map5.find {it.value.score>=60}
//查找map中及格的所有的人
println "找map中及格的所有的人: "+map5.findAll {it.value.score>=60}
//查找map中及格的所有的男生
println "找map中及格的所有的人: "+map5.findAll {it.value.score>=60&&it.value.sex=='男'}
//查找map中及格的所有的男生的名字
println "找map中及格的所有的人: "+map5.findAll {it.value.score>=60&&it.value.sex=='男'}.collect {it.key}
//将及格和不及格的分组
println "将及格和不及格的分组: "+map5.groupBy {it.value.score>=60 ? '及格' : '不及格'}

 执行结果:

遍历

//遍历
def map6=[
        "张三":new Student(score:70,sex:'男'),
        "莉莉":new Student(score:60,sex:'女'),
        "小红":new Student(score:59,sex:'女'),
        "老王":new Student(score:34,sex:'男')]
//方式1
map6.each {println it.key+"*******"+it.value}
//方式2
map6.each {key,value->println key+"*******"+value}
//方式3
map6.eachWithIndex{key,value,index->println key+"*******"+value+"*******"+index}

执行结果:

范围

//定义一个从2到5的范围
def fw1=2..5
println "定义一个从2到5的范围: "+fw1.size()

//定义一个从2到5 不包含5的范围
def fw2=2..<5
println "定义一个从2到5 不包含5的范围: "+fw2.size()

//操作
//获取范围中下标为0的元素
println "获取范围中下标为0的元素: "+fw1[0]
//判断范围是否包含3
println "判断范围是否包含3: "+fw1.contains(3)
//获取范围开始值
println "获取范围开始值: "+fw1.from
//获取范围结束值
println "获取范围结束值: "+fw1.to

执行结果:

面向对象

对象的定义

//创建空构造器的对象
def person1=new Person()

//创建构造器对象
def person2=new Person("张三","男",30)

//指定参数 创建对象
def person3=new Person(name:"张三",sex:"男",age:30)

//声明式
def person4=["王五",20] as Person
Person person5=["王五",20]

//属性取值
println "name:${person3.name} sex:${person3.sex} age:${person3.age}"
println "name:${person3.getName()} sex:${person3.getSex()} age:${person3.getAge()}"

方法的定义调用

//方法的定义和调用
def mth1={
    println "mth1"
}
mth1()
def mth2(p1,p2){
    println "mth2 p1: "+p1+" p2: "+p2
}
mth2("123","456")

执行结果:

接口

        接口的用法和java一致,不再赘述。


interface Car {

    run()
}

Trait

         类似于java中的抽象类,可以定义抽象方法,也可以定义非抽象方法(不含方法体)

trait CarTrait {

    run(){

    }

    abstract stop()
}

MetaClass

类似于java中的反射机制,通过此方法可以动态的为groovy类添加属性和增加方法:

//动态为类添加没有的属性
Person.metaClass.email="xxx123@qq.com"
def p1=new Person("丽丽","女",28)
println "动态为类添加没有的属性: "+p1.email
//修改属性
p1.email="xxx666@qq.com"
println "动态修改属性: "+p1.email

//动态为类添加方法 将名字大写
Person.metaClass.setNameUpCase={->name.toUpperCase()}
def p2=new Person("lili","女",28)
println "动态为类添加方法 将名字大写: "+p2.setNameUpCase()

//添加静态方法 将名字小写
Person.metaClass.static.setNameLowerCase={String name->name.toLowerCase()}
println "添加静态方法 将名字小写: "+Person.setNameLowerCase("LILI")

执行结果:

Groovy对Json的操作

对象与json之间的转换

import groovy.json.JsonOutput
import groovy.json.JsonSlurper

//将对象转换为json
def p1=new Person("lili","女",28)
//对象转json
def json = JsonOutput.toJson(p1)
println "将对象转换为json: "+json

//将对象列表转换为json
def list=[new Person("lili1","女",28),
          new Person("lili2","女",28),
          new Person("lili3","女",28),
          new Person("lili4","女",28)]
def json1 = JsonOutput.toJson(list)
println "将对象列表转换为json: "+json1

//将json转换为对象
def slurper = new JsonSlurper()
Person text = slurper.parseText(json)
println "将json转换为对象: "+text.class

//将json转换为list
def slurper1 = new JsonSlurper()
def list1 = slurper1.parseText(json1)
println "将json转换为list: "+list1.class

执行结果:

Gson处理json

import com.google.gson.Gson

def p1=new Person("张三","男",30)
//利用Gson转换为json 需要将Gson的jar包导入到项目中
def gson=new Gson()
def json = gson.toJson(p1)
println "对象利用Gson转换为json: "+json

//将list转换为json
def list=[new Person("张三1","男",30),
          new Person("张三2","男",30),
          new Person("张三3","男",30)]
def ljson = gson.toJson(list)
println "list利用Gson转换为json: "+ljson

//json转对象
def json1 = gson.fromJson(json, Person.class)
println "json转对象: "+json1

//json转list
def json2 = gson.fromJson(ljson, ArrayList.class)
println "json转list: "+json2
println "json转list: "+json2.class

执行结果:

Groovy解析xml

package com.groovy.test01

import groovy.xml.XmlSlurper
import groovy.xml.slurpersupport.GPathResult

final String xml='''
<students>
    <student id="1">
        <name>张三</name>
        <age>28</age>
        <sex>男</sex>
        <score>65</score>
    </student>
    <student id="2">
        <name>李四</name>
        <age>32</age>
        <sex>女</sex>
        <score>70</score>
    </student>
    <student id="3">
        <name>王五</name>
        <age>40</age>
        <sex>男</sex>
        <score>70</score>
    </student>
</students>
'''
//解析xml
def sp=new XmlSlurper()
def text = sp.parseText(xml)
//获取标签内的值
println "解析后的学生姓名: "+text.student[0].name.text()
//获取标签的属性
println "标签的属性: "+text.student[0].@id
//遍历获取的xml值
def list=[]
text.student.each{
    it->list.add(it.name.text()+"--"+it.age.text())
}
println "list的值是: "+list.toListString()

执行结果:

Groovy生成xml

import groovy.xml.MarkupBuilder

//生成xml的核心类
def writer = new StringWriter()
def builder = new MarkupBuilder(writer)

//创建根节点
builder.students(){
    //创建子节点 小括号里面填写属性
    student(id:'1'){
        //创建student中的子标签
        name('张三')
        sex('男')
        age(30)
    }
    //创建子节点 小括号里面填写属性
    student(id:'2'){
        //创建student中的子标签
        name('李四')
        sex('男')
        age(31)
    }
}
println "创建后的xml: "+writer

运行结果如下:

Groovy对文件的操作

//读取文件
def file = new File("f://student.xml")
//遍历文件中的每一行
file.eachLine {println "遍历文件中的每一行: "+it}
//读取文件中的所有的内容
def text = file.getText()
println "读取文件中的所有的内容: "+text
//获取所有的行 返回list
def lines = file.readLines()
println "获取所有的行 返回list: "+lines

//将文件复制到另一个文件中
def copy(String oldPath,String newPath){
    //确定目标文件
    def file = new File(newPath)
    if(!file.exists()){
        file.createNewFile()
    }
    //复制
    new File(oldPath).withReader{
        def lines = it.readLines()
        file.withWriter {
            lines.each {
                line->it.append(line+"\r\n")
            }
        }
    }
    return true
}
copy("f:\\student.xml","f:\\student2.xml")

执行结果:

Groovy把对象写入文件、读取文件中对象

创建Person对象:

class Person  implements Serializable{
    String name
    String sex
    Integer age

    Person() {
    }

    Person(String name, String sex, Integer age) {
        this.name = name
        this.sex = sex
        this.age = age
    }
}

使用IO流操作对象:

//将对象保存为文件
def saveObject(Object obj,String path){
    //将文件封装为对象
    def file=new File(path)
    if(!file.exists()){
        file.createNewFile()
    }
    file.withObjectOutputStream {
        it.writeObject(obj)
    }
    return true
}
//将文件读取为对象
def readObj(String path){
    def obj=null
    def file=new File(path)
    if(!file.exists()){
        return null
    }
    file.withObjectInputStream {
        obj=it.readObject()
    }
    return obj
}
//创建对象
def person = new Person()
//将对象写入文件
println saveObject(person,"f:\\person.txt")
//将文件读取为对象
println readObj("f:\\person.txt")

执行结果如下:

Gradle的学习和使用

下载和安装

这里推荐gradle-6.7-rc-1版本 

下载地址:https://services.gradle.org/distributions

环境变量配置

在环境变量中配置:GRADEL_HOME

配置path:

 验证gradle是否安装成功:

创建第一个gradle项目

1. 打开idea,new一个新项目

2.  输入包名称、项目名称、版本

 3. 点击finish

4. 项目信息介绍

构建脚本介绍

Project

        一个project代表一个正在构建的组件(jar/war),当开始构建时,gradle会基于build.gradle实例化出一个Project对象,并通过project来调用其成员。

project中的属性:

Task

        Gradle中内置了一些任务,比如build、clean等,也可以自定义任务,语法格式如下:

定义task的常用方法:

        可以在build.gradle中定义task

task task1,{
    println "task1"
    doFirst {

    }
    doLast{
        
    }
}

定义完毕后在右侧可以看到任务名称,点击后可以执行任务:

 这里的任务默认出现在other下,如果我们需要定义任务分组的话按照如下形式定义:

task task1,{
    println "task1"
    group("MyTask")
    doFirst {

    }
    doLast{

    }
}

定义多个任务

tasks{
    task task2{
        group("MyTask")
        println "task2"
    }
    task task3{
        group("MyTask")
        println "task3"
    }
}

任务依赖

使用dependsOn关键字实现任务依赖:

task task1,{
    println "task1"
    group("MyTask")
    doFirst {

    }
}

task task4,{
    println "task1"
    group("MyTask")
    dependsOn 'task1'
    doFirst {

    }
}

task task5(dependsOn:task4){
    println "task1"
    group("MyTask")
    dependsOn 'task1'
    doFirst {

    }
}

 任务的生命周期

        Gradle的生命周期分三个阶段:初始化阶段、配置阶段、执行阶段。

初始化阶段

        通过settings.gradlle判断有哪些项目需要初始化,加载所有需要初始化的项目的build.gradle文件 并为每个项目创建project对象

配置阶段

        执行各项目下的build.gradle脚本,完成project的配置,并且构造Task任务依赖关系图以便在执行 阶段按照依赖关系执行Task中的配置代码

执行阶段

        通过配置阶段的Task图,按II贿执行需要执行的任务中的动作代码,就廠行任务中写在doFirstdoLast中的代码。

新建插件,把一个项目打成jar包,发布到maven仓库给另一个项目使用

1. 新建gradle java项目。

2. 在新的gradle java项目的build.gradle文件中配置如下信息:

    id 'java-library'//配置此项表示项目可以打包为jar给其他项目使用
    id 'maven-publish'//将打包的项目发布到maven仓库中

 3. 在新的gradle java项目的build.gradle文件中新增插件,将打包好的项目发布到maven本地仓库中:

//发布插件 将打包的项目发布到maven本地仓库
publishing{
    //配置发布动作
    publications{
        maven(MavenPublication){
            from components.java
        }
    }
    //配置发布到本地maven库中
    repositories {
        mavenLocal()
    }
}

 4. 先在项目中建一个类,然后执行build,build完毕后点击publish,此时jar包就打包到本地的maven仓库中了。

5. 打开本地仓库,查看maven-metadata-local.xml,有jar包相关的信息,在另一个项目中直接进行引用即可使用此jar包。

6. 在另一个项目中进行引用,build.gradle文件中的配置如下:

//引入另一个jar包
    compile group: 'com.test', name: 'JarGradle', version: '1.0-SNAPSHOT'
mavenLocal()//先从maven本地仓库中找

 7. 引用一下jar测试完成

版本冲突的解决方案

(1)依赖传递性:

        假设你的项目依赖于一个库,而这个库又依赖于其他库。你不必自己去找出所有这些依赖,你只需 要加上你直接依赖的库,Gradle会隐式的把这些库间接依赖的库也加入到你的项目中。

(2)传递性做中版本冲突:

        由于传递性依赖的特点,两个不同版本的jg会被依赖进来,这样就存在版本冲突的问题。

多项目构建案例

通过案例完成多项目构建:

 

  • 25
    点赞
  • 129
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

只为code醉

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

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

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

打赏作者

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

抵扣说明:

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

余额充值