第一章 scala运行环境搭建
1.简介
- Scala 多范式(multi-paradigm)编程语言,设计初衷是要集成面向对象编程和函数式编程的各种特性。
- Scala 运行于JVM之上,并可以调用现有的Java,并兼容现有的Java程序。
- Scala 函数式编程
2.安装和配置、运行环境
- 前提:先安装JDK
- 安装Scala:下载 http://www.scala-lang.org/download/
注意:版本跟Spark的Scala版本一致
scala-2.11.8.zip - 设置变量 —> 跟Java完全一样
SCALA_HOME
把 %SCALA_HOME%/bin ====> PATH路径 - cala的运行环境(开发工具)
-
命令行:REPL
进入: scala 退出::quit paste 模式: 相当于vi编辑器 进入: :paste 退出: ctrl+D
-
IDEA 默认:没有Scala的环境 需要安装插件:SBT / scala
-
Scala IDE:基于Eclipse
3.HelloWorld
- 编写文件:HelloWorld.scala
object HelloWorld {
def main(args: Array[String]): Unit = {
println("Hello World")
}
}
- 编译运行
scalac HelloWorld.scala
scala HelloWorld
第二章 scala语音基础
1.基本数据类型:
- 数值类型: Byte Short Int Long Float Double
- 字符串:字符和字符串 Char String (Char 16位\u0000-\uffff)
- 布尔: Boolean 取值true false
- Unit类型: 相当于Java中void
- Null :null 或空引用
- Nothing :一般来说,表示在执行的过程中产生了Exception
- Any Any是所有其他类的超类
- AnyRef AnyRef类是Scala里所有引用类(reference class)的基类
2.Scala 关键字
下表列出了 scala 保留关键字,我们不能使用以下关键字作为变量:
abstract | case | catch | class |
---|---|---|---|
def | do | else | extends |
false | final | finally | for |
forSome | if | implicit | import |
lazy | match | new | null |
object | override | package | private |
protected | return | sealed | super |
this | throw | trait | try |
true | type | val | var |
while | with | yield | |
- | : | = | => |
<- | <: | <% | >: |
# | @ |
//1. sealed 关键字可以修饰类和特质,密封类提供了一种约束:不能在类定义的文件之外定义任何新的子类,放滥用
//2. implicit 隐式
//3. yield:用于接受序列的返回值为新的序列
3.Scala字面量
- 整型字面量:数字后面添加L或者l,表示 Long: 23 0xFFFFFFFF 7L
- 浮点型字面量:有小数点标示Float,有小数点Double
- 布尔型字面量:true false
- 符号字面量 '<标识符>
- 字符字面量 单引号 ‘’ ‘a’ ‘\u0041’ ‘\n’
- 字符串字面量 双引号 “”,多行""" “”"
- Null 值
4.Scala 转义字符
转义字符 | Unicode | 描述 |
---|---|---|
\b | \u0008 | 退格(BS) ,将当前位置移到前一列 |
\t | \u0009 | 水平制表(HT) (跳到下一个TAB位置 |
\n | \u000a | 换行(LF) ,将当前位置移到下一行开头 |
\f | \u000c | 换页(FF),将当前位置移到下页开头 |
\r | \u000d | 回车(CR) ,将当前位置移到本行开头 |
\" | \u0022 | 代表一个双引号(")字符 |
\’ | \u0027 | 代表一个单引号(’)字符 |
\\ | \u005c | 代表一个反斜线字符 ‘\’ |
5.scala变量声明
在 Scala 中,使用关键词 "var" 声明变量,使用关键词 "val" 声明常量。
var VariableName : DataType [= Initial Value]
-
变量类型引用:其数据类型是通过变量或常量的初始值推断出来的。
var myVar = 10; val myVal = "Hello, Scala!"; scala> val pa = (40,"Foo") pa: (Int, String) = (40,Foo)
-
Scala 多个变量声明 val xmax, ymax = 100 // xmax, ymax都声明为100
如果方法返回值是元组
6.修饰
- private 嵌套类情况下,外层类甚至不能访问被嵌套类的私有成员。
- protected 它只允许保护成员在定义了该成员的的类的子类中被访问
- 如果没有指定任何的修饰符,则默认为 public。这样的成员在任何地方都可以被访问
- 作用域保护 private[x] 或者 protected[x]
package bobsrocckets{ package navigation{ private[bobsrockets] class Navigator{ protected[navigation] def useStarChart(){} class LegOfJourney{ private[Navigator] val distance = 100 } private[this] var speed = 200 } } package launch{ import navigation._ object Vehicle{ private[launch] val guide = new Navigator } } }
上述例子中,类 Navigator 被标记为 private[bobsrockets]就是说这个类对包含在 bobsrockets 包里的所有的类和对象可见。
比如说,从 Vehicle 对象里对Navigator的访问是被允许的,因为对象Vehicle 包含在包 launch 中,而 launch 包在 bobsrockets 中,相反,所有在包bobsrockets之外的代码都不能访问类 Navigator。
7.Scala 运算符
- 算术运算符 + - * / %
- 关系运算符 == != >= > <= <
- 逻辑运算符 && || !
- 位运算符 ~,&,|,^ >> << >>>
- 赋值运算符 = += -+ …
- 运算符优先级
类别 | 运算符 | 关联性 |
---|---|---|
1 | () [] | 左到右 |
2 | ! ~ | 右到左 |
3 | * / % | 左到右 |
4 | + - | 左到右 |
5 | >> >>> << | 左到右 |
6 | > >= < <= | 左到右 |
7 | == != | 左到右 |
8 | & | 左到右 |
9 | ^ | 左到右 |
10 | | | 左到右 |
11 | && | 左到右 |
12 | || | 左到右 |
13 | = += -= *= /= %= >>= <<= &= ^= |= | 右到左 |
14 | , | 左到右 |
8.控制结构 if/else while/do for
for( a <- 1 to 10) // 含义 10
for( a <- 1 until 10) // 不包含 10
for( a <- 1 to 3; b <- 1 to 3) // 嵌套先a 后bs
for( var x <- List )
for( var x <- List if condition1; if condition2...) //过滤
retVal = for{ var x <- List if condition1; if condition2... }yield x // 生成 list 赋值给 retVal
9.函数 def
- 基本语法: def main(args: Array[String]) :Unit{ 函数体 return ret}
有返回值的函数,没有return,则返回函数的执行的最后表达式是返回值 - 参数调用:{传值调用,传名调用},2类似于C指针def function( arg: => type ) = {}
- 可变参数: def printStrings( args:String* ) = {}
- 默认参数: def addInt( a:Int=5, b:Int=7 ) : Int = {}
- 函数嵌套
def factorial(i: Int): Int = {
def fact(i: Int, accumulator: Int): Int = {} - 指定函数参数名: def printInt( a:Int,b:Int)= {} printInt(b=5, a=7);
- 递归函数: n!计算;1.函数内调用自己 2.有返回点
- 高阶函数:函数指针,函数名作为其他函数的参数,格式:call:(T,T)=>String
def oprate[T](call:(T,T)=>T,a:T,b:T):T=call(a,b)
def sum(a:Int,b:Int):Int=a+b
oprate(sum,10,20)
- 匿名函数:
var mul = (x: Int, y: Int) => x*y //=后面的就是匿名函数
- 函数柯里化(Currying)的语法; 有多个参数列表 的函数就是柯里化函数
def add(x:Int,y:Int)=x+y // 普通函数
def add(x:Int)(y:Int) = x + y //柯里化函数;
def sum(f: Int => Int,a: Int, b: Int): Int = if(a > b) 0 else f(a) + sum(f,a + 1, b) //普通函数
def sum(f: Int => Int)(a: Int, b: Int): Int = if(a > b) 0 else f(a) + sum(f)(a + 1, b)//柯里化函数
/*声明函数
scala> def sum(f: Int => Int)(a: Int, b: Int): Int = if(a > b) 0 else f(a) + sum(f)(a + 1, b)
sum: (f: Int => Int)(a: Int, b: Int)Int
//计算3-8的平方和
scala> sum(x=>x*x)(3,8)
res3: Int = 199
// 计算3-8中2的n次幂之和
scala> def powerOfTwo(x: Int): Int = if(x == 0) 1 else 2 * powerOfTwo(x-1)
powerOfTwo: (x: Int)Int
scala> sum(powerOfTwo)(3,8)
res0: Int = 504
*/
- 闭包:嵌套 函数。 下面的例子包含一个匿名函数,返回的是函数指针
// 嵌套的函数: 可以访问一个函数里面局部变量的另外一个函数
var factor = 3
val multiplier= (i:Int) => i * factor // multiplier可以访问 匿名函数的 i;
def multi(factor:Int)=(i:Int)=>i * factor
multi(3)(5) //15
- 懒值(lazy值)函数:他的初始化会被推迟,直到第一次使用他的时候
scala> lazy val words2 = scala.io.Source.fromFile("d:\\temp\\b.txt").mkString
words2: String = <lazy>
- 部分应用(Partial application)
def adder(m: Int, n: Int) = m + n
// (_,1)像这样的标示函数的参数
val add2 = adder(2, _:Int)
add2(3)
- api高阶函数
函数名称 | 说明 |
---|---|
map | 计算每一个元素,返回一个包含相同数目的列表 |
foreach | 与map相似,没有返回值 |
filter | 移除传入返回false的元素。断言函数 |
zip | 2个列表合成一个列表 |
partition | 拆封 |
find | 匹配 |
flatten | 嵌套结果展开 |
flatMap | combinator,结合了map和flatten的功能 |
数据: val numbers = List(1,2,3,4,5,6,7,8,9,10)
map: 作用于列表中的每个元素,并且返回一个新的列表
numbers.map((i:Int) => i*2)
foreach: 跟map一样,没有返回值
numbers.foreach((i:Int) => i*2)
filter: 移除函数函数false的元素
返回所有能够被2整除的元素
numbers.filter((i:Int)=> i%2 ==0 )
zip: 把两个列表合并到一个列表中
List(1,2,3).zip(List(4,5,6))
数据: val numbers = List(1,2,3,4,5,6,7,8,9,10)
partition: 能被2整除的放到一个分区中,不能被整除的放到另一个分区中
numbers.partition((i:Int) => i%2 ==0)
find: 第一个匹配条件的元素
找到第一个能被3整除的元素
numbers.find((x:Int) => x%3 == 0)
numbers.find(_ % 3 == 0)
flatten: 把一个嵌套的结构展开
List(List(1,2,3),List(4,5,6)).flatten
flatMap: 压平,结合了map和flatten的功能
var myList = List(List(1,2,3),List(4,5,6))
myList.flatMap(x=>x.map(_ *2))
分为两步
1、List(1,2,3),List(4,5,6) ===> 合并成一个List
2、再乘以2
10.字符串 String
声明:val str1: String = "Hello World!"//(:String)可以省略
格式化:var fs = printf("浮点型变量为 " +
"%f, 整型变量为 %d, 字符串为 " +
" %s", floatVar, intVar, stringVar)
常用方法总结:
序号 | 方法及描述 |
---|---|
1 | char charAt(int index) 返回指定位置的字符 |
2 | int compareTo(Object o) 比较字符串与对象 |
3 | int compareTo(String anotherString) 按字典顺序比较两个字符串 |
4 | int compareToIgnoreCase(String str) 按字典顺序比较两个字符串,不考虑大小写 |
5 | String concat(String str) 将指定字符串连接到此字符串的结尾 |
6 | boolean contentEquals(StringBuffer sb) 将此字符串与指定的 StringBuffer 比较。 |
7 | static String copyValueOf(char[] data) 返回指定数组中表示该字符序列的 String |
8 | static String copyValueOf(char[] data, int offset, int count) 返回指定数组中表示该字符序列的 String |
9 | boolean endsWith(String suffix) 测试此字符串是否以指定的后缀结束 |
10 | boolean equals(Object anObject) 将此字符串与指定的对象比较 |
11 | boolean equalsIgnoreCase(String anotherString) 将此 String 与另一个 String 比较,不考虑大小写 |
12 | byte getBytes() 使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中 |
13 | byte[] getBytes(String charsetName 使用指定的字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中 |
14 | void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) 将字符从此字符串复制到目标字符数组 |
15 | int hashCode() 返回此字符串的哈希码 |
16 | int indexOf(int ch) 返回指定字符在此字符串中第一次出现处的索引 |
17 | int indexOf(int ch, int fromIndex) 返回在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索 |
18 | int indexOf(String str) 返回指定子字符串在此字符串中第一次出现处的索引 |
19 | int indexOf(String str, int fromIndex) 返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始 |
20 | String intern() 返回字符串对象的规范化表示形式 |
21 | int lastIndexOf(int ch) 返回指定字符在此字符串中最后一次出现处的索引 |
22 | int lastIndexOf(int ch, int fromIndex) 返回指定字符在此字符串中最后一次出现处的索引,从指定的索引处开始进行反向搜索 |
23 | int lastIndexOf(String str) 返回指定子字符串在此字符串中最右边出现处的索引 |
24 | int lastIndexOf(String str, int fromIndex) 返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索 |
25 | int length() 返回此字符串的长度 |
26 | boolean matches(String regex) 告知此字符串是否匹配给定的正则表达式 |
27 | boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) 测试两个字符串区域是否相等 |
28 | boolean regionMatches(int toffset, String other, int ooffset, int len) 测试两个字符串区域是否相等 |
29 | String replace(char oldChar, char newChar) 返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的 |
30 | String replaceAll(String regex, String replacement 使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串 |
31 | String replaceFirst(String regex, String replacement) 使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串 |
32 | String[] split(String regex) 根据给定正则表达式的匹配拆分此字符串 |
33 | String[] split(String regex, int limit) 根据匹配给定的正则表达式来拆分此字符串 |
34 | boolean startsWith(String prefix) 测试此字符串是否以指定的前缀开始 |
35 | boolean startsWith(String prefix, int toffset) 测试此字符串从指定索引开始的子字符串是否以指定前缀开始。 |
36 | CharSequence subSequence(int beginIndex, int endIndex) 返回一个新的字符序列,它是此序列的一个子序列 |
37 | String substring(int beginIndex) 返回一个新的字符串,它是此字符串的一个子字符串 |
38 | String substring(int beginIndex, int endIndex) 返回一个新字符串,它是此字符串的一个子字符串 |
39 | char[] toCharArray() 将此字符串转换为一个新的字符数组 |
40 | String toLowerCase() 使用默认语言环境的规则将此 String 中的所有字符都转换为小写 |
41 | String toLowerCase(Locale locale) 使用给定 Locale 的规则将此 String 中的所有字符都转换为小写 |
42 | String toString() 返回此对象本身(它已经是一个字符串!) |
43 | String toUpperCase() 使用默认语言环境的规则将此 String 中的所有字符都转换为大写 |
44 | String toUpperCase(Locale locale) 使用给定 Locale 的规则将此 String 中的所有字符都转换为大写 |
45 | String trim() 删除指定字符串的首尾空白符 |
46 | static String valueOf(primitive data type x) 返回指定类型参数的字符串表示形式 |
11. 数组
- 数组定义和基本操作
import scala.collection.mutable.ArrayBuffer
//Scala数组
//类型一:定长数组 Array
val a = new Array[Int](10)
val b = new Array[String](3)
var c = Array("Tom","Mary","Mike")
//类型二:变长数组 ArrayBuffer
val d = new ArrayBuffer[Int]()
//往变长数组中添加元素
d += 1
d += 2
d += 3
d += 4
//一次添加多个元素
d += (10,20,30)
d.trimEnd(2) //去掉尾部的两个元素
//把ArrayBuffer --> Array
d.toArray
//遍历数组
//foreach 没有返回值;map有一个返回值
d.foreach(println)
d.map(println)
//常见的数组的操作
val myArray = Array(1,100,10,24,5,57)
//最大、最小、求和
myArray.max
myArray.min
myArray.sum
//排序: 以下两个排序是一样
myArray.sortWith(_ > _)
//完整一点
myArray.sortWith((a,b)=> a>b)
- 多维数组
var myMatrix = Array.ofDim[Int](3,4)
for(i<- 0 until 3; j<- 0 until 4 ){ myMatrix(i)(j) = j}
for(i<- 0 until 3){for(j<- 0 until 4 ){ print(" "+ myMatrix(i)(j)) }; println("")}
12.异常
// throw 抛出异常
// eg: throw new IllegalArgumentException
// 抛出的异常类型是 Nothing
def halt(n:Int): Unit ={
if(n%2==0)
println(n + " 是偶数")
else
throw new RuntimeException(n+ " 是奇数")
}
//使用 try catch finally 捕获异常
try{
val words = scala.io.Source.fromFile("d:\\temp\\a.txt").mkString
}catch {
case ex:java.io.FileNotFoundException => {
println("File Not Found")
}
case IllegalArgumentException => {
println("Illegal Argument Exception")
}
case _:Exception => {
println("Other Exception")
}
}finally{
println("Finally Block")
}
// 案例2
import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOException
object Test {
def main(args: Array[String]) {
try {
val f = new FileReader("input.txt")
} catch {
case ex: FileNotFoundException =>{
println("Missing file exception")
}
case ex: IOException => {
println("IO Exception")
}
}
}
}
13.文件读写
import java.io.{File, FileInputStream, PrintWriter}
//读取行
val source1 =scala.io.Source.fromFile("e:\\BigdData\\learn\\note\\0102_web.txt")
//1.整个文件读写
println(source1.mkString)
//2、按照行读写
val source2 =scala.io.Source.fromFile("e:\\BigdData\\learn\\note\\0102_web.txt")
val lines =source2.getLines()
for(l <-lines) println(l.toString)
//3.读取字符
val source3 =scala.io.Source.fromFile("e:\\BigdData\\learn\\note\\0102_web.txt")
for(c <-source3) print(c)
//4、Url读取
val source4 =scala.io.Source.fromURL("http://www.baidu.com/","UTF-8")
println(source4.mkString)
//5.二进制文 d g件读写
val file =new File("e:\\BigdData\\learn\\note\\0102_web.txt")
val in =new FileInputStream(file)
val buffer =new Array[Byte](file.length().toInt)
in.read(buffer)
in.close()
//6.文件的写入
var out =new PrintWriter("e:\\BigdData\\learn\\note\\tmp.txt")
for(i <- 1 to 20) out.print(i + "\t")
out.close()
14.Scala Collection
列表是不可变的,值一旦被定义了就不能改变
构造列表的两个基本单位是 Nil 和 ::
Nil标示空列表,::标示数据之间的连接关系
//声明一个lsit
// 字符串列表
val site: List[String] = List("Runoob", "Google", "Baidu")
// 整型列表
val nums: List[Int] = List(1, 2, 3, 4)
// 也可以如下
// 字符串列表
val site = "Runoob" :: ("Google" :: ("Baidu" :: Nil))
// 整型列表
val nums = 1 :: (2 :: (3 :: (4 :: Nil)))
```java
2. #### Map
(key/value)
```java
// 空哈希表,键为字符串,值为整型
var A:Map[Char,Int] = Map()
A += ('I' -> 1)
// Map 键值对演示
val colors = Map("red" -> "#FF0000", "azure" -> "#F0FFFF")
//Map 合并
// ++ 作为运算符
var colors = colors1 ++ colors2
// ++ 作为方法
colors = colors1.++(colors2)
//定义 Tuple6中的6标示元组长度为6;
val t = (1, 3.14, "Fred")
val t = new Tuple3(1, 3.14, "Fred")
//元组的实际类型取决于它的元素的类型,比如 (99, "runoob") 是 Tuple2[Int, String]
//('u', 'r', "the", 1, 4, "me") 为 Tuple6[Char, Char, String, Int, Int, String]
//目前 Scala 支持的元组最大长度为 22,
//我们可以使用 t._1 访问第一个元素, t._2 访问第二个元素
val sum = t._1 + t._2 + t._3 + t._4
//迭代
t.productIterator.foreach{ i =>println("Value = " + i )}
[^] 果你想使用可变集合,需要引用 scala.collection.mutable.Set 包。默认引用 scala.collection.immutable.Set
object Test {
def main(args: Array[String]) {
val site1 = Set("Runoob", "Google", "Baidu")
val site2 = Set("Faceboook", "Taobao")
// ++ 作为运算符使用
var site = site1 ++ site2
println( "site1 ++ site2 : " + site )
// ++ 作为方法使用
site = site1.++(site2)
println( "site1.++(site2) : " + site )
}
}
Scala Option(选项)类型用来表示一个值是可选的(有值或无值)。
Option[T] 是一个类型为 T 的可选值的容器: 如果值存在, Option[T] 就是一个 Some[T] ,如果不存在, Option[T] 就是对象 None 。
val myMap: Map[String, String] = Map("key1" -> "value")
val value1: Option[String] = myMap.get("key1")
val value2: Option[String] = myMap.get("key2")
println(value1) // Some("value1")
println(value2) // None
15. 模式匹配
//模式匹配
//1、Scala中的switch ... case...
//根据一个变量的值,判断是做加法运算、还是减法运算
var ch1 = '-'
var flag = 0 //flag<0 减法 > 0 加法
ch1 match{
case '+' => flag = 1
case '-' => flag = -1
case _ => flag = 0
}
println(flag) // -1
//2、Scala的守卫:匹配某种类型的所有值
//匹配的所有的数字
var ch2 = '6'
var digit:Int = -1
ch2 match{
case '+' =>println("这是一个加号")
case '-' =>println("这是一个减号")
case _ if Character.isDigit(ch2) => digit = Character.digit(ch2,10)
case _ => println("其他类型")
}// digit=6
println(digit) //6
//3、模式匹配中使用变量
var str3 = "Hello World"
str3(7) match {
case '+' =>println("这是一个加号")
case '-' =>println("这是一个减号")
case ch => println("这个字符是:"+ch)
}//这个字符是:o
//4、类型的模式
//Any: 任意的类型,类似Java中的Object
var v4:Any = 100
v4 match {
case x:Int => println("这是一个整数")
case s:String => println("这是一个字符串")
case _ => println("其他类型")
}//这是一个整数
//5、数组和列表
var myArray = Array(1,2,3)
myArray match{
case Array(0) => println("0")
case Array(x,y) => println("该数组中包含两个元素,和是:" + (x+y))
case Array(x,y,z) => println("该数组中包含三个元素,和是:" + (x+y+z))
case Array(x,_*) => println("这是一个数组")
}//该数组中包含三个元素,和是:6
//样本类匹配
case class Student(name:String,age:Int)
16.样本类
//使用样本类支持模式匹配: 类似: instanceof
class Vehicle
case class Car(name:String) extends Vehicle
case class Bike(name:String) extends Vehicle
//定义一个Car对象
var aCar:Vehicle = new Car("这是汽车")
aCar match {
case Car(name) => println("这是一辆汽车")
case Bike(name)=> println("这是一辆自行车")
case _ => println("其他类型")
}//这是一辆汽车
第三章 scala面对对象编程
1.面向对象的概念
把数据和操作封装在一起//属性 + 方法
面向对象的3大特点:封装 继承 多态
2.类的定义
class修饰符:private[x] 或者 protected[x]
作用域保护,x 标示包的名字
class Student {
//定义属性
private var stuName:String ="Tom"
private var stuAge:Int=20
//定义方法
def getStuName():String=stuName
def setStuname(name:String)=this.stuName=name
def getStuAge():Int=stuAge
def setStuAge(age:Int)=this.stuAge=age
}
3.属性的getter和setter方法
/*
属性的get和set方法:
1、当定义属性的时候,如果是private,Scala会自动生成对应的set和get方法
private var stuName:String = "Tom"
(1) get方法:stuName
(2) set方法: stuName_
2、如何只有get方法,没有set方法? ---> 将属性定义为: 常量 val
private val money:Int = 1000
3、不希望生成get和set方法: private[this]
该属性只属于该对象私有
*/
class Student2 {
//定义属性
private var stuName:String = "Tom"
//只有get方法
private val money:Int = 1000
}
object Student2{
def main(args: Array[String]): Unit = {
var s2 = new Student2
println(s2.stuName)
//修改money值 ===> error 错误
//s2.money = 2000
}
}
4.内部类–嵌套类
import scala.collection.mutable.ArrayBuffer
class Student3 {
//定义一个内部类(嵌套类): 学生选修的课程
//通过主构造器
class Course(val courseName:String,val credit:Int){
//其他的方法
}
//属性
private var stuName:String = "Tom"
private var stuAge:Int = 20
//定义一个数组来保存该学生选修的课程
private var courseList = new ArrayBuffer[Course]()
//定义方法:往学生信息中添加新的课程
def addNewCourse(cname:String,credit:Int): Unit ={
//创建一门课程
var c = new Course(cname,credit)
//加入list
courseList += c
}
}
object Student3{
def main(args: Array[String]): Unit = {
//创建学生
var s3 = new Student3
//给学生添加课程
s3.addNewCourse("Chinese",3)
s3.addNewCourse("English",3)
s3.addNewCourse("Math",3)
//输出
println(s3.stuName+"\t"+s3.stuAge)
for(s <- s3.courseList) println(s.courseName + "\t" + s.credit)
}
}
5. 类的构造器
/*
构造器:(1)主构造器 : 和类的申明在一起,只能有一个主构造器
(2)辅助构造器: 多个辅助构造器,通过关键字:this
*/
class Student4(val stuName:String,val stuAge:Int) {
//定义一个辅助构造器
def this(age:Int){
//调用主构造器
this("No Name",age)
}
}
object Student4{
def main(args: Array[String]): Unit = {
//使用主构造器创建学生对象
var s4 = new Student4("Tom",24)
println(s4.stuName + "\t" + s4.stuAge)
//使用辅助构造器创建学生对象
var s5 = new Student4(25)
println(s5.stuName + "\t" + s5.stuAge)
}
}
6.Scala中的Object对象
// 相当于static;静态对象
object CreditCard {
//定义变量:保存信用卡的卡号
//该属性只属于该对象
private[this] var creditCardNumbe:Long = 0
//产生卡号
def generateNewCCNumber():Long = {
creditCardNumbe += 1
creditCardNumbe
}
//测试程序
def main(args: Array[String]): Unit = {
//得到新的卡号
println(CreditCard.generateNewCCNumber())
println(CreditCard.generateNewCCNumber())
println(CreditCard.generateNewCCNumber())
println(CreditCard.generateNewCCNumber())
}
}
// 没有main函数,继承系统的App
object MainAppObject extends App {
//创建一个main方法
// def main(args: Array[String]): Unit = {
// println("Hello World")
// }
println("Hello World")
}
7.Scala的apply方法
/** apply
* 作用:省略new关键字,伴生对象创建类对象 类似于java构造函数,创建出来的对象可以调用成员和调用函数方法
* 下面2个例子:Foo测试案例 SDK库函数Array
* unapply用于模式匹配
*/
class Foo(val name:String){
def print():Unit={
println(name)
}
}
object FooMaker {
//定义类的apply方法
def apply(name:String) = {
println("调用到了apply方法")
//调用主构造器
new Foo(name)
}
}
class FooMaker2 {
//定义类的apply方法
def apply(name:String) = {
println("调用到了FooMaker2.apply方法")
//调用主构造器
new Foo(name)
}
}
class FooMaker3 (val name:String){
//定义类的apply方法
def apply(name:String) = {
println("调用到了FooMaker2.apply方法")
//调用主构造器
new Foo(name)
}
}
object FooMain {
def main(args: Array[String]): Unit = {
//1.创建Foo对象
var f1 = new Foo("Tom")
f1.print
println(f1.name)
//2.创建 Foo 对象,调用object 省略new关键字
var f2 = FooMaker("Mary")
println(f2.name)
//f2.print
//3.调用class,无参构造,调用处传入参数,才能够创建Foo对象
var f3 =new FooMaker2()
println(f3("Jetty").name)
f3("Jetty").print()
//4.与3类似,需要类似于柯里化函数
var f4 =new FooMaker2()("Jetty")
println(f4.name)
f4.print
//5.具有相同字段name,调用f5.name,显示 Tomy,即Foo的成员对象
var f5 =new FooMaker3("Make")("Tomy")
println(f5.name)
f5.print
}
}
val arr = new Array[Int](3)
val array = Array(1,2,3)
// 查看sdk,object Array就是class Array 伴生对象,点进去看构造函数
// arr是调用class Array 创建出来的
final class Array[T](_length : scala.Int) extends scala.AnyRef with java.io.Serializable with java.lang.Cloneable {...}
// array是Array对象创建的 内部 apply创建Array对象;
object Array extends scala.FallbackArrayBuilding with scala.Serializable {...
def apply(x : scala.Int, xs : scala.Int*) : scala.Array[scala.Int] = {...}
...}
// unapply用于模式匹配
class Money(val value: Double, val country: String) {}
object Money {
def apply(value: Double, country: String) : Money = new Money(value, country)
def unapply(money: Money): Option[(Double, String)] = {
if(money == null) {
None
} else {
Some(money.value, money.country)
}
}
}
val money = Money(10.1, "RMB")
money match {
case Money(num, "RMB") => println("RMB: " + num)
case _ => println("Not RMB!")
}
/** 运行结果
RMB: 10.1
*/
8.Scala中的继承
//继承 关键字:extends override
/*
1、基本的继承
2、复写父类中的方法
3、使用匿名子类
*/
//定义父类
class Person(val name:String,val age:Int){
//方法(函数)
def sayHello():String = "Hello " + name + " and the age is " + age;
}
//定义子类
class Employee(override val name:String,override val age:Int,salary:Int) extends Person(name,age){
//重写父类中的sayHello方法 重写没有注解
override def sayHello(): String = "子类中的sayHello方法"
}
object Demo1 {
def main(args: Array[String]): Unit = {
//创建一个Person对象
var p1 = new Person("Tom",20)
println(p1.sayHello())
//创建一个子类
var p2:Person = new Employee("Mike",25,1000)
println(p2.sayHello())
//创建一个匿名子类,从Person继承
var p3:Person = new Person("Jerry",26){
//在匿名子类中,重写父类的方法
override def sayHello(): String = "匿名子类中的sayHello方法"
}
println(p3.sayHello())
}
}
//抽象类型: 关键字 abstract 只能继承一个抽象类
//父类:抽象 交通工具
abstract class Vehicle{
//定义一个抽象方法
def checkType():String
}
//子类
class Car extends Vehicle{
//实现抽象方法
def checkType():String = {"I am a Car"}
}
class Bike extends Vehicle{
//实现抽象方法
def checkType():String = {"I am a Bike"}
}
object Demo2 {
def main(args: Array[String]): Unit = {
var v1:Vehicle = new Car
var v2:Vehicle = new Bike
println(v1.checkType())
println(v2.checkType())
}
}
//提供抽象字段
//父类:抽象
abstract class Person{
//就是一个抽象字段
var id:Int
var name:String
}
//一种做法
abstract class Employee1 extends Person{
//var id:Int = 1
var name:String = "No Name"
}
//另一种做法: 定义一个主构造器,在主构造器中,提供抽象字段
class Employee2(var id:Int) extends Person{
//只提供name
var name:String = "No Name"
}
9.Scala的trait(特制)
/* 特质/trait:支持多重继承,相当于Java 的抽象类,可以定义属性和方法的实现,但是没有构造函数;
一般情况下Scala的类只能够继承单一父类,但是如果是 Trait的话就可以继承多个,从结果来看就是实现了多重继承
*/
//定义第一个trait
trait Human{
//定义抽象字段
var id:Int
var name:String
// 方法:可以是抽象的 也可以不是抽象
def sayHello():String = "Hello " + name
}
//定义第二个trait
trait Action{
//定义一个抽象方法
def getActionName():String
}
//定义一个子类:从上面的两个trait继承
//关键字: extends ... with
class Student6(var id:Int,var name:String) extends Human with Action{
//实现Action中的getActionName
def getActionName():String = "Action is Running"
}
object Demo4 {
def main(args: Array[String]): Unit = {
//创建一个学生对象
var s6 = new Student6(1,"Tom")
println(s6.sayHello())
println(s6.getActionName())
}
}
-
Scala中特质trait与抽象类abstract的区别:
优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类。 如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数,而特质不行
-
trait构造顺序
- 调用超类的构造器;
- 特征构造器在超类构造器之后、类构造器之前执行;
- 特征由左到右被构造;
- 每个特征当中,父特征先被构造;
- 如果多个特征共有一个父特征,父特征不会被重复构造
- 所有特征被构造完毕,子类被构造。
10.包的使用 包对象
package 包
import …引入 ,可以再任何地方
包可以是 类 对象和特制;(class object trait)
包对象:常量 变量 方法 类 对象 trait
package com.twitter.example
// ._ 属于隐式导入,作用类似于 java 中的.* ,标示包下的所有类
import java.io._
// 可以出现在不同的文件内
package com.sunny.scala {
package service {
class Test {}
}
}
第四章 scala高级特性
1.泛型类
//问题:能否有一个通用的类,既能操作Int、也能操作String
class GenericClass[T]{
//定义一个变量
private var content:T = _
//定义get和set方法
def set(value:T) = {content = value}
def get():T = {content}
}
object GenericClass {
def main(args: Array[String]): Unit = {
//操作Int
var intGeneric = new GenericClass[Int]
intGeneric.set(123)
println("得到的值:" + intGeneric.get())
//操作String
var stringGeneric = new GenericClass[String]
stringGeneric.set("Hello Scala")
println("得到的值:" + stringGeneric.get())
}
}
2.泛型函数
//创建一个函数,功能:创建一个Int类型的数组
def mkIntArray(elems:Int*) = Array[Int](elems:_*)
mkIntArray(1,2,3,4,5)
//创建一个函数,功能:创建一个String类型的数组
def mkStringArray(elems:String*) = Array[String](elems:_*)
mkStringArray("Tom","Mary","Mike")
//问题:能否创建一个通用的函数,既能创建Int类型数组,也能创建String类型的数组
//在泛型函数中:T有要求:必须是ClassTag类型
import scala.reflect.ClassTag
def mkArray[T:ClassTag](elems:T*) = Array[T](elems:_*)
mkArray(1,2,3,4,5)
mkArray("Tom","Mary","Mike")
//ClassTag: 表示执行Scala程序时的运行信息,该例子中,表示数据的类型
// 类似于 public class Range[T extends Comparable<T>] {}
3.Upper Bounds与Lower Bounds
举例:
(1) 普通数据类型
int x 范围 00 <= x <= 200
下界 上界
(2)规定:泛型的取值范围
继承关系 A —> B —> C —> D
D <: y泛型 <: B ====> 表示:y的类型只能是:B C D
概念
上界:定义 S <: T,表示S的类型必须是T的子类
下界:定义 U >: T 表示U的类型必须是T的父类
//上界的例子
//父类: 代表所有的交通工具
class Vehicle{ def drive() = {println("Driving")} }
//定义两个子类
class Car extends Vehicle{ override def drive(): Unit = {println("Car Driving")} }
class Bike extends Vehicle{override def drive(): Unit = {println("Bike Driving")} }
class Apple{
}
object ScalaUpperBound {
//定义一个泛型函数 T 是 Vehicle 和 Vehicle的子类
def takeVehicle[T <: Vehicle](v:T) = { v.drive() }
def main(args: Array[String]): Unit = {
//定义一个交通工具
var v1:Vehicle = new Vehicle
takeVehicle(v1)
var v2:Car = new Car
takeVehicle(v2)
//能否使用Apple?------->结果 不可以
var v3:Apple =new Apple
takeVehicle(v3)
}
}
4.View Bounds 视图界定
// 定义:<%
// 接收除了所有的子类,还允许接收隐式转换过去的类型 -->功能增强,降低了可读性
def addTwoString[T <% String](x:T,y:T) = {println(x+"****"+y)}
//调用:
addTwoString("Hello","World")
//隐式转换函数
implicit def int2String(n:Int):String = {n.toString}
addTwoString(100,200)
//错误:error: No implicit view available from Int => String.
/* 视图界定的时候:一定有一个转换规则(隐式转换函数)
执行过程: (1) 调用隐式转换函数int2String: int ====> String
100 ---> "100"
200 ---> "200"
(2) 再调用addTwoString
*/
5.协变与逆变
[+T] 协变:泛型变量的值可以是本身类型或者子类
[-T] 逆变 :泛型变量的值可以是本身类型或者父类
//协变:可以理解为 父类指针可以指向子类对象
//逆变 : 可以理解为 父类指针可以指向父类对象
//Scala的协变: 一个泛型类接收的泛型参数的值可以是本身的类型或者是子类的类型
// 在泛型前 +
//动物
class Animal{}
//子类:鸟
class Bird extends Animal
//子类:麻雀
class Sparrow extends Bird
//类:吃东西(动作)
class EatSomething[+T](t:T){}
object DemoClass1 {
def main(args: Array[String]): Unit = {
//创建一个鸟吃东西的对象
var c1:EatSomething[Bird] = new EatSomething[Bird](new Bird)
//创建一个动物吃东西的对象
//var c2:EatSomething[Animal] = new EatSomething[Animal](new Animal)
//问题:能否将c1付给c2?
//原因:尽管Bird是Animal的子类,但是EatSomething[Bird]不是EatSomething[Animal]的子类
//把EatSomething[Bird] 变成 EatSomething[Animal] 子类
var c2:EatSomething[Animal] = c1
//再举个例子
var c3:EatSomething[Sparrow] = new EatSomething[Sparrow](new Sparrow)
var c4:EatSomething[Animal] = c3
}
}
案例2:
//逆变:可以理解为 子类指针指向 父类对象
//Scala的逆变: 一个泛型类接收的泛型参数的值可以是本身的类型或者是父类的类型
// 语法: -号
//动物
class Animal{}
//子类:鸟
class Bird extends Animal
//子类:麻雀
class Sparrow extends Bird
//定义吃东西的类
class EatSomething[-T](t:T){}
object DemoClass2 {
def main(args: Array[String]): Unit = {
//创建一个鸟吃东西的对象
var c1:EatSomething[Bird] = new EatSomething[Bird](new Bird)
//创建一个麻雀吃东西的对象
//var c2:EatSomething[Sparrow] = new EatSomething[Sparrow](new Sparrow)
//问题:能否将c1 付给c2?
//原因:尽管Bird是Sparrow的父类,但是EatSomething[Bird]不是EatSomething[Sparrow]的父类
var c2:EatSomething[Sparrow] = c1
}
}
6.隐式转换函数
//隐式转换函数
implicit def int2String(n:Int):String = {n.toString}
//定义一个隐式转换函数(规则): Fruit对象 ===> Monkey对象
implicit def fruit2Monkey(f:Fruit):Monkey = {new Monkey(f)}
// 隐式转换类中应用
package demo3
//水果
class Fruit(name:String){
def getFruitName():String = {name}
}
//猴子:Monkey
class Monkey(f:Fruit){
def say() = {println("Monkey like " + f.getFruitName() ) }
}
object ImplicitDemo {
//定义一个隐式转换函数(规则): Fruit对象 ===> Monkey对象
implicit def fruit2Monkey(f:Fruit):Monkey = {new Monkey(f)}
def main(args: Array[String]): Unit = {
//创建一个水果的对象
var f:Fruit = new Fruit("香蕉")
//希望:直接调用 f.say()方法???
//问题:将Fruit的对象 转换成 Monkey对象?
f.say()
}
}
7.隐式参数
//implicit 声明参数是隐式参数。 但是必须在函数外部申明变量, 主要作用:省却参数---有默认值
//Scala的隐式参数
def testParam(implicit name:String) = {println("The value is " + name)}
//定义一个隐式参数
implicit val name:String = "这是一个隐式参数"
//调用:不想传递参数
testParam
testParam("OK")
8.隐式类
implicit 类的功能增强,隐式类只能有一个构造参数
/* 常量值的功能加强;类的取值作为类对象
隐式类的功能:对类的功能进行加强,
顺序:
第一步:先调用隐式转换类,把 1 ===> Calc对象
第二步:调用Calc对象的add方法
*/
object ImplicitDemo4 {
//定义一个隐式类 :
implicit class Calc(x:Int){
def add(y:Int) = x + y
}
def main(args: Array[String]): Unit = {
//进行两个数的相加 1.add(2)
println("两个数字的和是:" + 1.add(2))
}
}
文章参考: