Groovy语法

1.注释

1.1 单行注释

单行注释开头为//,可以在行中的任何位置。从//开始,直到该行的末端,被视为注释的一部分。

// a standalone single line comment
println "hello" // a comment till the end of the line

1.2 多行注释

多行注释以行开头/*,可以在行中的任何位置找到。以下字符/*将被视为注释的一部分,包括换行符,直到第一个*/关闭注释。多行注释因此可以放在语句的末尾,甚至在语句内。

/* a standalone multiline comment
   spanning two lines */
println "hello" /* a multiline comment starting
                   at the end of a statement */
println 1 /* one */ + 2 /* two */

1.3 GroovyDoc注释

与多行注释类似,GroovyDoc注释是多行的,但以开头/**和结尾*/。第一个GroovyDoc注释行后面的行可以选择以星号开头*。这种注释一般用于:

  • 类型定义(类,接口,枚举,注释)

  • 字段和属性定义

  • 方法定义

/**
 * A Class description
 */
class Person {
    /** the name of the person */
    String name

    /**
     * Creates a greeting method for a certain person.
     *
     * @param otherPerson the person to greet
     * @return a greeting message
     */
    String greet(String otherPerson) {
       "Hello ${otherPerson}"
    }
}

GroovyDoc遵循与Java自己的JavaDoc相同的约定。因此,您将能够使用与JavaDoc相同的标签。

1.4 shebang行

除了单行注释,还有一个特殊的行注释,通常称为UNIX系统理解的shebang行,允许直接从命令行运行脚本,前提是已安装了Groovy发行版并且groovy命令已经配置到系统PATH

#!/usr/bin/env groovy
println "Hello from the shebang line"
 #字符必须是文件的第一个字符。任何缩进都会导致编译错误。

 

2.关键字(Keywords)

以下列表代表Groovy语言的所有关键字:

表1.关键字

as

assert

break

case

catch

class

const

continue

def

default

do

else

enum

extends

false

finally

for

goto

if

implements

import

in

instanceof

interface

new

null

package

return

super

switch

this

throw

throws

trait

true

try

while

  

 

3 标识符(类名、变量名以及方法名)

3.1 一般标识符

标识符以字母,$或下划线开头。他们不能以数字开头。

字母可以在以下范围内:

  • 'a'到'z'(小写ASCII字母)

  • 'A'到'Z'(大写ASCII字母)

  • '\ u00C0'至'\ u00D6'

  • '\ u00D8'至'\ u00F6'

  • '\ u00F8'至'\ u00FF'

  • '\ u0100'至'\ uFFFE'

以下是一些有效标识符(这里是变量名)的示例:

def name
def item3
def with_underscore
def $dollarStart

但以下是无效的标识符:

def 3tier
def a+b
def a#b

以点号分隔标示符也是有效的:

foo.as
foo.assert
foo.break
foo.case
foo.catch

3.2 带引号的标识符

带引号的标识符显示在点表达式的点后面。例如,表达式的name部分person.name可以用person."name"person.'name'来引用。某些被java认为不合法的字符,在groovy允许使用,例如,像破折号,空格,感叹号等字符。

def map = [:]

map."an identifier with a space and double quotes" = "ALLOWED"
map.'with-dash-signs-and-single-quotes' = "ALLOWED"

assert map."an identifier with a space and double quotes" == "ALLOWED"
assert map.'with-dash-signs-and-single-quotes' == "ALLOWED"

groovy的字符串有很多种类型,基本上所用类型的字符串都可以在点表达式的点后面:

map.'single quote'
map."double quote"
map.'''triple single quote'''
map."""triple double quote"""
map./slashy string/
map.$/dollar slashy string/$

在纯字符串和Groovy的GStrings(插值字符串)之间有一个区别,因为在后一种情况下,内插值插入到final字符串中以计算整个标识符:

def firstname = "Homer"
map."Simson-${firstname}" = "Homer Simson"

assert map.'Simson-Homer' == "Homer Simson"

 

4 字符串

Groovy通过实例化java.lang.String对象或GStrings (groovy.lang.GString)来创建和操作字符串。

4.1 单引号字符串

