文章目录
一、类和对象
主构造器
// 语法:
// class 类名(var/val 参数名:类型 = 默认值, var/val 参数名:类型 = 默认值) {
// 构造代码块
// }
// 使用
class Person(var name:String = "", var age:Int = 0) {
println("调用主构造器")
}
def main(args: Array[String]): Unit = {
val zhangsan = new Person("张三", 20)
println(zhangsan.name)
println(zhangsan.age)
val empty = new Person
println(empty.name)
println(empty.age)
val man40 = new Person(age = 40)
println(man40.name)
println(man40.age)
}
- 主构造器的参数列表是直接定义在类后面,添加了 val/var 表示直接通过主构造器定义成员变量
- 构造器参数列表可以指定默认值
- 创建实例,调用构造器可以指定字段进行初始化
- 整个 class 中除了字段定义和方法定义的代码都是构造代码
辅助构造器
// 语法:
// def this(参数名:类型, 参数名:类型) {
// 第一行需要调用主构造器获取其他构造器
// 构造器代码
// }
// 使用
// 定义主构造器
class Customer(var name:String = "", var address:String = "") {
// 定义辅助构造器
def this(data:Array[String]) {
this(data(0), data(1))
}
}
def main(args: Array[String]): Unit = {
// 使用辅助构造器创建对象
val customer = new Customer(Array("张三", "北京"))
println(customer.name)
println(customer.address)
}
辅助构造器的第一行代码,必须要调用主构造器或者其他辅助构造器
单例对象
Scala 中没有 Java 中的静态成员,我们想要定义类似于 Java 的 static 变量、static 方法,就是使用到 scala 中的单例对象——object。
单例对象表示全局仅有一个对象(类似于 Java static 概念)
- 定义单例对象和定义类很像,就是把 class 换成 object
- 在 object 中定义的成员变量类似于 Java 的静态变量
- 可以使用 object 直接引用成员变量
// 创建单例对象
object Dog {
// 定义了一个单例对象的成员
// 类似于Java中static变量
val LEG_NUM = 4
// 定义成员方法
def eat() = {
println("狗吃肉")
}
}
// 访问单例对象中的成员变量和成员方法
def main(args: Array[String]): Unit = {
println(Dog.LEG_NUM)
Dog.eat
}
实现 App Trait 来定义入口
// 语法:
// object 单例对象名 extends App {
// 方法体
// }
// 使用:
object Demo extends App {
println("hello")
}
伴生对象
一个 class 和 object 具有同样的名字。这个 object 称为伴生对象,这个 class 称为伴生类。
- 伴生对象必须要和伴生类一样的名字
- 伴生对象和伴生类在同一个 scala 源文件中
- 伴生对象和伴生类可以互相访问 private 属性
private[this] 访问权限
如果某个成员的权限设置为 private[this],表示只能在当前类中访问,伴生对象也不可以访问。
classs Person(private[this] var name:String)
apply 方法
class Person(var name:String, var age:Int)
object Person {
def main(args: Array[String]): Unit = {
def apply(name: String, age: Int): Person = new Person(name, age)
// 使用类名来快速创建对象
val zhangsan = Person("张三", 20)
println(zhangsan.name)
println(zhangsan.age)
}
}
override 和 super
类似于 Java 语言,我们在子类中使用 override 来重写父类的成员,可以使用 super 来引用父类。
用法:
- 子类要覆盖父类中的一个方法,必须要使用 override 关键字
- 使用 override 来重写一个 val 字段
- 使用 super 关键字来访问父类的成员方法
class Person {
val name:String = ""
def getName():String = this.name
}
class Student extends Person {
// 重写字段
override val name:String = "student"
// 重写成员方法
override def getName(): String = "hello, " + super.getName()
}
def main(args: Array[String]): Unit = {
val student = new Student
println(student.name) // student
println(student.getName()) // hello, student
}
isInstanceOf/asInstanceOf
// 用法:
// 判断对象是否为指定类以及其子类的对象,不能精确判断出对象就是指定类的对象
// val trueOrFalse:Boolean = 对象.isInstanceOf[类型]
// 将对象转换为指定类型
// val 变量 = 对象.asInstanceOf[类型]
// 使用:
class Person
class Student extends Person
def main(args: Array[String]): Unit = {
val student = new Student
if(student.isInstanceOf[Student]) {
// 是Student类型
val student1 = student.asInstanceOf[Student]
println(student1)
} else {
// 不是Student类型
println("不是Student类型")
}
}
getClass 和 classOf
用法:
- 对象.getClass 可以精确获取对象的类型
- classOf[x] 可以精确获取类型
- 使用 == 操作符可以直接比较类型
class Person
class Student extends Person
val student: Person = new Student
student.isInstanceOf[Person] // true
// 使用getClass获取对象的类型
// 使用classOf获取类的类型
student.getClass == classOf[Person] // false
student.getClass == classOf[Student] // true
样例类
样例类是一种特殊类,它可以用来快速定义一个用于保存数据的类(类似于 Java POJO 类)。
定义
// 语法:
// case class 样例类名([var/val] 成员变量名1:类型1, 成员变量名2:类型2, 成员变量名3:类型3)
// 用法:
// 1. 定义样例类,定义成员变量(默认不可变)
case class Person(var name:String, var age:Int)
def main(args: Array[String]): Unit = {
// 2. 创建样例类对象
val zhangsan = Person("张三", 20)
zhangsan.age = 23
println(zhangsan) // Person(张三,23)
}
样例类的方法
当我们定义一个样例类,编译器自动帮助我们实现了以下几个有用的方法:
- apply 方法
- toString 方法
- equals 方法
- hashCode 方法
- copy 方法
// copy方法:可以快速创建一个相同的实例对象,可以使用带名参数指定给成员进行重新赋值
case class Person(name:String, age:Int)
def main(args: Array[String]): Unit = {
var lisi = Person("李四", 21)
// 使用copy方法来创建一个新的对象
val wangwu = lisi.copy(name = "王五")
println(wangwu) // Person(王五,21)
}
样例对象
使用场景:
- 定义枚举
- 作为没有任何参数的消息传递(Akka 编程会使用到)
- 定义
样例对象是单例的,而且它没有主构造器。
// 语法:
// case object 样例对象名
// 1. 创建一个Sex枚举(样例对象)
trait Sex
// 创建两个样例对象,从特质继承
case object Male extends Sex
case object Female extends Sex
// 2. 定义一个样例类,使用Sex枚举
case class Person(name:String, sex:Sex)
def main(args: Array[String]): Unit = {
// 3. 创建样例类的对象
val zhangsan = Person("张三", Male)
val lisi = Person("李四", Female)
println(zhangsan) // Person(张三,Male)
println(lisi) // Person(李四,Female)
}
抽象类
如果类的某个成员在当前类中的定义是不包含完整的,它就是一个抽象类。
- 方法没有方法体(抽象方法)
- 变量没有初始化(抽象字段)
// 定义抽象类
abstract class 抽象类名 {
// 定义抽象字段
val 抽象字段名:类型
// 定义抽象方法
def 方法名(参数:参数类型, 参数:参数类型,...): 返回类型
}
匿名内部类
// 语法:
// val/var 变量名 = new 类/抽象类 {
// 重写方法
// }
abstract class Person {
def sayHello()
}
def main(args: Array[String]): Unit = {
val person = new Person {
override def sayHello(): Unit = println("hello")
}
person.sayHello()
}
二、特质(trait)
Scala 中没有 Java 中的接口,替代的的概念是——特质。
- 特质是 Scala 中代码复用的基础单元
- 它可以将方法和字段定义封装起来,然后添加到类中
- 与类继承不一样的是,类继承要求每个类都只能继承一个超类,而一个类可以添加任意数量的特质
- 特质的定义和抽象类的定义很像,但它是使用 trait 关键字
作为接口使用
// 语法:
// 定义特质
// trait 名称 {
// 抽象字段
// 抽象方法
// }
// 继承特质
// class 类 extends 特质1 with 特质2 {
// 字段实现
// 方法实现
// }
// 使用:继承单个trait
trait Logger {
def log(msg:String)
}
class ConsoleLogger extends Logger {
override def log(msg: String): Unit = println("控制台消息:" + msg)
}
def main(args: Array[String]): Unit = {
val logger = new ConsoleLogger
logger.log("NullPoiter Exception...")
}
// 使用:继承多个trait
trait MessageSender {
def send(msg:String)
}
trait MessageReceiver {
def receive():String
}
class MessageWorker extends MessageSender with MessageReceiver {
override def send(msg: String): Unit = println("发送消息:" + msg)
override def receive(): String = "接收消息:你好!收到!"
}
def main(args: Array[String]): Unit = {
val worker = new MessageWorker
worker.send("你好, 收到没?")
println(worker.receive())
}
// object继承trait
trait Logger {
def log(msg:String)
}
object ConsoleLogger extends Logger {
override def log(msg: String): Unit = println("控制台消息" + msg)
}
def main(args: Array[String]): Unit = {
ConsoleLogger.log("hello")
}
定义具体的方法
trait Logger {
def log(msg:String) = println(msg)
}
class UserService extends Logger {
def add() = log("添加用户")
}
def main(args: Array[String]): Unit = {
val service = new UserService
service.add()
}
trait 中定义具体的字段和抽象的字段
trait Logger {
// 具体字段
val simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm")
// 抽象字段
val TYPE:String
// 抽象方法
def log(msg:String)
}
class ConsoleLogger extends Logger {
override val TYPE: String = "控制台消息:"
override def log(msg: String): Unit = println(s"${TYPE}:${simpleDateFormat.format(new Date)}:$msg")
}
def main(args: Array[String]): Unit = {
val logger = new ConsoleLogger
logger.log("hello")
}
使用 trait 实现模板模式
trait Logger {
def log(msg:String)
// 具体方法调用抽象方法
def info(msg:String) = log("信息:" + msg)
def warn(msg:String) = log("警告:" + msg)
def error(msg:String) = log("错误:" + msg)
}
class ConsoleLogger extends Logger {
override def log(msg: String): Unit = println(msg)
}
// 调用不同级别的日志输出
def main(args: Array[String]): Unit = {
val logger = new ConsoleLogger
logger.info("Call Action method")
logger.warn("Type annotation required for Unit definition")
logger.error("NullPoiter Exception")
}
对象混入 trait
Scala 中可以将 trait 混入到对象中,就是将 trait 中定义的方法、字段添加到一个对象中。
// 语法:
// val/var 对象名 = new 类 with 特质
// 使用
trait Logger {
def log(msg:String) = println(msg)
}
class UserService
val service1 = new UserService with Logger
trait 实现调用链模式
责任链模式
trait 调用链
类继承了多个 trait 后,可以依次调用多个 trait 中的同一个方法,只要让多个 trait 中的同一个方法在最后都依次执行 super 关键字即可。类中调用多个 trait 中都有这个方法时,首先会从最右边的 trait 方法开始执行,然后依次往左执行,形成一个调用链条。
案例:
trait HandlerTrait {
def handle(data:String) = {
println("处理支付数据...")
}
}
trait DataValidateTrait extends HandlerTrait {
override def handle(data: String): Unit = {
println("数据校验...")
super.handle(data)
}
}
trait SignatureValidateTrait extends HandlerTrait {
override def handle(data: String): Unit = {
println("签名校验...")
super.handle(data)
}
}
class PayService extends DataValidateTrait with SignatureValidateTrait {
override def handle(data: String): Unit = {
println("准备支付...")
super.handle(data)
}
}
def main(args: Array[String]): Unit = {
val service = new PayService
service.handle("支付数据")
}
// 准备支付...
// 签名校验...
// 数据校验...
// 处理支付数据...