字符串输出
规则
(1) 字符串,通过+号连接
(2) printf 用法:字符串,通过%传值。
(3) 字符串模板(插值字符串):通过$获取变量值
案例实操
def main(args: Array[String]): Unit = {
var name: String = "jinlian"
var age: Int = 18
//(1)字符串,通过+号连接
println(name + " " + age)
//(2)printf 用法字符串,通过%传值。
printf("name=%s age=%d\n", name, age)
//(3)字符串,通过$引用
//多行字符串,在Scala 中,利用三个双引号包围多行字符串就可以实现。
//输入的内容,带有空格、\t 之类,导致每一行的开始位置不能整洁对齐。
//应用 scala 的 stripMargin 方法,在 scala 中 stripMargin 默认是“|”作为连接符,
//在多行换行的行头前面加一个“|”符号即可。
val s =
"""
|select
| name,
| age
|from user
|where name="zhangsan"
""".stripMargin
println(s)
//如果需要对变量进行运算,那么可以加${}
val s1 =
s"""
|select
| name,
| age
|from user
|where name="$name" and age=${age+2} """.stripMargin
println(s1)
val s2 = s"name=$name"
println(s2)
}
数据类型(重点)
Java数据类型
Java基本类型:char、byte、short、int、long、float、double、boolean
Java引用类型:(对象类型)
由于Java有基本类型,而且基本类型不是真正意义的对象,即使后面产生了基本类型的包装类,但是仍 然存在基本数据类型,所以Java语言并不是真正意思的面向对象。
Java基本类型的包装类:Character、Byte、Short、Integer、Long、Float、Double、Boolean
注意:Java中基本类型和引用类型没有共同的祖先。
Scala数据类型
1) Scala中一切数据都是对象,都是Any的子类。
2) Scala中数据类型分为两大类:数值类型(AnyVal)、 引用类型(AnyRef),不管是值类型还是引用类型都是对象。
3) Scala数据类型仍然遵守,低精度的值类型向高精度值类型,自动转换(隐式转换)
4) Scala中的StringOps是对Java中的String增强
5) Unit:对应Java中的void,用于方法返回值的位置,表 示方法没有返回值。Unit是一个数据类型,只有一个对象 就是()。Void不是数据类型,只是一个关键字
6) Null是一个类型,只有一个对象就是null。它是所有引用类型(AnyRef)的子类。
7) Nothing,是所有数据类型(应用类型和数值类型)的子类,主要用在一个函数没有明确返回值时使 用,因为这样我们可以把抛出的返回值,返回给任何的变量或者函数。
整数类型
数据类型 | 描述 |
---|---|
Byte [1] | 8 位有符号补码整数。数值区间为 -128 到 127 |
Short [2] | 16 位有符号补码整数。数值区间为 -32768 到 32767 |
Int [4] | 32 位有符号补码整数。数值区间为 -2147483648 到 2147483647 |
Long [8] | 64 位有符号补码整数。数值区间为 -9223372036854775808 到 9223372036854775807 = 2 的(64-1)次方-1 |
Unit 类型、Null 类型和Nothing 类型(重点)
基本说明
数据类型 | 描述 |
---|---|
Unit | 表示无值,和其他语言中 void 等同。用作不返回任何结果的方法的结果 类型。Unit 只有一个实例值,写成()。 |
Null | null , Null 类型只有一个实例值 null |
Nothing | Nothing 类型在 Scala 的类层级最低端;它是任何其他类型的子类型。当一个函数,我们确定没有正常的返回值,可以用 Nothing 来指定返回类型,这样有一个好处,就是我们可以把返回的值(异常)赋给其它的函数 或者变量(兼容性) |
案例实操
- Unit 类型用来标识过程,也就是没有明确返回值的函数。由此可见,Unit 类似于 Java 里的void。Unit 只有一个实例——( ),这个实例也没有实质意义
object TestSpecialType {
def main(args: Array[String]): Unit = {
def sayOk : Unit = {// unit 表示没有返回值,即 void
}
println(sayOk)
}
}
- Null 类只有一个实例对象,Null 类似于 Java 中的 null 引用。Null **可以赋值给任意引用类型(AnyRef),但是不能赋值给值类型(**AnyVal)
object TestDataType {
def main(args: Array[String]): Unit = {
//null 可以赋值给任意引用类型(AnyRef),但是不能赋值给值类型(AnyVal)
var cat = new Cat(); cat = null // 正 确
var n1: Int = null // 错 误
println("n1:" + n1)
}
}
class Cat {
}
- Nothing,可以作为没有正常返回值的方法的返回类型,非常直观的告诉你这个方法不会正常返回,而且由于 Nothing 是其他任意类型的子类,他还能跟要求返回值的方法兼容。
object TestSpecialType {
def main(args: Array[String]): Unit = {
def test() : Nothing={
throw new Exception()
}
test
}
}
数值类型自动转换
当Scala 程序在进行赋值或者运算时,精度小的类型自动转换为精度大的数值类型,这个就是自动类型转换(隐式转换)。数据类型按精度(容量)大小排序为:
基本说明
(1) 自动提升原则:有多种类型的数据混合运算时,系统首先自动将所有数据转换成精度大的那种数据类型,然后再进行计算。
(2) 把精度大的数值类型赋值给精度小的数值类型时,就会报错,反之就会进行自动类型转换。
(3)(byte,short)和 char 之间不会相互自动转换。
(4)byte,short,char 他们三者可以计算,在计算时首先转换为 int 类型。
案例实操
object TestValueTransfer {
def main(args: Array[String]): Unit = {
//(1)自动提升原则:有多种类型的数据混合运算时,系统首先自动将所有数据转换成精度大的那种数值类型,然后再进行计算。
var n = 1 + 2.0
println(n) // n 就 是 Double
//(2)把精度大的数值类型赋值给精度小的数值类型时,就会报错,反之就会进行自动类型转换。
var n2 : Double= 1.0
//var n3 : Int = n2 //错误,原因不能把高精度的数据直接赋值和低
精度。
//(3)(byte,short)和 char 之间不会相互自动转换。
var n4 : Byte = 1
//var c1 : Char = n4 //错误var n5:Int = n4
//(4)byte,short,char 他们三者可以计算,在计算时首先转换为 int类型。
var n6 : Byte = 1
var c2 : Char = 1
// var n : Short = n6 + c2 //当 n6 + c2 结果类型就是int
// var n7 : Short = 10 + 90 //错误
}
}
关系运算符
Java 和Scala 中关于==的区别
Java
==比较两个变量本身的值,即两个对象在内存中的首地址;
equals 比较字符串中所包含的内容是否相同。
public static void main(String[] args) {
String s1 = "abc";
String s2 = new String("abc"); System.out.println(s1 == s2);
System.out.println(s1.equals(s2));
}
输出结果:
false
true
Scala
==更加类似于 Java 中的 equals,参照 jd 工具
def main(args: Array[String]): Unit = {
val s1 = "abc"
val s2 = new String("abc")
println(s1 == s2)
println(s1.eq(s2))
}
输出结果:
true
false
注意:Scala 中没有++、–操作符,可以通过+=、-=来实现同样的效果;
Scala 运算符本质
在 Scala 中其实是没有运算符的,所有运算符都是方法。
1) 当调用对象的方法时,点.可以省略
2) 如果函数参数只有一个,或者没有参数,()可以省略
object TestOpt {
def main(args: Array[String]): Unit = {
// 标准的加法运算
val i:Int = 1.+(1)
// (1)当调用对象的方法时,.可以省略
val j:Int = 1 + (1)
// (2)如果函数参数只有一个,或者没有参数,()可以省略
val k:Int = 1 + 1
println(1.toString())
println(1 toString())
println(1 toString)
}
}
流程控制
IF流程
与Java类似
For 循环控制
Scala 也为 for 循环这一常见的控制结构提供了非常多的特性,这些 for 循环的特性被称为 for 推导式或 for 表达式。
范围数据循环(To)
基本语法
for(i <- 1 to 3){
print(i + " ")
}
println()
说明
(1) i 表示循环的变量,<- 规定 to
(2) i 将会从 1-3 循环,前后闭合
范围数据循环(Until)
基本语法
for(i <- 1 until 3) { print(i + " ")
}
println()
说明
(1) 这种方式和前面的区别在于 i 是从 1 到 3-1
(2) 即使前闭合后开的范围
循环守卫
基本语法
for(i <- 1 to 3 if i != 2) {
print(i + " ")
}
println()
说明
(1) 循环守卫,即循环保护式(也称条件判断式,守卫)。保护式为 true 则进入循环体内部,为false 则跳过,类似于continue。
(2) 上面的代码等价于
for (i <- 1 to 3){
if (i != 2) {
print(i + " ")
}
}
循环步长
基本语法
for (i <- 1 to 10 by 2) {
println("i=" + i)
}
说明
by 表示步长
案例实操
需求:输出 1 到 10 以内的所有奇数
for (i <- 1 to 10 by 2) {
println("i=" + i)
}
输出结果
i=1
i=3
i=5
i=7
i=9
嵌套循环
基本语法
for(i <- 1 to 3; j <- 1 to 3) {
println(" i =" + i + " j = " + j)
}
说明
没有关键字,所以范围后一定要加;来隔断逻辑
上面的代码等价
for (i <- 1 to 3) {
for (j <- 1 to 3) {
println("i =" + i + " j=" + j)
}
}
引入变量
基本语法
for(i <- 1 to 3; j = 4 - i) {
println("i=" + i + " j=" + j)
}
说明
(1) for 推导式一行中有多个表达式时,所以要加 ; 来隔断逻辑
(2) for 推导式有一个不成文的约定:当 for 推导式仅包含单一表达式时使用圆括号, 当包含多个表达式时,一般每行一个表达式,并用花括号代替圆括号,如下
for {
i <- 1 to 3
j = 4 - i
} {
println("i=" + i + " j=" + j)
}
循环返回值
基本语法
val res = for(i <- 1 to 10) yield i
println(res)
说明
将遍历过程中处理的结果返回到一个新 Vector 集合中,使用 yield 关键字。
案例实操
需求:将原数据中所有值乘以 2,并把数据返回到一个新的集合中。
object TestFor {
def main(args: Array[String]): Unit = {
var res = for(i <-1 to 10) yield
{
i * 2
}
println(res)
}
}
输出结果:
Vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
倒序打印
说明
如果想倒序打印一组数据,可以用 reverse。
案例实操
需求:倒序打印 10 到 1
for(i <- 1 to 10 reverse){
println(i)
}
While 和 do…While 循环控制
While 和 do…While 的使用和 Java 语言中用法相同。
循环中断
基本说明
Scala 内置控制结构特地去掉了 break 和 continue,是为了更好的适应函数式编程,推荐使用函数式的风格解决break 和continue 的功能,而不是一个关键字。Scala 中使用breakable 控制结构来实现 break 和 continue 功能。
案例实操
需求 1:采用异常的方式退出循环
def main(args: Array[String]): Unit = {
try {
for (elem <- 1 to 10) { println(elem)
if (elem == 5) throw new RuntimeException
}
}catch {
case e =>
}
println("正常结束循环")
}
需求 2:采用 Scala 自带的函数,退出循环
import scala.util.control.Breaks
def main(args: Array[String]): Unit = {
Breaks.breakable(
for (elem <- 1 to 10) { println(elem)
if (elem == 5) Breaks.break()
}
)
println("正常结束循环")
}
需求 3:对break 进行省略
import scala.util.control.Breaks._ object TestBreak {
def main(args: Array[String]): Unit = {
breakable {
for (elem <- 1 to 10) {
println(elem)
if (elem == 5) break
}
}
println("正常结束循环")
}
}
需求 4:循环遍历 10 以内的所有数据,奇数打印,偶数跳过(continue)
object TestBreak {
def main(args: Array[String]): Unit = {
for (elem <- 1 to 10) {
if (elem % 2 == 1) {
println(elem)
} else {
println("continue")
}
}
}
}
函数式编程
1) 面向对象编程
对象:用户
行为:登录、连接 JDBC、读取数据库属性:用户名、密码
Scala 语言是一个完全面向对象编程语言。万物皆对象
对象的本质:对数据和行为的一个封装
2) 函数式编程
解决问题时,将问题分解成一个一个的步骤,将每个步骤进行封装(函数),通过调用这些封装好的步骤,解决问题。
例如:请求->用户名、密码->连接 JDBC->读取数据库
Scala 语言是一个完全函数式编程语言。万物皆函数。
函数的本质:函数可以当做一个值进行传递
3) 在Scala 中函数式编程和面向对象编程完美融合在一起了。
函数基础
函数基本语法
案例实操
需求:定义一个函数,实现将传入的名称打印出来。
object TestFunction {
def main(args: Array[String]): Unit = {
// (1)函数定义
def f(arg: String): Unit = {
println(arg)
}
// (2)函数调用
// 函数名(参数)
f("hello world")
}
}
函数和方法的区别
核心概念
(1) 为完成某一功能的程序语句的集合,称为函数。
(2) 类中的函数称之方法。
案例实操
(1) Scala 语言可以在任何的语法结构中声明任何的语法
(2) 函数没有重载和重写的概念;方法可以进行重载和重写
(3) Scala 中函数可以嵌套定义
object TestFunction {
// (2)方法可以进行重载和重写,程序可以执行
def main(): Unit = {
}
def main(args: Array[String]): Unit = {
// (1)Scala 语言可以在任何的语法结构中声明任何的语法
import java.util.Date
new Date()
// (2)函数没有重载和重写的概念,程序报错
def test(): Unit ={
println("无参,无返回值")
}
test()
def test(name:String):Unit={ println()
}
//(3)Scala 中函数可以嵌套定义
def test2(): Unit ={
println("函数可以嵌套定义")
}
}
}
}
函数参数
案例实操
(1) 可变参数
(2) 如果参数列表中存在多个参数,那么可变参数一般放置在最后(def test( s : String* )😃
(3) 参数默认值,一般将有默认值的参数放置在参数列表的后面
(4) 带名参数
object TestFunction {
def main(args: Array[String]): Unit = {
// (1)可变参数
def test( s : String* ): Unit = { println(s)
}
// 有输入参数:输出 Array test("Hello", "Scala")
// 无输入参数:输出List() test()
// (2)如果参数列表中存在多个参数,那么可变参数一般放置在最后
def test2( name : String, s: String* ): Unit = {
println(name + "," + s)
}
test2("jinlian", "dalang")
// (3)参数默认值
def test3( name : String, age : Int = 30 ): Unit = {
println(s"$name, $age")
}
// 如果参数传递了值,那么会覆盖默认值
test3("jinlian", 20)
// 如果参数有默认值,在调用的时候,可以省略这个参数
test3("dalang")
// 一般情况下,将有默认值的参数放置在参数列表的后面
def test4( sex : String = "男", name : String ): Unit = { println(s"$name, $sex")
}
// Scala 函数中参数传递是,从左到右
//test4("wusong")
//(4)带名参数test4(name="ximenqing")
}
函数至简原则(重点)
至简原则细节
(1) return 可以省略,Scala 会使用函数体的最后一行代码作为返回值
(2) 如果函数体只有一行代码,可以省略花括号
(3) 返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
(4) 如果有 return,则不能省略返回值类型,必须指定
(5) 如果函数明确声明unit,那么即使函数体中使用 return 关键字也不起作用
(6) Scala 如果期望是无返回值类型,可以省略等号
(7) 如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
(8) 如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
(9) 如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
案例实操
object TestFunction {
def main(args: Array[String]): Unit = {
// (0)函数标准写法
def f( s : String ): String = {
return s + " jinlian"
}
println(f("Hello"))
// 至简原则:能省则省
//(1) return 可以省略,Scala 会使用函数体的最后一行代码作为返回值
def f1( s : String ): String = {
s + " jinlian"
}
println(f1("Hello"))
//(2)如果函数体只有一行代码,可以省略花括号
def f2(s:String):String = s + " jinlian"
//(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起
省略)
def f3( s : String ) = s + " jinlian"
println(f3("Hello3"))
//(4)如果有 return,则不能省略返回值类型,必须指定。
def f4() :String = {
return "ximenqing4"
}
println(f4())
//(5)如果函数明确声明 unit,那么即使函数体中使用 return 关键字也不起作用
def f5(): Unit = {
return "dalang5"
}
println(f5())
//(6)Scala 如果期望是无返回值类型,可以省略等号
// 将无返回值的函数称之为过程
def f6() {
"dalang6"
}
println(f6())
//(7)如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可
不加
def f7() = "dalang7"
println(f7())
println(f7)
//(8)如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省
略
def f8 = "dalang"
//println(f8())
println(f8)
//(9)如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
def f9 = (x:String)=>{println("wusong")}
def f10(f:String=>Unit) = {
f("")
}
f10(f9)
println(f10((x:String)=>{println("wusong")}))
}
}
函数高级
高阶函数
object TestFunction {
def main(args: Array[String]): Unit = {
// 调用函数
foo()
}
// 定义函数
def foo():Unit = { println("foo...")
}
函数可以作为值进行传递
object TestFunction {
def main(args: Array[String]): Unit = {
//(1)调用 foo 函数,把返回值给变量f
//val f = foo()
val f = foo
println(f)
传递给变量 f1
val f1 = foo _
//(3)如果明确变量类型,那么不使用下划线也可以将函数作为整体传递给
变量
var f2:()=>Int = foo
}
def foo():Int = { println("foo...") 1
}
函数可以作为参数进行传递
def main(args: Array[String]): Unit = {
//(1)定义一个函数,函数参数还是一个函数签名;f 表示函数名称;(Int,Int)表示输入两个 Int 参数;Int 表示函数返回值
def f1(f: (Int, Int) => Int): Int = {
f(2, 4)
}
// (2)定义一个函数,参数和返回值类型和f1 的输入参数一致
def add(a: Int, b: Int): Int = a + b
// (3)将 add 函数作为参数传递给 f1 函数,如果能够推断出来不是调用,_ 可以省略
println(f1(add)) println(f1(add _))
//可以传递匿名函数
}
函数可以作为函数返回值返回
def main(args: Array[String]): Unit = {
def f1() = {
def f2() = {
}
f2 _
}
val f = f1()
// 因为f1 函数的返回值依然为函数,所以可以变量f 可以作为函数继续调用
f()
// 上面的代码可以简化为
f1()()
匿名函数
说明
没有名字的函数就是匿名函数。
(x:Int)=>{函数体}
x:表示输入参数类型;Int:表示输入参数类型;函数体:表示具体代码逻辑
案例实操
需求 1:传递的函数有一个参数
传递匿名函数至简原则:
(1) 参数的类型可以省略,会根据形参进行自动的推导
(2) 类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过 1 的永远不能省略圆括号。
(3) 匿名函数如果只有一行,则大括号也可以省略
(4) 如果参数只出现一次,则参数省略且后面参数可以用_代替
def main(args: Array[String]): Unit = {
// (1)定义一个函数:参数包含数据和逻辑函数
def operation(arr: Array[Int], op: Int => Int) = {
for (elem <- arr) yield op(elem)
}
// (2)定义逻辑函数
def op(ele: Int): Int = {
ele + 1
}
// (3)标准函数调用
val arr = operation(Array(1, 2, 3, 4), op)
println(arr.mkString(","))
// (4)采用匿名函数
val arr1 = operation(Array(1, 2, 3, 4), (ele: Int) => { ele + 1})
// (4.1)参数的类型可以省略,会根据形参进行自动的推导;
val arr2 = operation(Array(1, 2, 3, 4), (ele) => {ele + 1})
println(arr2.mkString(","))
// (4.2)类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过 1 的永远不能省略圆括号。
val arr3 = operation(Array(1, 2, 3, 4), ele => { ele + 1 })
println(arr3.mkString(","))
// (4.3) 匿名函数如果只有一行,则大括号也可以省略
val arr4 = operation(Array(1, 2, 3, 4), ele => ele + 1) println(arr4.mkString(","))
//(4.4)如果参数只出现一次,则参数省略且后面参数可以用_代替
val arr5 = operation(Array(1, 2, 3, 4), _ + 1) println(arr5.mkString(","))
}
object TestFunction {
def main(args: Array[String]): Unit = {
def calculator(a: Int, b: Int, op: (Int, Int) => Int): Int
= {
op(a, b)
}
// (1)标准版
println(calculator(2, 3, (x: Int, y: Int) => {x + y}))
// (2)如果只有一行,则大括号也可以省略
println(calculator(2, 3, (x: Int, y: Int) => x + y))
// (3)参数的类型可以省略,会根据形参进行自动的推导;
println(calculator(2, 3, (x , y) => x + y))
// (4)如果参数只出现一次,则参数省略且后面参数可以用_代替
println(calculator(2, 3, _ + _))
}
}
函数柯里化&闭包
闭包:函数式编程的标配
说明
闭包:如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和他所处的环境,称为闭包
函数柯里化:把一个参数列表的多个参数,变成多个参数列表。
案例实操
object TestFunction {
def main(args: Array[String]): Unit = {
def f1()={
var a:Int = 10
def f2(b:Int)={
a + b
}
f2 _
}
// 在调用时,f1 函数执行完毕后,局部变量a 应该随着栈空间释放掉
val f = f1()
// 但是在此处,变量a 其实并没有释放,而是包含在了 f2 函数的内部,形成了闭合的效果
println(f(3))
println(f1()(3))
// 函数柯里化,其实就是将复杂的参数逻辑变得简单化,函数柯里化一定存在闭包
def f3()(b:Int)={
a + b
}
println(f3()(3))
}
控制抽象
值调用:把计算后的值传递过去
与java类似
名调用:把代码传递过去
object TestControl {
def main(args: Array[String]): Unit = { def f = ()=>{
println("f...")
10
}
foo(f())
}
//def foo(a: Int):Unit = {
def foo(a: =>Int):Unit = {//注意这里变量 a 没有小括号了
println(a) println(a)
}
}
输出结果:
f... 10 f...
10
注意:Java 只有值调用;Scala 既有值调用,又有名调用。
案例实操
object TestFunction {
def main(args: Array[String]): Unit = {
// (1)传递代码块
foo({
println("aaa")
})
// (2)小括号可以省略
foo{
println("aaa")
}
}
def foo(a: =>Unit):Unit = { println(a)
println(a)
}
}
惰性加载
说明
当函数返回值被声明为 lazy 时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行。这种函数我们称之为惰性函数。
案例实操
def main(args: Array[String]): Unit = {
lazy val res = sum(10, 30)
println(" ")
println("res=" + res)
}
def sum(n1: Int, n2: Int): Int = {
println("sum 被执行。。。") return n1 + n2
}
输出结果
----------------
sum 被执行。。。
res=40
注意:lazy 不能修饰 var 类型的变量
面向对象
Scala 的面向对象思想和 Java 的面向对象思想和概念是一致的。
Scala 中语法和 Java 不同,补充了更多的功能。
Scala 包
Scala 包的三大作用(和 Java 一样)
(1) 区分相同名字的类
(2) 当类很多时,可以很好的管理类
(3) 控制访问范围
类和对象
定义类
回顾
Java 中的类
如果类是 public 的,则必须和文件名一致。一般,一个.java 有一个 public 类
注意:Scala 中没有 public,一个.scala 中可以写多个类。
基本语法
[修饰符] class 类名 {
类体
}
属性
基本语法
[修饰符] var|val 属性名称 [:类型] = 属性值
**注:**Bean 属性(@BeanPropetry),可以自动生成规范的 setXxx/getXxx 方法
案例实操
package com.atguigu.scala.test import scala.beans.BeanProperty class Person {
var name: String = "bobo" //定义属性
var age: Int = _ // _表示给属性一个默认值
//Bean 属性(@BeanProperty)
@BeanProperty var sex: String = "男"
//val 修饰的属性不能赋默认值,必须显示指定
}
object Person {
def main(args: Array[String]): Unit = {
var person = new Person()
println(person.name)
person.setSex(" 女 ")
println(person.getSex)
}
}
封装
封装就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作。Java 封装操作如下,
(1) 将属性进行私有化
(2) 提供一个公共的 set 方法,用于对属性赋值
(3) 提供一个公共的 get 方法,用于获取属性的值
Scala 中的 public 属性,底层实际为 private,并通过 get 方法(obj.field())和 set 方法(obj.field_=(value))对其进行操作。所以 Scala 并不推荐将属性设为 private,再为其设置public 的 get 和 set 方法的做法。但由于很多 Java 框架都利用反射调用 getXXX 和 setXXX 方法,有时候为了和这些框架兼容,也会为 Scala 的属性设置 getXXX 和 setXXX 方法(通过@BeanProperty 注解实现)。
访问权限
说明
在 Java 中,访问权限分为:public,private,protected 和默认。在 Scala 中,你可以通过类似的修饰符达到同样的效果。但是使用上有区别。
(1) Scala 中属性和方法的默认访问权限为 public,但 Scala 中无 public 关键字。
(2) private 为私有权限,只在类的内部和伴生对象中可用。
(3) protected 为受保护权限,Scala 中受保护权限比 Java 中更严格,同类、子类可以访问,同包无法访问。
(4) private[包名]增加包访问权限,包名下的其他类也可以使用
案例实操
方法
创建对象
基本语法
val | var 对象名 [:类型] = new 类型()
案例实操
(1) val 修饰对象,不能改变对象的引用(即:内存地址),可以改变对象属性的值。
(2) var 修饰对象,可以修改对象的引用和修改对象的属性值
(3) 自动推导变量类型不能多态,所以多态需要显示声明
构造器
和 Java 一样,Scala 构造对象也需要调用构造方法,并且可以有任意多个构造方法。
Scala 类的构造器包括:主构造器和辅助构造器
基本语法
class 类名(形参列表) { // 主构造器
// 类 体
def this(形参列表) { // 辅助构造器
}
def this(形参列表) { //辅助构造器可以有多个...
}
}
说明
(1) 辅助构造器,函数的名称 this,可以有多个,编译器通过参数的个数及类型来区分。
(2) 辅助构造方法不能直接构建对象,必须直接或者间接调用主构造方法。
(3) 构造器调用其他另外的构造器,要求被调用构造器必须提前声明(及上述基本语法中的第一个this不能调第二个this)。
案例实操
如果主构造器无参数,小括号可省略,构建对象时调用的构造方法的小括号也可以省略。
//(1)如果主构造器无参数,小括号可省略
//class Person (){
class Person {
var name: String = _
var age: Int = _
def this(age: Int) {
this()
this.age = age
println("辅助构造器")
}
def this(age: Int, name: String) {
this(age)
this.name = name
}
println("主构造器")
}
object Person {
def main(args: Array[String]): Unit = {
val person2 = new Person(18)
}
}
构造器参数
说明
Scala 类的主构造器函数的形参包括三种类型:未用任何修饰、var 修饰、val 修饰
(1) 未用任何修饰符修饰,这个参数就是一个局部变量
(2) var 修饰参数,作为类的成员属性使用,可以修改
(3) val 修饰参数,作为类只读属性使用,不能修改
案例实操
class Person(name: String, var age: Int, val sex: String) {
}
object Test {
def main(args: Array[String]): Unit = {
var person = new Person("bobo", 18, "男")
// (1)未用任何修饰符修饰,这个参数就是一个局部变量
// printf(person.name)
// (2)var 修饰参数,作为类的成员属性使用,可以修改
person.age = 19 println(person.age)
// (3)val 修饰参数,作为类的只读属性使用,不能修改
// person.sex = "女"
println(person.sex)
}
}
继承和多态
基本语法
class 子类名 extends 父类名 { 类 体 }
(1) 子类继承父类的属性和方法
(2) scala 是单继承
案例实操
(1) 子类继承父类的属性和方法
(2) 继承的调用顺序:父类构造器->子类构造器
class Person(nameParam: String) {
var name = nameParam
var age: Int = _
def this(nameParam: String, ageParam: Int) {
this(nameParam)
this.age = ageParam
println("父类辅助构造器")
}
println("父类主构造器")
}
class Emp(nameParam: String, ageParam: Int) extends Person(nameParam, ageParam) {
var empNo: Int = _
def this(nameParam: String, ageParam: Int, empNoParam: Int) { this(nameParam, ageParam)
this.empNo = empNoParam
println("子类的辅助构造器")
}
println("子类主构造器")
}
object Test {
def main(args: Array[String]): Unit = { new Emp("z3", 11,1001)
}
}
(3)Scala 中属性和方法都是动态绑定,而Java 中只有方法为动态绑定。
案例实操(对比 Java 与 Scala 的重写)
Scala
class Person {
val name: String = "person"
def hello(): Unit = {
println("hello person")
}
}
class Teacher extends Person {
override val name: String = "teacher" //重写属性
override def hello(): Unit = {
println("hello teacher")
}
}
object Test {
def main(args: Array[String]): Unit = {
val teacher: Teacher = new Teacher()
println(teacher.name)
teacher.hello()
val teacher1:Person = new Teacher
println(teacher1.name)
teacher1.hello()
}
}
Java
class Person {
public String name = "person"; public void hello() {
System.out.println("hello person");
}
}
class Teacher extends Person {
public String name = "teacher"; @Override
public void hello() { System.out.println("hello teacher");
}
}
public class TestDynamic {
public static void main(String[] args) {
Teacher teacher = new Teacher();
Person teacher1 = new Teacher();
System.out.println(teacher.name); teacher.hello();
System.out.println(teacher1.name); teacher1.hello();
}
}
抽象类
抽象属性和抽象方法
基本语法
(1) 定义抽象类:abstract class Person{} //通过 abstract 关键字标记抽象类
(2) 定义抽象属性:val|var name:String //一个属性没有初始化,就是抽象属性
(3) 定义抽象方法:def hello():String //只声明而没有实现的方法,就是抽象方法
abstract class Person {
val name: String def hello(): Unit
}
继承&重写
(1) 如果父类为抽象类,那么子类需要将抽象的属性和方法实现,否则子类也需声明为抽象类
(2) 重写非抽象方法需要用 override 修饰,重写抽象方法则可以不加 override。
(3) 子类中调用父类的方法使用 super 关键字
(4) 子类对抽象属性进行实现,父类抽象属性可以用 var 修饰;
子类对非抽象属性重写,父类非抽象属性只支持 val 类型,而不支持 var
因为 var 修饰的为可变变量,子类继承之后就可以直接使用,没有必要重写