scala 的数据类型不用显示定义,直接用 var 或 val,会自动类型匹配
val i = 10
如果要指定类型 (没有 Int i = 10)
val i:Int = 10
scala 字符串 *** 作
可以支持 s" ${name}" 对变量取值填充,而不用+拼接
多行字符串
println( s""" | select * from table where id = '$count'; | |""".stripMargin)
val a:Double = 10/3 输出 3.0 val a:Double = 10.0/3 输出 3.3333333333333335 // 10(int)/3(int) 得到结果还是int类型的3; 3转为Double类型所以是3.0 // 10.0(Double)/3 得到结果就是Double所以是 3.3333333333333335
scala 和 python 都没有 i++; 只能老实写 i += 1;
选择结构
if 基础语句同java一致。
没有三目运算符,if(condition) a else b // {}里只有一行可以省略
循环结构
while 语句与java一致。while 没有返回值,可以显式写出返回 Unit,但Unit也表示没有返回,类似void。
for 语句比java的fori和foreach都有差别
for (i <- 1 to 10){ // 1 到 10循环,默认步长1,左闭右闭 } // 等价 写法不同: for (i <- 1.to(10)){} // 指定步长2 for (i <- 1.to(10,2)){} // 指定步长2等价于 for (i <- 1.to(10) by 2){} // 逆序输出 for (i <- 1.to(10) reverse){} // for (i <- 10.to(1) by -1){} // 左闭右开 for (i <- 1.until(10)){ // 只循环1到9 } // Range(1,10) 底层是 1 until 10 for (i <- Range(1,10)){ // 只循环1到9,与 1.until(10) 等价 } // Range 也可指定步长,步长不能为0 for (i <- Range(1,10,2)){}
for 循环才有的循环守卫
for(i <- 1 to 10 ){ if (i%2 == 0){ println(i) } } // 等价于 for(i <- 1 to 10 if (i%2 == 0)){ println(i) }
注意:while循环没有循环守卫,或者说while循环自带循环守卫。将条件写在while循环条件中和写在循环体中的逻辑不是等价的。写在循环条件如果遇到条件false就会结束整个循环。
即:
for循环的循环守卫为false时是跳过当此循环继续下一次循环。
while 里的条件为false则跳出整个循环。
for 循环返回值
val unit = for (i <- 1 to 5) { i * i } println(unit) // 只输出一个空括号,说明没有返回值 // 配合使用yield可以有返回值 val ints = for (i <- 1 to 5) yield i * i println(ints) // 输出 Vector(1, 4, 9, 16, 25) // 其实是在循环体的返回值,再根据循环组成集合
跳出循环
只有break(),没有continue;和break; 通过breakable {}的位置控制跳过当此循环还是跳出整个循环。
import scala.util.control.Breaks._
使用break()【注意是break()不是break;关键字】
需要配合 breakable {} 语句块中,才能使用break(),且写法决定了跳出还是跳过
// 跳出整个循环 breakable{ for(i <- 1 to 5 ){ if (i == 3) break() println(i) // 输出 1 2 } } // 跳过本次循环 for(i <- 1 to 5 ){ breakable{ if (i == 3) break() println(i) // 输出 1 2 4 5 } } // 输出 1 2 4 5 等价于循环守卫写法 for(i <- 1 to 5 if i != 3){ println(i) } // 跳出整个循环 breakable { while (n < 10){ // ... break() n += 1 } } // 跳过本次循环 while (n < 10){ breakable { // ... break() n += 1 } }
java中非基础类型比较要用equals(),但scala比较都用 ==
scala 定义函数/方法
// 定义方法甚至可以嵌套,可以定义在main方法中
def 函数名/方法名(x:Int, y:Int):Int = { // ... } // 使用 def关键字定义 // (x:Int, y:Int) 表示参数(个数和类型) // :Int = 有返回值且返回值类型为Int // {} 中是方法体 // 方法体中每最后一个逻辑行就是返回内容,不需要使用return;
// 如果方法体只有一行,大括号可以省略 def func(x:Int, y:Int):Int = x + y
// 没有返回值 def func(x:Int, y:Int):Unit = { println(x + y) } // 或 def func(x:Int, y:Int) { println(x + y) }
// 没有参数 def func() { println("") } println(func) // 调用时也可以不要括号 // 或 def func { println("") } // 没有参数但有返回值 def func :Int ={ val i = 10 i }
// 默认参数 def fun(x:Int = 0):Unit = { println(x) } fun(1) fun() // 可以不传入参数,则方法输出默认值
// 命名参数. 传递参数可以不按方法定义参数的顺序,而在调用的时候显示写出 形参 = 值 指定传参 def func(x:Int, y:Int) { println(x + y) } func(1,2) func(y = 2,x = 1)
// 可变参数.参数长度不固定,通过*号标识为可变参数.可变参数只能写在参数列表的最后 def sum(num:Int*):Int = { var res = 0 for(i <- num){ res = res+i } res } sum(1, 2, 3, 4,5) // 传入任意个数的Int类型 sum(1 to 5:_*) // 通过传范围,要加上:_* sum(Range(1,6):_*) sum(Array(1, 2, 3, 4, 5):_*)
// scala def factorial(n:Int):Int = { if (n == 1) 1 else n * factorial(n - 1) } // java public static Integer factorial(Integer n) { if (1 == n) return 1; else return n * factorial(n-1); }
无论javascala 递归都容易 StackOverflowError
factorial(10000);
递归的每一次调用都会保存当前局部变量,多次递归时容易造成栈内存溢出.
尾递归
如果一个函数中递归调用出现在函数的末尾(返回值为递归调用),我们称这个递归函数是尾递归的(需要编译器可以讲这样的递归优化为尾递归,java除外).
尾递归为什么不会栈内存溢出
函数递归调用出现在调用函数的尾部, 因为是尾部, 所以根本没有必要去保存任何局部变量.而直接把当前的运算结果放在参数里传给下层函数.
@tailrec def factorial(n:Int,acc:BigInt):BigInt = { if (n == 1) acc else factorial(n - 1,n * acc) } // 4 , 5 * 1 // 3 , 4 * (5 * 1) // 2 , 3 * (4 * (5 * 1)) // 1 , 2 * (3 * (4 * (5 * 1))) // return acc = 2*3*4*5*1 // 就是把每次累乘的结果也作为参数,继续往下传递,和当前调用再也没有关系,本次方法就能结束,释放堆栈,jvm及时回收 factorial(5,1)
java尽量能用循环就别用递归。
java没有@tailrec申明尾递归,但是也可以写成尾递归。尾递归的本质还是循环。
Scala Class
classobjectcase classcase objecttrait
class User { // 申明类,同时显示指定无参构造器为主构造器。 // 这种和java没太多差别 var name:String = _ //String类型的默认值,也就是null var name2:String = null // 也会提示使用 _ var age:Int = _ // Int 类型的默认值 0 var age2:Int = null // 不会有其他提示 @BeanProperty var size:Int = _ // getter/setter }
// new 创建对象也是调用构造方法,同样,如果声明了有参构造,无参构造也需要显示申明后才能使用
// 注意 一定要有val 修饰参数,否则只是个形参? class User(val name:String,val age:Int) { // 申明类,同时指定主构造器。 var address:String = _ var number:Int = _ def this() = def this(name:String, age:Int, address:String) { // 附属构造器,可以重载多个附属构造器 this(name,age) // 附属构造器的第一行必须调用其他构造器 this.address = address; } }
继承
// 继承父类的时候,可以选择父类的某一构造器继承.以下两个都是子类 class User3(val name3:String,val age3:Int,var number:Int) extends User2(name,age){} class User4(val name3:String,val age3:Int,var number:Int) extends User2 {} // 同样new 子类的时候会先触发父类对象创建 // 子类可以重写父类的属性/方法 override val name:String = _ class User4(override val name:String) extends User2 {} override def toString: Unit = println(s"$name"):Unit
抽象类
abstract class Person { def eat } // 抽象类不能被实例化。 // 也可以有匿名内部类
伴生(伴生类,伴生对象)
class User
object User
名称相同的class 和 object 互为伴生
object User 是 class User 的伴生对象
class User 是 object User 的伴生类
// 对 object 中的内容都是静态的可以被其他地方(不止伴生类)直接调用
// 是否可以理解为,object User 就是将 class User 中static静态成员都归到object User中统一书写,那么 class User 可以直接使用 object User中的内容,但object User 要使用 class User中的内容必选先创建对象?所以scala中没有static,而是用伴生对象起到同样的作用
1. 使用 val user = new User() 会执行 class User里 1.1 使用 user() [对象()] 会执行 class User 里的apply()方法[只能叫apply] 2.1 使用 User.xxx 会执行 object User (静态的) 2.2 直接使用 User() 还会执行 object User 里的apply()方法[只能叫apply] 源码中有很多,在 object 的apply方法中又new User() 创建对象,于是使用对象时可以有两种方式:
val arr1 = new Array[Int](5) val arr2 = Array[Int](5)
在 object 的apply方法中又new User() 创建对象,apply方法又是静态的,那么这是单例模式?–发现并不是
只是人家没有用apply来做单例而已,而是每次调用都new User.
如果我直接让apply返回user 而不是 new User,就能实现单例了。不过一般单例还是专门用getInstance方法来来创建
object User { private val user = new User() def getInstance():User = user def apply(): User = { // user // 如果像这样返回user,那么apply方法就能创建单例 new User() } } class User { var name:String = _ } // test 测试是否单例 val user1 = User.getInstance() println(user1) val user2 = User.getInstance() println(user2) println(user1 == user2) // true // 当然如果直接让apply返回user ,那就可以这样得到单例 val user1 = User() println(user1) val user2 = User() println(user2) println(user1 == user2) // true
case class
样例类(case class)适合用于不可变的数据。
在实例化case class类时,不需要使用关键字New,case class类编译成class文件之后会自动生成apply方法,这个方法负责对象的创建。
已经覆写了equals、hashcode、toString、序列化等.
在对对象模式匹配时,会自动提取到对象中的属性。如case Email(email, title, _ ),要匹配email和title属性
case class 定义时必须带上参数列表(case object 必须不能加参数列表)
Scala的import还支持导包时候取别名
import com.gargantua.{HomeWork => HW}
当Scala要调用Java API,而Scala的语法无能识别Java的类型,使用隐式转换需要导入
import scala.collection.JavaConverters._ List list = new ArrayList() for (e <- list.asScala) { // ... }
packge object
在当前包下可以唯一创建一个类名和包名相同的 packge object
package com package object gargantua { }
对于使用 packge object 中的方法,可以不用导包
Scala 使用 classOf() 返回运行时类型
可以使用 type 关键字定义新的数据类型名称 (换了个名字)
type S = String val str:S = ""
Scala的枚举没有单独的enum类型。使用枚举只需要创建object class 并继承自 Enumeration
object WeekDay extends Enumeration { type WeekDay = Value // Value也是一个类名 val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) } // println(WeekDay.isWorkingDay(WeekDay.Fri))
Scala没有interface接口,通过定义trait实现接口(javap 命令解析trait发现就是interface)
继承/实现 ,都是用extends,多个接口放在with 后面
class HomeWork extends Aaa with Bbb with Ccc { // 使用需要注意 AaaBbbCcc的顺序 // super.method 只会匹配到最后一个with父类中的方法 // super[Bbb].method 可以指定特定父类 }
动态混入
一个没有extends Aaa的普通的类,可以在创建对象时再with Aaa,而使用Aaa中的方法。
val hw = new HomeWork with Aaa class HomeWork {}
一个普通类 extends App 这个类,可以不用写main方法直接执行(App有)
object MyApp extends App { println("我可以直接输出。。。") util.Properties.setProp("scala.time","true") // 开启这个参数,还能记录并输出方法执行耗时 }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)