Scala入门详解(二)

个人博客原文链接

类的声明与定义
/**
  * 类的声明与定义
  * Scala是一种纯粹的面向对象编程语言,而又无缝地结合了命令式和函数式的编程风格。
  * 在面向对象的语言有两个重要的概念:类(class)和对象(object也被称为实例—instance),
  * 其中类是对象的抽象。可以把类理解为模板,对象才是真正的实体。
  */
// 1. scala 中每个类都有主构造器,且与类定义交织在一起。其参数直接放置在类名后面
// 2. 类的代码属于主构造器,每次创建对象时执行一次
// 3. 主构造器中被val/var声明的参数会被编译成类的字段,同时生成getter/setter。Scala
// 提供了java风格的getter/setter方法,在定义变量时加上注解BeanProperty(scala.beans包下的),
// 经过编译后scala字节码中就有java风格的getter/setter方法
// 4. 在scala的主构造器中,没有var/val修饰的参数且类中没有使用该参数,该参数不会被编译成属性,
// 反之如果使用了该参数,则会被编译成属性,相当于被val修饰,但不会为其生成getter方法。
// 构造器的参数它的作用域是整个类
// 5. Scala中的类不声明为public,一个Scala源文件中可以有多个类
class TestClass(@BeanProperty var name:String="张三") {
  @BeanProperty var aa:String = name+"66"
  @BeanProperty var age:Int = 18
  // private[this]只能在当前class中使用,不能在其它方法中使用
  private[this] var bb:Int = 120

  // 辅助构造器
  def this(name:String,age:Int){
    // 每个辅助构造器必须以主构造器或其他的辅助构造器的调用开始
    this(name)
    this.age = age
    println("执行了辅助构造器")
  }

  def show(str:String="hello"):String={
    str
  }
}

// 单例对象
// 单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象
// 必须在同一个源文件里定义类和它的伴生对象
// 类被称为是这个单例对象的伴生类
// *类和它的伴生对象可以互相访问其私有成员
object TestClass{
  def main(args: Array[String]): Unit = {
    val t = new TestClass("zs",18)
    t.name="lisi"
    println(t.name)
    t.setName("wangyi")
    println(t.getName)
    t.aa = "999"
    println(t.aa)
  }
}

类的继承
/**
  * 类的继承
  * scala和java一样只可以单继承
  * final修饰的类不可以被继承
  */
// 防止父类成员被重写后,结果可能不正确,有三种方式
// 1.将父类的val声明为lazy
// 2.将父类的val声明为final
// 3.在子类中使用提前定义语法
class Person(name:String) {
  final val sex = "男"
  // 懒加载,实现延迟加载,只能是val类型
  lazy val age = 20
  def show = {
    println(name+" "+sex+" "+age)
  }
}

// 1 重写一个非抽象方法必须使用override修饰符。
// 2 只有主构造函数才可以往基类的构造函数里写参数。
// 3 在子类中重写超类的抽象方法时,你不需要使用override关键字
class Student extends  Person("lisi"){
  override lazy val age: Int = 30
  override def show: Unit = super.show
  def study = {
    println("学习")
  }
}

object TestP{
  def main(args: Array[String]): Unit = {
    val t1 = new Person("zs")
    val t2 = new Student
    println("t1:"+t1.age)
    println("t1:"+t1.sex)
    t1.show
    println("t2:"+t2.age)
    println("t2:"+t2.sex)
    t2.show

    // isInstanceOf [T] : 判断对象是否为 T 类型的实例
    // classOf [T] : 获取类型 T 的 Class 对象
    // asInstanceOf [T] : 将对象类型强制转换为T类型,只对值类型(AnyVal)有效
    println(t2.isInstanceOf[Person])
    println(classOf[Person])
    println( 20.asInstanceOf[Long].getClass.getName)
  }
}

抽象类
/**
  * 抽象类
  * 用 abstract 关键字修饰的类是抽象类,抽象类中可以有抽象的属性和方法
  * 抽象类语法与普通类一致,就是多了抽象属性和方法。
  * 抽象属性不用初始化,抽象方法不能有方法体
  * 一个类中有抽象的属性或方法,这个类必须用关键字 abstract 修饰
  * 普通类可以继承抽象类,使用关键字extends
  */
