配置环境
配置$SCALA_HOME
在配置PATH
IDEA自带的下载后
remark一下,之后创建类即可
object hello {
def main(args: Array[String]): Unit = {
println("Hello World")
}
}
基础
可以通过编译后执行的方式,也可以通过直接执行的方式(编译的方式对用户透明的)
注意事项
1.源文件以.scala为扩展名
2.Scala程序的执行入口为main()函数
3.Scala语言严格区分大小写
4.Scala方法由多条语句构成,每个语句后不需要加分号(如果多条语句写在一行,除了最后一条语句,剩下的都要加)
输出的三种方式
object hello {
def main(args: Array[String]): Unit = {
// 声明几个变量
var name = "yyx"
var age = 10
var sex = '男'
println("name="+name + " age="+age +" sex="+sex)
printf("name = %s,age = %d ,sex = %s+\n",name,age,sex)// 数字为%d,多了个加号
println(s"name=$name+age=$age+sex=$sex")
}
}
变量创建
var|val 变量名 [:变量类型] = 变量值
- 声明变量时,类型可以省略(类型推断)
- 类型确定后,不能修改(强数据类型语言)
- 在声明、定义一个变量时,可以使用val或者var来修饰,var修饰的变量可以改变,val修饰的变量不可变
- val修饰的对象属性在编译后,相当于加上final
- var修饰的对象引用可以改变,val修饰的则不可变,但是对象的状态(值)却是可以改变的。(自定义对象、数组、集合)
- 声明变量时,必须有初始值
数据类型
- Scala与Java有相同的数据类型,但在scala中数据类型都是对象,没有java的基本数据类型
- Scala数据类型分为两大类 AnyVal(值类型) 和 AnyRef(引用类型), 注意:不管是AnyVal还是AnyRef 都是对象。
scala数据类型列表
整数类型Int
var i:Byte = 10
var d:Int = 100
println(i+d)
110
使用细节:
1.各整数类型有固定的表数范围和字段长度,不受OS影响,以保证可移植性。
2.Scala的整型常量\字面量默认为Int型,声明Long型常量\字面量需要加“L”或‘l’
3.Scala程序中变量常声明为Int型,除非不足以表示一个数,才使用Long
浮点型
浮点型可用来表示一个小数,如123.4f,7.8,等
浮点型分类:
Scala浮点型默认为Double型,声明Float型常量需加“f”或“F”
浮点型常量有两种表示方式:
十进制:5.12,512.5f
科学计数法形式:5.12e2=> 512*100
通常情况下,应该使用Double型,因为比Float更加精确
字符类型
Char,16位无符号Unicode字符(两个字节),区间为U+0000到U+FFFF
字符常量时用单引号
Scala也允许使用转义字符“\”
可以直接给Char赋值一个整数,然后输出时,会按照对应的unicode字符输出
Char可以进行运算,但是要用Int接
Boolean
只允许true和false
占1个字节
适用于逻辑运算(和Java相同)
Unit、Null、Nothing
1.NUll类只有一个实例对象,null,类似于Java中的null引用。null可以赋值给任意引用类型(AnyRef),但是不能赋值给值类型(AnyVal:如Int、Fault等)
2.Unit类用来标识过程,也就是没有明确返回值的函数,类似于Java方法中void
3.Nothing,可以作为没有正常返回值的方法的返回值类型,很明确的告诉你,这个方法不会正常返回
值类型转换
当Scala程序在进行赋值或者运算时,精度小的类型自动转为精度大的数据类型。自动转换数据类型(隐式转换)
有多种类型的数据混合运算时,系统会首先将所有的数据转换为容量最大的那种数据类型,然后再进行计算
当我们把精度(容量)大的数据类型赋值给精度小的数据类型时,就会报错,繁殖会进行自动转换
(byte, short) 和 char之间不会相互自动转换。byte,short,char 他们三者可以计算,在计算时首先转换为int类型。
自动提升原则: 表达式结果的类型自动提升为 操作数中最大的类型
强制转换
将容量大的数据类型转换为容量小的数据类型。使用时要加上强制转换函数,但可能造成精度降低或溢出
java : int num = (int)2.5
scala : var num : Int = 2.7.toInt //灵活
强转符号只针对于最近的操作数有效,往往会使用小括号提升优先级
Char类型可以保存 Int的常量值,但不能保存Int的变量值,需要强转
Byte和Short类型在进行运算时,当做Int类型处理。
标识符和命名规范
核心运算符
算数运算符
和java一样
要注意的是,Scala没有++、–等操作,只能re = re +1这样赋值
关系运算符
Boolean的赋值只能是true或者false
其它的和Java一样,使用陷阱: 如果两个浮点数进行比较,应当保证数据类型一致.
逻辑运算符
赋值运算符
二进制部分
这部分和Java一样
小测试:有两个变量,a和b,要求将其进行交换,但是不允许使用中间变量,最终打印结果
package chapter03
object Demo01 {
def main(args: Array[String]): Unit = {
var a = 10
var b = 20
a = a + b
b = a - b
a = a - b
print(a+"\t")
print(b)
}
}
位运算
和Java一样
且Scala不支持三元运算符
小案例:
求两个数的最大值
求三个数的最大值
object Demo01 {
def main(args: Array[String]): Unit = {
var a = 10
var b = 20
var c = 100
println(max(a,b))
println(max(a,b,c))
}
def max(b: Int,a: Int):Int={
if(a>b){
return a
}else{
return b
}
}
def max(a: Int,b: Int,c: Int):Int={
var t = max(a,b)
if(t>c){
return t
}else{
return c
}
}
}
键盘输入
import scala.io.StdIn
object Demo02 {
def main(args: Array[String]): Unit = {
// 测试键盘输入
println("name")
var name = StdIn.readLine();
println(name)
}
}
程序流程控制
顺序控制(正常写)
分支控制(单分支, 双分支,多分支)
循环控制(for、while)
分支流程
总体上和Java类似,不过要注意几个点
var flag : Boolean = true
if(flag){
print("t")
}else{
print("s")
}
在scala中,每一个语句都会有返回值,具体返回结果的值取决于满足条件的代码体的最后一行内容.
var flag : Boolean = false
val value = if (flag) {
"aaa"
}else {
"bbb"
}
print(value)
在scala中,没有switch语法
要通过if语法一层一层表达
for循环
package chaper04
object TestFor {
def main(args: Array[String]): Unit = {
for(i <- 0 to 5){
println(i)
}
}
}
0
1
2
3
4
5
左包右不包
for (i <- 0 until 5){
print(i+"\t")
}
0 1 2 3 4
设置步长
起始until的源数据是这样的
设置了一个Range(表示范围的类)
def until(end : scala.Int) : scala.collection.immutable.Range = { /* compiled code */ }
那我们就可以这样表示步长
for(i <- Range(0,10,2)){
print(i+"\t")
}
0 2 4 6 8
for循环控制
循环守卫
保护式为true则进入循环体内部,为false则跳过,类似于continue
for(i <- Range(0,6) if i!=4 ){
print(i)
}
01235
引入变量
(可以只用一行代码for打出九层塔)
for(i <- Range(0,6) if i!=4; j=4-i){
println(i+"iiii")
println(j+"jjjj")
}
0iiii
4jjjj
1iiii
3jjjj
2iiii
2jjjj
3iiii
1jjjj
5iiii
-1jjjj
for的小括号可以变为大括号
for循环控制
for(i<-Range(0,4) if i != 2;j <-Range(0,4) if j != 2){
print(i+j + "\t")
}
0 1 3 1 2 4 3 4 6
for返回值
val unit = for (i <- Range(0, 4) if i != 2; j <- Range(0, 4) if j != 2) {
print(i + j + "\t")
"aaa"
}
print(unit)
0 1 3 1 2 4 3 4 6 ()
我们可以改一下,用yield关键字(使结果加入一个Vector)(不能用大括号)
val unit = for (i <- Range(0, 4) if i != 2; j <- Range(0, 4) if j != 2)yield i + j
print(unit)
Vector(0, 1, 3, 1, 2, 4, 3, 4, 6)
while循环
scala中break实际上是抛出了一个异常,来终止循环
var flag:Boolean = true;
var i = 0
while(flag){
print(i)
i+=1
if(i==9) Breaks.break()
}
print("循环结束")
012345678Exception in thread "main" scala.util.control.BreakControl
注意导入类
package chaper04
// 用来导入
import scala.util.control.Breaks._
object TestWhile {
def main(args: Array[String]): Unit = {
var flag:Boolean = true;
var i = 0
breakable {//breakable要将整个while套在里面
while(flag){
print(i)
i += 1
if (i == 9){break()}
}
}
print("循环结束")
}
}
函数
package chapter05
object TestFunction {
def main(args: Array[String]): Unit = {
// 在方法中创建scala方法,不能用相同的类名,在方法外可以
def test():Unit={
print("sssss")
}
test()
}
// 创建scala方法
def max(b: Int, a: Int): Int = {
if (a > b) {
return a
} else {
return b
}
}
def max(a: Int, b: Int, c: Int): Int = {
var t = max(a, b)
if (t > c) {
return t
} else {
return c
}
}
}
scala的function和java的method最大的区别是method依靠类,而function不需要
scala中function可以省略很多东西
// 有很多可省
// def sum(b: Int,a: Int) :Int = {
// return a+b
// }
// 可以省略return和返回值类型()必须将return的卸载最后一行
// def sum(b:Int,a:Int) ={
// a+b
// }
// 省略大括号
//def sum(b:Int,a:Int) =a+b
省略=
def test(){
print("a")
}
test()
省略()
此时{}不能省略
// 在函数无参时,可以省略小括号
def test {print("a")}
test// 调用时,没有参数,()可加可不加
匿名函数
直接运行即可,不需要调用
//匿名函数
()->print("xxxxxxxxx")
设置可变参数
// 设置可变参数
def test(name:String*): Unit ={
println(name)
}
test("aaa","bbb")
test()
WrappedArray(aaa, bbb)
List()
设置默认值
def test(name : String = "zhang"): Unit ={
print(name)
}
test()
zhang
带名参数(防止给错)
// 设置参数默认值
def test(name2:String,name : String = "zhang"): Unit ={
print(s"${name},${name2}")
}
// test("zhang","sss")//此时容易给错
test(name2="sss")//必须要设置了默认值的
困难部分
Scala是完全面向对象的、同时也是完全面向函数式编程的
函数作为另一个函数的参数
def test(string: String):Unit={
print(string)
}
def p(): Unit ={
print("p运行了")
test _
}
p()
p运行了
同时注意,如果调用时没有加括号
def a = 100
def b = a _
print(b())//100
print(b)//<function0>
//将函数作为另一个函数的参数,需要_
def test(string: String):Unit={
print(string)
}
def p() ={// def p():String=>Unit ={}可省
print("p运行了")
test _
}
p()("sss")
以上代码存在闭包:第一个函数的参数的生命周期被改变,即其参数可以被第二个函数使用
结果相同
def p():String=>Unit ={
print("p运行了")
def test(string: String):Unit={
print(string)
}
test _
}
函数柯里化
// 函数柯里化
def te(b : Int)(a: Int): Int ={
a*b
}
println(te(6)(5))
使用匿名函数改善
//使用匿名函数改善代码
def test(f:()=>Unit): Unit ={//表示参数列表为空,返回值为空的函数
f()//f函数
}
test(()=>{print("xx")})
}
递归函数
斐波那契数列(递归不能省返回值类型)
object FunctionRe {
def main(args: Array[String]): Unit = {
println(fib(20))
}
// 来实现一个递归
def fib(i:Int):Int={
if(i<2) i;
else {
fib(i-1)+fib(i-2)
}
}
}
惰性函数
lazy关键字(只能对val的变量使用)
object FunctionRe {
def main(args: Array[String]): Unit = {
lazy val a:Int = test(5)
println("++++++++++++++++")
println("++++++++++++++++")
println("++++++++++++++++")
println("++++++++++++++++")
println("++++++++++++++++")
println(a)
}
def test(int: Int): Int ={
println("test在运行")
int
}
}
++++++++++++++++
++++++++++++++++
++++++++++++++++
++++++++++++++++
test在运行
5
如果去掉关键字
test在运行
++++++++++++++++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++++++++++++++++
5
异常捕获
java的异常捕获
public class TestException {
public static void main(String[] args) {
try {
System.out.println(1/0);
}catch (ArithmeticException e){
System.out.println("ArithmeticException");
e.printStackTrace();
}catch (Exception e){
System.out.println("Exception");
e.printStackTrace();
}finally {
System.out.println("finally");
}
}
}
ArithmeticException
finally
java.lang.ArithmeticException: / by zero
at TestException.main(TestException.java:4)
当然,和顺序有关
这样在Java中会报错
public class TestException {
public static void main(String[] args) {
try {
System.out.println(1/0);
}catch (Exception e){
System.out.println("Exception");
e.printStackTrace();
}catch (ArithmeticException e){
System.out.println("ArithmeticException");
e.printStackTrace();
}finally {
System.out.println("finally");
}
}
}
Scala中
object ExceptionTest {
def main(args: Array[String]): Unit = {
try {
println(1/0)
}catch {
case ce:ArithmeticException=>println("ArithmeticException")
case ce:Exception=>println("Exception")
}finally {
println("finally")
}
}
}
ArithmeticException
finally
但是如果把顺序换一下也不会报错,并且结果:
object ExceptionTest {
def main(args: Array[String]): Unit = {
try {
println(1/0)
}catch {
case ce:Exception=>println("Exception")
case ce:ArithmeticException=>println("ArithmeticException")
}finally {
println("finally")
}
}
}
Exception
finally
最基本的面向对象
package chapter060708
object TheFirstDemo {
def main(args: Array[String]): Unit = {
var user:User = new User
user.name = "yyx"
user.age = 18
println(user.getName())
}
}
class User{
var name : String = _
var age : Int = _
def getName():String={
name
}
}
Scala包和Java包的区别
1.Scala中包可以多次声明,声明后,下面的对象在最后的那个包中,即:
package chapter060708
package test1
class PackageTest {
}
那么这个对象就在chapter060708.test1中
2.Scala中所有语法都可以进行嵌套
package chapter060708
package test1{
package test2{
class T{
}
}
}
class PackageTest {
}
2.package可以进行多次嵌套
此时,PacketageTest不在test1中,而T在test2中,且包嵌套中方法和变量只能在类中声明
package test1{
package test2{
class T{
def test(i:Int):Unit ={}
}
}
}
class PackageTest
}
3.Scala中,父包中的类子包也可以调用
4.引用了包对象的概念
在scala中可以为每个包定义一个和包名同名的包对象,定义在包对象中的成员,作为其对应包下所有class和object的共享变量,可以直接访问
生命包对象必须和包在同一层级
package object test{
val i:Int = 1
}
import
class TestImport {
// scala中import用来导入类、包,且可以在任意地方,但是只能用在下一行
import java.util.Date
var D:Date = new Date();
}
import java.util._
class TestImport {
// scala中import想导入包全部类,不用*而是_
var D:Date = new Date();
var M:HashMap[Nothing, Nothing] = new HashMap();
}
import java.util.{Date,HashMap}
class TestImport {
var D:Date = new Date();
// scala中import某个包中大部分类,可以用{}
var M:HashMap[Nothing, Nothing] = new HashMap();
}
import java.util
class TestImport {
var D:util.Date = new util.Date();
// scala中import某个包,调用类时要特别点
var M:util.HashMap[Nothing, Nothing] = new util.HashMap();
}
package chapter060708
// 用特殊的方式来隐藏指定的类{类名=>_}
import java.util.{Date=>_}
import java.sql.Date
class TestImport {
var D:Date = new Date(0);
}
// 当包名和java相同时
package java{
package util{
class Map{
}
}
}
import _root_.java.util._
object TestImport {
def main(args: Array[String]): Unit = {
// 想要调用正常的java.util.map
var v:HashMap[Nothing,Nothing] = new HashMap()
println(v)
}
}
//{}
// 修改导入类名
import _root_.java.util.{HashMap=>JavaHashMap}
object TestImport {
def main(args: Array[String]): Unit = {
// 想要调用正常的java.util.map
var v:JavaHashMap[Nothing,Nothing] = new JavaHashMap()
println(v)
}
}
访问权限
先不说scala,Java中,Object的clone()方法时project的,也就是同包、同类、其子类可以调用,那么如下例子:
Java访问权限
public class TestPro {
public static void main(String[] args) {
A a = new A();
a.clone();
}
}
class A{
}
此时调用a.clone()会报错,因为:
我们此时调用的时A中Object中的clone,可是这个Object不是我们TestPro的父类,所以,我们要在A中重写方法,才能调用
public class TestPro {
public static void main(String[] args) throws CloneNotSupportedException {
A a = new A();
a.clone();
}
}
class A{
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
Scala中访问权限
package chapter060708
package P1{
package P2{
class Us{
var usname:String=_
private var password:String="123456"
private[P3] var address:String = "xxx"
protected var age:Int = 18;
}
}
package P3{
import chapter060708.P1.P2.Us
class test{
def get():Unit={
var us:Us = new Us;//需要倒类
us.usname="x"
}
}
}
}
此时能访问的有address和username
但是将包名修改一下,address就不能访问了
private[P2] var address:String = "xxx"
password属于私有属性,无法访问
age只有Us子类才能访问
总结:
public 是默认权限,没有关键字
protected是只有同类、子类才能访问,同包不能访问
private只有本类+伴生对象能调用,是私有的
private[包名] 指定包可以调用
伴生对象
scala是没有静态的,如果想实现静态的方式,即通过单例对象(伴生对象)
伴生对象的属性和方法都可以通过伴生类名.调用
class Student {
var sname:String=_
private var age:Int=_
}
// student的伴生对象
object Student{
// 伴生对象中可以调用伴生类的私有属性
new Student().age=1
}
并且可以通过伴生类(apply方式)
apply可以直接调用,不用.apply(参数)
class Student {
var sname:String=_
private var age:Int=_
}
// student的伴生对象
object Student{
// 伴生对象中可以调用伴生类的私有属性
//new Student().age=1
def apply(): Student = new Student()
}
object TestStudent {
def main(args: Array[String]): Unit = {
//通过伴生对象来创建伴生类对象
var student=Student()
println(student)
}
}
//chapter060708.Student@1888ff2c
为了符合JavaBean的标准(迎合多种框架),Bean类可以使用 @BeanProperty注解
是对象生成公共的set、get方法
object ScalaField {
def main(args: Array[String]): Unit = {
val user:User = new User
println(user.name)
user.setAddress("sss")
println(user.getAddress)
}
}
class User{
// 相当于定义了一个private String name,并且定义了public的getter和setter
var name:String = _
// 相当于用final定义的变量,并且,只有一个get方法
val sex:String = "男"
// 该属性在外部无法访问,因为该属性是一个私有的,只在内部private了get和set方法
private var age:Int = 18
// 为了符合java的规范,我们可以调用一个注解来实现get和set方法
@BeanProperty var address:String = "xxx"
}
通过scala实现单例
饿汉式:即直接先创建好对象,后面用的时候直接调用对象就好
package study
object Main {
def main(args: Array[String]): Unit = {
val student = Student()
student.printInfo()
val student2 = Student()
student2.printInfo()
println(student==student2)
}
}
class Student private(id: Int, name: String) {
def printInfo() = {
println(s"id= $id and name = $name")
}
}
object Student {
private val student = new Student(111, "yang")
def apply(): Student = {
student
}
}
/**
id= 111 and name = yang
id= 111 and name = yang
true
*/
懒汉式:用到的时候先看之前有没有,没有在创建,有的话直接用
package study
object Main {
def main(args: Array[String]): Unit = {
val student = Student()
student.printInfo()
val student2 = Student()
student2.printInfo()
println(student == student2)
}
}
class Student private(id: Int, name: String) {
def printInfo() = {
println(s"id= $id and name = $name")
}
}
object Student {
private var student: Student = _
def apply(): Student = {
if (null == student) {
student = new Student(111, "yang")
student
} else {
student
}
}
}
/**
id= 111 and name = yang
id= 111 and name = yang
true
*/
构造方法
Scala中,构造方法分为两类:主构造方法、辅助构造方法
辅助构造方法可以用来创建对象,但是必须要调用主构造方法
Scala是面向函数编程语言,所以类也是一个函数,并且,类所代表的函数起始就是这个类的构造方法,默认情况下,Scala提供无参构造,所以类后的()可以省略
在类后声明的构造方法为主构造方法,在类中声明的构造方法为辅助构造方法
1.空构造方法(默认构造方法)
object TestPu {
def main(args: Array[String]): Unit = {
var teacher:Teacher = new Teacher()
teacher.name = "LL"
println(teacher.name)
}
}
class Teacher(){// ()可省
var name:String=_
}
2.带参数的构造方法
object TestPu {
def main(args: Array[String]): Unit = {
var teacher:Teacher = new Teacher("调用主构造方法")
teacher.name = "LL"
println(teacher.name)
}
}
class Teacher(s:String){
var name:String=_
}
// LL
3.使用辅助构造方法(this)
package chapter060708
object TestPu {
def main(args: Array[String]): Unit = {
var teacher:Teacher = new Teacher(" ")// 调用主构造方法
var teacher1:Teacher = new Teacher()// 调用辅助构造方法
println(teacher)
println(teacher1)
}
}
class Teacher(s:String){// 类体、构造方法体
println("调用主构造方法")
var name:String=_
def this(){
this("ssss")//在辅助构造方法中调用主构造方法
println(s)
}
}
/*
调用主构造方法
调用主构造方法
ssss
chapter060708.Teacher@1888ff2c
chapter060708.Teacher@35851384
*/
辅助构造方法调用其它构造方法
package chapter060708
object TestPu {
def main(args: Array[String]): Unit = {
var teacher1:Teacher = new Teacher("ss",5)// 调用辅助构造方法
println(teacher1)
}
}
class Teacher(s:String){// 类体、构造方法体
println("调用主构造方法")
var name:String=_
def this(){
this("在辅助构造方法1中调用主构造方法")//在辅助构造方法中调用主构造方法
println(s)
}
def this(string: String,int: Int){
this()//调用辅助构造方法
println("在辅助构造方法2中调用辅助构造方法1")
}
}
/*
调用主构造方法
在辅助构造方法1中调用主构造方法
在辅助构造方法2中调用辅助构造方法1
chapter060708.Teacher@1888ff2c
*/
抽象类与继承
只继承时
object TestAbstract {
def main(args: Array[String]): Unit = {
var user:Ub = new Ub
//user.name = "sss" 不能修改属性
println(user.name)
}
}
class Person{
val name:String = "yyy" // 属性无法改变
def test():Unit={// 声明方法
println("父类方法")
}
}
class Ub extends Person{
// 声明的name属性也必须是val
var age:Int = _
//override def test(): Unit = {println("继承父类方法")}
}
// yyy
重写父类name时
object TestAbstract {
def main(args: Array[String]): Unit = {
var user:Ub = new Ub
//user.name = "sss" 不能修改属性
println(user.name)
}
}
class Person{
val name:String = "yyy" // 属性无法改变
def test():Unit={// 声明方法
println("父类方法")
}
}
class Ub extends Person{
// 声明的name属性也必须是val,并且要重写
override val name:String = "sss"
var age:Int = _
//override def test(): Unit = {println("继承父类方法")}
}
/*
sss
*/
当不重写父类方法时
object TestAbstract {
def main(args: Array[String]): Unit = {
var user:Ub = new Ub
user.test()
//user.name = "sss" 不能修改属性
println(user.name)
}
}
class Person{
val name:String = "yyy" // 属性无法改变
def test():Unit={// 声明方法
println("父类方法")
}
}
class Ub extends Person{
// 声明的name属性也必须是val
var age:Int = _
//override def test(): Unit = {println("继承父类方法")}
}
/*
父类方法
yyy
*/
重写父类方法:
object TestAbstract {
def main(args: Array[String]): Unit = {
var user:Ub = new Ub
user.test()
//user.name = "sss" 不能修改属性
println(user.name)
}
}
class Person{
val name:String = "yyy" // 属性无法改变
def test():Unit={// 声明方法
println("父类方法")
}
}
class Ub extends Person{
// 声明的name属性也必须是val,并且要重写
override val name:String = "sss"
var age:Int = _
override def test(): Unit = {println("继承父类方法")}
}
/*
继承父类方法
sss
*/
当父类改为抽象类时
abstract实现抽象类
有抽象方法的类一定是抽象类,抽象类不一定都是抽象方法
子类必须实现父类的抽象方法
实现子类实现父类抽象方法override可省,其他情况都不可以
object TestAbstract {
def main(args: Array[String]): Unit = {
var user:Ub = new Ub
user.test()
user.test1()
//user.name = "sss" 不能修改属性
println(user.name)
}
}
abstract class Person{
val name:String = "yyy" // 属性无法改变
def test():Unit={// 声明方法
println("父类方法")
}
// 声明抽象方法,不需要关键字,没有方法体即可
def test1()
}
class Ub extends Person{
// 声明的name属性也必须是val,并且要重写
override val name:String = "sss"
var age:Int = _
override def test(): Unit = {println("继承父类方法")}
// 子类必须实现抽象方法
override def test1(): Unit = {println("父类抽象方法")}
}
/*
继承父类方法
父类抽象方法
sss
*/
JVM动态访问机制
父类属性要子类重写时,一定要使用val声明,否则运行时Error
object TestAbstract {
def main(args: Array[String]): Unit = {
// 多态
var person:Person = new Ub()
println(person.name)
}
}
abstract class Person{
// 子类要重写的属性,一定是val的,否则运行时报错
var name:String="aaa"
}
class Ub extends Person{
override var name:String="bbb"
}
改为val后
object TestAbstract {
def main(args: Array[String]): Unit = {
// 多态
var person:Person = new Ub()
println(person.name)
}
}
abstract class Person{
// 子类要重写的属性,一定是val的,否则运行时报错
val name:String="aaa"
}
class Ub extends Person{
override val name:String="bbb"
}
// bbb
声明抽象属性
abstract class Person{
// 子类要重写的属性,一定是val的,否则运行时报错
val name:String="aaa"
// 声明抽象属性,不初始化即可
var age:Int
}
class Ub extends Person{
override val name:String="bbb"
var age: Int = _ // override 可省
}
重点(JVM的动态绑定)
先通过Java来看
public class TestDynamic {
public static void main(String[] args) {
AAA a = new BBB();
System.out.println(a.getI());// 输出结果为22,但是如果将BBB类中方法注释掉,输出11
}
}
class AAA{
public int i = 1;
public int getI() {
return i + 10;
}
}
class BBB extends AAA{
public int i = 2;
// public int getI() {
// return i + 20;
// }
}
构造方法值赋给对象
package chapter060708
// Scala 中声明构造方法
object TestPu2 {
def main(args: Array[String]): Unit = {
var s:Stu = new Stu("SSS");
println(s.name)
}
}
class Stu(){
var name : String =_
def this(string: String){
this()
name = string
}
}
// SSS
这样写太麻烦了,我们改一下
结果相同
package chapter060708
// Scala 中声明构造方法
object TestPu2 {
def main(args: Array[String]): Unit = {
var s:Stu = new Stu("SSS");
println(s.name)
}
}
class Stu(var name:String){
}
特质(类似于Java中接口)
在java中,接口声明sout会执行错误,但是scala中,trait中声明print方法不会报错,可以把它看作一个特殊的类,不过不能创建对象。
可以有抽象的属性和方法,也可以有具体的属性和方法,一个类可以混入多个特质
trait的执行顺序:
object TestTrait1 {
def main(args: Array[String]): Unit = {
new Teachersss()
}
}
trait People{
println("trait....")
}
class Headmaster{
println("person class....")
}
class Teachersss extends Headmaster with People{
println("class....")
}
/*
person class....
trait....
class....
*/
当父类子类都实现trait时
object TestTrait1 {
def main(args: Array[String]): Unit = {
new Teachersss()
}
}
trait People{
println("trait....")
}
class Headmaster extends People {
println("person class....")
}
class Teachersss extends Headmaster with People{
println("class....")
}
/*
trait....
person class....
class....
*/
trait只会实现一次,并且在实现类之前就会先输出trait的结果
特值可以继承其它特值
特质也可使设置属性
object TestTrait1 {
def main(args: Array[String]): Unit = {
var t = new Teachersss()
println(t.name)
}
}
trait People{
val name:String = "xxx"
println("trait....")
}
class Headmaster extends People {
println("person class....")
}
class Teachersss extends Headmaster with People{
println("class....")
}
/*
trait....
person class....
class....
xxx
*/
当父类和特质属性冲突时,需要重写,否则异常
package study
object Main {
def main(args: Array[String]): Unit = {
println(new Student().name)
}
}
class Person() {
val name: String = "this is person name"
}
trait Sleep {
val name: String = "Sleep"
}
class Student extends Person with Sleep {
}
重写
package study
object Main {
def main(args: Array[String]): Unit = {
println(new Student().name)
}
}
class Person() {
val name: String = "this is person name"
}
trait Sleep {
val name: String = "Sleep"
}
class Student extends Person with Sleep {
override val name = "this is student name"
}
/*
this is student name
*/
动态混入
object TestTrait1 {
def main(args: Array[String]): Unit = {
// 特质动态混入
var t = new Te with People
}
}
trait People{
println("success")
}
class Te{
// 假设Te要实现people的功能,但是还不能继承
}
/*
success
*/
当一个类实现多个特质,且多个特质有相同的方法时,通过super调用,会实现最后的特质对应方法
特值可以继承独立的类、Java接口
object TestTrait1 {
def main(args: Array[String]): Unit = {
// 特质动态混入
var t = new Te
val test = t.test
println(test)
}
}
trait People {
this:Exception =>
def test: Unit ={
println("success")
// 想要获取异常的方法,继承可以实现
this.getMessage
}
}
class Te extends Exception with People {
// 假设Te要实现people的功能,但是还不能继承
}
特质自身类型:相当于引入一个对象
举例:封装一个DAO特质,用于写入
package study
object Main {
def main(args: Array[String]): Unit = {
new UserDaoOpter(1,"1212").insert()
}
}
class User(val id: Int, val name: String)
trait UserDao {
user: User =>
def insert() = println(s"insert into user ${user.name}")
}
class UserDaoOpter(id: Int, name: String) extends User(id, name) with UserDao {
}
/*
insert into user 1212
*/
特质和抽象类的区别:
抽象类可以定义带参数的构造函数、特征不行
抽象类只能继承一个
隐式转换
一下这种情况会报错
object TestImplicit {
def main(args: Array[String]): Unit = {
// 隐式转换
var int:Int = 5.0
}
}
隐式转换后
关键字:implicit
object TestImplicit {
def main(args: Array[String]): Unit = {
// 隐式转换
implicit def test(double: Double): Int = {
double.toInt
}
var int:Int = 5.0
println(int)
}
}
/*
5
*/
注意事项:
隐式转换方法自动调用,与方法名无关,但是与签名(参数类型、返回值类型)有关,同一个签名的隐式转换方法只能有一个
还可以用来增加方法:
在如下mysql类中添加delete方法:
object TestImplicit {
def main(args: Array[String]): Unit = {
}
}
class MySql{
def insert(): Unit ={
println("insert")
}
}
class DB{
def delete(): Unit ={
println("delete")
}
}
除了继承的方式,还可以使用隐式转换的方法:
object TestImplicit {
def main(args: Array[String]): Unit = {
implicit def addDelete(mySql: MySql): DB = {
new DB
}
var s:MySql = new MySql
s.delete()
}
}
class MySql {
def insert(): Unit ={
println("insert")
}
}
class DB{
def delete(): Unit ={
println("delete")
}
}
隐式值
如下代码
object TestImplicit {
def main(args: Array[String]): Unit = {
def test(name:String = "xxx"):Unit={// 默认值
println(name)
}
test()
}
}
如果某天我们想换一个默认值:
调用函数时不能加括号
object TestImplicit {
def main(args: Array[String]): Unit = {
implicit var name:String = "yyx"
def test(implicit name:String = "xxx"):Unit={// 默认值
println(name)
}
test
}
}
集合
Scala中集合分为可变集合和不可变集合
可变集合在包scala.collection.mutable
中
不可变集合在包scala.collection.immutable
中
Scala对几乎所有集合类都有可变和不可变版本(默认用不可变版本)
1.不可变集合:scala不可变集合,就是这个集合本身不能变内存地址不能变(类似java的数值,是不可以动态增长的)
2.可变集合:可变集合(ArrayList , 是可以动态增长的) 就是这个集合的本身是可以变的内存地址可变,因为成为一个新的集合
不可变集合
可变集合
Seq
不可变数组
所谓不可变,不是数值不能变,而是地址(类似String)插入、删除不行,查询+修改是可以的
object TestArray {
def main(args: Array[String]): Unit = {
// 不可边数组
val ints: Array[Int] = Array(1, 2, 3)
println("不可变数组长度"+ints.length)
// 输出对应索引位值的值
val i = ints.apply(2)
println(i)
// 添加元素,已经变为一个新数组ints1
val ints1 = ints :+ 5
val ints2 = 5+:ints
// 更新元素
ints.update(0,55)
// 遍历元素
println(ints.mkString(" "))
println(ints1.mkString("|"))
println(ints2.mkString("|"))
for (i<-ints1){
println(i)
}
def testxxxx(int: Int)= {
print("foreach遍历" + int + " ")
}
/*
ints1.foreach(testxxxx)
ints2.foreach((i:Int)=>{println(i)})//简单点
ints2.foreach((i)=>{println(i)})//再简单点
ints2.foreach({println(_)})//再再简单点
ints2.foreach({println})//再再再简单点
*/
}
}
可变数组:
import scala.collection.mutable.ArrayBuffer
object TestArray {
def main(args: Array[String]): Unit = {
// 可变数组
val arrayBuffer = ArrayBuffer(1, 2, 3, 4)
// 访问
println(arrayBuffer(2))//3
// 修改
arrayBuffer(0) = 9
println(arrayBuffer(0))//9
// 增
//val ints = arrayBuffer :+ 5//:+会返回一个新的arrayBuffer
//arrayBuffer.insert(4,6)// 按索引添加,如果长度不够会扩容,返回值为空(在当前数组上修改)
//arrayBuffer+=(5,6)//添加多个元素
// 删除元素
//arrayBuffer.remove(0) 按照索引删除
arrayBuffer.remove(0,2) // 从当前索引开始删除多少位
// 遍历
println(arrayBuffer.mkString("|"))
arrayBuffer.foreach(println) // foreach遍历和不可变数组一样
}
}
可变数组与不可变数组转换:
val buffer = arrayBuffer.toArray.toBuffer
二维数组
package study
object Main {
def main(args: Array[String]): Unit = {
val array: Array[Array[Int]] = Array.ofDim[Int](2, 3)
array(0)(2) = 50
array.foreach(_.foreach(println))
for (i <- array.indices; j <- array(i).indices) {
print(array(i)(j) + "\t")
if (j == array(i).length - 1) println()
}
}
}
List和ListBuffer
import scala.collection.mutable.ListBuffer
object TestList {
def main(args: Array[String]): Unit = {
// 创建一个List
val ints = List(1, 2, 3)
// List也有空集合
//println(Nil)
//println(List())
// 访问list中元素
//println(ints(0))
// 添加元素并返回新List
//val ints1: List[Int] = ints :+ 4
//println(ints1)
//List追加
val ints1 = 6 :: ints // 添加在前面
val ints2 = List(4,5,6)
val ints3 = ints2:::ints// 如果不使用三个:会添加一整个List(返回的List存储类型也会变为object)
println(ints3.mkString("|"))
// 可变的List
val ints4 = ListBuffer(1, 2, 3, 4)
val ints5 = ListBuffer(5,6,7,8)
ints4.insertAll(1, ints5)// 第一个变量为插入位置
println(ints4.mkString("|"))
}
}
队列
import scala.collection.mutable.Queue
object TestQueue {
def main(args: Array[String]): Unit = {
// 创建一个队列,队列要可变
val ints = Queue(1, 2, 3)
// 添加数据
ints.enqueue(5,6)
// 遍历
println(ints.mkString("|"))
// 出队
//ints.dequeue()队首
//ints.dequeueAll((i:Int)=>{false})// 是否全部出队
ints.dequeueFirst((i:Int)=>{true})// 是否队首出队
println(ints.mkString("|"))
// 其它
println(ints.head)// 返回队首
println(ints.tail)// 返回除了队首以外的元素形成的队列
println(ints.last)// 返回队列最后的元素
}
}
Set
无序、不可重复
import scala.collection.mutable
object TestSet {
def main(args: Array[String]): Unit = {
// 无序、不可重复
val set: Set[Int] = Set(1, 2, 3, 4 )
// for (elem <- set) {
// print(elem)不可重复
// }
// 通过+添加元素都会生成新的Set
// 但是,可变set有+=方法,不可变set没有此方法
//val ints = set +(5, 6, 8, 9, 8)
//println(set.mkString("|"))
val set1: mutable.Set[Int] = mutable.Set(1, 5, 3, 5, 4)
set1.+=(5)
// 删除元素
val ints2: Set[Int] = set - (1)
val value: set1.type = set1 -= (5)
println(set.mkString("|"))
println(set1.mkString("|"))
val ints = set +(5, 6, 8, 9, 8)
val value1: set1.type = set1 ++= ints
println(set1)// Set(9, 1, 5, 2, 6, 3, 4, 8)
}
}
set其他操作
Map
不可变的Map是有序的、可变的Map是无序的
object TestMap {
def main(args: Array[String]): Unit = {
// 默认不可变
val map = Map("a" -> 1, "b" -> 2, "c" -> 3)
println(map.mkString("|"))
val map1 = map.+("ss" -> 5)
println(map.mkString("|"))
println(map1.mkString("|"))
// 创建可变map
val map2 = mutable.Map("a" -> 1, "b" -> 2, "c" -> 3)
map2.+=("c" -> 4, "d" -> 5, "e" -> 6)
println(map2.mkString("|"))
// 获取数据
println(map2.get("s"))// None
println(map2.getOrElse("s", "没得"))// 没得
println(map2.get("a"))// Some(1)
println(map2.get("a").get)// 1
}
}
元组
// 类似于一个容器,将不相关的属性连接到一起
// 创建一个元组,元组中最大只能有22个元素
val tuple: (String, Int, Double) = ("sss", 1, 5.0)
// 访问元素
println(tuple._1)
println(tuple._2)
println(tuple._3)
/*
sss
1
5.0
*/
//整体遍历
for (elem <- tuple.productIterator){
println(elem)// 结果相同
}
集合常用功能
import scala.collection.immutable
object TestMethodUsU {
// 常用的List方法
def main(args: Array[String]): Unit = {
// 创建List
val ints: List[Int] = List(6, 7, 5, 6, 78, 5, 1, 5, 4, 3, 5)
// 最值
println(s"ints.max=${ints.max}")
println(s"ints.min=${ints.min}")
// 所有元素相乘
println(s"product=${ints.product}")
// 倒叙集合
println(s"reserve=${ints.reverse}") // 按照初始顺序倒叙
// 分组
println(ints.groupBy(x => x)) // 直接按照值分组
/*
Map(5 -> List(5, 5, 5, 5), 78 -> List(78), 1 -> List(1), 6 -> List(6, 6), 7 -> List(7), 3 -> List(3), 4 -> List(4))
*/
// 排序
println(ints.sortBy(x => x > 5)) // 按照是否大于5排
/*
List(5, 5, 1, 5, 4, 3, 5, 6, 7, 6, 78)
*/
// 按顺序排
println(ints.sortWith((x, y) => x > y)) // 左边大于右边=降序
/*
List(78, 7, 6, 6, 5, 5, 5, 5, 4, 3, 1)
*/
// 只输出固定数量元素
println(ints.take(4)) // List(6, 7, 5, 6)
// 转换类型:map,想做到输出数字以及数字出现次数
val tuples: List[(Int, Int)] = ints.map(x => (x, 1))//输出的类型为tuple
println(tuples) // List((6,1), (7,1), (5,1), (6,1), (78,1), (5,1), (1,1), (5,1), (4,1), (3,1), (5,1))
// 想做到次数
val intToTuples: Map[Int, List[(Int, Int)]] = tuples.groupBy(t => t._1) // 分组
println(intToTuples)
/*
Map(5 -> List((5,1), (5,1), (5,1), (5,1)), 78 -> List((78,1)), 1 -> List((1,1)), 6 -> List((6,1), (6,1)), 7 -> List((7,1)), 3 -> List((3,1)), 4 -> List((4,1)))
*/
// 再次map
val strings: immutable.Iterable[String] = intToTuples.map(t => t._1 + "--" + t._2.size)
println(strings)// List(5--4, 78--1, 1--1, 6--2, 7--1, 3--1, 4--1)
// reduce
val ints3 = List(5, 6, 7, 6, 78,66,55,44,889,564)
//ints3.reduce((l,r)=>{l+r})// 用于减少结果(对数据每两个进行操作)
val i = ints3.reduce(_ + _)
println(i)// 1720
// 类似的,有一个折叠
//val i1 = ints3.fold(1000)(_ - _)
val ints4 = List(1, 2, 3, 4)
val i = ints4.foldRight(10)(_ - _)
// reserve => 4,3,2,1
// foldLeft => (((10-4)-3)-2)-1
// foldRight => 1-(2-(3-(4-10)))
println(i)// 8
}
}
WordCount
object WordCount {
def main(args: Array[String]): Unit = {
val list = List("String Hello", "Hadoop Scala", "Hello Int", "MapReduce Hive HBase")
// wordCount,可是每个单词中间有空格
val strings = list.flatMap(s => s.split(" "))//扁平化转
val stringToStrings = strings.groupBy(x => x) // 分组
val stringToInt: Map[String, Int] = stringToStrings.map(s => (s._1, s._2.size))// 转为map
// 按降序拍个序,转为List后为List((K,V),(K,V)...)
val tuples = stringToInt.toList.sortWith((l, r) => {
l._2 > r._2
})
// 遍历
println(tuples.mkString("|"))
}
}
/*
(Hello,2)|(Hive,1)|(MapReduce,1)|(Scala,1)|(HBase,1)|(Int,1)|(Hadoop,1)|(String,1)
*/
```java
object WordCount {
def main(args: Array[String]): Unit = {
// 新的WordCount测试
val list = List(("hello world", 7), ("hello hadoop", 5), ("hello MR", 5))
val tuples = list.flatMap(l => {
// 首先,找到单词
val word = l._1.split(" ")
val count = l._2
word.map(w => (w, count))
}) // List((hello,7), (world,7), (hello,5), (hadoop,5), (hello,5), (MR,5))
// 分组
val stringToTuples = tuples.groupBy(s => s._1) //按照第一位分组
// Map(hadoop -> List((hadoop,5)), MR -> List((MR,5))
val stringToInt = stringToTuples.mapValues(data => {
data.map(tt => tt._2).sum
})
println(stringToInt)
}
}
/*
Map(hadoop -> 5, MR -> 5, world -> 7, hello -> 17)
*/
fold的应用:
import scala.collection.mutable
object TestFold {
def main(args: Array[String]): Unit = {
// 将两个map合并,相同K的V进行累加
val map1 = mutable.Map("a" -> 1,"b" -> 2,"c" -> 4)//一定要可变的
val map2 = mutable.Map("d" -> 5,"b" -> 7,"c" -> 5)
val stringToInt = map1.foldLeft(map2)((map, t) => {
map(t._1) = map.getOrElse(t._1, 0) + t._2 // map的赋值方式map(k) = v
map
})
println(stringToInt)// Map(b -> 9, d -> 5, a -> 1, c -> 9)
}
}
交集并集差集、关联
zip关联
val ints1 = List(5, 5, 1, 5, 4, 3, 5, 6, 7, 6, 78)
val ints2 = List(5, 6, 7, 6, 78,66,55,44,889,564)
// 关联
val tuples1 = ints1.zip(ints2)
println(tuples1)//List((5,5), (5,6), (1,7), (5,6), (4,78), (3,66), (5,55), (6,44), (7,889), (6,564))
联合(并集)
// 联合
println(ints1.union(ints2))// List(5, 5, 1, 5, 4, 3, 5, 6, 7, 6, 78, 5, 6, 7, 6, 78, 66, 55, 44, 889, 564)
交集:
// 求交集
println(ints1.intersect(ints2))//List(5, 6, 7, 6, 78)
差集:
// 差集
println(ints1.diff(ints2))// List(5, 1, 5, 4, 3, 5)