一、scala包
面向对象编程,可以建立scala包来管理类和对象。同名的类放到不同包就可以加以区分;类很多时,不同类可以放到一个包中进行管理,相当于把多个类进行了归纳。
- 包定义:package 包名
- 包命名规则:字母、数字、下划线,但不能以数字开头。也不能包含关键字。用圆点实现不同级别包的命名
- 包管理:
每个源文件一个包,包名用圆点分隔以表示层级关系;(eg:com.test.byy_1)
每个源文件多个包,通过嵌套关系表示包的层级关系,子包可以直接调用父包内容,无需导包
eg:
package com{
package test{
package byy_1{
}
}
}
- 包对象:package object 包名,包对象一般定义在包底下的package.scala文件中。每个包可以定义与它同名的包对象,包对象中的成员可以被这个包中的所有类和对象访问。
- 类和对象
class 类名:scala中类可以有参数,类是对象的抽象,不占用空间,一个源文件可以有多个类;
object 对象名:对象是实例,占用空间。
package chapter06
//定义类
class Student{
//定义属性
private var name:String="alice" //private代表私有属性,变量前不加标识符默认为共有属性
var age:Int= _ //下划线代表默认值为0
var sex:String= _ //下划线代表默认值为null
}
//定义对象
object Test_Class{
def main(args:Array[String]):Unit={
//用关键字new来实例化类
val studert = new Student()
// student.name //error,私有属性,访问不到
println(student.age)
println(student.sex)
student.sex="female"
println(student.sex)
}
}
0
null
female
二、封装
封装指的是把抽象的数据与数据操作封装起来,程序的其他部分只有通过被授权的操作才能操作数据。
访问权限
- private:私有权限,只有类的内部和伴生对象可用
- protected:受保护的权限,只有同类子类可以访问,同包无法访问
- private[包名]:增加包访问权限,包下的其他类也可访问
主构造器与辅助构造器
辅助构造器的名称为this,每个辅助构造器都必须以一个对先前已经定义的其他构造器调用先开始
- 主构造器有参数
参数前没有修饰符时,此时的属性为类的局部变量而非属性,不能直接被调用,此时就需要在类中定义一个方法,从而调用这个方法
object Main {
def main(args:Array[String])
{
val student1 = new Student(name="cindy",age=20) //调用类及其方法
}
}
//类
class Student(name:String,age:Int){ //参数为变量
println(s"${name}今年${age}岁") //需定义调用参数的方法
}
cindy今年20岁
参数前有修饰符(var、val)时,相当于属性,就无需在类中单独定义属性了,参数可以直接被调用(推荐使用,灵活简单)
主构造器有修饰符的参数,定义辅助构造器时参数无需修饰符
object Main {
def main(args:Array[String]): Unit=
{
val student1 = new student("alice",18) //调用主构造器,参数直接赋值
student1.school="北大"
println(s"${student1.school}的${student1.name}今年${student1.age}岁") //参数赋值后调用
val student2 = new student("cindy",20,"清华") //调用第一个辅助构造器(主构造器也被调用)
}
}
class student(var name:String,var age:Int){ //参数相当于可变属性
var school:String= _ //单独定义属性
println(s"${school}的${name}今年${age}岁")
def this(name:String,age:Int,school:String){ //辅助构造器使用主构造器的所有属性为参数
this(name,age) //调用主构造器
this.school=school //将主构造器的属性定义为辅助构造器的参数
println(s"${age}岁的${name}在${school}上学")
}
}
- 主构造器无参数
object Main {
def main(args:Array[String]): Unit=
{
val student1 = new student //调用主构造器
//给主构造器的参数重新赋值后输出
student1.name="cindy"
student1.age=20
println(s"${student1.name}${student1.age}岁")
val student2 = new student("cindy") //调用第一个辅助构造器(主构造器也被调用)
val student3 = new student("cindy",16) //调用第二个辅助构造器(主构造器和第一个辅助构造器也被调用)
}
}
class student{ //类无参数,需要单独定义属性
var name:String="Alice" //可变属性
var age:Int=18 //可变属性
println(s"${name}今年${age}岁")
def this(name:String){
this() //调用主构造器
this.name=name
println(s"${name}")
}
def this(name:String,age:Int){
this(name:String) //调用第一个辅助构造器
this.age=age
println(s"${age}的${name}")
}
}
Alice今年18岁
cindy20岁
Alice今年18岁
cindy
Alice今年18岁
cindy
16的cindy
三、类的继承
- 子类继承父类的属性(私有属性除外)和方法
- 继承类:class 子类名 extends 父类名{}
- 子类中的方法重写:override 父类方法
- 方法调用:对象名.方法()
- 多态:一个接口的不同实现,即把一个实现类的对象实例传给接口。接口通过对不同类的引用,来调用不同类的实现
object Main {
def main(args:Array[String]): Unit=
{
val student1 = new Student("alice",18) //调用子类主构造器前,先调用父类主构造器
student1.printlninfo() //固定实现子类
val student2 = new Student("alice",18,"femal") //调用子类辅助构造器前,先调用父类、子类主构造器
student2.printlninfo() //固定实现子类的辅助构造器
println("======")
val person1 = new Person("bob",21)
def personprintinfo(a:Person):Unit={ //定义动态绑定类的方法,让同一接口实现不同类(可以理解为:方法的参数就是要传入的类,但是参数的类型一定要是父类或同类)
a.printlninfo()
}
personprintinfo(student1)
personprintinfo(person1)
}
}
//父类
class Person(var name:String){
var age:Int= _
println(s"1.父类主构造器调用")
def this(name:String,age:Int){
this(name)
this.age=age
println(s"2.父类辅助构造器调用")
}
def printlninfo():Unit={
println(s"${name} ${age}")
}
}
//子类
class Student(name:String,age:Int) extends Person(name){ //子类age直接继承父类属性age,所以不用单独给参数赋值
var sex:String= _
println(s"3.子类主构造器调用")
def this(name:String,age:Int,sex:String){
this(name,age)
this.sex=sex
println(s"4.子类辅助构造器调用")
}
override def printlninfo():Unit={ //重写方法
println(s"${name} ${age} ${sex}")
}
}
1.父类主构造器调用
3.子类主构造器调用
alice 18 null
1.父类主构造器调用
3.子类主构造器调用
4.子类辅助构造器调用
alice 18 femal
======
1.父类主构造器调用
2.父类辅助构造器调用
alice 18 null
bob 21
四、抽象类
抽象类就是包含非具体实现的方法和属性的不确定的类。
抽象类一般要用实例化子类去重写属性和方法,从而实现抽象类。
我们也可以在对象中直接用new关键字定义一个匿名子类来实现抽象类
- 抽象属性:没有初始值的属性
- 抽象方法:没有方法体的方法
- 子类实现规则
抽象属性:var定义的属性直接重写即可;val定义的属性需要加override进行重写
非抽象属性:var定义的属性直接给变量赋新值就好;val定义的属性需要加override
抽象方法:override可加可不加
非抽象方法:要加override - 也可以用匿名子类实例化抽象类
非匿名子类实例化抽象类
object Main {
def main(args:Array[String])
{
//调用子类,抽象类不能直接被调用
val student1=new Student
student1.printinfo()
student1.printinfo1()
println("=======")
val student2=new Student2
student2.printinfo()
student2.printinfo1()
}
}
//抽象父类
abstract class Person{
//抽象属性
var name:String
val age:Int
//非抽象属性
var sex:String="male"
val school:String="清华"
//非抽象方法
def printinfo():Unit={
println(s"person: ${name} ${age}")
}
//抽象方法
def printinfo1():Unit
}
//实现子类1
class Student extends Person{
//实现抽象属性
var name:String="alice" //var定义的属性可变,所以不需要加override
override val age:Int=19 //val定义的属性需要加override
//重写非抽象方法
override def printinfo():Unit={
println(s"student: ${name} ${sex} ${age}")
}
//实现抽象方法
override def printinfo1():Unit={
println(s"student: ${name} ${age}")
}
}
//实现子类2
class Student2 extends Person{
//实现抽象属性
var name:String="alice" //var定义的属性可变,所以不需要加override
override val age:Int=19 //val定义的属性需要加override
//重写抽象属性
sex="female" //var变量直接赋值
override val school="北大" //val变量重写
//重写非抽象方法
override def printinfo():Unit={
println(s"student: ${name} ${sex} ${age} ${school}")
}
//实现抽象方法
override def printinfo1():Unit={
println(s"student: ${name} ${age} ${school}")
}
}
student: alice male 19
student: alice 19
========
student: alice female 19 北大
student: alice 19 北大
匿名子类实例化抽象类
object Main {
def main(args:Array[String]):Unit=
{
val person:Person = new Person{ //匿名子类定义
var name:String="alice"
override val age:Int=18
override def printinfo1():Unit={
println(s"${name} ${age} ${sex} ${school}")
}
}
person.printinfo1()
}
}
//抽象父类
abstract class Person{
//抽象属性
var name:String
val age:Int
//非抽象属性
var sex:String="male"
val school:String="清华"
//非抽象方法
def printinfo():Unit={
println(s"person: ${name} ${age}")
}
//抽象方法
def printinfo1():Unit
}
alice 18 male 清华
五、伴生对象&伴生类
伴生对象用object声明,伴生对象的属性和方法是静态的,且可以直接通过伴生对象名调用,也可以直接被伴生类调用(包括私有的);
伴生对象对应的类为伴生类,伴生对象与伴生类的名字一样
伴生对象中定义的apply()方法,在调用时可以省略方法名
eg:调用伴生类实现功能,伴生类调用伴生对象的静态属性
object Test {
def main(args:Array[String]):Unit=
{
val student = new Person(name="alice",age=18) //伴生类重定义
Person.school="北大" //伴生对象Person调用其属性,并重新赋值
student.pri() //伴生类的方法调用
}
}
//伴生类
class Person(val name:String,val age:Int){
def pri(){
println(s"${name} ${age} ${Person.school}") //伴生类调用伴生对象的属性school
}
}
//伴生对象,定义静态属性school
object Person{
var school:String="清华"
}
alice 18 北大
eg:调用伴生对象实现功能,不调用伴生类:类的属性私有化,不能直接被调用;此时先建立伴生对象调用伴生类的私有属性,再调用伴生对象实现功能
object Test {
def main(args:Array[String]):Unit=
{
val student = Person.newpri(name="alice",age=18) //伴生对象中伴生类的实例化方法调用
val student = Person(name="alice",age=18) //apply方法调用
Person.school="北大" //伴生对象Person调用其属性,并重新赋值
student.pri() //伴生类的方法调用
}
}
//伴生类
class Person private(val name:String,val age:Int){
def pri(){
println(s"${name} ${age} ${Person.school}") //伴生类调用伴生对象的属性school
}
}
//伴生对象,定义静态属性school
object Person{
var school:String="清华"
//定义伴生类的实例化方法
def newpri(name:String,age:Int):Person=new Person(name,age) //方法newpri可以使用类Person的属性
//apply方法
def apply(name:String,age:Int):Person=new Person(name,age) //方法newpri可以使用类Person的属性
}
alice 18 北大
六、特质trait
scala特质既可以代替接口,又可以补充类的单继承
trait 特质名{
trait体
}
- 当多个类具有相同特质时,就可以把这些特质独立出来,用trait声明,这样也方便使用
- 类和特质的关系也是类继承特质。常说的一个类具有特质,指的是这个类满足特质的所有要素。一个类可以继承多个特质,因为一个类需要的元素可以存在于不同的特质中(比如一个子类只能有一个父类,但是这个子类需要的属性,其父类不一定都有。而在每个需要这些属性的子类中分别定义的话,又要重复很多次。像这样的多个类具有相同特质的情况,为了方便操作使用,就可以把这些特质独立出来,用trait声明。)
- 特质中可以有抽象和非抽象的属性与方法
- super:子类中在重写方法时,可以用super调用父类或trait中的方法,然后再定义新的输出。在方法名有重复时,优先调用最后一个出现的trait中的方法。也可以用super[类名或特质名] 来调用指定父类或特质的方法
- 特质与类的区别:
特质更灵活,可以实现多继承,优先使用
特质不能带参,类可以带参,需要使用参数的时候优先使用类 - 用法
无父类 class 类名 extends trait1 with trait2…
有父类 class 类名 extends 父类名 with trait1 with trait2…
特质继承 trait1 extends trait2 with trait3 …
eg:既定特质:直接输出子类
object Test {
def main(args:Array[String]):Unit=
{
val student = new Student
student.pri()
student.paly()
student.dating()
student.eat()
}
}
//父类
abstract class Person{
val name:String="alice"
val age:Int
def pri():Unit={
println(s"${name} ${age}")
}
}
//特质
trait Young{
val name:String
val age=18
def paly():Unit={
println(s"${name} is playing")
}
def dating():Unit
}
//子类
class Student() extends Person() with Young{
//冲突属性重写
override val name:String="cindy"
override val age:Int=20
//抽象方法实例
override def dating():Unit={
println(s"${name} ${age} is dating")
}
//重写父类方法
override def pri():Unit={
super.pri() //pri()方法调用
println(s"${name} ${age} is a student") //方法重写
}
//新建方法
def eat():Unit={
println(s"${name} is eating")
}
}
cindy 20
cindy 20 is a student
cindy is playing
cindy 20 is dating
cindy is eating
eg:动态混入特质:调用子类时,特定情况需要输出特质时再混入,否则都是按照子类输出
object Test {
def main(args:Array[String]):Unit=
{
val student = new Student with Talient{
override def sing():Unit={
println(s"${name} ${age} likes singing")
}
override def dance():Unit={
println(s"${name} ${age} likes dancing")
}
override def play():Unit={
super.play() //调用trait中的方法,优先调用最后一个出现的trait中的方法
println(s"${age}岁的${name} is playing")
}
override def pri():Unit={ //重写pri()方法
super.pri() //调用父类和trait中的方法,也优先调用最后一个出现的trait中的方法
println(s"${age}岁的${name}")
}
}
student.pri()
student.play()
student.dating()
student.eat()
student.sing()
student.dance()
}
}
//父类
abstract class Person{
val name:String="alice"
val age:Int
def pri():Unit={
println(s"${name} ${age}")
}
}
//特质
trait Young{
val name:String
val age=18
def play():Unit={
println(s"${name} is playing")
}
def dating():Unit
}
//特质2
trait Talient{
def sing():Unit
def dance():Unit
def play():Unit={
println("playing")
}
def pri():Unit={
println("pri")
}
}
//子类
class Student() extends Person() with Young{
//冲突属性重写
override val name:String="cindy"
override val age:Int=20
//抽象方法实例
override def dating():Unit={
println(s"${name} ${age} is dating")
}
//新建方法
def eat():Unit={
println(s"${name} is eating")
}
}
pri
20岁的cindy
playing
20岁的cindy is playing
cindy 20 is dating
cindy is eating
cindy 20 likes singing
cindy 20 likes dancing