day65、66 Scala 面向对象 面向函数


I know, i know
地球另一端有你陪我




一、Hello World

Scala 是 Scalable Language 的简写,是一门多范式的编程语言

Scala是把函数式编程思想和面向对象编程思想结合的一种编程语言

在函数式编程中,函数是基本单位,他几乎被用作一切,包括最简单的计算,甚至连变量都被计算所取代;
在函数式编程中,变量只是一个名称,而不是一个存储单元,这是函数式编程与传统的命令式编程最典型的不同之处。

大数据计算引擎 Spark 由 Scala 编写

从 Hello World 开始

我们知道,依赖于 JVM 的Java 程序,函数的入口是 main 方法
而它的修饰词是固定的(public static void)
而 Scala 中是没有这些修饰词的,取而代之的是 object
被他修饰的类中,所有的成员都将是静态的(static)
package day65


/*
    类在被加载到 jvm 中,会在内存中存在一个该类的“类对象”,
    该对象有且只有只有一个(单例模式)
    通过该对象可以直接调用方法中的静态成员

    scala 和 java 一样,需要先编译,在 jvm 中运行
    jvm 需要 main函数,而 main 函数必须是由 public 和 static 修饰
    scala 中并没有这个修饰词,于是提供了一个object,
    由object修饰的类中所有的成员都是静态的,可以通过“类对象”直接调用
    由class修饰的类中所有的成员都是普通的,需要类的对象进行调用


 */

object ScalaDemo1 {
  // 程序的入口 main
  def main(args: Array[String]): Unit = {

    print("Hello World")
  }
}

1、变量的定义 基本数据类型 结构体

定义变量时都要加上 val(不可变) 或 var(可变)
Scala 本质依然依赖大部分 Java,所以保留了很多 Java 中有的元素,
包括八个基本数据类型
package day65

import java.io.{BufferedReader, FileReader}
import java.sql.{Connection, DriverManager, PreparedStatement, ResultSet}
import scala.io.{BufferedSource, Source}

object ScalaDemo2 {

  def main(args: Array[String]): Unit = {

    //val 不可变
    //var 可变

    val a = 1

    var b = 10
    b = 11

    println(a)
    println(b)

    /*
        基本数据类型(首字母需要大写)

        Byte Short Int Long
        Float Double
        Character
        Boolean
     */

    // 定义变量
    val byte1: Byte = 1
    val short1: Short = 1
    val int1: Int = 1
    val long1: Long = 1

    val float1: Float = 1
    val double1: Double = 1

    val bool1: Boolean = true
    val bool2: Boolean = false

    val char1: Char = 'a'

    // String 是引用类型
    val string1: String = "fgh 韭菜盒子"

    /*
       隐式转换
       由于scala的底层依然是java,所以本身涵盖了大本分java方法
       而scala中新增了很多方法,这种额外添加方法的动作叫做隐式转换
       后面会详细说
    */

    val splits: Array[String] = string1.split(" ")

    // 通过下标取需要用小括号
    println(splits(0))
    println(splits(1))

    //拼接字符串
    val str1: String = "123"
    val str2: String = "456"

    // 直接 +
    val str3 = str1 + str2
    println(str3)

    // 利用StringBuilder或Buffer
    val builder: StringBuilder = new StringBuilder()
    builder.append(str1).append(str2)
    println(builder)

      // scala特有方式,类似引用
      // 底层依然是StringBuilder
      // 建议加大括号,当首个字符是_,不加会出错
      println(s"${str1}${str2}")

    // 类型转换
    // 通过Java中的方法
    val int2: Int = 1
    val str4: String = String.valueOf(int2)
    val int5: Integer = Integer.valueOf(str4)

    // 通过scala中的方法,注意没有括号
    val srt5 = int2.toString
    val byte5 = int2.toByte
    val short5 = int2.toShort

    // 关于 *
    // 这里的*是一个方法
    // scala 中的方法调用也可以也不加上 "."
    println("*".*(100))
    println("*" * 100)


    // 选择结构
    if(1>2){
      println(">")
    } else if(1==2){
      println("=")
    } else {
      println("<")
    }


    // 循环
    var cnt:Int = 1
    var sum:Int = 0
    while (cnt <= 100){
      sum += cnt
      cnt += 1
    }
    println(sum)


    do{
      println("说啥都要打印")
    }while(false)


    val str6:String = "1,2,3,4,5"
    val array1: Array[String] = str6.split(",")

    // for each 循环
    for (elem <- array1) {
      println(elem)
    }

    // scala 采用函数式编程来遍历
    // 将函数作为参数,注意不加()
    array1.foreach(println)
    array1.map(print)


    // 文件读写
    // java的方法
    val br: BufferedReader = 
    		new BufferedReader(new FileReader("data/students.txt"))

    var line:String = br.readLine()

    //while((line=br.readLine())!=null) scala不能解析这句

    while (line!= null){
      System.out.println(line)
      line = br.readLine()
    }


    println("*" * 100)

    // scala io 的方式
    // 也可以读url等等
    val source: BufferedSource = Source.fromFile("data/students.txt")
    // 可以直接输出
    source.foreach(print)

    // 也可以弄一个迭代器慢慢玩
    val lines: Iterator[String] = source.getLines()
    for (elem <- lines) {
      println(elem)
    }

    println("*" * 100)


    // 链式编程
    Source
      .fromFile("data/students.txt")
      .getLines()
      .foreach(println)


    // 连接 mysql
    val conn: Connection = DriverManager.getConnection
    		("jdbc:mysql://master:3306/student","root","123456")

    val ps: PreparedStatement = conn.prepareStatement
    		("select * from student where age<?")

    ps.setString(1,"23")

    val rs: ResultSet = ps.executeQuery()
    val id: String = rs.getString("id")
    val name: String = rs.getString("name")
    val age: Int = rs.getInt("age")

    println(s"${id},${name},${age}")

    ps.close()
    conn.close()
  }
}

