scala基础:
hello world:
- 写scala可运行文件的注意事项
- 1、如果一个scala文件要运行,class要改成object
- 2、如果是class,就仅单纯代表一个类,如果是object代表的是单例对象
- 3、scala语法中,一句话结束不需要加分号
- 4、scala文件中,可以无缝使用java中的类和方法
object HelloWorld {
def main(args: Array[String]): Unit = {
// 输出一句hello world
println("hello world")
// java语言的输出一句话
System.out.println("hello world")
}
}
变量、常量
/**
* 变量:在程序的运行过程中,其值可以发生改变的量
* 在scala中定义一个变量,需要使用一个关键词:var
*常量: 定义一个常数,使用关键字:var
* 注意:
* 1、变量一旦定义,它的类型就确定,可以不用手动指定类型,根据赋的值自动推断出类型
* 2、也可以手动的指定变量的数据类型,完整的写法:var 变量名:数据类型 = 值
*
* scala中的数据类型和java的数据类型对应关系(Byte->Double 按所占字节数的大小,从小到大进行排序):
* java: scala:
* byte Byte
* short Short
* int Int
* long Long
* float Float
* double Double
* boolean Boolean
* char Char
*/
// var定义一个变量
var a1 = 100
println(a1)
//获取变量的类型
println(a1.getClass)
// 更改a1的值
a1 = 200
println(a1)
var a2: Int = 10
println(a2.getClass)
val a3: Int = 100
println(a3)
// val 定义常量,若是修改其值会报错
// a3 = 200
// * : 为一个函数,底层通过StringBuilder来实现字符的链接
println("=" * 50)
字符串
/**
* scala中字符串、及其函数的使用
* 字符串:由若该字符串组成的序列
*/
// 可以使用双引号构建字符串
var s1: String = "这是一个字符串"
println(s1)
// 使用""" """" 构建一个长字符串
var sql: String =
"""
|这是一个长字符串
|真的很长
|注意了!!!
|""".stripMargin
println(sql)
// String类和Java是一个共同的字符串类,String类中的功能在scala中正常使用
var s3 = "hello,world,java,hadoop,scala"
val arr1: Array[String] = s3.split(",")
// scala中的数组下标也是从0开始的,不过取的时候要使用arr1(0)
println(arr1(0))
println(arr1(1))
println(arr1(2))
/**
* scala中字符串的拼接:
* 1、使用 + 进行拼接,不过这种方式很消耗性能
* 2、使用StringBuilder
* 3、使用scala的特有函数mkString,前提条件是:有一组可拼接的序列
* 4、使用scala特有的字符串传递方式 s"{变量}” (类似于python语言) 底层就是使用StringBuilder方式拼接的
*/
var q1: String = "hello"
var q2: String = "hello"
var q3: String = "hello"
var res1: String = q1 + "," + q2 + "," + q3
println(res1)
var res2: StringBuilder = new StringBuilder()
res2.append(q1).append(",").append(q2).append(",").append(q3)
println(res2)
var res3: String = arr1.mkString(",")
println(res3)
// 使用s"${}", 功能强大可以在括号中调用函数
var res4: String = s"${q1.toUpperCase},${q2},${q3}"
println(res4)
运算符
/**
* 运算符
*/
var x: Int = 3
var y: Int = 4
println(x + y)
println(x - y)
println(x * y)
//此处的 / 为整除,若想让其取小数,可以让两个数中的其中一个变成一个浮点数
println(x / y)
println(x * 1.0 / y)
println(x % y)
循环语句
/**
* 循环语句:
* 注:
* 1、在scala语言中,没有++或--, 如 i++ 、 i--
* 2、在scala语言中,不存在和java一样的普通for循环
* 3、scala中的循环的写法不太一样
*/
var i: Int = 0
while (i < arr2.length){
println(arr2(i))
}
// 1 to 10 : 相当于闭区间的1到10
for (e <- 1to 10){
println(e)
}
for(e <- 1 until 10){
println(e)
}
控制语句
/**
* 控制语句
* 注:在scala中没有break、continue关键字
* 要想使用break得导包:import scala.util.control.Breaks.break
*/
//TODO 在break后,程序的执行结束,如若想继续执行,那么需要再加上一个breakable
breakable{
for (e <- 1 to 10) {
if (e == 5) {
//TODO:底层为一个异常抛出, def break(): Nothing = { throw breakException }
break;
}
println(e)
}
}
println("太牛了!")
}
IO流
def main(args: Array[String]): Unit = {
//读取一个文件内容
//使用java的方式读取文件, 使用带缓冲区的字符输入流
val br: BufferedReader = new BufferedReader(new FileReader("scala/data/words.txt"))
var line:String = br.readLine()
while (line!=null){
println(line)
line = br.readLine()
}
//scala中的读取文件的方式
//Source.fromFil 底层是使用了字节输入流读取数据FileInputStream
val bs: BufferedSource = Source.fromFile("scala/data/words.txt")
// getLines();返回的是一个迭代器对象, 使用迭代器的hasNext()、next() 方法进行数据的输出
val lineIterator: Iterator[String] = bs.getLines()
while (lineIterator.hasNext){
val s: String = lineIterator.next()
println(s)
}
// 既然返回的是一个迭代器,那么就可以使用for循环来进行输出
for (e <- bs.getLines()) {
println(e)
}
//java写文件
/**
* FileWriter对象被用作参数来创建一个BufferedWriter对象。
* 这样,就可以通过BufferedWriter来写入字符,而实际的写入操作(包括可能的缓冲)将由BufferedWriter处理。
*/
val bw = new BufferedWriter(new FileWriter("scala/data/words2.txt"))
bw.write("写入数据!")
// newLine()方法用于写入一个行分隔符
bw.newLine()
bw.write("太棒了!")
//flush()方法用于将缓冲区中的数据强制写入到底层输出流(如FileWriter)中,并清空缓冲区。
bw.flush()
//TODO 纯scala中没有写文件的方式!!
}
异常抛出(与java中很像)
手动抛出异常:
val sc = new Scanner(System.in)
print("输入除数:")
val cs: Int = sc.nextInt()
if(cs!=0){
println(10/cs)
}else{
throw new ArithmeticException("您输入的除数是0")
}
使用try、catch捕获异常
def main(args: Array[String]): Unit = {
/**
* scala中的异常和java的很像
*/
try {
println(10/2)
val arr1: Array[Int] = Array(1, 2, 3, 4, 5)
println(arr1(2))
val br: BufferedReader = new BufferedReader(new FileReader("scala/data/words888.txt"))
val sc = new Scanner(System.in)
print("输入除数:")
val cs: Int = sc.nextInt()
println(10/cs)
// 异常被捕获后,后续代码都可以运行
}catch{
//类似于sql语句中case when
// 使用case来选择抛出的异常
case e:ArithmeticException=>
println("除0异常")
e.printStackTrace()
case e:ArrayIndexOutOfBoundsException=>
println("数组越界异常")
// TODO _ : 表示所有的异常都可以抛出,相当于Exception
case _ =>
println("出现异常")
}finally {
//TODO 用于确保无论是否发生异常,都会执行一段代码。
// 今后finally中的处理大部分情况下都与释放资源有关
println("这是finally代码块")
}
println("hello world")
}
scala中的函数
/**
* def: 定义函数或者方法的关键字
* main: 是函数或者方法的名字,符合标识符的命名规则
* args: 函数形参的名字
* Array[String]: 参数的数据类型是一个元素为字符串的数组
* =: 后面跟着函数体(与Java中不同之处)
* Unit: 等同于java中的void 表示无返回值的意思
*
*形式:
* def main(args: Array[String]): Unit = {
*
* }
*
* 在不同的地方定义,称呼不一样
* 函数:在object中定义的叫做函数
* 方法:在class中定义的叫做方法
*/
object Demo5Function {
def main(args: Array[String]): Unit = {
//调用函数
val res1: Int = add(3, 4)
println(res1)
// scala中的函数可以嵌套定义,函数中可以再定义函数
def plus(x: Int, y: Int): Int = {
return x + y
}
//调用必须在定义之后
val res2: Int = plus(10, 20)
println(res2)
// 函数无法成功调用
// val res3: Int = add2(11, 22)
// println(res3)
val d1: Demo1 = new Demo1()
val res4: Int = d1.add2(11, 22)
println(res4)
//调用形式1:object中的函数可以使用类名调用,类似于静态一样
val res5: Int = Demo5Function.add(100, 200)
println(res5)
//调用形式2:object中的函数调用时,可以省略类名
val res6: Int = add(200, 300)
println(res6)
val res7: Int = fun1("1000")
println(res7)
//TODO 如果方法调用的函数只有一个参数的时候,可以将.和小括号用空格代替调用
val res9: Int = Demo5Function.fun1("1000")
val res8: Int = Demo5Function fun1 "1000" // "=" * 50 -> "=".*(50)
println(res8)
//TODO 如果定义的时候,没有小括号,调用的时候,就不需要加小括号(无需传入参数)
show
}
//定义格式1:如果函数有返回值,且最后一句话作为返回值的话,return关键字可以不写
def add3(a1: Int, b1: Int): Int = {
a1 + b1
}
//定义格式2:如果函数体中只有一句实现,那么大括号也可以不写
def add4(a1: Int, b1: Int): Int = a1 + b1
//定义格式3:如果函数没有参数的时候,小括号省略不写
def show= println("好好学习,天天向上!")
//需求1:定义一个求两个数之和的函数,返回结果
def add(a1: Int, b1: Int): Int = {
return a1 + b1
}
def fun1(s:String): Int = {
return s.toInt
}
}
//TODO 函数或者方法必须定义在class或者object中,否则将会报错,无法进行编译
//def add2(a1: Int, b1: Int): Int = {
// return a1 + b1
//}
class Demo1{
//这里叫方法,将来调用时需要创建该类的对象才可以调用
def add2(a1: Int, b1: Int): Int = {
return a1 + b1
}
}
递归调用
/**
* scala中的函数也可以递归
* 方法定义时,自身调用自身的现象
*
* 条件:要有出口(停止递归调用条件),不然就是死递归
*/
object Demo6Function {
def main(args: Array[String]): Unit = {
//求阶乘 5!
val res1: Int = factorial(5)
println(s"5的阶乘是$res1")
println(s"5的阶乘是${Demo6Function factorial 5}")
}
def factorial(number: Int): Int = {
if (number == 1) {
1
} else {
number * factorial(number - 1)
}
}
}
scala中定义class类
object Demo7Class {
def main(args: Array[String]): Unit = {
// val s1: Student = new Student()
// val s1: Student = new Student("张三",18)
val s2: Student = new Student("张三", 18, "男")
println(s2)
//如果调用的是一个类的无参构造方法,new的时候小括号可以不用写
val s3: Student2 = new Student2
s3.fun1()
//也可以使用多态的方式创建对象
val s4:Object = new Student("张三111", 19, "男")
// s4.fun1()
println(s4.toString)
}
}
/**
* 可以在scala程序定义类
* 类:构造方法 成员方法 成员变量
*
* 构造方法:
* 1、在scala中构造方法的编写和在java中不太一样,类所拥有的大括号中都是构造代码块的内容
* 2、默认情况下,每一个类都应该提供一个无参的构造方法
* 3、构造方法可以有许多
*/
class Student(name: String, age: Int) {
/**
* 定义成员变量
*/
val _name: String = name
val _age: Int = age
// _: 这个下划线,就表示将来不传值时,会赋予其默认值。String的默认值是一个特殊的值,即null
var _gender: String = _
/**
* 构造方法也可以写多个
*/
// TODO def this () :为重载的构造器,有着不同的参数列表,
// 在创建类的对象时,若传递三个参数,则会使用该构造方法进行初始化对象
def this(name: String, age: Int, gender: String) {
/**
* this():
* 用于在辅助构造器中调用主构造器或其他辅助构造器,
* 以确保对象被正确初始化。需要注意的是,this(...)调用必须是构造器体中的第一条语句。
*/
this(name: String, age: Int)
_gender = gender
}
// println("好好学习,天天向上!")
/**
* 也可以重写方法
* 此处定义的类的父类都是Object,重写继承自父类的toString方法
*/
override def toString: String = {
// 使用s"${}"的形式会报错
"姓名:" + _name + ", 年龄:" + _age + ", 性别:" + _gender
}
// override def toString: String = super.toString
}
class Student2{
def fun1()={
println("666")
}
}
样例类
/**
* scala提供了一个非常好用的功能:样例类
* 较少用户创建类所编写代码量,只需要定义成员变量即可,自动扩充成员变量,构造方法,重写toString方法
*/
object Demo8CaseClass {
def main(args: Array[String]): Unit = {
val t1 = new Teacher("小虎", 16, "学习")
println(t1)
println(t1.name)
println(t1.age)
println(t1.like)
t1.like = "敲代码"
println(t1)
}
}
/**
* 样例类中的成员变量,编译后默认是被jvm添加了final关键字,用户是改变不了的
* 对于scala来说,默认是被val修饰的
* 如果将来想要被改变,定义的时候需要使用var进行修饰
*/
case class Teacher(name:String,age:Int,var like:String)
伴生对象(apply方法)
object Demo9Apply {
def main(args: Array[String]): Unit = {
val b: Book1 = new Book1()
b.apply() // 定义在class中是一个普通的方法
// TODO: 若定义在object中,那么可以直接用Book("中华上下五千年", 999)的形式来调用这个方法
val b1: Book = Book("中华上下五千年", 999)
println(b1)
}
}
class Book1 {
def apply(): Unit = {
println("哈哈哈")
}
}
// TODO object Book 为 class Book的伴生对象
object Book {
def apply(name:String,price:Int): Book = {
new Book(name,price)
}
}
class Book(name: String, price: Int) {
val _name: String = name
val _price: Int = price
override def toString: String = "书名:" + _name + ", 价格:" + _price
}
scala面向函数式编程
/**
* scala中的函数式编程
*
* 面向对象编程:将对象当作参数一样传来传去
* 1、对象可以当作方法参数传递
* 2、对象也可以当作方法的返回值返回
* 当看到类,抽象类,接口的时候,今后无论是参数类型还是返回值类型,都需要提供对应的实现类对象
*
* 面向函数式编程:将函数当作参数一样传来传去
* 1、函数A当作函数B的参数进行传递
* 2、函数A当作函数B的返回值返回
*
* 在scala中,将函数也当作一个对象,对象就有类型
* 函数在scala也有类型的说法
* 函数的类型的形式为:
* 参数类型=>返回值类型
*
*/
将函数当作对象,赋值给类型是函数类型的变量
//是一个参数为字符串类型,返回值是整数类型的函数
def fun1(s: String): Int = {
s.toInt + 1000
}
val res1: Int = fun1("1000")
println(res1)
//定义变量的方式,定义一个函数
//将函数当作对象,赋值给类型是函数类型的变量,将来可以直接通过变量调用函数
val fun2: String => Int = fun1
val res2: Int = fun2("2000")
println(res2)
/**
* 函数A作为函数B的参数定义
*
* 本质上是将函数A的处理逻辑主体传给了函数B,在函数B中使用这个处理逻辑
*/
// show1 show2 相当于函数A
// fun1 相当于函数B
//定义
def fun1(f: String => Int): Int = {
val a1: Int = f("1000")
a1 + 3000
}
def show1(s:String): Int = {
s.toInt
}
//调用
val res1: Int = fun1(show1)
println(res1)
def show2(s: String): Int = {
s.toInt+11111
}
val res2: Int = fun1(show2)
println(res2)
//定义一个函数fun1, 函数的参数列表中,既有正常的类型参数,也有函数类型的参数
def fun1(s: String, f: String => Int): Int = {
val a1: Int = f(s)
a1 + 1000
}
def show1(s: String): Int = {
s.toInt
}
def show2(s: String): Int = {
s.toInt + 1111
}
//.....
val res1: Int = fun1("2000", show2)
println(res1)
//使用lambda表达式改写函数作为参数传递的调用形式:(s: String) => s.toInt
fun1("2000", (s: String) => s.toInt)
fun1("2000", (s: String) => s.toInt+1000)
//在scala中,数据类型可以自动类型推断
fun1("2000", s => s.toInt+1000)
//如果当作参数的函数的参数只在函数主体使用了一次,那么可以使用_代替
fun1("2000", _.toInt+1000)
val res2: Int = fun1("2000", _.toInt+1000)
println(res2)
函数当作参数传递的应用
object Demo11Fun {
def main(args: Array[String]): Unit = {
val arr1: Array[Int] = Array(11, 22, 33, 44, 55)
// for循环输出数组
for (e <- arr1) {
println(e)
}
// 定义一个函数
def fun1(i: Int): Unit = {
println(i*2)
}
//def foreach[U](f: A => U): Unit
//foreach函数需要一个参数,它和数组元素一样的类型,返回值是Unit的函数
//foreach函数的主要作用是将调用该方法的序列中的元素,依次取出并传递给传入的函数进行处理
arr1.foreach(fun1)
// scala自带的一个函数
def println(x: Any) = Console.println(x)
// Any可以接收任意的数据类型元素
arr1.foreach(println)
}
}
函数当作返回值返回
//定义返回值是函数的函数方式1:
def fun1(s1: String): String => Int = {
def show(s: String): Int = {
s.toInt + s1.toInt
}
show
}
val resFun1: String => Int = fun1("1")
val res1: Int = resFun1("1000")
println(res1)
//定义方式2(是方式1的简化写法):
/**
* 方式2这种将参数分开定义,今后调用时可以分开传递,这种做法,在scala中叫做函数柯里化
*
* 面试题:什么是函数柯里化?
* 1、本身是一个数学界的一个名词,本意是原来一次传递多个参数,现在被改成了可以分开传递的形式,这种做法叫做柯里化
* 2、在scala中体现柯里化,指的是函数的返回值也是一个函数,将来调用时参数可以分开传递。
* 3、提高了程序的灵活性和代码复用性
* 4、在scala中也可以通过偏函数实现参数分开传递的功能
*/
def fun1(s1: String)(s: String): Int = {
s.toInt + s1.toInt
}
//调用函数的返回值是函数的方式1:
val resFun1: String => Int = fun1("1")
val r1: Int = resFun1("11")
println(r1)
val r2: Int = resFun1("12")
println(r2)
val r3: Int = resFun1("13")
println(r3)
//调用方式2:
val res2: Int = fun1("1")("1000")
println(res2)
def function1(s1: String, s2: String): Int = {
s1.toInt + s2.toInt
}
val res1: Int = function1("1", "1000")
println(res1)
/**
* 偏函数
*/
//TODO 将第二个参数用 _ 代替,则会返回一个函数(由底层代码进行操作)
val f1: String => Int = function1("1", _)
val res1: Int = f1("1000")
val res2: Int = f1("2000")
val res3: Int = f1("3000")
println(s"res1:$res1,res2:$res2,res3:$res3")