柚子快报邀请码778899分享:大数据技术之

http://www.51969.com/

第五章 函数式编程-基础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.chapter05object Method2Function {  def main(args: Array[String]): Unit = {    // 传统的方式使用方法    // 先创建一个对象    val dog = new Dog    println(dog.sum(10, 20))    // 方法转成函数后使用函数    val f1 = dog.sum _    println("f1=" + f1) // f1=    println("f1=" + f1(50, 60))    // 直接写一个函数并使用函数    // 格式:val f2 = (Int, Int) => {}    val f2 = (n1: Int, n2: Int) => {      n1 + n2 // 函数体    }    println("f2=" + f2) // f2=    println("f2=" + f2(80, 90))  }}class Dog {  // 方法  def sum(n1: Int, n2: Int): Int = {    n1 + n2 // 方法体  }}

输出结果如下:

30f1=f1=110f2=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.chapter05object 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.recursiveimport 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.recursiveimport 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.recursiveimport com.atguigu.chapter05.recursive.Exercise02.fimport 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.fundetailsobject 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.fundetailsobject 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.fundetailsobject 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.fundetailsobject 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.fundetailsobject 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.fundetailsobject 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.fundetailsobject 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.mylazyobject 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.exceptionobject 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.exceptionobject 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.exceptionobject 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.exercisesimport 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.exercisesimport 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 = %d\t", 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.oopobject 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 ,默认是publicclass 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.oopobject 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 = ""}

输出结果如下:

null00.0false

6.1.7 属性的高级部分

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

6.1.8 如何创建对象

示例代码如下:

package com.atguigu.chapter06.oopobject 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.oopobject 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.methodobject 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.constructorobject 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")  }}

输出结果如下:

bbbAaaa

示例代码如下:

package com.atguigu.chapter06.constructorobject 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=scottage=0

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

6.5 属性高级

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

6.5.1 构造器参数

示例代码如下:

package com.atguigu.chapter06.constructorobject 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.constructorimport scala.beans.BeanPropertyobject BeanPropertyDemo {  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.exercisesimport 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.exercisesimport 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.exercisesimport 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 代码的乘积为:9415087488res=9415087488

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

package com.atguigu.chapter06.exercisesimport 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=9415087488res1=9415087488

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

package com.atguigu.chapter06.exercisesimport 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=9415087488Hello

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 中集合.函数(函数)

如下图所示:

柚子快报邀请码778899分享:大数据技术之

http://www.51969.com/

查看原文