目录
面向对象编程
类
- 这部分可以类比Java中的很多概念
- 在IDEA中新建object
import java.util.Date
class Customer {
var name:String = _ // 下划线表示占位,传入相应类型的默认值
var sex:String = _
val registerDate:Date = new Date
def sayHi(msg:String):Unit = { // Unit即void
println(msg)
}
}
object Main {
// main方法必须要放在一个scala的object(单例对象)中才能执行
def main(args: Array[String]): Unit = {
val customer = new Customer
//给对象的成员变量赋值
customer.name = "张三"
customer.sex = "男"
println(s"姓名: ${customer.name}, 性别:${customer.sex}, 注册时间: ${customer.registerDate}") // 使用${}格式化输出变量,s表示字符串
//对象调用方法
customer.sayHi("你好!")
}
}
构造器
- 主构造器:指在类名的后面跟上一系列参数
- 辅助构造器:在类中使用this来定义
class Student(val name:String, val age:Int) { // 类的默认成员属性都是val,可以使用var声明
val address:String="beijing"
// 定义一个参数的辅助构造器
def this(name:String) {// 子类主构造器(传参即可)
// 第一行必须调用主构造器、其他辅助构造器或者super父类的构造器
this(name, 20) // 调用主构造器
}
def this(age:Int) {
this("某某某", age)
}
}
- 这里的主构造器类似Java的父类构造器,但是传参即可
- 辅助构造器类似子类构造器,但必须使用
this
调用父类的构造方法
注:只是类比学习
对象
- scala中是没有静态(static)成员
- 需要实现static变量、static方法的效果,要通过scala中的单例对象(object)
- scala主要进行函数式编程,这里可以类比JavaScript中的模式,静态属性/方法可以借助对象实现
import java.text.SimpleDateFormat
object DateUtils {
// 在object中定义的成员变量,相当于Java中定义一个静态变量
// 定义一个SimpleDateFormat日期时间格式化对象
val simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm")
// 构造代码
println("构造代码")
// 相当于Java中定义一个静态方法
def format(date:Date) = simpleDateFormat.format(date)
// main是一个静态方法,所以必须要写在object中
def main(args: Array[String]): Unit = {
println { DateUtils.format(new Date()) }; // 通过 . 调用
}
}
伴生对象
- class和object具有同样的名字,那么就称这个object是class的伴生对象,class是object的伴生类
- 最大特点是:可以相互访问,包括私有变量
class ClassObject {
val id = 1
private var name = "itcast"
def printName(): Unit ={
//在Dog类中可以访问伴生对象Dog的私有属性
println(ClassObject.CONSTANT + name )
}
}
object ClassObject{
//伴生对象中的私有属性
private val CONSTANT = "汪汪汪 : "
def main(args: Array[String]) {
val p = new ClassObject // 实例化类
p.name = "123" //访问私有的字段name
p.printName()
}
}
apply方法
- 我们可以使用如下代码新建数组,不需要关键字
new+类名
,如何实现?
// 创建一个Array对象
val a = Array(1,2,3,4)
- 通过下面的scala源码可以发现,其实默认调用了Array对象的apply()方法
- 伴生对象的apply方法用来快速地创建一个伴生类的对象(实例)
class Person(var name:String, var age:Int) {
override def toString = s"Person($name, $age)"
}
object Person {
// 实现apply方法
// 返回的是伴生类的对象
// 有没有点像Java自定义异常?必须实现所有构造方法
def apply(name:String, age:Int): Person = new Person(name, age)
// apply方法支持重载
def apply(name:String):Person = new Person(name, 20)
def apply(age:Int):Person = new Person("某某某", age)
def apply():Person = new Person("某某某", 20)
}
object Main2 {
def main(args: Array[String]): Unit = {
val p1 = Person("张三", 20) // Person对象的第一个apply()方法
val p2 = Person("李四")
val p3 = Person(100)
val p4 = Person()
println(p1)
println(p2)
println(p3)
println(p4)
}
}
main方法
- 我们在对象中实现main方法
- 也可以继承自App Trait(特质),可以理解为Java中的接口
object Main2 extends App {
println("hello, scala")
}
继承
- 使用
extends
关键字实现继承,基本和Java相同
class Person1 {
var name = "super"
def getName = this.name
}
class Student1 extends Person1
object Main1 {
def main(args: Array[String]): Unit = {
val p1 = new Person1()
val p2 = new Student1()
p2.name = "张三"
println(p2.getName)
}
}
// 必须在子类的主构造器中调用父类的构造器
class Person8(var name:String){
println("name:"+name)
}
// 传参,调用父类的构造器
class Student8(name:String, var clazz:String) extends Person8(name)
object Main8 {
def main(args: Array[String]): Unit = {
val s1 = new Student8("张三", "三年二班")
println(s"${s1.name} - ${s1.clazz}")
}
}
// output:
// name:张三
// 张三 - 三年二班
- 对象也可以继承类油!
class Person2 {
var name = "super"
def getName = this.name
}
object Student2 extends Person2 // 单例对象继承
object Main2 {
def main(args: Array[String]): Unit = {
println(Student2.getName) // 相当于调用静态方法(object中的方法)
}
}
- 与Java不同,子类要覆盖父类中的一个非抽象方法,必须要使用
override
关键字 - 相同的是,使用
super
访问父类成员
class Person3 {
val name = "super"
def getName = name
}
class Student3 extends Person3 {
// 重写val字段
override val name: String = "child"
// 重写getName方法
override def getName: String = "hello, " + super.getName
}
object Main3 {
def main(args: Array[String]): Unit = {
println(new Student3().getName)
}
}
抽象类
- 就是这么
abstract
,为了更好的实现多态? - 没有方法体的方法称为抽象方法,没有初始化的变量称为抽象字段
abstract class Person(val name:String) {// 还有个构造方法呢
//抽象方法
def sayHello:String
def sayBye:String
//抽象字段
val address:String
}
class Student(name:String) extends Person(name){
//重写抽象方法,不需要使用override
def sayHello: String = "Hello,"+name
def sayBye: String ="Bye,"+name
//重写抽象字段,需要使用override
override val address:String ="beijing "
}
object Main{
def main(args: Array[String]) {
val s = new Student("tom")
println(s.sayHello)
println(s.sayBye)
println(s.address)
}
}
匿名内部类
- 这个非常常用,配合匿名类使用
- 匿名内部类是没有名称的子类,直接用来创建实例对象
abstract class Person {
//抽象方法
def sayHello:Unit
}
object Main {
def main(args: Array[String]): Unit = {
// 直接用new来实例化一个匿名内部类对象
val p1 = new Person { // 通过new实例化,但是实现了抽象方法,还是属于子类
override def sayHello: Unit = println("我是一个匿名内部类")
}
p1.sayHello
}
}
isInstanceOf
- 在代码中要经常进行类型的判断和类型的转换,在scala中
isInstanceOf
判断对象是否为指定类的对象asInstanceOf
将对象转换为指定类型
class Person4
class Student4 extends Person4
object Main4 {
def main(args: Array[String]): Unit = {
val s1:Person4 = new Student4 // Person4 类型
// 判断s1是否为Student4类型
if(s1.isInstanceOf[Student4]) {
// 将s1转换为Student3类型
val s2 = s1.asInstanceOf[Student4]
}
}
}
getClass
- isInstanceOf 只能判断出对象是否为指定类以及其子类的对象,而不能精确的判断
- 要求精确地判断出对象就是指定类的对象,那么就只能使用
getClass
和classOf
class Person
class Student extends Person
object Student{
def main(args: Array[String]) {
val p:Person=new Student
//判断p是否为Person5类的实例
println(p.isInstanceOf[Person])//true
//判断p的类型是否为Person5类
println(p.getClass == classOf[Person])//false
//判断p的类型是否为Student5类
println(p.getClass == classOf[Student])//true
}
}
访问修饰符
- 可以在成员前面添加
private/protected
关键字来控制成员的可见性 - 但在scala中,任何没有被标为private或protected的成员都是公共的,没有public关键字
private[this]
- 被修饰的成员只能在当前类中被访问
class Person6 {
// 只有在当前对象中能够访问
private[this] var name = "super"
def getName = this.name // 正确!
def sayHelloTo(p:Person6) = {
println("hello" + p.name) // 报错!无法访问
}
}
object Person6 {
def showName(p:Person6) = println(p.name) // 报错!外部无法访问
}
protected[this]
- 可以通过
this.
访问或者子类通过this.访问,和java很像
class Person7 {
// 只有在当前对象以及继承该类的当前对象中能够访问
protected[this] var name = "super"
def getName = {
// 正确!
this.name
}
def sayHelloTo1(p:Person7) = {
// 编译错误!无法访问
println(p.name) // 只能this.
}
}
class Student7 extends Person7 {
def showName = {
// 正确!
println(name) // 默认使用this.
}
def sayHelloTo2(p:Person7) = {
// 编译错误!无法访问
println(p.name)
}
}
特质
- 特质
trait
是scala中代码复用的基础单元 - 它可以将方法和字段定义封装起来,然后添加到类中,我们叫混入
- 一个类可以添加任意数量的特质,像Java接口吧!!!
- 使用
extends
来继承trait,如果要继承多个trait,则使用with
关键字
trait Logger {
// 抽象方法
def log(msg:String)
}
trait MessageSender {
def send(msg:String)
}
class ConsoleLogger extends Logger with MessageSender {
override def log(msg: String): Unit = println(msg)
override def send(msg: String): Unit = println(s"发送消息:${msg}")
}
object LoggerTrait {
def main(args: Array[String]): Unit = {
val logger = new ConsoleLogger2
logger.log("控制台日志: 这是一条Log")
logger.send("你好!")
}
}
- trait中也可以定义具体的方法
- 实例对象混入trait
trait LoggerMix {
def log(msg:String) = println(msg)
}
class UserService
object FixedInClass {
def main(args: Array[String]): Unit = {
// 使用with关键字直接将特质混入到对象中
val userService = new UserService with LoggerMix// 只有这个实例的对象有
userService.log("你好")
}
}
- 可以了解一下trait调用链
- 参考如下代码,理解一下trait的优势
// 支付数据处理
trait HandlerTrait {
def handle(data: String) = {
println("处理数据...")
}
}
// 数据校验处理
trait DataValidHandlerTrait extends HandlerTrait {
override def handle(data: String) = {
println("验证数据...")
super.handle(data)
}
}
// 签名校验处理
trait SignatureValidHandlerTrait extends HandlerTrait {
override def handle(data: String) = {
println("检查签名...")
super.handle(data)
}
}
// 支付服务
class PaymentService extends DataValidHandlerTrait with SignatureValidHandlerTrait {
def pay(data:String) = {
println("准备支付...")
this.handle(data)
}
}
object PaymentService {
def main(args: Array[String]) {
val payService = new PaymentService()
payService.pay("signature:10233123||md5:123889a3d5s1f6123||data:{order:001,money:200}")
}
}
// 程序运行输出如下:
// 准备支付...
// 检查签名...
// 验证数据...
// 处理数据...
模板模式
- 使用具体方法依赖于抽象方法,而抽象方法可以放到继承trait的子类中实现
trait Logger {
// 抽象方法
def log(msg:String)
// 具体方法,依赖于抽象方法log
def info(msg:String) = log("INFO:" + msg)
def warn(msg:String) = log("WARN:" + msg)
def error(msg:String) = log("ERROR:" + msg)
}
class ConsoleLogger extends Logger {
// 实现抽象方法
override def log(msg: String): Unit = println(msg)
}
object LoggerTrait {
def main(args: Array[String]): Unit = {
val logger = new ConsoleLogger
logger3.info("这是一条普通信息")
logger3.warn("这是一条警告信息")
logger3.error("这是一条错误信息")
}
}
trait构造机制
- trait也有构造代码,但和类不一样,特质不能有构造器参数
- 每个特质只有一个无参数的构造器
- 一个类继承另一个类、以及多个trait,当创建该类的实例时,它的构造顺序如下:
- 执行父类的构造器
- 从左到右依次执行trait的构造器
- 如果trait有父trait,先构造父trait,如果多个trait有同样的父trait,则只初始化一次
- 执行子类构造器
class Person_One {
println("执行Person构造器!")
}
trait Logger_One {
println("执行Logger构造器!")
}
trait MyLogger_One extends Logger_One {
println("执行MyLogger构造器!")
}
trait TimeLogger_One extends Logger_One {
println("执行TimeLogger构造器!")
}
class Student_One extends Person_One with MyLogger_One with TimeLogger_One {
println("执行Student构造器!")
}
object exe_one {
def main(args: Array[String]): Unit = {
val student = new Student_One
}
}
// 程序运行输出如下:
// 执行Person构造器!
// 执行Logger构造器!
// 执行MyLogger构造器!
// 执行TimeLogger构造器!
// 执行Student构造器!
模式匹配
- scala有一个十分强大的模式匹配机制,可以应用到很多场合
- 并且scala还提供了样例类,对模式匹配进行了优化,可以快速进行匹配
- 类似于Java中的switch这里使用
match
//todo:匹配字符串
object CaseDemo01 extends App{
//定义一个数组
val arr=Array("hadoop","zookeeper","spark","storm")
//随机取数组中的一位,使用Random.nextInt
val name = arr(Random.nextInt(arr.length))
println(name)
name match {
case "hadoop" => println("大数据分布式存储和计算框架...")
case "zookeeper" => println("大数据分布式协调服务框架...")
case "spark" => println("大数据分布式内存计算框架...")
//表示以上情况都不满足才会走最后一个
case _ => println("我不认识你")
}
}
//todo:匹配类型
object CaseDemo02 extends App{
//定义一个数组
val arr=Array("hello",1,-2.0,CaseDemo02)
//随机获取数组中的元素
val value=arr(Random.nextInt(arr.length))
println(value)
value match {
case x:Int => println("Int=>"+x)
case y:Double if(y>=0) => println("Double=>"+y)
case z:String => println("String=>"+z)
case _ => throw new Exception("not match exception")
}
}
//匹配数组
object CaseDemo03 extends App{
//匹配数组
val arr=Array(1,3,5)
arr match{
case Array(1,x,y) =>println(x+"---"+y) // 会分别将数组中的元素复制给x,y
case Array(1,_*) =>println("1...")
case Array(0) =>println("only 0")
case _ =>println("something else")
}
}
//匹配集合
object CaseDemo04 extends App{
val list=List(0,3,6)
list match {
case 0::Nil => println("only 0") // Nil表示空
case 0::tail => println("0....") // :: 表示追加到列表,tail表示列表尾部
case x::y::z::Nil => println(s"x:$x y:$y z:$z")
case _ => println("something else") // _ 表示啥也没匹配到,即default
}
}
//匹配元组
object CaseDemo05 extends App{
val tuple=(1,3,5)
tuple match{
case (1,x,y) => println(s"1,$x,$y")
case (2,x,y) => println(s"$x,$y")
case _ => println("others...")
}
}
样例类
- 样例类是一种特殊类,它可以用来快速定义一个用于保存数据的类
- 使用
case class
关键字
// 定义一个样例类
// 样例类有两个成员name、age
case class CasePerson(name:String, age:Int)
// 使用var指定可变成员变量
case class CaseStudent(var name:String, var age:Int)
object CaseClassDemo {
def main(args: Array[String]): Unit = {
// 1. 使用new创建实例
val zhangsan = new CasePerson("张三", 20)// 一般不这样做(意思是也可以)
println(zhangsan)
// 2. 使用类名直接创建实例
val lisi = CasePerson("李四", 21)
println(lisi)
val xiaohong = CaseStudent("小红", 23)
xiaohong.age = 24
println(xiaohong)
}
}
- 样例对象时可以序列化的,先了解即可
case class SubmitTask(id: String, name: String)
case class HeartBeat(time: Long)
case object CheckTimeOutTask
object CaseDemo06 extends App{ // 继承App特质
val arr = Array(CheckTimeOutTask,
HeartBeat(10000),
SubmitTask("0001", "task-0001"))
arr(Random.nextInt(arr.length)) match {
case SubmitTask(id, name) => println(s"id=$id, name=$name")
case HeartBeat(time) => println(s"time=$time")
case CheckTimeOutTask => println("检查超时")
case _ => println("what???")
}
}
Option类型
- Option[T] 是一个类型为 T 的可选值的容器
- 如果值存在, Option[T] 就是一个 Some[T] ,如果不存在, Option[T] 就是对象 None
- 包含两个子类:
- Some包装了某个值
- None表示没有值
val myMap: Map[String, String] = Map("key1" -> "value")
val value1: Option[String] = myMap.get("key1") // 我希望是String类型的
val value2: Option[String] = myMap.get("key2")
println(value1) // Some("value1")
println(value2) // None
object TestOption {
def main(args: Array[String]) {
val map = Map("a" -> 1, "b" -> 2)
val value: Option[Int] = map.get("b") // 我希望是Int类型的
val v1 =value match {
case Some(i) => i
case None => 0
}
println(v1) // 2
//更好的方式
val v2 = map.getOrElse("c", 0)
println(v2) // 0
}
}
偏函数
- 定义一个函数,只接受和处理其参数定义域范围内的子集,对于参数范围外的参数则抛出异常
- 它是
PartialFunction[A, B]
的一个实例 - A代表输入参数类型
- B代表返回结果类型
- 实例得到的偏函数是一个特质trait
object TestPartialFunction {
// func1是一个输入参数为Int类型,返回值为String类型的偏函数
val func1: PartialFunction[Int, String] = {// 使用定义方法的格式
case 1 => "一"
case 2 => "二"
case 3 => "三"
case _ => "其他"
}
func1(1) // String: 一
def main(args: Array[String]): Unit = {
val list=List(1,2,3,4,5,6)
// filter继承PartialFunction的特质
val result=list.filter{
case x if x >3 => true
case _ => false
}
println(result) // List(4,5,6)
}
}
小结
这里主要介绍了scala面向对象编程中经常用到的方法,每种特性都有适用的场合。还有很多可以扩展的地方,可以根据场景再次拓宽。下一节从异常处理开始,总结其基本概念和用法。