scala语言的定义
scala语言是java的一种脚本语言,书写简单方便,使用scala语言首先需要安装scala解释器scala-2.13.3.msi
scala的语法
定义常量、变量
//定义常量,val定义的常量值不能改变,但是val定义的对象,除了地址不变外,对象里面的值还是可以变的
val a = 10;
//定义变量
var b = 10;
//定义常量和变量时指定数据类型
var a : String = "100"
常量、变量范围
scala中在同级区域中,相同名称的变量和函数不能重复定义,但是在子级区域中可以定义相同名称的变量(不推荐使用,容易混乱)
def demo1 = {
val x = 10;
println(x) //10
if(x > 0){
//在子级区内可以再次定义x
val x = 20
println(x) //20
}
}
//在用一1区域内不能定义与函数名相同的变量
val demo1 = "zhangsan" //demo1已被定义成函数名,不能再定义成方法,编译不通过
scala的类型树
├─Nothing
│ └Any
│ ├─AnyVal(基本数据类型)
│ │ └─Boolean
│ │ └─Byte
│ │ └─Char
│ │ └─Double
│ │ └─Float
│ │ └─Int
│ │ └─Long
│ │ └─Short
│ │ └─Unit
│ └─ AnyRef(引用数据类型)
│ ┌────┘ └─Scala classes
│ │ └─Java classes
└─Null
scala函数
普通scala函数
scala使用def定义函数,函数内的参数都必须声明类型,因为scala无法推断函数参数的类型,并且函数内的参数声明都是val,所以不可以给参数赋值
//全写(便于阅读)
def max(x:Int , y:Int):Int = {
//scala的if表达式是有值的,最后一个if里面表达式的值就是if表达式的返回值
if(x > y) x
else y
}
//简写(方便开发)
//1. 对于非递归函数,scala能够推断出函数的返回值类型,可以省略返回值类型
//2. 对于函数体由一个子句组成,可以省略大括号
def max(x:Int , y:Int) = if(x > y) x else y
//调用
val res0 = max(2,3)
---------------------------------------------------------------------------
//无参数,无返回值的函数
//全写
//Unit类型表示无返回值,相当于java里面的void
def greet():Unit = {
println("Hello world")
}
//简写
//函数没有参数可以省略小括号,如果省略了小括号,调用函数时不能加小括号
def greet = println("Hello world")
//调用
greet
嵌套函数
//嵌套函数
def fun1 = {
//函数内部可以继续定义函数
def fun2(str:String) = {
println(str)
}
//定义在函数内部的函数,只能在该函数体内部访问,外部无法访问
fun2("Hello world")
}
函数文本和函数值
函数文本是一段代码,而函数值,则是函数文本的对象,可以通过函数值去调用函数文本
//fun1就是函数值,而他所等于的代码,就是函数文本
val fun1 = (x:String) => x * 2
//调用
val a = fun1("2")
//函数体由多条语句用大括号括住
val fun2 = (x:String) => {
x match {
case "fruit" => println("apple")
case "zhiwu" => println("tree")
case "people" => println("zhangsan")
case _ => println("others")
}
}
---------------------------------------------------------------------------
//应用
//集合遍历
val list = List(1,3,4,7,8,6)
list.foreach((x:Int) => println(x))
//简写1
//1. 参数可以省略数据类型,自动推断
//2. 一个参数的话可以省略括号
list.foreach(x => println(x))
//简写2
//可以使用_代替函数文本中的参数(可以是多个参数,只要这个参数在函数文本的函数体中只出现一次即可),使用_代替后就不需要声明这个参数了(省略了 x => )
list.foreach(println(_))
//简写3
list.foreach(println)
//集合过滤
list.filter(_ > 5)
---------------------------------------------------------------------------
//使用_代替函数文本
val fun3 = (_:Int) + (_:Int)
//相当于,因为x和y只出现了一次,所以可以用_代替,但是编译器无法推断文本函数参数的类型,所以还需要给_标注类型
val fun3 = (x:Int , y:Int) => x + y
偏函数
val fun1 = (_:Int) + (_:Int)
//fun2是fun1的偏函数,固定了其中一个参数,用_代替可变参数
val fun2 = fun1(3 , _:Int)
//调用
fun2(10) //10 + 3 = 13
闭包
闭包是指函数使用函数之外的变量,这个变量被称为自由变量,函数捕捉的是这个变量的本身,而不是这个变量的值,一旦这个变量的值发生变化,函数捕捉到的值同样会发生变化
//利用闭包的特点求集合的和
var sum = 0
val list = List(1,3,5,7,9)
//sum是foreach里面函数文本的自由变量
list.foreach(sum += _)
println(sum)
可变参数
scala允许函数最后一个参数作为可变长度的参数
//在参数类型后面加* ,表示这个参数是可变长度的参数
def echo(args :String*) = {
args.foreach(println)
}
echo("zhangsan","lisi","wangwu","zhaoliu")
高阶函数
//定义一个函数作为参数的函数(函数的类型是(参数类型1,参数类型2...) => 返回值类型)
def containsNeg(nums:Array[Int] , matcher:(Int) => Boolean) = {
var exisits = false
for(num <- nums ; if matcher(num) && !exisits) exisits = true
exisits
}
//调用
//判断数组中有没有偶数
val flag = containsNeg(Array(1,3,5,8,7,9),_ % 2 == 0)
//判断数组中有没有负数
val flag = containsNeg(Array(-1,-3,5,8,7,9),_ < 0)
//对于传入没有参数的函数值,如果你参数类型加了(),那么调用的时候就不能省略()=>,如果你不加就可以省略
//不省略括号
def conter(expression:() => Int)={
try{
val count = expression()
println(count)
}catch {
case e:Exception => e.printStackTrace()
}
}
conter(() => 3 + 5)
//省略括号
def conter(expression: => Int)={
try{
val count = expression
println(count)
}catch {
case e:Exception => e.printStackTrace()
}
}
conter(3 + 5)
函数Curry化
def curriedSum(x:Int)(y:Int)(z:Int) = x + y + z
//add3是由原函数curriedSum演变过来的函数值,由原函数演变过来的函数值需要加上下划线代替剩下的参数
val add3 = curriedSum(3)_
//add4是由add3演变过来的函数值,它不是原函数演变而来的,不需要加下划线
val add34 = add3(4)
//add345是最终结果,值
val add345 = add34(5)
//也可以直接调用原函数
val add345_2 = curriedSum(3)(4)(5)
//当函数只有一个参数的时候可以把小括号换成大括号,大括号一般用来包裹函数文本,这样代码看起来会比较赏心悦目,但是许多情况下,函数不可能只能一个参数,但是如果结合curry化,就可以实现
def containsNeg(nums:Array[Int])(matcher:(Int) => Boolean) = {
var exisits = false
for(num <- nums ; if matcher(num) && !exisits) exisits = true
exisits
}
val flag = containsNeg(Array(1,3,5,7,9)){
x => (x % 2 == 0)
}
scala脚本
scala是java一种脚本语言,我们可以编写一个scala脚本文件放到命令中去执行
//不带参数的脚本文件
//编写一个hello.scala文件
println("Hello world")
//运行
//在hello.scala文件目录下打开cmd命令,输入以下命令,运行脚本文件
scala hello.scala
---------------------------------------------------------------------------
//带参数的脚本文件
//编写脚本,参数会按顺序传入到args数组中,注意与java不同,scala的数组下标获取值的方式是小括号
println("Hello " + args(0))
//运行
//传入的参数直接放在命令的后面即可
scala hello.scala zhangsan
scala的运算符
demo:
//scala里面的所有运算符都是方法,注意:scala里面不支持 ++ 和 --,但支持 += ,-=类型的操作
var a = 1 + 2
var b = 1.+(2)
var c = 4 * 2
var d = "a" * 3 //"aaa"
//生成1-10的数组的两种写法,to是一个方法,默认步长为1,因为它只有一个参数,所以可以省略.和()
var arr1 = 1 to 10
var arr2 = 1.to(10)
//设置步长为2
var arr3 = 1 to 10 by 2
scala的函数和方法
区别:
- 函数不需要通过对象调用
- 方法需要通过对象调用
demo:
//使用min函数需要导包,_是通配符,类似于java中的*
import scala.math._
//求最小值,min是函数,不需要任何对象,可直接调用
min(1,2)
//将数字1转为String类型,toString是方法,在scala中,使用to + 类型去强转类型,与java不同
1.toString()
1.toString //不带参数的方法可以省略括号
1 toString //方法与对象之间还可以省略点
apply方法
scala的经典方法
功能:
- 获取字符串指定索引上的值
- 创建对象
写法:
//apply方法的特点是使用它可以省略apply
//获取字符串上的值
"hello".apply(1) //e
"hello" apply 2 //l
"hello"(0) //h
//创建一个BigInt对象
BigInt.apply("1233445455")
BigInt apply "1233445455"
BigInt apply "1233445455"
//创建一个数组对象
Array.apply(1,2,5,67,778,22)
Array apply (1,2,5,67,778,22)
Array(1,2,4,67,778,22)
使用apply方法创建自定义对象
//首先我们的自定义对象是伴生对象
class Dog(var nam:String , var age:Int) {
def eat(){
println("狗吃骨头")
}
def work(): Unit = {
println("狗看门")
}
}
object Dog{
//apply方法需要定义在object对象里面
def apply(nam: String,age: Int) = new Dog(nam,age)
//unapply是将对象拆分
def unapply(dog: Dog) = Some(dog.nam , dog.age)
}
//创建自定义对象dog
var dog = Dog("Bobo",2)
dog.work()
dog.eat()
//拆分对象
var Dog(a,b) = dog
printf("a: %s , b : %d" , a , b)
条件表达式
在scala中条件表达式是有值的,它的值便是最后一个表达式的值
var a = 10
//注意:else必须跟if的最后一条子句或者是子句的花括号保持在同一行,否则scala识别不到else
//注意:if条件表达式外面的小括号不能省略
var i = if( a > 5) 6 else 7 //i=6
//如果if子句有多条语句,需要用{}包裹
var i = if( a > 5) { 6; 10} else 7 //10
打印输出
//打印不换行
print("aa")
//打印换行
println("aa")
//c语言风格的打印,%s和%d代表占位符,%s是String类型的参数的占位符,%d是Int类型的参数的占位符,%f是浮点数的占位符
printf("name is %s , age is %d", "Tom", 12)
控制台输入
循环语句
//注意:while条件表达式外面的小括号不能省略
//注意:在java中例如:a=10,这个表达式的值是10,但是在scala中这个表达式的值是(),所以在做流读写的时候要注意
while(条件表达式){
//循环体
}
do{
//循环体
}while(条件表达式)
//x不需要声明,to包含1和10
for ( x <- 1 to 10){
//代码逻辑
}
//until包含1不包含10
for (x <- 1 until 10){
//代码逻辑
}
//高级for循环,类似于java的嵌套循环,先循环最后面的一个表达式,然后依次往外
for(x <- 1 to 10; y <- 1 to 10){
}
//带守卫条件的高级for循环,满足的才循环,不满足不循环
for(x <- 1 to 10; y <- 1 to 10 if x != y){
}
//多个守卫条件也是用;隔开,if开头,多个守卫条件之间是与关系
for(x <- 1 to 10 if x % 3 == 0;if x % 2 == 0){
}
//也可以这么写
for(x <- 1 to 10 if x % 3 == 0 && x % 2 == 0){
}
//条件表达式比较多也可以这么写,用大括号代替小括号,好处就是不需要用;去分隔
for{
x <- 1 to 10
y <- 1 to 10
//在for语句里面定义的变量默认就是val,不需要你加val
k = 10
if x != y
}{
}
//yield将每个循环最后一个表达式的值集中起来,产生一个新的集合(Vector类型的集合),通常与for循环的守卫条件结合使用,过滤不需要的值
var list = for ( x <- 1 to 10) yield {
x % 3
}
//foreach循环遍历数组,foreach是高阶函数,参数是函数
//全写:匿名函数
(1 to 9).foreach((a:Int) => println(a))
//简写1:一个参数可以使用_代替
(1 to 9).foreach(println(_))
//简写2:一个参数可以省略
(1 to 9).foreach(println)
中断循环
import scala.util.control.Breaks._
for( x <- 1 to 10){
//break是函数,因为没有参数,所以可以省略小括号
break;
}
//scala对于break的支持并不是很好,你可以用以下方法代替它
var flag = true;
for( x <- 1 to 10 if flag){
if(x > 6){
flag = false
}
}
函数
函数都是有返回值的
定义一个函数名为add,参数a和b,都是Int类型,返回值为Int类型:
//完整体
def add(a:Int , b:Int):Int = {
var c = a + b;
return c;
}
//1.省略返回值类型,(注意:递归函数必须指定返回值类型,不能省略)2.函数里面只有一条语句,可以略大括号,3. 省略return,函数体里面最后一个表达式的值便是函数的返回值
def add1(a:Int , b:Int) = a + b
//2. 省略括号,当函数没有参数,定义时可以不加括号
def getA = a;
//调用,注意:定义函数时不加括号,调用时也不能加括号
getA
//设置函数默认值
def add2(a:String = "[",b:String,c:String = "]") = a + b + c
//调用默认值函数
add2(b="hello")
//变长参数
def add3(arr:Int*) = for(x <- arr)println(x)
//调用
add3(1,2,3,4,5);
//传入数组的话后面要加上:_*符号
add3(1 to 10:_*);
//匿名函数
//方法一
def multipe(n:Int) = 2 * n
val f = multipe _
//方法二
val f2 = (n:Int) => 2 * n
//调用匿名函数
Array(1,2,3,4).map(f)
//函数作为参数传递,作为返回值返回
def sjcl(a:Int , b:Int, f1:(Int , Int) => Int , f2:(Int , Int) => Int): (Int) => Int ={
var m = 0;
if( a > b){
m = f2(a , b)
}else{
m = f1(a , b)
}
(x:Int) => m * x
}
//调用
sjcl(3,4,(a:Int , b:Int) => a + b , (a:Int , b:Int) => a - b)(10)
}
过程
过程都没有返回值
//过程不需要定义返回值类型,也不需要=,但是大括号不能省略
def add(a:Int , b:Int){
println(a + b)
}
控制抽象
//没有参数括号可以省略
def newThread(block : () => Unit){
new Thread(){
override def run(){
block();
}
}.start()
}
延时计算
//lazy只适用于常量的定义,不会立即计算,只有当常量被调用的时候才会计算
lazy val a = 1 + 2
a
异常
//抛出异常,终止操作,与java不同,这个是写在逻辑代码中的,不是方法上
throw new RuntimeException("自定义")
//捕获异常
try{
}catch{
//每一个catch子句都会按顺序尝试一遍,如果找到了,就不会继续往下找了,如果都不是,那么 try-catch 将终结并把异常上升出去。
//抛出指定异常
case e:java.io.IOException => e.printStackTrace()
//抛出自定义异常
case _:Exception => println("xxxx")
}finally{
//代码终结之前一定会执行
}
//捕获异常也会返回值:注意finally一般是用作关闭流,文件什么的,最好不要在finally中返回数据
val str = try{
throw new RuntimeException("有异常")
"lisi"
}cath{
case e:Exception => "zhangsan"
}
数组
定长数组
//定义一个长度为10,类型是Int的数组
var arr1 = new Array[Int](10);
//查看数组的长度
arr1.length
//定义一个数组,里面有一个元素为10
var arr1 = Array(10)
//全写:apply可以省略
var arr1 = Array.apply(10)
var arr2 = Array("hello","world");
//根据索引获取数组的值
arr2(0)
//head方法,拿到第一个值
arr2.head
//tail方法,拿到除第一个元素以外的其他元素组成的新数组
arr2.tail
var arr3 = Array(1 to 10:_*)
//filter方法是过滤器,里面的参数是函数,过滤偶数,产生一个新的数组,_代表通配符,代表arr3里面的每个元素,map里面必须要做运算,否则只写_会报错,因为map的参数类型是函数,这个是简化版
var newArr = arr3.filter(_ % 2 == 0).map(_ * 1)
//filer,map完整版
var newArr = arr3.filter((x:Int) => x % 2 == 0).map((x:Int) => x * 1)
//计算总数
arr3.sum
//找出最大值
arr3.max
//找出最小值
arr3.min
//排序
import scala.util.Sorting._
//对原数组进行从小到大排序,不会产生新的数组
quickSort(arr3)
//数组转字符串
arr3.mkString //直接将所有元素拼在一起作为字符串返回
arr3.mkString(",") //将所有元素拼在一起,用","隔开,作为字符串返回
arr3.mkString("[",",","]")//将所有元素拼在一起,用","隔开,添加前缀"[",后缀"]",作为字符串返回
//拉链操作(配对操作,元素个数对不上的直接砍掉)
var husband = Array("西门庆","牛郎","董永","许仙");
var wife = Array("潘金莲","织女","七仙女","白素贞");
husband.zip(wife)
//左聚合
var result = (1 to 5).reduceLeft(_ * _) //相当于1 * 2 * 3 * 4 * 5 = 120
//右聚合
var result2 = (1 to 5).reduceRight(_ - _) //相当于 5 - (4 - (3 - (2-1))) -> 5-4+3-2+1 = 3
变长数组
//先导包
import scala.collection.mutable.ArrayBuffer;
//创建变长数组
var buf = ArrayBuffer[Int]();
//变长数组添加元素,在末尾添加
buf += 1
//变长数组删除元素,减少最后一个元素为1的
buf -= 1
//在末尾追加1-10的元素,加的是数组拆分出来的元素,不是整个数组
buf ++= 1 to 10
//从末尾开始计数移除5个元素
buf trimEnd 5
//从开头开始计数移除2个元素
buf trimStart 2
//在索引0前面插入1和2,inset第二个参数是一个变长参数,inset操作效率低,因为需要平移操作
buf.insert(0,1,2)
buf.insert(0,1 to 5:_*)
//移除索引为1的值,并返回
var m = buf.remove(1)
//产生一个新的定长数组
var newArray = buf.toArray
多维数组
var arr1 = new Array[Array[Int]](3);
var arr2 = Array(Array(1,2),Array(1,2,3,4));
//3行4列,没有上面的灵活,数组里面的数组长度固定
var arr3 = Array.ofDim[Int](3,4);
//取值
arr2(0)(1)
变长数组与java的list集合之间的交互
//导包
import scala.collection.JavaConversions._
import scala.collection.mutable.ArrayBuffer;
var buf = ArrayBuffer(1,2,3,4,5,6,76,8)
//java集合与变长数组之间是隐式转换
var list:java.util.List[Int] = buf
映射
var map = Map("name" -> "zhangsan" ,"age" -> 23 , "address" -> "beijing");
map("name")
var newMap1 = map + ("name" -> "lisi")
var newMap2 = map - ("name")
//注意:scala的map默认是scala.collection.immutable.Map类型,其值不可改变,如果想要可以改变得使用scala.collection.mutable.HashMap类型
var map2 = scala.collection.mutable.HashMap("name" -> "zhangsan" ,"age" -> 23 , "address" -> "beijing")
var map3 = new scala.collection.mutable.HashMap[String,Any]()
//添加一个映射关系
map3("name") = "zhangsan"
//一次添加多个映射关系
map3 += ("age" -> 23 , "address" -> "nanjing")
//可以根据key移除映射关系,参数是可变长度的参数
map3 -= ("age","address")
//遍历
for((k,v) <- map2)printf("key: %s , value: %s\n",k,v)
//遍历key
for(k <- map2.keySet)printf("key: %s\n",k)
//遍历value
for(v <- map2.values)printf("value: %s\n",v)
//key和value对调,有风险,可能会有相同的value
var newMap3 = for((k,v) <- map2)yield (v,k)
元组
scala最多支持22个元素,最少支持1个,映射是一个最简单的二元元组
元组与集合数组不同的是他可以放入不同数据类型的数据,元组的下标是从1开始,元组的类型取决于元组中元素的数量和每个元素的类型,scala目前只支持到22个元素的元组
//创建元组
var t = (1,"Tom",12);
//访问元组里面的元素,索引从1开始
t._1 //1
t._2 //Tom
//将t中的元素分别赋值到a,b,c上
var (a,b,c) = t;
类
普通类
import scala.beans.BeanProperty
//scala中所有的类都是公有的,不可以对类进行修饰
class Person{
//scala中定义变量必须要赋值初始值,注意:使用_代表初始值时,必须要为该字段声明类型,并且是全局变量,不是常量,也不是局部变量
var id:Int = _;
var name:String = _;
@BeanProperty var age:Int = _;
}
Person p = new Person();
//公有属性scala会自动生成get和set方法,get方法的名字就是属性的名字,set方法的名字是属性名_=,并且属性变成私有
p.name_=("zhangsan")
p.name
//BeanProperty注解可以生成我们熟悉的get和set方法,并且它只能修饰公有属性
p.setAge(23)
p.getAge
#编译scala文件,编译成功会生成一个.class文件
D:\aa\scala> scalac Person.scala
#查看类结构
D:\aa\scala> javap -private Person.class
编译后生成的类结构:
public class Person {
//可以看到所有在scala中定义的公有属性全部变成了私有属性
private int id;
private java.lang.String name;
private int age;
//公有属性都会默认生成对应的scala风格的get和set方法
public int id();
public void id_$eq(int);
public java.lang.String name();
public void name_$eq(java.lang.String);
public int age();
public void age_$eq(int);
//使用BeanProperty注解可以生成java风格的get和set方法
public void setAge(int);
public int getAge();
public Person();
}
抽象类
抽象类使用abstract修饰,里面可以有抽象方法,也可以有非抽象方法,抽象类不能实例化,与java不同,抽象类中的抽象方法不需要abstract来声明
abstract class animal{
def eat():Unit
def play():Unit
final def getType = println("it is animal")
}
//使用extends关键字来继承抽象类的公有属性,extends也可以扩展其他普通类,但是不能扩展用fianl修饰的最终类,换句话说final修饰的最终类没有子类
class Dog extends animal {
//使用overrideoverride来重载父类方法,注意父类中final修饰的方法不能被重载
override def eat():Unit = {
println("狗吃骨头")
}
}
//注意:当父类的构造方法中有参数,子类想要想要重写父类的参数时,参数名不能跟父类相同
abstract class Animal(var name:String) {
def eat();
def work();
}
//将子类Dog的nam参数值传递给父类Animal的name参数
class Dog(var nam:String , var age:Int) extends Animal(nam) {
override def eat(){
println("狗吃骨头")
}
override def work(): Unit = {
println("狗看门")
}
}
构造方法
主构造器
//主构造是写在类的后面的
class Person(val id:Int , var name:String,var age:Int){
}
编译后生成的类结构:
public class Person {
private final int id;
private java.lang.String name;
private int age;
//val声名的常量只能构造出get方法
public int id();
public java.lang.String name();
public void name_$eq(java.lang.String);
public int age();
public void age_$eq(int);
//构造器
public Person(int, java.lang.String, int);
}
辅助构造
class Person{
var id:Int = _;
var name:String = _;
var age:Int = _;
//主构造参数需要用var,辅助构造不需要
def this(name:String){
//使用辅助构造器必须要先调用主构造器
this()
this.name = name;
}
def this(name:String , age:Int){
this(name)
this.age = age;
}
}
编译后生成的类结构:
public class Person {
private int id;
private java.lang.String name;
private int age;
public int id();
public void id_$eq(int);
public java.lang.String name();
public void name_$eq(java.lang.String);'
public int age();
public void age_$eq(int);
//主构造
public Person();
//辅助构造
public Person(java.lang.String);
public Person(java.lang.String, int);
}
单例对象
scala中没有静态和非静态之说,想要实现Java中的静态效果,可以定义一个单例对象
object Util{
val name = "zhangsan";
//对象里面有一种特殊的方法名字叫apply,他可以通过对象直接调用,省略apply方法名,如Util("lisi")
def apply(str:String){
println(str);
}
def prntHe {
println("Hello world");
}
}
编译后生成的类结构:
//Util.class
public final class Util {
public static void prntHe();
public static void apply(java.lang.String);
public static java.lang.String name();
}
//Util$.class(单例模式)
public final class Util$ {
//定义一个公有的不能修改的对象
public static final Util$ MODULE$;
private final java.lang.String name;
public static {};
public java.lang.String name();
public void prntHe();
public void apply();
//私有构造方法
private Util$();
}
伴生对象
object定义的对象和class定义的类名字必须相同,且在同一个scala文件中才行
object Util{
val name = "zhangsan";
def prntHe {
println("Hello world");
}
}
class Util{
var age = 23;
def prntAge {
println("my years old is " + age)
}
}
编译后生成的类结构:
public class Util {
private int age;
public static void prntHe();
public static java.lang.String name();
public int age();
public void age_$eq(int);
public void prntAge();
public Util();
}
//单例模式类中没有age和prntAge成员
public final class Util$ {
public static final Util$ MODULE$;
private final java.lang.String name;
public static {};
public java.lang.String name();
public void prntHe();
private Util$();
}
包对象
包对象主要是scala用来封装工具函数用的,通过包对象是不需要使用对象来调用工具函数的
package object Util {
def BlankNUll(str:String):String = {
if(str == ()){
""
}else{
str.toString
}
}
}
工具函数的调用
//首先导包
import Util._
object Person {
def main(args: Array[String]): Unit = {
//调用工具函数不需要使用对象来调用了
println(BlankNUll("zhangsan"))
}
}
scala定义main方法
object Person {
//main方法只能定义在对象中
def main(args:Array[String]){
}
}
接口
trait PersonDao {
//这个表示如果有类想要实现这个接口,那这个类就必须得是Person的子类
this:Person =>
def selectName(id:String);
//接口中的方法可以有方法体,有方法体的方法都不是抽象方法,JVM虚拟机会解析成静态方法
def getNJ():String = {
"一年级"
}
}
//接口实现
//实现一个接口,使用extends关键字即可
class TruckDaoImpl extends TruckDao {
}
//实现两个以上接口,第一个接口使用关键字extends,其他接口使用with
class TruckDaoImpl extends TruckDao with TruckDao2 {
}
//接口还可以扩展接口
trait TruckDao2 extends TruckDao {
}
instanceof
instanceof方法只适合引用类型的判断,不适合基本类型
//Dog是继承Animal的
var dog = new Dog()
//判断dog是不是属于Animal类型(模糊判断,包括其父类都满足条件)
if(dog.isInstanceOf[Animal]){
//将dog对象的类型强转为Animal
dog.asInstanceOf[Animal]
}
//得到对象的类
dog.getClass //class Dog
//判断类型的方法二(精准判断,只有其本身的类型才能满足条件)
if(dog.getClass == classOf[Dog]){
}
读取文件
import scala.io.Source;
object Person {
def main(args: Array[String]): Unit = {
val s = Source.fromFile("D:\\aa\\aa.txt","UTF-8")
//lines是迭代器,遍历完成后就会失效
val lines = s.getLines()
for(line <- lines){
println(line)
}
}
操作符
- 中置操作符
1 + 2 1 to 10 ....
- 单元操作符
1 toString - a + b !true ~ 1 (按位取反) ...
- 赋值操作符
a += b b -= c ...
- 右结合操作符(冒号,从右往左算)
//先定义变量为String类型,在定义变量名 var a:String = ""; //先定义list为集合,然后在list集合的最前面插入2,再在前面插入1 var list = 1::2::Nil
集合
scala中集合分为可变集合和不可变集合,可变集合封装在scala.collection.mutable.*下,不可变集合封装在scala.collection.immutable.*下,默认情况下,scala调用的是不可变集合,不可变集合不能定义为val
List
//创建一个空集合
var list = Nil
//创建一个带初始值的集合1,2
//方法一,注意:不可变集合list不能定义为val
var list = 1::2::Nil
//方法二
var list = List(1 , 2)
//添加元素,产生新集合,注意 :: 是右操作符,即右边的对象调用的方法::
var newList = 5 :: list //5,1,2
//全写
val newList = list.::(5)
//集合的模式匹配
newList match {
case Nil => 0
//::将集合拆分成头部和尾部分别赋值给a和b
case a::b => printf("集合首部:%d ,集合尾部:%s" , a ,b.toString())
}
val list1 = Array(1,3,5)
val list2 = Array(2,4,6)
//集合叠加
val list3 = list1 ::: list2 //1,3,5,2,4,6
//集合默认是不可变集合,创造可变集合需要导包
import scala.collection.mutable.ListBuffer
var list = ListBuffer(1,2)
//添加集合元素在集合后面
list += 3
//添加多条集合在集合后面
list ++= list
//从前往后匹配,匹配后移除元素便不再继续匹配
list -= 4
//注意:构造一个可变的空集合需要加类型
var list:ListBuffer[Int] = ListBuffer[Int]()
println(list == Nil) //true
//折叠,左折叠,跟左聚合的区别就是可以设置初始值
var m = list.foldLeft(0)(_ - _)
//折叠,右折叠,跟右聚合的区别就是可以设置初始值
var m = list.foldRight(0)(_ - _)
Set
//可变集合Set和不可变集合Set都叫Set,我们可以对他起别名用于区分它们
import scala.collection.mutable.{Set => SSet}
//与java一样,Set集合是无序的,不可重复的,注意,不可变集合set不能定义为val
val set = SSet(1,23,4,6,7,4)
//可变集合+=就是添加元素的方法,不可变集合+=就是一个普通的方法,添加元素后产生新的集合并赋值给原来的集合
set += 5
set.add(8)
//求两个集合的并集
var newSet1 = set | set2
//求两个集合的交集
var newSet2 = set2 & set
//求两个集合的差集
var newSet3 = set2 &~ set
//拉链配对,zip多余的砍掉
var newSet4 = set.zip(set2)
//zipAll,set少,用-1代替;set2少,用-2代替
var newSet5 = set.zipAll(set2 , -1, -2)
//集合跟索引的配对
var newSet6 = set.zipWithIndex
Map
//注意,不可变集合map不能定义为val
var map = Map[Int,String]()
map += (1 -> "zhangsan")
var map2 = Map(1 -> "张三" , 2 -> "lisi")
import scala.collection.mutable.{Map => MMap}
va1 map = MMap[Int,String]()
map += (1 -> "zhangsan")
集合常用方法
模式匹配
相当于java里面的switch case语句
区别:
- scala里面的match可以匹配任意类型的数据
- scala里面的match没有break,但是具有break的效果
- scala里面的match有返回值
val a = 1
val t = a match {
case 1 => "水果"
case 2 => "动物"
case 3 => "植物"
case 4 => "人"
//_代表可匹配任意值
case _ => "未知"
}
var x = '9'
x match {
case '+' => println("++++")
case '-' => println("----")
//带守卫条件
case _ if(Character.isDigit(x)) => println("is number")
case _ => println("...")
}
//匹配数据类型,需要匹配的变量的数据类型需要是判断类型的超类
var k:Any = 2;
k match {
case a : Int => println("is Int")
case b : String => println("is String")
case _ => println("...")
}
//匹配数组
var arr = Array(3,2)
arr match {
//匹配数组中是否有0
case Array(0) => println("数组里面有0")
//匹配数组是否是2个元素的数组,如果是把数组对应赋值到相应的x和y
case Array(x,y) => printf("x : %d , y : %d\n" , x , y)
//匹配数组是否以0开头
case Array(0 , _*) => println("该数组以0开头")
case _ => println("...")
}
//变量声明模式匹配
var (a, b, c) = (1, 2, 3)//a=1 , b=2 , c=3
//for循环的模式匹配
val map = Map("name" -> "zhangsan" , "age" -> 23 , "address" -> "beijing")
for((k , v) <- map){
printf("key: %s ,value: %s \n" , k , v.toString)
}
样例类
样例类主要用于模式匹配,内置了apply和unapply方法,还有实现了串行化等接口
//样例类开头以case声明
case class myDefineArray(n:Int*) {
}
//使用样例类
val myMap = myDefineArray(4,5,6)
myMap match {
case myDefineArray(x) => printf("自定义数组中有一个值为%d\n" ,x)
case myDefineArray(x, y) => printf("自定义数组中有两个值为%d , %d\n" ,x , y)
case myDefineArray(x, y, z) => printf("自定义数组中有三个值为%d , %d , %d\n" ,x , y , z)
case _ => printf("...")
}
密封样例类
密封样例类要求子类和父类都必须在同一文件中,作用不大,主要是为了避免滥用继承
//密封类的父类要用关键字sealed修饰
sealed abstract class Fruite {
}
case class Apple(name:String) extends Fruite {
}
case class Banal(name:String) extends Fruite {
}
偏函数
所谓偏函数,其实就是自定义match函数
//PartialFunction名字是系统定义的,我们不能随便更改,中括号里面是输入参数类型和返回值类型
val f:PartialFunction[Char , Int] = {
case '+' => 1
case '-' => 2
case _ => 0
}
println(f('+'))
泛型
在java中是用<T>
定义泛型,在scala中是用[T]
定义泛型
//泛型类
class Pair[T ,S](param1:T , param2:S) {
}
var pair1 = new Pair[String , Int]("zhangsan" , 12);
var pair2 = new Pair("zhangsan" , 12);//scala自己推断
//泛型方法
def getMiddle[T](arr : Array[T]):T = {
arr(0)
}
var arr3 = Array(1,4,56,7,712,1,2)
println(getMiddle(arr3))
//设置泛型的界限
//泛型T是Animal的子类或其本身
def isAnimal[T <: Animal](animal: T){
println("是动物")
}
//泛型T是Dog的父类或其本身
def isAnimal[T >: Dog](animal: T){
println("是动物")
}
//泛型T可以隐式转化为Long类型
def isLong[T <% Long](k : T){
println("是长整形")
}
隐式转换
隐式转换函数:使用implicit修饰的具有一个参数的函数就是隐式转换函数
//将Int类型可以隐式转换为自定义的myDefineArray类型
implicit def int2myDefineArray(n : Int) = myDefineArray(n)
def getArray(arr:myDefineArray){
println("是自定义数组")
}
getArray(10)
使用maven将scala打成jar包的注意事项
通常情况下idea自动编译可以将scala代码编译成class文件,但也有编译不及时的情况,而且自动编译急耗内存,对于大型的项目一般不会使用自动编译的。使用maven打成jar包,通常不会将scala的文件打进去,需要添加插件才行。
- 关闭idea自动编译
- 添加maven插件,使maven支持自动编译
<build> <sourceDirectory>src/main/java</sourceDirectory> <plugins> <!--打包java1.8--> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <!--打包scala--> <plugin> <groupId>net.alchim31.maven</groupId> <artifactId>scala-maven-plugin</artifactId> <version>3.2.2</version> <configuration> <recompileMode>incremental</recompileMode> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>testCompile</goal> </goals> </execution> </executions> </plugin> </plugins> </build>