scala学习复习笔记超详细(模式匹配)

Scala模式匹配

Scala中的模式匹配类似于Java中的switch语法,但是更加强大。

模式匹配语法中,采用match关键字声明,每个分支采用case关键字进行声明,当需要匹配时,会从第一个case分支开始,如果匹配成功,那么执行对应的逻辑代码,如果匹配不成功,继续执行下一个分支进行判断。如果所有case都不匹配,那么会执行case _ 分支,类似于Java中default语句。

1. Scala模式匹配快速入门

/**
  * @Date 2021/4/3 14:55
  * @Version 10.21
  * @Author DuanChaojie
  */
object MatchDemo01 {
  def main(args: Array[String]): Unit = {
    val oper = '+'
    val n1 = 20
    val n2 = 10
    var res = 0

    /**
      * 说明
      *  1. match (类似java switch) 和  case 是关键字
      *  2. 如果匹配成功, 则执行 => 后面的代码块.
      *  3. 匹配的顺序是从上到下,匹配到一个就执行对应的 代码
      *  4. => 后面的代码块 不要写 break,会自动的退出match
      *  5. 如果一个都没有匹配到,则执行 case _ 后面的代码块
      */
    oper match {
      case '+' => res = n1 + n2
      case '-' => res = n1 - n2
      case '*' => res = n1 * n2
      case '/' => res = n1 / n2
      case '%' => res = n1 % n2
      case _ => println("oper error")
    }

    println("res = " + res)
  }
}

match的细节和注意事项:

  1. 如果所有case都不匹配,那么会执行case _ 分支,类似于Java中default语句
  2. 如果所有case都不匹配,又没有写case _ 分支,那么会抛出MatchError
  3. 每个case中,不用break语句,自动中断case
  4. 可以在match中使用其它类型,而不仅仅是字符
  5. => 等价于 java swtich 的 :
  6. => 后面的代码块到下一个 case, 是作为一个整体执行,可以使用{} 扩起来,也可以不扩。
条件守卫

如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫。

/**
  * @Date 2021/4/3 15:21
  * @Version 10.21
  * @Author DuanChaojie
  */
object MatchIfDemo01 {
  def main(args: Array[String]): Unit = {
    /**
      * 是对"+-3!" 遍历
      */
    for (ch <- "+-3!") {

      var sign = 0
      var digit = 0
      // 模式匹配
      ch match {
        case '+' => sign = 1
        case '-' => sign = -1

        /**
          * 如果 case 后有 条件守卫即if ,那么这时的 _ 不是表示默认匹配
          * 表示忽略传入的 ch
          */
        case _ if ch.toString.equals("3") => digit = 3
        case _ if (ch > 1110 || ch < 120) => println("ch > 10")
        case _ => sign = 2
      }

      /**
        * 分析
        * + 1 0
        * - -1 0
        * 3 0 3
        * ! 2 0
        * ch + "" + sign + " " + digit
        */
      println(s"\nch = $ch \nsign = $sign \ndigit = $digit")
    }
  }
}
课堂练习

练习一:

/**
  * @Date 2021/4/3 15:40
  * @Version 10.21
  * @Author DuanChaojie
  */
object MatchExercise01 {
  def main(args: Array[String]): Unit = {

    for (ch <- "+-3!") {
        
      var sign = 0
      var digit = 0

      ch match {
        case '+' => sign = 1
        case '-' => sign = -1
        // 可以有多个 默认匹配,但是后面的默认匹配无效,编译器没有报错
        case _ => digit = 3
        case _ => sign = 2
      }

      /**
        * + 1 0
        * - -1 0
        * 3 0 3
        * ! 0 3
        */
      println(s"\nch = $ch \nsign = $sign \ndigit = $digit")
    }
  }
}

练习二:

/**
  * @Date 2021/4/3 16:00
  * @Version 10.21
  * @Author DuanChaojie
  */
