淘先锋技术网

首页 1 2 3 4 5 6 7

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的经典方法
功能:

  1. 获取字符串指定索引上的值
  2. 创建对象
    写法:
	//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语句
区别:

  1. scala里面的match可以匹配任意类型的数据
  2. scala里面的match没有break,但是具有break的效果
  3. 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的文件打进去,需要添加插件才行。

  1. 关闭idea自动编译
    在这里插入图片描述
  2. 添加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>