二、面向对象


1、类

package day65

/*
      面向对象

      类
 */

class student(id: String, name: String) {
  // 关于构造方法。这个类{}中就是构造方法
  // 需要在类名处就定义好参数
  // 即此处的 id name age
  // 在构造方法的赋值时,为了不冲突,一般会加一个_
  //不能使用this.id = id 的方法
  val _id: String = id
  val _name: String = name
  var _age: Int = _
  // _下划线表示稍后赋值,定义时必须声明类型,以及可变

  // alt+ins 可以快捷重写超类object已经定义好的类,例如 toString
  // 可以发现这种格式已经接近变量
  // 体现出了scala中面向函数的特点
  // 自动带出来的会没有{},记得加上
  override def toString = s"student(${_id}, ${_name}, ${_age})"

  //原型是
  /*
      override def toString():String={
          return s"student($_id, $_name, $_age)"
      }
      不写return会默认返回最后一行,可以省略
      返回值类型可以推断,也可以省略,代码一行,{}也可以省略
      没有参数,()也可以省略
   */


  // 如果想要重载多个构造方法,需要使用 this 有点类似构造方法的继承
  def this(id: String, name: String, age: Int) {
    // 首先需要调用默认构造方法
    this(id, name)
    _age = age
    // 且赋值的时候不能加类型
    // 感觉像是只能越写越长,所以第一个构造方法很重要
  }
}


// 不同修饰词下的同名类可以共存
object student {

  def main(args: Array[String]): Unit = {

    // 创建对象
    val stu1: student = new student("1001", "韭菜盒子")
    val stu2: student = new student("1002", "fgh", 21)

    // 取对象的属性
    println(stu1._name)
    println(stu1._age)
    println(stu1.toString)
    println(stu2 toString)
    println(stu1)
  }
}

2、继承、多态(一点点)

package day65

class A(id: String, name: String) {

  val _id: String = id
  val _name: String = name

  override def toString = s"A(_id=${_id}, _name=${_name})"

  def printAB(): Unit ={
    println("A")
  }
}

// 继承的时候要带上父类的属性(像是子类构造方法的初始化)
class B(id: String, name: String, age: Int) extends A(id, name) {

  // 初始化只需要对多出来成员的进行
  val _age: Int = age

  override def printAB(): Unit ={
    println("B")
  }

  //重写父类方法
  override def toString = 
  	s"B(_id=${_id}, _name=${_name},_age=${_age})"
}


object ScakaDemo4 {

  def main(args: Array[String]): Unit = {

    val a: A = new A("1001", "A")
    val b: B = new B("1002", "B", 21)

    println(a)
    // 继承父类方法
    println(b)

    a.printAB()
    b.printAB()


    // 多态
    def printAABB(a: A): Unit ={
      a.printAB()
    }

    printAABB(a)
    printAABB(b)
  }
}

3、样例类(case class)