单引号字符串是由单引号包围的一系列字符:

'a single quoted string'
 单引号字符串是纯的java.lang.String,不支持插值。

4.2 字符串连接

所有Groovy字符串可以与+运算符连接:

assert 'ab' == 'a' + 'b'

4.3 三重单引号字符串

三重单引号字符串是由三个单引号包围的一系列字符:

'''a triple single quoted string'''
 三重单引号字符串是纯的java.lang.String,不支持插值。

三重单引号中的字符串可以分多行来写,无需连接运算符(+)连接和转义:

def aMultilineString = '''line one
line two
line three'''

4.3.1 转义特殊字符

您可以使用反斜杠字符转义单引号:

'an escaped single quote: \' needs a backslash'

你可以用双反斜杠转义转义字符本身:

'an escaped escape character: \\ needs a double backslash'

一些特殊字符也使用反斜杠作为转义字符:

转义字符表示

'\ t'

制表

'\ b'

退格

'\ n'

换行

'\ r'

回车

'\F'

换页

'\\'

反斜杠

'\''

单引号(单引号和三单引号字符串)

'\“'

双引号(对于双引号和三双引号字符串)

4.3.2 Unicode转义序列

对于键盘上不存在的字符,可以使用unicode转义序列:反斜杠,后跟'u',然后是4个十六进制数字。

例如,欧元货币符号可以表示为:

'The Euro currency symbol: \u20AC'

4.4 双引号字符串

双引号字符串是由双引号包围的一系列字符:

"a double quoted string"

4.4.1 字符串插值

双引号字符串中,我们可以插入一个变量占位符,当字符串输出时,占位符显示的是变量的值:

def name = 'Guillaume' // a plain string
def greeting = "Hello ${name}"

assert greeting.toString() == 'Hello Guillaume'

我们还可以在双引号字符串中插入表达式,在字符串输出时,显示表达式的值:

def sum = "The sum of 2 and 3 equals ${2 + 3}"
assert sum.toString() == 'The sum of 2 and 3 equals 5'

除了${}占位符之外,还可以去掉{}而直接使用$:

def person = [name: 'Guillaume', age: 36]
assert "$person.name is $person.age years old" == 'Guillaume is 36 years old'

如果使用$的形式求值,而非${}形式,要注意$后的以点号串起来的内容一直到遇到非法字符或字符串末尾都是一个变量标示符:

def number = 3.14

以下语句将抛出一个错误groovy.lang.MissingPropertyException

shouldFail(MissingPropertyException) {
    println "$number.toString()"
}

原因就是:解析器把"$number.toString()"解释为"${number.toString}()"

如果你需要在GString 中转义$${}占位符,你只需要使用\反斜杠字符来转义$符号:

assert '${name}' == "\${name}"

4.4.2 内插闭包表达式的特殊情况

到目前为止,我们已经看到我们可以在占位符内插入${}任意表达式,但是有一个特殊的情况——插入闭包表达式。当占位符包含一个箭头时${->},表示在字符串中插入闭包表达式:

def sParameterLessClosure = "1 + 2 == ${-> 3}" 
assert sParameterLessClosure == '1 + 2 == 3'

def sOneParamClosure = "1 + 2 == ${ w -> w << 3}" 
assert sOneParamClosure == '1 + 2 == 3'

下面的例子来说明内插闭包表达式相对于内插普通表达式的优势:惰性求值。

def number = 1 
def eagerGString = "value == ${number}"
def lazyGString = "value == ${ -> number }"

assert eagerGString == "value == 1" 
assert lazyGString ==  "value == 1" 

number = 2 
assert eagerGString == "value == 1" 
assert lazyGString ==  "value == 2" 

定义一个number变量赋值为1,然后在eagerGString作为一个普通表达式插入,在lazyGString作为一个闭包表达式插入,通过验证可知两个变量的结果相同;接下来我们改变number的值为2,验证发现eagerGString的值不变,lazyGString的值变为2。

4.4.3 GString和String的转化

如果一个方法(无论是在Java还是Groovy中实现)形参是java.lang.String,但是我们传了一个groovy.lang.GString实型的,GString的toString() 的方法被自动调用转化该字符串。

