大数据技术之_16_Scala学习_04_函数式编程-基础+面向对象编程-基础

时间:2022-06-19
本文章向大家介绍大数据技术之_16_Scala学习_04_函数式编程-基础+面向对象编程-基础,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

第五章 函数式编程-基础5.1 函数式编程内容说明5.1.1 函数式编程内容5.1.2 函数式编程授课顺序5.2 函数式编程介绍5.2.1 几个概念的说明5.2.2 方法、函数、函数式编程和面向对象编程关系分析图5.2.3 函数式编程小结5.3 为什么需要函数5.4 函数的定义5.4.1 函数的定义5.4.2 快速入门案例5.5 函数的调用机制5.5.1 函数的调用过程5.5.2 函数的递归调用5.5.3 递归练习题5.6 函数注意事项和细节讨论5.7 函数练习题5.8 过程5.8.1 基本概念5.8.2 注意事项和细节说明5.9 惰性函数5.9.1 看一个应用场景5.9.2 画图说明(大数据推荐系统)5.9.3 Java 实现懒加载的代码5.9.4 惰性函数介绍5.9.5 案例演示5.9.6 注意事项和细节5.10 异常5.10.1 介绍5.10.2 Java 异常处理回顾5.10.3 Java 异常处理的注意点5.10.4 Scala 异常处理举例5.10.5 Scala 异常处理小结5.11 函数的练习题第六章 面向对象编程-基础6.1 类与对象6.1.1 Scala 语言是面向对象的6.1.2 快速入门-面向对象的方式解决养猫问题6.1.3 类和对象的区别和联系6.1.4 如何定义类6.1.5 属性6.1.6 属性/成员变量6.1.7 属性的高级部分6.1.8 如何创建对象6.1.9 类和对象的内存分配机制(重要)6.2 方法6.2.1 基本说明和基本语法6.2.2 方法的调用机制原理6.2.3 方法练习题6.3 类与对象应用实例6.4 构造器6.4.1 看一个需求6.4.2 回顾-Java 构造器的介绍+基本语法+特点+案例6.4.3 Scala 构造器的介绍+基本语法+快速入门6.4.4 Scala 构造器注意事项和细节6.5 属性高级6.5.1 构造器参数6.5.2 Bean 属性6.6 Scala 对象创建的流程分析6.7 作业03


第五章 函数式编程-基础

5.1 函数式编程内容说明

5.1.1 函数式编程内容

函数式编程-基础   1、函数定义/声明   2、函数运行机制   3、递归【难点:最短路径,邮差问题,背包问题,迷宫问题,回溯】   4、过程   5、惰性函数和异常

函数式编程-高级   6、值函数(函数字面量)   7、高阶函数   8、闭包   9、应用函数   10、柯里化函数,抽象控制…

5.1.2 函数式编程授课顺序

  1、在 scala 中,函数式编程和面向对象编程融合在一起,学习函数式编程式需要 oop 的知识,同样学习 oop 需要函数式编程的基础。[矛盾]   2、二者关系如下图:

  3、授课顺序:函数式编程基础 -> 面向对象编程 -> 函数式编程高级

5.2 函数式编程介绍

5.2.1 几个概念的说明

  在学习 Scala 中将方法、函数、函数式编程和面向对象编程明确一下:   1、在 scala 中,方法函数几乎可以等同(比如他们的定义、使用、运行机制都一样的),只是函数的使用方式更加的灵活多样。   2、函数式编程是从编程方式(范式)的角度来谈的,可以这样理解:函数式编程把函数当做一等公民,充分利用函数、支持的函数的多种使用方式。   比如:在 Scala 当中,函数是一等公民,像变量一样,既可以作为函数的参数使用,也可以将函数赋值给一个变量,函数的创建不用依赖于类或者对象,而在 Java 当中,函数的创建则要依赖于类、抽象类或者接口。   3、面向对象编程是以对象为基础的编程方式。   4、在 scala 中函数式编程和面向对象编程融合在一起了。 示例代码如下:

package com.atguigu.chapter05

object Method2Function {
  def main(args: Array[String]): Unit = {
    // 传统的方式使用方法
    // 先创建一个对象
    val dog = new Dog
    println(dog.sum(10, 20))

    // 方法转成函数后使用函数
    val f1 = dog.sum _
    println("f1=" + f1) // f1=<function2>
    println("f1=" + f1(50, 60))

    // 直接写一个函数并使用函数
    // 格式:val f2 = (Int, Int) => {}
    val f2 = (n1: Int, n2: Int) => {
      n1 + n2 // 函数体
    }
    println("f2=" + f2) // f2=<function2>
    println("f2=" + f2(80, 90))
  }
}

class Dog {
  // 方法
  def sum(n1: Int, n2: Int): Int = {
    n1 + n2 // 方法体
  }
}

输出结果如下:

30
f1=<function2>
f1=110
f2=<function2>
f2=170

5.2.2 方法、函数、函数式编程和面向对象编程关系分析图

在学习 Scala 中将方法、函数、函数式编程和面向对象编程关系分析图如下:

5.2.3 函数式编程小结

  1、“函数式编程”是一种“编程范式”(programming paradigm)。   2、它属于“结构化编程”的一种,主要思想是把运算过程尽量写成一系列嵌套的函数调用。   3、函数式编程中,将函数也当做数据类型,因此可以接受函数当作输入(参数)和输出(返回值)。   4、函数式编程中,最重要的就是函数。

5.3 为什么需要函数

学习一个技术或者知识点的流程:

5.4 函数的定义

5.4.1 函数的定义

5.4.2 快速入门案例

使用函数完全前面的案例。 示例代码如下:

package com.atguigu.chapter05

object FunDemo01 {
  def main(args: Array[String]): Unit = {
    println("" + getRes(10, 20, '+'))
  }

  // 定义一个函数/方法
  def getRes(n1: Int, n2: Int, oper: Char) = { // 返回值形式2: = 表示返回值类型不确定,使用类型推导完成。
    if (oper == '+') {
      // return n1 + n2 // return 关键字可以写可以不写
      n1 + n2
    } else if (oper == '-') {
      n1 - n2
    } else {
      // 返回 null
      null
    }
  }
}

5.5 函数的调用机制

5.5.1 函数的调用过程

  为了让大家更好的理解函数调用机制,看1个案例,并画出示意图,这个很重要,比如 getSum 计算两个数的和,并返回结果。

5.5.2 函数的递归调用

