Scala 是一门多范式(multi-paradigm)的编程语言,设计初衷是要集成面向对象编程和函数式编程的各种特性。Scala 运行在 Java 虚拟机上,并兼容现有的 Java 程序。
下面是我的scala学习笔记连接:
一、类和对象
scala是支持面向对象的,也有类和对象的概念,依然可以基于scala语言来开发面向对象的应用程序
1、创建类和对象
- 使用class定义一个类
- 使用new来创建对象
示例:
创建一个Person
类,并创建它的对象
步骤:
- 在idea中创建一个Scala项目,并创建一个Object
- 添加main方法
- 创建类和对象
package com.wzq.scala.oop
object _01ClassObject {
class Person {
}
def main(args: Array[String]): Unit = {
val person = new Person()
println(person)
}
}
执行结果:
简写方式:
- 如果类是空的,没有任何成员,可以省略{}
- 如果构造器的参数为空,可以省略()
示例:
package com.wzq.scala.oop
object _02ClassObject {
// 省略类后面的{}
class Person
def main(args: Array[String]): Unit = {
//构造器没有参数,省略()
val person = new Person
println(person)
}
}
2、定义和访问成员变量
一个类会有自己的属性
- 在类中只要var/val来定义成员变量
- 对象直接使用成员变量名称来访问成员变量
示例:
package com.wzq.scala.oop
object _03ClassObject$ {
//1、定义类
class Person {
//2、定义成员变量
var name: String = ""
var age: Int = 0
}
def main(args: Array[String]): Unit = {
//3、创建对象,访问成员变量,设置成员变量的值
val person = new Person
person.name = "张三"
person.age = 20
println(person.name)
println(person.age)
}
}
3、使用下划线初始化成员变量
scala中有一个更简介的初始化成员变量的方式,可以让代码看起来更加简介
- 在定义var类型的成员变量时,可以使用
_
来初始化成员变量- String => null
- Int => 0
- Boolean => false
- Double => 0.0
- …
- val类型的成员变量,必须自己手动初始化
package com.wzq.scala.oop
object _04ClassObject {
class Person {
var name: String = _
var age: Int = _
}
def main(args: Array[String]): Unit = {
val person = new Person
person.name = "wzq"
person.age = 20
println(person.name + " " + person.age)
}
}
4、定义成员方法
类可以有自己的行为,scala中也可以通过定义成员方法来定义类的行为
在scala的类中,也是使用def
来定义成员方法
package com.wzq.scala.oop
object _05ClassObject {
class Customer {
var name: String = _
var sex: String = _
//定义成员方法
def printHello(msg: String) = println(msg)
}
def main(args: Array[String]): Unit = {
val customer = new Customer
customer.printHello("你好")
}
}
5、访问修饰符
和java应用,scala可以通过访问修饰符来控制成员变量和成员方法是否可以被访问
Java中的访问控制同样适用于scala,可以在成员签名添加private/protected
关键字来控制成员的可见性
但在scala
中,没有public
关键字,任何没有被标为private
或protected
的成员都是公共的
示例:定义一个Person
实体类,包含setter、getter方法
package com.wzq.scala.oop
object _06ClassObject {
class Person{
//定义成员变量,使用类型推断
private var name = ""
private var age = 0
def getName() = this.name
def setName(name:String) = this.name = name
def getAge() = this.age
def setAge(age:Int) = this.age = age
//获取姓名和年龄,返回
private def getNameAndAge() = (this.name,this.age)
}
def main(args: Array[String]): Unit = {
val person = new Person
person.setAge(18)
person.setName("wzq")
println(person.getAge() + " " + person.getName())
}
}
6、类的构造器
当创建类对象的时候,会自动调用类的构造器。之前使用的都是默认构造器,接下来学习如何自定义构造器
1)主构造器
Java定义的构造器:
class Person{
private String name;
private Integer age;
public Person(String name,Integer age){
this.name = name;
this.age = age;
}
}
在scala中,可以用更简单的语法来实现,语法:
class 类型(var/val 参数名:类型 = 默认值, var/val 参数名:类型 = 默认值){
//构造代码块
}
Note
- 主构造器的参数列表是直接定义在类名后面,添加了val/var表示直接通过主构造器定义成员变量
- 构造器参数列表可以指定默认值
- 创建实例,调用构造器可以指定字段进行初始化
- 整个class中除了字段定义和方法定义的代码都是构造代码
示例:
- 定义一个Person类,通过主构造器参数列表定义姓名和年龄字段,并且设置他们的默认值
- 在主构造器中输出“调用主构造器”
- 创建“张三”对象(姓名 = 张三,年龄 = 20),打印对象的姓名和年龄
- 创建“空”对象,不给构造器传入任何的参数,打印对象的姓名和年龄
- 创建“man40”对象,不传入姓名参数,指定年龄为40,打印…
package com.wzq.scala.oop
object _07ClassObject {
//创建Person类,使用主构造器来进行字段定义和默认值赋值
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)
println("-----------")
val empty = new Person()
println(empty.name)
println(empty.age)
println("-----------")
val man40 = new Person(age = 40)
println(man40.name)
println(man40.age)
}
}
2)辅助构造器
在scala中,除了定义主构造器外,还可以根据需要来定义辅助构造器。
例如:运行通过多种方式,来创建对象,这时候就可以定义其他更多的构造器,我们把除了主构造器之外的构造器称为辅助构造器
语法:
- 定义辅助构造器与定义方法一样,也使用def关键字来定义
- 整个方法的名字为this
def this(参数名:类型,参数名:类型....){
//第一行需要调用主构造器或者其他构造器
//构造器代码
}
Attention
辅助构造器的第一行代码,必须要调用主构造器或者其他赋值构造器
示例:
- 定义一个
Customer
类,包含一个姓名和地址字段 - 定义
Customer
类的主构造器(初始化姓名和地址) - 定义
Customer
类的辅助构造器,该辅助构造器接收一个数字参数,使用数组参数来初始化成员变量 - 使用Person类的赋值构造器来创建一个"zhangsan"类型,姓名为张三,地址为背景
- 打印对象的姓名、地址
package com.wzq.scala.oop
object _08ClassObject {
class Customer(var name: String = "", var address: String = "") {
//定义辅助构造器
def this(arr: Array[String]) = {
//辅助构造器必须要调用主构造器或者其他辅助构造器
this(arr(0), arr(1))
}
}
def main(args: Array[String]): Unit = {
//使用辅助构造器来创建对象
val customer = new Customer(Array("张三","郑州"))
println(customer.name + " " + customer.address)
}
}
7、单例对象
scala中没有Java中的静态成员,我们想要定义类似于Java的static变量、static方法,就要使用到scala中的单例对象——object
1)定义单例对象
单例对象表示全局仅有一个对象(类似Java static概念)
- 定义单例对象和定义类很像,就是把class换成object
- 在object中定义的成员变量类似于Java的静态变量
- 可以使用object直接引用成员变量
示例:
- 定义一个Dog单例对象,保存狗有几条腿
- 在main方法中打印狗腿子的数量
package com.wzq.scala.oop
object _09ClassObject {
// 创建一个单例对象
object Dog{
//定义单例对象的成员
//类似与Java的static变量
val LEG_NUM = 4
}
// 访问单例对象中的成员变量
def main(args: Array[String]): Unit = {
//直接访问
println(Dog.LEG_NUM)
}
}
2)在单例对象中定义成员方法
在object中定义成员方法类似于Java的静态方法
示例:
- 设计一个单例对象,定义一个能够打印分割线(15个减号)的方法
- 在main方法调用该方法,打印分割线
package com.wzq.scala.oop
object _10ClassObject {
object PrintUtil{
//打印分割线
def printSpliter(): Unit = {
for(i <- 1 to 10) print("-")
}
}
def main(args: Array[String]): Unit = {
PrintUtil.printSpliter()
}
}
3)工具类案例
需求:
- 编写一个DateUtil工具类专门用来格式化日期时间
- 定义一个方法,用于将日期转换为年月日字符串,例如:2021-4-11
步骤:
- 定义一个DateUtil单例对象,定义日期格式化方法(format)
- 使用
SimpleDateFormat
将日期转换为字符串
package com.wzq.scala.oop
import java.text.SimpleDateFormat
import java.util.Date
object _11ClassObject {
// 定义一个DateUtil工具类
object DateUtil{
val simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd")
// 定义一个用于日期格式化的方法
def format(date:Date) = simpleDateFormat.format(date)
}
// 测试格式化日期
def main(args: Array[String]): Unit = {
val date = new Date()
println(DateUtil.format(date))
}
}
4)main方法
scala和Java一样,如果要运行一个程序,必须要有一个main方法,而在Java中main方法是static的,而在scala中没有静态方法。在scala中,整个main方法必须放在一个单例对象中
定义main方法:
def main(args:Array[String]):Unit = {
// 方法体
}
示例:创建一个单例对象,在该单例对象中打印”hello,scala“
package com.wzq.scala.oop
object _12ClassObject {
def main(args: Array[String]): Unit = {
println("hello,scala")
}
}
5)实现App Trait定义入口
创建一个object
,继承自App Trait
(特质),然后将需要编写在main方法中的代码,写在object的构造方法体内
object 单例对象名 extend App{
//方法体
}
示例:继承App特质,来实现一个入口,同样输出"hello,scala"
package com.wzq.scala.oop
object _13ClassObject extends App {
println("hello,scala")
}
8、伴生对象
在Java中,经常会有一些类,同时有示例成员又有静态成员,例如:
public class CustomerService{
private static String SERVICE_NAME = "CustomerService";
public void save(){
System.out.println(SERVICE_NAME+":保存客户");
}
public static void main(String[] args){
new CustomerService.save();
}
}
在scala中,要实现类似的效果,可以使用伴生对象来实现
我们还可以使用伴生对象来实现快速创建对象,例如:
//无需使用new就可以快速来创建对象
val a = Array(1,2,3)
val b = Set(1,2,3)
1)定义伴生对象
一个class和object具有同样的名字。这个object称为伴生对象,这个class称为伴生类
- 伴生对象必须要和伴生类一样的名字
- 伴生对象和伴生类在同一个scala源文件中
- 伴生对象和伴生类可以互相访问private属性
示例:
- 编写一个CustomerService类,有一个save方法,打印”服务类名称:保存客户“
- 编写一个CustomerService伴生对象,定义一个私有变量,用于保存服务类名称
- 创建CustomerService对象,调用save方法
package com.wzq.scala.oop
object _14ClassObject {
// 创建伴生类
class CustomerService{
def save() = println(CustomerService.SERVICE_NAME + ":保存客户")
}
// 创建伴生对象
object CustomerService{
//定义私有变量
private val SERVICE_NAME = "CustomerService"
}
// 创建对象,调用方法
def main(args: Array[String]): Unit = {
val service = new CustomerService
service.save()
}
}
2)private[this]访问权限
如果某个成员的权限设置为private[this]
,表示只能在当前类中访问,伴生对象也不可以访问
示例:
- 定义一个Person类,包含一个name字段
- 定义Person类的伴生对象,定义printPerson方法
- 测试伴生对象是否能访问private[this]权限的成员
package com.wzq.scala.oop
object _15ClassObject {
//创建Person类,定义一个private[this]成员变量
class Person(private[this] var name: String)
//创建Person伴生对象,在伴生对象中,测试访问成员变量
object Person {
def pirntPerson(p: Person) = println(p.name)
}
//调用伴生对象方法
def main(args: Array[String]): Unit = {
Person.pirntPerson(new Person("zhangsan"))
}
}
因为不能访问private[this]
成员变量,所以上面的代码是错误的,会报以下错误:
Error:(10, 44) value name is not a member of com.wzq.scala.oop._15ClassObject.Person
def pirntPerson(p: Person) = println(p.name)
3)apply方法
之前使用过下面这种方式来创建一个Array对象
val a = Array(1,2,3,4)
这种写法非常简单,不需要再写一个new,然后敲一个空格,再写类名,可以通过伴生对象的apply方法来实现
定义apply方法:
object 伴生对象名{
def apply(参数名:参数类型,参数名:参数类型...) = new 类(...)
}
创建对象:
伴生对象名(参数1,参数2...)
示例:
- 定义一个Person类,它包含两个字段:姓名和年龄
- 重写apply方法,使用Person类名就可以创建对象
- 在main方法中创建该类的对象,并打印姓名和年龄
package com.wzq.scala.oop
object _16ClassObject {
class Person(var name: String = "", var age: Int = 0)
object Person {
//定义apply方法,接收两个参数
def apply(name:String,age:Int) = new Person(name,age)
}
def main(args: Array[String]): Unit = {
val person = Person("wzq",18)
println(person.name + " " + person.age)
}
}
9、继承
scala语言是支持面向对象编程的,我们也可以使用scala来实现继承,通过继承来减少重复代码
1)类继承
定义语法
- scala和Java一样,使用
extends
关键字来实现继承 - 可以在子类中定义父类中没有的字段和方法,或者重写父类的方法
- 类和单例对象都可以从某个父类继承
语法:
class/object 子类 extends 父类{
...
}
示例:
package com.wzq.scala.oop
object _17ClassObject {
class Person(){
private var name:String = _
def getName() = this.name
def setName(name:String) = this.name = name
}
class Student extends Person
def main(args: Array[String]): Unit = {
val student = new Student
student.setName("wzq")
println(student.getName())
}
}
2)单例对象继承
示例:
- 创建一个Student单例对象,让单例对象继承Person类
- 设置单例对象的名字为张三,调用Student单例对象的getName方法
package com.wzq.scala.oop
object _18ClassObject {
class Person() {
private var name: String = _
def getName() = this.name
def setName(name: String) = this.name = name
}
object Student extends Person
def main(args: Array[String]): Unit = {
Student.setName("wzq")
println(Student.getName())
}
}
3)override和super
类似于Java,在子类中使用override需要来重写父类的成员,可以使用super来引用分类
用法:
- 子类要覆盖父类中的一个方法,必须要使用override关键字
- 使用override来重写一个val字段
- 使用super关键字来访问符文的成员方法
package com.wzq.scala.oop
object _19ClassObject {
class Person{
val name:String = ""
def getName = 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)
println(student.getName)
}
}
10、类型推断
有时候,我们设计的程序,要根据变量的类型来执行对应的逻辑
在scala中,有两种方式进行类型判断:
- isInstanceOf
- getClass/classOf
1)isInstanceOf / asInstanceOf
在Java中,可以使用instanceOf
关键字来判断类型、以及object来进行类型判断,在scala中如何实现呢?
scala中对象提供isInstanceOf和asInstanceOf方法
- isInstanceOf判断对象是否为指定类的对象
- asInstanceOf将对象转换为指定类型
用法:
//判断对象是否为指定类型
val trueOrFalse:Boolean = 对象.isInstanceOf[类型]
//将对象转换为指定类型
val 变量 = 对象.asInstanceOf[类型]
示例:
- 定义一个Person类
- 定义一个Student类继承自Person类
- 创建一个Student类对象
- 判断该对象是否为Student类型,如果是,将其转换为Student类型并打印该对象
package com.wzq.scala.oop
object _20ClassObject {
// 创建Person
class Person
// 创建Student继承Person
class Student extends Person
//测试
def main(args: Array[String]): Unit = {
val student = new Student
if (student.isInstanceOf[Student]) {
//是student类型
var student1 = student.asInstanceOf[Student]
println(student1)
} else {
//不是
println("不是student类型")
}
}
}
2)getClass 和 classOf
isInstanceOf
只能判断对象是否为指定类及其子类的对象,而不能精确的判断出,对象就是指定类的对象。
如果要求精确的判断出对象就是指定类对象,那么就只能使用getClass
和classOf
用法:
- p.getClass可以精确获取对象的类型
- classOf[x]可以精确获取类型
- 使用 == 操作符可以直接比较类型
示例:
- 定义一个Person
- 定义Student继承Person
- 创建Student对象,并指定它的类型是Person
- 测试isInstance判断该对象是否为Person
- 测试使用getClass/classOf判断该对象是否为Person
- 测试使用getClass/classOf判断该对象是否为Student
package com.wzq.scala.oop
object _21ClassObject {
class Person
class Student extends Person
def main(args: Array[String]): Unit = {
val student: Person = new Student
if (student.isInstanceOf[Person]) {
println("isInstanceOf:student是Person类型")
} else {
println("isInstanceOf:student不是Person类型")
}
println("-" * 10)
if (student.getClass == classOf[Person]) {
println("getClass/classOf:student是Person类型")
} else {
println("getClass/classOf:student不是Person类型")
}
println("-" * 10)
if (student.getClass == classOf[Student]) {
println("getClass/classOf:student是Student类型")
} else {
println("getClass/classOf:student不是Student类型")
}
}
}
11、抽象类
如果类的某个成员在当前类中的定义是不包含完整的,他就是一个抽象类
不完整定义有两种情况:
- 方法没有方法体(抽象方法)
- 变量没有初始化(抽象字段)
和Java一样,在类前加abstract
关键字
abstract class 抽象类名{
val 抽象字段名:类型
def 方法名(参数:参数类型,参数:参数类型...):返回类型
}
示例:
package com.wzq.scala.oop
object _22ClassObject {
abstract class shape {
def area(): Double
}
class Square(var edge: Double) extends shape {
override def area(): Double = edge * edge
}
class Rectangle(var length: Double, var width: Double) extends shape {
override def area(): Double = length * width
}
class Circle(var radius: Double) extends shape {
override def area(): Double = Math.PI * radius * radius
}
}
12、匿名内部类
val/var 变量名 = new 类/抽象类{
//重写方法
}
二、特质
scala中没有Jav中的接口(interface),替代的概念是——特质
1、定义
- 特质是scala中代码复用的基础单元
- 它可以将方法和字段定义封装起来,然后添加到类中
- 于类继承不一样的是,类继承要求每个类都只能继承一个超类,而一个类可以添加任意数量的特质
- 特质的定义和抽象类的定义很像,但它只有
trait
关键字
定义特质:
trait 名称{
//抽象字段
//抽象方法
}
继承特质:
class 类 extends 特质1 with 特质2{
//字段实现
//方法实现
}
- 使用
extends
来继承trait
- 如果要继承多个trait,则使用
with
关键字
2、trait作为接口使用
1)继承单个trait
package com.wzq.scala.trait_demo
object _01TraitDemo {
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("NullPointer Exception...")
}
}
2)继承多个trait
package com.wzq.scala.trait_demo
object _02TraitDemo {
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 = "接受消息:你好,我是wzq"
}
}
3)object继承trait
package com.wzq.scala.trait_demo
object _03TraitDemo {
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("我叫wzq")
}
}