object MatchExercise02 {
  def main(args: Array[String]): Unit = {
    for (ch <- "+-3!") {

      var sign = 0
      var digit = 0

      ch match {
        case _ if ch > 10000  => digit = 3
        case '+' => sign = 1
        case '-' => sign = -1
        // 说明..
        case _ => println("没有任何匹配~~~")
      }

      /**
        * 结果分析
        * + 1 0
        * - -1 0
        * 没有任何匹配~~~ 3 0 0
        * 没有任何匹配~~~ ! 0 0
        */
      println(s"\nch = $ch \nsign = $sign \ndigit = $digit")
    }
  }
}
模式中的变量

如果在case关键字后跟变量名,那么match前表达式的值会赋给那个变量

/**
  * @Date 2021/4/3 16:05
  * @Version 10.21
  * @Author DuanChaojie
  */
object MatchVarDemo01 {
  def main(args: Array[String]): Unit = {
    val ch = 'U'
    // 模式匹配
    ch match {
      case '+' => println("ok~")
      // 下面 case mychar 含义是 mychar = ch
      case mychar => println("ok~" + mychar)
      case _ => println("ok~~")
    }

    val ch1 = '+'
    /**
      * match是一个表达式,因此可以有返回值
      * 返回值就是匹配到的代码块的最后一句话的值
      */
    val res = ch1 match {
      case '+' => ch1 + " hello "
      // 下面 case mychar 含义是 mychar = ch
      case _ => println("ok~~")
    }

    println("res = " + res)
  }
}

变量声明中的模式:match中每一个case都可以单独提取出来,意思是一样的。

/**
  * @Date 2021/4/3 16:52
  * @Version 10.21
  * @Author DuanChaojie
  */
object MatchVarDemo02 {
  def main(args: Array[String]): Unit = {
    val (x, y, z) = (1, 2, "Hello")

    /**
      * x = 1
      * y = 2
      * z = Hello
      */
    println(s"x = $x\ny = $y\nz = $z\n")
    /**
      * i = BigInt(10)/3
      * j = BigInt(10)%3
      */
    val (i, j) = BigInt(10) /% 3

    /**
      * i = 3
      * j = 1
      */
    println(s"i = $i\nj = $j")
    val arr = Array(1, 3, 5, 7, 9)
    // 提出 arr 的前两个元素
    val Array(first, second, _*) = arr

    /**
      * first = 1
      * second = 3
      */
    println(s"first = $first \nsecond = $second")
  }
}
For表达式中的模式

for表达式中的模式


/**
  * @Date 2021/4/3 17:18
  * @Version 10.21
  * @Author DuanChaojie
  */
object MatchForDemo01 {
  def main(args: Array[String]): Unit = {

    val map = Map("A" -> 1, "B" -> 0, "C" -> 3)

    // 出来三个key-value ("A"->1), ("B"->0), ("C"->3)
    for ((k, v) <- map) {
      println(k + " -> " + v)
    }

    /**
      * 说明 : 只遍历出 value = 0 的key-value ,其它的过滤掉
      */
    println("--------------(k, 0) <- map-------------------")
    for ((k, 0) <- map) {
      println(k + " --> " + 0)
    }

    /**
      * 说明:这个就是上面代码的另外写法,只是下面的用法灵活和强大
      */
    println("--------------(k, v) <- map if v == 0-------------------")
    for ((k, v) <- map if v >= 1) {
      println(k + " ---> " + v)
    }
  }
}

2. Scala模式匹配详解

类型匹配

可以匹配对象的任意类型,这样做避免了使用isInstanceOf和asInstanceOf方法

/**
  * @Date 2021/4/3 16:09
  * @Version 10.21
  * @Author DuanChaojie
  */
object MatchTypeDemo01 {
  def main(args: Array[String]): Unit = {
    val a = 8
    // 说明 obj 实例的类型 根据 a 的值来返回
    val obj = if (a == 1) 1
    else if (a == 2) "2"
    else if (a == 3) BigInt(3)
    else if (a == 4) Map("aa" -> 1)
    else if (a == 5) Map(1 -> "aa")
    else if (a == 6) Array(1, 2, 3)
    else if (a == 7) Array("aa", 1)
    else if (a == 8) Array("aa")

    /**
      * 根据 obj的类型来匹配
      */
    val result = obj match {
      case a: Int => a
      case b: Map[String, Int] => "对象是一个字符串-数字的Map集合"
      case c: Map[Int, String] => "对象是一个数字-字符串的Map集合"
      case d: Array[String] => d //"对象是一个字符串数组"
      case e: Array[Int] => "对象是一个数字数组"
      case f: BigInt => Int.MaxValue
      case y: Float => println("xx")
      case _ => "啥也不是"
    }

    println(result)
  }
}