注意:Struts2 中的拦截器的底层实现机制就是把一个对象放到堆中,然后不停的开栈去指向该对象的内存地址,每一个栈都有机会去修改该对象的值。

函数递归需要遵守的重要原则(总结)   1、程序执行一个函数时,就创建一个新的受保护的独立空间(新函数栈)。   2、函数的局部变量是独立的,不会相互影响。   3、递归必须向退出递归的条件逼近,否则就是无限递归,死龟了:)   4、当一个函数执行完毕,或者遇到 return,就会返回,遵守谁调用,就将结果返回给谁。

5.5.3 递归练习题

题1:斐波那契数,请使用递归的方式,求出斐波那契数1,1,2,3,5,8,13…给你一个整数n,求出它的斐波那契数是多少? 示例代码如下:

package com.atguigu.chapter05.recursive

import scala.io.StdIn

/**
  * 题1:斐波那契数,请使用递归的方式,求出斐波那契数1,1,2,3,5,8,13... 给你一个整数n,求出它的斐波那契数是多少?
  * 思路:f(1)=1, f(2)=1, f(3)=f(2)+f(1)=1+1=2, f(4)=f(2)+f(3)=1+2=3, ..., f(n)=f(n-1)+f(n-2)
  */
object Exercise01 {
  def main(args: Array[String]): Unit = {

    println("请输入一个正整数:")
    val n = StdIn.readInt()
    printf("%d的斐波那契数是:%d", n, fbn(n))
  }

  def fbn(n: Int): Int = {
    if (n == 1 || n == 2) {
      1
    } else {
      fbn(n - 1) + fbn(n - 2)
    }
  }
}

题2:求函数值,已知 f(1)=3; f(n) = 2*f(n-1)+1; 请使用递归的思想编程,求出 f(n) 的值? 示例代码如下:

package com.atguigu.chapter05.recursive

import scala.io.StdIn

/**
  * 题2:求函数值,已知 f(1)=3; f(n) = 2*f(n-1)+1; 请使用递归的思想编程,求出 f(n) 的值?
  * n=1, f(1)=3
  * n=2, f(2)=2*f(1)+1=7
  * n=3, f(3)=2*f(2)+1=15
  *
  */
object Exercise02 {
  def main(args: Array[String]): Unit = {
    println("请输入一个正整数:")
    val n = StdIn.readInt()
    printf("f(%d) 的值是:%d", n, f(n))
  }

  def f(n: Int): Int = {
    if (n == 1) {
      3
    } else {
      2 * f(n - 1) + 1
    }
  }
}

题3:猴子吃桃子问题:有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!以后每天猴子都吃其中的一半,然后再多吃一个。当到第十天时,想再吃时(还没吃),发现只有1个桃子了。问题:最初共多少个桃子? 示例代码如下:

package com.atguigu.chapter05.recursive

import com.atguigu.chapter05.recursive.Exercise02.f

import scala.io.StdIn

/**
  * 题3:猴子吃桃子问题:有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!
  * 以后每天猴子都吃其中的一半,然后再多吃一个。当到第十天时,想再吃时(还没吃),发现只有1个桃子了。问题:最初共多少个桃子?
  *
  * day = 10 桃子有 1
  * day =  9 桃子有 (day10的桃子 + 1) *2
  * day =  8 桃子有 (day9 的桃子 + 1) *2
  *
  */
object Exercise03 {
  def main(args: Array[String]): Unit = {
    println("最初共有:" + f(1) + "个桃子")
  }

  def f(n: Int): Int = {
    if (n == 10) {
      1
    } else {
      (f(n + 1) + 1) * 2
    }
  }
}

5.6 函数注意事项和细节讨论

  1、函数的形参列表可以是多个,如果函数没有形参,调用时可以不带()。

  2、函数的形参列表和返回值列表的数据类型可以是值类型和引用类型。【案例演示】 示例代码如下:

package com.atguigu.chapter05.fundetails

object Details01 {
  def main(args: Array[String]): Unit = {
    val tiger = new Tiger
    val tiger2 = test01(10, tiger)
    println(tiger2.name) // tom
    println(tiger.name)  // tom
    println(tiger.hashCode() + " " + tiger2.hashCode()) // 2101440631 2101440631
  }

  // 2、函数的形参列表和返回值列表的数据类型可以是值类型和引用类型。
  def test01(n: Int, tiger: Tiger): Tiger = {
    println("n=" + n)
    tiger.name = "tom"
    tiger
    // return tiger // 3、Scala 中的函数可以根据函数体最后一行代码自行推断函数返回值类型。那么在这种情况下,return 关键字可以省略。
  }
}

class Tiger {
  var name = ""
}

  3、Scala 中的函数可以根据函数体最后一行代码自行推断函数返回值类型。那么在这种情况下,return 关键字可以省略。【案例同上】   4、因为 Scala 可以自行推断,所以在省略 return 关键字的场合,返回值类型也可以省略。

  5、如果函数明确使用 return 关键字,那么函数返回就不能使用自行推断了,这时要明确写成 : 返回值类型 = ,当然如果你什么都不写,即使有 return,那么返回值为(),即这时 return 无效。   6、如果函数明确声明无返回值(声明 Unit),那么函数体中即使使用 return 关键字也不会有返回值。 示例代码如下:

package com.atguigu.chapter05.fundetails

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

    println(getSum2(10, 30))  // ()

    println(getSum3(9, 9))    // ()

  }

  // 如果写了 return,那么返回值类型就不能省略。
  def getSum(n1: Int, n2: Int): Int = {
    return n1 + n2
  }

  // 如果返回值这里什么什么都没有写,即表示该函数没有返回值。
  // 这时 return 无效
  def getSum2(n1: Int, n2: Int) {
    return n1 + n2
  }

  // 如果函数明确声明无返回值(声明Unit),那么函数体中即使使用 return 关键字也不会有返回值。
  def getSum3(n1: Int, n2: Int): Unit = {
    return n1 + n2
  }
}

  7、如果明确函数无返回值或不确定返回值类型,那么返回值类型可以省略(或声明为 Any)。 示例代码如下:

package com.atguigu.chapter05.fundetails

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

  }

  // 7、如果明确函数无返回值或不确定返回值类型,那么返回值类型可以省略(或声明为 Any)。
  def f3(s: String) = {
    if (s.length >= 3)
      s + "123"
    else
      3
  }

  def f4(s: String): Any = {
    if (s.length >= 3)
      s + "123"
    else
      3
  }
}

  8、Scala 语法中任何的语法结构都可以嵌套其他语法结构(很灵活),即:函数中可以再声明/定义函数类中可以再声明类方法中可以再声明/定义方法。 示例代码如下:

package com.atguigu.chapter05.fundetails

object Details04 {
  def main(args: Array[String]): Unit = { // public void main(String[] args)

    def f1():Unit = { // private final void f1$1
      println("f1")
    }

    def sayok(): Unit = { // private final void sayok$1
      println("sayok~")
      def sayok(): Unit = { // private final void sayok$2
        println("sayok~~")
      }
    }

    println("ok")
  }

  def sayok(): Unit = { // public void sayok()
    println("sayok")
  }
}

  9、Scala 函数的形参,在声明参数时,直接赋初始值(默认值),这时调用函数时,如果没有指定实参,则会使用默认值。如果指定了实参,则实参会覆盖默认值。 示例代码如下:

package com.atguigu.chapter05.fundetails

object Details05 {
  def main(args: Array[String]): Unit = {
    println(sayOk())      // jack ok!
    println(sayOk("tom")) // tom ok!
  }

  def sayOk(name: String = "jack"): String = {
    return name + " ok! "
  }
}

  10、如果函数存在多个参数,每一个参数都可以设定默认值,那么这个时候,传递的参数到底是覆盖默认值,还是赋值给没有默认值的参数,就不确定了(默认按照声明顺序[从左到右])。在这种情况下,可以采用带名参数。 示例代码如下:

package com.atguigu.chapter05.fundetails

object Details06 {
  def main(args: Array[String]): Unit = {
    mysqlCon()
    mysqlCon("127.0.0.1", 7777) // 从左到右覆盖

    // 如果我们希望指定覆盖某一个默认值,则使用带名参数即可,比如只想修改用户名和密码,其他的不改
    mysqlCon(user = "tom", pwd = "1234")

    // 练习
    // f6("v2") // 报错,p2的值没有指定
    f6(p2 = "v2") // v1v2
  }

  def mysqlCon(add: String = "localhost", port: Int = 3306,
               user: String = "root", pwd: String = "root"): Unit = {
    println("add=" + add)
    println("port=" + port)
    println("user=" + user)
    println("pwd=" + pwd)
  }

  def f6(p1: String = "v1", p2: String) {
    println(p1 + p2);
  }
}

  11、scala 函数的形参默认是 val 的,因此不能在函数中进行修改。

  12、递归函数未执行之前是无法推断出来结果类型,在使用时必须有明确的返回值类型。 示例代码如下:

  def f(n: Int) = { // 错误,递归不能使用类型推断,必须指定返回的数据类型。
    if (n <= 0)
      1
    else
      n * f(n - 1)
  }

  13、Scala 函数支持可变参数。 示例代码如下:

  // 支持0到多个参数
  def sum(args: Int*): Int = {
  }

  // 支持1到多个参数
  def sum(n1: Int, args: Int*): Int = {
  }

  说明:   1、args 是集合, 通过 for 循环 可以访问到各个值。【args 是参数名,可以任意起】   2、案例演示: 编写一个函数 sum,可以求出 1 到多个 int 的和。   3、可变参数需要写在形参列表的最后。 示例代码如下:

package com.atguigu.chapter05.fundetails

object VarParameters {
  def main(args: Array[String]): Unit = {
    println(sum(10, 20, 30)) // 这里可变参数为2个
  }

  // 支持1到多个参数
  def sum(n1: Int, args: Int*): Int = {
    println("args.length=" + args.length)
    var sum = n1
    for (item <- args) {
      sum += item
    }
    sum
  }
}

5.7 函数练习题

判断下面的代码是否正确: 示例代码如下:

  object Hello01 {
    def main(args: Array[String]): Unit = {
      def f1 = "venassa"
      println(f1) // 输出 venassa
    }
  }

  // 上面代码 def f1 = "venassa" 等价于
  def f1() = {
    "venassa"
  }
  // 说明:
  // 1、函数的形参列表可以是多个,如果函数没有形参,函数可以不带()。
  // 2、函数的函数体只有一行代码时,可以省略{}。

5.8 过程

5.8.1 基本概念

基本介绍:   将函数的返回类型为 Unit 的函数称之为过程(procedure),如果明确函数没有返回值,那么等号可以省略。

案例说明:

  // f10 没有返回值,可以使用 Unit 来说明
  // 这时,这个函数我们也叫过程(procedure)
  def f10(name: String): Unit = { // 如果明确函数没有返回值,那么等号可以省略。
    println(name+ "hello")
  }

5.8.2 注意事项和细节说明

  1、注意区分: 如果函数声明时没有返回值类型,但是有 = 号,可以进行类型推断最后一行代码。这时这个函数实际是有返回值的,该函数并不是过程。(这点在讲解函数细节的时候讲过的)   2、开发工具的自动代码补全功能,虽然会自动加上 Unit,但是考虑到 Scala 语言的简单,灵活,最好不加。

5.9 惰性函数

5.9.1 看一个应用场景

  惰性计算(尽可能延迟表达式求值)是许多函数式编程语言的特性。惰性集合在需要时提供其元素,无需预先计算它们,这带来了一些好处。首先,您可以将耗时的计算推迟到绝对需要的时候。其次,您可以创造无限个集合,只要它们继续收到请求,就会继续提供元素。函数的惰性使用让您能够得到更高效的代码。Java 并没有为惰性提供原生支持,Scala 提供了。

5.9.2 画图说明(大数据推荐系统)

5.9.3 Java 实现懒加载的代码

示例代码如下:

package com.atguigu.chapter05;

public class LazyDemo {

    private String property; // 属性也可能是一个数据库连接,文件等资源

    public String getProperty() {
        if (property == null) { // 如果没有初始化过,那么就进行初始化
            property = initProperty();
        }
        return property;
    }

    private String initProperty() {
        return "property";
    }
}
// 比如常用的【单例模式懒汉式】实现时就使用了上面类似的思路实现

5.9.4 惰性函数介绍

当函数返回值被声明为 lazy 时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行,这种函数我们称之为惰性函数。在 Java 的某些框架代码中称之为懒加载(延迟加载)

5.9.5 案例演示

示例代码如下:

package com.atguigu.chapter05.mylazy

object LazyDemo01 {
  def main(args: Array[String]): Unit = {
    lazy val res = sum(10, 20)
    println("----------")
    println("res=" + res) // 在要使用 res 前,才执行
  }

