scala(五):面向对象-包、类型、继承和多态、抽象类、单列对象、特质

目录

类和对象

封装

继承和多态

抽象类

单列对象&伴生对象

特质(Trait)

扩展


(1)基本语法

package 包名

(2)Scala包的三大作用(和 Java 一样)

1)区分相同名字的类;

2)可以很好的管理类;

3)控制访问范围;

包命名

(1)命名规则

只能包含数字、字母、下划线、小圆点.,不能用数字、关键字开头。

(2)命名规范

一般是小写字母+小圆点

com.公司名.项目名.业务模块名

包说明

(1)说明

Scala有两种包的管理风格,一种方式和Java的包管理风格相同,包名用“.”进行分隔以表示包的层级关系,如com.zj.scala。

另一种风格,通过嵌套的风格表示层级关系

package com{
    package zj{
        package scala{
        }   
    }
}

第二种风格有以下特点:

1)一个源文件中可以声明多个package;

2)子包中的类可以直接访问父包中的内容,而无需导包;

包对象

在Scala中可以为每个包定义一个同名的包对象,定义在包对象中的成员,作为其对应包下所有class和object的共享变量,可以被直接访问。

package com {
  object Test01 {
    val out: String = "out"
    def main(args: Array[String]): Unit = {
      println(name)
      println(out)
    }
  }
}

package object com {
  val name : String = "com"
}

导包说明

import.com.zj.test引入com.zj包下的test(class和object)
import.com.zj._引入com.zj下的所有成员
import.com.zj.test._引入com.zj下test的object所有成员
import.com.zj.{test,test2}引入com.zj下的test和test2
import.com.zj.{test=>TEST}引入com.zj下的test并改名为TEST
import.com.zj.{test=>TEST,_}引入com.zj下的所有成员,并将test改名为TEST
import.com.zj.{test=>_,_}引入com.zj下的屏蔽test类
new_root_.java.util.HashMap引入的java绝对路径

scala的三个默认包:import java.lang._        import scala._        import scala.Predef._

访问权限

在Java中访问权限分为:public,private,protected和默认。Scala中可以通过类似的修饰符达到同样的效果。但是使用上有区别。

(1)Scala中无public关键字,但Scala中属性和方法的默认访问权限为public。

(2)private为私有权限,只在类的内部和伴生对象中可用。

(3)protected为受保护权限,Scala中受保护权限比Java中更严格,同类、子类可以访问,同包无法访问。

(4)private[包名]增加包访问权限,包名下的其他类也可以使用。

class Person {
  private var name: String = "jeffry"
  protected var age: Int = 20
  private[faceobject] var sex: String = "男"
  def say(): Unit = {
    println(name)
    println(age)
    println(sex)
  }
}

object Test01 {
  def main(args: Array[String]): Unit = {
    //
    val person = new Person
    person.say()
//    println(person.name)  //不能使用
//    println(person.age)   //不能使用
    println(person.sex)
  }
}

class Teacher extends Person {
  def test(): Unit = {
//    this.name   //不能使用
    this.age
    this.sex
  }
}

class Person02 {
  def test(): Unit = {
//    new Person().name   //不能使用
//    new Person().age    //不能使用
    new Person().sex
  }
}

类和对象

定义类

基本语法:【修饰符】class 类名 {类体}

1Scala语法中,类并不声明为public,所有这些类都具有公有可见性(即默认就是public);

2)一个Scala源文件可以包含多个类;

属性

基本语法:【修饰符】var/val 属性名称 【:类型】 = 属性值

注:Bean属性(@BeanPropetry可以自动生成规范的setXxx/getXxx方法。

import scala.beans.BeanProperty

class Person03 {
  var name: String = "jeffry"
  var age: Int = _      // _表示给属性一个默认值
  //Bean 属性(@BeanProperty)
  @BeanProperty var sex: String = "男"
}

object Tets02 {
  def main(args: Array[String]): Unit = {
    var person = new Person03()
    println(person.name)
    person.setSex("男")
    println(person.getSex)
  }
}

方法

基本语法:def 方法名(参数列表)【:返回值类型】 = {方法体}

class SUM {
  def Sum(a:Int,b:Int): Int = {
    a + b
  }
}
object Tets03 {
  def main(args: Array[String]): Unit = {
    val sum = new SUM()
    println(sum.Sum(5,10))
  }
}

创建对象

基本语法:var/val 对象名 【:类型】 = new 类型()

1val修饰对象,不能改变对象的引用(即:内存地址),可以改变对象属性的值。

2var修饰对象,可以修改对象的引用和修改对象的属性值。

3)自动推导变量类型不能多态,所以多态需要显示声明。

