Scala简介
hadoop生态圈—>java spark生态圈—>scala
1.scala是面向对象的、面向函数的基于静态类型的编程语言。
静态语言(强类型语言)
静态语言是在编译时变量的数据类型即可确定的语言,多数静态类型语言要求在使用变量之前必须声明数据类型。
例如:C++、Java、Delphi、C#,Scala等。 scala编译后是字节码文件可以调用java源有的库
动态语言(弱类型语言)
动态语言是在运行时确定数据类型的语言。变量使用之前不需要类型声明,通常变量的类型是被赋值的那个值的类型。
例如PHP/ASP/Ruby/Python/Perl/ABAP/SQL/JavaScript/Unix Shell等等。
(大型项目不太适合于动态语言)
2.安装Scala,版本选型2.11.8。原因:spark版本选型为2.1.2,由scala2.11.8版本编译的
注意:a.安装目录不能有" "(空格)出现,否则抛:主类找不到…
b.必须安装JDK,需版本为1.8.X以上;
1)scala windows运行环境安装(安装版自动生成环境变量):
同上jdk环境的安装类似
配置用户变量: SCALA_HOME:D:\Programs
配置Path: %SCALA_HOME%\bin;%SCALA_HOME%\jre\bin;
配置class_path: .;%SCALA_HOME%\bin;%SCALA_HOME%\lib\dt.jar;%SCALA_HOME%\lib\tools.jar.;
至此,scala运行环境搭建完成,cmd,scala -version,没有错误表示搭建完成且正确。
2)scala liunx运行环境安装:
[hyxy@master soft]$ cp /mnt/hgfs/工具/scala/scala-2.11.8.tgz .
[hyxy@master soft]$ tar -zxvf scala-2.11.8.tgz
[hyxy@master soft]$ rm scala-2.11.8.tgz
[hyxy@master soft]$ ln -s scala-2.11.8 scala
[hyxy@master scala]$ gedit ~/.bash_profile
export SCALA_HOME=/home/crx/soft/scala
export PATH=$SCALA_HOME/bin:$PATH
[hyxy@master scala]$ source ~/.bash_profile
/**
- 第一个Scala程序 E:HelloScala.scala
- Scala和Java最大的区别是:Scala语句末尾的分号(;)是可选的!
- –>cmd E:>scalac HelloScala.scala
编译运行: - 先编译:E:>scalac HelloScala.scala 将会生成两个文件:HelloScala$.class和HelloScala.class
- 在运行:E:>scala HelloScala
- 输出结果:hello scala!!!
**/
object HelloScala{
//主方法默认不用写static ,所以scala是静态的
def main(args: Array[String]): Unit = {
println(“hello scala!!!”)
}
}
1)def main(args:Array[String]):scala程序从main方法开始处理,程序的入口。
2)Scala注释:分为多行/**/和单行//
3)换行符:Scala是面向行的语言,语句可以用分号(;)结束或换行符(println())
def 方法名(参数名:参数类型) :返回值类型 {
//方法体
//不需要写return时:方法体内的最后一行为返回值
}
例:带参数的主方法
def main(args: Array[String]): Unit = {
println("Hello, " + args(0) + “!”)
}
运行:
E:>scala HelloScala.scala zhangsan
例:带多个参数的主方法
object HelloScala{
//主方法默认不用写static ,所以scala是静态的
def main(args: Array[String]): Unit = {
var i = 0
while (i < args.length) {
println(args(i))
i += 1
}
}
}
E:>scala HelloScala.scala A B C D
/**
- Scala数据类型:
- Scala与Java有着相同的数据类型,下面列出一些Scala有的数据类型。
- Unit:表示无值,和其他语言的void一样。
- Null:null或空引用。
- Nothing:是Scala的类层级的最低端,是任何其他类型的子类型。
- Any:是所有其他类的超类。
- AnyRef:是Scala所有引用类的基类。
- 多行字符串的表示方法:
val foo =""“第一行
第二行
第三行”""
*/
Scala基本语法
第一个 Scala 交互式编程 : 交互式编程不需要创建脚本文件,可以通过以下命令调用
Scala是区分大小写
类名首字母大写(MyFirstScalaClass)
方法名称第一个字母小写(myMethodName())
Scala 的命名规则采用和 Java 类似的 camel 命名规则,首字符小写,比如 toString。
类名的首字符还是使用大写。此外也应该避免使用以下划线结尾的标志符以避免冲突
:quit :q ctrl+D 退出scala模式
在学习如何声明变量与常量之前,我们先来了解一些变量与常量。
一、变量: 在程序运行过程中其值可能发生改变的量叫做变量。如:时间,年龄。
二、常量 在程序运行的过程中一直不会改变的量称为常量(constant),通常也称为“final变量”。
常量在整个过程中能被赋值一次。在为所有的对象共享时,常量时非常有用的。
final double PI = 3.1415926D; //声明double型常量PI并赋值
final double BOOL = true; //声明double型常量BOOL并赋值
在JAVA语言中声明一个常量,除了要指定数据类型外,还需要通过final关键字进行限定。
scala中常量被定义为val 变量被定义为var
val类似于Java里的final变量。一旦初始化了,val就不能再赋值了
在Scala中 字符串类型就是:java.lang.String
如果想声明类型,那么类型和java中的类型都是一样的,声明方式不一样
【java】 类型 变量名 String name = “hello”
【scala】 (常量val/变量var)变量名:类型 val name:String = “hello”
eg:常量赋值
scala> val k = 12
k: Int = 12
scala> k = 23
<console>:13: error: reassignment to val
k = 23
eg:变量赋值,类型推断
scala> var c = 100
c: Int = 100
scala> var c = "12"
c: String = 12
scala> var c = true
c: Boolean = true
scala> var c = 12.8
c: Double = 12.8
eg:变量赋值
scala> val pa = (40,“Foo”)
pa: (Int, String) = (40,Foo)
scala> println(pa)
(40,Foo)
eg:多变量赋值
scala> val xmax, ymax = 100
xmax: Int = 100
ymax: Int = 100
scala中的类型有哪些?
Scala中的类型转换Int -->Double (scala中都是对象,所以类型首字母要大写)
scala> val i =10
i: Int = 10
as按Table补全 Int -->Double
scala> i.asInstanceOf[Double]
res1: Double = 10.0
Double -->Float
scala> res1.asInstanceOf[Float]
res2: Float = 10.0
isInstanceOf判断是不是这个类型
scala> res2.isInstanceOf[Float]
res3: Boolean = true
scala> res2.isInstanceOf[Double]
res4: Boolean = false
2.函数定义
函数定义采用def,格式如下:
def 函数名(参数列表[arg*:type]…):返回值类型 = {函数体,表达式}
注意:参数默认是val ,每个函数参数后面必须带前缀冒号的类型标注,因为Scala编译器没办法推断函数参数类型;但是可以进行结果类型的推断;
scala>def max(x:Int,y:Int):Int={if(x>y)x else y}
scala>def max(x:Int,y:Int)={if(x>y)x else y}
scala>def max(x:Int,y:Int)=if(x>y) x else y
scala>def max(x:Int,y:Int)={println(x);println(y);if(x>y)x else y}
无返回值,采用Unit
3.遍历or枚举
scala> val _list = List(1,2,3,4,5,6)
scala> _list.foreach(x=>println(x))
scala> list.foreach(println())
scala> _list.foreach(println)
scala> for(list <- _list) println(list)
1
2
3
4
5
6
循环三种方式 [to] [Range] [until]
scala> 1 to 10
res26: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> Range(1,10)
res27: scala.collection.immutable.Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9)
scala> 1.to(10)
res28: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> Range(1,10,2)
res29: scala.collection.immutable.Range = Range(1, 3, 5, 7, 9)
scala> Range(1,10,4)
res30: scala.collection.immutable.Range = Range(1, 5, 9)
scala> 1 until 10
res31: scala.collection.immutable.Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9)
定义数组:
val greetStrings = new Array[String](3)
greetStrings(0) = “Hello”
greetStrings(1) = ", "
greetStrings(2) = “world!\n”
for (i <- 0 to 2)
print(greetStrings(i))
val的数组元素可以被重新赋值
greetStrings(0) = “Hello”
将被转化为
greetStrings.update(0, “Hello”)
定义数组:
scala> var arr = new Array[Int](10)
arr: Array[Int] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
scala> arr(1)=3
数组方法:
scala> arr.mkString(";")
res35: String = 0;3;0;0;0;0;0;0;0;0
scala> arr.mkString("<",“and”,">")
res37: String = <0and3and0and0and0and0and0and0and0and0>
改变数据元素:yield属性
scala> var a = Array[Int](1,2,3)
a: Array[Int] = Array(1, 2, 3)
scala> for(i<-a) i* 2
scala> a
res39: Array[Int] = Array(1, 2, 3)
以上数组元素并没有改变其值
scala> for(i <- a) yield i* 2
res42: Array[Int] = Array(2, 4, 6) 注:yield 会把当前的元素记下来,保存在集合中,循环结束后将返回该集合
yield元数组元素并没有改变
scala> a
res44: Array[Int] = Array(1, 2, 3)
scala> res42
res45: Array[Int] = Array(2, 4, 6)
4.List集合的相关操作
表达式“1 :: twoThree”中,::是它右操作数(列表twoThree)的方法,等同于twoThree.::(1)
注::: 用于的是向队列的头部追加数据,产生新的列表, x::list,x就会添加到list的头部
:+ 用于在list尾部追加元素; list :+ x
+: 用于在list的头部添加元素;
scala> val l2 = 1 :: Nil
l2: List[Int] = List(1)
scala> val l3 = 2 :: l2
l3: List[Int] = List(2, 1)
scala> val l4 = 3 :: l2
l4: List[Int] = List(3, 1)
scala> val l4 = 1 :: 2 :: 3 :: Nil
l4: List[Int] = List(1, 2, 3)
List 有个叫“ ::: ”的方法实现叠加功能
scala> val oneTwo = List(1, 2)
oneTwo: List[Int] = List(1, 2)
scala> val threeFour = List(3, 4)
threeFour: List[Int] = List(3, 4)
scala> val oneTwoThreeFour = oneTwo ::: threeFour
oneTwoThreeFour: List[Int] = List(1, 2, 3, 4)
-
Array和List说明:
Array和List都不可变,它为何这样设计呢?它最大的好处就是用在高并发的地方。
因为在高并发时,如果多个线程都访问一个公用的变量,那这个变量就要加共享锁。
这里也是最容易出错的地方,不光影响性能的问题。那scala呢,干脆就换了个思路,来解决多线程时并发的问题。
它换了什么思路呢?它就是让这个共享变量不可变。
那这个集合只能读,不能写,就自然不会存在并发访问问题。所以这两个类要比java中的性能高。
当然scala也提供了可变版本,ArrayBuffer对应Array,ListBuffer对应List。 -
Array和List的区别
Array是连续存储结构,所以初始化时必须设定初始值,List是不连续的存储结构,所以可以不初始化。
当不确定数组大小时,使用List替代Array;
当需要大量查找操作时使用Array替代List;当需要进行频繁的插入和删除时使用List代替Array。List相对比Array占用更多空间。
查询某个值而言hashtable更快。当然它们结构完全不同,没有可比性。毕竟数组是节约空间,而hash表是散列的,牺牲空间来换取速度。
List
List(类似java中的LinkedList)基于链表实现,插入元素比较快,随机访问速度稍慢。
val list = List[Int]() //构造一个空元素的集合,元素类型为Int
val list = List(1,2,3,4); //构造一个集合,初始化其内容,可以根据初始化的类型判断其类型,所以无需写泛型。
list.head //list中的第一个元素
list(3) //按下标访问,获取第4个元素,但越往后越慢。
list.length //获取链表的长度
注意:和java不同的是,它的内容不可变。 list(3)=40这是不行的,它是只读的。
换成var list也不行,它们概念完全不同。看看它的内存分配。
List(1,2,3,4,5)它在堆内存中,除了基本变量和局部变量都是在堆内存中。
如main方法中的局部变量使用的是栈内存。val也好,var也好这个变量的引用都在它都在栈内存。
变量在堆内存中,只是说这个变量的引用指向堆内存。
var只是说这个引用的地址可以改变,val是引用的地址不能发生变更。
既然是不可变的集合,那需要增加一个元素怎么做呢?
list.:+(6) //list :+ 6,它会创建一个新的list,最后一个元素是6。这点和java中String的处理如出一辙。
注意实现方式,计算现有集合然后转换成一个新的集合,这个思想是scala乃至spark都广泛使用,如spark中的RDD也是这个思想。
那在list前面加元素呢?
val list3 = 6 +: list //这就是在前还是在后加,冒号跟着list走
合并两个list为一个list
val lista = List(1,2)
val listb = List(5,6)
println(lista ::: listb) //使用三个冒号连接
如果listb = List(1,2)呢? //list是可以有重复元素
中间插入元素
println(list.take(2) ::: List(0) ::: list.takeRight(2))
take(2)取list集合的前2个,takeRight(2)取list集合的后两个元素
val list = List(1, 2, 3, 4, 5)
val l = list.take(2) ::: list.takeRight(2)
--》l: List[Int] = List(1, 2, 4, 5)
删除左侧元素
println(list.drop(2)) //删除左边的2个元素
删除右侧元素
println(list.dropRight(2)) //删除右边的2个元素
更新元素
println(list.updated(2,30)) //更新第三个元素为30。其实它是创建了一个新的list,而不是真正更新了。
ArrayBuffer可变数组
//ArrayBuffer需要导入包,mutable可变,immutable不可变
import scala.collection.mutable.ArrayBuffer
val buffer = ArrayBuffer(1,2,3,4,5)
buffer += 6
println(buffer)
结果:ArrayBuffer(1,2,3,4,5,6)
它就非常类似java中的String和StringBuffer。String是不可变的,StringBuffer是可变的。
注意:java下导入包是java.*,而scala导入时scala.collection._
Set
元素不能重复,重复的会自动剔除,无序
val set = Set(1,2,3,4,45,3,2,6)
println(set)
结果:Set(45, 1, 2, 6, 3, 4)
Set特质(接口)---》 immutable(不可变的)的set 和 mutable(可变的)的Set
默认情况下Set使用的是不可变的immutable
val movieSet = Set("Hitch", "Poltergeist")
movieSet: scala.collection.immutable.Set[String] = Set(Hitch, Poltergeist)
如果需要使用可变的Set需要显示的声明
import scala.collection.mutable.Set
val movieSet = Set("Hitch", "Poltergeist")
movieSet += "Shrek"
println(movieSet)
Map
val map = Map(1 -> “北京”, 2 -> “上海”) //->前面是key,后面是值
val map2 = map + (3 -> “广州”) //产生新的map
val map3 = map -2 //删除key=2的元素
println(map)
println(map2)
println(map(1))
结果:
Map(1 -> “北京”, 2 -> “上海”)
Map(1 -> “北京”, 2 -> “上海”, 3 –> “广州”)
Map(1 -> “北京”)
Map 是 Scala 里另一种有用的集合类。和集一样,Scala 采用了类继承机制提供了可变的和不可变的两种版本的 Map(同set包)
————————————————————————————————————————————
Tuple元组个键一个值,tuple一个键可以有多个值。
val t1 = (1,“北京”,“100086”)
println(t1._1) //访问key,注意tuple下标是从1开始
println(t1._2) //获取北京
println(t1._3) //获取邮编
tuple可以设置多个值,不是无限的,最多22个
两个值它的类型就是Tuple2,三个值它的类型就是Tuple3,最多到Tuple22。
val tuplea=(1,"adn",1->"afd")
println(tuplea._3._1)
println(tuplea._3._2)
————————————————————————————————————————————
IDE安装scala插件
IDEA全称InteliJ IDEA,支持Java、Kotlin、Groovy、Scala等语言,能够实现智能编码。
相比于Eclipse来说,界面UI更现代化,代码提示补充等功能更智能。
IntelliJ IDEA官网下载:https://www.jetbrains.com/idea/download/other.html
IntelliJ IDEA的在线注册码生成页面 http://idea.iteblog.com
新的License server地址为:http://idea.iteblog.com/key.php
激活服务器地址:http://47.104.86.228:8888
安装IDEA。安装Scala插件
a.选择【File】–>【Setting】–>【plugins】–> 选择scala,点击Install JetBrains Plugins… 重新启动idea
b.选择【File】–>【New–>project】 -->选择scala选项,并指定【IDEA】,next
c.添加 工程名,指定JDK版本,Scala SDK 找到Scala版本安装路径(create),然后Finish;
d.新建一个模块–> 选择【File】–>【new】–>【Module】–>【选择Scala】–>【Finish】
object HelloWord {
def main(args: Array[String]): Unit = {
System.out.println(“Hello World!”)
}
}
函数调用:
object T2 {
def main(args: Array[String]): Unit = {
println(add(1,2))
}
def add(x:Int,y:Int): Int = {
x + y ;
}
}
def main(args: Array[String]): Unit = {
// println(add()) //函数调用
println(add) //函数调用 无参数时可以省略掉小括号
}
//方法定义 一条数据可以省略大括号
def add() = 1 + 2
//不写return 最后一行做为值返回
// def add(): Int = {
// 1 + 2
// }
定义包有两种方法:
1、package com.ahu
class HelloScala
2、package com.ahu{
class HelloScala
}
1.创建Scala Class Object文件
伴生类和伴生对象
如果有一个class,还有一个与class同名的object
那么就称这个object是class的伴生对象,class是object的伴生类
scala中默认的主函数入口要在单例模式中(Object)
类的伴生对象可以访问类的私有变量
SingletonObject在伴生对象中可以访问伴生类的私有属性,SingletonObject111不可以访问
object SingletonObject111{
def main(args:Array[String]){
val s = new SingletonObject()
//println(s.init)
println(s.name)
//s.hello()
}
}
伴生对象SingletonObject
object SingletonObject{
def main(args:Array[String]){
val s = new SingletonObject()
println(s.init)
println(s.name)
s.hello()
}
}
伴生类SingletonObject
class SingletonObject{
private var init = 0;
val name:String = “zhangsan”;
private def hello(){
println(“Hello, This is Singleton Object” + name)
}
}
例:伴生类<–>伴生对象,相互关系 可互相调用所有元素
package com.hyxy
import scala.math._
class Circle(radius: Double) {
private def area: Double=Circle.calculateAre(radius)
}
object Circle111 {
private def calculateAre(radius:Double):Double = Pi * pow(radius,2.0)
def main(args:Array[String]){
val s = new Circle(5.0); // 其它类访问不了伴生类
//println(s.area)
}
}
object Circle{
private def calculateAre(radius:Double):Double = Pi * pow(radius,2.0)
def main(args:Array[String]){
val s = new Circle(5.0);
println(s.area)
}
}
Scala闭包:
闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量。
例子:
object Test{
def main(args: Array[String]){
println("muliplier(1) value = " + muliplier(1))
println("muliplier(2) value = " + muliplier(2))
}
var factor = 3 // 定义在函数外的自由变量
val muliplier = (i:Int) => i * factor // muliplier函数变量就是一个闭包
}
输出结果:
muliplier(1) value = 3
muliplier(2) value = 6
下载源代码包
点击这个链接:http://www.scala-lang.org/download/all.html;
在intellij idea设置指向源代码
在intellij中打开File –> Project Structure,快捷键(Ctrl + Alt + Shift + s);
选择Global Libraries –> 目标SDK;
在右侧面板中的Standard Library下方点击“+”按钮;
选择解压后的源码包的src目录;
apply即可。