  def sum(n1: Int, n2: Int): Int = {
    println("sum() 执行了..")
    return n1 + n2
  }
}

输出结果如下:

----------
sum() 执行了..
res=30

5.9.6 注意事项和细节

  1、lazy 不能修饰 var 类型的变量。   2、不但是在调用函数时,加了 lazy,会导致函数的执行被推迟,我们在声明一个变量时,如果声明了 lazy,那么变量值的分配也会推迟。 比如 lazy val i = 10。

5.10 异常

5.10.1 介绍

Scala 提供 try 块和 catch 块来处理异常。try 块用于包含可能出错的代码。catch 块用于处理 try 块中发生的异常。可以根据需要在程序中有任意数量的 try…catch 块。   语法处理上和 Java 类似,但是又不尽相同。

5.10.2 Java 异常处理回顾

示例代码如下:

package com.atguigu.chapter05.exception;

public class JavaExceptionDemo {
    public static void main(String[] args) {
        try {
            // 可疑代码
            int i = 0;
            int b = 10;
            int c = b / i; // 执行代码时,会抛出 ArithmeticException 异常
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 最终要执行的代码
            System.out.println("java finally");
        }

        System.out.println("继续执行");
    }
}

输出结果如下:

java finally
继续执行
java.lang.ArithmeticException: / by zero
    at com.atguigu.chapter05.exception.JavaExceptionDemo.main(JavaExceptionDemo.java:9)

5.10.3 Java 异常处理的注意点

  1、java 语言按照 try-catch-catch…-finally 的方式来处理异常。   2、不管有没有异常捕获,都会执行 finally,因此通常可以在 finally 代码块中释放资源。   3、可以有多个 catch,分别捕获对应的异常,这时需要把范围小的异常类写在前面,把范围大的异常类写在后面,否则编译错误。会提示 "Exception 'java.lang.xxxxxx' has already been caught"。

5.10.4 Scala 异常处理举例

示例代码如下:

package com.atguigu.chapter05.exception

object ScalaExceptionDemo {
  def main(args: Array[String]): Unit = {
    try {
      val r = 10 / 0
    } catch {
      // 说明
      // 1. 在 scala 中只有一个 catch
      // 2. 在 catch 中有多个 case, 每个 case 可以匹配一种异常
      // 3. => 关键符号,表示后面是对该异常的处理代码块
      // 4. finally 最终要执行的代码
      case ex: ArithmeticException => { println("捕获了除数为零的算数异常") } // 当对该异常的处理代码块为一行时,{}可以省略
      case ex: Exception => println("捕获了异常")
    } finally {
      // 最终要执行的代码
      println("scala finally")
    }

    System.out.println("继续执行")
  }
}

输出结果如下:

捕获了除数为零的算数异常
scala finally
继续执行

5.10.5 Scala 异常处理小结

  1、我们将可疑代码封装在 try 块中。在 try 块之后使用了一个 catch 处理程序来捕获异常。如果发生任何异常,catch 处理程序将处理它,异常处理了程序将不会异常终止。   2、Scala 的异常的工作机制和 Java 一样,但是 Scala 没有“checked(编译期)” 异常,即 Scala 没有编译异常这个概念,异常都是在运行的时候捕获处理。   3、Scala 用 throw 关键字,抛出一个异常对象。所有异常都是 Throwable 的子类型。throw 表达式是有类型的,就是 Nothing,因为 Nothing 是所有类型的子类型,所以 throw 表达式可以用在需要类型的地方。 示例代码如下:

package com.atguigu.chapter05.exception

object ThrowDemo {
  def main(args: Array[String]): Unit = {
    // val res = test()
    // println(res.toString)
    // println("继续执行002") // 异常抛出了,但是没有被处理,后续程序不能执行

    // 如果我们希望在 test() 抛出异常后,后续代码可以继续执行,则我们需要如下处理
    try {
      test()
    } catch {
      case ex: Exception => {
        println("捕获到异常是:" + ex.getMessage)
        println("继续执行001")
      }
      case ex: ArithmeticException => println("得到一个算术异常(小范围异常)")
    } finally {
      // 写上对 try{} 中的资源的分配
    }

    println("继续执行002")
  }

  def test(): Nothing = {
    // Exception("异常出现")
    throw new ArithmeticException("算术异常")
  }
}

输出结果如下:

捕获到异常是:算术异常
继续执行001
继续执行002

  4、在 Scala 里,借用了模式匹配的思想来做异常的匹配,因此,在 catch 的代码里,是一系列 case 子句来匹配异常。【前面案例可以看出这个特点,模式匹配我们后面详解】,当匹配上后 => 有多条语句可以换行写,类似 java 的 switch case x: 代码块…   5、异常捕捉的机制与其他语言中一样,如果有异常发生,catch 子句是按次序捕捉的。因此,在 catch 子句中,越具体的异常越要靠前,越普遍的异常越靠后,如果把越普遍的异常写在前,把具体的异常写在后,在 scala 中也不会报错,但这样是非常不好的编程风格。   6、finally 子句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,一般用于对象的清理工作,这点和 Java 一样。   7、Scala 提供了 throws 关键字来声明异常。可以使用方法定义声明异常。它向调用者函数提供了此方法可能引发此异常的信息。它有助于调用函数处理并将该代码包含在 try-catch 块中,以避免程序异常终止。在 scala 中,可以使用 throws 注释来声明异常。 示例代码如下:

package com.atguigu.chapter05.exception

object ThrowsComment {
  def main(args: Array[String]): Unit = {
    f()
  }

  @throws(classOf[NumberFormatException]) // 等同于 Java 中 NumberFormatException.class
  def f() = {
    "abc".toInt
  }
}

输出结果如下:

Exception in thread "main" java.lang.NumberFormatException: For input string: "abc"
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Integer.parseInt(Integer.java:580)
    at java.lang.Integer.parseInt(Integer.java:615)
    at scala.collection.immutable.StringLike$class.toInt(StringLike.scala:272)
    at scala.collection.immutable.StringOps.toInt(StringOps.scala:29)
    at com.atguigu.chapter05.exception.ThrowsComment$.f(ThrowsComment.scala:10)
    at com.atguigu.chapter05.exception.ThrowsComment$.main(ThrowsComment.scala:5)
    at com.atguigu.chapter05.exception.ThrowsComment.main(ThrowsComment.scala)

5.11 函数的练习题

1、函数可以没有返回值案例,编写一个函数,从终端输入一个整数打印出对应的金子塔。 示例代码如下:

package com.atguigu.chapter05.exercises

import scala.io.StdIn

/**
  * 1、函数可以没有返回值案例,编写一个函数,从终端输入一个整数打印出对应的金子塔。
  * 思路:本质是打印出所有的 n行m列 数据。 分别循环即可!
  */
object Exercise01 {
  def main(args: Array[String]): Unit = {
    println("请输入一个整数n(n>=1):")
    val n = StdIn.readInt()
    printJin(n)
  }

  def printJin(n: Int): Unit = {
    for (i <- 1 to n) { // 行数
      for (j <- 1 to (n - i)) {
        printf("-")
      }
      for (j <- 1 to (2 * i - 1)) { // 列数
        printf("*")
      }
      for (j <- 1 to (n - i)) {
        printf("-")
      }
      println()
    }
  }
}

输出结果如下:

请输入一个整数n(n>=1):
5
----*----
---***---
--*****--
-*******-
*********

2、编写一个函数,从终端输入一个整数(1—9),打印出对应的乘法表。 示例代码如下:

package com.atguigu.chapter05.exercises

import scala.io.StdIn

/**
  * 2、编写一个函数,从终端输入一个整数(1—9),打印出对应的乘法表。
  */
object Exercise01 {
  def main(args: Array[String]): Unit = {
    println("请输入数字(1-9)之间:")
    val n = StdIn.readInt()
    print99(n)
  }

  def print99(n: Int): Unit = {
    for (i <- 1 to n) {
      for (j <- 1 to i) {
        printf("%d * %d = %dt", j, i, j * i)
      }
      println()
    }
  }
}

3、编写函数,对给定的一个二维数组 (3×3) 转置,这个题讲数组的时候再完成。

    1 2 3       1 4 7            
    4 5 6       2 5 8               
    7 8 9       3 6 9

第六章 面向对象编程-基础

6.1 类与对象

看一个养猫猫问题:   张老太养了只猫猫:一只名字叫小白,今年3岁,白色。还有一只叫小花,今年10岁,花色。请编写一个程序,当用户输入小猫的名字时,就显示该猫的名字,年龄,颜色。如果用户输入的小猫名错误,则显示张老太没有这只猫猫。

问题:   1、猫有三个属性,类型不一样。   2、如果使用普通的变量就不好管理。   3、使用一种新的数据类型:     (1) 可以管理多个不同类型的数据 [属性]。     (2) 可以对属性进行操作 => 方法。

类与对象的关系示意图

6.1.1 Scala 语言是面向对象的

  1、Java 是面向对象的编程语言,由于历史原因,Java 中还存在着非面向对象的内容:基本类型,null,静态方法等。   2、Scala 语言来自于 Java,所以天生就是面向对象的语言,而且 Scala 是纯粹的面向对象的语言,即在 Scala 中,一切皆为对象。   3、在面向对象的学习过程中可以对比着 Java 语言学习。

6.1.2 快速入门-面向对象的方式解决养猫问题

示例代码如下:

package com.atguigu.chapter06.oop

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

    // 创建一只猫
    val cat = new Cat

    // 给猫的属性赋值
    // 说明
    // 1. cat.name = "小白" 其实不是直接访问属性,而是等价于 cat.name_$eq("小白")
    // 2. cat.name 等价于 cat.name()
    cat.name = "小白"
    cat.age = 3
    cat.color = "白色"

    printf("n小猫的信息如下:%s %d %s", cat.name, cat.age, cat.color)
  }
}

/* 反编译查看源码
public void main (String[] args) {
  Cat cat = new Cat ();

  cat.name_$eq ("小白");
  cat.age_$eq (3);
  cat.color_$eq ("白色");
}
*/

// 定义一个 Cat 类
// 一个class Cat 对应的字节码文件只有一个 Cat.class ,默认是public
class Cat {
  // 定义/声明三个属性
  // 说明
  // 1. 当我们声明了 var name: String 时,同时在底层对应生成 private name
  // 2. 同时在底层会生成 两个 public 方法 public String name() 类似 => getter 和 public void name_$eq(String x$1) => setter
  var name: String = "" // Scala 中定义变量必须给初始值
  var age: Int = _ // 下划线表示给 age 一个默认值,如果是 Int 类型,默认就是 0
  var color: String = _ // 如果是 String 类型,默认值就是 null
}

/* 反编译查看源码
public class Cat {
  private String name = "";
  private int age;
  private String color;

  public String name() {
    return this.name;
  }
  public void name_$eq(String x$1) {
    this.name = x$1;
  }
  public int age() {
    return this.age;
  }
  public void age_$eq(int x$1) {
    this.age = x$1;
  }
  public String color() {
    return this.color;
  }
  public void color_$eq(String x$1) {
    this.color = x$1;
  }
}
*/

输出结果如下:

小猫的信息如下:小白 3 白色

6.1.3 类和对象的区别和联系

通过上面的案例和讲解我们可以看出:   1、类是抽象的,概念的,代表一类事物,比如人类,猫类…   2、对象是具体的,实际的,代表一个具体事物。   3、类是对象的模板,对象是类的一个个体,对应一个实例。   4、Scala 中类和对象的区别和联系 和 Java 是一样的。

6.1.4 如何定义类

我们可以通过反编译来看 scala 的类默认为 public 的特性。

6.1.5 属性

示例代码如下:

class Dog {
  var name = "jack"
  var lover = new Fish
}

class Fish {

}

6.1.6 属性/成员变量

示例代码如下:

package com.atguigu.chapter06.oop

object PropertyDemo {
  def main(args: Array[String]): Unit = {
    // val p1 = new Person
    // println(p1.Name)     // Null
    // println(p1.address)  // String 类型

    val a = new A
    println(a.var1) // null
    println(a.var2) // 0
    println(a.var3) // 0.0
    println(a.var4) // false

    // 不同对象的属性是独立,互不影响,一个对象对属性的更改,不影响另外一个
    // 创建两个对象
    var worker1 = new Worker
    worker1.name = "jack"
    var worker2 = new Worker
    worker2.name = "tom"
  }
}

class Person3 {
  var age: Int = 10   // 给属性赋初值,省略类型,会自动推导
  var sal = 8090.9
  var Name = null     // Name 是什么类型
  var address: String = null // ok
}

class A {
  var var1: String = _  // null   String 和 引用类型默认值是 null
  var var2: Byte = _    // 0
  var var3: Double = _  // 0.0
  var var4: Boolean = _ // false
}