abstract class TestA(age:Int){
  var name:String
  def tShow(str:String):Unit
  val sex = "男"
  def tLook={
    println("666"+age)
  }
}

class TestB() extends TestA(20){
  var name: String = _
  def tShow(str: String): Unit = {
    println("7777"+str)
  }
}

object TestAbstract {
  def main(args: Array[String]): Unit = {
    val t1 = new TestB
    t1.name
    t1.sex
    t1.tShow("lisi")
    t1.tLook
  }
}

特质(接口)

/**
  * 接口
  * 实现接口关键字extends,和继承一样
  * Scala Trait(特征) 相当于 Java 的接口,实际上它比接口还功能强大
  * 与接口不同的是,它还可以定义属性和方法的实现。一般情况下Scala的类只能够继承单一父类,
  * 但是如果是 Trait(特征) 的话就可以继承多个,从结果来看就是实现了多重继承
  *
  * 构造器执行顺序
  * 调用超类的构造器;
  * 特征构造器在超类构造器之后、类构造器之前执行;
  * 特征由左到右被构造;
  * 每个特征当中,父特征先被构造;
  * 如果多个特征共有一个父特征,父特征不会被重复构造
  * 所有特征被构造完毕,子类被构造。
  */
trait TestTrait {
  // 没有定义方法的实现
  def isEqual(x: Any): Boolean
  // 定义方法的实现
  def isNotEqual(x: Any): Boolean = !isEqual(x)
}

class Point(xc: Int, yc: Int) extends TestTrait {
  var x: Int = xc
  var y: Int = yc
  def isEqual(obj: Any) =
    obj.isInstanceOf[Point] &&
      obj.asInstanceOf[Point].x == x
}

object Test {
  def main(args: Array[String]) {
    val p1 = new Point(2, 3)
    val p2 = new Point(2, 4)
    val p3 = new Point(3, 3)

    println(p1.isNotEqual(p2))
    println(p1.isNotEqual(p3))
    println(p1.isNotEqual(2))
  }
}

多态

/**
  * 多态
  * 1、被final修饰的方法和属性不能重写
  * 2、var修饰的非抽象属性不能重写
  * 3、抽象的属性或者方法,重写时可以不加 override 关键字,
  * 非抽象属性或方法必须加 override 关键字
  * 4、没有参数的方法,可以用val来重写
  */
trait TestPolymorphic {
  // 被final修饰的方法和属性不能重写
  final val a = "final属性"
  final def methodT1 = {
    println("final修饰的方法")
  }
  // var修饰的非抽象属性不能重写
  var b = "var属性"
  val b2 = "val属性"
  // 抽象的属性或者方法,重写时可以不加 override关键字
  var c:String
  val d:String
  def methodT2
 //  没有参数的方法,可以用val来重写
  def methodT3 = {
    println("没有参数的方法")
  }
}

class TestBB extends TestPolymorphic{
  // var修饰的非抽象属性不能重写
//  override var b:String = "b"
  override val b2: String = "b2"
  var c: String = "c"
  val d: String = "d"

  def methodT2: Unit = println("抽象方法")

  override val methodT3 = println("没有参数的方法2")

}

object TestBB{
  def main(args: Array[String]): Unit = {
    val t = new TestBB()
    println(t.b)
    println(t.b2)
    println(t.c)
    println(t.d)
    t.methodT2
    t.methodT3
  }
}

方法和函数

方法

Scala中的+ - * / %等操作符的作用与Java一样,位操作符 & | ^ >> <<也一样。只是有一点特别的:这些操作符实际上是方法。例如:
a + b是如下方法调用的简写:a.+(b)

  * 方法的声明与定义
  * a 方法 b 可以写成 a.方法(b)
  * 方法的返回值类型可以不写,编译器可以自动推断出来,
  * 但是对于递归函数,必须指定返回类型
  */

class TestMethod {
  // 有参数,有返回值,参数还可以有默认值
  // 省略return关键字将最后一行表达式的结果作为返回值
  def methodT1(str:String="张三"):String={
    str
  }

  // 有参数,无返回值
  def methodT2(str:String):Unit={
    println(str)
  }

  // 无参数,无返回值(完整写法)
  def methodT3():Unit={
    println("T3")
  }

