scala完整笔记-5万字一周入门到精通
写在开篇
1.scala学习前一定要具备了解一些java基本知识,无需精通;如果从未接触java,最好熟悉一门编程语言,否则相对还是学习起来相对吃力且很难学懂
2.本篇主要以代码示例为主,很多概念理论并没有展开很多篇幅,毕竟不是研究学术亦或是大厂面试专攻,代码为主的本质还是因为知道有什么功能,特点是什么,知道scala有这个东西,有了印象后便于以后工作中用到时回来ctrl+f搜索一下关键词,可以很快看一下代码示例,再展开使用
3.非常多的代码并不是都写在一个文件中,所以在一处写了如何创建包创建对象文件的方法等,就不会反复写重复的创建包创建类文件等内容
4.如果scala和java有很大区别的地方会对比代码,凸显不同之处
5.scala许多代码块可以简写、代码逐步精简,对于初步接触的情况,不建议花大量时间去研究如何把代码精简一步到位,至少先保证代码逻辑能敲出来,理解为先,再随着熟练度提升慢慢提高精简度
第一章 scala入门
第一节 scala概述
1.1.1 为什么学习scala
作为程序员一定对python不陌生,python无疑成为了最为热门的语言,如今盘踞编程语言前三,但这背后原因则是因为人工智能、机器学习、区块链等超级大概念持续性的高热度和不断增长的关注度所带动;
而scala的发展趋势比较雷同python,只是领域不同,大数据现在也是一个有着非常优秀的发展趋势,而大数据当前最火的框架要数spark和kafka,而这2个框架又都是scala编写的,也就是想在大数据走的远,深挖进去就得学习scala。
1.1.2 scala发展历史与作者
Scala语言是一种能够运行于JVM和.Net平台之上的通用编程语言,既可用于大规模应用程序开发,也可用于脚本编程,它由Martin Odersk于2001开发,2004年开始程序运行在JVM与.Net平台之上,由于其简洁、优雅、类型安全的编程模式而受到关注。 (JVM - Java Virtual Machine [java虚拟机])
在Scala的创建之初,并没有怎么引起重视,随着Apache Spark和Apache Kafka这样基于Scala的大数据框架的崛起,Scala逐步映入大数据从业者的眼帘。Scala的拥护者们认为Scala的主要优势是速度和它的表达性。目前使用scala的作为支撑公司开发语言的包括Foursquare和Twitter。2009年Twitter把大部分后台系统的开发语言从Ruby换成了Scala
Scala的创建者——Martin Odersk (马丁·奥德斯基)
马丁·奥德斯基是编译器及编程的狂热爱好者,他希望发明一种语言,能够使编程变得更加高效简洁。当他接触到 Java 语言后,对 Java 语言产生了极大的兴趣,所以他决定将函数式编程融入到 Java 中,由此发明了两种语言(Pizza & Scala)。Pizza 和 Scala 极大地推动了 Java 语言的发展。
jdk 5.0 的泛型,for 循环的增强,自动类型转换等,都是从 Pizza 引入的新特性。
jdk 8.0 的类型推断,Lambda 表达式就是从 Scala 引入的特性。
现在主流的 JVM 的 javac 编辑器就是马丁·奥德斯基编写的。jdk 5.0 和 jdk 8.0 的编译器就是他编写的。可以说是一个人就能做到整个团队的工作量。
1.1.3 scala和java关系
scala与java可以无缝链接,在互联网领域java无疑是最具影响力的语言之一,良好的社区,规范优美的代码规则,都使得java成为近年来互联网应用最为广泛的语言,而scala与java几乎可以称作无缝链接,通过简单的import就可以调用java中各式各样的类和方法,同时,scala完成的算法包也是以jar包的形式出现,同样的语言实现都是经过JVM来实现。
1.1.4 scala语言特点
● scala 是一门以 jvm 为运行环境并将面向对象和函数式编程的最佳特性结合在一起的静态类型编程语言。
● scala 是一门多范式(multi-paradigm)的编程语言,scala 支持面向对象和函数编程。
● scala 源代码会被编译为 .class 文件,然后运行于 jvm 上,并可以调用 java 类库,实现两种语言的无缝对接。
● scala 非常简洁,如三元运算、++、-- 等。
第二节 scala开发环境搭建
1.2.1 scala环境搭建前期准备
因为很多学习scala的学者几乎多多少少接触过java的,或者是准备步入大数据行业相关的从业人员,如果从未接触过java语言并且也未打算从事大数据行业;讲道理学习scala会相对吃力,不知道JAVA也不参与大数据那以后就不会频繁用到,意义就小很多。
搭建scala开发环境必须电脑上已经具备了JAVA环境,安装了idea等开发软件[软件都可以,建议用ide],安装JAVA环境和开发软件自行准备。
JAVA1.8 环境
# 我这里用的是win10 1.8.0_221版本
Microsoft Windows [版本 10.0.19041.928]
(c) Microsoft Corporation。保留所有权利。
C:\Users\33450>java -version
java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)
idea编辑器 (自行下载安装包并破解)
编辑器 我用的是IntelliJ IDEA 2020.1 x64
1.2.2 scala环境安装步骤
(1)开始前首先确保JDK1.8安装成功且环境变量没问题
(2)下载对应的Scala安装文件scala-2.11.8.zip
scala官方网站:https://www.scala-lang.org/
找到download - 大版本分scala3 和 scala2 - 点下载scala2
其中 页面有一行 :Or are you looking for previous releases of Scala?
点击previous releases可以找到过往所有版本下载,然后点击对应版本之后进入到下载页面,一般开发都在windows或者Mac环境,下载对应版本即可(我这里使用的是 windows scala-2.11.8.zip)。
(3)解压scala-2.11.8.zip
解压即用,解压后的文件夹可以剪切到自己习惯存放程序的目录,D:\scala-2.11.8 (我直接放置在D盘目录下,解压路径中不能有任何中文名路径,最好也不要有空格,毕竟编辑器最后都要配置scala环境,避免出现不可预估的疑难杂症 [ 例如: D:\新建文件夹\scala-2.11.8] 这样带中文的路径不要用 )
(4)配置Scala的环境变量
WIN 10 是右键我的电脑,点击高级系统设置 - 在系统属性里点高级分页 - 下面有一个环境变量 - 在环境变量中,分当前用户配置和系统变量配置,直接在系统变量中新建SCALA配置(环境变量要大写SCALA_HOME)
变量名(N): SCALA_HOME
变量值(V): D:\scala-2.11.8
添加完scala变量配置后,还需要修改PATH变量
建议把JAVA的PATH位置上移到第一个,这里新增 D:\scala-2.11.8\bin 上面是新增SCALA_HOME是程序目录,这里加的是把程序目录中bin目录下的执行文件配置到系统执行文件路径中去。
1.2.3 测试初试scala
如果以上操作都顺利完成,可以进入到验证阶段,打开cmd命令行;
Microsoft Windows [版本 10.0.19041.928]
(c) Microsoft Corporation。保留所有权利。
E:\tmp>java -version
java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)
E:\tmp>scala -version
Scala code runner version 2.11.8 -- Copyright 2002-2016, LAMP/EPFL
E:\tmp>scala
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_221).
Type in expressions for evaluation. Or try :help.
scala>
能进入到scala命令行,说明环境已经没有问题。
根据java之前的经验,来个小实验
scala>
scala> var w = 10
w: Int = 10
scala> var t = 20
t: Int = 20
scala> var wt = w + t
wt: Int = 30
scala> :quit
# 退出命令行前面需要加 ":"
E:\tmp>
现在一切准备就绪,在准备idea开发环境之前,需要了解2件事情
第一件:简单的java和scala运行流程,有必要知道这个过程
java运行原理
- .java源文件 --> 编译器( javac ) --> .class字节码文件 --> JVM ( java 不同平台 ) --> 机器指令
Scala运行原理
- .scala源文件 --> 编译器( scalac ) --> .class字节码文件 --> JVM ( scala 不同平台 ) --> 机器指令
对比一下流程基本一样,局部不同
第二件:scala能否运行java代码、java能否运行scala代码? (通过实验来论证)
新建2个空文件: JavaHelloworld.java 、ScalaHelloWorld.scala
在 JavaHelloworld.java 文件中编辑内容并保存退出
public class JavaHelloworld{
public static void main(String[] args){
System.out.println("Helloworld from java");
}
}
在ScalaHelloWorld.scala 文件中编辑内容并保存退出 [ 不用在意语法,这里只是验证结果 ]
object ScalaHelloWorld {
def main(args: Array[String]): Unit = {
println("HelloWorld from scala")
}
}
在当前目录下运行cmd命令行
Microsoft Windows [版本 10.0.19041.928]
(c) Microsoft Corporation。保留所有权利。
E:\tmp>javac JavaHelloworld.java
E:\tmp>scalac ScalaHelloWorld.scala
可以看到
编译器( javac ) 编译JavaHelloworld.java 文件 -> JavaHelloworld.class字节码文件
编译器( scalac )编译ScalaHelloWorld.scala 文件 - > 默认是ScalaHelloWorld.class(伴生类) 、ScalaHelloWorld$.class(伴生对象所属类) 2个字节码文件
结论验证:
# java运行JavaHelloworld 成功输出 (注意后面不用跟文件后缀)
E:\tmp>java JavaHelloworld
Helloworld from java
# scala运行ScalaHelloWorld 成功输出
E:\tmp>scala ScalaHelloWorld
HelloWorld from scala
# 采用scala来运行JavaHelloworld 成功输出 说明scala可以运行一些java编译字节码文件
E:\tmp>scala JavaHelloworld
Helloworld from java
# 采用java运行ScalaHelloWorld 报错 说明java不能运行scala编译字节码文件
E:\tmp>java ScalaHelloWorld
Exception in thread "main" java.lang.NoClassDefFoundError: scala/Predef$
at ScalaHelloWorld$.main(ScalaHelloWorld.scala:4)
at ScalaHelloWorld.main(ScalaHelloWorld.scala)
Caused by: java.lang.ClassNotFoundException: scala.Predef$
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 2 more
E:\tmp>
第三节 idea中scala插件安装
首先确定ide已经安装并且可以正常使用
默认情况下IDEA不支持Scala的开发,需要安装Scala插件。在File - Settings - Plugins 查询Scala 点击Install安装即可
安装完Scala插件后,根据提示重启idea程序应用
第四节 scala第一个demo示例
1.4.1 新建一个Maven项目工程
内容可以自定义,一般不影响实验,但还是注意工程存储路径以及GroupId等不要有中文和空格
1.4.2 工程项目配置Scala框架
在Add Frameworks Support 窗口中,选择Scala,并且在右侧Use library中配置scala目录,第一次引入框架,Use libary看不到,需要选择你的Scala安装目录,然后工具就会自动识别,就会显示user libary
1.4.3 运行一个hello案例
1.4.3-1 创建项目的源文件目录
右键点击main目录 -> New->点击Diretory -> 新建代码目录 -> 命名 scala
右键点击scala目录 -> Mark Directory as选项 -> 选择Sources root,观察文件夹颜色发生变化。
当scala目录和java目录颜色相同时,配置成功
1.4.3-2 在scala包下,创建包com.zuoli.chapter01包名和Hello类名
右键点击scala目录 -> New -> Package -> 输入 com.zuoli.chapter01 -> 点击OK。
右键点击com.zuoli.chapter01 -> New -> Scala Class -> Kind项选择 Object -> Name项输入 Hello。
1.4.3-3 编写输出Hello Scala案例
在类中中输入main,然后回车可以快速生成main方法;
在main方法中输入
// scala
println(“hello ided from scala code”)
// java
System.out.println(“hello idea from java code”)
从上面执行结果来看,System.out.println(“hello idea from java code”) 再次验证Java中部分代码也是可以在Scala中直接运行。
1.4.4 class和object说明
# java
public class JavaHelloworld{
public static void main(String[] args){
System.out.println("Helloworld from java");
}
}
# scala
object ScalaHelloWorld {
def main(args: Array[String]): Unit = {
println("HelloWorld from scala")
}
}
public修饰符 //scala中没有public关键词,如不申明访问权限,就代表为公共的
static修饰符 //scala中没有静态语法,所以没有static关键词
void关键词 //表示返回值,但是不 遵循面向对象语法,所以scala中 废弃了,但是scala引入了Unit类型,表示没有返回值
object关键词 //从语法角度讲,语法表示声明了一个伴生对象,Scala是纯面向对象的,去除了java中的static关键字,通过伴生对象模拟static效果
ScalaHelloWorld //伴生对象的名称
def关键词 //标识声明一个方法
main //方法的名称
args //参数名称
Array[String] //参数类型,在Scala语言中,[]表示泛型 ;声明参数的时候,args名称在前,参数类型在后,名称和类型之间用冒号分隔
Unit //返回值类型为空,相当于java语言中的void关键字;Unit是一个类型,当前类型只有一个实例()
1.4.5 java static的作用回顾
1、修饰成员变量
给变量加上static关键字后,此变量变为全局变量,JVM在加载时会直接将此变量加载到方法区里而不是堆里,无论哪一个方法修改了此变量,此变量就会改变,可以让对象共享属性。并且,当再次new该类的对象时,static修饰的类成员不会被再次初始化,在程序运行过程中,static只会在第一次new时被初始化,当然final另当别论
2、修饰成员方法
static修饰成员方法最大的作用,就是可以使用"类名.方法名"的方式操作方法,避免了先要new出对象的繁琐和资源消耗
3、静态代码块
当new一个类对象时,static修饰的成员变量首先被初始化,随后是普通成员,最后调用Person类的构造方法完成初始化。也就是说,在创建对象时,static修饰的成员会首先被初始化。并且,当再次new该类的对象时,static修饰的类成员不会被再次初始化,在程序运行过程中,static只会在第一次new时被初始化
4、静态导包
静态导包用法,将类的方法直接导入到当前类中,从而直接使用“方法名”即可调用类方法,更加方便
1.4.6 伴生对象
scala里面没有 static 关键字。那么如果想实现static的效果要怎么做呢?那就可以使用伴生对象来实现。
在同一个scala文件中定义一个类,同时定义一个同名的object,那么它们就是伴生类和伴生对象的关系,可以互相直接访问私有的field。
● 伴随类产生的一个对象
● 当我们对源文件进行编译之后,默认会生成两个字节码文件,一个是伴生类,另一个是伴生对象所属类(带有$符)
其实真正的伴生对象是 伴生对象所属类中创建的单例的对象
● 如果不想默认生成伴生类,可以手动生成,要求伴生类名称和伴生对象名称一致
实例代码(尝试理解)
package com.zuoli.chapter01
// object创建一个伴生对象
object Hello {
def main(args: Array[String]): Unit = {
// scala
println("hello ided from scala code")
// java
System.out.println("hello idea from java code")
// 测试伴生对象
println(new Student().name)
println(Student.bzr)
}
}
//定义类
class Student {
var name: String = _
var age: Int = _
//static bzr:String
}
//定义同名伴生对象
object Student {
var bzr: String = "wangt"
}
/*
代码运行结果
hello ided from scala code
hello idea from java code
null
wangt
Process finished with exit code 0
*/
第五节 scala查看关联源码
下载官网上scala-sources-2.11.8.tar.gz源码包,解压出来的目录放在D:\scala-2.11.8\lib 目录下,注意解压后是目录,并非是一个jar包
放置到对应lib目录后,CTRL + 鼠标点击Array ,可以跳转至源码,如果没有跳转,看一下代码窗口右上角是否有提示,Attach Sources…的提示,点一下关联即可
第二章 变量和数据类型
第一节 注释
Scala注释使用和Java完全一样
2.1.1 单行注释
在内容前加 //
// DO NOT EDIT, CHANGES WILL BE LOST
// This auto-generated code can be modified in scala.tools.cmd.gen.
// Afterwards, running tools/codegen-anyvals regenerates this source file.
2.1.2 多行注释
包含在 /* 和 */ 之间
/*
* 多行注释的内容不能用于生成一个开发者文档
* 而文档注释的内容可以生产一个开发者文档
*/
2.1.3 文档注释
包含在 /** 和 */ 之间
/** `Unit` is a subtype of [[scala.AnyVal]]. There is only one value of type
* `Unit`, `()`, and it is not represented by any object in the underlying
* runtime system. A method with return type `Unit` is analogous to a Java
* method which is declared `void`.
*/
文档注释可以通过 scaladoc 命令把文档注释中的内容生成文档,并输出到 HTML 文件中,方便记录程序信息
2.1.4 idea编辑中代码规范
(1)使用一次tab操作,实现缩进,默认整体向右边移动,用shift+tab整体向左移动
(2)使用CTRL+ ALT + L快捷键用来进行代码格式化,常规的一些格式自动优化
(3)运算符两边习惯性各加一个空格。例如:2+4*5 -> 2 + 4 * 5
(4)一行最长不超过80个字符,超过的请使用换行符""展示,尽量保持格式优雅
第二节 变量和常量
2.2.1 java变量回顾
常量:在程序执行的过程中,其值不会被改变的变量
变量类型 变量名称 = 初始值
final常量类型 常量名称 = 初始值
package com.zuoli.test1;
public class test {
public static void main(String[] args) {
int a = 10;
// final修饰的变量不可改变 关键词 表示b不能被修改,定义成b = 20, 不能再赋新的值
final int b = 20;
System.out.println(a);
System.out.println(b);
System.out.println("===============");
a = 100;
//b = 200; 如果这里定义b = 200 会报错
System.out.println(a);
System.out.println(b);
}
}
// 输出内容
/*
10
20
===============
100
20
*/
2.2.2 scala变量基本语法
var 变量名 [: 变量类型] = 初始值
val 常量名 [: 常量类型] = 初始值
【注意】:
(1)能用常量的地方不用变量
(2)声明变量时,类型可以省略,编译器自动推导,即类型推导
(3)类型确定后,就不能修改,说明Scala是强数据类型语言。
(4)变量声明时,必须要有初始值
(5)在声明/定义一个变量时,可以使用var或者val来修饰,var修饰的变量可改变,val修饰的变量不可改。
package com.zuoli.chapter02
object Test_Var {
def main(args: Array[String]): Unit = {
// 声明变量时,类型可以省略,编译器自动推导,即类型推导
var w:Int = 100
var t = 100
println(w)
println(t)
println("==========")
// Scala是强数据类型语言,类型确定后,就不能修改
// w = "wang" # 这样会报错,因为已经申明了w是Int类型,不能再赋值String
// 变量声明时,必须要有初始值
// var wt # 这样会报错,没有初始值
// 在声明一个变量时,可以使用var或者val来修饰,var修饰的变量可改变,val修饰的变量不可改变
var wang = 50
// val修饰的变量不可改变
val ting = 50
println(wang)
println(ting)
wang = 500
println(wang)
// ting = 500 # 如果这里定义ting = 500 会报错
println("==========")
// var修饰的对象引用可以改变
// val修饰的对象则不可改变
// 但对象的状态(值)却是可以改变的。(比如:自定义对象、数组、集合等等)
// p1是var修饰的,p1的属性可以变,而且p1本身也可以变
var p1 = new Person()
p1.name = "wangt_p1"
println(p1.name)
println(p1)
p1 = null
println(p1)
// p2是val修饰的,则p2本身就不可变(即p2的内存地址不能变)
// 但p2的属性是可以变,因为属性并没有用val修饰。
val p2 = new Person()
p2.name = "wangt_p2"
println(p2.name)
// p2 = null # 这样会报错,因为p2是val修饰的
println(p2)
}
}
class Person {
var name: String = "wangt"
}
// 输出内容
/*
100
100
==========
50
50
500
==========
wangt_p1
com.zuoli.chapter02.Person@1888ff2c
null
wangt_p2
com.zuoli.chapter02.Person@35851384
Process finished with exit code 0
*/
第三节 标识符的命名规范
Scala对各种变量、方法、函数等命名时使用的字符序列称为标识符。即:凡是自己可以起名字的地方都叫标识符。
命名规则
Scala中的标识符声明,基本和Java是一致的,但是细节上会有所变化
(1)以字母或者下划线开头,后接字母、数字、下划线
(2)以操作符开头,且只包含操作符(+ - * / # !等)
(3)用反引号``包括的任意字符串,即使是Scala关键字(39个)也可以
•package, import, class, object, trait, extends, with, type, for
•private, protected, abstract, sealed, final, implicit, lazy, override
•try, catch, finally, throw
•if, else, match, case, do, while, for, return, yield
•def, val, var
•this, super
•new
•true, false, null
package com.zuoli.chapter02
object TestName {
def main(args: Array[String]): Unit = {
/**以字母或者下划线开头,后接字母、数字、下划线*/
var wt: String = "wt" //正确
println(wt)
var wt1234: String = "wt1234" //正确
println(wt1234)
//var 1234wt:String = "wt" //错误,因为不能数字开头
//var w-t:String = "wt" //错误,不能用-,-代表减号,scala中代表函数
//var w t:String = "wt" //错误,不能有空格
var w_t: String = "wt" //正确
println(w_t)
var _wt: String = "wt" //正确
println(_wt)
var Int:String = "wt_Int" //正确,一般不推荐(因为在Scala中Int是预定义的字符,不是关键字)
println(Int)
//var _:String = "wt" //错误,因为下划线单独使用不可以作为标识符,因为_被认为是一个方法
/** 以操作符开头,且只包含操作符(+ - * / # !等)*/
var +-*/!# :String = "wt_+-*/!#" //正确,一般不推荐使用
println(+-*/!#)
//var +-*/!#1234:String = "wt" //错误,如果操作符开头,那后面必须都是操作符才可以
/** 用反撇号`**`包括的任意字符串,即使是Scala关键字(39个)也可以 */
//var if:String = "wt" //错误,不能直接用关键词
var `if`:String = "wt_`if`" //正确,使用反撇号把关键词包起来可以,一般不推荐使用
println(`if`)
}
}
//输出内容
/*
wt
wt1234
wt
wt
wt_Int
wt_+-*/!#
wt_`if`
*/
第四节 字符串输出
基本语法
(1)字符串,通过+号连接
(2)printf用法:字符串,通过%传值。
(3)字符串模板(插值字符串):通过${}获取变量值 ;
注意:要使得${} 变量引用生效时,开头需要加“s”;否则会作为普通字符串输出
package com.zuoli.chapter02
object TestCharType {
def main(args: Array[String]): Unit = {
var name: String = "wangt"
var age: Int = 30
//字符串,通过+号连接
println(name + "_666_" + age)
println("===============")
//printf用法字符串,通过%传值
printf("姓名=%s ; 年龄=%d", name, age)
println()
println("===============")
//多行字符串,在Scala中三个双引号包围多行字符串
//应用scala的stripMargin方法,在scala中stripMargin默认是“|”作为连接符
//在多行换行的行头前面加一个“|”符号即可,IDEA回车后会自动加
var jay =
"""
|久未放晴的天空
|依旧留着你的笑容
|哭过却无法掩埋歉疚
|风筝在阴天搁浅
|想念还在等待救援
|我拉着线复习你给的温柔
|""".stripMargin
println(jay)
println("===============")
//如果需要对变量进行运算,那么可以加${}实现
var wt =
s"""
|歌名:搁浅
|演唱: 周杰伦
|作词: 宋健彰 作曲: 周杰伦
|钢琴: 周杰伦 编曲: 钟兴民
|周杰伦年龄是: ${age + 10}
|""".stripMargin
println(wt)
println("===============")
val wt2 = s"name=${name}"
println(wt2)
}
}
//输出内容
/*
wangt_666_30
===============
姓名=wangt ; 年龄=30
===============
久未放晴的天空
依旧留着你的笑容
哭过却无法掩埋歉疚
风筝在阴天搁浅
想念还在等待救援
我拉着线复习你给的温柔
===============
歌名:搁浅
演唱: 周杰伦
作词: 宋健彰 作曲: 周杰伦
钢琴: 周杰伦 编曲: 钟兴民
周杰伦年龄是: 40
===============
name=wangt
*/
第五节 键盘输入
在编程中,需要接收用户输入的数据,就可以使用键盘输入语句来获取
基本语法:
StdIn.readLine()、StdIn.readShort()、StdIn.readDouble() 等等…
package com.zuoli.chapter02
import scala.io.StdIn
object TestInput {
def main(args: Array[String]): Unit = {
println("请输入角色名: ")
var name = StdIn.readLine()
println("请输入角色等级: ")
var level = StdIn.readShort()
println("请输入需要充值的金币数量: ")
var gold = StdIn.readDouble()
println("===== 本次充值信息 =====")
println("游戏角色: " + name)
println("当前角色等级: " + level)
println("充值金币数: " + gold)
}
}
// 输出内容
/*
请输入角色名:
飘飘
请输入角色等级:
100
请输入需要充值的金币数量:
600000
===== 本次充值信息 =====
游戏角色: 飘飘
当前角色等级: 100
充值金币数: 600000.0
*/
第六节 数据类型
2.6.1 java数据类型
基本类型名 | 关键字 | 字节大小 | 位数 | 描述 |
---|---|---|---|---|
整数类型 | byte | 1字节 | 8位 | 最大存储数据量是255,存放的数据范围是-128~127之间。 |
整数类型 | short | 2字节 | 16位 | 最大数据存储量是65536,数据范围是-32768~32767之间。 |
整数类型 | int | 4字节 | 32位 | 最大数据存储容量是2的32次方减1,数据范围是负的2的31次方到正的2的31次方减1。 |
整数类型 | long | 8字节 | 64位 | 最大数据存储容量是2的64次方减1,数据范围为负的2的63次方到正的2的63次方减1。 |
浮点类型 | float | 4字节 | 32位 | 数据范围在3.4e-45~1.4e38,直接赋值时必须在数字后加上f或F。 |
浮点类型 | double | 8字节 | 64位 | 数据范围在4.9e-324~1.8e308,赋值时可以加d或D也可以不加。 |
字符型 | char | 2字节 | 16位 | 存储Unicode码,用单引号赋值。 |
布尔型 | boolean | 只有true和false两个取值 |
包装类:基本类型的包装类主要提供了更多的实用操作,这样更容易处理基本类型。所有的包装类都是 final 的,所以不能创建其子类,包装类都是不可变对象
基本类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
char | Character |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
2.6.2 scala数据类型
Scala与Java有着相同的数据类型,Scala数据类型都是对象,Scala中没有类似Java中那样的原始类型
Scala基本数据类型有: Byte,Short,Int,Long 和 Char
整数类型加上 Float 和 Double 成为数值类型
此外还有 String 类型,除 String 类型在 java.lang 包中定义,其它的类型都定义在包 scala 中。
比如 Int 的全名为 scala.Int。实际上 Scala 运行环境自动会载入包 scala 和 java.lang 中定义的数据类型
- 在 scala 中有一个根类型 Any ,他是所有类的父类.
- scala中一切皆为对象,分为两大类AnyVal(值类型),AnyRef(引用类型),他们都是Any子类,Scala所有引用类的基类
- Unit:表示无值,对应java中的void,用于方法返回值的位置,表示方法没有返回值,Unit是一个数据类型,只有一个对象就是“()”;void不是数据类型,只是一个关键字
- Null:null或空引用,NULL是一个类型,只有一个对象就是null,它是所有引用类型(AnyRef)的子类
- Nothing:是Scala的类层级的最底端,是任何其他类型的子类型,主要用在一个函数没有明确返回值时使用,因为这样我们可以把抛出的返回值,返回给任何的变量或者函数
第七节 整数类型
Byte、Short、Int、Long
数据类型 | 描述 |
---|---|
Byte [1] | 8位有符号补码整数。数值区间为 -128 到 127 |
Short [2] | 16位有符号补码整数。数值区间为 -32768 到 32767 |
Int [4] | 32位有符号补码整数。数值区间为 -2147483648 到 2147483647 |
Long [8] | 64位有符号补码整数。数值区间为 -9223372036854775808 到 9223372036854775807 = 2的(64-1)次方-1 |
package com.zuoli.chapter02
object TestDataType {
def main(args: Array[String]): Unit = {
// 整数类型 - Byte
var n1: Byte = -128
var n2: Byte = 127
println(n1, n2)
//var n3: Byte = 128 # 错误,超过127不能用Byte类型
//var n4: Byte = -129 # 错误,小于-128,不能用Byte类型
var n5: Int = 6666
//Scala的整型,默认为Int型,声明Long型,须后加‘l’或‘L’
var n6 = 7777
println(n5, n6)
var n7 = 2147483647777L
var n8 = 2147483648888l
println(n7,n8)
//Scala程序中变量常声明为Int型,除非不足以表示大数,才使用Long
}
}
//输出内容
/*
(-128,127)
(6666,7777)
(2147483647777,2147483648888)
*/
第八节 浮点类型
Float、Double
数据类型 | 描述 |
---|---|
Float [4] | 32 位, IEEE 754标准的单精度浮点数 |
Double [8] | 64 位 IEEE 754标准的双精度浮点数 |
package com.zuoli.chapter02
object TestDataType {
def main(args: Array[String]): Unit = {
//需要高精度小数时,请选择Double
var n9 = 2.22222222222333f
var n10 = 2.22222222222333
//注意对比输出区别
println(n9)
println(n10)
}
}
//输出内容
/*
2.2222223
2.22222222222333
*/
第九节 字符类型
字符类型可以表示单个字符,字符类型是Char
package com.zuoli.chapter02
object TestCharType_2 {
def main(args: Array[String]): Unit = {
//字符常量是用单引号 ' ' 括起来的单个字符
var c1: Char = 'w'
println(c1)
//这个c2这样的格式在IDEA中会提示格式语法不对,可以忽略;能正常执行
var c2: Char = 'w' - 3
println("w - 3 = ",c2)
println("==================")
// \t :一个制表位,实现对齐的功能
println("牛逼plus\t牛逼pro")
println("==================")
//\n :换行符
println("第一行\n第二行")
println("==================")
// \符号需要转义,转义符也是\
println("想打印一个反斜杠\\")
}
}
// 数据输出
/*
w
(w - 3 = ,t)
==================
牛逼plus 牛逼pro
==================
第一行
第二行
==================
想打印一个反斜杠\
*/
第十节 布尔类型
(1)布尔类型也叫Boolean类型,Booolean类型数据只允许取值true和false
(2)boolean类型占1个字节。
package com.zuoli.chapter02
object TestBooleanType {
def main(args: Array[String]): Unit = {
var b1: Boolean = false
var b2: Boolean = true
var b3 = false
var b4 = true
println(b1, b2, b3, b4)
println(b1.getClass.getSimpleName)
println(b2.getClass.getSimpleName)
println(b3.getClass.getSimpleName)
println(b4.getClass.getSimpleName)
}
}
//输出内容
/*
(false,true,false,true)
boolean
boolean
boolean
boolean
*/
第十一节 Unit类型、Null类型、Nothing类型
数据类型 | 描述 |
---|---|
Unit | 表示无值,和java语言中void等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。 |
Null | null , Null 类型只有一个实例值null |
Nothing | Nothing类型在Scala的类层级最低端;它是任何其他类型的子类型。当一个函数,我们确定没有正常的返回值,可以用Nothing来指定返回类型,这样有一个好处,就是我们可以把返回的值(异常)赋给其它的函数或者变量(兼容性) |
2.11.1 Unit
package com.zuoli.chapter02
object TestSpecialType {
def main(args: Array[String]): Unit = {
// Unit 表示无值
def wangt: Unit = {
}
println("Unit输出结果: " + wangt)
}
}
//输出内容
/*
Unit输出结果: ()
*/
2.11.2 Null
package com.zuoli.chapter02
object TestSpecialType2 {
/**Null类只有一个实例对象
* Null类似于Java中的null引用
* Null可以赋值给任意引用类型(AnyRef)
* 但Null不能赋值给值类型(AnyVal)*/
def main(args: Array[String]): Unit = {
var wt = new wangt()
wt = null
println(wt)
//var n1: Int = null //错误,Int属于AnyVal
}
}
class wangt {}
//输出内容
/*
null
*/
2.11.3 Nothing
package com.zuoli.chapter02
object TestSpecialType3 {
def main(args: Array[String]): Unit = {
/** Nothing,可以作为没有正常返回值的方法的返回类型
* 非常直观的告诉你这个方法不会正常返回
* 由于Nothing是其他任意类型的子类,他还能跟要求返回值的方法兼容。 */
def test(): Nothing = {
throw new Exception("抛出异常")
}
test()
}
}
//输出内容
/*
Exception in thread "main" java.lang.Exception: 抛出异常
at com.zuoli.chapter02.TestSpecialType3$.test$1(TestSpecialType3.scala:9)
at com.zuoli.chapter02.TestSpecialType3$.main(TestSpecialType3.scala:13)
at com.zuoli.chapter02.TestSpecialType3.main(TestSpecialType3.scala)
*/
第十二节 类型转换
2.12.1 数据类型自动转换
当Scala程序在进行赋值或者运算时,精度小的类型自动转换为精度大的数值类型,这个就是自动类型转换(隐式转换)。
1)基本说明
(1)自动提升原则:有多种类型的数据混合运算时,系统首先自动将所有数据转换成精度大的那种数据类型,然后再进行计算。
(2)把精度大的数值类型赋值给精度小的数值类型时,就会报错,反之就会进行自动类型转换。
(3)(byte,short)和char之间不会相互自动转换。
(4)byte,short,char他们三者可以计算,在计算时首先转换为int类型。
package com.zuoli.chapter02
object TestValueTransfer {
def main(args: Array[String]): Unit = {
/*自动提升原则:
有多种类型的数据混合运算时,
系统首先自动将所有数据转换成精度大的那种数值类型,
然后再进行计算
*/
var w1 = 1
println("w1类型为:" + w1.getClass.getSimpleName)
var w2 = w1 + 2.0
println("w2类型为:" + w2.getClass.getSimpleName)
println("======================")
/*把精度大的数值类型赋值给精度小的数值类型时,就会报错
反之就会进行自动类型转换
*/
var t1: Double = 1.0
println("t1类型为:" + t1.getClass.getSimpleName)
var t2: Int = 3
println("t2类型为:" + t2.getClass.getSimpleName)
//var t3:Int = t1 //错误,不能把高精度的double数据直接赋值给低精度int
println("======================")
//byte,short和char之间不会相互自动转换
var n1: Byte = 1
//var c1:Char = n1 //错误,byte和char之间不能相互自动转换
var n2: Short = 2
//var c2:Char = n2 //错误,short和char之间不能相互自动转换
// 其它类型支持
var i1: Int = n1
var i2: Int = n2
println(i1, i2)
println("======================")
//byte,short,char他们三者可以计算,在计算时首先转换为int类型
var b1: Byte = 1
var c1: Char = 2
var wt = b1 + c1
println(wt)
println(wt.getClass.getSimpleName)
//var n5:Short = 10 + 90 //错误 需要转Int才可以计算
}
}
//输出内容
/*
w1类型为:int
w2类型为:double
======================
t1类型为:double
t2类型为:int
======================
(1,2)
======================
3
int
*/
2.12.2 强制类型转换
自动类型转换的逆过程,将精度大的数值类型转换为精度小的数值类型。使用时要加上强制转函数,但可能造成精度降低或溢出,格外要注意
JAVA强制类型转换回顾
语法: int num = (int)2.5
package com.zuoli.test1;
public class test2 {
public static void main(String[] args) {
int num1 = (int)2.5;
int num2 = (int)(-6.666);
byte b1 = (byte)1000000;
System.out.println("num1: " + num1);
System.out.println("num2: " + num2);
System.out.println("b1: " + b1);
}
}
// 输出内容
/*
num1: 2
num2: -6
b1: 64
*/
Scala强制类型转换
语法:var num : Int = 2.5.toInt
(1)将数据由高精度转换为低精度,就需要使用到强制转换
(2)强转符号只针对于最近的操作数有效,往往会使用小括号提升优先级
package com.zuoli.chapter02
object TestForceTransfer {
def main(args: Array[String]): Unit = {
var num1: Int = 2.5.toInt
var num2 = (-6.666).toInt
var b1 = 1000000.toByte
println("num1: " + num1)
println("num2: " + num2)
println("b1: " + b1)
//在强转的时候需要注意,同样的运算强转位置不同,结果有差异
var w: Int = 10 * 3.14.toInt + 10 * 3.14.toInt //相当于10*3 + 10*3 -> 60
println("w值为: " + w)
var t: Int = (10 * 3.14 + 10 * 3.14).toInt //相当于(31.4+31.4)-> 62.8 ->62
println("t值为: " + t)
}
}
//输出内容
/*
num1: 2
num2: -6
b1: 64
w值为: 60
t值为: 62
*/
2.12.3 数值类型和String类型间转换
在程序开发中,我们经常需要将基本数值类型转成String类型。或者将String类型转成基本数值类型
(1)基本类型转String类型(语法:将基本类型的值+“” 即可)
(2)String类型转基本数值类型(语法:s1.toInt、s1.toFloat、s1.toDouble、s1.toByte、s1.toLong、s1.toShort)
(3)在将String类型转成基本数值类型时,要确保String类型能够转成有效的数据,比如我们可以把"123",转成一个整数,但是不能把"hello"转成一个整数
package com.zuoli.chapter02
object TestStringTransfer {
def main(args: Array[String]): Unit = {
/**
* 基本类型转String类型;语法:将基本类型的值+"" 即可
**/
println(true.getClass.getSimpleName)
var str1: String = true + "" // boolean + 空 = String
println("str1的值: " + str1 + ";" + "str1的类型为: " + str1.getClass.getSimpleName)
println(5.getClass.getSimpleName)
var str2: String = 5 + "" //int + 空 = String
println("str2的值: " + str2 + ";" + "str2的类型为: " + str2.getClass.getSimpleName)
println(3.14.getClass.getSimpleName)
var str3: String = 3.14 + "" //double + 空 = String
println("str3的值: " + str3 + ";" + "str3的类型为: " + str3.getClass.getSimpleName)
println("==============================")
/**
* String类型转基本数值类型(语法:调用相关API)
*/
var s1: String = "12"
var n1: Byte = s1.toByte
var n2: Short = s1.toShort
var n3: Int = s1.toInt
var n4: Long = s1.toLong
var n5: Double = s1.toDouble
println("n1的值: " + n1 + " ; " + "n1的类型为: " + n1.getClass.getSimpleName)
println("n2的值: " + n2 + " ; " + "n2的类型为: " + n2.getClass.getSimpleName)
println("n3的值: " + n3 + " ; " + "n3的类型为: " + n3.getClass.getSimpleName)
println("n4的值: " + n4 + " ; " + "n4的类型为: " + n4.getClass.getSimpleName)
println("n5的值: " + n5 + " ; " + "n5的类型为: " + n5.getClass.getSimpleName)
}
}
//输出内容
/*
boolean
str1的值: true;str1的类型为: String
int
str2的值: 5;str2的类型为: String
double
str3的值: 3.14;str3的类型为: String
==============================
n1的值: 12 ; n1的类型为: byte
n2的值: 12 ; n2的类型为: short
n3的值: 12 ; n3的类型为: int
n4的值: 12 ; n4的类型为: long
n5的值: 12.0 ; n5的类型为: double
*/
第三章 运算符
第一节 算术运算符
运算符 | 运算 | 范例 | 结果 |
---|---|---|---|
+ | 正号 | +3 | 3 |
- | 负号 | b=4; -b | -4 |
+ | 加 | 5+5 | 10 |
- | 减 | 6-4 | 2 |
* | 乘 | 3*4 | 12 |
/ | 除 | 5/5 | 1 |
% | 取模(取余) | 7%5 | 2 |
+ | 字符串相加 | “He”+”llo” | “Hello” |
(1)对于除号“/”,它的整数除和小数除是有区别的:整数之间做除法时,只保留整数部分而舍弃小数部分。
(2)对一个数取模a%b,和Java的取模规则一样。
package com.zuoli.chapter03
object TestArithmetic {
def main(args: Array[String]): Unit = {
var n1: Int = 5 / 2
println("n1值为: " + n1) // 5/2=2.5 -> int取整=2
var n2: Int = 5 % 2
println("n2值为: " + n2) // 5%2=2余1 -> 取余=1
var n3: Double = 10 / 3
println("n3值为: " + n3)
var n4: Double = 10 / 3
println("n4值为: " + n4.formatted("%.4f"))
}
}
//输出内容
/*
n1值为: 2
n2值为: 1
n3值为: 3.0
n4值为: 3.0000
*/
第二节 关系比较运算符
3.2.1 scala比较运算
运算符 | 运算 | 范例 | 结果 |
---|---|---|---|
== | 相等于 | 4==3 | false |
!= | 不等于 | 4!=3 | true |
< | 小于 | 4<3 | false |
> | 大于 | 4>3 | true |
<= | 小于等于 | 4<=3 | false |
>= | 大于等于 | 4>=3 | true |
package com.zuoli.chapter03
object TestRelation {
def main(args: Array[String]): Unit = {
var w: Int = 728
var t: Int = 421
println("w: " + w + " ; " + "t: " + t)
println("w > t : " + (w > t))
println("w >= t : " + (w >= t))
println("w < t : " + (w < t))
println("w <= t : " + (w <= t))
println("w == t : " + (w == t))
println("w != t : " + (w != t))
}
}
// 输出内容
/*
w: 728 ; t: 421
w > t : true
w >= t : true
w < t : false
w <= t : false
w == t : false
w != t : true
*/
3.2.2 Java和Scala中关于==的区别
java
package com.zuoli.test1;
public class test3 {
public static void main(String[] args) {
String s1 = "aaa";
String s2 = new String("aaa");
//==比较两个变量本身的值,即两个对象在内存中的内存首地址
System.out.println(s1 == s2); //false 内存地址不同
//equals比较字符串中所包含的内容是否相同,即比较表面内容差异
System.out.println(s1.equals(s2)); //true;"aaa"和“aaa”内容一样
}
}
//输出内容
/*
false
true
*/
scala
package com.zuoli.chapter03
object test3 {
def main(args: Array[String]): Unit = {
//scala中==更加类似于Java中的equals
val s1 = "abc"
val s2 = new String("abc")
println(s1 == s2) //true
println(s1.equals(s2)) //true
//如果需要比较类似java中的==功能,用eq()
println(s1.eq(s2)) //false
}
}
//输出内容
/*
true
true
false
*/
第三节 逻辑运算符
用于连接多个条件(一般来讲就是关系表达式),最终的结果也是一个Boolean值。
运算符 | 描述 | 实例 |
---|---|---|
&& | 逻辑与 | (true && false) 运算结果为 false |
|| | 逻辑或 | (true || false) 运算结果为 true |
! | 逻辑非 | !(true && false) 运算结果为 true |
package com.zuoli.chapter03
object TestLogic {
def main(args: Array[String]): Unit = {
var w = true
var t = false
println("w: " + w + " ; " + "t: " + t)
println("w && t :" + (w && t)) //true和false取并为false
println("w || t :" + (w || t)) //true和false取或为true
println("!(w && t) :" + (!(w && t))) // true和false取并为false,然后再取反为true
println("!(w || t) :" + (!(w || t))) //true和false取或为true,然后再取反为false
}
}
//输出内容
/*
w: true ; t: false
w && t :false
w || t :true
!(w && t) :true
!(w || t) :false
*/
第四节 赋值运算符
- 赋值运算符就是将某个运算后的值,赋给指定的变量
- Scala中没有++、–操作符,可以通过+=、-=来实现同样的效果
运算符 | 描述 | 实例 |
---|---|---|
= | 简单的赋值运算符,将一个表达式的值赋给一个左值 | C = A + B 将 A + B 表达式结果赋值给 C |
+= | 相加后再赋值 | C += A 等于 C = C + A |
-= | 相减后再赋值 | C -= A 等于 C = C - A |
*= | 相乘后再赋值 | C *= A 等于 C = C * A |
/= | 相除后再赋值 | C /= A 等于 C = C / A |
%= | 求余后再赋值 | C %= A 等于 C = C % A |
<<= | 左移后赋值 | C <<= 2等于 C = C << 2 |
>>= | 右移后赋值 | C >>= 2 等于 C = C >> 2 |
&= | 按位与后赋值 | C &= 2 等于 C = C & 2 |
^= | 按位异或后赋值 | C ^= 2 等于 C = C ^ 2 |
|= | 按位或后赋值 | C |= 2 等于 C = C | 2 |
package com.zuoli.chapter03
object TestAssignment {
def main(args: Array[String]): Unit = {
var w = 10
w += 1 //相当于w = w + 1
println(w)
var t = 10
t -= 1 //相当于t = t - 1
println(t)
}
}
//输出内容
/*
11
9
*/
第五节 位运算符
运算符 | 描述 | 实例(a = 60,b = 13) |
---|---|---|
& | 按位与运算符 | (a & b) 输出结果 12 ,二进制解释: 0000 1100 |
| | 按位或运算符 | (a | b) 输出结果 61 ,二进制解释: 0011 1101 |
^ | 按位异或运算符 | (a ^ b) 输出结果 49 ,二进制解释: 0011 0001 |
~ | 按位取反运算符 | (~a ) 输出结果 -61 ,二进制解释: 1100 0011, 在一个有符号二进制数的补码形式。 |
<< | 左移动运算符 | a << 2 输出结果 240 ,二进制解释: 0011 0000 |
>> | 右移动运算符 | a >> 2 输出结果 15 ,二进制解释: 0000 1111 |
>>> | 无符号右移 | a >>>2 输出结果 15, 二进制解释: 0000 1111 |
package com.zuoli.chapter03
object TestPosition {
def main(args: Array[String]): Unit = {
// 建议了解一下有这个东西,用到时再查
var n: Int = 8
n = n << 1
println("8 << 1位运算值: " + n)
n = n << 1
println(16 << 1)
println(n)
var w: Int = 1000
w = w << 1
println(w)
}
}
//输出内容
/*
8 << 1位运算值: 16
32
32
2000
*/
第六节 scala运算符原理
在Scala中其实是没有运算符的,所有运算符都是方法。
1)当调用对象的方法时,这个点‘.’可以省略
2)如果函数参数只有一个,或者没有参数,()可以省略
package com.zuoli.chapter03
object TestOpt {
def main(args: Array[String]): Unit = {
// 1.标准的加法运算
val n1: Int = 1.+(1) //相当于1 .+() 方法 里面传值1 -> 1.+(1)
// 2.当调用对象的方法时,.可以省略
val n2: Int = 1 + (1) //1.+(1)点可以省略 -> 1+(1)
// 3.如果函数参数只有一个,或者没有参数,()可以省略;
val n3: Int = 1 + 1 //1+(1) 括号可以省略 -> 1 + 1
//1.toString()
println(1.toString())
//1 toString() 省略了点
println(1 toString())
//1 toString 省略了括号
println(1 toString)
}
}
//输出内容
/*
1
1
1
*/
第四章 流程控制
第一节 分支控制if else
让程序有选择的的执行,分支控制有三种:单分支、双分支、多分支;
package com.zuoli.chapter04
object TestIfElse {
def main(args: Array[String]): Unit = {
// 单分支
var wangt = 100
if (wangt < 10000) {
println("100 < 10000")
}
println("=====================")
//双分支
if (wangt < 50) {
println("比50小")
} else {
println("比50大")
}
println("=====================")
//多分支
if (wangt < 50) {
println("比50小")
} else if (wangt >= 50 && wangt < 100) {
println("大于等于50,小于100")
} else {
println("其它情况(大于等于100)")
}
println("=====================")
//Scala中if else表达式其实是有返回值的
//具体返回值取决于满足条件的代码体的最后一行内容
val res1: String = if (wangt < 50) {
"小于50"
} else if (wangt >= 50 && wangt < 100) {
"大于等于50,小于100"
} else {
"其它情况(大于等于100)"
}
println("res1的值为: " + res1)
println("=====================")
//Scala中返回值类型不一致
//取它们共同的祖先类型
val res2: Any = if (wangt < 50) {
"小于50"
} else if (wangt >= 50 && wangt < 100) {
"大于等于50,小于100"
} else {
1000000
}
println("res2的值为: " + res2)
println("=====================")
//1.如果大括号{}内的逻辑代码只有一行,大括号可以省略
//2.如果省略大括号,if只对最近的一行逻辑代码起作用
val res3: Any = if (wangt < 18) "100 < 18 正确" else "100 < 18 错误"
"这里的内容不起作用"
println("res3的值为: " + res3)
}
}
//输出内容
/*
100 < 10000
=====================
比50大
=====================
其它情况(大于等于100)
=====================
res1的值为: 其它情况(大于等于100)
=====================
res2的值为: 1000000
=====================
res3的值为: 100 < 18 错误
*/
第二节 嵌套分支
在一个分支结构中又完整的嵌套了另一个完整的分支结构,里面的分支的结构称为内层。分支外面的分支结构称为外层分支。嵌套分支不要超过3层
package com.zuoli.chapter04
object TestIfElse2 {
def main(args: Array[String]): Unit = {
var age = 60
//在else中再嵌套了一个(if - else if - else)
val res: String = if (age < 18) {
"未成年人"
} else {
if (age >= 18 && age < 30) {
"小青年"
} else if (age > 30 && age < 50) {
"中年人"
}
else {
"老年"
}
}
println(res)
}
}
//输出内容
/*
老年
*/
第三节 switch分支结构
在Scala中没有Switch,而是使用模式匹配来处理(第八章涉及模式匹配)
在Scala中没有Switch,而是使用模式匹配来处理(第八章涉及模式匹配)
在Scala中没有Switch,而是使用模式匹配来处理(第八章涉及模式匹配)
第四节 for循环控制
4.4.1 范围数据循环 to
java for循环回顾
package com.zuoli.test;
public class test04 {
public static void main(String[] args) {
//输出1 - 5 [1,5]
for (int i = 1; i <= 5; i++) {
System.out.println(i);
}
}
}
//输出内容
/*
1
2
3
4
5
*/
Scala for循环:to
package com.zuoli.chapter04
object TestFor01 {
def main(args: Array[String]): Unit = {
for (i <- 1 to 5) {
println("i:" + i)
}
}
}
// 输出内容
/*
i:1
i:2
i:3
i:4
i:5
*/
4.4.2 范围数据循环 until
java for循环回顾
package com.zuoli.test;
public class test04 {
public static void main(String[] args) {
//输出1-4 [1,5)
for (int j = 1; j < 5; j++) {
System.out.println(j);
}
}
}
//输出内容
/*
1
2
3
4
*/
Scala for循环:until
package com.zuoli.chapter04
object TestFor01 {
def main(args: Array[String]): Unit = {
//输出1-4 [1,5)
for (j <- 1 until 5) {
println("j:" + j)
}
}
}
// 输出内容
/*
j:1
j:2
j:3
j:4
*/
4.4.3 循环守卫
回顾java中的continue
package com.zuoli.test;
public class test04 {
public static void main(String[] args) {
//输出1 - 5 ; 如果等于3的时候跳过这次输出 (continue)
for (int i = 1; i <= 5; i++) {
if (i == 3) {
continue;
}
System.out.println(i);
}
}
}
//输出内容
/*
1
2
4
5
*/
scala的循环守卫,即循环保护式(也称条件判断式,守卫)。保护式为true则进入循环体内部,为false则跳过,类似于java的continue
方法1:
package com.zuoli.chapter04
object For02 {
def main(args: Array[String]): Unit = {
for (i <- 1 to 5 if i != 3) {
println(i)
}
}
}
//输出内容
/*
1
2
4
5
*/
方法2:
package com.zuoli.chapter04
object For02 {
def main(args: Array[String]): Unit = {
for (i <- 1 to 5) {
if (i != 3) {
println(i)
}
}
}
}
//输出内容
/*
1
2
4
5
*/
4.4.4 循环步长
package com.zuoli.chapter04
object TestFor03 {
def main(args: Array[String]): Unit = {
for (i <- 1 to 10 by 3) {
println(i)
}
}
}
//输出内容
/*
1
4
7
10
*/
4.4.5 嵌套循环
package com.zuoli.chapter04
object TestFor03 {
def main(args: Array[String]): Unit = {
for (i <- 1 to 2) {
for (j <- 1 to 3) {
println("i: " + i + "; j: " + j)
}
}
println("=======================")
//上面示例可以简化,这种方式前提是外循环没有代码块
for (i <- 1 to 2; j <- 1 to 3) { //不同层的循环用;号
println("i: " + i + "; j: " + j)
}
}
}
//输出内容
/*
i: 1; j: 1
i: 1; j: 2
i: 1; j: 3
i: 2; j: 1
i: 2; j: 2
i: 2; j: 3
=======================
i: 1; j: 1
i: 1; j: 2
i: 1; j: 3
i: 2; j: 1
i: 2; j: 2
i: 2; j: 3
*/
4.4.6 引入变量
package com.zuoli.chapter04
object TestFor04 {
def main(args: Array[String]): Unit = {
//内循环引入外循环变量
for (i <- 1 to 3; j = 3 - i) {
println("i: " + i + "; j=3-i: " + j)
}
}
}
//输出内容
/*
i: 1; j=3-i: 2
i: 2; j=3-i: 1
i: 3; j=3-i: 0
*/
4.4.7 循环返回值
将遍历过程中处理的结果返回到一个新Vector集合中,使用yield关键字
package com.zuoli.chapter04
object TestFor04 {
def main(args: Array[String]): Unit = {
//将遍历过程中处理的结果返回到一个新Vector集合中,使用yield关键字
var res = for (i <- 1 to 10) yield {
"第" + i + "个超神"
}
println(res)
//利用刚接触的for循环去取Vector集合中的数据
for (i <- res) {
println(i)
}
}
}
//输出内容
/*
Vector(第1个超神, 第2个超神, 第3个超神, 第4个超神, 第5个超神, 第6个超神, 第7个超神, 第8个超神, 第9个超神, 第10个超神)
第1个超神
第2个超神
第3个超神
第4个超神
第5个超神
第6个超神
第7个超神
第8个超神
第9个超神
第10个超神
*/
4.4.8 倒序打印
package com.zuoli.chapter04
object TestFor05 {
def main(args: Array[String]): Unit = {
//reverse相当于翻转
for (i <- 1 to 4 reverse) {
println(i)
}
//利用by -1 和 5 to 1 可以实现,但reverse可用场景更多
for (i <- 4 to 1 by -1) {
println(i)
}
}
}
//输出内容
/*
4
3
2
1
4
3
2
1
*/
第五节 while和do while 循环控制
scala的While和do While的使用和Java语言中用法相同
4.5.1 While循环控制
(1)循环条件是返回一个布尔值的表达式
(2)while循环是先判断再执行语句
(3)与for语句不同,while语句没有返回值,即整个while语句的结果是Unit类型()
(4)因为while中没有返回值,所以当要用该语句来计算并返回结果时,就不可避免的使用变量,而变量需要声明在while循环的外部,那么就等同于循环的内部对外部的变量造成了影响,所以不推荐使用,而是推荐使用for循环。
package com.zuoli.chapter04
object TestWhile {
def main(args: Array[String]): Unit = {
var i = 0
while (i < 5) {
println("飘飘")
println("i=" + i + " ; 小于5")
i += 1
}
}
}
//输出内容
/*
飘飘
i=0 ; 小于5
飘飘
i=1 ; 小于5
飘飘
i=2 ; 小于5
飘飘
i=3 ; 小于5
飘飘
i=4 ; 小于5
*/
4.5.2 do while循环控制
(1)循环条件是返回一个布尔值的表达式
(2)do while循环是先执行,再判断;不管判断如何,第一次循环都会执行到do中的代码块,这也是和while循环最大不同之处
package com.zuoli.chapter04
object TestDoWhile {
def main(args: Array[String]): Unit = {
var i = 20
do {
println("飘飘")
println("i=" + i + " ; 小于10")
i += 1
} while (i < 10)
//i初始值是20,while判断是i小于10才符合规则,但显然还是可以输出一次内容
}
}
//输出内容
/*
飘飘
i=20 ; 小于10
*/
第六节 循环中断
Scala内置控制结构特地去掉了break和continue;
为了更好的适应函数式编程,使用函数式的风格解决break和continue的功能,而不是像JAVA一个关键字;
Scala中使用breakable控制结构来实现break和continue功能。
import scala.util.control.Breaks
Breaks.breakable(
代码块
)
package com.zuoli.chapter04
import scala.util.control.Breaks
object TestBreakable {
def main(args: Array[String]): Unit = {
Breaks.breakable(
for (i <- 1 to 5) {
if (i == 3)
Breaks.break()
println(i)
}
)
}
}
//输出内容
/*
1
2
*/
上面示例可以简化:
import scala.util.control.Breaks._
Breaks.可以省略;
break 括号可以省略;
package com.zuoli.chapter04
import scala.util.control.Breaks._
object TestBreakable {
def main(args: Array[String]): Unit = {
breakable(
for (i <- 1 to 5) {
if (i == 3)
break
println(i)
}
)
}
}
//输出内容
/*
1
2
*/
第七节 多重循环
(1)将一个循环放在另一个循环体内,就形成了嵌套循环。其中,for,while,do…while均可以作为外层循环和内层循环。【建议一般使用两层,最多不要超过3层】
(2)设外层循环次数为m次,内层为n次,则内层循环体实际上需要执行m*n次。
print() 默认没有换行
println() 默认有换行
下面示例中 + j * i 因为scala中都是函数方法,就近原则,所以这里如果要先算乘法,用括号括起来,达到优先原则
package com.zuoli.chapter04
object TestForFor {
def main(args: Array[String]): Unit = {
for (i <- 1 to 9) {
for (j <- 1 to i) {
print(j + "*" + i + "=" + (j * i) + "\t")
}
println()
}
}
}
//输出内容
/*
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
1*4=4 2*4=8 3*4=12 4*4=16
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
*/
第五章 函数式编程
1.解决问题时,将问题分解成一个一个的步骤,将每个步骤进行封装(函数),通过调用这些封装好的步骤,解决问题
2.Scala语言是一个完全函数式编程语言。万物皆函数,将以往java的一些关键字形式也都以函数方式呈现
3.函数的本质:函数可以当做一个值进行传递
4.在scala中,方法和函数几乎可以等同(比如他们的定义、使用、运行机制都一样的),只是函数的使用方式更加的灵活多样 [方法转函数]
第一节 函数基础
5.1.1 函数基本语法
前面章节函数已经出现多次,这里简单介绍个示例
package com.zuoli.chapter05
object TestFun01 {
def main(args: Array[String]): Unit = {
//函数Flower
def Flower(f_name: String): Unit = {
println(f_name)
}
Flower("牡丹花")
Flower("玫瑰花")
Flower("三花")
//def -> 固定格式 定义函数关键词使用def
//Flower -> 是函数名;定义一个函数需要为其取名
//f_name: String -> 参数名: 参数类型
//Unit -> 函数返回值类型
//println(f_name) 函数体,代码块
}
}
//输出内容
/*
牡丹花
玫瑰花
三花
*/
5.1.2 函数和方法的区别
Scala中既有函数也有方法,大多数情况下我们都可以不去理会他们之间的区别。但是有时候我们必须要了解他们之间的不同
Scala 中的方法跟 Java 的方法一样,方法是组成类的一部分。方法有名字、类型签名,有时方法上还有注解,以及方法的功能实现代码
Scala 方法是类的一部分,而函数是一个对象可以赋值给一个变量。换句话来说在类中定义的函数即是方法
方法:由方法名、参数、方法体构成,一般指的是类中定义的函数即为方法
函数:由函数名、参数、函数体构成,一般指的是可以独立构建的称为函数
package com.zuoli.chapter05
object TestFun02 {
//这里定义一个main方法
def main(): Unit = {
}
//再定义一个同名main方法;
//显然实现了重载和重写,应用并没有报错,否则下面这个main方法创建失败
//说明方法可以重载重写
def main(args: Array[String]): Unit = {
//定义一个Flower函数,然后再定义一次Flower
//提示报错 Flower is already defined in the scope
//def Flower():Unit = {
// println("创建一个Flower函数,无参无返回值,验证下方可否重载")
//}
//这个同名的Flower如果需要成功创建,上面Flower不能存在
def Flower(f_name: String): Unit = {
println(f_name)
}
Flower("向日葵") //Flower函数调用
//定义一个动物函数
def Animal(a_name: String): Unit = {
println(a_name)
}
Animal("猪") //Animal函数调用
//
def Huahua(): Unit = {}
//函数可以嵌套定义
def OutFunc(): Unit = {
def InFunc(): Unit = {
println("看到这句在执行InFunc函数,在OutFunc函数内部")
}
println("看到这句在执行OutFunc函数")
InFunc()
}
OutFunc()
}
}
//输出内容
/*
向日葵
猪
看到这句在执行OutFunc函数
看到这句在执行InFunc函数,在OutFunc函数内部
*/
5.1.3 函数定义
(1)函数1:无参,无返回值
(2)函数2:无参,有返回值
(3)函数3:有参,无返回值
(4)函数4:有参,有返回值
(5)函数5:多参,无返回值
(6)函数6:多参,有返回值
package com.zuoli.chapter05
object TestFun03 {
def main(args: Array[String]): Unit = {
//(1)函数1:无参,无返回值
def Test01(): Unit = {
println("函数1:无参,无返回值")
}
Test01()
println("============================")
//(2)函数2:无参,有返回值(有返回值不是Unit)
def Test02(): String = {
"函数2:无参,有返回值"
}
var res_test02 = Test02()
println(res_test02)
println("============================")
//(3)函数3:有参,无返回值
def Test03(wt: String): Unit = {
println("函数3:有参,无返回值" + " " + wt + "<-参数")
}
Test03("sanhua_test03")
println("============================")
//(4)函数4:有参,有返回值
def Test04(wt_4: String): String = {
"函数4:有参,有返回值" + " " + wt_4 + "<-参数"
}
var res_test04 = Test04("sanhua_test04")
println(res_test04)
println("============================")
//(5)函数5:多参,无返回值
def Test05(sh: String, hh: String): Unit = {
println("函数5:多参,无返回值" + s"${sh} <-sh参数 ;${hh} <-hh参数")
}
Test05("三花", "花花")
println("============================")
//(6)函数6:多参,有返回值
def Test06(zy: String, pp: String): String = {
"函数6:多参,有返回值" + " " + zy + "<-zy参数 ;" + pp + "<-pp参数"
}
var res_test06 = Test06("紫玉", "飘飘")
println(res_test06)
}
}
//输出内容
/*
函数1:无参,无返回值
============================
函数2:无参,有返回值
============================
函数3:有参,无返回值 sanhua_test03<-参数
============================
函数4:有参,有返回值 sanhua_test04<-参数
============================
函数5:多参,无返回值三花 <-sh参数 ;花花 <-hh参数
============================
函数6:多参,有返回值 紫玉<-zy参数 ;飘飘<-pp参数
*/
5.1.4 函数参数
直接上示例,比较一下不同之处
(1)任意个参数接收0个-n个
package com.zuoli.chapter05
object TestFun04 {
def main(args: Array[String]): Unit = {
//(1)任意个参数接收0个-n个
def Flower(f_name: String*): Unit = {
println(f_name)
}
//有输入参数;输出 Array,参数逐个放在Array中
Flower("牡丹花", "玫瑰花", "百合花", "牵牛花")
//无输入参数;输出List()
Flower()
//输出内容
/*
WrappedArray(牡丹花, 玫瑰花, 百合花, 牵牛花)
List()
*/
(2)多个参数,那么可变参数一般放置在后面
package com.zuoli.chapter05
object TestFun05 {
def main(args: Array[String]): Unit = {
//多个参数,那么可变参数一般放置在后面
def Flower(first_name: String, f_name: String*): Unit = {
println(first_name + ":<--first_name参数内容 | 可变参数内容-->:" + f_name)
}
Flower("花花", "玫瑰花", "百合花", "牵牛花")
}
//把可变参数放在前面 发现提示 *-parameter must come last *参数分布必须在最后
//def Flower_2(f_name: String*, first_name: String): Unit = {
//println(f_name + ":<--first_name参数内容 | 可变参数内容-->:" + first_name)
//}
}
//输出内容
/*
花花:<--first_name参数内容 | 可变参数内容-->:WrappedArray(玫瑰花, 百合花, 牵牛花)
*/
(3)参数有默认值,一般将有默认值的参数放置在参数列表的后面
package com.zuoli.chapter05
object TestFun06 {
def main(args: Array[String]): Unit = {
//参数有默认值,一般将有默认值的参数放置在参数列表的后面
def Wow(characters: String, level: Int = 60): Unit = {
/** 魔兽世界
* characters 角色名
* level 游戏等级
* 默认值是60,为满级
* 有默认值的参数放在后面
*/
println(s"角色名:$characters,当前等级:$level")
}
//不传入level参数,则level去函数定义的默认值
Wow("紫灬玉")
//传入level参数,则level新传入参数覆盖函数定义的默认值
Wow("紫丨玉", 52)
}
}
//输出内容
/*
角色名:紫灬玉,当前等级:60
角色名:紫丨玉,当前等级:52
*/
(4)Scala函数中参数传递是,从左到右
package com.zuoli.chapter05
object TestFun07 {
def main(args: Array[String]): Unit = {
def Company(name: String, address: String, area: String, city: String): Unit = {
println(s"公司名:$name,\n地址:$address,\n地区:$area,\n城市:$city,")
}
//Scala函数中参数传递是,从左到右,参数按顺序传递
Company("bigdata", "金桥", "浦东", "上海")
//虽然传入0 4 2 1,但最终和变量对应顺序还是0 4 2 1
Company("0", "4", "2", "1")
}
}
//输出内容
/*
公司名:bigdata,
地址:金桥,
地区:浦东,
城市:上海,
公司名:0,
地址:4,
地区:2,
城市:1,
*/
(5)带名参数
package com.zuoli.chapter05
object TestFun08 {
def main(args: Array[String]): Unit = {
def Company(name: String, address: String, area: String, city: String): Unit = {
println(s"公司名:$name,\n地址:$address,\n地区:$area,\n城市:$city,")
}
//Scala函数中参数传递是,从左到右,参数按顺序传递
Company("bigdata", "金桥", "浦东", "上海")
//带名参数时,值与变量一一对应匹配
Company(city = "上海", address = "金桥", area = "浦东", name = "bigdata")
}
}
//输出内容
/*
公司名:bigdata,
地址:金桥,
地区:浦东,
城市:上海,
公司名:bigdata,
地址:金桥,
地区:浦东,
城市:上海,
*/
5.1.5 函数至简原则
这里的至简原则指的是代码精简,效果相同时能省略的代码则省略;
(1)return可以省略,Scala会使用函数体的最后一行代码作为返回值
(2)如果函数体只有一行代码,可以省略花括号
(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
(4)如果有return,则不能省略返回值类型,必须指定
(5)如果函数明确声明unit,那么即使函数体中使用return关键字也不起作用
(6)Scala如果期望是无返回值类型,可以省略等号
(7)如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
(8)如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
package com.zuoli.chapter05
object TestFun09 {
def main(args: Array[String]): Unit = {
//标准正常的函数
def Flower(f_name: String): String = {
return f_name
}
//调用
println(Flower("玫瑰原版"))
//(1)return可以省略,Scala会使用函数体的最后一行代码作为返回值
def Flower01(f_name: String): String = {
f_name
}
//调用
println(Flower01("玫瑰01"))
//(2)如果函数体只有一行代码,可以省略花括号
def Flower02(f_name: String): String = f_name
//调用
println(Flower02("玫瑰02"))
//(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
def Flower03(f_name: String) = f_name
//调用
println(Flower03("玫瑰03"))
//(4)如果有return,则不能省略返回值类型,必须指定
// 函数体有return,参数体后面:String = 的返回值不能省略
def Flower04(f_name: String): String = {
return f_name
}
//调用
println(Flower04("玫瑰04"))
//(5)如果函数明确声明unit,那么即使函数体中使用return关键字也不起作用
// 返回值 : Unit = ;有了这个 函数体内的return代码可以忽略
def Flower05(): Unit = {
return "玫瑰05"
}
//调用
println(Flower05()) // 打印出来是() 没玫瑰05
//(6)Scala如果期望是无返回值类型,可以省略等号
def Flower06() {
"玫瑰06"
}
//调用
println(Flower06())
//(7)如果函数 无参,但是声明了参数列表,那么调用时,小括号,可加可不加
def Flower07() = "玫瑰07"
//调用
println(Flower07()) //加小括号 可以
println(Flower07) //不加小括号 可以
//(8)如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
def Flower08 = "玫瑰08"
//调用
//println(Flower08()) //加小括号 不正确函数本身就没有参数列表
println(Flower08) //不加小括号 正确返回
}
}
//输出内容
/*
玫瑰原版
玫瑰01
玫瑰02
玫瑰03
玫瑰04
()
()
玫瑰07
玫瑰07
玫瑰08
*/
第二节 函数进阶
5.2.1 高阶函数
函数可以作为值进行传递
package com.zuoli.chapter05
object TestFun10 {
def main(args: Array[String]): Unit = {
//定义个foo函数
def foo(): Int = {
println("我是foo函数.")
9527
}
// 1.调用foo函数,把返回值给变量f1,f2
val f1 = foo()
println(f1) //我是foo函数. + 9527
println("==========")
val f2 = foo
println(f2) //我是foo函数. + 9527
//f1和f2输出一样,括号可以省略
// 2.在被调用函数foo后面加上 _,相当于把函数foo当成一个整体,传递给变量f3
// 下方foo()和f3() 输出相同,说明效果等同于把foo当成一个整体,传递给变量f3
println("==========")
val f3 = foo _
foo()
f3()
// 3.如果明确变量类型,那么不使用下划线也可以将函数作为整体传递给变量
println("==========")
val f4: () => Int = foo
foo()
f4()
}
}
//输出内容
/*
我是foo函数.
9527
==========
我是foo函数.
9527
==========
我是foo函数.
我是foo函数.
==========
我是foo函数.
我是foo函数.
*/
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
函数可以作为参数进行传递
package com.zuoli.chapter05
object TestFun11 {
def main(args: Array[String]): Unit = {
// 1.定义一个函数,函数参数还是一个函数签名;f表示函数名称;(Int,Int)表示输入两个Int参数;Int表示函数返回值
def f1(f: (Int, Int) => Int): Int = {
f(1, 2)
}
// 2.定义一个函数,参数和返回值类型和f1的输入参数一致
def f2(a: Int, b: Int): Int = a + b
// 3.将add函数作为参数传递给f1函数,如果能够推断出来不是调用,_可以省略
println(f1(f2))
println("========== 华丽分割线 ==========")
println(f1(f2 _))
}
}
//输出内容
/*
3
========== 华丽分割线 ==========
3
*/
函数可以作为函数返回值返回
package com.zuoli.chapter05
object TestFun12 {
def main(args: Array[String]): Unit = {
def f1() = {
def f2() = {
666
}
f2 _
}
val f = f1()
// 因为f1函数的返回值依然为函数,所以可以变量f可以作为函数继续调用
println(f())
println("========== 华丽分割线 ==========")
println(f1()())
// f2() 返回值是666
// f1()中 有f2 _ 说明返回值是f2函数 ;而f2函数返回值是666
// f相当于f1()
// f()相当于f1()()
}
}
//输出内容
/*
666
========== 华丽分割线 ==========
666
*/
5.2.2 匿名函数
没有名字的函数就是匿名函数
一般为参数类型和代码逻辑块组成:
(x:Int)=>{函数体}
x:表示输入参数类型;Int:表示输入参数类型;函数体:表示具体代码逻辑
package com.zuoli.chapter05
object TestFun14 {
def main(args: Array[String]): Unit = {
// 先定义一个函数operation;
// 其中参数包含数据和逻辑函数op
def operation(arr: Array[Int], op: Int => Int) = {
for (elem <- arr) yield op(elem)
}
//定义逻辑函数
def op(huahua: Int): Int = {
huahua + 1
}
//函数调用
val arr1 = operation(Array(1, 2, 3, 4, 5, 6), op)
println(arr1.mkString(" _ "))
// arr1调用,需要定义逻辑函数op
// er使用arr2匿名函数方式,即使注释上方逻辑函数一样可以执行
val arr2 = operation(Array(1, 2, 3, 4, 5, 6), (huahua: Int) => {
huahua + 1
})
println(arr2.mkString(" | "))
}
}
//输出内容
/*
2 _ 3 _ 4 _ 5 _ 6 _ 7
2 | 3 | 4 | 5 | 6 | 7
*/
package com.zuoli.chapter05
object TestFun13 {
def main(args: Array[String]): Unit = {
def ji_suan_qi(a: Int, b: Int, op: (Int, Int) => Int): Int = {
op(a, b)
}
// 不作省略,以下标准代码格式
println(ji_suan_qi(111, 222, (x: Int, y: Int) => {
x + y
}))
// 代码块只有一行,大括号可以省略
println(ji_suan_qi(111, 222, (x: Int, y: Int) => x + y))
// 参数的类型可以省略,会根据形参进行自动的推导
println(ji_suan_qi(111, 222, (x, y) => x + y))
// 如果参数只出现一次,则参数省略且后面参数可以用_代替,x y 各出现一次,则
println(ji_suan_qi(111, 222, _ + _))
}
}
//输出内容
/*
333
333
333
333
*/
5.2.3 函数柯里化闭包
函数柯里化:把一个参数列表的多个参数,变成多个参数列表
闭包:如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和他所处的环境,称为闭包
package com.zuoli.chapter05
object TestFun15 {
def main(args: Array[String]): Unit = {
def f1() = {
val a: Int = 1
def f2(b: Int) = {
a + b
}
f2 _
}
//变量a在f1函数中,在调用时,f1函数执行完毕后,局部变量a应该随着栈空间释放掉
val f = f1()
//但是在案例中,变量a其实并没有释放,而是包含在了f2函数的内部,形成了闭合的效果
println(f(666))
println(f1()(666))
println("========== 华丽分割线 ==========")
//函数柯里化,其实就是将复杂的参数逻辑变得简单化,函数柯里化一定存在闭包
def f3() = {
val a: Int = 2
(b: Int) => a + b
}
println(f3()(666))
}
}
//输出内容
/*
667
667
========== 华丽分割线 ==========
668
*/
5.2.4 递归
一个函数/方法在函数/方法体内又调用了本身,我们称之为递归调用
package com.zuoli.chapter05
object TestFun16 {
def main(args: Array[String]): Unit = {
//定义一个函数huahua,i参数类型Int
//huahua函数代码块中,调用了函数自身
def huahua(i: Int): Int = {
if (i == 1) {
1
} else {
i * huahua(i - 1)
}
}
println(huahua(5))
// 5 *4 *3 *2 *1
}
}
//输出内容
/*
120
*/
5.2.5 控制抽象
值调用:把最后的值传递过去
package com.zuoli.chapter05
object TestFun17 {
def main(args: Array[String]): Unit = {
def f1 = () => {
println("zuoli_666是f1返回值")
"zuoli_666"
}
//调用huahua函数,huahua函数需要接一个参数String,
huahua(f1()) // 注意输出结果 huahua函数打印了3遍返回值
}
def huahua(pp: String): Unit = {
println(pp)
println(pp)
println(pp)
}
}
//输出内容
/*
zuoli_666是f1返回值
zuoli_666
zuoli_666
zuoli_666
*/
名调用:把代码传递过去
package com.zuoli.chapter05
object TestFun18 {
def main(args: Array[String]): Unit = {
def f2 = () => {
println("zuoli_666是f2返回值")
"zuoli_666"
}
huahua(f2()) // 注意输出结果 huahua函数打印了3遍f2代码块内容
}
def huahua(a: => String): Unit = {
println(a)
println(a)
println(a)
}
}
//输出内容
/*
zuoli_666是f2返回值
zuoli_666
zuoli_666是f2返回值
zuoli_666
zuoli_666是f2返回值
zuoli_666
*/
5.2.6 惰性函数
当函数返回值被声明为lazy时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行。这种函数我们称之为惰性函数
以下代码注意不加lazy和加了lazy的区别
package com.zuoli.chapter05
object TestFun19 {
def main(args: Array[String]): Unit = {
val res1 = sum(666, 1)
println("==================")
println("666+1 = " + res1)
}
def sum(a: Int, b: Int): Int = {
println("sum函数式求a+b的和")
a + b
}
}
//输出内容
/*
sum函数式求a+b的和
==================
666+1 = 667
*/
/**
不加lazy时:
val res1 = sum(666, 1) 调用了sum函数
所以先执行了sum中代码块println("sum函数式求a+b的和")
然后把返回值传给res1接住
之后执行到下一行println("==================")
最后利用println打印出res1
所以结果是:
sum函数式求a+b的和
==================
666+1 = 667
*/
package com.zuoli.chapter05
object TestFun20 {
def main(args: Array[String]): Unit = {
lazy val res2 = sum(666, 2)
println("==================")
println("666+2 = " + res2)
}
def sum(a: Int, b: Int): Int = {
println("sum函数式求a+b的和")
a + b
}
}
//输出内容
/*
==================
sum函数式求a+b的和
666+2 = 668
*/
/**
加lazy时:
lazy val res2 = sum(666, 2) 调用了sum函数,但加了lazy关键词
所以println("sum函数式求a+b的和")先不执行,它要等待sum函数中被第一次取值才执行
那就只能先执行下一行的println("==================")
利用println打印出res2时,这时候就调用了sum函数的返回值
所以可以执行println("sum函数式求a+b的和"),执行完后
最后利用println打印出res2
所以结果是:
==================
sum函数式求a+b的和
666+2 = 668
*/
标签:万字,String,scala,var,Unit,println,def,入门
From: https://blog.csdn.net/weixin_43977307/article/details/139235347