可以说是为了懒人特地设计好的,已经封装好绝大部分常用方法
只需要创建时定义好成员属性,类用 case class 修饰
package day65

// 样例类
// 在样例类中定义的参数 默认由val修饰 不可以改变
// 如果需要对属性重新赋值 则需要在定义的时候 加上 var修饰
// 会自动实现get set(针对var修饰的属性)方法,
// 还会实现序列化接口(可以在网络中进行传输)
case class Student(var id:String
                   ,name:String
                   ,gender:String)

object ScalaDemo5{

  def main(args: Array[String]): Unit = {

    // 创建样例类的对象
    // new关键字可以省略
    val stu1 = Student("1001","fgh","male")
    println(stu1.id)
    stu1.id = "1000"
    println(stu1.id)
  }
}

4、apply (伴生对象)

在定义类的时候,可以定义 apply 方法
作用是直接可以通过类名(属性)直接返回一个当前属性的类对象(省个 new)
样例类中已定义好,可以直接使用
package day65


object ScalaDemo6{

  def main(args: Array[String]): Unit = {

    val aa = new AA("1001")
    val bb = new BB("1002")

    // apply 方法,可以通过调用类名默认调用
    // 创建时系统也默认写为对象的创建
    val bb2 = BB("1003")
  }

}

class AA(id:String) {
  val _id = id
}

class BB(id:String){
  val _id = id
}

// 该类称为 BB 的伴生对象
object BB {
  def apply(id: String): BB = new BB(id)
}

三、面向函数


1、函数定义

package day65


object test {

  // scala 允许一个文件中包含多个main方法
  // 调用方法尽可能在main方法的内部,
  // 否则会先运行main方法外的函数调用
  // 同一文件夹下不能有同名的 object

  def main(args: Array[String]): Unit = {

    def a(): Unit = {
      println("a")
    }
    a()
  }

  def b(): Unit = {
    println("b")
  }
  b()
}


object ScalaDemo7 {
  /*
   * 函数(方法)可以定义在什么位置?
   * 类中、Object中、方法中
   */

  def main(args: Array[String]): Unit = {

    /*
         def 这是一个函数
         func1 函数名
         str1: String, str2: String 函数的参数
         Unit: 函数的返回值类型,这里相当于void
         {} : 方法体
         等号别忘了
     */
    def fun1(str1: String, str: String): Unit = {
      print("fgh")
    }

    def fun2(int: Int): Int = {
      return int + 1
    }

    // 1、有返回值的函数,不写 return 默认返回最后一行
    def fun3(int: Int): Int = {
      int + 1
    }

    // 2、只有一行代码的函数,{}可以省略
    def fun4(int: Int): Int = int + 1

    // 3、程序会推断返回值的类型,可以省略
    def fun5(int: Int) = int + 1

    // 4、倘若函数没有参数列表,那么括号也可以省略
    // 虽然很怪,但是他是一个没有参数的函数
    def fun6 = 1 + 1

	println(fun1("fgh","fgh"))
    println(fun2(1))
    println(fun3(1))
    println(fun4(1))
    println(fun5(1))
    println(fun6)
  }
}

2、函数式编程 — 函数作为参数

package day65

object ScalaDemo8 {

  /*
        函数式编程(高阶函数、面向函数编程)

        面向对象编程:把对象传来传去
        注意:对象作为 参数 和 返回值 的时候有类型的限制

        面向函数编程:把函数传来传去
        注意:函数作为 参数 和 返回值 的时候也有类型的限制

        函数式编程的分类:
        1、以函数作为参数
        2、以函数作为返回值

        如何描述一个函数fun

        参数类型 => 返回值类型
   */

  def main(args: Array[String]): Unit = {

    // 此处是将函数作为参数进行传参
    // 而不是进行调用,因此不能加括号!
    println(fun2S(fun2))

    // 调用匿名函数
    println(funX("1", 1))


    // 使用匿名函数来传参 fun: String => Int
    println(fun2S((str: String) => {
      str.toInt + 1
    }))

    // 1、函数体只有一行代码,花括号可以省略
    println(fun2S((str: String) => str.toInt + 1))

    // 2、如果匿名函数作为另一个函数的参数传入 参数的类型可以省略
    println(fun2S(str => str.toInt + 1))

    // 3、如果参数只被使用的一次 则可以省略 并用下划线_替代
    println(fun2S(_.toInt + 1))

  }