String takeString(String message) {         
    assert message instanceof String        
    return message
}

def message = "The message is ${'hello'}"   
assert message instanceof GString           

def result = takeString(message)            
assert result instanceof String
assert result == 'The message is hello'

4.4.4 GString和String hashCodes

String和GString的hashCodes是不同的,前者的hashCodes是不可变的,而后者的hashCodes取决于它的内插值,即时是相同的字符串结果,两者的hashCodes也不相同。

assert "one: ${1}".hashCode() != "one: 1".hashCode()

不要使用GString作为Map键,要用String作为键来检索。

def key = "a"
def m = ["${key}": "letter ${key}"]     

assert m["a"] == null                   

4.5 三重双引号字符串

三重双引号字符串的特性同双引号字符串,只不过它们可以是多行的,像三重单引号的字符串。

def name = 'Groovy'
def template = """
    Dear Mr ${name},

    You're the winner of the lottery!

    Yours sincerly,

    Dave
"""

assert template.toString().contains('Groovy')
 双引号和单引号都不需要在三重双引号字符串中转义。

4.6 斜线字符串

除了通常引用的字符串,Groovy提供了斜线字符串,/用作分隔符。斜线字符串对定义正则表达式和模式特别有用,因为不需要反斜杠,是表达式看上去简洁。

斜线字符串示例:

def fooPattern = /.*foo.*/
assert fooPattern == '.*foo.*'

内部的正斜杠需要用反斜杠转义:

def escapeSlash = /The character \/ is a forward slash/
assert escapeSlash == 'The character / is a forward slash'

斜线字符串可以是多行的:

def multilineSlashy = /one
    two
    three/

assert multilineSlashy.contains('\n')

斜线字符串也可以插值(即GString,这个特性同双引号字符串):

def color = 'blue'
def interpolatedSlashy = /a ${color} car/

assert interpolatedSlashy == 'a blue car'

空的字符串不能用双正斜杠表示,因为Groovy解析器将其理解为行注释:

assert '' == //

4.7  $/.../$字符串

$/.../$字符串是多行GStrings与一个开始$/和一个结束/$组成的。要注意的是,它用来转义的字符是$,而不是反斜线。

看如下例子:

def name = "Guillaume"
def date = "April, 1st"

def dollarSlashy = $/
    Hello $name,
    today we're ${date}.

    $ dollar sign
    $$ escaped dollar sign
    \ backslash
    / forward slash
    $/ escaped forward slash
    $/$ escaped dollar slashy string delimiter
/$

assert [
    'Guillaume',
    'April, 1st',
    '$ dollar sign',
    '$ escaped dollar sign',
    '\\ backslash',
    '/ forward slash',
        '$/ escaped forward slash',
        '/$ escaped dollar slashy string delimiter'

        ].each { dollarSlashy.contains(it) }

4.8 字符串汇总表

字符串名称

字符串语法

插值

多行

用来转义的字符

单引号

'…​'

 

 

\

三重单引号

'''…​'''

 

\

双引号

"…​"

 

\

三重双引号

"""…​"""

\

双斜线

/…​/

\

$/.../$

$/…​/$

$

4.9 字符

Groovy可以通过以下3种方式将字符串变为字符串:

char c1 = 'A' 
assert c1 instanceof Character

def c2 = 'B' as char 
assert c2 instanceof Character

def c3 = (char)'C' 
assert c3 instanceof Character

 

5 数字类型

5.1 整数类型

整数文本类型与Java中的相同:

  • byte

  • char

  • short

  • int

  • long

  • java.lang.BigInteger

使用具体类型声明:

// primitive types
byte  b = 1
char  c = 2
short s = 3
int   i = 4
long  l = 5

// infinite precision
BigInteger bi =  6

使用def关键字声明创建,将有所不同:Groovy会根据数值大小自动适配类型。

正数情况:

def a = 1
assert a instanceof Integer

// Integer.MAX_VALUE
def b = 2147483647
assert b instanceof Integer

// Integer.MAX_VALUE + 1
def c = 2147483648
assert c instanceof Long

// Long.MAX_VALUE
def d = 9223372036854775807
assert d instanceof Long

