scala特点
Scala介绍
- Scala是把函数式编程思想和面向对象编程思想结合的一种编程语言
- 大数据计算引擎Spark由Scala编写
Scala特点
- 多范式
- 面向对象
- 函数式编程
- 兼容JAVA
- 类库调用
- 互操作
- 语法简洁
- 代码行短
- 类型推断
- 抽象控制
- 静态类型化
- 可检验
- 安全重构
- 支持并发控制
- 强计算能力
- 自定义其他控制结构
scala基础编程
scala的第一个代码 hello world
注意事项:
- 如果一个scala文件想要运行,class要改成object
- 如果是class,就仅单纯代表一个类,如果是object代表的是单例对象
- scala语法中,一句话结束不需要加分号
- scala文件中,可以使用java中的类和方法
object HelloWorld {def main(args: Array[String]): Unit = {println("hello world!")System.out.println("hello world!!!")}
}
初学scala
变量:在程序的运行过程中,其值可以发生改变的
在scala中定义一个变量,需要使用一个关键字:var
注意:
1、变量一旦定义,它的类型就确定,可以不用手动指定类型,根据赋的值自动推断出类型
2、也可以手动的指定变量的数据类型,完整的写法:var 变量名:数据类型 = 值
scala和java中类型关系对应:
java: scala:
byte Byte
short Short
int Int
long Long
float Float
double Double
boolean Boolean
char Char
定义常量:在程序的运行过程中,其值不能发生改变的量
在scala中定义一个常量,需要使用一个关键词:val
scala中字符串的使用
字符串:由若干个字符组成的序列
在scala中字符串是如何做拼接的?
- 使用+号拼接 这种拼接方式比较消耗性能
- 使用StringBuilder
- 前提:有一组序列,使用scala特有的函数 mkString
- 使用scala特有的字符串传递方式s"${变量}"底层就是使用StringBuilder
scalaIO流
读取一个文件
1、可以使用java的方式读取一个文件
def main(args: Array[String]): Unit = {// 使用java的方式读取val br: BufferedReader = new BufferedReader(new FileReader("scala/data/demo"))var line:String = br.readLine()while (line != null){println(line)line = br.readLine()} }
2、使用scala中的读取文件
val bs: BufferedSource = Source.fromFile("scala/data/demo.txt")val strings: Iterator[String] = bs.getLines()while (strings.hasNext){val str: String = strings.next()println(str)}
- 注意:Source.fromFil 底层是使用了字节输入流读取数据FileInputStream
scala读取文件简写
for (e <- bs.getLines()){println(e)}
3、使用java方法写文件
val bw: BufferedWriter = new BufferedWriter(new FileWriter("scala/data/text.txt"))bw.write("杰克爱是越南战俘!!")bw.newLine()bw.write("小手也是!!")bw.flush()bw.close()
4、scala里面没有写文件的方式!!!
scala异常Exception
scala中的异常和java中的很像
可以手动抛出异常
import java.io.{BufferedReader, FileReader} import java.util.Scannerobject ExceptionDemo { def main(args: Array[String]): Unit = {try {println(10/2)val arr: Array[Int] = Array(11, 22, 33, 44, 55) // println(arr(5))// 手动抛出异常var sc:Scanner = new Scanner(System.in)val reader: BufferedReader = new BufferedReader(new FileReader("scala/data/demo1.txt"))println("输入除数:")var num:Int = sc.nextInt()if(num != 0){println(10/num)}else{throw new ArithmeticException("您输入的除数是0!!!")}}catch{case e:ArithmeticException => // println("除0异常")e.printStackTrace()case e:ArrayIndexOutOfBoundsException =>e.printStackTrace()case _ =>println("出现了异常")}finally {println("finally代码块")}println("hi nihao haha e") } }
scala函数
def:定义函数或者方法的关键字
main:是函数或者方法的名字,符合标识符的命名规则
args:函数形参的名字
Array[String]:参数的数据类型是一个元素为字符串的数组
Unit:等于java中的void viod表示无返回值的意思
=:后面跟着函数体
在不同的地方定义,称呼不一样
函数:在object中定义的叫做函数
方法:在class中定义的叫做方法
def main(args: Array[String]): Unit = {}
函数的定义格式
// 定义格式一:如果函数有返回值,且最后一句话有返回值的话,return可以不写 def add1(a1: Int, b1: Int): Int = {a1 + b1 } // 定义格式二:如果函数体中只有一句话那么大括号也可以不写 def add2(a1: Int, b1: Int): Int = a1 + b1 //定义格式三:如果函数没有参数的时候,小括号也可以不写 def show = println("他是铁头功!我是金刚腿!") }
scala中的函数可以嵌套定义,函数中可以再定义函数
但是调用必须在定义之后
def plus(x:Int,y:Int):Int = {x + y}val sum3: Int = plus(20, 20)println(sum3)
注意:
- 函数或方法的定义必须定义在class或者object中
创建类用来调用方法
class Demo1{ // 这里叫方法,将来调用时需要创建该类的对象才可以调用 def add(a:Int,b:Int):Int = {a + b } }// 调用类中的方法val demo = new Demo1()val sum4: Int = demo.add(1, 2)println(sum4)
通过类名调用函数
//object中的函数可以使用类名调用,类似于静态一样val res1: Int = Demo5Function.add2(200, 100)println(res1)//object调用形式二:object中的函数调用时,可以省略类名// 如果方法调用的函数只有一个参数的时候,可以将.和小括号用空格代替调用val res2: Int = Demo5Function fun1 "1000"println(res2)
函数递归
object Demo6Function { def main(args: Array[String]): Unit = {/*** 递归方法定义时要有出口,不然就是死递归*/val res1: Int = factorial(5)println(s"5的阶乘是:$res1")println(s"5的阶乘是${Demo6Function factorial 5}")}def factorial(num:Int):Int = {if(num == 1){1}else{num * factorial(num -1)}} }
class
可以在scala程序中定义类
类:构造方法 成员方法 成员变量
构造方法:
1、在scala中构造方法的编写和在java中的不太一样。类所拥有的大括号中都是构造代码块的内容
2、默认情况下,每一个类都应该提供一个无参的构造方法
3、构造方法可以有许多
创建两个类一个有参,一个无参
class Student(name: String, age: Int) { /*** 定义成员变量*/val _name: String = nameval _age: Int = agevar _gender: String = _ //下划线表示将来会赋予默认值//构造方法可以写很多个def this(name: String, age: Int,gender:String) {this(name:String,age:Int)_gender = gender}// 重写toString方法override def toString:String = {"姓名:"+_name+", 年纪:"+_age+",性别:"+_gender} }
class Student2{def fun1(): Any= {println("杰克爱会赢的!")} }
创建main函数调用类中的方法
def main(args: Array[String]): Unit = {val s1: Student = new Student("Real-person", 18, "女")println(s1)// 如果调用的是一个类的无参构造方法,new的时候小括号可以不用写val s2: Student2 = new Student2s2.fun1()val s3:Object= new Student("five-piece",30,"男")println(s3.toString)}
样例类
scala提供了一个非常好用的功能:样例类
较少用户创建类所编写代码量,只需要定义成员变量即可,自动扩充成员变量,构造方法,重写toString方法
- 样例类中的成员变量,编译后默认是被jvm添加了final关键字,用户是改变不了的
- 对于scala来说,默认是被val修饰的
- 如果将来想要被改变,定义的时候需要使用var进行修饰
创建样例类
case class Teacher(name:String,age:Int, var like:String)
def main(args: Array[String]): Unit = {val t1: Teacher = new Teacher("teacher.lee", 16, "study")println(t1)t1.like = "watchingTV"println(t1.like)
apply
创建一个Object对象时可以直接调用里面的函数
object Demo9Apply { def main(args: Array[String]): Unit = {Book() } }object Book{ def apply():Unit = {println("你的牌打的也忒好了!!") } }
当Object名与class名一样是叫做伴生对象
object Demo9Apply { def main(args: Array[String]): Unit = {val book: Book = Book("我的奋斗", 99)println(book) } }object Book{ def apply(name:String,price:Int):Book = {new Book(name, price) } }class Book(name:String,price:Int){ var _name:String = name var _price:Int = priceoverride def toString: String = "书名:" + _name + ", 价格:" + _price }
Scala中的函数式编程
面向对象编程:将对象当作参数一样传来传去
1、对象可以当作方法参数传递
2、对象也可以当作方法的返回值返回
3、当看到类、抽象类,接口的时候,今后无论是参数类型还是返回值类型,都需要提供对应的实现类对象
面向函数式编程:将函数当作参数一样传来传去
1、函数A当作函数B的参数进行传递
2、函数A当作函数B的返回值返回
在scala中,将函数也当作一个对象,对象就有类型
函数在scala也有类型的说法
参数类型 => 返回值类型
-
一个参数为字符串类型,返回值是整数类型的函数
def fun1(s:String):Int = {s.toInt + 1000}val res1: Int = fun1("100")
-
定义变量的方式,定义一个函数,将函数当作对象,赋值给类型是函数类型的变量,将来可以直接通过变量调用函数
val fun2: String => Int = fun1val res2: Int = fun2("200")println(res2)
-
函数A作为函数B的参数定义,本质上讲函数A的处理逻辑主体传给了函数B,在函数B中使用这个处理逻辑
//定义def fun3(f:String => Int):Int = {val a1: Int = f("1000")a1 + 300}def show1(s:String):Int={s.toInt}def show2(s:String):Int={s.toInt + 11111}//调用val res3: Int = fun3(show1)println(res3)val res4: Int = fun3(show2)println(res4)
-
定义一个函数,函数的参数列表中,既有正常的类型参数,也有函数类型的参数
def fun4(s:String,f:String=>Int):Int={val a1: Int = f(s)a1 + 1000}def show3(s:String):Int={s.toInt}def show4(s: String): Int = {s.toInt + 1000}val res5: Int = fun4("2000", show4)println(res5)
使用lambda表达式改写函数作为参数传递的调用形式
val r1: Int = fun4("2000", (s: String) => s.toInt)val r2: Int = fun4("2000", (s: String) => s.toInt + 1000)//在scala中,数据类型可以自动类型推断val r3: Int = fun4("2000", s => s.toInt)//如果当作参数的函数的参数只在函数主体使用了一次,那么可以使用_代替val r4: Int = fun4("2000", _.toInt + 1000)println(r1+"|"+r2+"|"+r3+"|"+r4)
-
函数当作参数传递的应用
def main(args: Array[String]): Unit = {val arr: Array[Int] = Array(11, 22, 33, 44, 55)// for(e<-arr){// println(e)// }def fun1(i: Int): Unit ={println(i*2)}//def foreach[U](f: A => U): Unit//foreach函数需要一个参数和数组元素一样类型的类型,返回值是Unit的函数//foreach函数的主要作用是将调用该方法的序列中的元素,依次取出传递给后面的函数进行处理arr.foreach(fun1)println("*" * 50)//scala自带的一个函数// def println(x:Any) = Console.println(x)//Any可以接受任意的数据类型元素arr.foreach(println)}
-
函数当返回值返回
- 参数是String类型,返回值是一个函数(参数值是String类型,返回值是Int)
- 方式2这种将参数分开定义,今后调用可以分开传递,这种做法,在scala中叫做函数柯里化
- 偏函数的使用
def main(args: Array[String]): Unit = {def fun1(s1: String): String => Int = {def show(s: String): Int = {s.toInt + s1.toInt}show}val resFun1: String => Int = fun1("1")val res1: Int = resFun1("1000")println(res1)def fun2(s1:String)(s:String):Int = {s.toInt+ s1.toInt}val res2: Int = fun2("1")("1000")println(res2)// 偏函数调用def function1(s1:String,s2:String):Int={s1.toInt+s2.toInt}val f1: String => Int = function1("1", _)val res3: Int = f1("1000")val res4: Int = f1("2000")val res5: Int = f1("3000")println(s"res3:$res3,res4$res4,res5$res5")}
柯里化:
- 本身是一个数学界的名词,本意是原来一次传递多个参数,现在被改变成了可以分开传递的形式,这种做法叫做柯里化
- 在scala中体现柯里化,指的是函数的返回值也是一个函数,将来调用时参数可以分开传递
- 提高了程序的灵活性和代码的复用性
- 在Scala中也可以通过偏函数实现参数分开传递的功能
scala中的集合
scala中的集合:
- List:元素有序,且可以发生重复,长度固定的
- Set:元素无序,且唯一,长度固定的
- Map:元素是键值对的形式,键是唯一的
- Tuple:元组,长度是固定的,每个元素的数据类型可以不一样
arraylist
在utiljava包下的集合
object Demo13ArrayList { def main(args: Array[String]): Unit = {val list1: util.ArrayList[Int] = new util.ArrayList[Int]()list1.add(11)list1.add(22)list1.add(33)list1.add(44)list1.add(55)println(list1)println("*" * 50)var i: Int = 0while (i < list1.size()) {println(list1.get(i))i += 1} }
注意: scala中的for循环只能遍历scala中的序列,无法遍历java中的序列
List
可以创建一个空的集合
使用size或length函数获取List集合的长度
可以直接通过索引下标获取元素
scala推荐获取第一个元素的方式是调用head函数
可以根据指定的分隔符拼接元素
reverse函数反转,返回一个新的集合
distinict函数去重,返回一个新的集合
tail函数去除第一个元素,其余的元素返回一个新的集合
take(n)函数从左向右取元素,取若干个
takeRight(n)函数 取右边,组成新的集合
takewhile(条件表达式) 从第一个判断取数据,直到不符合条件停止
sum函数 元素必须是数值
List高阶函数
foreach:将集合中的元素依次取出传入到后面的函数中
注意:foreach函数是没有返回值的,要么就输出,要么就其他的方式处理掉的
map:依次处理每一个元素,得到一个新的结果,返回到一个新的集合中
filter:保留符合条件的元素
sortBy:根据条件排序
sortWith:两个数之间的关系排序
flatMap:扁平化
groupBy:根据条件分组
Set
scala中的Set集合也是不可变的,除了排序相关的函数以外,List集合有的高阶函数,Set集合也有
set 和list可以相互转换
val set1: Set[Int] = Set(1, 4, 3, 6, 5)val set2: Set[Int] = Set(3, 6, 5, 7, 8)// 求交集val set3: Set[Int] = set1 & set2println(s"set1:${set1}")println(s"set2:${set2}")println(s"set3:${set3}")println("*" * 50)// 求并集val set4: Set[Int] = set1 | set2println(s"set1:${set1}")println(s"set2:${set2}")println(s"set4:${set4}")println("*" * 50)//求差集val set5: Set[Int] = set1 &~ set2println(s"set1:${set1}")println(s"set2:${set2}")println(s"set5:${set5}")println("*" * 50)//Set集合和List集合相互转换// list to setval list1: List[Int] = List(11, 22, 33, 44, 55, 11, 22, 44, 88, 33, 44, 99, 11, 22, 55)val resSet1: Set[Int] = list1.toSetprintln(s"list1:${list1}")println(s"resSet1:${resSet1}")println("*" * 50)//set to listval resList1: List[Int] = resSet1.toListprintln(s"list1:${list1}")println(s"resSet1:${resSet1}")println(s"resList1:${resList1}")
Mutable
注意导包:通过观察api发现,不可变的集合是属于scala.collection.immutable包下的
如果将来想要使用可变的集合,就要去scala.collection.mutable包下寻找
普通list集合的中函数在可变list集合都可以调用
def main(args: Array[String]): Unit = {// 定义一个可变的list集合val listBuffer = new ListBuffer[Int]println(listBuffer)listBuffer.+=(11)listBuffer.+=(22)listBuffer.+=(33)listBuffer.+=(11)listBuffer.+=(55)listBuffer.+=(22)listBuffer.+=(33)listBuffer.+=(66)println(listBuffer)println("*" * 80)// 获取元素println(listBuffer(2))println(listBuffer.head)println(listBuffer.last)// 删除元素listBuffer.-=(33)println(listBuffer)println("*" * 80)//批量添加元素listBuffer.+=(100,200,300,400)println(listBuffer)println("*" * 80)val list: List[Int] = List(99, 88, 77)listBuffer.++=(list)println(list)println("*" * 80)/*可变的Set集合*/val hashSet = new mutable.HashSet[Int]()val resSet: hashSet.type = hashSet.+=(123, 456, 789)println(resSet)}
tuple
大小,值是固定的,根据创建的类来定,每个元素的数据类型可以是不一样的,最高可以创建存储22个元素的元组
object Demo17Tuple {def main(args: Array[String]): Unit = {val tuple1: (Int, String, Int, String, String) = Tuple5(1001, "jack爱", 18, "男", "用黑人牙膏")println(tuple1)val s2 = new Student3("zobayan", 4, "helicopter")val tuple: (Int, Student3) = Tuple2(1002, s2)println(tuple._2.like)}}case class Student3(name:String,age:Int,like:String)
Map
val map1: Map[Int, String] = Map((1001, "张三"), (1002, "李四"), (1003, "王五") (1001, "赵六"), 1005 -> "易政")println(map1)//键是唯一的,当键一样的时候值会覆盖println("*" * 80)//苦于根据键获取值// 小括号获取值 当键不存在的时候会报错println(map1(1002))// 使用get获取值 当键不存在时不会报错 返回Noneprintln(map1.get(1002))// 根据键获取值,当键不存在时,返回所提供的默认值,提供的默认值苦于是任何类型的数据println(map1.getOrElse(1001, 0))println("*" * 80)//获取所有的键组成一个迭代器val keys: Iterable[Int] = map1.keyskeys.foreach(println)println("*" * 80)//获取所有的值组成一个迭代器val values: Iterable[String] = map1.valuesvalues.foreach(println)println("*" * 80)// 遍历集合的第一种方式,先获取所有的键在根据键获取对应的值val keys1: Iterable[Int] = map1.keysfor (e <- keys1) {val value: Any = map1.getOrElse(e, 0)println(s"键:$e,值:$value")}println("*" * 80)//遍历集合的第二种方式,直接遍历map集合,得到每一个由键值对组成的元组for (kv <- map1) {println(s"键:${kv._1},值:${kv._2}")}println("*" * 80)// 遍历集合的第三种方式,foreach的方式map1.foreach((kv: (Int, String)) => println(s"键:${kv._1},值:${kv._2}") )
综合小练习:根据上述所讲内容完成数字统计
hello|world java|hadoop|linux java|scala|hadoop hive|java|hello|world hello|world java|hadoop|linux java|scala|hadoop hive|java|hello|world hello|world java|hadoop|linux java|scala|hadoop hive|java|hello|world java|hadoop|linux java|scala|hadoop hive|java|hello|world hello|world java|hadoop|linux
def main(args: Array[String]): Unit = {val lineList: List[String] = Source.fromFile("scala/data/words.txt").getLines().toListval newLineList: List[String] = lineList.flatMap(line => line.split("\\|"))val wordMap: Map[String, List[String]] = newLineList.groupBy(e => e)val wordList: List[(String, Int)] = wordMap.map { word =>(word._1, word._2.size)}.toListwordList.foreach { word =>println(s"${word._1}:${word._2}")}wC()}// 链式编程简写private def wC(): Unit = {Source.fromFile("scala/data/words.txt").getLines().toList.flatMap(_.split("\\|")).groupBy(e => e).map { word => (word._1, word._2.size) }.toList.foreach { word => println(s"${word._1}:${word._2}") }}
scala其他常用知识点
JDBC
JDBC的连接步骤:
- 1、注册驱动
- 2、创建数据库连接对象
- 3、创建数据操作对象
- 4、执行sql语句
- 5、如果第四步是查询的话,分析查询结果
- 6、释放资源
Json文件处理
{"flag": "SUCCESS","student_list": [{"id": "1001","name": "易政","age": 18,"like": ["学习","看美女","打游戏"]},{"id": "1002","name": "小虎","age": 19,"like": ["踢足球","打联盟","睡觉"]}] }
def main(args: Array[String]): Unit = {val lineList: List[String] = Source.fromFile("scala/data/stu.json").getLines().toListval jsonStr: String = lineList.mkString("\r\n")// 使用fastjson中的JSON类,将一个字符串转成一个json对象// 转成json对象之后可以通过键获取值//parseObject将整体转换成一个json格式数据val jsonObject: JSONObject = JSON.parseObject(jsonStr)val s1: String = jsonObject.getString("student_list")// parseArray将一个"[{},{}]"变成一个元素是json对象的数组val jSONArray: JSONArray = JSON.parseArray(s1)var i:Int = 0while (i<jSONArray.size()){val obj1: JSONObject = jSONArray.getJSONObject(i)val name: String = obj1.getString("name")val like: String = obj1.getString("like")println(s"姓名:$name,兴趣:$like")i += 1}}
Scala和java的相互转换
将java中的集合转成scala中的集合,java中的集合本来是没有转换scala的功能,需要导入隐式转,scala中的导包,可以在任意地方
def main(args: Array[String]): Unit = {// 创建一个java中的集合val array1 = new util.ArrayList[Int]()array1.add(11)array1.add(22)array1.add(33)array1.add(66)array1.add(55)array1.add(44)println(array1)/*TODO将java中的集合转成scala中的集合java中的集合本来是没有转换scala的功能,需要导入隐式转换scala中的导包,可以在任意地方*/import scala.collection.JavaConverters._val list1: List[Int] = array1.asScala.toListprintln(list1)val javaList: util.List[Int] = list1.asJavaprintln(javaList)}
Macth模式匹配
语法:
语法:表达式 match{case 值|[变量名:类型]|元组|数组|对象 =>匹配成功执行的语句case xxx => xxxcase _ xxx => xxx}
若模式匹配没有对应的匹配,会报错!!!
案例:
def main(args: Array[String]): Unit = {// 匹配变量值var i: Int = 200i match {case 20 => println("该值是20")case 200 => println("该值是200")case _ => println("该值是200")}// 可以匹配数据类型var flag: Int = 200flag match {case _: Int => println("是Int类型")case _ => println("是Boolean类型")}//匹配元组val tuple1: (Int, String, Int) = Tuple3(1001, "张三", 18)tuple1 match {case (a1: Int, a2: String, a3: Int) =>println(s"学号:$a1,姓名:$a2,年龄:$a3")}//模式匹配的应用:避免异常val map1: Map[Int, String] = Map((1001, "张三"), (1002, "李四"))val sc: Scanner = new Scanner(System.in)println("请输入你想要想查询的键:")val key: Int = sc.nextInt()map1.get(key) match {case Some(a:Any) =>println(s"${key}键对应的值为$a")case None =>println(s"${key}键对应的值不存在!")}println("*" * 80)val stuList: List[String] = Source.fromFile("scala/data/students.txt").getLines().toListval stuArrayList: List[Array[String]] = stuList.map((line: String) => line.split(","))stuArrayList.map {case Array(id: String, name: String, gender: String, age: String, clazz: String) =>(id,name,age,ger,clazz)}.foreach(println)}
implicit 隐式转换
-
隐式转换函数
// 定义隐式转换函数 // 在需要返回值类型的功能的时候,自动地根据已有隐式转换函数将参数的类型转成返回值的类型implicit def implicitFun1(str: String): Int = {return Integer.parseInt(str)}implicit def implicitFun2(str: String): Int = {return Integer.parseInt(str) + 200}def fun1(s: Int): Int = {return s + 100}println(fun1(100))println(fun1("200"))
-
隐式转换类
注意:implicit使用的地方,不能超过object作用域
def main(args: Array[String]): Unit = {// val demo1 = new Demo12("scala/data/students.txt")// val lineList: List[String] = demo1.show1()//传入字符串时会自动匹配成为一个Demo12的对象,可以调用对象里面的方法val lineList1: List[String] = "scala/data/students.txt".show1()val lineList2: List[String] = "scala/data/score.txt".show1()"jackeylove".f()}implicit class Demo12(path: String) {def show1(): List[String] = {Source.fromFile(path).getLines().toList}def f(): Unit = {println(s"找到一个越南战俘${path}")}}
-
隐式转换变量
def main(args: Array[String]): Unit = {//定义一个隐式转换参数def fun1(a1: Int)(implicit a2: Int): Int = a1 + a2// 定义一个隐式转换变量implicit var i1:Int = 100val i: Int = fun1(200)println(i) // 在没有定义隐式转换时,必须要穿两个数字不然返回的是一个函数 会报错}
-
隐式转换的重要知识点(面试题)
scala中的隐式转换知道吗?请介绍一下。
- scala中的隐式转换,本质上就是将一个类型转换成另一个类型去使用另一个类型中的功能
- scala中的隐式转换分为3种:隐式转换函数、隐式转换类、隐式转换变量
- 隐式转换函数,在使用隐式转换函数返回值类型的功能的时候,可以自动的将参数的类型转成返回值类型进行使用
- 隐式转换类,可以自动的将构造方法的参数类型转成类的类型,将来可以直接使用构造方法中的类型调用类中的方法
- 隐式转换变量,配合函数定义中的隐式转换参数使用,将来调用函数的是,可以不用传入隐式转换参数的值,自动使用对应类型的隐式转换变量,当然也可以手动传入具体的值给隐式转换参数