Scala 类和对象
类是对象的抽象,而对象是类的具体实例。类是抽象的,不占用内存,而对象是具体的,占用存储空间。类是用于创建对象的蓝图,它是一个定义包括在特定类型的对象中的方法和变量的软件模板。
scala 单例对象
在 Scala 中, 是没有 static 这个东西的, 但是它也为我们提供了单例模式的实现方法, 那
就是使用关键字 object, object 对象不能带参数。就是说每次调用这个单例对象的时候,不需要new,直接ScalaSingleton.saySomething(“滚犊子…”)就完事,参数也是。
/**
* 单例对象
*/
object ScalaSingleton {
def saySomething(msg: String) = {
println(msg)
}
}o
bject test {
def main(args: Array[String]): Unit = {
ScalaSingleton.saySomething("滚犊子....")
println(ScalaSingleton)
println(ScalaSingleton)
// 输出结果:
// 滚犊子....
// cn.demo.ScalaSingleton$@28f67ac7
// cn.demo.ScalaSingleton$@28f67ac7
}
}
类的定义和实例化
-
在 Scala 中, 类并不用声明为 public,而是用class。
-
我们可以使用class定义类,然后用 new 关键字来创建类的对象,
-
Scala 的类定义可以有参数,称为类参数。
-
如果你没有定义构造器, 类会有一个默认的空参构造器
关于类中变量的定义 -
var 修饰的变量, 这个变量对外提供 getter setter 方法
-
val 修饰的变量, 对外提供了 getter 方法,没有 setter
-
如果主构造器中成员变量属性没有val var修饰的话,该属性不能被访问,相当于没有对外提供get
实例如下:
import java.io._
//没传入构造器,默认空参构造器
class Student {
// _ 表示一个占位符, 编译器会根据你变量的具体类型赋予相应初始值
// 注意: 使用_ 占位符是, 变量类型必须指定
var name: String = _ //用var定义了一个参数,可以getter setter
// val age: Int = _错误代码, val 修饰的变量不能使用占位符
val age: Int = 10 //用val定义了一个参数,可以getter,不可setter,所以不可变
}
def birthday(age1: Int) { //编写一个方法birthday
age=age+1
println ("我已经" + age1+"岁了");
}
}
object Test{
val name: String = "zhangsan" //使用setter功能中,设立参数
def main(args: Array[String]): Unit = {
// 调用空参构造器,可以加() 也可以不加
val student = new Student() //new 来实例化类
student.name = "laoYang" //使用setter功能中,设立参数,可改变参数值
// 类中使用 val 修饰的变量不能更改
// student.age = 20
println(s"student.name ====== ${student.name} ${student.age}") //用getter功能中,即调出该对象的函数
student.birthday(student.age) //调用birthday该方法
}
以上实例的类定义了两个变量age和name,一个方法:birthday,方法没有返回值。
我们可以使用 new 来实例化类,并访问类中的方法和变量:
主构造器/辅助构造器
主构造器
定义在类后面的为类主构造器, 一个类可以有多个辅助构造器
定义个 2 个参数的主构造器
Student1(val name: String, var age: Int)
如果有了主构造器,之前的构造方法就可以不要了(下面的不要了)
//var name:String= _ //初始值是一个null
//var age:Int= _
辅助构造器
辅助构造器, 使用 def this
在辅助构造器中必须先调用类的主构造器
def this(name: String, age:Int, gender: String){
this(name, age) //在辅助构造器中必须先调用类的主构造器
this.gender = gender
}
实例
class Student1 (val name: String, var age: Int) { //主构造器
var gender: String = _
def this(name: String, age:Int, gender: String){ //辅助构造器
this.gender = gender
object Test1{
def main(args: Array[String]): Unit = {
val s = new Student1("laoduan", 38) //调用主构造器
println(s"${s.name} ${s.age}")
val s1 = new Student1("laoYang", 18, "male") //调用辅助构造器
println(s"${s1.gender}")
}
}
}
}
访问权限问题
构造器的访问权限
在构造器前面加修饰权限
说明主构造器是私有的,外部类或外部对象不能访问
class Teacher private (name:String,age:Int){`
同样也适用于辅助构造器
private def this(name: String, age:Int, gender: String){
成员变量访问权限
成员变量前面如果加上了 private 修饰, 也就意味着, age 只能在这个类的内部以及其伴生类对象中可以访问修改, 其他外部类不能访问
class Student3 private (val name: String, private var age: Int) {
类的访问权限
类的前面加上private[this]标识这个类在当前包下都可见,当前包下的子包及其其他包不可见,伴生对象不能访问
类的前面加上private[包名]标识这个类在该包下及其子包可见
伴生对象/伴生类
在 Scala 中,是没有 static 这个东西的,但是它也为我们提供了单例模式的实现方法,那就是使用关键字 object。
Scala 中使用单例模式时,除了定义的类之外,还要定义一个同名的 object 对象,它和类的区别是,object对象不能带参数。可以不用new。
当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象:companion object。你必须在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类:companion class。类和它的伴生对象可以互相访问其私有成员。
伴生对象实例
/* 文件名:Marker.scala
- author:菜鸟教程
- url:www.runoob.com
*/
class Student3 private (val name: String, private var age: Int) {
var gender: String = _
def this(name: String, age:Int, gender: String){
this(name, age)
this.gender = gender
}
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}") 伴生类不能访问
}
}
apply 方法在伴生对象中的应用
class Student3 private (val name: String, private var age: Int) {
var gender: String = _
def this(name: String, age:Int, gender: String){
this(name, age)
this.gender = gender
}
def getAge = 18
}
伴生对象
object Student3 {
def apply(name:String,age:Int):Student3 ={
//apply方法中,可以对类进行初始化的工作,到时别人调用时候就可以直接调用不用new
new Student3 (name,age)
}
def main(args: Array[String]): Unit = {
val Student =Student3 ("林",12) //不用new
}
}
特质
Scala Trait(特质) 相当于 Java 的接口, 实际上它比接口还功能强大。
与接口不同的是, 它还可以定义属性和方法的实现。
一般情况下 Scala 的类只能够继承单一父类, 但是如果是 Trait(特质) 的话就可以继承多个,实现了多重继承。 使用的关键字是 trait .
trait T2 {
// 定义一个属性
val className: String = "NB 大神班"
// 定义一个没有实现的方法
def teacherSay(name: String)
// 定义个带有具体的实现的方法
def doSomething() = {
println("改吃中午饭了...")
以上Trait(特征)由两个方法组成:isEqual 和 isNotEqual。isEqual 方法没有定义方法的实现,isNotEqual定义了方法的实现。子类继承特征可以实现未被实现的方法。
- 父类已经实现了的功能, 子类必须使用 override 关键字重写
- 父类没有实现的方法, 子类必须实现
抽象类
在 Scala 中, 使用 abstract 修饰的类称为抽象类. 在抽象类中可以定义属性、 未实现的方法和
具体实现的方法。
- 使用关键字abstract 定义一个抽象类
- 可以没有具体实现的方法
- 也可以有具体实现的方法
/*
* abstract 修饰的类是一个抽象类
* */
abstract class Animal {
println("Animal's constructor ....")
// 定义一个 name 属性
val name: String = "animal"
// 没有任何实现的方法
def sleep()
// 带有具体的实现的方法
def eat(f: String): Unit = {
println(s"$f")
}
}
混入特质
混入特质的两种方式
方式一,在创建该类的实例的时候混入特质
val student = new Student with Fly with ScalaTrait
//student.hello("老羊")
student.fly("gaoxing")
student.small("丁丁")
方式二,在定义该类的时候混入特质
object ScalaTraitImpl extends ScalaTrait with Fly{
// 如果特质中某个方法有具体的实现,在子类继承重写的时候,必须使用override关键字
override def small(name: String): Unit = {
println(s"丁丁 对 $name 哈哈大笑")
}}
继承
承是面向对象的概念, 用于代码的可重用性。 被扩展的类称为超类或父类, 扩展的类称为派生类或子类。 Scala 可以通过使用 extends 关键字来实现继承其他类或者特质。
-
with 后面只能是特质
-
父类已经实现了的功能, 子类必须使用 override 关键字重写
-
父类没有实现的方法, 子类必须实现
class Dog extends Animal {
println("Dog's constructor ...")
override val name: String = "Dog"
def sleep(): Unit = {
println("躺着睡...")
}
override def eat(f: String): Unit = {
println("")
}
}
scala中可以实现一个类同时继承多个特质
- 在scala中第一个继承抽象类或者特质,只能使用关键字extends
- 如果想继承多个特质的话,可以在extends之后使用with关键字
package cookBook.chapter8
//轮胎
trait tire{
def run: Unit ={
println("I can run fast")
}
}
//方向盘
trait SteeringWheel{
def control: Unit ={
println("I can control the cars'direction")
}
}
//同时继承多个特质
class Roadster extends tire with SteeringWheel{
def display(): Unit ={
println("I'm a Roadster")
}
}
//敞篷跑车
object Roadster extends App{
var roadster = new Roadster
roadster.display()
roadster.run
roadster.control
}
final 关键字
- 被 final 修饰的类不能被继承;
- 被 final 修饰的属性不能重写;
- 被 final 修饰的方法不能被重写。
type 关键字
Scala 里的类型, 除了在定义 class,trait,object 时会产生类型, 还可以通过 type 关键字来声明
类型。
type 相当于声明一个类型别名,就是用S代替String::
// 把 String 类型用 S 代替
type S = String
val name: S = "小星星"
println(name)
通常 type 用于声明某种复杂类型, 或用于定义一个抽象类型
模式匹配
-
Scala 提供了强大的模式匹配机制,应用也非常广泛。
-
一个模式匹配包含了一系列备选项,每个都开始于关键字 case。每个备选项都包含了一个模式及一到多个表达式。箭头符号 => 隔开了模式和表达式。
-
match 对应 Java 里的 switch,但是写在选择器表达式之后。即: 选择器 match {备选项}。
-
match 表达式通过以代码编写的先后次序尝试每个模式来完成计算,只要发现有一个匹配的case,剩下的case不会继续匹配。
object ScalaMatchCase {
def main(args: Array[String]): Unit = {
// 匹配字符串内容--------------------------------------
def contentMatch(str: String) = str match {
case "hello" => println("hello")
case "Dog" => println("Dog")
case "1" => println("1")
case "1" => println("2")
case _ => println("匹配不上") //_表示默认的全匹配备选项,即没有找到其他匹配时的匹配项,类似 switch 中的 default。
}
println("----------匹配字符串内容--------")
contentMatch("hello")
contentMatch("Dog")
contentMatch("1")
contentMatch("fuck")
// 匹配数据类型--------------------------------------
println("----------匹配数据类型--------")
def typeMatch(tp: Any) = tp match {
case x: Int => println(s"Int $x")
case y: Long => println(s"Long $y")
case b: Boolean => println(s"boolean $b")
case _ => println("匹配不上")
}
typeMatch(1)
typeMatch(10L)
typeMatch(true)
typeMatch("Scala")
// 匹配Array-----------------------------------
println("----------匹配Array--------")
def arrayMatch(arr: Any) = arr match {
case Array(0) => println("只有一个0元素的数组")
case Array(0, _) => println("以0开头的,拥有2个元素的数组")
case Array(1, _, 3) => println("已1开头,3结尾,中间为任意元素的三个元素的数组")
case Array(8, _*) => println("已8开头,N个元素的数组") // _*标识0个或者多个任意类型的数据
}
arrayMatch(Array(0))
arrayMatch(Array(0, "1"))
arrayMatch(Array(1, true, 3))
arrayMatch(Array(8,9,10,100,666))
// 匹配List-------------------------------
def listMatch(list: Any) = list match {
case 0 :: Nil => println("只有一个0元素的List")
case 7 :: 9 :: Nil => println("只有7和9元素的List")
case x :: y :: z :: Nil => println("只有三个元素的List")
case m :: n if n.length > 0 => println("------") // 拥有head,和 tail的数组 if n.length > 0 守卫
case _ => println("匹配不上")
}
listMatch(List(0))
listMatch(List(7,9))
listMatch(List(8,9, 666))
listMatch(List(666))
//匹配元组----------------------------------
val tup = (1, 3, 7)
def tupleMatch(tuple:Any)=tuple match {
case (0,_) => println(“元组第一个元素为0,第二个是任意数据,且只有两个”)
case (x,m,k) => println("拥有是哪个元素的元组")
case(_,"AK47")=>println("第一个任意,,第二个ak47,两个元素")
//错误案例 case (666, _*, 5) => println(w) //有数量限制
case _ => println("else")
}
//匹配对象---------------------------------
//样例类, 模式匹配, 封装数据(多例) ,不用 new 即可创建实例
case class SubmitTask(id: String, name: String)
case class HeartBeat(time: Long)
//样例对象, 模式匹配(单例)
case object CheckTimeOutTask
def objmatch(obj:Any) =obj match{
case SubmitTask(id, name) => {
println(s"$id, $name")
}
case HeartBeat(time) => {
println(time)
}
case "字符串" => {
println("字符串")
}
样例类/样例对象/模式匹配
-
样例类,使用 case 关键字 修饰的类, 其重要的特征就是支持模式匹配
-
样例类默认是实现了序列化接口
//样例类, 模式匹配, 封装数据(多例) ,不用 new 即可创建实例
case class SubmitTask(id: String, name: String)
case class HeartBeat(time: Long)
//样例对象, 模式匹配(单例)
case object CheckTimeOutTask
val arr = Array(CheckTimeOutTask, new HeartBeat(123),
HeartBeat(88888), new HeartBeat(666), SubmitTask("0001",
"task-0001"))
val i = Random.nextInt(arr.length)
val element = arr(i)
println(element)
element match {
case SubmitTask(id, name) => {
println(s"$id, $name")
}c
ase HeartBeat(time) => {
println(time)
}c
ase CheckTimeOutTask => {
println("check")
}
}