类型匹配注意事项:

  1. Map[String, Int] 和Map[Int, String]是两种不同的类型,其它类推。

  2. 在进行类型匹配时,编译器会预先检测是否有可能的匹配,如果没有则报错。

  3. 一个说明

    // case i : Int => i 表示 将 i = obj (其它类推),然后再判断类型
    val result = obj match {  
        case i : Int => i
    } 
    
  4. 如果 case _ 出现在match 中间,则表示隐藏变量名,即不使用,而不是表示默认匹配。

匹配数组
  1. Array(0) 匹配只有一个元素且为0的数组。
  2. Array(x,y) 匹配数组有两个元素,并将两个元素赋值为x和y。当然可以依次类推Array(x,y,z) 匹配数组有3个元素的等等…
  3. Array(0,_*) 匹配数组以0开始
import scala.collection.mutable.ArrayBuffer

/**
  * @Date 2021/4/3 16:14
  * @Version 10.21
  * @Author DuanChaojie
  */
object MatchArrDemo01 {
  def main(args: Array[String]): Unit = {

    // 这里arrs仅仅是一个数据
    val arrs = Array(
      Array(0), Array(1, 0),
      Array(0, 1, 0), Array(1, 1, 0),
      Array(1, 1, 0, 1)
    )

    // 遍历数据,arrs总共有五个元素
    for (arr <- arrs) {
      // 开始模式匹配
      val result = arr match {
        case Array(0) => "0"
        case Array(x, y) => x + " = " + y
        case Array(0, _*) => "以0开头和数组"
        case _ => "什么集合都不是"
      }

      /** 对结果分析:
        * result = 0
        * result = 1 = 0
        * result = 以0开头和数组
        * result = 什么集合都不是
        * result = 什么集合都不是
        */
      println("result = " + result)
    }

    println("--------------------匹配数组练习-------------------------")
    /**
      * 给你一个数组集合,如果该数组时  Array(10,20) , 请使用默认匹配,返回Array(20,10)
      */
    val arrs2 = Array(
                        Array(0),
                        Array(1, 0),
                        Array(0, 1, 0), Array(1, 1, 0),
                        Array(1, 1, 0, 1)
                      )

    for (arr <- arrs2) {
      val result = arr match {
        case Array(x, y) => ArrayBuffer(y, x) //Array(y,x).toBuffer //? ArrayB(y,x)
        case _ => "不处理~~"
      }

      println("result = " + result) //ArrayBuffer(0,1)
    }
  }
}
匹配列表
/**
  * @Date 2021/4/3 16:21
  * @Version 10.21
  * @Author DuanChaojie
  */
object MatchListDemo01 {
  def main(args: Array[String]): Unit = {
    val array = Array(
                        List(0),
                        List(1, 0),
                        List(88),
                        List(0, 0, 0), List(1, 0, 0)
                      )

    for (list <- array) {

      // 进行模式匹配
      val result = list match {
        case 0 :: Nil => "0" //
        case x :: y :: Nil => x + " " + y

        // "以0开头的数组"
        case 0 :: tail => "0 ..." //
        case x :: Nil => x
        case _ => "something else"
      }

      /**
        * result = 0
        * result = 1 0
        * result = 88
        * result = 0 ...
        * result = something else
        */
      println("result = " + result)
    }
  }
}
匹配元组
/**
  * @Date 2021/4/3 16:27
  * @Version 10.21
  * @Author DuanChaojie
  */