  // 无参数,无返回值(省略写法)
  // 对于无参方法,可以省略括号。但是在调用时,写了括号的方法,通过方
  // 法名或者方法名()来调用,没有括号的方法,只能通过方法名调用
  def methodT4={
    println("T4")
  }

  // 这种形式的方法叫过程,无返回值
  // 对于没有等号的方法叫过程,返回值类型永远是 Unit
  def methodT5{
    println("T5")
  }

  // 换名调用参数
  // 方法调用时,参数不会被求值,这样的函数参数叫换名调用参数。对应的
  // 调用时,参数表达式被求值,这样的叫换值调用参数
  def methodT6(t:Int=>Int){
    println(t)
  }
}

object TestMethod{
  def main(args: Array[String]): Unit = {
    val t = new TestMethod
    println(t.methodT1("网速"))
    t.methodT2("李四")
    t.methodT3()
    t.methodT4
    t.methodT5
    // 定义函数
    val f = (x:Int) => 20
    t.methodT6(f)

  }
}
函数
/**
  * 函数
  * 可以像任何其他数据类型一样被传递和操作
  */
object TestFunction {
  def main(args: Array[String]): Unit = {
    // 先定义一个方法,再定义一个函数,将函数传递到方法
    def method2(f:(Int,Int)=>Int) = f(2,3)
    val ff = (x:Int,y:Int) => x+y
    println(method2(ff) )

    // 指定函数参数名
    // 参数列表可以不用按顺序传递,直接指定传递
    def m(x:Int,y:Int) = x+y
    println(m(y=2,x=3))

    // 函数,可变参数
    // 不需要指定函数参数的个数,可以向函数传入可变长度参数列表
    def printStrings( args:String* ) = {
      var i : Int = 0;
      for( arg <- args ){
        println("Arg value[" + i + "] = " + arg );
        i = i + 1;
      }
    }
    printStrings("zhangsan","lisi","wangwu")

    // 匿名函数
    // 使用匿名函数后,我们的代码变得更简洁了
    var m1 = (x:Int) => x+1
    println(m1(1))
    var m2 = (x:Int,y:Int) => x*y-(x+y)
    println(m2(2,2))
    var m3 = () => 8
    println(m3())

    // 偏函数
    // A代表参数类型,B代表返回类型,常用作输入模式匹配
    def func1: PartialFunction[String, Int] = {
      case "one" => 1
      case "two" => 2
      case _ => -1
    }

    def func2(num: String) : Int = num match {
      case "one" => 1
      case "two" => 2
      case _ => -1
    }

    // 偏应用函数
    // 绑定第一个date参数,第二个参数使用下划线(_)替换缺失的参数列表,并把这个新的函数值的索引的赋给变量
    var log = (date:Date,message:String)=>println(date+"----"+message)
    var date = new Date
    var logWithBound = log(date,_:String)
    logWithBound("hhhhhh")
    logWithBound("llllll")
    logWithBound("kkkkkk")

    // 高阶函数
    // Scala中允许使用高阶函数, 高阶函数可以使用其他函数作为参数,或者使用函数作为输出结果
    // 函数f和值v作为参数,而函数f又调用了参数v
    def apply(f: Int => String, v: Int) = f(v)
    def iToString[A](x: A) = "[" + x.toString() + 66 + "]"
    println( apply(iToString, 10) )

    // 函数柯里化
    // 柯里化(Currying)指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。
    // 新的函数返回一个以原有第二个参数为参数的函数
    def add1(x:Int,y:Int) = x+y
    println(add1(1,1))
    // 柯里化
    def add2(x:Int)(y:Int) = x+y
    println(add2(1)(1))

    // 方法和函数的区别
    // 1. 函数可以作为参数传递到方法中,方法不行
    val m4 = (x:Int,y:Int) => x-y
    def min(f:(Int,Int) => Int,v:Int) = f(10,v)
    println(min(m4,5))
    // 2. 在Scala中无法直接操作方法,如果要操作方法,必须先将其转换成函数
    // 通常情况下编译器会自动将方法转换成函数,例如在一个应该传入函数参数
    // 的地方传入了一个方法,编译器会自动将传入的方法转换成函数
    def m5(x:Int):Int={
      x * 10
    }
    // 转化
    (x:Int) => m5(x)
    // 3. 函数必须要有参数列表,而方法可以没有参数列表
    def m6 = 100
    //      val ff = => 100
    // 4. 在需要函数的地方,如果传递一个方法,会自动进行ETA展开(把方法转换为函数)
    // 5. _将方法转化为函数
      def m7(x:Int,y:Int) = x + y
      println(m7(1,1))
      val f7 = m7 _
      println(f7(1,1))
  }
}

