Scala思维导图
2022-04-01 10:24:57 73 举报
AI智能生成
Scala思维导图
作者其他创作
大纲/内容
Scala是一门以Java虚拟机 (JVM)为运行环境的并将面向对象和函数式编程的最佳特性结合在一起的静态类型编程语言
Scala介绍
1.Scala是一门多范式的编程语言,支持面向对象和函数式编程
2.Scala源代码(.Scala)会编译成Java字节码(.class)然后运行在JVM之上,并可以调用现有的Java类库,实现两种语言的无缝对接
3.Scala但作为一门语言来看,非常的简洁高效,不支持三元运算,++,--
Scala特点
object helloscala{ def main(arg:Array[String]):Unit={ printlin(\
①object表示一个伴生对象,可以理解成一个对象②helloscala是对象名,他底层对应的类名是helloscala$③当编写object helloscala时,底层会生成两个.class文件,分别时helloscala.class和helloscala$.class(Java程序编写时只会生成一个.class文件)④def表示一个方法,是一个关键字⑤main表示方法的名字,是程序的入口⑥args:Array[String]表示形参,Scala特点是参数名在前,类型在后⑦Array[String]表示字符串类型的数组⑧Unit=表示该函数的返回值为空(void)
基本语法
Scala plugin下载地址:https://plugins.jetbrains.com/plugin/1347-scala/versions
1.打开idea2.点击左上角file>settings3.点击plugins4.在搜索框中输入scala5.如果没搜到则表示没有安装scala plugin6.点击install plugin from disk7.找到插件路径并点击ok8.点击右下角apply9.点击ok10.重启idea11.如果安装成功,则会在plugin中搜索到scala
Scala plugin安装
1.点击file>new>project...2.新建一个maven工程3.到main下创建一个Scala文件夹4.将Scala文件夹source成root(根目录)\t①右键文件夹\t②选择mark directory as\t③选择source root5.导入Scala framework6.右键项目名(Scala)7.点击add framework support...8.从右边的选择栏中选择Scala9.点击Use library 右侧的create10.点击底侧的browse11.找到Scala的安装主目录点击ok即可12.创建Scala object
用idea运行Scala程序
运行第一个Scala程序
数据类型一览
1.Scala的数据类型与Java的基本一样
2.在Scala中有一个根类型Any,它是是所有类的父类
3.Scala中一切皆为对象,分为两大类AnyVal(值类型),AnyRef(引用类型)
3.Null类型是Scala的特别类型,它只有一个值null,它是bottom class,是所有AnyRef类型的子类
4.Nothing类型也是bottom class,它是所有类的子类,在开发过程中可以将Nothing类型的值返回给任意变量或者函数,这里抛出异常使用很多
5.在Scala中仍然遵守,低精度的值向高精度的值自动转换(implictit conversion)隐式转换
数据类型说明
1.当进行数据的从大到小时,就需要使用强制转换
2.强转符号指针对最近的操作数有效,往往会使用小括号提升优先级
3.char类型可以保存int的常量值,但是不能保存int的变量值
4.byte和short类型在进行运算时,当做int类型处理
数据类型的强制转换
一.介绍 在程序开发中,经常需要将基本数据类型转换成string类型,或者将string类型转换成基本数据类型
二.基本数据类型转string类型 语法:在基本数据类型的值后+\"\"即可 例子:String str1 = true+\"\" String str2 = 4.5+\"\" String str3 = 100+\"\"
三.string类型转换成基本数据类型 语法:通过基本数据类型的String的toxxx方法即可转换 例子:s1.toInt s1.toFloat s1.toDouble s1.toByte s1.toLong s1.toShort
值类型和String类型的转换
数据类型
一.标识符概念 1.Scala对各种变量,方法,函数等命名时使用的字符序列称为标识符 2.凡是自己可以取名字的地方都叫标识符
二.标识符的命名规则 Scala中的标识符声明基本和Java一致,但是细节上会有些变化 1.首字母为字母,后续可以接字母数字,美元符号,也可以接下划线 2.数字不可作为开头 3.首字符为操作符(比如+-*/),后续字符也要跟操作符,至少一个 4.操作符(比如+-*/)不能在标识符的中间或最后 5.关键字也是可以作为标识符,但是需要加上反引号,比如(`true`)
三.标识符命名的细节 1.在Scala中Int,Float不是关键字,而是预定义标识符。所以Int,Float可以作为标识符,但是不推荐 2.单独的下划线不能作为标识符(_)。因为_有很多其他的作用。
四.标识符命名注意事项 1.包名:尽量采取有意义的包名,简短,有意义 2.变量名,函数名,方法名尽量采用驼峰法
标识符命名规则
一.运算符介绍 运算符是一种特殊的符号,用来表示数据的运算,赋值和比较等 1.算术运算符 + - * / % 2.赋值运算符 = 3.关系运算符 < > = 等 4.逻辑运算符 与(&&) 或(||) 非(!) 5.位运算符
二.算术运算符中的细节 1./除的运算规则:a/b的结果去整数部分,比如3/2=1 2.%取余的运算规则:a%b=a-a/b+b
三.关系运算符中的细节 1.如果两个浮点数进行比较,应当保证数据类型一致
运算符
1.从控制台接受用户的信息 import scala.io.StdIn println(\"请输入姓名\") var name = StdIn.readLine
键盘输入语句
流程控制有三种 1.顺序控制 2.条件(分支)控制 3.循环控制
条件控制1.Scala条件控制和Java基本一样,Scala中没有三元表达式2.注意:Scala用match替代了switch语句,而且更加强大
1.循环守卫 ①.基本案例 for(i <- 1 to 3 if i!=2){ println(\"\") } ②.循环守卫,即循环保护式(也称条件判断式),保护式为true则进入 循环体内部,为false则跳过,类似于continue 2.引入变量 ①基本案例 for(i <- 1 to 3;j=4-1){ print(j+\" \
for循环
二.while语句循环 var i = 0 while (i < 10) { println(\
while循环
三.do while循环 var j = 1 do { println(\"中国牛逼!\" + j) j += 1 } while (j < 10) }
do-while循环
循环控制
流程控制
1.基本案例 import util.control.Breaks._ var n=10 breakable( while (n <=20){ n+= 1 if (n == 18){ break() } } )
break
Scala内置控制结构特地的去掉了break和continue,是为了更好的适应函数化编程,推荐使用函数的风格解决break和continue的功能,而不是一个关键字二.continue continue可以利用循环守卫实现
continue
循环中断
1.scala.collection\t该包和子包,包含了Scala集合架构\t①scala.collection.immutable\t不可变的顺序数据结构\t②scala.collection.mutable\t可变的顺序数据结构\t③scala.collection.concurrent\t可变的并发数据结构\t④scala.collection.parallel.immutable\t不可变的并行数据结构\t⑤scala.collection.parallel.mutable\t可变的并行数据结构2.scala.concurrent\t并行编程的基础类3.scala.io\t输出和输入操作4.scala.math\t基本数学函数和附加数字类型 bigInt等5.scala.sys\t以其他进程和操作系统进行交互6.scala.util.matching\t\t常规操作包
scala常用包
Scala语法基础
一.函数式编程基础 1.函数定义/声明 2.函数运行机制 3.递归 4.过程 5.惰性函数和异常
函数式编程基础
函数式编程高级
学习过程
一.方法,函数,函数式编程和面向对象概念 1.在Scala中,方法和函数几乎可以等同(比如它们的定义,使用,运行机制都是一样的),只是函数的使用方法更加灵活多样 【方法转函数】 2.函数式编程是从编程方式(范式)的角度来谈的。可以理解为,函数式编程把函数当成一个公民,充分并灵活利用函数,支持函数的多种使用方式。函数的创建不在依赖类或者对象 3.面向对象编程是以对象为基础的编程方式。 4.在Scala中函数式编程和面向对象编程融合在一起
三.函数的递归 1.在执行一个函数时,就创建一个新的受保护的独立空间(新函数栈) 2.函数的局部变量是独立的,不会相互影响 3.递归必须向退出递归条件逼近,否则就无限递归了 4.当一个函数执行完毕,或者遇到return,就会返回,遵守谁调用就返回给谁
函数式编程介绍
函数的注意事项和细节
1.过程的基本介绍 将函数的返回类型为Unit的函数称为过程,如果没有明确函数没有返回值,那么等号可以去掉。简单来说:没有返回值的函数称为过程。
过程
一.惰性函数使用的场景 惰性计算(尽可能延迟表达式计算)是许多函数式编程语言的特性。惰性集合在需要时提供其元素,无需预先计算它们,这带来一些好处,首先,可以将耗时计算推迟到绝对需要的时候。其次,可以创造无限个集合,只要它们继续收到请求,就会继续提供元素。函数的惰性使用可以得到更高效的代码。Java并没有为惰性提供原生支持,Scala提供了二.惰性函数介绍 当函数返回值被声明为lazy时,函数执行将会被推迟,直到我没首次对此取值,该函数才会执行。这种函数称之为惰性函数三.注意事项和细节 1.lazy不能修饰var类型的变量 2.不但是在调用函数时,加了lazy,会导致函数的执行被推迟,我们在声明一个变量时,如果声明了lazy,那么变量值的分配也会被推出 例如:lazy val i =10
惰性函数
一.异常1.Scala提供try和catch块来处理异常,try块用于包含可能出错的代码,catch块用于处理try块中发生的异常。可以根据需要在程序中设置任意数量的try...catch块二.回顾:Java异常处理的注意点1.Java语言按照try-catch-catch...-finally的方式来处理异常2.不管有没有异常捕获,都会执行finally,因此通常可以在finally代码块中释放资源3.可以有多个catch,分别捕获对应的异常,这时需要把范围小的异常写在前面,把范围大的异常写在后面,否则编译错误三.Scala异常处理1.Scala异常处理语法与Java基本的相同,但是Scala中没有checked(编译期)异常,即Scala中没有编译异常这个概念,异常都是在运行时处理2.在Scala中只有一个catch,在这个catch中可以有多个case3.用throw关键字,抛出一个异常对象。所有异常都是throwable的子类型,throw表达式是有类型的,就是nothing,因为nothing是所有类型的子类型,所有throw关键字可以用在需要类型的地方def main(args:Array[String]):Unit={val res=test()println(res.roString)}def test():Nothing={throw new Exception(\"异常\")}
基本介绍
//判断输入的字符串内容是否为数字 def main(args: Array[String]): Unit = { val s1=\"1231\" val r1=scala.util.Try(s1.toInt) val result = r1 match { case Success(_) => s1.toInt ; case _ => s1 } val result2 = result.getClass.getSimpleName print(result2.asInstanceOf[Int]) }
使用基本案例
异常
Scala函数式编程
一.Scala面向对象介绍1.在Scala中一切皆为对象2.面向对象的三大特征:封装,继承,多态
二.类和对象的区别 1.类是抽象的,概念的,代表一类事物,比如人类,猫类 2.对象是具体的,实际的,代表一个具体事物 3.类是对象的一个模板,对象是类的一个个体,对应一个实例 4.Scala中类和对象的区别和联系是跟Java一样的
三.属性(成员变量) 1.属性是类的一个组成部分,一般是值数据类型,也可以是引用类型。 2.属性的定义语法同变量,实例 【访问修饰符默认是私有的】var name:String=\"\" 3.Scala中声明一个属性,必须给定默认值,然后根据默认值自动推断数据类型,数据类型可以省略 4.如果赋值为null,则一定要加类型,如果不加类型,那么该属性的类型就是null 5.属性的高级部分和构造器(构造方法/函数)相关。
四.方法 1.方法的调用机制①当Scala程序开始执行时,先会在栈区开辟一个main栈。main栈是最后被销毁的 ②当Scala程序执行到一个方法时,会开辟一个新栈 ③每个栈是独立的空间,变量(基本数据类型)是独立的,相互不受影响,引用类型除外 ④当方法执行完毕后,该方法的栈会被jvm回收
五.创建对象 基本语法 val | var 对象名[:类型] = new 类型() 1.如果我们不希望改变对象的引用(即:内存地址),应该声明val,否则声明var。Scala设计者推荐使用val,因为一般来说,我们只改变对象属性值,而不是改变对象的引用 2.Scala在声明对象变量时,可以根据创建对象的类型自动判断,所以类型声明可以省略,但是当类型和后面new对象类型有继承和多态时,就必须写了
面向对象基础
一.使用构造器的需求 前面在创建Person对象时,是先把对象创建好后,再给这个对象的姓名和年龄赋值。现在有个需求,就是在创建人类这个对象时,就直接指定这个对象的年龄和姓名,这时就需要用到构造器或者构造方法
二.回顾:Java构造器特点 1.在Java中一个类可以定义多个不同的构造方法,构造方法重载 2.如果程序员没有定义构造方法,系统会自动给类生成一个默认的无参构造方法(也叫默认构造器)比如Person(){} 3.一旦顶定义了直接的构造方法(构造器),默认的就会被覆盖,除非显示定义以下
三.Scala构造器的介绍 和Java一样,Scala构造对象也需要调用构造方法,并且有任意多个构造方法(即Scala构造器也支持重载) Scala类的构造器包括:主构造器,辅助构造器
四.Scala构造器的基本语法 class 类名(形参列表){//主构造器 //类体 def this(形参列表){//辅助构造器 } def this(形参列表){//辅助构造器 }说明:主构造器只有一个,辅助构造器函数名称为this,可以有多个。编译器通过不同参数来区分
基本说明
1.Scala构造器作用是完成对新对象的初始化,构造器没有返回值2.主构造器的声明直接放置于类名之后3.主构造器会执行类定义中的所有语句,这里可以体会到Scala的函数式编程和面向对象编程融合在一起,即:构造器也是方法(函数),传递方法以及使用方法和前面函数部分内容没有区别4.如果主构造器无参数,小括号可以省略,构造对象时调用构造方法的小括号也可以省略5.辅助构造器的名称为this(这个和Java是不一样的),多个辅助构造器通过不同参数列表进行区分,在底层就是构造器重载。6.辅助构造器一定要先调用主构造器,执行主构造器中的逻辑,并且需要在第一行调用7.Java里辅助构造器可以直接super去调用父类的主构造器,但是在Scala中只允许主构造器去调用父类的主构造器,使用辅助构造器先要调用主构造器,主构造器会默认先执行父类中的构造器8.如果想让主构造器变成私有的,可以在()之前加上private,这样用户只能通过辅助构造器来构造对象9.辅助构造器的声明和主构造器的声明一致,否则会发生错误
构造器使用注意事项和细节
1.Scala类主构造器的形参未用任何修饰符,那么这个参数就是局部变量\tclass B(name:String){}2.如果参数使用val关键字声明,那么Scala会将参数作为类的私有的只读属性\tclass B(val name:String){}3. 如果参数使用var关键字声明,那么Scala会将参数作为类的成员属性使用,\tclass A(var name:String){}4.Bean属性,使用@BeanProperty注解 会在底层自动生成getXxx()和setXxx()方法 \t@BeanProperty var name =\"张三\"
属性高级(结合构造器)
构造器
1.回顾:Java包的作用 ①区分相同名字的类 ②当类很多时,可以更好的管理类 ③控制访问范围2.打包基本语法 package com.java3.打包的实质 就是创建不同文件夹来保存类文件
Java包介绍
一.Scala包基本介绍 和Java一样,Scala中管理项目可以使用包,但Scala包中功能更强大,使用也相对复杂。
二.Scala包和Predef包可以直接使用
三.Scala包的引入 1.Scala引入包也是import,基本原理和机制和Java一样,但是Scala中的import功能更加强大,灵活 2.因为Scala语言源自于Java,所以Java.lang包中的类会自动引入到当前环境中,Scala中的Scala包和predef(预定义)包的类也会自动引入到当前环境中,即下面的类可以直接使用 3.如果想把其他包中的类引入到当前环境中,需要用import
Scala包介绍
一.包对象介绍 包可以包含类,对象和特质trait,但不能包含函数/方法或变量的定义。这是Java虚拟机的局限,为了弥补这一点不足,Scala提供包对象来解决这个问题
二.包对象的注意事项 1.每个包中只能有一个包对象 2.包对象名称需要与包名一致,一般用来对包的功能补充
包对象使用注意事项
Scala包对象
一.回顾:Java访问修饰符基本介绍 1.公开级别:public 2.受保护级别:protected,对子类和同一个包中的类公开 3.默认级别:没有修饰符,向同一个包的类公开 4.私有级别:private,只有类本身可以访问,不对外公开
二.Scala包的可见性 1.当属性访问权限为默认时,从底层看属性时private,但是提供了xxx_$eq()【类似setter】/xxx()【类似getter】方法,因此从效果上看时任何地方都可以访问 2.当方法访问权限问默认时,默认为public访问权限 3.private时私有权限,只能在类的内部和伴生对象中使用 4.protected为受保护权限,在Scala中受保护权限比Java更严格,只能子类访问,同包无法访问 5.在Scala中没有public关键字,即不能用public显式的修饰属性和方法 6.包访问权限(表示属性有了限制,同时包也有了限制),这点和Java不一样,体现出Scala包使用的灵活性
包的可见性
包
一.封装 1.封装就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序其他部分只有通过被授权的的操作(成员方法),才能对数据进行操作 2.封装的理解和好处 ①隐藏实现的细节 ②可以对数据进行验证,保证安全合理 ③同时可以加入业务逻辑3.如何体现封装 ①对类的属性进行封装 ②通过成员方法,包实现封装 4.封装的实现步骤 ①将属性进行私有化 ②提供一个公共的set方法,对属性判断并赋值 def setXxx(参数名:类型){ //加入数据验证的业务逻辑 //属性=参数名 } ③提供一个公共的get方法,用于获取属性\t的值 def getXxx(){ return 属性 } 5.Scala的封装特性 ①Scala中为了简化代码的开发,当声明属性时,本身就提供了对应的setting/getting方法,如果属性声明为private,那么生成setting/getting方法也是private,如果属性省略访问权限修饰符,那么生成setting/getting方法就是public ②如果我们对一个属性进行简单的set和get,只要声明一下该属性(属性使用默认访问修饰符)不用专门的getting和setting,默认会创建 ③从形式上看dog.food直接访问属性,其实底层仍然是访问方法, ④有了上面的特性,目前很多新的框架,在进行反射时,也支持对属性的直接反射
封装
一.回顾:Java继承 class 子类名 extends 父类{类体}
二.继承的基本介绍 1.继承可以解决代码复用,让编程更加靠近人类思维,当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的方法和属性,所有子类不需要重新定义这些方法和属性,只需要通过extends语句声明继承父类即可。和Java一样,Scala也支持类的单继承 2.子类继承了父类所有属性,只是私有的属性不能直接访问,需要通过公共的方法去访问
三.重写方法 Scala明确规定,重写一个非抽象方法需要用override修饰符,调用超类(父类)方法使用super方法
四.Scala类型检查和转换 1.要测试某个对象是否属于给定的类,可以用isInstanceOf方法。用asInstanceOf方法将引用转换为子类的引用。classOf获取对象的类名\t①obj.isInstanceOf[T] 就如同Java中的obj isInstanceOf T 判断obj是不是T类型\t②obj.asInstanceOf[T] 就如同Java的(T).obj 将obj强转成T类型\t③classOf[String] 就如同Java的 String.class 输出方法的类名 2.类型的检查和转换最大的价值在于:可以判断传入对象的类型,然后转换成对应的子类对象,进行相关的操作,这里也体现出多态的特点
五.Java中超类的构造 1.超类:表示父类或者父类的父类 2.从代码上看,在Java中,创建子类对象时,子类的构造器总是去调用父类的构造器(显式或者隐式
六.Scala中超类的构造 1.类有一个主构造器和任意数量的辅助构造器,而每个辅助构造器都必须先调用主构造器(也可以是间接调用)。 2.只有主构造器可以调用父类的构造器。子类的辅助构造器不能直接调用父类的构造器。在Scala的构造器中,不能直接调用super(params)
七.回顾 :Java中的覆写字段 1.在Scala中,子类改写父类的字段,称之为覆写(重写)字段,覆写字段需要用到override修饰 2.回顾:在Java中只有方法的重写,没有属性(字段)的重写,准确的讲,是隐藏字段代替了重写 3.Java中有一个隐藏字段的概念,当父类和子类定义了一个同名的字段,不会报错。但对于同一个对象,用父类的引用去取值(字段),会取到父类字段的值,用子类的引用去取值(字段),就会取到子类字段的值。在实际开发中,要尽量避免父类和子类使用了同名的字段,否则会出现一些不容易发现的bug 4.Java中的动态绑定机制(类与类属性和方法都重名) ①如果调用的是方法,则jvm机会将方法和对象的内存地址动态的绑定。简单来说,方法会先在自己类中调用,如果自己类中没有该方法,则会到父类中调用 ②如果调用的是属性,则没有动态绑定机制,在哪里调用就返回对应的值。简单来说,方法调用属性,会直接在本类中寻找该属性
八.覆写字段的注意事项和细节 1.def只能重写另一个def(即方法只能重写另一个方法) 2.val只能重写另一个val属性 或不带参数的def 3.var只能重写另一个抽象的var属性 \t抽象属性:声明未初始化的变量就是抽象的属性,抽象属性在抽象类 var重写抽象的var属性小结\t①一个属性没有初始化,那么这个属性就是抽象属性\t②抽象属性在编译成字节码文件时,属性并不会声明,但是会自动生成抽象方法,所有类必须声明为抽象类\t③如果时覆写一个父类的抽象属性,那么override关键字可以省略(原因:父类的抽象属性生成的时抽象方法,因此就不会涉及到方法重写的概念,因此override可以省略)
子主题
Scala继承关系层级图
继承
什么是多态:目的是为了让代码更加,降低耦合\t\t\t有继承或实现特质(接口)\t\t\t父类引用指向子类对象或接口指向实现类
多态
面向对象的三大特性
Scala面向对象编程
一.抽象类 在Scala中,通过abstract关键字标记并不能被实例化的类。方法不用标记abstract,只要省略方法体即可。抽象类可以拥有抽象字段,抽象字段就是没有初始化的字段
二.抽象类的价值 抽象类的价值更多的是在于价值,是设计者设计好后,让子类实现并继承抽象类(即:实现抽象类的抽象方法)
三.抽象类的使用注意事项和细节 1.默认情况下抽象类不能被实例化,但是在实例化时动态的实现了抽象类中所有的抽象方法,就可以实例化 2.抽象类不一定要包含abstract方法。也就是说抽象类可以没有abstract方法 3.一旦类中包含抽象方法或者抽象属性,这个类必须要声明abstract 4.抽象方法不能有主体,不允许使用abstract修饰 5.如果一个类继承了抽象类,那么这个类必须实现这个抽象类的所有抽象方法和抽象属性,除非它自己声明abstract 6.抽象方法和抽象属性不能使用private和final关键字,因为这些关键字是和重写/实现相违背的 7.抽象类可以有实现的方法 8子类重写抽象方法不需要加上abstract,加了也不会错
抽象
一.Java静态概念 1.代码的基本使用 public static 返回值类型 方法名(参数列表){方法体} 说明:Java中的静态方法并不是通过对象调用的,而是通过类对象调用的,使用静态操作并不是面向对象的
二.Scala的静态概念(伴生对象) Scala语言完全是面向对象的(万物皆对象)的语言,所以并没静态的操作(即在Scala中没有静态的概念)。但是为了和Java语言交互(因为Java中友静态概念),就产生了一种特殊的对象来模拟类对象,我们称之为伴生对象。这个类的所有静态内容都可以放置在它的伴生对象中声明和调用
静态介绍
三.伴生对象小结 1.scala中伴生对象用object关键字声明,伴生对象中的内容全部是静态 2.伴生对象对应的是伴生类,伴生对象的名称和伴生类的名称要一致 3.伴生对象中的属性和方法可以直接通过伴生对象名称调用 4.从语法上来看,所谓的伴生对象就是静态方法和静态属性的集合 5.从技术上来看,Scala还是没有生成静态的内容,只不过是将伴生对象生成了一个新的类,实现属性和方法的调用 6.从底层原理上看,伴生对象静态特性是依赖于public static final MODULE$实现的 7.伴生对象和伴生类的声明必须在同一个源码文件中,但是没有伴生类,就没有所谓的伴生对象了,所有放哪里无所谓,但是底层还是生成了一个空的伴生类 8.如果class A独立存在,那么A就是一个类;如果object A独立存在,那么A就是一个“静态”性质的对象(类对象) 9.当一个文件中同时存在伴生对象和伴生类,那么文件的图标会发生变化
伴生对象小结
四.伴生对象apply方法 1.在伴生对象中定义apply方法,可以实现类名()的方式创建对象实例
apply方法
伴生对象和伴生类
一.特质的声明 trait 特质名{ trait体 } trait的命名一般首字母大写
二.Scala中特质的使用 一个类中具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所有要素,所有在使用时也是用extends关键字,如果有多个特征存在父类,那么需要用with关键字连接 1.没有父类 class 类名 extends 特质1 with 特质2 with 特质3 2.有父类 class 类名 extends 父类 with 特质1 with 特质2
三.快速入门 1.可以把特质看作是对继承的一种补充。Scala的继承是单继承,也就是一个类最多只能有一个父类,这就对子类功能的扩展有一定的影响。所以我们认为:Scala引入trait特质,第一可以替代Java的接口,第二也是对单继承的一种补充
四.特质的再说明 1.trait可以同时拥有抽象方法和具体方法,一个类可以实现或者继承多个特质 2.Java中的接口可以直接当成Scala特质使用
特质基本介绍与快速入门
一.回顾Java接口 1.声明接口 interface 接口名 2.实现接口 class 实现接口类类名 implements 接口名1,接口名2 3.小结 ①在Java中,一个类可以实现多个接口 ②在Java中,接口之间支持多继承 ③接口中属性都是常量 ④方法都是抽象的也可以是具体的
Java接口
二.Scala接口的介绍 1.从面向对象来看,接口并不属于面向对象的范畴,Scala是纯面向对象的语言,在Scala中没有接口 2.Scala语言中采用特质trait(特征)来代替接口的概念,多个类具有相同的特征时,就可以将这个特征独立出来,采用关键字trait声明。简单理解为 trait等价于 interface+abstract
Scala用特质代替接口
一.叠加特质的基本介绍 构建对象的同时如果混入多个特质,称之为叠加特质 那么特质声明顺序从左到右,方法执行顺序从左到右
二.叠加特质的注意事项和细节 1.特质声明顺序从左到右 2.Scala在执行叠加对象方法时,会首先从最右边的特质开始执行,类似于数据结构栈 3.Scala特质中如果调用super方法,并不是表示调用父特质中的方法,而是向左边继续查找特质,如果左边没有特质,此时就会调用父特质 4.如果想要调用具体特质的方法,可以指定super[特质名].方法,其中泛型必须是该类的直接超类类型
三.在特质中重写抽象方法 trait A{ def insert() //抽象方法}trait B extends A{ override def insert(): Unit = { println(\"将数据保存到文件中!\") super.insert() //思考这里super如何调用父特质中的抽象方法 }} 以上代码会出错,原因:是没有完全实现insert方法,同时还要声明abstract override 解决方法:①去掉super.insert()\t②调用父特质的抽象方法,那么在实际使用中,没有方法的具体实现,无法通过编译,为了避免这种情况的发生。可以重写抽象方法,这样在使用过程中必须考虑动态混入的顺序问题
叠加特质
一.富接口的概念 当一个特质中既有抽象方法,又有非抽象方法,这种特质我们称之为富接口
二.特质中的具体字段 特质中可以定义具体字段,如果初始化了就是具体字段,如果没有初始化就是抽象字段。混入该特质的类就具有了该字段,字段不是继承而是直接加入类,成为自己的字段
三.特质构造顺序 1.特质也是有构造器的,构造器中的内容由“字段初始化”和一些其他语句构成。具体实现请参考“叠加特质”第一种特质构造顺序(声明类的同时混入特质【静态混入】) ①调用当前类的超类构造器 ②第一个特质的父类构造器 ③第一个特质构造器 ④第二个特质构造器的父特质构造器,如果已经执行,就不会在执行 ⑤第二个特质构造器 ⑥重复4,5步骤(如果有第3,4个特质) ⑦最后执行本类的构造 第二种特质构造顺序(构建类的同时混入特质【动态混入】) ①调用当前类的超类构造器,然后执行本类的构造 ②第一个特质的父类构造器 ③第一个特质构造器 ④第二个特质构造器的父特质构造器,如果已经执行,就不会在执行 ⑤第二个特质构造器 ⑥重复4,5步骤(如果有第3,4个特质) 分析两种方式对构造顺序的影响 第1种方式实际是构建类对象,在混入特质时,该对象还没有创建 第2种方式实际是构造匿名子类,可以理解成在混入特质时,该对象已经创建
四.扩展类的特质 1.特质可以继承类,以用来扩展该特质的一些功能 2.所有混入该特质的类,会自动成为那个特质所继承的超类的子类 3.如果混入该特质的类,已经继承了另一个类(A类),则要求A类是特质超类的子类,否则就出现了多继承,就会报错。五.自身类型 说明:自身类型主要是为了解决特质的循环依赖问题,同时可以确保特质在不扩展某个类的情况下,依然可以做到限制混入该特质的类的类型
特质扩展
特质
一.嵌套类 在Scala种,你几乎可以在任何语法结构中嵌套任何语法结构。如果类中可以再定义一个类,这样的类就是嵌套类,其他语法结构也是一样的。嵌套类类似于Java种的内部类 面试题:Java种,类有哪五大成员?\t1.属性\t2.方法\t3.内部类\t4.构造器\t5.代码块
二.回顾:Java内部类 在Java种,一个类的内部又完整的嵌套了另一个完整的类结构。被嵌套的类成为内部类(inner class),嵌套其他类的类称为外部类。内部类最大的特点就是可以直接访问私有属性,并且可以体现类于类的之间的包含关系三.Java内部类的分类 1.从定义在外部类的成员位置上来看 ①成员内部类(没有用static修饰) ②静态内部类(用static修饰) 2.定义在外部类局部位置上(比如方法内)来看 ①分为局部内部类(有类名) ②匿名内部类(没有类名)
四.Scala嵌套类的使用 1.内部类如果想要访问外部类中的属性,可以通过外部类对象访问。即访问方式:外部类名.this.属性名 2.内部类如果想要访问外部类的属性,也可以通过外部类别名访问(推荐)。即访问方式:外部类别名.this.属性名五.类型投影 是指在方法声明上,如果使用 外部类#内部类 的方式,表示忽略内部类对象关系,等同于Java内部类的操作,我们将这种方式称为类型投影
嵌套类
一.隐式转换的实际需要 指定某些类型的相互转换 比如 Double <=> Int
二.隐式转换基本介绍 隐式转换函数是以implicit关键字声明的带有单个参数的函数。这种函数将会自动应用,将值从一种类型转换成另一种类型
三.快速入门 implicit def f1(d:Double):Int={ //将double类型转换成int类型 d.toInt }
四.隐式转换注意事项和细节 1.隐式转换函数的函数名可以是任意的,隐式转换于函数名称无关,只与函数签名(函数参数类型和返回值类型)有关 2.隐式函数可以有多个(隐式函数列表),但是要保证当前环境下只有一个隐式函数能被识别
五.隐式转换丰富类库的功能 1.如果需要为一个类增加一个方法,可以通隐式转换来实现。(动态增加功能),比如想为MySQL类增加一个delete方法 2.当前程序中,如果想要给MySQL类增加功能是非常简单的,但是在实际项目中,如果想要增加新的功能就要修改源代码,这是很难接受的。而且违背了软件开发的OCP开发原则(闭合原则:open close priceple),这种情况下,可以通过隐式转换函数给类添加功能
六.隐式值 1.隐式值也叫隐式变量,将形参变量标记成implicit,所以编译器会在方法省略隐式参数的情况下,去搜索作用域中的隐式变量作为缺省参数(默认值)。 2.当同时有implicit值和默认值时,会调用implicit值,因为implicit值的优先级高 3.当同时有implicit值和默认值时,但是两则类型不匹配,则会调用默认值 4.当同时有implicit值和没有默认值时,但是implicit值类型与形参类型不匹配,此时会报错
总结:\t①在程序中,同时有 隐式值 默认值 传值 时\t②优先级为 传值 > 隐式值 > 默认值\t③在隐式值匹配时,不能有二义性\t④如果隐式值 默认值 传值都不存在,则程序会报错
隐式转换和隐式参数
一.隐式类的基本介绍 在Scala2.10后提供了隐式类,可以使用implicit声明类,隐式类非常强大,同时可以扩展类的功能,比前面使用的隐式转换(函数)丰富类库功能更加方便,在集合中隐式类会发挥重要作用
二.隐式类的特点 1.其他带有构造参数有且只能有一个 2.隐式类必须定义在“类” “伴生对象”“包对象”中,既隐式类不能时顶级的(top-class objects) 3.隐式类不能是case class样例类(case class后面讲解) 4.作用域内不能有与之相同名称的标识符(名称)
三.创建方式implicit class BBB(i: Int){ def add(j: Int) = i + j }}
隐式类
一.隐式转换时机 1.当方法中的参数的类型与目标类型不一致时 2.当对象调用所在类中不存在的方法和成员时,编译器会自动将对象进行隐式转换
二.隐式解析机制 即:编译器是如何查找到缺失信息的,解析具有以下两种规则 1.首先会在当前代码作用域下查找隐式实体(隐式方法,隐式类,隐式对象),这是一般情况 2.如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域查找。类型的作用域是指与该类型相关联的全部伴生模块,一个隐式实体的类型T它的查找范围如下(这种情况复杂并且使用范围广,应当避免出现)
三.隐式转换的规则 1.隐式转换不能有二义性 2.隐式操作不能嵌套使用
隐式转换细节
隐式转换
Scala面向对象高级
一.Scala集合基本介绍 1.Scala同时支持不可变集合和可变集合,不可变集合可以安全的并发访问 2.两个主要的包\t不可变集合:scala.collection.immutable\t可变集合:scala.collection.mutable 3.Scala默认采用不可变集合,对于几乎所有的集合类,Scala都同时提供了可变()和不可变(immutable)的版本, 4.Scala的集合有三大类:序列Seq,集Set,映射Map,所有集合都扩展自Iterable特质,在Scala中集合有可变(mutable)和不可变(immutable)两种类型
二.可变集合和不可变集合 1.不可变集合:Scala不可变集合,就时这个集合本身不能动态变化。类似于Java的数组,时不可以动态增长的 2.可变集合:就是这个集合本身可以动态变化,是可以动态增长的
三.集合的种类 1.数组Array 2.元组Tuple 3.列表list 4.队列Queue 5.映射Map 6.集Set
集合的基本介绍
第一种方式定义数组(动态创建) 这里的数组等同于Java中的数组,中括号的类型就是数组的类型,小括号中的数字代表数组的长度 var arr1 = new Array[int](10)
注意:如果赋值元素全部为Int,则该数组的泛型就是Int;如果有各种类型的元素,则该数组的泛型为Any
定长数组
一.变长数组的基本使用 val array = new ArrayBuffer[Int]() arrar.append(7) //追加元素 array.remove(0) //删除元素 ,索引位置 array(0) = 7 //重新赋值
二.变长数组分析小结 1.ArrayBuffer是变长数组,类似于Java的ArrayBuffer 2.val arr = ArrayBuffer[Int]() 3.def append(elems:A*) {append(elems)}接受的是可变参数 4.每append一次,arr在底层会重新分配空间,进行扩容,arr的内存地址会发生变化,也就成为新的arrayBuffer
变长数组
三.定长数组和变长数组的转换 arr1.toBuffer //定长数组转变长数组 arr2.toArray //变长数组转可变数组 ①arr2.toArray 返回的结果才是一个定长数组,arr2本身没有发生变化 ②arr1.toBuffer返回结果才是一个可变数组,arr1本身没有发生变化
定长数组与变长数组相互转换
二.多维数组的遍历 for (item <- arr){ for (item2 <- item){ print(item) } }
多维数组
数组(Array)
一.tuple介绍 元组可理解成一个容器,可以存放各种相同和不同的数据。简单来说,就是将多个无关的数据封装成一个整体,称为元组。注意:元组内最多只能有22个数据
三.小结 1.tuple的类型取决于tuple中有多少个元素,比如有 6个元素,则类型就是tuple6 2.元组中最大只能有22个元素
四.元组的访问 访问元组中的数据,可以采用顺序序号(_顺序号),也可以通过索引(productElement)访问 tuple1._1 //1代表顺序号 tuple1.productElement(0)\t//0代表索引
五.元组的遍历 1.元组的遍历需要用到迭代器 2.即 for(item <- tuple1.productIterator){ \tprint(item) }
元组(Tuple)
一.列表的基本介绍 Scala中list和Java List本不一样,Java中的List是一个接口,真正存放数据的是ArrayList,而Scala的list可以直接存放数据,就是一个object,默认情况下,Scala的list是不可变的,list属于序列Seq
二.说明 1.在默认情况下list是Scala.collection.immutable.list,即list是不可变的 2.在Scala中,list就是不可变的,如果需要使用可变的list,则使用listBuffer 3.Nil代表空集合
三.小结 1.list默认为不可变的集合 2.list在Scala包对象声明的,因此不需要引入其他包也可以使用 3.val List = scala.collection.immutable.List 4.list中可以存放任何数据类型,比如arr1的类型为list[Any] 5.如果希望得到一个空列表,可以使用Nil对象,在Scala包对象声明的,因此不需要引入其他包也可以使用 val Nil = scala.collection.immuteable.Nuil
七.列表的其他操作 list.head //返回列表的第一个元素 list.tail //返回列表除第一个元素外的元素 list.isEmpty //判断列表是否为空,空true,非空false list.last //返回列表最后一个元素 list.init //返回列表除最后一个元素外的元素
列表(List)
一.队列的基本介绍 1.队列是一个有序列表,再底层可以用数组或链表来实现 2.其输出和输入要遵循先入先出的原则。即:先存入队列的数据先取出,后存入的数据后取出 3.在Scala中,由设计者直接给我们提供队列类型使用 4.在Scala中,由scala.collection.mutable.Queue 和 scala.collect.immutable.Queue 我们使用最多的是可变的队列
二.队列的创建 val queue = new mutable.Queue[Int] //Int为队列的泛型
四.返回队列中的元素 1.返回队列第一个元素 queue.head 2.返回队列最后一个元素 queue.last 3.返回队列尾部元素,返回的是一个队列,所以可以级联使用该方法(除了第一个元素,其他元素都返回)\tqueue.tail\tqueue.tail.tail
队列(Queue)
一.回顾:Java中的Map的基本介绍 HashMap 是一个散列表(数组+链表),它存储的内容是键值对映射(key-value),key不能重复,Java中的Hash是无序的
二.Scala中的Map 1.Scala中的Map和Java类似,也是一个散列表,它存储的内容也是键值对(key-value)映射,Scala中不可变的Map是有序的,不可变的Map是无序的 2.Scala中,有可变Map:scala.collection.mutable.Map\t不可变Map:scala.collection.immutable.Map
三.构建不可变的Map //Map默认是不可变的val map1 = Map(\"zhangsan\
四.构建可变映射Map val map2 = mutable.Map(\"name\" -> \"zhangsan\
Map的创建
val map = mutable.Map(\"name\"<- \"张三\
五.更新map中的元素 val map2 = mutable.Map(\"name\"-> \"张三\
六.增加map中的元素 map2 += (\"D\" -> 2) //添加单个元素,如果key已经存在,则会更新 map2 += (\"C\
Map的操作
映射(Map)
一.集的基本介绍 集是不重复元素的集合。集不保留顺序,默认是以哈希集实现二.回顾:Java中Set Java中,Hash是实现Set<E>接口的一个实现类,数据是以哈希表的顺序存放的,里面不能包含重复数据。Set接口是一个不包含重复数据的collection,HashSet中的数据也是没有顺序的三.Scala中的Set说明 1.默认情况下,Scala使用的是不可变的集合,如果想使用可变的集合,需要引入scala.collection.mutable.Set包。 2.set(1) 可以判断元素 1 在集合中是否存在,存在返回true,不存在返回false
五.可变集合元素的添加 set.add(4) //只有可变集才能添加元素,如果添加的元素重复也不会报错,但是不会添加 set += 8
六.可变集合元素的删除 set.remove(4) //集Set中可以根据值来删除,因为Set中的元素不重复 set -= 8 //如果删除的元素不存在,则不会报错。
集(Set)
集合
map映射操作
flatmap映射:flat 即压扁flatmap:就是集合中每个元素的子元素映射到某个函数并返回新的集合 val names = ListBuffer(\"Python\
flatmap扁平化操作
filter:将符合要求的数据(筛选)放置到新的集合中应用案例: /* 将 val names = List(\"Alice\
filter过滤
reduce化简
fold折叠
scan扫描
groupBy分组
zip拉链
常用高阶函数
iterator迭代器
一.基本说明 stream是一个集合。它可以存放无穷多个元素,但是这无穷多个元素并不会一次性生产出来,而是需要多大的区间,就会动态的产生,末尾元素遵循lazy规则(即:需要是,才计算) //创建一个流 def numForm(n:BigInt):Stream[BigInt]={n #:: numForm(n+1)} val stream = numForm(1) println(stream) //取出第一个元素 println(stream.head) println(stream.tail)说明:1.stream集合中存放的数据类型是BigInt2.numForm是一个函数名,由程序员指定3.创建的集合的第一个元素是n,后续生成的规则是n+14.后续元素生成的规则可以自行指定5.使用tail方法就可以产生一个新的元素
stream流
一.基本介绍 stream的懒加载特性,也可以对其他集合应用view方法来得到类似的效果,具有如下特点: 1.view方法产生出一个总是被懒加载执行的集合 2.view不会缓存数据,每次都要重新计算,比如遍历view时。 val view = 1 to 100 val res = view.filter(eq) println(res) def eq(n:Int):Boolean={ n.toString.equals(n.toString.reverse) }
view视图
一.所有线程安全的集合都是以synchronized开头的集合 SynchronizedBuffer SynchronizedMap SynchronizedPriorityQueue SynchronizedQueue SynchronizeSet SynchronizeStack二.并行集合 1.Scala为了充分使用多核CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算 2.主要用到的算法有:Divied and conquer(分治算法),Scala通过splitters,combiners等抽象层来实现,主要原理是将计算工作分解成很多任务,分发给一些处理器去完成,并将它们处理的结果合并返回 3.Work stealin算法【学数学】,主要用于任务调度负载均衡(load-balancing),通俗点完成自己的所有任务之后,发现其他人还有活没干完,主动(或被安排)帮他人一起干,这样达到尽早干完的目的
三.使用并行模块 Scala 2.13之后,并行模块变成了外部库,和XML、Swing、parser-combinators等一样需要在maven项目的pom.xml中手动导入:<dependency> <groupId>org.scala-lang.modules</groupId> <artifactId>scala-parallel-collections_2.13</artifactId> <version>0.2.0</version> </dependency>然后在代码文件中导入如下的包就行了: import scala.collection.parallel.CollectionConverters._ 参考链接:https://stackoverflow.com/questions/57287607/answer/submit[code=html]四.基本案例 1.输出1-5 (1 to 5).foreach(println(_)) //单行操作 println() (1 to 5).par.foreach(println(_)) //并行操作 2.查看并行集合中元素访问的线程 val result1 = (0 to 100).map{case _ => Thread.currentThread.getName} val result2 = (0 to 100).par.map{case _ => Thread.currentThread.getName} println(result1) println(result2)
线程安全
一.操作符扩展 1.如果想在变量名,类名等定义中使用语法关键字(保留字),可以配合反引号`` val`val`=42 2.中置操作符:A操作符B (等同于) A.操作符(B)
操作符
集合基本操作
Scala数据结构
一.基本介绍 Scala中的模式匹配类似于Java中中的switch语法,但是更加强大 模式匹配语法中,采用match关键字声明,每个分支采用case关键字进行声明,当需要匹配时,会从第一个case分支开始,如果执行成功,就会执行相应的逻辑代码,如果不匹配,则继续执行下一个分支case。如果所有的case都不匹配,那么会执行case_分支,类似于Java中default
二.代码案例val oper = '#'val n1 = 20val n2 = 10var res = 0oper match {/*1.match类似于Java的switch,2.如果匹配成功,则会执行=> 后面的代码块3.匹配顺序是从上往下4.=>后面的代码块不需要写break,因为match会自动退出5.如果所有的case都不匹配,则会执行case_后的代码块6.如果所有的case都不匹配,并且没有case_,则会抛出异常MatchError4.可以再match中使用其他类型,不仅仅是Char*/case '+' => res = n1 + n2case '-' => res = n1 - n2case '*' => res = n1 * n2case '/' => res = n1 / n2case _ => println(\"oper error\")}println(\"res=\" + res)
模式匹配初体验
一.基本介绍 如果想要表达匹配某个范围的数据,就需要再模式匹配中增加条件守卫 for (ch <- \"+-3!\") { var sign = 0 var digit = 0 ch match { case '+' => sign = 1 case '-' => sign = -1 // 说明case _ if表示条件守卫,这里的下划线 _ 表示不接收ch case _ if ch.toString.equals(\"3\") => digit = 3 case _ => sign = 2 } println(ch + \" \" + sign + \" \" + digit) }
条件守卫
二.模式中的变量 如果在case关键字后跟变量名,那么match前表达式的值会赋给那个变量 val ch = 'V'ch match {case '+' => println(\"ok~\")case mychar => println(\"ok~\" + mychar)case _ => println (\"ok~~\") }
模式匹配中的变量
模式匹配扩展
匹配数组
匹配列表
匹配元组
一.基本介绍 1.case中对象的unapply方法(提取器)返回some集合则为匹配成功 2.返回none则为匹配失败
二.基本案例object Square{ //对象提取器,它会提取出创建对象时传入的参数 //简单来说,unapply就是逆向拆解对象 def unapply(z:Double): Option[Double] = { println(\"unapply方法被调用,z= \
三.小结 1.构建对象时,apply方法方法会被调用,比如 val n = Square(6) 2.当Square(n)写再case后面时,即【case Square(n) => xxx】,会默认调用unapply方法(对象提取器) 3.number会被传递给def unapply(z:Double) 的形参 z 4.如果返回unapply方法返回的时Some,则会将结果传递给Square(n)中的 n 5.case中对象的unapply方法(提取器)返回的时Some则匹配成功,为None则匹配失败
匹配对象
模式匹配扩展2
声明变量中的匹配
一.基本介绍 for循环中也可以进行模式匹配 val map = Map(\"A\
for循环中的模式匹配
模式匹配扩展3
一.基本介绍 1.样例类仍然是类 2.样例类用case关键字进行声明 3.样例类是为模式匹配而优化的类 4.构造器中的每一参数都成为了val,除非显示的声明var (不推荐) 5.在样例类对应的伴生对象中存在apply方法,可以是在实例化时不使用new关键字,就可以直接构造出对象 6.也提供unapply方法,让模式匹配可以正常的工作 7.将自动生成toString,equal,hashCode和copy方法(有点类似模板类,直接生成使用) 8.除了上述外,样例类和其他类完全一样,可以添加字段和方法
样例类
一.基本介绍 操作原理类似于正则表达式三.最佳实践案例-商品捆绑打折出售 现在有一些商品,请使用Scala设计相关的样例类,完成商品捆绑打折出售。要求商品捆绑可以是单个商品,也可以是多个商品。打折时按照折扣x元进行设计.能够统计出所有捆绑商品打折后的最终价格
匹配嵌套结构
一.基本介绍 1.如果想让case类的所有子类都必须在申明该类的相同源文件中定义,可以将样例类通过超类声明为sealed,这个超类就成为了密封类 2.密封类就是不能在其他文件中定义子类
密封类
Scala模式匹配(特色)
偏函数的引出
一.基本介绍 1.在对某个集合进行处理时,其中有些元素不符合处理条件,这时就可以使用偏函数 2.将函数中封装了一组case语句的函数,叫做偏函数。它只会对作用于指定类型的参数或者指定范围的参数实施计算,超出范围的值会另外处理 3.偏函数在Scala中是一个特质PartialFunction
偏函数介绍
偏函数的简化形式二 这种方式不能指定传入值的类型和返回值的类型,只能使用默认的 //偏函数的简化形式二 val res2 = list.collect{ case n:Int => n+1 case n:Double => n*2 } println(res2)
偏函数的化简形式
做为参数的函数
偏函数
一.基本介绍 没有名字的函数就是匿名函数,可以通过函数表达式来设置匿名函数二.代码演示 val res = (n:Int) => n+3 println(res(2))三.小结 1.不需要写def函数名 2.不需要写返回值类型,因为默认使用类型推导 3.如果代码块有多行,就使用{}
匿名函数的简写(参数类型推断)
匿名函数
一.基本介绍 可以传入函数的函数就作高阶函数,在某些情况下也可以返回一个函数
高级函数
一.基本介绍 闭包(closure)就是一个函数和与其相关的引用环境组合的一个整体(实体)。
三.代码小结 1.test函数返回的是 (m:Int) => n-m 匿名函数,因为该函数引用到了函数外的变量 n ,那么该函数和 n 整体形成了一个闭包。val f1 = test(2)这里的f1就是一个闭包 2.可以这样理解,返回函数是一个对象,而n就是该对象的一个字段,它们共同形成了一个闭包 3.当多次调用f1时(可以理解多次调用闭包),发现使用的是同一个n ,所以 n 不变 4.当使用闭包时,主要搞清楚返回函数引用了函数外的哪些变量,因为它们组合成了一个整体(实体),形成了一个闭包
四.添加后缀名的案例object clusureDemon { def main(args: Array[String]): Unit = { val f = makeSuffix(\".jpg\") println(f(\"scala\")) //scala.jpg println(f(\"python.jpg\")) //python.jpg } /* 传入一个文件名,如果这个文件名带有后缀名,那么直接返回; 如果这个文件名没有后缀名,则为它添加一个后缀名并返回 */ def makeSuffix(suffix:String) ={ (fileName:String) => { if (fileName.endsWith(suffix)) fileName else fileName+suffix } }}五.闭包的好处 1.返回的匿名函数和 test(n:Int) 的 n 变量组合成了一个闭包,因为返回的函数要引用 n 这个变量 2.可以反复引用上一次传入的值
闭包
一.基本介绍(curry) 1.函数编程中,接收多个参数的函数都可转换成接收一个参数的函数,这个转换过程就是函数柯里化 2.柯里化就是证明了函数只需要一个参数而已。在前面的学习中也用到了函数柯里化 3.柯里化是面向函数思想的必要产生结果
函数柯里化
一.基本介绍 控制抽象是这样的函数 1.参数是函数 2.函数参数没有输入值也没有返回值
二.代码演示 //像这样,参数是函数,函数没有输入值也没有返回值的函数,就是控制抽象 def myRun(f:()=>Unit): Unit ={ new Thread{ override def run(): Unit = { f() } }.start() } myRun { //这里就是调用myRun这个函数 () => //这里表示没有传入参数也没有返回值 println(\"开始干活!\") Thread.sleep(5000) println(\"干完了\") }三.案例 /* 利用控制抽象写出until函数,实现一下代码 var n = 10 while (n > 0){ n -= 1 println(n) } */ var x = 10 until(x > 0){ x -= 1 println(x) } //创建一个控制抽象函数,这里也用到了函数柯里化 def until(f1: => Boolean)(f2: => Unit): Unit = { if (f1){ f2 until(f1)(f2) }
控制抽象
Scala函数式编程高级
一.基本介绍 1.Akka是Java虚拟机JVM平台上构建高并发,分布式和容错应用的工具包和运行时。可以理解成Akka是编写并发程序的框架。 2.Akka用Scala语言编写,同时提供了Scala和Java的开发接口。 3.Akka主要解决的问题是:可以轻松的写出高效稳定的并发程序,程序员不再过多的考虑线程,锁和资源竞争等细节
二.Actor模型 1.处理并发问题关键是保证共享数据的一致性和正确性,因为程序是多线程的,多个线程对同一个数据修改,若不加同步条件,势必会造成数据污染。但是我们对关键代码加同步条件synchronized后,实际上开发就会堵塞在这段代码,对程序的效率有很大影响 2.若是用单线程处理,不会有数据一致性的问题,但是系统的性能又不能保证。 3.Actor模型的出现解决了这个问题,简化并发编程,提升程序性能。可以这样理解:Actor模型是一种处理并发问题的解决方案
Actor模型
三.Actor模型说明 1.Akka处理并发的方法基于Actor模型 2.在就基于Actor的系统里,所有事物都是Actor,就好像在面向对象设计里面所有的事物都是对象一样 3.Actor模型是作为一个并发编程设计和架构的。Actor与Actor之间只能通过消息通信 4.Actor与Actor之间只能用消息进行通信,当一个Actor给另一个Actor发消息,消息是有顺序的,只需要将消息投寄到相应的邮箱即可。 5.怎么处理消息是由接受消息的Actor决定的,发送消息的Actor可以等待回复,也可异步处理 6.ActorSystem的职责是负责创建并管理其创建的Actor,ActorSystem是单例的,一个jvm进程中有一个即可,而Actor可以有多个 7.Actor模型是对并发编程更高的抽象 8.Actor模型是异步的 非阻塞 高性能的事件驱动编程模型(最经典的案例就是Ajax异步请求) 9.Actor模型是轻量级事件处理(1GB内存可以容纳百万级别的Actor),因此处理大并发性能高
Actor模型说明
四.Actor模型工作机制 1.ActorSystem创建Actor 2.ActorRef:可以理解成是Actor的代理或者引用。消息是通过ActorRef发送,而不能通过Actor发送消息,通过哪个ActorRef发消息,就表示把消息发给哪个Actor 3.消息发送到Dispatcher Message(消息发送器),他得到消息后,会将消息分发到对应的mailBox。Dispatcher Message可以理解成一个线程池,MailBox可以理解成消息队列,可以缓冲多个消息,遵守FIFO 4.Actor可以通过receive方法来获取消息,然后进行处理
Actor工作机制
1.每个消息就是一个Message对象。Message继承Runable,因为Message就是线程类 2.从Actor模型工作机制看上去很麻烦,但是程序员编程时只需要编写Actor就可以了,其他工作交给Actor模型完成 3.A Actor要给B Actor发消息,先要拿到B Actor的代理对象ActorRef才能发消息
Actor间传递消息机制
Akka基本介绍
object actorToActorDemo{ //先创建一个ActorSystem,专门用于创建创建actor private val actorSystem = ActorSystem(\"actorSystem\") //括号里面的是ActorSystem的名字 //利用ActorSystem创建一个actor的引用,即实例化actorToActor类 \"A_actor\
Actor自我通讯
一.代码演示class AActor(actorRef: ActorRef) extends Actor { val bActorRef :ActorRef = actorRef var count = 0 override def receive: Receive = { case \"start\" => { println(\"游戏开始!!\") self ! \"我打!\" } case \"我打!\" => { println(\"我是A aaaaaaa\") count += 1 Thread.sleep(1000) bActorRef ! \"我打!\" //向B Actor发送 “我打” if (count == 10) { //当count等于10时,关闭AActor和BActor和ActorSystem context.stop(self) context.stop(sender()) context.system.terminate() } } }}class BActocr extends Actor { override def receive: Receive = { case \"我打!\" => { println(\"我是B bbbbbbbbbb\") Thread.sleep(1000) sender() ! \"我打!\" //向发件人发送 “我打” } }}object ActorGame extends App { //创建ActorSystem,名字叫做actorSystem val actorSystem = ActorSystem(\"actorSystem\
二.Actor的receive方法小结 1.每个Actor都有对应的mailbox 2.mailbox实现了Runnable接口,一直处于运行状态 3.当消息到达mailbox时,mailbox就会调用receive方法,将消息推送给对应的Actor
Actor之间通信
Akka支持面向大并发后端服务程序1.网络编程有两种 1.TCP socket编程,是网络编程的主流。之所以叫TCP socket编程,是因为底层是基于ICP/IP协议的。比如:QQ聊天 2.b/s结构的http编程,我们使用浏览器去访问服务器时,使用的就是http协议,而http底层依旧是用tcp socket实现的。比如:京东2.TCP/IP协议 TCP/IP协议,中文译名叫做传输控制协议/因特网互联协议,又叫网络通讯协议,这个协议时internet最基本的协议,Internet国际互联网的基础,简单的说,就是由网络层的ip协议和传输层的icp协议组成的3.端口使用注意 1.在计算机(尤其是服务器)要尽量的少开端口 2.一个端口只能被一个程序监听 3.如果使用netstat -an 可以查看本机有哪些端口在监听 4.可以使用netstat -anb 来查看监听端口pid,在结合任务管理器关闭不安全的端口
Akka网络编程基础
Akka网络编程
一.学习设计者模式的必要性 1.面试需要 2.读源码时可以用到,尤其是一些大型的框架使用设计模式 3.设计模式能让专业人之间交流方便 4.提高代码的易维护性 5.设计模式是编程经验的总结,简单理解:即通用的编程应用场景的模式化,套路化(站在软件设计层面思考)
学习设计模式必要性
二.设计模式的介绍 1.设计模式是程序员在面对同类软件工程设计问题上所总结出来的有用的经验,它不是代码,是一种思想2.设计模式的本质提高了软件的维护性,通用性和扩展性,并降低了软件的复杂度 3.设计模式不限于某种语言
设计模式介绍
三.设计模式的分类(23种) 1.创建型模式:单例模式,抽象工厂模式,建造者模式,工厂模式,原型模式 2.结构性模式:适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式 3.行为者模式:模板方法模式,命令模式,迭代器模式,观察者模式,中介者模式,备忘录模式,解释器模式,状态模式,策略模式,职责链模式,访问者模式
设计模式分类
泛型
Java上界 在Java泛型里表示某个类型是A类型的子类型,使用extend关键字,这种形式叫upper bounds(上线或上界)语法如下:\t<T extend A>\t//使用通配符的形式\t<? extends A>Java下界在Java中泛型里表示某个类型是A类型的父类型,使用super关键字<T super A>//使用通配符<? super A>
Java下界 下界
Scala上界 在Scala里表示某个类型是A类型的子类型,也称上限或者上界。使用<:关键字。语法如下\t[T <: A]\t//使用通配符\t[_ <: A]Scala下界 在Scala中下界,使用 >: 关键字\t[T >: A]\t//表示T的下界是A\t//使用通配符\t[_ >: A]
Scala下界使用小结 ①对于下界,可以传入任意类型 ②传入和A直系的,是A父类就按照A父类处理,如果是A的子类,则按照A处理 ③如果传入的对象与A无关,一律按照Object处理 ④也就是说下界可以随便传,只是处理方式不一样 ⑤不能使用上界的思路来类推下界的含义
类型约束 上界和下界
一.基本介绍 1.Scala的协变(+),逆变(-),协变covariant、逆变contravariant、不可变invariant 2.对于一个带类型参数的类型,比如 List[T],如果对A及其子类型B,满足 List[B]也符合List[A]的子类型,那么就称为covariance(协变) ,如果 List[A]是 List[B]的子类型,即与原来的父子关系正相反,则称为contravariance(逆变)。如果一个类型支持协变或逆变,则称这个类型为variance(翻译为可变的或变型),否则称为invariance(不可变的) 3在Java里,泛型类型都是invariant,比如 List<String> 并不是 List<Object> 的子类型。而scala支持,可以在定义类型时声明(用加号表示为协变,减号表示逆变),如: trait List[+T] // 在类型定义时声明为协变这样会把List[String]作为List[Any]的子类型。
二.应用实例 在这里引入关于这个符号的说明,在声明Scala的泛型类型时,“+”表示协变,而“-”表示逆变 C[+T]:如果A是B的子类,那么C[A]是C[B]的子类,称为协变 C[-T]:如果A是B的子类,那么C[B]是C[A]的子类,称为逆变 C[T]:无论A和B是什么关系,C[A]和C[B]没有从属关系。称为不变.val t: Temp[Super] = new Temp[Sub](\"hello world1\")class Temp3[A](title: String) { //Temp3[+A] //Temp[-A] override def toString: String = { title }}//支持协变class Superclass Sub extends Super
逆变不变协变
泛型,上下界,逆变不变协变
Scala学习
0 条评论
回复 删除
下一页