object MatchTupleDemo01 {
  def main(args: Array[String]): Unit = {
    val array = Array((0, 1), (1, 0), (10, 30), (1, 1), (1, 0, 2))

    /**
      * 如果要匹配 (10, 30) 这样任意两个元素的对偶元组,应该如何写
      */
    for (pair <- array) {
      // 开始模式匹配
      val result = pair match {
        case (0, _) => "0 ..."
        case (y, 0) => y
        case (x, y) => (y, x) //"匹配到(x,y)" + x + " " + y
        case _ => "other"
      }

      /**
        * result = 0 ...
        * result = 1
        * result = (30,10)
        * result = (1,1)
        * result = other
        */
      println("result = " + result)
    }
  }
}
匹配对象

对象匹配,什么才算是匹配呢?规则如下:

case中对象的unapply方法(对象提取器)返回Some集合则为匹配成功,返回none集合则为匹配失败。

快速入门案例:

/**
  * @Date 2021/4/3 16:32
  * @Version 10.21
  * @Author DuanChaojie
  */
object MatchObjectDemo01 {
  def main(args: Array[String]): Unit = {
    val number: Double = Square(6.0)
    println(number)
    number match {
      /**
        * 说明 case Square(arg) 的运行的机制
        * 1. 当匹配到 case Square(arg)
        * 2. 调用Square 的 unapply(arg: Double),arg的值就是 number
        * 3. 如果对象提取器 unapply(arg: Double) 返回的是Some(6.0) ,
        *    则表示匹配成功,同时将6.0 赋给 Square(arg) 的 arg
        * 4. 果对象提取器 unapply(arg: Double) 返回的是None ,则表示匹配不成功
        */
      case Square(arg) => println(s"匹配成功arg = $arg")
      case _ => println("nothing matched")
    }
  }
}

object Square {
  /**
    * unapply方法是对象提取器
    *
    * @param arg 接收的Double类型的参数
    * @return 返回类型是Option[Double],
    *         返回值是Some(math.sqrt(arg)),返回arg的开平方的值并放入到Some()中
    */
  def unapply(arg: Double): Option[Double] = {
    Some(math.sqrt(arg))
  }

  def apply(arg: Double): Double = arg * arg
}

应用案例二:

/**
  * @Date 2021/4/3 16:48
  * @Version 10.21
  * @Author DuanChaojie
  */
object MatchObjectDemo2 {
  def main(args: Array[String]): Unit = {

    val namesString = "Alice,Bob,Thomas"
    // 开始模式匹配
    namesString match {
      /**
        * 当执行case Names(first, second, third)
        * 1. 会调用 unapplySeq(str),把 "Alice,Bob,Thomas" 传入给 str
        * 2. 如果 返回的是 Some("Alice","Bob","Thomas"),分别给 (first, second, third)
        *    注意,这里的返回的值的个数需要和 (first, second, third)要一样
        * 3. 如果返回的None ,表示匹配失败
        */
      case Names(first, second, third) => {
        // the string contains three people's names
        // Alice Bob Thomas
        println("the string contains three people's names")
        // 打印字符串
        println(s"$first $second $third")
      }
      case _ => println("nothing matched")
    }
  }
}

object Names {
  //当构造器是多个参数时,就会触发这个对象提取器
  def unapplySeq(str: String): Option[Seq[String]] = {
    if (str.contains(",")) {
      Some(str.split(","))
    } else {
      None
    }
  }
}
  1. 当case 后面的对象提取器方法的参数为多个,则会默认调用def unapplySeq() 方法
  2. 如果unapplySeq返回是Some,获取其中的值,判断得到的sequence中的元素的个数是否是三个,如果是三个,则把三个元素分别取出,赋值给first,second和third
  3. 其它的规则不变。

3. 样例类

  1. 样例类仍然是类
  2. 样例类用case关键字进行声明。
  3. 样例类是为模式匹配而优化的类
  4. 构造器中的每一个参数都成为val——除非它被显式地声明为var(不建议这样做)
  5. 在样例类对应的伴生对象中提供apply方法让你不用new关键字就能构造出相应的对象提供unapply方法让模式匹配可以工作
  6. 将自动生成toString、equals、hashCode和copy方法(有点类似模板类,直接给生成,供程序员使用)
  7. 除上述外,样例类和其他类完全一样。你可以添加方法和字段,扩展它们。
样例类快速入门
package com.atguigu.chapter12.caseclass