// Long.MAX_VALUE + 1
def e = 9223372036854775808
assert e instanceof BigInteger

负数情况:

def na = -1
assert na instanceof Integer

// Integer.MIN_VALUE
def nb = -2147483648
assert nb instanceof Integer

// Integer.MIN_VALUE - 1
def nc = -2147483649
assert nc instanceof Long

// Long.MIN_VALUE
def nd = -9223372036854775808
assert nd instanceof Long

// Long.MIN_VALUE - 1
def ne = -9223372036854775809
assert ne instanceof BigInteger

5.1.1 非十进制表示

数字也可以用二进制,八进制,十六进制和十进制表示。

二进制数字以0b前缀开头:

int xInt = 0b10101111
assert xInt == 175

short xShort = 0b11001001
assert xShort == 201 as short

byte xByte = 0b11
assert xByte == 3 as byte

long xLong = 0b101101101101
assert xLong == 2925l

BigInteger xBigInteger = 0b111100100001
assert xBigInteger == 3873g

int xNegativeInt = -0b10101111
assert xNegativeInt == -175

八进制以0前缀开头:

int xInt = 077
assert xInt == 63

short xShort = 011
assert xShort == 9 as short

byte xByte = 032
assert xByte == 26 as byte

long xLong = 0246
assert xLong == 166l

BigInteger xBigInteger = 01111
assert xBigInteger == 585g

int xNegativeInt = -077
assert xNegativeInt == -63

十六进制文字

十六进制以0x前缀开头:

int xInt = 0x77
assert xInt == 119

short xShort = 0xaa
assert xShort == 170 as short

byte xByte = 0x3a
assert xByte == 58 as byte

long xLong = 0xffff
assert xLong == 65535l

BigInteger xBigInteger = 0xaaaa
assert xBigInteger == 43690g

Double xDouble = new Double('0x1.0p0')
assert xDouble == 1.0d

int xNegativeInt = -0x77
assert xNegativeInt == -119

5.2 小数类型

  • float

  • double

  • java.lang.BigDecimal

十进制表示方法:

// primitive types
float  f = 1.234
double d = 2.345

// infinite precision
BigDecimal bd =  3.456

也可以用指数表示法,带有eE指数字母,后跟可选符号和表示指数的整数:

assert 1e3  ==  1_000.0
assert 2E4  == 20_000.0
assert 3e+1 ==     30.0
assert 4E-2 ==      0.04
assert 5e-1 ==      0.5

为了精确小数计算,Groovy def定义的数据类型默认是java.lang.BigDecimal的。

 小数不能使用二进制,八进制或十六进制表示形式表示。

5.3 分组下划线

Groovy允许对使用下滑线对数字分组,对于较长的数组比较有用:

long creditCardNumber = 1234_5678_9012_3456L
long socialSecurityNumbers = 999_99_9999L
double monetaryAmount = 12_345_132.12
long hexBytes = 0xFF_EC_DE_5E
long hexWords = 0xFFEC_DE5E
long maxLong = 0x7fff_ffff_ffff_ffffL
long alsoMaxLong = 9_223_372_036_854_775_807L
long bytes = 0b11010010_01101001_10010100_10010010

5.4 数字类型后缀

我们可以通过后缀来指定数字类型(大小写不区分)。

类型后缀

BigInteger

G or g

Long

L orl

Integer

I ori

BigDecimal

G org

Double

D ord

Float

F orf

例子:

assert 42I == new Integer('42')
assert 42i == new Integer('42') // lowercase i more readable
assert 123L == new Long("123") // uppercase L more readable
assert 2147483648 == new Long('2147483648') // Long type used, value too large for an Integer
assert 456G == new BigInteger('456')
assert 456g == new BigInteger('456')
assert 123.45 == new BigDecimal('123.45') // default BigDecimal type used
assert 1.200065D == new Double('1.200065')
assert 1.234F == new Float('1.234')
assert 1.23E23D == new Double('1.23E23')
assert 0b1111L.class == Long // binary
assert 0xFFi.class == Integer // hexadecimal
assert 034G.class == BigInteger // octal

5.5 数学运算符

下面来看一下二元运算类型规则,这里不包括除法运算和幂运算:

  • bytecharshortint之间,结果为int

  •   longbytecharshortint中任意一个,结果为 long

  •  BigInteger和任何其他积分类型的结果BigInteger

  •  BigDecimalbytecharshortintBigInteger中一个,结果为BigDecimal

  •  floatdoubleBigDecimal,结果为double

  • 两个BigDecimal,结果BigDecimal

下表总结了这些规则:

 bytecharshortintlongBigIntegerfloatdoubleBigDecimal

byte

int

int

int

int

long

BigInteger

double

double

BigDecimal

char

 

int

int

int

long

BigInteger

double

double

BigDecimal

short

  

int

int

long

BigInteger

double

double

BigDecimal

int

   

int

long

BigInteger

double

double

BigDecimal

long

    

long

BigInteger

double

double

BigDecimal

BigInteger

     

BigInteger

double

double

BigDecimal

float

      

double

double

double

double

       

double

double

BigDecimal

        

BigDecimal

5.5.1 除法运算

  • 除法运算中两个操作数中,其中一个如果为floatdouble,则结果是double类型的。
  • 当两个操作数都是整型(shortcharbyteintlongBigInteger)或者BigDecimal时,结果是BigDecimal
  • 如果要像Java那样取整,需要调用intdiv方法。

5.5.2 幂运算

幂运算由**运算符表示,具有两个参数:基数和指数。幂运算的结果类型取决于其操作数和操作的结果(特别是如果结果可以表示为整数值)。

Groovy的幂运算使用以下规则来确定结果类型:

  • 如果指数是十进制值

    • if结果可以表示为一个Integer,那么返回一个Integer

    • else if结果可以表示为 Long,那么返回Long

    • else返回 Double

  • 如果指数是整数值

    • 如果指数负,则返回 IntegerLong或者Double如果结果值适合该类型

    • 如果指数为正或零

      • 如果基数是 BigDecimal,则返回一个BigDecimal结果值

      • 如果基数是 BigInteger,则返回一个BigInteger结果值

      • 如果基数是一个Integer,如果结果值适合它则返回一个Integer,否则BigInteger

      • 如果基数是 Long,如果结果值适合它则返回Long,否则BigInteger

我们可以用几个例子来说明这些规则:

// base and exponent are ints and the result can be represented by an Integer
assert    2    **   3    instanceof Integer    //  8
assert   10    **   9    instanceof Integer    //  1_000_000_000

// the base is a long, so fit the result in a Long
// (although it could have fit in an Integer)
assert    5L   **   2    instanceof Long       //  25

// the result can't be represented as an Integer or Long, so return a BigInteger
assert  100    **  10    instanceof BigInteger //  10e20
assert 1234    ** 123    instanceof BigInteger //  170515806212727042875...

// the base is a BigDecimal and the exponent a negative int
// but the result can be represented as an Integer
assert    0.5  **  -2    instanceof Integer    //  4

// the base is an int, and the exponent a negative float
// but again, the result can be represented as an Integer
assert    1    **  -0.3f instanceof Integer    //  1

// the base is an int, and the exponent a negative int
// but the result will be calculated as a Double
// (both base and exponent are actually converted to doubles)
assert   10    **  -1    instanceof Double     //  0.1

// the base is a BigDecimal, and the exponent is an int, so return a BigDecimal
assert    1.2  **  10    instanceof BigDecimal //  6.1917364224

// the base is a float or double, and the exponent is an int
// but the result can only be represented as a Double value
assert    3.4f **   5    instanceof Double     //  454.35430372146965
assert    5.6d **   2    instanceof Double     //  31.359999999999996

// the exponent is a decimal value
// and the result can only be represented as a Double value
assert    7.8  **   1.9  instanceof Double     //  49.542708423868476
assert    2    **   0.1f instanceof Double     //  1.0717734636432956

 

6 布尔类型

Boolean是用于表示真值的特殊数据类型:truefalse

布尔值可以存储在变量中,分配给字段,就像任何其他数据类型:

def myBooleanVariable = true
boolean untypedBooleanVar = false
booleanField = true

truefalse是唯一的两个原始布尔值。但是更复杂的布尔表达式可以使用逻辑运算符来表示

