摘自:http://www.runoob.com/scala/scala-tutorial.html
认识Scala:scala是一门多范式的编程语言,设计初衷是要集成面向对象编程和函数式编程的各种特性。运行在java虚拟机上并兼容现有的java程序,与Java一样,scala源代码会被编译成Java字节码运行在JVM之上。
几个特性:
基本语法:
区分大小写
一般来讲类名要和文件名一致,类名第一个字母大写,方法名第一个字母小写
程序从def main(args:Array[String]) 处的main方法开始处理。每一个scala程序的入口部分。
语句可以使用分号;结束或者换行符,但是语句末尾的分号一般都是可选的,可以输入一个,若一行只有一个语句,也可以不写。
Scala 包
定义包
Scala 使用 package 关键字定义包,在Scala将代码定义到某个包中有两种方式:
第一种方法和 Java 一样,在文件的头定义包名,这种方法就后续所有代码都放在该包中。 比如:
package com.runoob
class HelloWorld
第二种方法有些类似 C#,如:
package com.runoob {
class HelloWorld
}
第二种方法,可以在一个文件中定义多个包。
引用
Scala 使用 import 关键字引用包。
import java.awt.Color // 引入Color
import java.awt._ // 引入包内所有成员
def handler(evt: event.ActionEvent) { // java.awt.event.ActionEvent
… // 因为引入了java.awt,所以可以省去前面的部分
}
import语句可以出现在任何地方,而不是只能在文件顶部。import的效果从开始延伸到语句块的结束。这可以大幅减少名称冲突的可能性。
如果想要引入包中的几个成员,可以使用selector(选取器):
import java.awt.{Color, Font}
// 重命名成员
import java.util.{HashMap => JavaHashMap}
// 隐藏成员
import java.util.{HashMap => , } // 引入了util包的所有成员,但是HashMap被隐藏了
注意:默认情况下,Scala 总会引入 java.lang. 、 scala. 和 Predef._,这里也能解释,为什么以scala开头的包,在使用时都是省去scala.的。
变量声明:变量的类型在变量名之后等号之前声明。定义变量的类型的语法格式如下:
var VariableName : DataType [= Initial Value]
或
val VariableName : DataType [= Initial Value]
变量声明一定需要初始值,否则会报错。
在 Scala 中声明变量和常量不一定要指明数据类型,在没有指明数据类型的情况下,其数据类型是通过变量或常量的初始值推断出来的。
所以,如果在没有指明数据类型的情况下声明变量或常量必须要给出其初始值,否则将会报错。
var myVar = 10;
val myVal = “Hello, Scala!”;
Scala 多个变量声明
Scala 支持多个变量的声明:
val xmax, ymax = 100 // xmax, ymax都声明为100
如果方法返回值是元组,我们可以使用 val 来声明一个元组:
scala> val pa = (40,“Foo”)
pa: (Int, String) = (40,Foo)
基本语句算法:
def main(args: Array[String]): Unit = {
println("sd", "123", 123 + 12);
Byte2byte();
var num: Int = 12
if (num < 13){
print(num < 13);
}else if(true){
}
val loop = new Breaks;
loop.breakable{
while(true){
println("1");
//break; //scala语言不支持break关键字打断,但是实现循环中断一般使用下面的写法.并且支持多层Break嵌套循环中断
loop.break;
}
}
do{
num += 1
println(num);
}while(num <= 14)
var a = 0
for (a <- 1 to 10){
println(a);
}
for (a <- 1 until 10){
println(a);
}
var a1 = 0;
var b = 0;
for(a1 <- 1 to 10;b <- 1 to 2){ //双层嵌套循环
println("a:"+a1+",b:"+b);
}
val numList = List(1,2,3,4);
for(a <- numList if a != 2 ){ //list循环,,可以包含if条件句
println("numList:"+ a);
}
//可以使用yield关键字将for循环的返回值作为一个变量存储,如下
var resVal = for{ x <- numList if x != 3}yield x
// 输出返回值
for( a <- resVal){
println( "Value of a: " + a );
}
}
}
函数和方法:
1.函数可以作为一个参数传入到方法中,方法不行
object MethodAndFunctionDemo {
//定义一个方法
//方法 m1 参数要求是一个函数,函数的参数必须是两个Int类型
//返回值类型也是Int类型
def m1(f:(Int,Int) => Int) : Int = {
f(2,6)
}
//定义一个函数f1,参数是两个Int类型,返回值是一个Int类型
val f1 = (x:Int,y:Int) => x + y
//再定义一个函数f2
val f2 = (m:Int,n:Int) => m * n
//main方法
def main(args: Array[String]): Unit = {
//调用m1方法,并传入f1函数
val r1 = m1(f1)
println(r1)
//调用m1方法,并传入f2函数
val r2 = m1(f2)
println(r2)
}
}
2.scala中无法直接操作方法,如果要操作方法,比如某个方法内要传入一个方法作为变量,必须先将其转换为函数,有两种方法将方法转换成函数:val f1 = m _
在方法名称m后面紧跟一个空格和下划线告诉编译器将方法m转换成函数,而不是要调用这个方法。 也可以显示地告诉编译器需要将方法转换成函数:
val f1: (Int) => Int = m
通常情况下编译器会自动将方法转换成函数,例如在一个应该传入函数参数的地方传入了一个方法,编译器会自动将传入的方法转换成函数。
3.函数必须有参数列表,方法可以没有参数列表
4.在函数出现的地方我们可以提供一个方法,也就是有些需要函数的地方,如果传递了一个方法,会自动进行ETA展开就是说把方法转换为函数。
5.函数中的传值和传引用:
Scala的解释器在解析函数参数(function arguments)时有两种方式:
传值调用(call-by-value):先计算参数表达式的值,再应用到函数内部;
传名调用(call-by-name):将未计算的参数表达式直接应用到函数内部
在进入函数内部前,传值调用方式就已经将参数表达式的值计算完毕,而传名调用(相当于引用)是在函数内部进行参数表达式的值计算的。
这就造成了一种现象,每次使用传名调用时,解释器都会计算一次表达式的值。
以上实例中我们声明了 delayed 方法, 该方法在变量名和变量类型使用 => 符号来设置传名调用。
object Test {
def main(args: Array[String]) {
delayed(time());
}
def time() = {
println("获取时间,单位为纳秒")
System.nanoTime
}
def delayed( t: => Long ) = {//加不加=>符号,输出结果不一样,就可以看出来
println("在 delayed 方法内")
println("参数: " + t)
t
}
}
6.可变参数:Scala 允许你指明函数的最后一个参数可以是重复的,即我们不需要指定函数参数的个数,可以向函数传入可变长度参数列表。
Scala 通过在参数的类型之后放一个星号来设置可变参数(可重复的参数)。例如:
object Test {
def main(args: Array[String]) {
printStrings("Runoob", "Scala", "Python");
}
def printStrings( args:String* ) = {
var i : Int = 0;
for( arg <- args ){
println("Arg value[" + i + "] = " + arg );
i = i + 1;
}
}
}
7.可以设置函数的默认值,如果调用的时候没有传入参数,就会使用默认参数值。若传入了则函数传递值会取代默认值:
def addInt( a:Int=5, b:Int=7 ) : Int = {//5,7就是这个函数的默认值。
var sum:Int = 0
sum = a + b
return sum
}
8.函数内可以再定义函数,称为局部函数。也即是函数嵌套
9.偏应用函数,也就是一种表达式:不需要提供函数需要的所有参数,只需要提供部分或者不提供。比如下面的例子中,绑定第一个data参数,第二个参数使用下划线(_)替换缺失的参数列表,并把这个新的函数值的索引的赋给变量。以上实例修改如下:
import java.util.Date
object Test {
def main(args: Array[String]) {
val date = new Date
val logWithDateBound = log(date, _ : String)
logWithDateBound("message1" )
Thread.sleep(1000)
logWithDateBound("message2" )
Thread.sleep(1000)
logWithDateBound("message3" )
}
def log(date: Date, message: String) = {
println(date + "----" + message)
}
}
10.可以指定函数参数名进行参数传递,并不一定按照函数定义时候参数的顺序:
object Test {
def main(args: Array[String]) {
printInt(b=5, a=7);
}
def printInt( a:Int, b:Int ) = {
println("Value of a : " + a );
println("Value of b : " + b );
}
}
11.支持递归函数,即调用自身
12.高阶函数:就是可以在某个函数内操作其他函数,比如说传入进来的一个函数。
13.匿名函数的定义:var inc = (x:Int) => x+1
14.函数柯里化:指将原来接受两个参数的函数变成新的接受一个参数的函数过程,新的函数返回一个以原有第二个参数为参数的函数。,如下把本该def add(x:Int,y:Int)=x+y,改为了def add(x:Int)(y:Int) = x + y
object Test {
def main(args: Array[String]) {
val str1:String = "Hello, "
val str2:String = "Scala!"
println( "str1 + str2 = " + strcat(str1)(str2) )
}
def strcat(s1: String)(s2: String) = {
s1 + s2
}
}
15.scala闭包:闭包是一个函数,就是说函数的返回值依赖于一个声明在函数外部的一个或者多个变量。
object Test {
def main(args: Array[String]) {
println( "muliplier(1) value = " + multiplier(1) )
println( "muliplier(2) value = " + multiplier(2) )
}
var factor = 3
val multiplier = (i:Int) => i * factor
}
16.数组的一些操作:
合并数组:使用concat()方法合并两个数组
创建区间数组:使用了 range() 方法来生成一个区间范围内的数组。range() 方法最后一个参数为步长,默认为 1。比如下
import Array._
object Test {
def main(args: Array[String]) {
var myList1 = range(10, 20, 2)
var myList2 = range(10,20)
// 输出所有数组元素
for ( x <- myList1 ) {
print( " " + x )
}
println()
for ( x <- myList2 ) {
print( " " + x )
}
}
}
输出:
10 12 14 16 18
10 11 12 13 14 15 16 17 18 19
17.scala中的集合Collection:
包括list,Set,Map,元组,Option,Iterator迭代器,具体内容见API文档。
// 定义整型 List
val x = List(1,2,3,4)
// 定义 Set
val x = Set(1,3,5,7)
// 定义 Map
val x = Map(“one” -> 1, “two” -> 2, “three” -> 3)
// 创建两个不同类型元素的元组
val x = (10, “Runoob”)
// 定义 Option
val x:Option[Int] = Some(5)
18.scala中的迭代器Iterator,一种用于访问集合的方法,具有next和hasNext方法,可以查找最大最小元素,获取迭代器长度等常用方法属性
object Test {
def main(args: Array[String]) {
val it = Iterator("Baidu", "Google", "Runoob", "Taobao")
while (it.hasNext){
println(it.next())
}
val ita = Iterator(20,40,2,50,69, 90)
val itb = Iterator(20,40,2,50,69, 90)
println("最大元素是:" + ita.max )
println("最小元素是:" + itb.min )
println("ita.size 的值: " + ita.size )
println("itb.length 的值: " + itb.length )
}
}
19.Scala中的类和对象:对象就是抽象的类class,不占用内存,对象就是具体的实例占用存储空间,类是创建对象的蓝图。一个源文件中可以有多个类,类定义的时候可以有参数,相当于构造函数。
可以使用extends关键字继承一个类,只允许继承一个父类,可以重写子类的方法或字段,在需要重写的字段或者方法前加上override关键字。
import java.io._
class Point(xc: Int, yc: Int) {
var x: Int = xc
var y: Int = yc
def move(dx: Int, dy: Int) {
x = x + dx
y = y + dy
println ("x 的坐标点: " + x);
println ("y 的坐标点: " + y);
}
}
class Location(override val xc: Int, override val yc: Int,
val zc :Int) extends Point(xc, yc){
var z: Int = zc
override def move(dx: Int, dy: Int, dz: Int) {
x = x + dx
y = y + dy
z = z + dz
println ("x 的坐标点 : " + x);
println ("y 的坐标点 : " + y);
println ("z 的坐标点 : " + z);
}
}
object Test {
def main(args: Array[String]) {
val pt = new Point(10, 20);
// 移到一个新的位置
pt.move(10, 10);
}
}
20.scala 中没有static的描述,但是实现单例模式可以使用关键字object,使用单例模式时候,除了定义类之外还要定义个同名的object对象,object对象不能带参数。
当单例对象与某个类共享同一个名字的时候,他被称作是这个类的伴生对象:companion object,伴生对象必须和类在同一个源文件中。
21.Scala Trait(特征):相当于java中的接口,但相比于接口,它还可以定义属性和方法的实现,一个类只能单一继承父类,但是可以继承多个trait,实现了多重继承。特征也可以有构造器,由字段初始值和其它特征体重的语句构成
特征构造顺序:
调用超类的构造器;
特征构造器在超类构造器之后、类构造器之前执行;
特征由左到右被构造;
每个特征当中,父特征先被构造;
如果多个特征共有一个父特征,父特征不会被重复构造
所有特征被构造完毕,子类被构造。
/* 文件名:Test.scala
* author:菜鸟教程
* url:www.runoob.com
*/
trait Equal {
def isEqual(x: Any): Boolean
def isNotEqual(x: Any): Boolean = !isEqual(x)
}
class Point(xc: Int, yc: Int) extends Equal {
var x: Int = xc
var y: Int = yc
def isEqual(obj: Any) =
obj.isInstanceOf[Point] &&
obj.asInstanceOf[Point].x == x
}
object Test {
def main(args: Array[String]) {
val p1 = new Point(2, 3)
val p2 = new Point(2, 4)
val p3 = new Point(3, 3)
println(p1.isNotEqual(p2))
println(p1.isNotEqual(p3))
println(p1.isNotEqual(2))
}
}
22.模式匹配和样例类(一种优化过的类,主要用于模式匹配)
//模式匹配:每个都开始于关键字case,每个备选项都包含了一个模式及一到多个表达式,=>隔开了模式和表达式
def matchTest(x: Int): String = x match {
case 1 => “one” //match相当于Java中的switch,case后面即为备选项,只要发现有一个匹配的case,剩下的case就不会继续匹配。
case 2 => “two”
case _ => “many” //_表示默认的全匹配选项,类似于default。
}
//样例类:在类前面使用了case关键字的类定义就是样例类case classes,是一种经过优化用于模式匹配的类
case class Person(name: String, age: Int)
def main(args: Array[String]): Unit = {
val alice = new Person("Alice", 25)
val bob = new Person("Bob", 32)
val charlie = new Person("Charlie", 32)
for (person <- List(alice, bob, charlie)) {
person match {
case Person("Alice", 25) => println("Hi Alice!")
case Person("Bob", 32) => println("Hi Bob!")
case Person(name, age) =>
println("Age: " + age + " year, name: " + name + "?")
}
}
}
对于声明的样例类,下面的过程自动发生了:
构造器的每个参数都成为val,除非显式被声明为var,但是并不推荐这么做;
在伴生对象中提供了apply方法,所以可以不使用new关键字就可构建对象;
提供unapply方法使模式匹配可以工作;
生成toString、equals、hashCode和copy方法,除非显示给出这些方法的定义。
23.Scala中的正则表达式:Scala通过 scala.util.matching包中的Regex类来支持正则表达式。比如下面的实例
import scala.util.matching.Regex
object Test {
def main(args: Array[String]) {
val pattern = "Scala".r //使用string类中的r()方法构造了一个regex对象
val str = "Scala is Scalable and cool"
println(pattern findFirstIn str) //使用regex类中的findFirstIn 方法找到首个匹配项
println((pattern findAllIn str).mkString(",")) // findAllIn方法找到所有匹配项。并使用逗号 , 连接返回结果
}
}
24.Scala的异常处理:scala中的异常处理和java类似,可以通过抛出异常的方法的方式来终止相关代码的运行,不必通过返回值。捕获异常的机制和Java类似并且用了Java中的异常包。如下面实例
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")
}
} finally {
println("Exiting finally...")
}
}
}
catch子句是按照次序捕获异常的,因此一般来说,越具体的异常要越靠前,越普遍的异常要越靠后,如果抛出的异常不在catch语句中,则异常无法处理,程序就会在调用处报错处理。
25.Scala提取器(从传递给它的对象中提取出构造该对象的参数):其标准库中预定义了一些提取器,其提取器是一个带有unapply方法的对象。unapply方法是apply方法的一个反向操作:unapply方法接受一个对象,并将用于构造该对象的值提取出来,比如下面的邮件地址的提取器对象
object Test {
def main(args: Array[String]) {
println ("Apply 方法 : " + apply("Zara", "gmail.com"));
println ("Unapply 方法 : " + unapply("[email protected]"));
println ("Unapply 方法 : " + unapply("Zara Ali"));
}
// 注入方法 (可选)
def apply(user: String, domain: String) = {
user +"@"+ domain
}
// 提取方法(必选),option中定义将要提取的构造参数的内容的类型。
def unapply(str: String): Option[(String, String)] = {
val parts = str split "@"
if (parts.length == 2){
Some(parts(0), parts(1))
}else{
None
}
}
}
当实例化一个类的时候,带上0个或多个参数,编译器在实例化的时候会自动调用apply方法,所以建议在类中都要定义apply方法:当使用模式匹配匹配一个对象的时候,会自动调用unapply方法用于查找构造时用的值,比如下面的例子:
object Test {
def main(args: Array[String]) {
val x = Test(5)
println(x)
x match
{
case Test(num) => println(x + " 是 " + num + " 的两倍!")
//unapply 被调用
case _ => println("无法计算")
}
}
def apply(x: Int) = x*2
def unapply(z: Int): Option[Int] = if (z%2==0) Some(z/2) else None
}
26.Scala 文件I/O:对文件进行读写操作,使用的还是java的中I/O类
几个列子:
import java.io._
object Test {
def main(args: Array[String]) {
val writer = new PrintWriter(new File("test.txt" ))
writer.write("菜鸟教程")
writer.close()
//在当前目录产生一个test.txt文件。并写入内容菜鸟教程
}
}
从屏幕上读取用户输入:
import scala.io._
object Test {
def main(args: Array[String]) {
print("请输入菜鸟教程官网 : " )
val line = StdIn.readLine()
println("谢谢,你输入的是: " + line)
}
}
从文件上读取内容:使用Source类及伴生对象来读取文件
import scala.io.Source
object Test {
def main(args: Array[String]) {
println("文件内容为:" )
Source.fromFile("test.txt" ).foreach{
print
}
}
}