class Person03 {
  var name: String = "jeffry"
}

object Tets02 {
  def main(args: Array[String]): Unit = {
    val person = new Person03()
    person.name = "zj"
    println(person.name)
  }
}

构造器 

 和Java一样,Scala构造对象也需要调用构造方法,并且可以有任意多个构造方法。

Scala类的构造器包括:主构造器和辅助构造器

基本语法:

class 类名(形参列表) { // 主构造器

                                        // 类体

def this(形参列表) { // 辅助构造器

}

def this(形参列表) { //辅助构造器可以有多个...

}

}

说明:

(1)辅助构造器,函数的名称this可以有多个,编译器通过参数的个数及类型来区分。

(2)辅助构造方法不能直接构建对象,必须直接或者间接调用主构造方法。

(3)构造器调用其他另外的构造器,要求被调用构造器必须提前声明。  

class Person04 {
  var name : String = _
  var age : Int = _
  def this(age: Int) {
    this()
    this.age = age
    println("辅助构造器1")
  }

  def this(age: Int,name: String) {
    this()
    this.age = age
    this.name = name
    println("辅助构造器2")
  }
  println("主构造器")
}
object Test04 {
  def main(args: Array[String]): Unit = {
    val person = new Person04(18)
  }
}

构造器参数

 (1)未用任何修饰符修饰,这个参数就是一个局部变量;

(2var修饰参数,作为类的成员属性使用,可以修改;

(3val修饰参数,作为类只读属性使用,不能修改; 

封装

封装就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作。Java封装操作如下:

(1)将属性进行私有化;

(2)提供一个公共的set方法,用于对属性赋值;

(3)提供一个公共的get方法,用于获取属性的值;

Scala中的public属性,底层实际为private,并通过get方法(obj.field())和set方法 (obj.field_=(value))对其进行操作。所以Scala并不推荐将属性设为private,再为其设置public的get和set方法的做法。但由于很多Java框架都利用反射调用getXXX和setXXX方法,有时候为了和这些框架兼容,也会为Scala的属性设置getXXX和setXXX方法(通过 @BeanProperty 注解实现)。

继承和多态

基本语法:class 子类名· extends 父类名 {类体}

(1)子类继承父类的属性和方法;

(2)scala是单继承;

案例:

(1)继承的调用顺序:父类构造器->子类构造器

class Person05(names: String) {
  var name = names
  var age : Int = _

  def this(nameP: String,ageP: Int) {
    this(nameP)
    this.age = ageP
    println("父类辅助构造器")
  }
  println("父类主构造器")
}

class Person06(names : String,ages : Int) extends Person05(names,ages) {
  var no : Int = _
  def this(names : String,ages : Int,no : Int) {
    this(names,ages)
    this.no = no
    println("子类的辅助构造器")
    println(names,ages,no)
  }
}

object Test05 {
  def main(args: Array[String]): Unit = {
    new Person06("jeffry",20,1001)
  }
}

 重写

Scala中属性和方法都是动态绑定,而Java中只有方法为动态绑定。

class Person07 {
  val name : String = "person07"
  def say() : Unit = {
    println("原方法")
  }
}

class Person08 extends Person07 {
  override val name: String = "person08"

  override def say(): Unit = {
    println("重写方法!")
  }
}

object Test06 {
  def main(args: Array[String]): Unit = {
    val person01 : Person07 = new Person07()
    println(person01.name)
    person01.say()

    val person02 : Person08 = new Person08()
    println(person02.name)
    person02.say()
  }
}

抽象类

抽象属性和抽象方法

基本语法:

(1)定义抽象类:abstract class Person{} //通过 abstract 关键字标记抽象类;

2)定义抽象属性:val|var name:String //一个属性没有初始化,就是抽象属性;

3)定义抽象方法:def hello():String //只声明而没有实现的方法,就是抽象方法;

