面向对象
单例对象
在Scala 中,是没有static 这个东西的,但是可以使用关键字object,使用object修饰的类是单例的,而且类中的方法/属性都是static的。
在scala中被object关键字修饰的类有如下特征:
- 是单例的(内存中只有一个对象)
- 类中的所有属性和方法都是静态的
- 不需要通过new来创建对象,直接通过类名使用
- 通常用于封装一些常量、工具类等 调用方式:类名.属性/方法
package day03
object ScalaSingleton {
def saySomething(msg: String)={
println(msg)
}
}
object ScalaSingletonTest{
def main(args: Array[String]): Unit = {
ScalaSingleton.saySomething("hello scala") //hello scala
println(ScalaSingleton)
println(ScalaSingleton)
//day03.ScalaSingleton$@47f37ef1
//day03.ScalaSingleton$@47f37ef1
}
}
Scala类
/*
* 在 Scala中,类并不用声明为 public。
* 如果你没有定义构造器, 类会有一个默认的空参构造器
*
* var 修饰的变量, 这个变量对外提供 getter setter 方法
* val 修饰的变量, 对外提供了 getter 方法,没有 setter
* */
class Student {
// _ 表示一个占位符, 编译器会根据你变量的具体类型赋予相应初始值
// 注意: 使用_ 占位符是, 变量类型必须指定
var name : String = _
// 错误代码, val 修饰的变量不能使用占位符
// val age: Int = _
val age : Int = 10
}
object Test{
val name : String = "zhangsan"
def main(args: Array[String]): Unit = {
// 调用空参构造器,可以加() 也可以不加
val student = new Student()
student.name = "lisi"
// 类中使用 val 修饰的变量不能更改
// student.age = 20
println(s"student.name ====== ${student. name} ${student.age }")
println ("Test.name======" + Test. name )
}
}
定义在类后面的为类主构造器, 一个类可以有多个辅助构造器
/*
* Student1(val name: String, var age: Int)
* 定义个 2 个参数的主构造器
* */
class Student1 (val name: String, var age: Int) {
var gender : String = _
// 辅助构造器, 使用 def this
// 在辅助构造器中必须先调用类的主构造器
def this(name: String, age:Int, gender: String){
this(name, age)
this. gender = gender
}
}
object Test1{
def main(args: Array[String]): Unit = {
val s = new Student1( "laozhang", 38)
println ( s"${s.name} ${s.age}")
val s1 = new Student1( "laowang", 18, "male")
println ( s"${s1.gender}")
}
}
访问权限
构造器的访问权限
/*
* Student1(val name: String, var age: Int)
* 定义个 2 个参数的主构造器
*
* private 加在主构造器前面标识这个主构造器是私有的, 外部不能访问这个构造器
* */
class Student2 private (val name: String, var age: Int) {
var gender : String = _
// 辅助构造器, 使用 def this
// 在辅助构造器中必须先调用类的主构造器
def this(name: String, age:Int, gender: String){
this(name, age)
this. gender = gender
}
}
object Test2{
def main(args: Array[String]): Unit = {
val s1 = new Student2("laoYang", 18,"male")
println ( s"${s1. gender }")
}
}
成员变量的访问权限
/*
* private 加在主构造器前面标识这个主构造器是私有的, 外部不能访问这个构造器
*
* private var age
* age 在这个类中是有 getter setter 方法的
* 但是前面如果加上了 private 修饰, 也就意味着, age 只能在这个类的内部以及其伴生类对象中可以访
* 问修改
* 其他外部类不能访问
* */
class Student3 private (val name: String,private var age: Int) {
var gender : String = _
// 辅助构造器, 使用 def this
// 在辅助构造器中必须先调用类的主构造器
def this(name: String, age:Int, gender: String){
this(name, age)
this. gender = gender
}
// private[this]关键字标识该属性只能在类的内部访问, 伴生类不能访问
private[ this] val province : String = "北京市"
def getAge = 18
}
// 类的伴生对象
object Student3 {
def main(args: Array[String]): Unit = {
// 伴生对象可以访问类的私有方法和属性
val s3 = new Student3("Angelababy", 30)
s3.age = 29
println ( s"${s3.age}")
// println(s"${s3.province}") 伴生类不能访问
}
}
类包的访问权限
/*
* private[包名] class 放在类声明最前面, 是修饰类的访问权限, 也就是说类在某些
包下不可见或不能访问
*
* private[day03] class 代表 student4 在 day03 包下及其子包下可以见, 同级包中不能访问
*
* */
private[day03] class Student4(val name: String,private var age: Int)
{
var xx : Int = _
}
object Student4{
def main(args: Array[String]): Unit = {
val s = new Student4("张三", 20)
println ( s"${s.name}")
}
}
伴生类/apply方法
在 Scala 中, 当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象。必须在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类。类和它的伴生对象可以互相访问其私有成员。
-
伴生类的主构造器可以私有化
-
在伴生对象中仍然可以调用伴生类私有化的构造器创建对象,所以可以在伴生对象的apply方法中完成对象创建以及一些初始化的动作(以这种方式暴漏对象创建接口)
除上述外,apply方法我们在调用的时候.apply可以省略,又简化了我们代码的编写
package day03
class ClassObject private (var name: String, var age: Int) {
}
object ClassObject{
def apply(name: String,age: Int): ClassObject = new ClassObject(name,age)
def getInstance(name: String, age: Int): ClassObject ={
new ClassObject(name,age)
}
def main(args: Array[String]): Unit = {
val classObject = new ClassObject("王五",19)
}
}
package day03
object ClassObjectTest {
def main(args: Array[String]): Unit = {
val classObject = ClassObject.apply("sili", 18)
println(classObject.name)
val value = ClassObject("wangwu",22)
println(value.name)
}
}
特质(Trait)
Scala中的Trait相当于Java中的接口,实际上它比接口还功能强大。
与接口不同的是,它还可以定义属性和方法的实现。一般情况下 Scala 的类只能够继承单一父类,但是如果是 Trait(特质) 的话就可以继承多个,实现了多重继承。
trait T2 {
// 定义一个属性
val className: String = "scala"
// 定义一个没有实现的方法
def teacherSay(name: String)
// 定义个带有具体的实现的方法
def doSomething() = {
println("该吃中午饭了...")
}
}
特质的动态混入
package day03
class Student {
}
object Student{
def main(args: Array[String]): Unit = {
// 特质的动态混入:给student对象扩展ScalaTrait1特质功能和Fly功能
val student = new Student with ScalaTrait1 with Fly
student.dance("赵四")
student.fly()
}
}
//定义两个trait
package day03
trait ClassTrait1 {
def hello(name: String): Unit ={
println(s"hello ${name}")
}
def dance(name: String) = {
println(s"$舞王{name}")
}
}
package day03
trait Fly{
def fly(): Unit ={
println("i can fly...")
}
}
也可以在类上直接继承多个特质,extend...with
class Student3Trait extends ScalaTrait1 with Fly{
}
抽象类
在 Scala 中, 使用 abstract 修饰的类称为抽象类. 在抽象类中可以定义属性、未实现的方法和具体实现的方法。
/*
* abstract 修饰的类是一个抽象类
* */
abstract class Animal {
println ("Animal's r constructor ....")
// 定义一个 name 属性
val name : String = "animal"
// 没有任何实现的方法
def sleep()
// 带有具体的实现的方法
def eat(f: String): Unit = {
println ( s"$f")
}
}
继承
final关键字
被 final 修饰的类不能被继承;
被 final 修饰的属性不能重写;
被 final 修饰的方法不能被重写。
type关键字
Scala 中可以通过 type 关键字来声明类型。
type 相当于声明一个类型别名(Alias):
/*
* with 后面只能是特质
*
* 父类已经实现了的功能, 子类必须使用 override 关键字重写
* 父类没有实现的方法,子类必须实现,这个override可带可不带
* */
package day03
class TypeTraitImpl extends TypeTriat {
//实现时指定该type类型变量的具体类型
override type T = String
override def learn(s: String): Unit = {
}
}
package day03
trait TypeTriat {
//type类型
type T
//当变量T的类型指定后s的类型也就随之确定
def learn(s: T): Unit ={
println("====>>" ,s)
}
}
模式匹配
匹配字符串/类型/守卫
def main(args: Array[String]): Unit = {
val arr = Array ( "AAA", "BBB", "CCC")
val i = Random.nextInt(arr.length)
println (i)
val name = arr(i)
println(name)
name match {
case "AAA" => println ("I am AAA")
case "BBB" => println ("I am BBB")
case _ => println ("Who are you ...")
}
//定义一个数组
val arr :Array[Any] = Array ( "hello123", 1, 2.0, StudentTest, 2L)
//取出一个元素
val elem = arr(3)
elem match {
case x: Int => println ("Int" + x)
case y: Double if(y >= 0) => println ("Double"+ y) // if 守卫
case z: String => println ("String"+ z)
case w: Long => println ("long" + w)
case StudentTest => {
println ("case StudentTest")
//throw new Exception("not match exception")
}
case _ => { // 其他任意情况
println ( "no")
println ( "default")
}
}
}
匹配数组
匹配数组的情况下属于拿着数组的整体结构进行匹配,而不是判断个别元素了
val arr = Array (1, 1, 7, 0, 2,3)
arr match {
case Array (0, 2, x, y) => println (x + " " + y) //x,y看作是一个占位符,和_效果相同
case Array (2, 1, 7, y) => println ("only 0" + y)
case Array (1, 1, 7, _*) => println ("0 ...") // _* 任意多个
case _ => println ("something else")
}
匹配集合
val lst = List (0, 3, 4)
println ( lst .head)
println ( lst .tail)
lst match {
// 0 :: Nil 代表集合中只有0一个元素
case 0 :: Nil => println ("only 0")
// x :: y :: Nil 代表两个元素插入到空集合中了,所以这里匹配的是2个元素的集合
case x :: y :: Nil => println (s"x $x y $y")
// :: 代表着要向一个集合的头部添加元素,::前面的值就是要插入的元素,此处其实就是0
// 0 :: a == List (0, 3, 4)
// a = List(3,4)
case 0 :: a => println (s"value : $a")
case _ => println ("something else")
//输出结果:
// 0
// List(3, 4)
// value : List(3, 4)
}
匹配元组
val tup = (1, 3, 7)
tup match {
case (3, x, y) => println (s"hello 123 $x ,$y")
case (z, x, y) => println (s"$z, $x ,$y")
case (_, w, 5) => println (w)
case _ => println ( "else")
}