  def fun1(int: Int): Int = int + 1
  def fun2(str: String): Int = str.toInt + 1
  
  // 这个函数的参数就是一个函数
  // 其形参需要是String 返回值需要是Int (可以是他们的父类)
  def fun2S(fun: String => Int): Int = {

    val i = fun("100")
    // 默认返回最后一行
    i
  }

  // 匿名函数
  // => 左边是参数列表记得加小括号,右边是{方法体}
  (str: String, int: Int) => {
    str.toInt + int
  }

  // 也可以用一个变量去接收
  val funX: (String, Int) => Int = (str: String, int: Int) => {
    str.toInt + int
  }
}

3、函数式编程的一些小小引用参观

用 Scala 自带的遍历数组的方式进行演示
package day65

object ScalaDemo9 {

  def main(args: Array[String]): Unit = {

    // 创建数组
    val arr: Array[Int] = Array[Int](1,2,3)

    // 遍历输出
    // 方法一
    var i:Int = 0
    while (i < arr.length){
      // scala 中下标用小括号
      println(arr(i))
      i += 1
    }

    // 方法二
    // foreach方法:需要传入一个函数f:
    //            参数类型为Int,返回值类型为Unit
    // foreach方法会将array中的每一个元素依次作为参数传递给函数f
    // println是一个函数:参数类型为Any,返回值类型为Unit
    // 这里就是将数组中的每一个Int ,传入到 println 当中,多态

    // Any是任意类型的基类
    // AnyRef:任意引用类型的基类
    // AnyVal:任意值类型的基类
    arr.foreach(println)

    // 方法三
    for (elem <- arr) {
      println(elem)
    }

//    // 方法四
//    for(i <- 0 to arr.length - 1){
//      println(arr(i))
//    }
//
//    for(i <- 0 until arr.length){
//      println(arr(i))
//    }
//
//    for(i <- Range(0,arr.length)){
//      println(arr(i))
//    }

    // 将数组中每个元素翻倍
    // 方法一 在循环中挨个 * 2
    def double(int: Int): Unit = int * 2

    arr.foreach(double)

    // mkString 类似 Python 中的 join
    // 能够将容器中的元素按照指定分隔符进行拼接
    println(arr.mkString(","))

    // 问题是 foreach 没有返回值,无法得到翻倍后的数组
    // 这里可以使用 map 方法,和 foreach 的唯一区别是有返回值

    def double2(int: Int):Int = int * 2

    // 会返回一个新对象
    val arr2: Array[Int] = arr.map(double2)
    println(arr2.mkString(","))

    // 也可以使用匿名函数
    arr.map( int=>int * 2)
    println(arr.map(_ * 2).mkString(","))
  }
}

4、函数式编程 — 函数作为返回值

package day65


object ScalaDemo10 {

  def main(args: Array[String]): Unit = {

    val f1 = fun1(1)
    val str = f1(1)
    println(str)

    // 也可以一步到位
    println(fun1(1)(1))

    pow1(2,5)

    // 固定参数,底数相同,运算不同次幂
    val a_2: Int => Unit = pow2(2)
    a_2(1)
    a_2(2)
    a_2(3)
  }

  // 以函数作为返回值时 返回值类型需要手动指定
  def fun1(int: Int): Int => String = {

    def fun2(int: Int): String = {
      val str: String = "fgh"
      // 对应fun2 :String
      str
    }
    // 对应fun1 :Int => String
    fun2
  }

  // 两个参数的函数
  def fun3(int1: Int,int2: Int)={
    println("fgh")
  }

  // 柯里化
  // 像是将参数拆开,能够将原本一个函数拆成N个
  // 特定场合可以用来固定参数
  def fun4(int1: Int)(int2: Int)={
      println("fgh")
  }

  // 幂运算
  // 方法一 可以使用默认参数
  def pow1(a: Int,b: Int =2)={
    println(Math.pow(a,b))
  }

  // 方法二
  def pow2(a:Int)(b:Int)={
    println(Math.pow(a,b))
  }
}

零碎

Scala 经过反编译后,会成为一个 Java 函数
并且变量会被反编译为函数

Any是任意类型的基类
AnyRef:任意引用类型的基类
AnyVal:任意值类型的基类
Nothing 无参数
B 任意类型返回值
Unit 无返回值
GenTraversable 容器

Any 是所有类的父类(一个始祖的概念)
Nothing 是所有类的子类(一个倒霉孙子的概念)

foreach 无返回值
map 有返回值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值