/**
  * @Date 2021/4/3 17:23
  * @Version 10.21
  * @Author DuanChaojie
  */
object CaseClassDemo01 {
  def main(args: Array[String]): Unit = {
    println("-------------------------------")

    /**
      * 类型(对象)--序列化---字符串:
      *   1.可以保存到文件中
      *   2.反序列化
      *   3.网络传输
      */
  }
}

abstract class Amount

/**
  * 样例类
  * @param value
  */
case class Dollar(value: Double) extends Amount

/**
  * 样例类
  * @param value
  * @param unit
  */
case class Currency(value: Double, unit: String) extends Amount

/**
  * 样例类
  */
case object NoAmount extends Amount
样例类最佳实践

样例类最佳实践一:当我们有一个类型为Amount的对象时,可以用模式匹配来匹配他的类型,并将属性值绑定到变量(即:把样例类对象的属性值提取到某个变量,该功能有用)

package com.atguigu.chapter12.caseclass

/**
  * @Date 2021/4/3 18:15
  * @Version 10.21
  * @Author DuanChaojie
  */
object CaseClassDemo02 {
  def main(args: Array[String]): Unit = {
    val array = Array(Dollar2(1000.0), Currency2(1000.0, "RMB"), NoAmount2)
    for (amt <- array) {
      val result = amt match {
        case Dollar2(v) => "$" + v
        case Currency2(v, u) => v + " " + u
        case NoAmount2 => ""
      }
      println("amt = " + amt)
      println("result =" + result)
    }
  }
}

abstract class Amount2

/**
  * 样例类
  *
  * @param value
  */
case class Dollar2(value: Double) extends Amount2

/**
  * 样例类
  *
  * @param value
  * @param unit
  */
case class Currency2(value: Double, unit: String) extends Amount2

/**
  * 样例类
  */
case object NoAmount2 extends Amount2

样例类最佳实践二:

  1. 样例类的copy方法和带名参数
  2. copy创建一个与现有对象值相同的新对象,并可以通过带名参数来修改某些属性。
/**
  * @Date 2021/4/3 18:30
  * @Version 10.21
  * @Author DuanChaojie
  */
object CaseClassDemo03 {
  def main(args: Array[String]): Unit = {
    val amt1 = new Currency3(3000.0, "RMB")
    // 克隆,创建的对象和amt的属性一样
    val amt2 = amt1.copy()

    // amt2.value = Currency3(3000.0,RMB).value 	 amt2.unit = Currency3(3000.0,RMB).unit
    println(s"amt2.value = $amt2.value \t amt2.unit = $amt2.unit")

    val amt3 = amt1.copy(value = 8000.0)
    // amt3 = Currency3(8000.0,RMB)
    println("amt3 = " + amt3)

    val amt4 = amt1.copy(unit = "美元")
    // amt4 = Currency3(3000.0,美元)
    println("amt4 = " + amt4)
  }
}

abstract class Amount3

/**
  * 样例类
  *
  * @param value
  */
case class Dollar3(value: Double) extends Amount3

/**
  * 样例类
  *
  * @param value
  * @param unit
  */
case class Currency3(value: Double, unit: String) extends Amount3

/**
  * 样例类
  */
case object NoAmount3 extends Amount3
case语句的中置(缀)表达式

什么是中置表达式?1 + 2,这就是一个中置表达式。如果unapply方法产出一个元组,你可以在case语句中使用中置表示法。比如可以匹配一个List序列

/**
  * @Date 2021/4/3 18:38
  * @Version 10.21
  * @Author DuanChaojie
  */
object MidCase {
  def main(args: Array[String]): Unit = {
    List(1, 3, 5, 9) match {
      /**
        * 修改并测试
        * 1.两个元素间::叫中置表达式,至少first,second两个匹配才行.
        * 2.first 匹配第一个 second 匹配第二个, rest 匹配剩余部分(5,9)
        */
      case first :: second :: rest => println(first + " " + second  + "\nrest.length = "+ rest.length + "\n" + rest) //
      case _ => println("匹配不到...")
    }
  }
}
匹配嵌套结构

操作原理类似于正则表达式