案例:

abstract class Person {
  val name : String
  def say() : Unit
}
class Teacher extends Person {
  val name : String = "teacher"

  override def say(): Unit = {
    println("hello teacher!!!!!")
  }
}

继承和重写

1)如果父类为抽象类,那么子类需要将抽象的属性和方法实现,否则子类也需声明为抽象类;

2)重写非抽象方法需要用override修饰,重写抽象方法则可以不加override;

3)子类中调用父类的方法使用super关键字;

4)子类对抽象属性进行实现,父类抽象属性可以用var修饰;

(5)子类对非抽象属性重写,父类非抽象属性只支持val类型,而不支持var类型,因为var修饰的为可变变量,子类继承之后就可以直接使用,没有必要重写。

匿名子类

Java一样,通过包含带有定义或重写的代码块的方式创建一个匿名的子类。

案例:

abstract class Person {
  val name : String
  def say() : Unit
}

object Test07 {
  def main(args: Array[String]): Unit = {
    val person = new Person {
      override val name: String = "jeffry"

      override def say(): Unit = {
        println("hello jeffry!!!!")
      }
    }
  }
}

单列对象&伴生对象

Scala语言是完全面向对象的语言,所以并没有静态的操作。但是为了能够和Java语言交互,就产生了一种特殊的对象来模拟类对象,该对象为单例对象。若单例对象名与类名一致,则称该单例对象为这个类的伴生对象,这个类的所有“静态”内容都可以放置在它的伴生对象中声明。

单列对象语法

基本语法:

object person {

    val name:String=“jeffry”

}

(1)单例对象采用 object 关键字声明。

(2)单例对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。

(3)单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。

object Person {
  var names : String = "Tom"
}
class Person {
  var name : String = "jeffry"
}

object Test07 {
  def main(args: Array[String]): Unit = {
    //单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。
    println(Person.names)
    val person = new Person()
    println(person.name)
  }
}

apply方法

 (1)通过伴生对象的apply方法,实现不使用new方法创建对象。

(2)如果想让主构造器变成私有的,可以在()之前加上private。

(3)apply方法可以重载。

(4)Scala中obj(arg)的语句实际是在调用该对象的apply方法,即obj.apply(arg)。用以统一面向对象编程和函数式编程的风格。

(5)当使用new关键字构建对象时,调用的其实是类的构造方法,当直接使用类名构建对象时,调用的其实时伴生对象的apply方法。 

案例:

object Test07 {
  def main(args: Array[String]): Unit = {
    //通过伴生对象的 apply 方法,实现不使用 new 关键字创建对象
    val P = Person()
    println("P.name=" + P.name)

    val P1 = Person("Tom")
    println("P1.name=" + P1.name)
  }
}

//如果想让主构造器变成私有的,可以在()之前加上 private
class Person private(names : String) {
  var name : String = names
}
object Person {
  def apply(): Person = {
    println("apply空参被调用")
    new Person("jeffry")
  }

  def apply(names: String): Person = {
    println("apply有参被调用")
    new Person(names)
  }
}

特质(Trait)

Scala语言中,采用特质trait(特征)来代替接口的概念,也就是说,多个类具有相同的特质时,就可以将这个特质独立出来,采用关键字trait声明。

Scala中的trait中即可以有抽象属性和方法,也可以有具体的属性和方法,一个类可以混入多个特质。这种感觉类似于Java中的抽象类。

Scala引入trait特征,第一可以替代Java的接口,第二个也是对单继承机制的一种补充。

基本语法:

trait 特质名 {

    trait 主体

}

案例:

trait PersonTrait {
  //声明属性
  var name : String = _
  //声明方法
  def say() : Unit = {}
  //抽象属性
  var age : Int
  //抽象方法
  def sayy() : Unit
}

特质基本语法

一个类具有某种特质,就意味着这个类满足了这个特质的所有要素,所以在使用时,也采用了extends关键字,如果有多个特质或存在父类,那么需要采用with关键字连接。

基本语法:

没有父类:class 类名 extends 特质 1 with 特质 2 with 特质 3 …

有父类:class 类名 extends 父类 with 特质 1 with 特质 2 with 特质 3…

说明:

(1)类和特质的关系:使用继承的关系。

