scala 学习【基础一】

scala 学习【基础一】,第1张

scala 学习【基础一】 scala 基础

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") // 开启这个参数,还能记录并输出方法执行耗时
}

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/zaji/5713252.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存