最佳实践案例——商品捆绑打折出售

  1. 现在有一些商品,请使用Scala设计相关的样例类,完成商品捆绑打折出售。
  2. 要求商品捆绑可以是单个商品,也可以是多个商品。
  3. 打折时按照折扣x元进行设计
  4. 能够统计出所有捆绑商品打折后的最终价格。

样例类的设计:

/**
  * 设计样例类
  */
abstract sealed class Item

case class Book(description: String, price: Double) extends Item

case class Food(description: String, price: Double) extends Item

/**
  * Bundle 捆 , discount: Double 折扣 , item: Item*
  *
  * @param description 描述
  * @param discount    折扣的价格
  * @param item
  */
case class Bundle(description: String, discount: Double, item: Item*) extends Item

完成这个案例需要有以下知识储备:

  1. 如果我们进行对象匹配时,不想接受某些值,则使用_ 忽略即可,_* 表示所有
  2. 通过@表示法将嵌套的值绑定到变量。_*绑定剩余Item到rest
  3. 不使用_*绑定剩余Item到rest
/**
  * @Date 2021/4/3 18:43
  * @Version 10.21
  * @Author DuanChaojie
  */
object CaseClassExercise {
  def main(args: Array[String]): Unit = {
    /**
      * 这里给出了一个具体的打折的案例
      */
    val sale = Bundle("书籍", 10, Book("漫画", 40),
      Bundle("文学作品", 20, Book("《阳关》", 80), Book("《围城》", 30), Book("天龙八部", 100)))

    /** 完成上面例子之前需要学习三个知识点:
      * 知识点1 :使用case语句,得到 "漫画"
      */
    val res1 = sale match {
      /**
        * 如果我们进行对象匹配时,不想接受某些值,则使用_ 忽略即可,_* 表示所有
        */
      case Bundle(_, _, Book(desc, _), _*) => desc
    }
    // res1 = 漫画
    println("res1 = " + res1)

    /**
      * 知识点2-通过@表示法将嵌套的值绑定到变量。_*绑定剩余Item到rest
      */
    val res2 = sale match {
      //如果我们进行对象匹配时,不想接受某些值,则使用_ 忽略即可,_* 表示所有
      case Bundle(_, _, art@Book(_, _), rest@_*) => (art, rest)
    }
    // res2 = (Book(漫画,40.0),WrappedArray(Bundle(文学作品,20.0,WrappedArray(Book(《阳关》,80.0), Book(《围城》,30.0), Book(天龙八部,100.0)))))
    println("res2 = " + res2)


    /**
      * 知识点3-不使用_*绑定剩余Item到rest
      */
    val res3 = sale match {
      // 如果我们进行对象匹配时,不想接受某些值,则使用_ 忽略即可,_* 表示所有
      case Bundle(_, _, art3@Book(_, _), rest3) => (art3, rest3)
    }
    // res3 = (Book(漫画,40.0),Bundle(文学作品,20.0,WrappedArray(Book(《阳关》,80.0), Book(《围城》,30.0), Book(天龙八部,100.0))))
    println("res3 = " + res3)
  }
}

完成上面的案例:

/**
  * @Date 2021/4/3 18:43
  * @Version 10.21
  * @Author DuanChaojie
  */
object CaseClassExercise {
  def main(args: Array[String]): Unit = {
    /**
      * 这里给出了一个具体的打折的案例
      */
    val sale = Bundle("书籍", 10, Book("漫画", 40),
      Bundle("文学作品", 20, Book("《阳关》", 80), Book("《围城》", 30), Book("天龙八部", 100)))

    /**
      * 完成案例
      * price(sale) = 220.0
      */
    println("price(sale) = " + price(sale))

  }

  def price(it: Item): Double = {
    it match {
      case Book(_, p) => p
      case Bundle(_, disc, its@_*) => its.map(price).sum - disc
    }
  }
}

4. 密闭类

  1. 如果想让case类的所有子类都必须在申明该类的相同的源文件中定义,可以将样例类的通用超类声明为sealed,这个超类称之为密封类。
  2. 密封就是不能在其他文件中定义子类。
  3. 在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱敲代码的小黑

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

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

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

打赏作者

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

抵扣说明:

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

余额充值