数组

/**
  * 数组
  * 如果想使用(动态)可变数组,需要导入import scala.collection.mutable.ArrayBuffer包
  */
object TestArray {
  def main(args: Array[String]): Unit = {
    // 静态数组
    // 声明静态数组
    val arr = new Array[Int](4)
    arr(0)=6
    arr(1)=7
    val arr2 = Array(1,2,3,4)
    // 遍历数组
    for (i <- arr2){
      println(i)
    }
    // 创建长度为6,元素全为4的数组
    val arr7 = fill(6)(4)
    // 创建长度为10,类型为Function0[Int]的数组
    val arr8 = fill(10)(() => 4)
    // 创建长度为10的数组
    val arr9 = ofDim(10)

    // 多维数组
    val arr3 = ofDim[Int](3,3)
    // 创建矩阵
    for (i <- 0 to 2) {
      for ( j <- 0 to 2) {
        arr3(i)(j) = j;
      }
    }
    // 打印二维阵列
    for (i <- 0 to 2) {
      for ( j <- 0 to 2) {
        print(" " + arr3(i)(j));
      }
      println();
    }

    // 合并数组,两种方式
    val arr4 = concat(arr,arr2)
    val arr44 = arr.++(arr2)
    // 遍历数组
    for (i <- arr44){
      println(i)
    }

    // 创建区间数组[i,j)
    val arr5 = range(10,20)
    val arr6 = range(10,20,2)
    for (i <- arr5){
      println(i)
    }
    for (i <- arr6){
      println(i)
    }

    // 动态数组
    // ArrayBuffer
    val array = ArrayBuffer(1,2,3,8)
    // 末端追加元素
    array += 9
    // 末段追加多个元素
    array += (6,6,6)
    // 末端追加集合
    array ++= arr
    // 在指定位置插入元素
    array.insert(0,11)
    // 在指定位置插入多个元素
    array.insert(5,2,3,5)
    // 删除位置是2的元素
    array.remove(6)
    // 从第2个元素开始移除3个元素
    array.remove(2,3)
    // 将动态数组转化为数组
    val array2 = array.toArray
    // 将数组转化为动态数组
    val array3 = array2.toBuffer
    for (n <- array){
      println(n)
    }


    // 排序
    val arrayorder = array.sorted
    for (n <- arrayorder) println(n)
    // 使用分隔符连接元素
    println(arrayorder.mkString("|"))
    // 前后以[]包围,中间以,分割
    println(arrayorder.mkString("[",",","]"))


    // zip
    // 按照两个数组的序列进行匹配组成新得元素
    val a1 = Array("a","b","c")
    val a2 = Array(1,2,3)
    val a3= a1.zip(a2)
    for(n <- a3) println(n)

  }
}

元组

/**
  * 元组
  * 与列表一样,元组也是不可变的,但与列表不同的是元组可以包含不同类型的元素。
  * 元组的值是通过将单个的值包含在圆括号中构成的
  */
object TestTuple {
  def main(args: Array[String]): Unit = {

    // 声明,两种方式
    // 元组的实际类型取决于它的元素的类型
    // 目前Scala支持的元组最大长度为 22。对于更大长度你可以使用集合,或者扩展元组
    // 元组的下标索引是从1开始的
    val t1 = (1,"ok",3.14)
    val t2 = new Tuple3(2,"hhh",6.6)

    // 访问元组元素
    println(t1._1)
    println(t1._2)
    println(t1._3)

    // 迭代输出元组的所有元素
    t1.productIterator.foreach{
      i => println("value:"+i)
    }

    // 元组转化为字符串
    println(t1.toString())

    // 元组元素交换位置,仅限于两个元素的元组
    val t3 = (1,"2")
    println(t3.swap)
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值