Scala包
- 一个源文件中可以有多个packge
- 子包中的类可以直接访问父包中的内容,无需导包
实例:
package com{// 在外层包中定义单例对象object Outer{var out: String = "out"def main(args: Array[String]): Unit = {Inner // 不能在外层调用内层对象//通过导入包可以访问import com.atguigu.scala.Innerprintln(Inner.in)}}package atguigu{package scala{// 内层包中定义单例对象Object Inner{val in: String = "in"def main(args: Array[String]): Unit = {println(Outer.out) // 访问Outer.out = "outer" // 更改}}}}}// 在同一文件中定义多个包package aaa{package bbb{import com.atguigu.scala.Innerobject Test{def main(args: Array[String]): Unit = {println(Inner.in)}}}}
包对象
- 与包同名的对象,定义在包对象中的成员,作为其对应包下所有class和object的共享变量,可以直接访问 ```scala package object chapter{ // 定义当前包共享的属性和方法 val commonValue = “NJU” def commonMethod() = { println(s”我们在${commonValue}学习”) } }
// 在同一个包下可以调用
- 必须在同一层级下调用<a name="nsa2J"></a>### 导包1. import可以在顶部使用1. 局部导入。作用范围内可以使用1. 通配符导入: import java.util._1. 给类起名:import java.util.{ArrayList => JL} // 将ArrayList类起别名为JL1. 导入相同包的多个类:import java.util.{HashSet, ArrayList}1. 屏蔽类:import java.util.{ArrayList => _,_} //导入util下的所有包,不导入ArrayListScala中三个默认导入:1. import java.lang._1. import scala._1. import scala.Predef._<a name="APRqc"></a>## 类和对象<a name="IBmEH"></a>### 定义类**Java中**<br />如果是public的,则必须和文件名一致<br />一般,一个.java有一个public类<br />**Scala**<br />Scala中没有public(默认就是public)<br />一个.scala中可以写多个类<br />**Class没有实现可以不写大括号**<br />**创建对象时如果构造器为空,创建对象时可以不写小括号**<br />val类型必须手动初始化,var类型可以使用_ 指定默认值```scalaclass Student{// 定义属性private var name: String = "alice"@BeanProperty // 自动创建getter setter...var age: Int = 18var sex: String = _ // 默认初始值}object Test {def main(args: Array[String]): Unit = {// 创建对象val student = new Student()// 不能 student.nameprintln(student.age)student.sex = "female"}}
封装
Scala中的public属性,底层实际为private,并通过get方法和set方法对其进行操作。所以Scala并不推荐将属性设置为private,但由于Java框架都利用反射调用getXXX/setXXX方法,有时候为了兼容,为Scala属性设置getset方法(通过@BeanProperty注解实现)
访问权限
- Scala中属性默认访问权限public,但无该关键字
- private为私有权限,只在类的内部和伴生对象中可用
- protected为受保护权限,Scala中同类子类可以使用,同包无法使用
- private[包名] 增加包访问权限,包名下其它类可以使用
- private可以在伴生类伴生对象中互相访问,private[this] 伴生类都不能访问 ```scala package chapter
// 定义一个父类 class Person { private var idCard: String = “3322” protected var name: String = “alice” var sex String = “female” private[chapter] var age: Int = 18
def printInfo(): Unit = { println(s”Person: $idCard $name $sex $age”) } }
// 定义一个子类 class Worker extends Person { override def printlnInfo(): Unit = { // println(idCard) 不能访问 name = “bob” age = 25 sex = “male” println(s”Worker: $name $sex $age”) } }
object Test { def main(args: Array[String]): Unit = { val person: Person = new Person() // person.idCard //error // person.name // error println(person.age) println(person.sex) person.printInfo()
val worker: Worker = new Worker()worker.printInfo()
} }
<a name="dJlFT"></a>### 构造器Scala类构造器包括主构造器和辅助构造器<br />**主构造器**- 主构造器参数列表直接定义在类名后面,添加了val/var表示直接通过主构造器定义成员变量- 构造器参数列表可以有默认值- 创建实例,调用构造器可以指定字段进行初始化- 整个class中除了字段定义和方法定义的代码都是构造代码```scalaclass 类名(形参列表) { // 主构造器// 类体def this(形参列表) { // 辅助构造器}def this(形参列表) { // 辅助构造器可以有多个}}
object Test {Class Person(val name: String = "张三", val age: Int = 23) {println("调用主构造器")}def main(args: Array[String]): Unit = {val person1 = new Personval person2 = new Person("李四", 24)val person3 = new Person(age =30)
说明:
- 辅助构造器,函数名称this,可以有多个,编译器通过参数个数及类型区分
- 辅助构造方法不能直接创建对象,第一行代码必须直接或间接调用主构造方法
构造器调用其它另外的构造器,要求被调用的必须提前声明 ```scala object Test { def main(args: Array[String]): Unit = { val student1 = new Student //可以省略括号 输出:1. 主构造方法调用 student1.Student() // 输出:一般方法被调用
val student2 = new Student1(“alice”) // 1. 2. 被调用 val student3 = new Student1(“Bob”, 18) // 1.2.3.都被调用 } }
class Student1() { // 括号一般省略 var name: String = //默认null var age: Int = // 默认0
println(“1. 主构造方法调用”)
//辅助构造器 def this(name:String) { this() // 直接调用主构造方法 println(“2. 辅助构造方法”) this.name = name println(s”name: $name age: $age”) }
def this(nameL String, age: Int) { this(name) // 间接调用主构造器 println(“3. 辅助构造方法二被调用”) this.age = age println(s”name: $name age: $age”) }
def Student(): Unit = { println(“一般方法被调用”) } }
<a name="MzXDj"></a>#### 构造器参数- Scala类的主构造器形参包括三种类型:未用任何修饰、var修饰、val修饰1. 未用任何修饰符,这个参数为局部变量1. var,作为类的成员属性使用,可以修改1. val,作为类只读属性使用,不能更改```scalaclass Student1 {var name: String = _ //这里必须是varvar age: Int = _}class Student2(var name: String, var age: Int)// 主构造器参数无修饰符class Student3(name: String, age: Int)object Test {def main(args: Array[String]): Unit = {// 方式一val student1 = new Student1student1.name = "alice"student1.age = 21//如果不想这样设置参数,想像下面一样,就得在类中增加辅助构造器println(s"student1: name = ${student1.name}, age = ${student1.age}")//方式二val student2 = new Student2("Bob", 12)println(s"student2: name = ${student2.name}, age = ${student2.age}")val student3 = new Student3("xxx",111)println(s"student3: name = ${student3.name}, age = ${student3.age}")// 这样就不行,name和age不是student3的属性}}
主构造器+辅助构造器
class Student4(var name: String, var age: Int) {var school: String = _def this(name: String, age: Int, school: String) {this(name,age)this.school = school}def this(arys: Array[String]) {this(arys(0), arys(1))}
apply方法
在Scala中,支持创建对象不写new关键字,对此就要通过伴生对象的apply()方法实现
apply必须在object里
object Test {// 定义Person类class Person(var name: String = "", var age: Int = 0) {}// 定义Person伴生对象object person {// 定义apply方法def apply(name: String, age: Int) = new Person(name,age)}def main(args: Array[String]): Unti = {val p = Person("张三", 23) // 调用apply方法println(p.name, p.age)}
定义时间工具类
object ClassDemo {// 定义工具类object DateUtils {// 定义SimpleDateFormat类型对象var sdf: SimpleDateFormat = null// 定义date2String方法,用来将日期转化成对应字符串def date2String(date: Date, template: String) = {sdf = new SimpleDateFormat(template)sdf.format(date)}// 定义string2Date方法def string2Date(dataString: String, template: String) = {sdf = new SimpleDateFormat(template)sdf.parse(dataString)}}def main(args: Array[String]): Unti = {println(DateUtils.date2String(new Date(), "HH:mm:ss"))println(DateUtils.string2Date("1314年5月22日","yyyy年MM月dd日"))}}
继承和多态
class/object A类 extends B类 {}
- 类和单例对象都可以有父类 ```scala class Person { var name = “” def sayHello() = println(“Hello, Scala..”) }
object Student extends Person
def main(args: Array[String]): Unit = { Student.name = “张三” println(Student.name) Student.sayHello() }
- 继承的调用顺序:父类构造器->子类构造器```scalaobject Test {def main(args: Array[String]): Unit = {val student1 = new Student}}class Person {var name: String = _var age : Int = _println("1.父类主构造器")def this(name: String, age: Int){this()println("2. 父类辅助构造器调用")this.name = namethis.age = age}def printlnInfo(): Unit = {println(s"Person: $name $age")}}class Student(name: String, age: Int) extends Person {var stdNo: String = _println("3.子类主构造器")def this(name: String, age: Int, stdNo: String) {this(name, age)println("4. 子类的辅助构造器")this.stdno = stdNo}override def printInfo(): Unit = {println(s"Person: $name $age $stdNo")}}
多态
在Java中 属性是静态绑定的
// Javaclass Person{String name = "person";public void hello(){System.out.println("hello person")}}class Worker extends Person{String name = "worker"public void hello(){System.out.println("hello worker")}public void hi(){System.out.println("hi worker")}}public class Test{public static void main(String[] args){Person person = new Worker()person.name // person 静态绑定person.hello() // hello worker 动态绑定person.hi() //error}}
- Scala中更加彻底,属性也是动态绑定
```scala
object Test {
def main(args: Array[String]): Unit = {
println(student.name) // student student.hello() // hello student } }val student: Person = new Student
class Person { var name: String = “person” def hello(): Unit = { println(“hello person”) } }
class Student extend Person { var name: String = “student” override def hello(): Unit = { println(“hello student”) } }
<a name="zFKy7"></a>## 抽象类如果类中有抽象字段或抽象方法,那么该类就是一个抽象类- 抽象字段:没有初始化值的变量- 抽象方法:没有方法体的方法<a name="T8RQ7"></a>### 抽象类属性和抽象方法<a name="yVatH"></a>#### 基本语法1. 定义抽象类: abstract class Person()1. 定义抽象属性:val name: String //只有抽象属性没有初始化1. 定义抽象方法:def hello(): String //只声明没有实现- 只要出现抽象属性和方法必须是抽象类- 抽象类可以有具体方法<a name="zZ7Ub"></a>#### 重写和继承1. 如果父类为抽象类,那么子类需要将抽象的属性和方法实现,否则子类也需要声明为抽象类1. 重写非抽象方法需要用override修饰,重写抽象方法可以不加1. 子类中调用父类的方法使用super关键字1. 子类对抽象属性进行实现,父类抽象属性可以用var修饰子类对非抽象属性进行重写,父类非抽象属性只支持val类型,而不支持var,因为var修饰符为可变变量,子类继承之后就可以直接使用,没有必要重写```scalaobject Test {def main(args: Array[String]): Unit = {val student = new Studentstudent.eat() // person eat student eatstudent.sleep() //student sleep}}// 定义抽象类abstract class Person{// 非抽象属性val name: String = "person"// 抽象属性var age: Int// 非抽象方法def eat(): Unit = {println("person eat")}// 抽象方法def sleep(): Unit// 非抽象方法def eat(): Unit = {println("person eat")}}class Student extends Person {var age: Int = 18def sleep(): Unti = {println("student sleep")}// 重写非抽象属性和方法,如果父类是var的,直接改就行了override val name: String = "student"override def eat(): Unit = {super.eat()println("student eat")}}
isInstanceOf & asInstanceOf
- isInstanceOf 判断是否为指定类型对象
- asInstanceOf将对象转换为指定类型 ```scala class Person
class Student extends Person { def sayHello(): Unit = { println(“Hello!”) } }
object Test02 { def main(args: Array[String]): Unit = { val person: Person = new Student if (person.isInstanceOf[Student]) person.asInstanceOf[Student].sayHello() } }
<a name="uEPAi"></a>### getClass & classOf- isInstanceOf只能判断出对象是否为指定类型及其子类的对象,而不能精准的判断出对象就是指定类的对象。如果要求精准的判断出指定数据类型,只能使用一下方法- getClass可以精准获取对象的类型- classOf[类名] 可以精准获取数据类型- 使用==操作符可以直接比较类型```scalaclass Person2class Student2 extends Person2 {def sayHello(): Unit = {println("Hello!")}}object Test03 {def main(args: Array[String]): Unit = {val person: Person2 = new Student2println(person.isInstanceOf[Person2]) //trueprintln(person.isInstanceOf[Student2]) // trueprintln(person.getClass == classOf[Person2]) //falseprintln(person.getClass == classOf[Student2]) //true}}
匿名内部类(抽象类型的子类)
使用场景:
- 成员方法只调用一次
- 可以作为方法的实际参数传递
如果匿名内部类的主构造器为空,小括号可以不写
object Test {def main(args: Array[String]): Unit = {// 1val person: Person = new Person{override var name: String = _override def eat(): Unit = println("person eat1")}.eat()println(person.name)// 2def show(p:person) = p.eat()val person2: Person = new Person{ //实际上是多态override def eat(): Unit = println("person eat2")}show(person2)}}// 定义抽象类abstract class Person {var name: Stringdef eat(): Unit}
单例对象(伴生对象)
Scala语言完全面向对象,所以没有静态操作。但是为了能够和Java交互,就产生了一种特殊的对象来模拟类对象,该对象为单例对象。单例对象与类名一致,类所有静态内容都可以放置在它的半生对象中声明。
- 单例对象采用object关键字
- 单例对象对应的伴生类名称一样
- 单例对象的属性和方法都可以通过伴生对象名(类名)直接调用访问(private也可以) ```scala Object Test { def main(args: Array[String]): Unit = { val student = new Student(“alice”, 18) student.printInfo() } }
// 定义类 class Student(val name: String, val age: Int) { def printInfo() = { println(s”student: name = ${name}, age = $age, school = $Student.school”) } }
// 伴生对象 object Student { val school: String = “atg” }
<a name="Y1xaw"></a>#### 伴生对象调用构造方法```scalaObject Test {def main(args: Array[String]): Unit = {val student = Student.newStudent("alice",14)val student2 = Student.apply("bob",18)val student2 = Student("bob",18) // apply方法可以省略student.printInfo()}}// 定义类class Student private(val name: String, val age: Int) { //构造器私有化def printInfo() = {println(s"student: name = ${name}, age = $age, school = $Student.school")}}// 伴生对象object Student {val school: String = "atg"// 定义了一个类的对象实例创建方法,包装好def newStudent(name: String, age: Int): Student = new Student(name, age)def apply(name: String, age: Int): Student = new Student(name, age)}
- 对于伴生对象中的apply() 调用时可以省略 .apply
使用继承App特质的方式定义程序主入口
object Test extends App{println("")}
单例设计模式
```scala class Student private(val name: String, val age: Int) { //构造器私有化 def printInfo() = { println(s”student: name = ${name}, age = $age, school = $Student.school”) } }
// 饿汉式 object Student { private val student: Student = new Student(“alice”,12) def getInstance(): Student = student }
// 懒汉式 object Student2 { private var student: Student2 = _ def getInstance(): Student2 = { if (student == null) { // 如果没有实例的话,创建一个 student = new Student2(“alice”, 18) } student } }
object Test { def main(args: Array[String]): Unit = { val student1 = Student.getInstance() val student2 = Student.getInstance()
// 引用地址完全一样println(student1)println(student2)
} }
<a name="e2p75"></a>## 特质(Trait)Scala语言中,采用特质来代替接口,也就是说多个类具有相同特质时,就可以将这个特质独立出来,采用trait声明<br />Scala中trait可以有抽象属性和方法,**也可以有具体的属性和方法**,一个类可以混入多个特质,类似于Java中的抽象类```scalatrait 特质名{}class 类名 extends 特质1 with 特质2 // 没有父类class 类名 extends 父类 with 特质1 with 特质2 // 有父类
object Test {def main(args: Array[String]): Unit = {val student = new Studentstudent.sayHello()student.study()student.dating()}class Person {val name: String = "person"var age: Int = 18def sayHello(): Unit = {println("hello from:" + name)}}trait Young{// 声明抽象和非抽象属性var age: Intval name: String = "young"// 非抽象方法def play(): Unit = {println("young people is playing")}// 抽象方法def dating(): Unit}class Student extends Person with Young {// 重写冲突的属性override val name: String = "student"def dating(): Unit = println(s"student $name is dating")def study(): Unit = println(s"student $name is studying")// 重写父类的方法override def sayHello(): Unit = {super.sayHello()println(s"hello froms: student $name")}}
单例对象继承特质
object Test07 {def main(args: Array[String]): Unit = {ConsoleLogger.log("Success")ConsoleLogger.warn("Warning")}}trait Logger {def log(msg: String)}trait Waring {def warn(msg: String)}object ConsoleLogger extends Logger with Waring {override def log(msg: String) = println(s"Log: $msg" )override def warn(msg: String): Unit = println(s"Warn: $msg")}
特质的混入
object Test {def main(args: Array[String]): Unit = {val student = new Studentstudent.study()student.increase()// 动态混入:让某个对象临时可以访问某个特质中的成员val studentWithTalent = new Student with Talent {override def dancing(): Unit = println("dancing")override def singing(): Unit = println("singing")}studentWithTalen.dancing()}}trait Talent {def singing(): Unitdef dancing(): Unit}// 再定义一个特质trait Knowledge {var amount: Int = 0def increase(): Unit}class Student extends Person with Young with Knowledge {// 重写冲突的属性override val name: String = "student"def dating(): Unit = println(s"student $name is dating")def study(): Unit = println(s"student $name is studying")// 重写父类的方法override def sayHello(): Unit = {super.sayHello()println(s"hello froms: student $name")}override def increase()L Unit = {amount += 1println(s"student $name knowledge increase $amount")}}
特质叠加
trait Talent{def dancing(): Unitdef increase(): Unit = println("talent increase")}trait Knowledge{def dancing(): Unitdef increase(): Unit = println("knowledge increase")}class Student extend Person with Talent with Knowledge {override def dancing(): Unit = println("dancing")override def increase(): Unit = {super.increase() // konwledge increase}}
- 优先使用特质,一个类扩展多个特质是方便的,但只能扩展一个抽象类
- 如果需要构造函数,使用抽象类。因为抽象类可以定义带参数的构造函数,而特质不行
特质自身类型
```scala object Test { def main(args: Array[String]): Unit = { var user = new Register(“alice”, 12345) user.insert() } }
class User(val name: String, val password: String)
trait UserDao { // 想要拥有User的属性又不想继承User _: User => // 将自身类指定为User 依赖注入
// 向数据库插入数据 def insert(): Unit = { println(s”insert into db: ${this.name}”) } }
// 定义注册用户类 class RegisterUser(name: String, password: String) extends User(name, password) with UserDao {}
<a name="ND1Kp"></a>### 使用trait实现适配器模式设计模式是前辈们对代码开发经验的总结,用来提高代码可复用性、可维护性、可读性、稳健性、安全性<a name="sSC5U"></a>#### 分类:1. 创建型:单例模式,工厂方法1. 结构型:类、特质之间的关系架构,适配器模式,装饰模式1. 行为型:类能够做什么,模板方法模式,职责链模式<a name="nAjXu"></a>#### 适配器模式当特质中有多个抽象方法,而我们只需要其中的某一个或某几个方法时,不得不将该特质中的所有抽象方法重写,这样很麻烦。我们可以定义一个抽象类去继承该特质,重写特质中的抽象方法,方法体为空。这时候我们需要哪个方法只需要定义类继承抽象类,重写指定方法即可,这个抽象类就叫做适配器类```scalaobject Test01 {def main(args: Array[String]): Unit = {val greenHand = new GreenHandgreenHand.support()greenHand.schoolchild()}}trait PlayLOL {def top()def mid()def adc()def support()def jungle()def schoolchild()}abstract class Play extends PlayLOL {override def top(): Unit = ???override def mid(): Unit = ???override def adc(): Unit = ???override def support(): Unit = ???override def jungle(): Unit = ???override def schoolchild(): Unit}class GreenHand extends Play {override def support() = {println("support")}override def schoolchild() = {println("schoolchild")}}
模板方法
扩展性更强,符合开闭原则
abstract class Template {// 记录获取时间def code()// 具体规则def getRuntime() = {val start = System.currentTimeMillis()code()val end = System.currentTimeMillis()end - start}}class ForDemo extends Template {override def code(): Unit = for(i <- 1 to 10000) println("say hello")}def main(args: Array[String]): Unit = {val fd = new ForDemoprintln(fd.getRuntime())}
样例类
case class 样例类(val/var 成员变量名:类型 …) val/var 如果不写默认是val , 括号不能省
样例类中默认的方法
- apply() 让我们快速使用类名创建对象,省去new关键字
- toString() 打印对象属性
- equals() 可以让我直接用==判断对象成员变量值
- hasCode() 用来获取对象的哈希值,同一对象哈希值相同
copy() 可以快速创建一个属性值相同的实例对象,还可以使用带名参数赋值 ```scala object Test02 { def main(args: Array[String]): Unit = { val p1 = Person(“李四”, 24) // 因为有apply方法所以省略new
println(p1) // 默认调用类toString方法
val p2 = Person(“李四”, 24)
println(p1 == p2) // equals()让我们可以用==比较对象的属性
// 同一对象哈希值肯定相同,不同对象哈希值一般不同,下面相同 println(p1.hashCode()) println(p2.hashCode())
// 基于一个对象快速构建另一个对象 val p3 = p2.copy(age = 35) println(p3) } }
case class Person(name: String = “张三”, var age: Int = 23)
```scala// 计算器样例object Test04 {def main(args: Array[String]): Unit = {val c = Calculate(3, 2)println(c.add()) //5}}case class Calculate(a: Int, b: Int) {def add() = a + bdef subtract() = a - bdef multiply () = a * bdef divide() = a / b}
样例对象
用case修饰,没有主构造器
- 当作枚举值使用:固定值用于统一规范
- 作为没有任何参数的消息传递 ```scala object Test03 { def main(args: Array[String]): Unit = { val person = Person03(“张三”, Male) println(person.name, person.sex) //(张三,Male) } }
trait Sex {
}
case object Male extends Sex
case object Female extends Sex
case class Person03(var name: String, var sex: Sex)
<a name="DTIjE"></a>### trait构造机制```scalatrait Logger {println("执行Logger构造器")}trait MyLogger extends Logger{println("执行MyLogger构造器")}trait TimeLogger extends Logger {println("执行TimeLogger构造器")}class Person {println("执行Person构造器")}class Student extends Person with MyLogger with TimeLogger {println("执行Student类构造器")}def main(args: Array[String]): Unit = {val student = new Student/**1 执行Person类构造器2 执行Logger构造器3 执行MyLogger构造器4 执行TimeLogger构造器5 执行Student类构造器*/}
案例:程序员类
class Programeer {var name: String = _var age: Int = _def eat() {}def skill() {}}trait BigData {def learningBigDate() {println("learnint bigdata")}}class JavaProgrammer extends Programmer {override eat() {println("Java eat")}override skill() {println("Java skill")}}class PythonProgrammer extends Programmer {override eat() {}override skill(){}}class PartJavaProgrammer extends JavaProgrammer with BigData {override eat() {}override skill(){super.skill()learningBigData()}}class PartPythonProgrammer extends PythonProgrammer with BigData {override eat() {}override skill(){super.skill()learningBigData()}}object Test {def main(args: Array[String]): Unit = {val jp = new JavaProgrammerjp.name = "zhangsan"jp.age = 23jp.skll()jp.eat()val pjp = new PartJavaProgrammerpjp.name = "666"pjp.age = 23pjp.skill()}}
Object Test {def main(args: Array[String]): Unit = {// 创建二维数组val array: Array[Array[Int]] = Array.ofDim[Int](2,3)// 访问元素array(0)(2) = 19array(1)(0) = 25println(array.mkString(", "))for (i <- 0 until array.length; j <- 0 until array(i).length) {println(array(i)(j))}for ( i <- array.length; j <- array(i).indices) {print(array(i)(j) + "\t")if (j == array(i).length -1) println()}array.foreach(line => line.foreach(println))array.foreach(_.foreach(println))}}
