文章目录
I know, i know
地球另一端有你陪我
一、Hello World
Scala 是 Scalable Language 的简写,是一门多范式的编程语言
Scala是把函数式编程思想和面向对象编程思想结合的一种编程语言
在函数式编程中,函数是基本单位,他几乎被用作一切,包括最简单的计算,甚至连变量都被计算所取代;
在函数式编程中,变量只是一个名称,而不是一个存储单元,这是函数式编程与传统的命令式编程最典型的不同之处。
大数据计算引擎 Spark 由 Scala 编写
从 Hello World 开始
我们知道,依赖于 JVM 的Java 程序,函数的入口是 main 方法
而它的修饰词是固定的(public static void)
而 Scala 中是没有这些修饰词的,取而代之的是 object
被他修饰的类中,所有的成员都将是静态的(static)
package day65
/*
类在被加载到 jvm 中,会在内存中存在一个该类的“类对象”,
该对象有且只有只有一个(单例模式)
通过该对象可以直接调用方法中的静态成员
scala 和 java 一样,需要先编译,在 jvm 中运行
jvm 需要 main函数,而 main 函数必须是由 public 和 static 修饰
scala 中并没有这个修饰词,于是提供了一个object,
由object修饰的类中所有的成员都是静态的,可以通过“类对象”直接调用
由class修饰的类中所有的成员都是普通的,需要类的对象进行调用
*/
object ScalaDemo1 {
// 程序的入口 main
def main(args: Array[String]): Unit = {
print("Hello World")
}
}
1、变量的定义 基本数据类型 结构体
定义变量时都要加上 val(不可变) 或 var(可变)
Scala 本质依然依赖大部分 Java,所以保留了很多 Java 中有的元素,
包括八个基本数据类型
package day65
import java.io.{BufferedReader, FileReader}
import java.sql.{Connection, DriverManager, PreparedStatement, ResultSet}
import scala.io.{BufferedSource, Source}
object ScalaDemo2 {
def main(args: Array[String]): Unit = {
//val 不可变
//var 可变
val a = 1
var b = 10
b = 11
println(a)
println(b)
/*
基本数据类型(首字母需要大写)
Byte Short Int Long
Float Double
Character
Boolean
*/
// 定义变量
val byte1: Byte = 1
val short1: Short = 1
val int1: Int = 1
val long1: Long = 1
val float1: Float = 1
val double1: Double = 1
val bool1: Boolean = true
val bool2: Boolean = false
val char1: Char = 'a'
// String 是引用类型
val string1: String = "fgh 韭菜盒子"
/*
隐式转换
由于scala的底层依然是java,所以本身涵盖了大本分java方法
而scala中新增了很多方法,这种额外添加方法的动作叫做隐式转换
后面会详细说
*/
val splits: Array[String] = string1.split(" ")
// 通过下标取需要用小括号
println(splits(0))
println(splits(1))
//拼接字符串
val str1: String = "123"
val str2: String = "456"
// 直接 +
val str3 = str1 + str2
println(str3)
// 利用StringBuilder或Buffer
val builder: StringBuilder = new StringBuilder()
builder.append(str1).append(str2)
println(builder)
// scala特有方式,类似引用
// 底层依然是StringBuilder
// 建议加大括号,当首个字符是_,不加会出错
println(s"${str1}${str2}")
// 类型转换
// 通过Java中的方法
val int2: Int = 1
val str4: String = String.valueOf(int2)
val int5: Integer = Integer.valueOf(str4)
// 通过scala中的方法,注意没有括号
val srt5 = int2.toString
val byte5 = int2.toByte
val short5 = int2.toShort
// 关于 *
// 这里的*是一个方法
// scala 中的方法调用也可以也不加上 "."
println("*".*(100))
println("*" * 100)
// 选择结构
if(1>2){
println(">")
} else if(1==2){
println("=")
} else {
println("<")
}
// 循环
var cnt:Int = 1
var sum:Int = 0
while (cnt <= 100){
sum += cnt
cnt += 1
}
println(sum)
do{
println("说啥都要打印")
}while(false)
val str6:String = "1,2,3,4,5"
val array1: Array[String] = str6.split(",")
// for each 循环
for (elem <- array1) {
println(elem)
}
// scala 采用函数式编程来遍历
// 将函数作为参数,注意不加()
array1.foreach(println)
array1.map(print)
// 文件读写
// java的方法
val br: BufferedReader =
new BufferedReader(new FileReader("data/students.txt"))
var line:String = br.readLine()
//while((line=br.readLine())!=null) scala不能解析这句
while (line!= null){
System.out.println(line)
line = br.readLine()
}
println("*" * 100)
// scala io 的方式
// 也可以读url等等
val source: BufferedSource = Source.fromFile("data/students.txt")
// 可以直接输出
source.foreach(print)
// 也可以弄一个迭代器慢慢玩
val lines: Iterator[String] = source.getLines()
for (elem <- lines) {
println(elem)
}
println("*" * 100)
// 链式编程
Source
.fromFile("data/students.txt")
.getLines()
.foreach(println)
// 连接 mysql
val conn: Connection = DriverManager.getConnection
("jdbc:mysql://master:3306/student","root","123456")
val ps: PreparedStatement = conn.prepareStatement
("select * from student where age<?")
ps.setString(1,"23")
val rs: ResultSet = ps.executeQuery()
val id: String = rs.getString("id")
val name: String = rs.getString("name")
val age: Int = rs.getInt("age")
println(s"${id},${name},${age}")
ps.close()
conn.close()
}
}
二、面向对象
1、类
package day65
/*
面向对象
类
*/
class student(id: String, name: String) {
// 关于构造方法。这个类{}中就是构造方法
// 需要在类名处就定义好参数
// 即此处的 id name age
// 在构造方法的赋值时,为了不冲突,一般会加一个_
//不能使用this.id = id 的方法
val _id: String = id
val _name: String = name
var _age: Int = _
// _下划线表示稍后赋值,定义时必须声明类型,以及可变
// alt+ins 可以快捷重写超类object已经定义好的类,例如 toString
// 可以发现这种格式已经接近变量
// 体现出了scala中面向函数的特点
// 自动带出来的会没有{},记得加上
override def toString = s"student(${_id}, ${_name}, ${_age})"
//原型是
/*
override def toString():String={
return s"student($_id, $_name, $_age)"
}
不写return会默认返回最后一行,可以省略
返回值类型可以推断,也可以省略,代码一行,{}也可以省略
没有参数,()也可以省略
*/
// 如果想要重载多个构造方法,需要使用 this 有点类似构造方法的继承
def this(id: String, name: String, age: Int) {
// 首先需要调用默认构造方法
this(id, name)
_age = age
// 且赋值的时候不能加类型
// 感觉像是只能越写越长,所以第一个构造方法很重要
}
}
// 不同修饰词下的同名类可以共存
object student {
def main(args: Array[String]): Unit = {
// 创建对象
val stu1: student = new student("1001", "韭菜盒子")
val stu2: student = new student("1002", "fgh", 21)
// 取对象的属性
println(stu1._name)
println(stu1._age)
println(stu1.toString)
println(stu2 toString)
println(stu1)
}
}
2、继承、多态(一点点)
package day65
class A(id: String, name: String) {
val _id: String = id
val _name: String = name
override def toString = s"A(_id=${_id}, _name=${_name})"
def printAB(): Unit ={
println("A")
}
}
// 继承的时候要带上父类的属性(像是子类构造方法的初始化)
class B(id: String, name: String, age: Int) extends A(id, name) {
// 初始化只需要对多出来成员的进行
val _age: Int = age
override def printAB(): Unit ={
println("B")
}
//重写父类方法
override def toString =
s"B(_id=${_id}, _name=${_name},_age=${_age})"
}
object ScakaDemo4 {
def main(args: Array[String]): Unit = {
val a: A = new A("1001", "A")
val b: B = new B("1002", "B", 21)
println(a)
// 继承父类方法
println(b)
a.printAB()
b.printAB()
// 多态
def printAABB(a: A): Unit ={
a.printAB()
}
printAABB(a)
printAABB(b)
}
}
3、样例类(case class)
可以说是为了懒人特地设计好的,已经封装好绝大部分常用方法
只需要创建时定义好成员属性,类用 case class 修饰
package day65
// 样例类
// 在样例类中定义的参数 默认由val修饰 不可以改变
// 如果需要对属性重新赋值 则需要在定义的时候 加上 var修饰
// 会自动实现get set(针对var修饰的属性)方法,
// 还会实现序列化接口(可以在网络中进行传输)
case class Student(var id:String
,name:String
,gender:String)
object ScalaDemo5{
def main(args: Array[String]): Unit = {
// 创建样例类的对象
// new关键字可以省略
val stu1 = Student("1001","fgh","male")
println(stu1.id)
stu1.id = "1000"
println(stu1.id)
}
}
4、apply (伴生对象)
在定义类的时候,可以定义 apply 方法
作用是直接可以通过类名(属性)直接返回一个当前属性的类对象(省个 new)
样例类中已定义好,可以直接使用
package day65
object ScalaDemo6{
def main(args: Array[String]): Unit = {
val aa = new AA("1001")
val bb = new BB("1002")
// apply 方法,可以通过调用类名默认调用
// 创建时系统也默认写为对象的创建
val bb2 = BB("1003")
}
}
class AA(id:String) {
val _id = id
}
class BB(id:String){
val _id = id
}
// 该类称为 BB 的伴生对象
object BB {
def apply(id: String): BB = new BB(id)
}
三、面向函数
1、函数定义
package day65
object test {
// scala 允许一个文件中包含多个main方法
// 调用方法尽可能在main方法的内部,
// 否则会先运行main方法外的函数调用
// 同一文件夹下不能有同名的 object
def main(args: Array[String]): Unit = {
def a(): Unit = {
println("a")
}
a()
}
def b(): Unit = {
println("b")
}
b()
}
object ScalaDemo7 {
/*
* 函数(方法)可以定义在什么位置?
* 类中、Object中、方法中
*/
def main(args: Array[String]): Unit = {
/*
def 这是一个函数
func1 函数名
str1: String, str2: String 函数的参数
Unit: 函数的返回值类型,这里相当于void
{} : 方法体
等号别忘了
*/
def fun1(str1: String, str: String): Unit = {
print("fgh")
}
def fun2(int: Int): Int = {
return int + 1
}
// 1、有返回值的函数,不写 return 默认返回最后一行
def fun3(int: Int): Int = {
int + 1
}
// 2、只有一行代码的函数,{}可以省略
def fun4(int: Int): Int = int + 1
// 3、程序会推断返回值的类型,可以省略
def fun5(int: Int) = int + 1
// 4、倘若函数没有参数列表,那么括号也可以省略
// 虽然很怪,但是他是一个没有参数的函数
def fun6 = 1 + 1
println(fun1("fgh","fgh"))
println(fun2(1))
println(fun3(1))
println(fun4(1))
println(fun5(1))
println(fun6)
}
}
2、函数式编程 — 函数作为参数
package day65
object ScalaDemo8 {
/*
函数式编程(高阶函数、面向函数编程)
面向对象编程:把对象传来传去
注意:对象作为 参数 和 返回值 的时候有类型的限制
面向函数编程:把函数传来传去
注意:函数作为 参数 和 返回值 的时候也有类型的限制
函数式编程的分类:
1、以函数作为参数
2、以函数作为返回值
如何描述一个函数fun
参数类型 => 返回值类型
*/
def main(args: Array[String]): Unit = {
// 此处是将函数作为参数进行传参
// 而不是进行调用,因此不能加括号!
println(fun2S(fun2))
// 调用匿名函数
println(funX("1", 1))
// 使用匿名函数来传参 fun: String => Int
println(fun2S((str: String) => {
str.toInt + 1
}))
// 1、函数体只有一行代码,花括号可以省略
println(fun2S((str: String) => str.toInt + 1))
// 2、如果匿名函数作为另一个函数的参数传入 参数的类型可以省略
println(fun2S(str => str.toInt + 1))
// 3、如果参数只被使用的一次 则可以省略 并用下划线_替代
println(fun2S(_.toInt + 1))
}
def fun1(int: Int): Int = int + 1
def fun2(str: String): Int = str.toInt + 1
// 这个函数的参数就是一个函数
// 其形参需要是String 返回值需要是Int (可以是他们的父类)
def fun2S(fun: String => Int): Int = {
val i = fun("100")
// 默认返回最后一行
i
}
// 匿名函数
// => 左边是参数列表记得加小括号,右边是{方法体}
(str: String, int: Int) => {
str.toInt + int
}
// 也可以用一个变量去接收
val funX: (String, Int) => Int = (str: String, int: Int) => {
str.toInt + int
}
}
3、函数式编程的一些小小引用参观
用 Scala 自带的遍历数组的方式进行演示
package day65
object ScalaDemo9 {
def main(args: Array[String]): Unit = {
// 创建数组
val arr: Array[Int] = Array[Int](1,2,3)
// 遍历输出
// 方法一
var i:Int = 0
while (i < arr.length){
// scala 中下标用小括号
println(arr(i))
i += 1
}
// 方法二
// foreach方法:需要传入一个函数f:
// 参数类型为Int,返回值类型为Unit
// foreach方法会将array中的每一个元素依次作为参数传递给函数f
// println是一个函数:参数类型为Any,返回值类型为Unit
// 这里就是将数组中的每一个Int ,传入到 println 当中,多态
// Any是任意类型的基类
// AnyRef:任意引用类型的基类
// AnyVal:任意值类型的基类
arr.foreach(println)
// 方法三
for (elem <- arr) {
println(elem)
}
// // 方法四
// for(i <- 0 to arr.length - 1){
// println(arr(i))
// }
//
// for(i <- 0 until arr.length){
// println(arr(i))
// }
//
// for(i <- Range(0,arr.length)){
// println(arr(i))
// }
// 将数组中每个元素翻倍
// 方法一 在循环中挨个 * 2
def double(int: Int): Unit = int * 2
arr.foreach(double)
// mkString 类似 Python 中的 join
// 能够将容器中的元素按照指定分隔符进行拼接
println(arr.mkString(","))
// 问题是 foreach 没有返回值,无法得到翻倍后的数组
// 这里可以使用 map 方法,和 foreach 的唯一区别是有返回值
def double2(int: Int):Int = int * 2
// 会返回一个新对象
val arr2: Array[Int] = arr.map(double2)
println(arr2.mkString(","))
// 也可以使用匿名函数
arr.map( int=>int * 2)
println(arr.map(_ * 2).mkString(","))
}
}
4、函数式编程 — 函数作为返回值
package day65
object ScalaDemo10 {
def main(args: Array[String]): Unit = {
val f1 = fun1(1)
val str = f1(1)
println(str)
// 也可以一步到位
println(fun1(1)(1))
pow1(2,5)
// 固定参数,底数相同,运算不同次幂
val a_2: Int => Unit = pow2(2)
a_2(1)
a_2(2)
a_2(3)
}
// 以函数作为返回值时 返回值类型需要手动指定
def fun1(int: Int): Int => String = {
def fun2(int: Int): String = {
val str: String = "fgh"
// 对应fun2 :String
str
}
// 对应fun1 :Int => String
fun2
}
// 两个参数的函数
def fun3(int1: Int,int2: Int)={
println("fgh")
}
// 柯里化
// 像是将参数拆开,能够将原本一个函数拆成N个
// 特定场合可以用来固定参数
def fun4(int1: Int)(int2: Int)={
println("fgh")
}
// 幂运算
// 方法一 可以使用默认参数
def pow1(a: Int,b: Int =2)={
println(Math.pow(a,b))
}
// 方法二
def pow2(a:Int)(b:Int)={
println(Math.pow(a,b))
}
}
零碎
Scala 经过反编译后,会成为一个 Java 函数
并且变量会被反编译为函数
Any是任意类型的基类
AnyRef:任意引用类型的基类
AnyVal:任意值类型的基类
Nothing 无参数
B 任意类型返回值
Unit 无返回值
GenTraversable 容器
Any 是所有类的父类(一个始祖的概念)
Nothing 是所有类的子类(一个倒霉孙子的概念)
foreach 无返回值
map 有返回值