柚子快報(bào)邀請(qǐng)碼778899分享:開發(fā)語(yǔ)言 后端 Scala
柚子快報(bào)邀請(qǐng)碼778899分享:開發(fā)語(yǔ)言 后端 Scala
數(shù)據(jù)類型:
Unit 表示無(wú)值,和其它語(yǔ)言中void相同
Null 空值或空引用
Nothing 所有類型的子類型,表示沒有值
Any 所有其它類型的超類,任何實(shí)例都是Any類型
AnyRef 所有引用類型的超類
AnyVal? 所有值類型的超類
var和val的區(qū)別:var是可變量,val是不可變量(相當(dāng)于java中的final)。
val支持推導(dǎo)類型,即根據(jù)變量的初始值推導(dǎo)出變量的類型,但var必須顯式聲明
val線程安全,因?yàn)樗豢勺?,不?huì)引起結(jié)果的差異和競(jìng)態(tài)條件。
val更適合函數(shù)式編程
競(jìng)態(tài)條件(Race Condition)是指在多線程編程中,多個(gè)線程對(duì)共享資源的訪問和操作沒有正確同步導(dǎo)致的不確定結(jié)果。競(jìng)態(tài)條件可能會(huì)導(dǎo)致程序出現(xiàn)意外的行為,產(chǎn)生不一致的結(jié)果。
競(jìng)態(tài)條件發(fā)生的原因是多個(gè)線程并發(fā)執(zhí)行時(shí),它們對(duì)共享資源的訪問沒有進(jìn)行適當(dāng)?shù)耐讲僮?。這意味著多個(gè)線程可以以任意的順序讀取和寫入共享資源,導(dǎo)致操作結(jié)果的不可預(yù)測(cè)性。
以下是一些常見的競(jìng)態(tài)條件情況:
讀-修改-寫(Read-Modify-Write)操作:多個(gè)線程同時(shí)讀取同一個(gè)變量的值,并根據(jù)這個(gè)值進(jìn)行修改后再寫回。如果多個(gè)線程同時(shí)執(zhí)行讀取和寫入操作,可能會(huì)導(dǎo)致最終結(jié)果不正確。 檢查-執(zhí)行(Check-Then-Act)操作:多個(gè)線程同時(shí)檢查某個(gè)條件,并根據(jù)條件執(zhí)行相應(yīng)的操作。如果多個(gè)線程同時(shí)執(zhí)行檢查操作,可能導(dǎo)致條件的結(jié)果在執(zhí)行操作之前被修改,從而產(chǎn)生錯(cuò)誤的行為。 非原子操作:多個(gè)線程同時(shí)執(zhí)行多個(gè)操作,這些操作需要以原子方式執(zhí)行才能保證正確性。如果沒有適當(dāng)?shù)耐酱胧?,可能?dǎo)致操作之間的交叉執(zhí)行,產(chǎn)生不一致的結(jié)果。
為了避免競(jìng)態(tài)條件,需要使用適當(dāng)?shù)耐綑C(jī)制來(lái)保護(hù)共享資源的訪問,例如使用鎖(Lock)、互斥量(Mutex)、信號(hào)量(Semaphore)等。這些同步機(jī)制可以確保多個(gè)線程按照正確的順序訪問共享資源,避免競(jìng)態(tài)條件的發(fā)生。
在Java和Scala中,數(shù)值類型之間的轉(zhuǎn)換關(guān)系如下:
從較小的類型向較大的類型轉(zhuǎn)換是自動(dòng)進(jìn)行的,稱為隱式類型轉(zhuǎn)換。這是因?yàn)檩^小的類型可以容納較大類型的值,不會(huì)導(dǎo)致數(shù)據(jù)丟失或溢出。
Byte 可以自動(dòng)轉(zhuǎn)換為 Short、Int、Long、Float、Double。Short 可以自動(dòng)轉(zhuǎn)換為 Int、Long、Float、Double。Char 可以自動(dòng)轉(zhuǎn)換為 Int、Long、Float、Double。Int 可以自動(dòng)轉(zhuǎn)換為 Long、Float、Double。Long 可以自動(dòng)轉(zhuǎn)換為 Float、Double。Float 可以自動(dòng)轉(zhuǎn)換為 Double。
從較大的類型向較小的類型轉(zhuǎn)換需要顯式進(jìn)行,稱為顯式類型轉(zhuǎn)換或強(qiáng)制類型轉(zhuǎn)換。這是因?yàn)檩^大的類型可能無(wú)法完全容納較小類型的值,可能會(huì)導(dǎo)致數(shù)據(jù)丟失或溢出。
Double 需要顯式轉(zhuǎn)換為 Float、Long、Int、Short、Byte。Float 需要顯式轉(zhuǎn)換為 Long、Int、Short、Byte。Long 需要顯式轉(zhuǎn)換為 Int、Short、Byte。Int 需要顯式轉(zhuǎn)換為 Short、Byte。Short 需要顯式轉(zhuǎn)換為 Byte。
val intValue: Int = 1000
val byteValue: Byte = intValue.toByte
val doubleValue: Double = 3.14
val intValue: Int = doubleValue.toInt
隱式轉(zhuǎn)換案例
通過定義一個(gè)隱式轉(zhuǎn)換函數(shù) intToString,它接受一個(gè) Int 類型的參數(shù),并將其轉(zhuǎn)換為 String 類型。然后,在主函數(shù)中將 intValue 賦值給 stringValue 變量時(shí),由于沒有直接的類型匹配,編譯器會(huì)自動(dòng)應(yīng)用隱式類型轉(zhuǎn)換函數(shù)將 intValue 隱式轉(zhuǎn)換為 String 類型。
// 定義一個(gè)隱式轉(zhuǎn)換函數(shù)
implicit def intToString(value: Int): String = value.toString
def main(args: Array[String]): Unit = {
val intValue: Int = 42
// 隱式類型轉(zhuǎn)換:將 intValue 轉(zhuǎn)換為 String 類型
val stringValue: String = intValue
println(stringValue)
}
if-else舉例
def main(args: Array[String]): Unit = {
val x = 10
if (x > 5) {
println("x is greater than 5")
} else {
println("x is less than or equal to 5")
}
}
for循環(huán)
for(i <- 1 to 9;j <- 1 until 10){
}
scala中沒有break和continue的實(shí)現(xiàn),但可以通過breakable關(guān)鍵字和遞歸函數(shù)替代實(shí)現(xiàn)。
使用breakable{}代碼塊包裹需要使用break功能的代碼塊
import scala.util.control.Breaks._
var i = 0
breakable {
while (i < 10) {
if (i == 5) break // 使用 breakable 塊中的 break 方法實(shí)現(xiàn)中斷循環(huán)
println(i)
i += 1
}
}
遞歸
def printNumbers(n: Int): Unit = {
if (n < 1) return // 遞歸終止條件
if (n == 5) return // 使用 return 語(yǔ)句實(shí)現(xiàn)跳過當(dāng)前迭代
println(n)
printNumbers(n - 1) // 遞歸調(diào)用
}
printNumbers(10)
方法的定義:Method
def fun(x int ,y int) :unit = {
}
def fun1 (a : Int , b : Int)= a+b
方法可以寫返回值的類型也可以不寫,會(huì)自動(dòng)推斷,有時(shí)候不能省略,必須寫,比如在遞歸方法中或者方法的返回值是函數(shù)類型的時(shí)候。scala中方法有返回值時(shí),可以寫return,也可以不寫return,會(huì)把方法中最后一行當(dāng)做結(jié)果返回。當(dāng)寫return時(shí),必須要寫方法的返回值。如果返回值可以一行搞定,可以將{}省略不寫傳遞給方法的參數(shù)可以在方法中使用,并且scala規(guī)定方法的傳過來(lái)的參數(shù)為val的,不是var的。如果去掉方法體前面的等號(hào),那么這個(gè)方法返回類型必定是Unit的。這種說(shuō)法無(wú)論方法體里面什么邏輯都成立,scala可以把任意類型轉(zhuǎn)換為Unit.假設(shè),里面的邏輯最后返回了一個(gè)string,那么這個(gè)返回值會(huì)被轉(zhuǎn)換成Unit,并且值會(huì)被丟棄。
函數(shù)Function
定義一個(gè)函數(shù)
val? f1=(x:Int,y:Int)=>x+y
調(diào)用: f1(1,2)
匿名函數(shù)
(x:Int,y:Int)=>x+y
函數(shù)和方法的區(qū)別:函數(shù)式獨(dú)立的、可單獨(dú)調(diào)用的代碼塊,方法是類對(duì)象中的一個(gè)成員,通過類的實(shí)例進(jìn)行調(diào)用
函數(shù)式編程:
高階函數(shù):返回值是函數(shù),或參數(shù)是函數(shù),或同時(shí)都是的函數(shù)
返回值是函數(shù)的函數(shù):
def f(a:int,b:int):(int,int)=>int ={
def f2(x:int,y:int):int={
x+y+a+b
}
f2
}
println(f(1,2)(3,4))
//函數(shù)的返回是函數(shù)
//1,2,3,4相加
def hightFun2(a : Int,b:Int) : (Int,Int)=>Int = {
def f2 (v1: Int,v2:Int) :Int = {
v1+v2+a+b
}
f2
}
println(hightFun2(1,2)(3,4))
這段代碼定義了一個(gè)高階函數(shù) hightFun2,它接受兩個(gè)整型參數(shù) a 和 b,并返回一個(gè)函數(shù) (Int, Int) => Int。
函數(shù) hightFun2 內(nèi)部定義了一個(gè)局部函數(shù) f2,它接受兩個(gè)整型參數(shù) v1 和 v2,并返回它們的和再加上外部參數(shù) a 和 b 的值。這個(gè)局部函數(shù) f2 捕獲了外部函數(shù) hightFun2 的參數(shù) a 和 b,因此在函數(shù)體內(nèi)部可以直接訪問這兩個(gè)參數(shù)。
最后,函數(shù) hightFun2 返回了局部函數(shù) f2。在打印語(yǔ)句中,我們通過 hightFun2(1, 2) 調(diào)用了 hightFun2 函數(shù),并將返回的函數(shù) (Int, Int) => Int 作為參數(shù)傳遞給了另一個(gè)函數(shù)調(diào)用 println。然后,我們通過 (3, 4) 作為參數(shù)調(diào)用了這個(gè)返回的函數(shù),得到結(jié)果 1 + 2 + 3 + 4 = 10,并將結(jié)果打印出來(lái)。
因此,整體來(lái)說(shuō),這段代碼的含義是定義了一個(gè)接受兩個(gè)參數(shù)的高階函數(shù) hightFun2,它返回一個(gè)函數(shù),這個(gè)返回的函數(shù)接受兩個(gè)參數(shù),并返回這兩個(gè)參數(shù)的和再加上外部傳入的參數(shù)的值。然后,通過調(diào)用 hightFun2(1, 2)(3, 4),得到了最終的結(jié)果并打印出來(lái)。
def hightFun3(f : (Int ,Int) => Int) : (Int,Int) => Int = {
f
}
println(hightFun3(f)(100,200))
println(hightFun3((a,b) =>{a+b})(200,200))
//以上這句話還可以寫成這樣
//如果函數(shù)的參數(shù)在方法體中只使用了一次 那么可以寫成_表示
println(hightFun3(_+_)(200,200))
這段代碼定義了一個(gè)高階函數(shù) hightFun3,它接受一個(gè)函數(shù) f,該函數(shù)的類型為 (Int, Int) => Int,并返回一個(gè)函數(shù) (Int, Int) => Int。
在第一個(gè)打印語(yǔ)句中,我們調(diào)用了 hightFun3(f),將函數(shù) f 作為參數(shù)傳遞給 hightFun3,然后將返回的函數(shù)再次調(diào)用,并傳入?yún)?shù) (100, 200)。由于函數(shù) hightFun3 的實(shí)現(xiàn)中直接返回了參數(shù)函數(shù) f,因此第一個(gè)打印語(yǔ)句的結(jié)果就是調(diào)用函數(shù) f,即 f(100, 200)。
在第二個(gè)打印語(yǔ)句中,我們調(diào)用了 hightFun3,并傳入了一個(gè)匿名函數(shù) (a, b) => { a + b }。這個(gè)匿名函數(shù)接受兩個(gè)參數(shù),并返回它們的和。因此,第二個(gè)打印語(yǔ)句的結(jié)果就是調(diào)用這個(gè)匿名函數(shù),即 (a, b) => { a + b }(200, 200),最終結(jié)果為 400。
注意點(diǎn):{}中最后一行為默認(rèn)返回值
匿名函數(shù) 中的參數(shù)如果只使用一次,可以 簡(jiǎn)寫,即? (a,b)=>{a+b} 簡(jiǎn)化為 (_+_)
柯里化函數(shù): 高階函數(shù)調(diào)用的簡(jiǎn)化
將原來(lái)需要接受多個(gè)參數(shù)的函數(shù)轉(zhuǎn)換成只要一個(gè)參數(shù)的函數(shù)過程,并且返回 一個(gè)函數(shù)(參數(shù)為 剩余的參數(shù))。
scala柯里化風(fēng)格的使用可以簡(jiǎn)化主函數(shù)的復(fù)雜度,提高主函數(shù)的自閉性,提高功能上的可擴(kuò)張性、靈活性??梢跃帉懗龈映橄?功能化和高效的函數(shù)式代碼。
//柯理化
object KLH {
def main(args: Array[String]): Unit = {
def klh(x:Int)(y:Int) =x*y
val res=klh(3)(_)
println(res(4))
// KLH(3)(_)(4)
}
}
/**
* 柯里化函數(shù)
*/
def fun7(a :Int,b:Int)(c:Int,d:Int) = {
a+b+c+d
}
println(fun7(1,2)(3,4))
// val res = fun7(1,2)_
// val res1 = res(3,4)
接下來(lái),使用 klh(3)(_)(4) 的方式調(diào)用柯里化函數(shù)。其中,第一個(gè)參數(shù)列表 klh(3) 接受參數(shù) 3,并返回一個(gè)函數(shù),這個(gè)函數(shù)需要一個(gè)參數(shù) y。而 _ 表示占位符,代表后續(xù)需要傳入的參數(shù)。
然后,將返回的函數(shù)賦值給變量 res。最后,通過 res(4) 調(diào)用這個(gè)函數(shù),將參數(shù) 4 傳遞給 res,并輸出結(jié)果。
因此,程序的輸出結(jié)果為 12,即 3 * 4。
面向?qū)ο?/p>
scala和java在繼承方面的區(qū)別:
1.java只能單繼承,scala通過trait實(shí)現(xiàn)多重繼承(一個(gè)類可以繼承多個(gè)父類),即混入mixin。
2.trait,類似于java中的接口,比接口強(qiáng)大。定義一組方法的規(guī)范,但java在接口里不會(huì)寫實(shí)現(xiàn),而scala包含具體的實(shí)現(xiàn)。
3.構(gòu)造方法,java中的是與類同名的特殊方法,scala中是對(duì)象中一個(gè)代碼塊。
4.方法重寫:java使用@override注解,scala直接使用override關(guān)鍵字
單例對(duì)象
某個(gè)對(duì)象的實(shí)例在整個(gè)應(yīng)用程序的生命周期內(nèi)是唯一的,類似于java單例模式。使用Singleton Object創(chuàng)建只有一個(gè)實(shí)例的類。
object SingletonExample {
def sayHello(): Unit = {
println("Hello, I am a singleton object!")
}
}
SingletonExample.sayHello()
伴生對(duì)象
scala允許定義和class結(jié)構(gòu)同名的object結(jié)構(gòu)object稱之為伴生對(duì)象class稱之為伴生類當(dāng)只有object對(duì)象時(shí),我們也稱這個(gè)對(duì)象為單例對(duì)象、孤立對(duì)象
集合
數(shù)組 使用 Array 類型表示,可變長(zhǎng)度的有序集合,使用索引訪問元素? val array = Array(1, 2, 3, 4)
list列表 使用 List 類型表示,不可變的有序集合,可以通過 :: 運(yùn)算符構(gòu)建新的列表。 val list = List(1, 2, 3, 4)
set 去重,不可變
/**
* 可變長(zhǎng)Set
*/
import scala.collection.mutable.Set
val set = Set[Int](1,2,3,4,5)
set.add(100)
set.+=(200)
set.+=(1,210,300)
set.foreach(println)
map k-v類型的集合 val map = Map("a" -> 1, "b" -> 2, "c" -> 3)
元組 可以包含不同類型的元素,用()包裹
隊(duì)列(Queue):使用 Queue 類型表示,可變的先進(jìn)先出集合。
import scala.collection.mutable.Queue
val queue = Queue(1, 2, 3, 4)
可變集合在操作時(shí)可以修改其內(nèi)容,而不可變集合的操作會(huì)返回一個(gè)新的集合,原集合保持不變。
scala集合常用的計(jì)算函數(shù)
sum:計(jì)算集合中元素的總和。
val numbers = List(1, 2, 3, 4, 5) val total = numbers.sum println(total) // 輸出:15
max:返回集合中的最大值。
val numbers = List(1, 2, 3, 4, 5) val maxNum = numbers.max println(maxNum) // 輸出:5
min:返回集合中的最小值。
val numbers = List(1, 2, 3, 4, 5) val minNum = numbers.min println(minNum) // 輸出:1
average:計(jì)算集合中元素的平均值。
val numbers = List(1, 2, 3, 4, 5) val avg = numbers.sum.toDouble / numbers.length println(avg) // 輸出:3.0
count:統(tǒng)計(jì)滿足特定條件的元素個(gè)數(shù)。
val numbers = List(1, 2, 3, 4, 5) val evenCount = numbers.count(_ % 2 == 0) println(evenCount) // 輸出:2
filter:過濾集合中滿足特定條件的元素。
val numbers = List(1, 2, 3, 4, 5) val evenNumbers = numbers.filter(_ % 2 == 0) println(evenNumbers) // 輸出:List(2, 4)
map:對(duì)集合中的每個(gè)元素應(yīng)用某個(gè)函數(shù)并返回新的集合。
val numbers = List(1, 2, 3, 4, 5) val doubledNumbers = numbers.map(_ * 2) println(doubledNumbers) // 輸出:List(2, 4, 6, 8, 10)
模式匹配
case關(guān)鍵字,每個(gè)備選項(xiàng)都包含一個(gè)模式和一個(gè)或多個(gè)表達(dá)式? => 隔開了模式和表達(dá)式
1.可以匹配值和類型
2.從上到下匹配,匹配上了就不會(huì)再往下匹配
3. 都匹配不上時(shí),會(huì)匹配case _ 相當(dāng)于匹配默認(rèn)值
4.match 的{}? 可以去掉
def matchTest(x:Any) ={
x match {
case x:Int=> println("type is Int")
case 1 => println("result is 1")
case 2 => println("result is 2")
case 3=> println("result is 3")
case 4 => println("result is 4")
case x:String => println("type is String")
// case x :Double => println("type is Double")
case _ => println("no match")
}
}
}
match {
case a =>
case b =>
case c =>
}
偏函數(shù):方法中沒有match只有case
異常
將會(huì)發(fā)生異常的代碼封裝在 try 塊中。在 try 塊之后使用了一個(gè) catch 處理程序來(lái)捕獲異常。如果發(fā)生任何異常,catch處理程序?qū)⑻幚硭?,程序?qū)⒉粫?huì)異常終止。Scala 的異常的工作機(jī)制和 Java 一樣,但是 Scala 沒有“checked(編譯期)”異常,即 Scala沒有編譯異常這個(gè)概念,異常都是在運(yùn)行的時(shí)候捕獲處理。異常捕捉的機(jī)制與其他語(yǔ)言中一樣,如果有異常發(fā)生,catch 子句是按次序捕捉的。因此,在 catch 子句中,越具體的異常越要靠前,越普遍的異常越靠后,如果把越普遍的異常寫在前,把具體的異常寫在后,在 Scala 中也不會(huì)報(bào)錯(cuò),但這樣是非常不好的編程風(fēng)格。 object Test_Exception {
def main(args: Array[String]): Unit = {
try{
val n =10 /0;
}catch {
case e: ArithmeticException =>{
println("發(fā)生算數(shù)異常")
}
case e: Exception => {
println("發(fā)生一般異常")
}
}finally {
println("處理結(jié)束")
}
}
}
object a {
def main():Unit = {
try{
}
catch{
//模式匹配
case e : 異常處理類 => {}
}
finally{
}
}
}
隱式轉(zhuǎn)換
類型匹配時(shí),如果找不到合適的類型,會(huì)讓編譯器在作用范圍內(nèi)自動(dòng)推斷類型
隱式方法,即隱式轉(zhuǎn)換函數(shù)
使用關(guān)鍵字 implicit修飾,當(dāng)A對(duì)象調(diào)用一個(gè)方法時(shí),發(fā)現(xiàn)A類里面沒有這樣的方法,但B類中存在這個(gè)方法,那么編譯器就會(huì)在作用域里面找有沒有可以將A類對(duì)象轉(zhuǎn)換B類對(duì)象的隱式轉(zhuǎn)換函數(shù),如果有,就使用,A類就可以調(diào)用該方法。
class Animal(name:String){
def canFly(): Unit ={
println(s"$name can fly...")
}
}
class Rabbit(xname:String){
val name = xname
}
object Lesson_ImplicitFunction {
implicit def rabbitToAnimal(rabbit:Rabbit):Animal = {
new Animal(rabbit.name)
}
def main(args: Array[String]): Unit = {
val rabbit = new Rabbit("RABBIT")
rabbit.canFly()
隱式參數(shù):方法的參數(shù)用implicit關(guān)鍵字修飾,必須用KLH的方式書寫,寫在后面的()中
隱式轉(zhuǎn)換的作用是當(dāng)調(diào)用方法時(shí),不用傳參,編譯器會(huì)自動(dòng)在作用域范圍內(nèi)搜尋并將隱式參數(shù)傳入
隱式類:用implicit修飾的類。當(dāng)A變量沒有某個(gè)方法,可以在定義一個(gè)隱式類,在里面定義方法,然后隱式類的方法傳給A去實(shí)現(xiàn)
隱式類注意:
1).隱式類必須定義在類,包對(duì)象,伴生對(duì)象中。
2).隱式類的構(gòu)造必須只有一個(gè)參數(shù),同一個(gè)類,包對(duì)象,伴生對(duì)象中不能出現(xiàn)同類型構(gòu)造的隱式類。
隱式轉(zhuǎn)換機(jī)制即隱式轉(zhuǎn)換函數(shù)或類和隱式參數(shù)
泛型:通用的類、方法、函數(shù),可以接受各種類型的參數(shù),提高代碼靈活性。
class Box[T](value: T) {
def getValue: T = value
}
val boxInt = new Box[Int](42)
val boxString = new Box[String]("Hello")
val intValue: Int = boxInt.getValue
val stringValue: String = boxString.getValue
柚子快報(bào)邀請(qǐng)碼778899分享:開發(fā)語(yǔ)言 后端 Scala
文章鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。