[修飾符] class 類名 {java
類體程序員
}編程
scala語法中,類並不聲明爲public,全部這些類都具備公有可見性(即默認就是public)app
一個Scala源文件能夠包含多個類框架
定義一個最簡單的類函數式編程
object Demo {
def main(args: Array[String]): Unit = {
var man = new Man
man.name = "cris"
man.age = 12
println(man.name + "----" + man.age) // cris----12
}
}
class Man {
var name = ""
var age = 0
}
複製代碼
反編譯對應的 class 文件函數
屬性是類的一個組成部分,通常是值數據類型,也但是引用類型工具
def main(args: Array[String]): Unit = {
val man = new Man()
val pc = new PC
man.pc = pc
man.pc.brand = "惠普"
// man.pc().brand()
println(man.pc.brand) // 惠普
}
class Man {
var name = "" // 手動設置初始值,此時能夠省略成員屬性的數據類型聲明
var age = 0
var pc: PC = _ // _ 表示讓 Scala 自動賦默認值,此時聲明帶上成員屬性的數據類型,不然編譯器沒法肯定默認值
}
class PC {
var brand: String = _
}
複製代碼
針對 for(int i = 10;i>0;i--){System.out.println(i)} 翻譯成 Scala 代碼this
object Practice {
def main(args: Array[String]): Unit = {
for (i <- 0.to(10).reverse) {
print(i + "\t") // 10 9 8 7 6 5 4 3 2 1 0
}
}
}
複製代碼
使用過程重寫上面的 Scala 代碼編碼
def func(x: Int) {
for (i <- 0 to x reverse) {
print(i + "\t")
}
}
複製代碼
編寫一個for循環,計算字符串中全部字母的Unicode代碼(toLong方法)的乘積。舉例來講,"Hello"中全部字符串的乘積爲9415087488L
def cal(str:String): Unit ={
var result = 1L
for(x <- str){
result*=x.toLong
}
print(result)
}
複製代碼
使用 StringOps 的 foreach 方法重寫上面的代碼
var r2 = 1L
// _ 能夠理解爲字符串的每個字符
"Hello".foreach(r2 *= _.toLong)
print(r2)
複製代碼
使用遞歸解決上面求字符串每一個字符 Unicode 編碼乘積的問題
def recursive(str: String): Long = {
if (str.length == 1) str.charAt(0).toLong
/*drop(n)從索引爲 1 開始切片到結尾*/
else str.take(1).charAt(0).toLong * recursive(str.drop(1))
}
複製代碼
編寫函數計算 x^n,其中 n 是整數(負數,0,正數),請使用遞歸解決
def pow(x: Int, n: Int): Double = {
if (n == 0) 1
else if (n < 0) {
1.0 / x * pow(x, n + 1)
} else {
x * pow(x, n - 1)
}
}
複製代碼
val | var 對象名 [:類型] = new 類型()
若是咱們不但願改變對象的引用(即:內存地址), 應該聲明爲val 性質的,不然聲明爲var, scala設計者推薦使用val ,由於通常來講,在程序中,咱們只是改變對象屬性的值,而不是改變對象的引用
scala在聲明對象變量時,能夠根據建立對象的類型自動推斷,因此類型聲明能夠省略,但當類型和後面new 對象類型有繼承關係即多態時,就必須寫了
Scala中的方法其實就是函數,只不過通常將對象中的函數稱之爲方法
def 方法名(參數列表) [:返回值類型] = {
方法體
}
練習
嵌套循環打印圖形
def func1(): Unit ={
for (i <- 1 to 4; j <- 1 to 3) {
if (j == 3) println("*")
else print("*\t")
}
}
複製代碼
計算矩形的面積
class Test {
def area(): Double = {
(this.width * this.length).formatted("%.2f").toDouble
}
var width: Double = _
var length: Double = _
複製代碼
java 的構造器回顧
[修飾符] 方法名(參數列表){
構造方法體
}
在Java中一個類能夠定義多個不一樣的構造方法,構造方法重載
若是程序員沒有定義構造方法,系統會自動給類生成一個默認無參構造方法(也叫默認構造器)
3)一旦定義了本身的構造方法,默認的構造方法就覆蓋了,就不能再使用默認的無參構造方法,除非顯示的定義一下,即: Person(){}
Scala 構造器
和Java同樣,Scala構造對象也須要調用構造方法,而且能夠有任意多個構造方法。
Scala類的構造器包括: 主構造器 和 輔助構造器
基礎語法
class 類名(形參列表) { // 主構造器
// 類體
def this(形參列表) { // 輔助構造器
}
def this(形參列表) { //輔助構造器能夠有多個...
}
}
簡單示例
abstract class Dog {
var name = ""
var age = 0
val color: String
def this(name: String, age: Int) {
this()
this.name = name
this.age = age
}
def eat(): Unit = {
println("吃狗糧")
}
def run()
}
複製代碼
class Cat(var name: String, val color: String) {
println("constructor is processing")
def describe: String = name + "--" + color
}
def main(args: Array[String]): Unit = {
var cat = new Cat("tom", "gray")
println(cat.describe)
var cat2 = new Cat("jack", "red")
println(cat2.describe)
}
複製代碼
細節
Scala構造器做用是完成對新對象的初始化,構造器沒有返回值。
主構造器的聲明直接放置於類名以後 [反編譯]
主構造器會執行類定義中的全部語句,這裏能夠體會到Scala的函數式編程和麪向對象編程融合在一塊兒,即:構造器也是方法(函數),傳遞參數和使用方法和前面的函數部份內容沒有區別
若是主構造器無參數,小括號可省略,構建對象時調用的構造方法的小括號也能夠省略
輔助構造器名稱爲this(這個和Java是不同的),多個輔助構造器經過不一樣參數列表進行區分, 在底層就是java的構造器重載,輔助構造器第一行函數體必須爲 this.主構造器
abstract class Dog {
var name = ""
var age = 0
val color: String
def this(name: String, age: Int) {
this()
this.name = name
this.age = age
}
def eat(): Unit = {
println("吃狗糧")
}
def run()
}
複製代碼
6)) 若是想讓主構造器變成私有的,能夠在()以前加上private,這樣用戶只能經過輔助構造器來構造對象了,說明:由於Person3的主構造器是私有,所以就須要使用輔助構造器來建立對象
class Car private(){}
複製代碼
Scala類的主構造器函數的形參未用任何修飾符修飾,那麼這個參數是局部變量
若是參數使用val關鍵字聲明,那麼Scala會將參數做爲類的私有的只讀屬性使用
若是參數使用var關鍵字聲明,那麼那麼Scala會將參數做爲類的成員屬性使用,並會提供屬性對應的xxx()[相似getter]/xxx_$eq()[相似setter]方法,即這時的成員屬性是私有的,可是可讀寫
class Counter {
/*1. 有公開的 getter 和 setter 方法*/
var count = 0
/*2. 私有化 getter 和 setter,能夠手動提供 setter 和 getter*/
private var number = 1
/*3. 只能被訪問getter,沒法修改setter,final 修飾的 age 屬性*/
val age = 12
/*4. 對象級別的私有*/
private[this] var length = 12
def compare(other: Counter): Boolean = other.number > number
// def compareLength(other: Counter): Boolean = length > other.length
def increase(): Unit = {
number += 1
}
/*無參方法能夠省略(),{}也能夠省略*/
def current: Int = number
}
def main(args: Array[String]): Unit = {
var c = new Counter()
c.count = 3
println(c.count) // 3
c.increase()
println(c.current) // 2
println(c.age) // 12
}
複製代碼
若是在主構造器中爲屬性設置了默認值,那麼就沒必要在函數體內再去聲明屬性以及賦值了,大大簡化代碼的書寫
def main(args: Array[String]): Unit = {
val dog = new Dog()
println(dog.name) // cris
println(dog.age) // 10
}
}
class Dog(var name :String= "cris",var age:Int = 10){
}
複製代碼
JavaBean 註解
JavaBeans規範定義了Java的屬性是像getXxx()和setXxx()的方法。許多Java工具(框架)都依賴這個命名習慣。爲了Java的互操做性。將Scala字段加@BeanProperty時,這樣會自動生成規範的 setXxx/getXxx 方法。這時可使用 對象.setXxx() 和 對象.getXxx() 來調用屬性
給某個屬性加入@BeanPropetry註解後,會生成getXXX和setXXX的方法
而且對原來底層自動生成相似xxx(),xxx_$eq()方法,沒有衝突,兩者能夠共存
請針對如下代碼簡述對象建立流程
class Bike {
var brand = ""
var color = ""
def this(brand: String, color: String) {
this
this.brand = brand
this.color = color
}
}
def main(args: Array[String]): Unit = {
var bike = new Bike("ofo", "黃色")
}
複製代碼
加載類信息(屬性信息,方法信息)
在堆中,給對象開闢空間
調用主構造器對屬性進行初始化
使用輔助構造器對屬性進行初始化
把對象空間的地址,返回給 bike 引用
回顧 Java 的包知識
做用
區分相同名字的類
當類不少時,能夠很好的管理類
控制訪問範圍
打包基本語法
package com.cris;
打包的本質分析
實際上就是建立不一樣的文件夾來保存類文件
示例代碼
先在不一樣的包下創建同名的類
若是想要在一個類中同時使用上面的兩個 Pig,Java 的解決方式以下:
public static void main(String[] args) {
Pig pig1 = new Pig();
cris.package2.Pig pig2 = new cris.package2.Pig();
// pig1.getClass() = class cris.package1.Pig
System.out.println("pig1.getClass() = " + pig1.getClass());
// pig2.getClass() = class cris.package2.Pig
System.out.println("pig2.getClass() = " + pig2.getClass());
}
複製代碼
再來看看咱們的源碼所在路徑和字節碼文件所在路徑,都是一一對應的
Java 要求源碼所在路徑和字節碼文件所在路徑必須保持一致,若是咱們此時去修改源碼的打包路徑
基本語法
import java.awt.* or import java.util.List
注意事項:java中包名和源碼所在的系統文件目錄結構要一致,而且編譯後的字節碼文件路徑也和包名保持一致
接着看看 Scala 是如何處理的
咱們使用 Scala 重寫上面的 Java 包案例
def main(args: Array[String]): Unit = {
var b1 = new cris.package1.Bird1
var b2 = new cris.package2.Bird2
// class cris.package1.Bird1
println(b1.getClass)
// class cris.package2.Bird2
println(b2.getClass)
}
複製代碼
此時咱們若是修改了 Bird1 的打包路徑
再看看源代碼和字節碼文件所在的路徑
Scala 的包
和Java同樣,Scala中管理項目可使用包,但Scala中的包的功能更增強大,使用也相對複雜些
基本語法 package 包名
Scala包的三大做用(和Java同樣)
Scala中包名和源碼所在的系統文件目錄結構要能夠不一致,可是編譯後的字節碼文件路徑和包名會保持一致(這個工做由編譯器完成)
圖示
命名規範
只能包含數字、字母、下劃線、小圓點.,但不能用數字開頭, 也不要使用關鍵字
通常是小寫字母+小圓點通常是 com.公司名.項目名.業務模塊名
Scala 自動 import 的包有:java.lang.*,scala,Predef 包
Scala 打包細節(難點)
經常使用的兩種打包形式
源代碼的路徑和字節碼文件路徑保持一致
源代碼的路徑和字節碼文件路徑不一致
上面的演示中已經很清楚的展現了 Scala 包的這一特色,咱們繼續用下面代碼演示 Scala 包的嵌套
咱們在 Detail 類文件中寫入以上很是奇怪的代碼,編譯運行後再查看源代碼和字節碼文件的位置
進一步印證了 Scala 中源文件和字節碼文件路徑能夠不一致
包也能夠像嵌套類那樣嵌套使用(包中有包), 見上面圖示。好處是:程序員能夠在同一個文件中,將類(class / object)、trait 建立在不一樣的包中,很是靈活
做用域原則:能夠直接向上訪問。即: Scala中子包中直接訪問父包中的內容, 大括號體現做用域。(提示:Java中子包使用父包的類,須要import)。在子包和父包 類重名時,默認採用就近原則,若是但願指定使用某個類,則帶上包名便可
示例代碼
package com.cris {
class Apple {
}
package scala {
class Apple {
}
object Boy {
def main(args: Array[String]): Unit = {
/*1. Scala 中子包能夠直接訪問父包的內容;2. 子包和父包的類重名,默認採起就近原則;3. 能夠帶上類的路徑名指定使用該類*/
val apple = new Apple
val apple2 = new com.cris.Apple
// class com.cris.scala.Apple
println(apple.getClass)
// class com.cris.Apple
println(apple2.getClass)
}
}
}
}
複製代碼
父包要訪問子包的內容時,須要import對應的類
package com.cris {
import com.cris.scala.Apple
object Apple{
def main(args: Array[String]): Unit = {
// 推薦只在使用的時候再引用,控制做用域
import com.cris.scala.Apple
val apple = new Apple()
// class com.cris.scala.Apple
println(apple.getClass)
}
}
package scala {
class Apple {
}
}
}-
複製代碼
能夠在同一個.scala文件中,聲明多個並列的package(建議嵌套的pakage不要超過3層)
包對象
基本介紹:包能夠包含類、對象和特質trait,但不能包含函數或變量的定義。這是Java虛擬機的侷限。爲了彌補這一點不足,scala提供了包對象的概念來解決這個問題
參見以下代碼
package com.cris {
// 不能直接在 package 中定義函數和變量
// var name = "cris"
/** * 包對象的名字須要和包名一致 * package object emp 會在 com.cris.emp 包下生成 package.class 和 package$.class */
package object emp {
def eat(): Unit = {
println("eat")
}
val salary = 1000.0
}
package emp {
object test {
def main(args: Array[String]): Unit = {
eat() // eat=》等價於使用了 package$.class 中的 MODULE$.eat()
println(salary) // 1000.0=> 等價於使用了 package$.class 中的 MODULE$.salary()
}
}
}
}
複製代碼
使用反編譯工具打開瞧瞧
具體的執行流程第二章節已經解釋過,這裏再也不贅述
注意事項:
包的可見性
在Java中,訪問權限分爲: public,private,protected和默認。在Scala中,你能夠經過相似的修飾符達到一樣的效果。可是使用上有區別
當屬性訪問權限爲默認時,從底層看屬性是private的,可是由於提供了xxx_$eq()[相似setter]/xxx()[相似getter] 方法,所以從使用效果看是任何地方均可以訪問)
當方法訪問權限爲默認時,默認爲public訪問權限
private爲私有權限,只在類的內部和伴生對象中可用
示例:
protected爲受保護權限,scala中受保護權限比Java中更嚴格,只能子類訪問,同包沒法訪問
在scala中沒有public關鍵字,即不能用public顯式的修飾屬性和方法。
包訪問權限(表示屬性有了限制。同時增長了包的訪問權限),這點和Java不同,體現出Scala包使用的靈活性
包的引入
細節說明
在Scala中,import語句能夠出如今任何地方,並不只限於文件頂部,import語句的做用一直延伸到包含該語句的塊末尾。這種語法的好處是:在須要時在引入包,縮小import 包的做用範圍,提升效率
示例以下:
Java中若是想要導入包中全部的類,能夠經過通配符*,Scala中採用下 _
若是不想要某個包中所有的類,而是其中的幾個類,能夠採用選取器(大括號)
若是引入的多個包中含有相同的類,那麼能夠將不須要的類進行重命名進行區分,這個就是重命名
或者使用 import java.util.{HashMap => _ } 對衝突的包進行隱藏
編寫一個Time類,加入只讀屬性hours和minutes,和一個檢查某一時刻是否早於另外一時刻的方法before(other:Time):Boolean。Time對象應該以new Time(hrs,min)方式構建
object Practice {
def main(args: Array[String]): Unit = {
val time1 = new Time(4, 12)
val result = time1.before(new Time(4, 14))
println(result)
}
}
class Time(val hour: Int, val minute: Int) {
def before(other: Time) = {
if (this.hour < other.hour) true
else if (this.hour > other.hour) false
else if (this.hour == other.hour) {
if (this.minute < other.minute) true
else if (this.minute > other.minute) false
else false
}
}
}
複製代碼
建立一個Student類,加入可讀寫的JavaBeans屬性name(類型爲String)和id(類型爲Long)。有哪些方法被生產?(用javap查看。)你能夠在Scala中調用JavaBeans的getter和setter方法嗎?
object Practice {
def main(args: Array[String]): Unit = {
var s = new Student
println(s.getName)
println(s.age)
}
}
class Student {
@BeanProperty var name = "好學生"
@BeanProperty var age = 0
}
複製代碼
編寫一段程序,將Java哈希映射中的全部元素拷貝到Scala哈希映射。用引入語句重命名這兩個類
object Ex extends App {
import java.util.{HashMap => JavaHashMap}
import scala.collection.mutable.{HashMap => ScalaHashMap}
var map1 = new JavaHashMap[Int, String]()
map1.put(1, "cris")
map1.put(2, "james")
map1.put(3, "simida")
var map2 = new ScalaHashMap[Int, String]()
for (key <- map1.keySet().toArray()) { // key 的數據類型是 AnyRef
// asInstanceOf 強制數據類型轉換
map2 += (key.asInstanceOf[Int] -> map1.get(key))
}
println(map2.mkString("||")) // 2 -> james||1 -> cris||3 -> simida
}
複製代碼
咱們在前面去定義一個類時候,實際上就是把一類事物的共有的屬性和行爲提取出來,造成一個物理模型(模板)。這種研究問題的方法稱爲抽象
示例代碼
object Demo extends App {
var account = new Account("招行:888888", 200, "123456")
account.query("123456")
account.save("123456", 100)
account.query("123456")
account.withdraw("123456", 250)
account.query("123456")
}
class Account(val no: String, var balance: Double, var pwd: String) {
def query(pwd: String): Unit = {
if (pwd != this.pwd) {
println("密碼錯誤!")
} else {
println(s"卡號:${this.no},餘額還有:${this.balance}")
}
}
def save(pwd: String, money: Double): Unit = {
if (pwd != this.pwd) {
println("密碼錯誤")
} else {
this.balance += money
println(s"卡號:${this.no},存入:${money},餘額爲:${this.balance}")
}
}
def withdraw(pwd: String, money: Double): Unit = {
if (pwd != this.pwd) {
println("密碼錯誤")
} else if (money > this.balance) {
println("餘額不足")
} else {
this.balance -= money
println(s"卡號:${this.no},取出:${money},餘額爲:${this.balance}")
}
}
}
複製代碼