class Worker {
  var name = ""
}

输出结果如下:

null
0
0.0
false

6.1.7 属性的高级部分

  说明:属性的高级部分和构造器(构造方法/函数) 相关,我们把属性高级部分放到构造器那里讲解。

6.1.8 如何创建对象

示例代码如下:

package com.atguigu.chapter06.oop

object CreateObjDemo {
  def main(args: Array[String]): Unit = {
    val emp = new Emp // 此时的 emp 类型就是 Emp

    // 如果我们希望将子类对象,交给父类引用,这时就需要写上类型,不能省略!
    val emp1: Person = new Emp
  }
}

class Person {

}

class Emp extends Person {

}

6.1.9 类和对象的内存分配机制(重要)

内存布局图:

示例代码如下:

package com.atguigu.chapter06.oop

object MemState {
  def main(args: Array[String]): Unit = {
    val p2 = new Person2
    p2.name = "jack"
    p2.age= 10

    val p1 = p2
    println(p1 == p2) // true
    println("p2.age=" + p2.age) // 10
    println("p1.age=" + p1.age) // 10
  }
}

class Person2 {
  var name = ""
  var age: Int = _ // 如果是用下划线的方式给默认值,则属性必须指定类型,因为这有这样,scala 底层才能够进行类型推断
}

6.2 方法

6.2.1 基本说明和基本语法

示例代码如下:

package com.atguigu.chapter06.method

object MethodDemo01 {
  def main(args: Array[String]): Unit = {
    // 使用一把
    val dog = new Dog
    println(dog.cal(10, 20))
  }
}

class Dog {
  private var sal: Double = _
  var food: String = _

  def cal(n1: Int, n2: Int): Int = {
    return n1 + n2
  }
}

6.2.2 方法的调用机制原理

6.2.3 方法练习题

1~3题的示例代码如下:

package com.atguigu.chapter06.method

/**
  * 1、编写类(MethodExec),编写一个方法,方法不需要参数,在方法中打印一个10*8的矩形,在main方法中调用该方法。
  *
  * 2、修改上一个程序,编写一个方法中,方法不需要参数,计算该矩形的面积,并将其作为方法返回值。在main方法中调用该方法,接收返回的面积值并打印(结果保留小数点2位)。
  *
  * 3、修改上一个程序,编写一个方法,提供m和n两个参数,方法中打印一个m*n的矩形,再编写一个方法计算该矩形的面积(可以接收长len和宽width), 将其作为方法返回值。在main方法中调用该方法,接收返回的面积值并打印。
  */
object MethodDemo02 {
  def main(args: Array[String]): Unit = {
    val m = new MethodExec
    m.printRect1()

    m.len = 1.2
    m.width = 3.4
    println("面积=" + m.area1())

    m.printRect2(5, 4)
    println("面积=" + m.area2(1.2, 3.4))
  }
}

class MethodExec {

  var len = 0.0
  var width = 0.0

  def printRect1(): Unit = {
    for (i <- 0 until 10) {
      for (j <- 0 until 8) {
        print("*")
      }
      println()
    }
  }

  def area1(): Double = {
    this.len * this.width.formatted("%.2f").toDouble
  }

  def printRect2(m: Int, n: Int): Unit = {
    for (i <- 0 until m) {
      for (j <- 0 until n) {
        printf("*")
      }
      println()
    }
  }

  def area2(len: Double, width: Double): Double = {
    len * width.formatted("%.2f").toDouble
  }
}

输出结果如下:

********
********
********
********
********
********
********
********
********
********
面积=4.08
****
****
****
****
****
面积=4.08

4~6题的示例代码原理同上1~3题,不在赘述!

6.3 类与对象应用实例

景区门票案例

小狗案列的示例代码如下:

package com.atguigu.chapter06.dogcase

/**
  * 小狗案例
  *
  * 编写一个Dog类,包含name(String)、age(Int)、weight(Double)属性。
  * 类中声明一个say方法,返回String类型,方法返回信息中包含所有属性值。
  * 在另一个DogCaseTest类中的main方法中,创建Dog对象,并访问say方法和所有属性,将调用结果打印输出。
  */
object DogCaseTest {
  def main(args: Array[String]): Unit = {
    val dog = new Dog
    dog.name = "泰斯特"
    dog.age = 2
    dog.weight = 50
    println(dog.say())
  }
}

class Dog {
  var name = ""
  var age = 0
  var weight = 0.0

  def say(): String = {
    "小狗的信息是:name=" + this.name + "tage=" + this.age + "tweight=" + this.weight
  }
}

输出结果如下:

小狗的信息是:name=泰斯特    age=2   weight=50.0

盒子案列、景区门票案例的示例代码原理同上小狗案例,不在赘述!

6.4 构造器

6.4.1 看一个需求

  前面我们在创建 Person 的对象时,是先把一个对象创建好后,再给他的年龄和姓名属性赋值,如果现在我要求,在创建人类的对象时,就直接指定这个对象的年龄和姓名,该怎么做? 这时就可以使用构造方法/构造器。

6.4.2 回顾-Java 构造器的介绍+基本语法+特点+案例

Java 构造器的介绍   构造器(constructor)又叫构造方法,是类的一种特殊的方法,它的主要作用是完成对新对象的初始化

Java 构造器的基本语法

Java 构造器的特点

Java 构造器的案例

6.4.3 Scala 构造器的介绍+基本语法+快速入门

Scala 构造器的介绍   和 Java 一样,Scala 构造对象也需要调用构造方法,并且可以有任意多个构造方法(即 scala 中构造器也支持重载)。   Scala 类的构造器包括: 主构造器辅助构造器

Scala 构造器的基本语法

Scala 构造器的快速入门 示例代码如下:

package com.atguigu.chapter06.constructor

/**
  * Scala构造器的快速入门:创建Person对象的同时初始化对象的age属性值和name属性值
  */
object ConstructorDemo01 {
  def main(args: Array[String]): Unit = {
    val p1 = new Person("bruce", 20)
    println(p1)
  }
}

class Person(inName: String, inAge: Int) {
  var name: String = inName
  var age: Int = inAge

  // 重写toString方法
  override def toString: String = {
    "name=" + this.name + "tage=" + this.age
  }
}

输出结果如下:

name=bruce    age=20