此外,Groovy有特殊规则(通常称为Groovy Truth)用于将非布尔对象强制转换为布尔值。

 

7 Lists

Groovy使用逗号分隔的值列表,用方括号括起来表示List。Groovy List是普通的JDK java.util.List,Groovy没有定义自己的集合类,只是扩展了JDK的集合类,使它们用起来更简洁方便。

def numbers = [1, 2, 3]         

assert numbers instanceof List  
assert numbers.size() == 3      

 

在上面的例子中,我们创建了一个同类型值列表,你也可以创建包含不同类型值的列表:

def heterogeneous = [1, "a", true]  
 这里的列表包含一个数字,一个字符串和一个布尔值

默认情况下,Groovy声明的列表引用是 java.util.ArrayList 类型的,可以通过as运算符或显示声明改为其他类型:

def arrayList = [1, 2, 3]
assert arrayList instanceof java.util.ArrayList

def linkedList = [2, 3, 4] as LinkedList    
assert linkedList instanceof java.util.LinkedList

LinkedList otherLinked = [3, 4, 5]          
assert otherLinked instanceof java.util.LinkedList

 

可以使用[]带正索引或负索引的下标运算符访问(读取和设置值)列表中的元素,如果下表是正值则访问是从左向右,如果为负值访问则是从右向左的;可以用<<将元素添加到列表中:

def letters = ['a', 'b', 'c', 'd']

assert letters[0] == 'a'     
assert letters[1] == 'b'

assert letters[-1] == 'd'    
assert letters[-2] == 'c'

letters[2] = 'C'             
assert letters[2] == 'C'

letters << 'e'               
assert letters[ 4] == 'e'
assert letters[-1] == 'e'

assert letters[1, 3] == ['b', 'd']         
assert letters[2..4] == ['C', 'd', 'e']    

 

列表还可以包含其他列表以创建多维列表:

def multi = [[0, 1], [2, 3]]     
assert multi[1][0] == 2          

 

8 数组

Groovy数组的定义有两种方式,一种是显示声明创建,另一种是用as运算符:

String[] arrStr = ['Ananas', 'Banana', 'Kiwi']  

assert arrStr instanceof String[]    
assert !(arrStr instanceof List)

def numArr = [1, 2, 3] as int[]      

assert numArr instanceof int[]       
assert numArr.size() == 3

可以创建多维数组:

def matrix3 = new Integer[3][3]         
assert matrix3.size() == 3

Integer[][] matrix2                     
matrix2 = [[1, 2], [3, 4]]
assert matrix2 instanceof Integer[][]

数组元素的访问和列表访问方式是相同的:

String[] names = ['Cédric', 'Guillaume', 'Jochen', 'Paul']
assert names[0] == 'Cédric'     

names[2] = 'Blackdrag'          
assert names[2] == 'Blackdrag'
 Groovy不支持Java的数组初始化符号,因为花括号可能被错误解释为Groovy闭包的符号。

 

9 MAP

某些语言称为字典或关联数组,Groovy叫映射。将键和值用冒号分隔,键/值对之间使用逗号分隔,然后将所有键和值对放到方括号里:

def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF']   

assert colors['red'] == '#FF0000'    
assert colors.green  == '#00FF00'    

colors['pink'] = '#FF00FF'           
colors.yellow  = '#FFFF00'           

assert colors.pink == '#FF00FF'
assert colors['yellow'] == '#FFFF00'

assert colors instanceof java.util.LinkedHashMap

如果你检索映射中不存在的键,结果为null:

assert colors.unknown == null

在上面的示例中,使用字符串键,也可以使用其他类型的值作为键:

def numbers = [1: 'one', 2: 'two']

assert numbers[1] == 'one'

如果你想把变量或表达式作为映射中的键,需要把把变量和表达式放到小括号里:

person = [(key): 'Guillaume']        

assert person.containsKey('name')    
assert !person.containsKey('key')    

 

10 实例代码

源码地址

11 工具推荐——Intellj idea

232610_ynYx_2832792.png

12 官网地址

http://www.groovy-lang.org 

转载于:https://my.oschina.net/kun123/blog/848443

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值