(2)当一个类去继承特质时,第一个连接词是extends,后面是with。

(3)如果一个类在同时继承特质和父类时,应当把父类写在extends后。

案例:

trait PersonTrait {
  //特质可以同时拥有抽象方法和具体方法
  //声明属性
  var name : String = _
  //抽象属性
  var age : Int
  //声明方法
  def say() : Unit = {}
  //抽象方法
  def sayy() : Unit
}

trait SexTrait {
  var sex : String
}
//一个类可以实现/继承多个特质
//所有的java接口都可以当做scala特质使用
class TeacherTest extends PersonTrait with java.io.Serializable {
  override def sayy(): Unit = {
    println("sayy")
  }
  override var age: Int = _
}
object Test08 {
  def main(args: Array[String]): Unit = {
    val teacher = new TeacherTest
    teacher.say()
    teacher.sayy()
    //动态混入:可灵活的扩展类的功能
    val teacher2 = new TeacherTest with SexTrait {
      override var sex: String = "男"
    }
    //调用混入trait的属性
    println(teacher2.sex)
  }
}

特质叠加 

由于一个类可以混入(mixin)多个trait,且trait中可以有具体的属性和方法,若混入的特质中具有相同的方法(方法名,参数列表,返回值均相同),必然会出现继承冲突问题。

冲突分为以下两种:

第一种,一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait之间没有任何关系,解决这类冲突问题,直接在类(Sub)中重写冲突方法。

第二种,一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait继承自相同的trait(TraitC),及所谓的“钻石问题”,解决这类冲突问题,Scala采用了特质叠加的策略。所谓的特质叠加,就是将混入的多个trait中的冲突方法叠加起来,案例如下,

trait Ball {
def describe(): String = {
"ball"
        }
}
trait Color extends Ball {
override def describe(): String = {
"blue-" + super.describe()
        }
}
trait Category extends Ball {
override def describe(): String = {
"foot-" + super.describe()
        }
}
class MyBall extends Category with Color {
override def describe(): String = {
"my ball is a " + super.describe()
        }
}

1)案例中的super,不是表示其父特质对象,而是表示上述叠加顺序中的下一个特质,即,MyClass中的super指代Color,Color中的super指代Category,Category中的super指代Ball。

2)如果想要调用某个指定的混入特质中的方法,可以增加约束:super[],例如:super[Category].describe() 

特质自身类型

自身类型可以实现依赖注入的功能。

案例:

class User(val name : String,val age : Int)

trait Dao {
  def insert(user: User) = {
    println("Dao :"user.name)
  }
}
trait APP {
  _: Dao =>

  def login(user: User) : Unit = {
    println("login :" + user.name)
    insert(user)
  }
}
object Test09 extends APP with Dao {
  def main(args: Array[String]): Unit = {
    login(new User("jeffry",20))
  }
}

特质和抽象类的区别

(1)优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类。

(2)如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数,而特质不行,特质是有无参构造。

扩展

类型检查和转换

1obj.isInstanceOf[T]:判断obj是不是T类型。

2obj.asInstanceOf[T]:将obj强转成T类型。

3classOf获取对象的类名。

class Person11 {

}

object Test10 {
  def main(args: Array[String]): Unit = {
    val person = new Person11
    // 判断是不是person类型
    val bool : Boolean = person.isInstanceOf[Person11]
    // 强转成person类型。
    if (bool) {
      val p1 : Person11 = person.asInstanceOf[Person11]
      println(p1)
    }
    // 获取对象的类名
    val PClass : Class[Person11] = classOf[Person11]
    println(PClass)
  }
}

枚举类型和应用类型

枚举类:需要继承Enumeration

应用类:需要继承App

object Test11 {
  def main(args: Array[String]): Unit = {
    println(Color.BLUE)
    println(APPS)
  }
}
//枚举类
object Color extends Enumeration {
  val RED = Value(1,"red")
  val BLUE = Value(2,"blue")
  val YELLOW = Value(3,"yellow")
}

//应用类
object APPS extends App {
  println("++++++++++++")
}

type定义新类型

使用type关键字可以定义新的数据数据类型名称,本质上就是类型的一个别名。

 def main(args: Array[String]): Unit = {
     type S=String
     var v:S="abc"
     def test():S="jeffry"
 }

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值