6.4.4 Scala 构造器注意事项和细节

  1、Scala 构造器作用是完成对新对象的初始化,构造器没有返回值。   2、主构造器的声明直接放置于类名之后。【可以反编译查看】   3、主构造器会执行类定义中的所有语句(除掉函数部分),这里可以体会到 Scala 的函数式编程和面向对象编程融合在一起,即:构造器也是方法(函数),传递参数和使用方法和前面的函数部分内容没有区别。【案例演示+反编译查看-语法糖】   4、如果主构造器无参数,小括号可省略,构建对象时调用的构造方法的小括号也可以省略。   5、辅助构造器名称为 this(这个和 Java 是不一样的),多个辅助构造器通过不同参数列表进行区分, 在底层就是f构造器重载。【案例演示+反编译查看】 示例代码如下:

package com.atguigu.chapter06.constructor

object ConstructorDemo02 {
  def main(args: Array[String]): Unit = {
    // val a = new A
    val aa = new A("jack")
    // 执行顺序:
    // 1、bbb  父类构造器
    // 2、A    子类主构造器
    // 3、aaa  子类辅助构造器
  }
}

class B {
  println("bbb")
}

class A extends B {
  println("A")
  def this(name: String) {
    this // 调用A的主构造器,其根本原因就是实现子类与父类之间的继承关系,不然继承关系就断了!!!
    println("aaa")
  }
}

输出结果如下:

bbb
A
aaa

示例代码如下:

package com.atguigu.chapter06.constructor

object ConstructorDemo03 {
  def main(args: Array[String]): Unit = {
    val p1 = new Person("scott")
    p1.showInfo()
  }
}

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

  def this(name: String) {
    // 辅助构造器无论是直接或间接,最终都一定要调用主构造器,执行主构造器的逻辑
    // 而且需要放在辅助构造器的第一行[这点和 java 一样,java 中一个构造器要调用同类的其它构造器,也需要放在第一行]
    this() // 直接调用主构造器
    this.name = name
  }

  def this(name: String, age: Int) {
    this() // 直接调用主构造器
    this.name = name
    this.age = age
  }

  def this(age: Int) {
    this("匿名") // 间接调用主构造器,因为 def this(name: String) 中直接调用了主构造器
    this.age = age
  }

  def showInfo(): Unit = {
    println("person信息如下:")
    println("name=" + this.name)
    println("age=" + this.age)
  }
}

输出结果如下:

person信息如下:
name=scott
age=0

  6、如果想让主构造器变成私有的,可以在()之前加上 private,这样用户只能通过辅助构造器来构造对象了。【反编译查看】   7、辅助构造器的声明不能和主构造器的声明(即形参列表)一致,会发生错误(即构造器名重复)。

6.5 属性高级

  前面我们讲过属性了,这里我们再对属性的内容做一个加强。

6.5.1 构造器参数

示例代码如下:

package com.atguigu.chapter06.constructor

object ConstructorDemo04 {
  def main(args: Array[String]): Unit = {
    val worker1 = new Worker1("smith1")
    worker1.name    // 不能访问 inName

    val worker2 = new Worker2("smith2")
    worker2.inName  // 可以访问 inName
    println("hello!")

    val worker3 = new Worker3("jack")
    worker3.inName = "mary"
    println(worker3.inName)
  }
}

// 1. 如果 主构造器是 Worker1(inName: String),那么 inName 就是一个局部变量。
class Worker1(inName: String) {
  var name = inName
}
// 2. 如果 主构造器是 Worker2(val inName: String),那么 inName 就是 Worker2 的一个 private 的只读属性。
class Worker2(val inName: String) {
  var name = inName
}

// 3. 如果 主构造器是 Worker3(var inName: String),那么 inName 就是 Worker3 的一个 private 的可以读写属性。
class Worker3(var inName: String) {
  var name = inName
}

6.5.2 Bean 属性

示例代码如下:

package com.atguigu.chapter06.constructor

import scala.beans.BeanProperty

object BeanPropertDemo {
  def main(args: Array[String]): Unit = {
    val car = new Car
    car.name = "宝马"
    println(car.name)

    // 使用 @BeanProperty 自动生成 getXxx() 和 setXxx()
    car.setName("奔驰")
    println(car.getName())
  }
}

class Car {
  @BeanProperty var name: String = null
}

6.6 Scala 对象创建的流程分析

6.7 作业03

1、一个数字如果为正数,则它的 signum 为1.0,如果是负数,则 signum 为-1.0,如果为0,则 signum 为0.0。编写一个函数来计算这个值。 示例代码如下:

package com.atguigu.chapter06.exercises

import scala.io.StdIn

/**
  * 1、一个数字如果为正数,则它的 signum 为1,如果是负数,则 signum 为-1,如果为0,则 signum 为0。编写一个函数来计算这个值。
  */
object Exercise01 {
  def main(args: Array[String]): Unit = {
    println("请输入一个数字:")
    val n = StdIn.readDouble()
    println("该数的 signum 为:" + signum(n))
  }

  def signum(n: Double): Double = {
    if (n > 0) {
      1
    } else if (n < 0) {
      -1
    } else {
      0
    }
  }
}

输出结果如下:

请输入一个数字:
0
该数的 signum 为:0.0

请输入一个数字:
5
该数的 signum 为:1.0

请输入一个数字:
-3
该数的 signum 为:-1.0

2、一个空的块表达式{}的值是什么?类型是什么? 示例代码如下:

package com.atguigu.chapter06.exercises

/**
  * 2、一个空的块表达式 {} 的值是什么?类型是什么?
  */
object Exercise02 {
  def main(args: Array[String]): Unit = {
    val t = {}
    println("t=" + t) // t=()
    println(t.isInstanceOf[Unit]) // true
  }
}

3、针对下列 Java 循环编写一个 Scala 版本:

    for (int i=10; i>=0; i–-) {
        System.out.println(i);
    }

示例代码如下:

package com.atguigu.chapter06.exercises

/**
  * 3、针对下列 Java 循环编写一个 Scala 版本:
  * for (int i=10; i>=0; i–-) {
  *   System.out.println(i);
  * }
  */
object Exercise03 {
  def main(args: Array[String]): Unit = {
    // 方式一:
    for (i <- 0 to 10) {
      println("i=" + (10 - i))
    }

    println("----------")

    // 方式二:
    for (i <- 0 to 10 reverse) { // 逆序
      println("i=" + i)
    }

    // 定义一个 List 集合
    val list = List(1, 2, 3)
    println(list) // List(1, 2, 3)
    println(list.reverse) // List(3, 2, 1)
  }
}

4、编写一个过程 countdown(n:Int),打印从 n 到 0 的数字。 示例代码如下:

package com.atguigu.chapter06.exercises

import scala.io.StdIn

/**
  * 4、编写一个过程 countdown(n:Int),打印从 n 到 0 的数字。
  */
object Exercise04 {
  def main(args: Array[String]): Unit = {

    val m=3
    val res1 = (0 to m).reverse
    println(res1) // Range(3, 2, 1, 0)

    // foreach
    // foreach 函数可以接收 (f: Int => U),即接收一个输入参数为 Int,输出参数为 Unit 的函数
    // 下面这句代码的含义是:
    // 1、将 res1 的每个元素依次遍历出来,传递给 println(x)
    // 调用系统的 println 函数
    res1.foreach(println)

    // 调用自定义的 println 函数
    res1.foreach(myPrintln)


    println("----------")
    println("请输入一个数字:")
    val n = StdIn.readInt()
    println("----------")
    countdown(n)
    println("----------")
    countdown2(n)
    println("----------")
    countdown3(n)
  }

  // 自定义一个 println 函数
  def myPrintln(n:Int):Unit = {
    println(n)
  }

  // 方式一:
  def countdown(n: Int): Unit = {
    for (i <- 0 to n) {
      println(n-i)
    }
  }

  // 方式二:
  def countdown2(n: Int): Unit = {
    for (i <- 0 to n reverse) {
      println(i)
    }
  }

  // 方式三:
  def countdown3(n: Int): Unit = {
    // 说明
    // 这里使用到高阶函数的特性
    (0 to n).reverse.foreach(println)
  }
}

5、编写一个 for 循环,计算字符串中所有字母的 Unicode 代码(toLong 方法)的乘积。举例来说,"Hello" 中所有字符串的乘积为 9415087488L。 示例代码如下:

package com.atguigu.chapter06.exercises

import scala.io.StdIn

/**
  * 5、编写一个 for 循环,计算字符串中所有字母的 Unicode 代码(toLong 方法)的乘积。举例来说,"Hello" 中所有字符串的乘积为 9415087488L。
  */
object Exercise05 {
  def main(args: Array[String]): Unit = {
    println("请输入一行字符串:")
    val str = StdIn.readLine()
    println("该字符串中所有字母的 Unicode 代码的乘积为:" + unicode(str))

    unicode2()
  }

  // 方式一:
  def unicode(str: String): Long = {
    var res:Long = 1
    for (i <- 0 to str.length - 1) { // 索引从0开始
      var s = str.charAt(i).toLong
      res *= s
    }
    res
  }

  // 方式二:
  def unicode2() = {
    var res:Long = 1
    for (i <- "Hello") {
      res *= i.toLong
    }
    println("res=" + res)
  }
}

输出结果如下:

请输入一行字符串:
Hello
该字符串中所有字母的 Unicode 代码的乘积为:9415087488
res=9415087488

6、同样是解决前一个练习的问题,请用 StringOps 的 foreach 方式解决。 示例代码如下:

package com.atguigu.chapter06.exercises

import scala.io.StdIn

/**
  * 6、同样是解决前一个练习的问题,请用 StringOps 的 foreach 方式解决。
  */
object Exercise06 {
  def main(args: Array[String]): Unit = {
    var res1: Long = 1
    // 说明
    // 方式一:
    // "Hello".foreach((_) => {res *= _.toLong})
    "Hello".foreach(res1 *= _.toLong)
    println("res1=" + res1)

    // 方式二:
    var res2 = 1L
    "Hello".foreach(myCount)
    println("res1=" + res2)
    def myCount(char: Char): Unit = {
      res2 *= char.toLong
    }
  }
}

输出结果如下:

res1=9415087488
res1=9415087488

7、编写一个函数 product(str: String),计算字符串中所有字母的 Unicode 代码(toLong 方法)的乘积。 示例代码如下:

package com.atguigu.chapter06.exercises

import scala.io.StdIn

/**
  * 7、编写一个函数 product(str: String),计算字符串中所有字母的 Unicode 代码(toLong 方法)的乘积。
  */
object Exercise07 {
  def main(args: Array[String]): Unit = {
    println("请输入一行字符串:")
    val str = StdIn.readLine()
    println("该字符串中所有字母的 Unicode 代码的乘积为:" + product1(str))
    println("该字符串中所有字母的 Unicode 代码的乘积为:" + product2(str))
  }

  // 方式一:
  def product1(str: String): Long = {
    var multi = 1L
    for (i <- 0 to str.length - 1) { // 索引从0开始
      var s = str.charAt(i).toLong
      multi *= s
    }
    multi
  }

  // 方式二:
  def product2(str: String): Long = {
    var multi = 1L
    for (i <- str) {
      multi *= i.toLong
    }
    multi
  }
}

输出结果如下:

请输入一行字符串:
Hello
该字符串中所有字母的 Unicode 代码的乘积为:9415087488
该字符串中所有字母的 Unicode 代码的乘积为:9415087488

8、把7练习中的函数改成递归函数。 示例代码如下:

package com.atguigu.chapter06.exercises

/**
  * 8、把7练习中的函数改成递归函数。
  */
object Exercise08 {
  def main(args: Array[String]): Unit = {
    println("res=" + product("Hello")) // res=9415087488

    println("Hello".take(1)) // H     获取的是该字符串的第一个字符串
    println("Hello".drop(1)) // ello  获取的是该字符串的除第一个字符串之外的剩余字符串
  }

  def product(str: String): Long = {
    if (str.length == 1) return str.charAt(0).toLong
    else str.take(1).charAt(0).toLong * product(str.drop(1))
  }
}

输出结果如下:

res=9415087488
H
ello

9、编写函数计算,其中 n 是整数,使用如下的递归定义:

示例代码如下:

package com.atguigu.chapter06.exercises

/**
  * 9、编写函数计算,其中 n 是整数,使用如下的递归定义:
  */
object Exercise09 {
  def main(args: Array[String]): Unit = {
    println(mi(2.5, 3))
  }

  // 递归的妙用:求 x 的 n 次方,厉害啊!!!
  def mi(x: Double, n: Int): Double = {
    if (n == 0) 1   // x 的 0 次方等于 1
    else if (n > 0) x * mi(x, n - 1)
    else 1 / mi(x, -n)
  }
}

注意:本题可以用于好好理解“递归”的妙用!!!

Java 与 Scala 在函数层面上的不同体现: // 在 Java 中 函数(接收参数)

// 在 Scala 中 集合.函数(函数)

如下图所示: