六、Scala从入门到精通一一面对对象编程(初级)

6、面向对象编程

6.1、类与对象

->

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

//问题
猫有三个属性,类型不一样.
如果使用普通的变量就不好管理
使用一种新的数据类型((1) 可以管理多个不同类型的数据 [属性]) (2) 可以对属性进行操作-方法
因此类与对象

6.1.1、Scala语言是面向对象的

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

3、在面向对象的学习过程中可以对比着Java语言学习

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

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 = 10
    cat.color = "白色"
    println("ok···")
    printf("\n小猫的信息如下:%s,%d,%s",cat.name,cat.age,cat.color)
  }
}
// 定义一个类
// 一个class Cat 对应的字节码文件只有一个Cat.class,默认是public
class Cat {
  // 声明3个属性
  // 说明
  // 1.当我们声明了 var name :String 时,在底层对应private name
  // 2.同时会生成 两个public方法 name() <=类似=> getter  name_$et() => setter
  var name:String = "" // 给初始值
  var age:Int = _ // _ 表示给age 一个默认的值,如果是Int,默认就是0
  var color:String = _ // _给 color 默认值,如果时String,默认就是""

}

6.1.3、类与对象的区别和联系

通过上面的案例和讲解我们可以看出:

1、类是抽象的,概念的,代表一类事物,比如人类,猫类…
2、对象是具体的,实际的,代表
一个具体
事物
3、类是对象的模板,对象是类的一个个体,对应一个实例
4、Scala中类和对象的区别和联系 和 Java是一样的。

6.1.4、如何定义类

->基本语法

[修饰符] class 类名 {
类体
}

定义类的注意事项
scala语法中,类并不声明为public,所有这些类都具有公有可见性(即默认就是public),[修饰符在后面再详解].
一个Scala源文件可以包含多个类.

6.1.5、属性

基本介绍

案例演示:

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

属性是类的一个组成部分,一般是值数据类型,也可是引用类型。比如我们前面定义猫类 的 age 就是属性

6.1.6、属性/成员变量

注意事项和细节说明

1)属性的定义语法同变量,示例:[访问修饰符]var 属性名称[:类型] = 属性值

2)属性的定义类型可以为任意类型,包括值类型或引用类型[案例演示]

3)Scala中声明一个属性,必须显示的初始化,然后根据初始化数据的类型自动推断,属性类型可以省略(这点和Java不同)。

4)如果赋值为null则一定要加类型,因为不加类型,那么该属性的类型就是Null类型.

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

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

5)如果在定义属性时,暂时不赋值,也可以使用符号**_(下划线),**让下同分配默认值

6)不同对象的属性是独立,互不影响,一个对象对属性的更改,不影响另外一个。

案列演示+图(Monster)//这点和Java完全一样

def main(args: Array[String]): Unit = {
var worker1 = new Worker
worker1.name = "jack"
var worker2 = new Worker
worker2.name = "tom"
}
class Worker {
  var name = ""
}

6.1.7、属性的高级部分

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

6.1.8、如何创建对象

基本语法

val | var 对象名[:类型] = new 类型 ()

说明:

1) 如果我们不希望改变对象的引用(即:内存地址),应该声明为val性质的,否则声明为var,scala设计者推荐使用val,因为一般来说,在程序中,我们只是改变对象的属性值,而不是改变对象的引用

2)scala在声明对象变量时,可以根据创建对象的类型自动推断,所以类型声明可以省略,但当类型和后面new对象类型有继承关系即多态时,就必须写了

object CreateObj {
  def main(args: Array[String]): Unit = {
    val emp = new Emp // emp 类型就是Emp
    // 如果我们希望将子类对象,交给父类的引用,这时就需要写上类型
    val emp2:Person = new Emp

  }
}
class Person{

}

class Emp extends Person{

}

6.1.9、类和对象的内存分配机制

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

    val p2 = p1
    println(p1 == p2) // true
    p1.name = "tom"
    println(p2.name)  // tom
  }
}

class Person2 {
  var name = ""
  var age: Int = _ // 如果是以_ 的方式给默认值,则属性必须指定类型
}

在这里插入图片描述

6.2、方法

6.2.1、基本说明

Scala中的方法就是函数

6.2.2、基本语法

def 方法名(参数列表)[:返回值类型]={

方法体

}

6.2.3、方法案例演示

object MethodDemo01 {
  def main(args: Array[String]): Unit = {
    // 使用一下
    val dog = new Dog
    println(dog.cal(10,30))
  }
}
class Dog {
  private var sal: Double = _
  var food:String = _

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

6.2.4、方法的调用机制原理

  1. 当我们scala开始执行时,先在栈区开辟一个main栈。main栈是最后被销毁
  2. 当scala程序在执行到一个方法时,总会开一个新的栈。
  3. 每个栈是独立的空间,变量(基本数据类型)是独立的,相互不影响(引用类型除外)
  4. 当方法执行完毕后,该方法开辟的栈就会被jvm机回收。

6.2.5、方法的相关练习题

1、编写类(MethodExec),编程一个方法,方法不需要参数,在方法中打印一个10*8 的矩形,在main方法中调用该方法。

object MethodDemo02 {
  def main(args: Array[String]): Unit = {
    val m = new MethodExec
    m.printRect()
  }
}
class MethodExec {
  def printRect(): Unit = {
    for (i <- 0 until (10)) {
      for (j <- 0 until (8)) {
        print("*")
      }
      println()
    }
  }
}

2、修改上一个程序,编写一个方法中,方法不需要参数,计算该矩形的面积,并将其作为方法返回值。在main方法中调用该方法,接收返回的面积值并打印(结果保留小数点2位)。

m.len = 2.1
m.width = 3.4
println("面积等于="+m.area())
def area() ={
  (this.len * this.width).formatted("%.2f")
}

3、修改上一个程序,编写一个方法,提供m和n两个参数,方法中打印一个m*n的矩形,再编写一个方法算该矩形的面积(可以接收长len,和宽width), 将其作为方法返回值。在main方法中调用该方法,接收返回的面积值并打印。

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

    val m = new MethodExec

    m.len
    m.width
    m.printRect()
    println(m.area())

  }
}

class MethodExec {
  // 属性
  println("请输入矩形的长:")
  var len = StdIn.readInt()
  println("请输入矩形的宽")
  var width = StdIn.readInt()

  def printRect(): Unit = {
    for (i <- 0 until (len)) {
      for (j <- 0 until (width)) {
        print("*")
      }
      println()
    }
  }

  // 计算面积的方法
  def area(): Int = {
    this.len * this.width
  }
}

4、编写方法:判断一个数是奇数odd还是偶数

object MethodDemo03 {
  def main(args: Array[String]): Unit = {
    val o = new ood
    o.n
    o.decide()
  }
}

class ood {
  println("请输入一个数字:")
  var n = StdIn.readInt()

  def decide() = {
    if (n % 2 == 0) {
      println("你输入的数字为偶数")
    } else {
      println("你输入的数字为奇数")
    }
  }
}

5、小小计算器(Calcuator),实现加减乘除四个功能

1、分四个方法完成

2、分一个方法完成

object MethodDemo04 {
  def main(args: Array[String]): Unit = {
    val c = new Calcuator
    c.n1
    c.n2
    c.symbol
    c.cal()
  }
}
class Calcuator {
  println("请输入两个数字:")
  var n1 = StdIn.readDouble()
  var n2 = StdIn.readDouble()
  println("请输入运算符:")
  var symbol = StdIn.readChar()

  def cal()= {
    if (symbol == '+') {
      println(n1 + n2)
    } else if (symbol == '-') {
      println(n1 - n2)
    } else if (symbol == '*') {
      println(n1 * n2)
    } else if (symbol == '/') {
      println(n1 / n2)
    }else{
      println("你的输入有误,请重新输入···")
    }
  }
}

6.3、类与对象应用案例

1、小狗案列

​ 1、编写一个Dog类,包含name(String),age(Int),weight(Double)属性

​ 2、类中声明一个say方法 ,返回String两类型,方法返回信息中包含所有属性值

​ 3、在另一个TestDog类中的main方法中,创建Dog对象,并反问say方法和所有属性,将调用结果打印输出。

object DogCaseTest {
  def main(args: Array[String]): Unit = {
    val dog = new Dog
    dog.name = "tom"
    dog.age = 2
    dog.weight = 6
    println(dog.say())
  }
}
class Dog {
  var name: String = ""
  var age: Int = 0
  var weight: Double = 0.0
  def say(): String = {
    "小狗的信息如下:\n name = " + this.name +
      "\n age =" + this.age +
      "\n weight = " + this.weight
  }
}

2、盒子案例

​ 1、编程创建一个Box类,在其中定义三个变量表示一个立方体的长、宽和高,长宽高可以通过控制台输入

​ 2、定义一个方法获取立方体的体积(volumn)长 * 宽 * 高

​ 3、创建一个对象,打印给定尺寸的立方体的体积

object BoxCaseTest {
  def main(args: Array[String]): Unit = {
    val b  = new Box
    b.len
    b.width
    b.hieght
    b.volumn()
  }
}

class Box{
  println("请输入立方体的长、宽和高:")
  var len = StdIn.readDouble()
  var width = StdIn.readDouble()
  var hieght = StdIn.readDouble()
  def volumn(){
    println("立方体的体积:"+(this.len * this.width * this.hieght))
  }
}

3、景区门票案列

  • 1、一个景区根据有人的年龄收取不通价格的门票

  • 2、请编写游人类,根据年龄段决定能够购买的门票价格并输出

  • 3、规则:年龄>18,门票为20,其他情况免费

  • 4、可以循环从控制台输入名字和年龄,打印门票收费情况,如果名字输入n,则退出程序。

  • object TicketsTestDemo {
      def main(args: Array[String]): Unit = {
        val t = new Tourist
        t.name
        t.age
        t.rule()
    
      }
    }
    
    class Tourist {
      println("请输入你的姓名和年龄:")
      var name = StdIn.readChar()
      var age = StdIn.readInt()
    
      def rule() = {
        if (name == 'n') {
          println("退出程序")
        }
        else if (age > 18) {
          println("%s先生您好,您的年龄为%d,门票为20元".format(name, age))
        } else if (age <= 18) {
          println("%s先生您好,您的年龄为%d,门票免费".format(name, age))
        }
      }
    }
    

6.4、构造器

6.4.1、看一个需求

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

6.4.2、基本介绍

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

6.4.3、Scala构造器的介绍

和Java一样,Scala构造对象也需要调用构造方法,并且可以有任意多个构造方法(即scala中构造器也支持重载)。

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

6.4.4、Scala构造器的基本语法

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

// 类体

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

}

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

}

}

6.4.5、Scala构造器的快速入门

创建Person对象的同时初始化对象的age属性值和name属性值

object ConDemo01 {
  def main(args: Array[String]): Unit = {
    val p = new Person("jack", 20)
    println(p)
  }
}

// 构造器的快速入门
//创建Person对象的同时初始化对象age属性值和name属性值
class Person(inName: String, inAge: Int) {
  var name: String = inName
  var age: Int = inAge

  // 重写toString,便于输出对象的信息
  override def toString: String = {
    "name = "+this.name +"\n"+ "age = "+this.age
  }
}

6.4.6、Scala构造器注意事项和细节

1、Scala构造器作用是完成对新对象的初始化,构造器没有返回值

2、主构造器的声明直接放置于类名之后[反编译]

3、主构造器会执行类定义中的所有语句,这里可以体会到Scala的函数式编程和面对对象编程融合在一起**,即:构造器也是方法(函数),传递参数和使用方法和前面的函数部分内容没有区别[案列演示+反编译](主构造器其实就是将类体里面除了函数部分其他都执行)**

4、如果主构造器五参数,小括号可省略,构建对象时调用的构造方法的小括号也可以省略

5、辅助构造器名称为this(这个和Java是不一样的),多个辅助构造器通过不同参数列表进行区分,在底层就是java的构造器重载

object ConDemo02 {
  def main(args: Array[String]): Unit = {
    val a = new AA("")
    // 输出的顺序是
    // 1、b···  父类
    // 2、AA() 构造器
    // 3、A this(name:String) 辅助构造器
  }
}

class BB() {
  println("b···")
}

class AA() extends BB() {
  println("AA()")
  def this(name: String) {
    this // 调用A的柱构造器
    println("A  this(name:String)")
  }
}
object ConDemo03 {
  def main(args: Array[String]): Unit = {
    val p1 = new Person2()
    println(p1)
  }
}

// 定义了一个Person类
// 运行后Person 有几个构造器  4
class Person2(){
  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)
  }

  override def toString: String = {
    this.name+this.age
  }
}

6、如果想让主构造器变成私有的,可以再()之前加上private,这样用户只能通过辅助构造器来构造对象了

class Person2 private(){}

7、辅助构造器的声明不能和主构造器的声明一致,会发生错误(即构造器名重复)

6.5、属性高级

6.5.1、构造器参数

1、Scala类的主构造器的形参未用任何修饰符修饰,那么这个参数是局部变量。

// 1.如果 主构造器是Worker(inName:String),  那么 inName就是一个局部变量
class Worker(inName:String){
  var name = inName
}

2、如果参数使用val关键字声明,那么Scala会将参数作为类的私有的只读属性使用

// 2.主构造器是Worker(val inName:String),那么 inName就是Worker2的一个private的只读属性
class Worker2(val inName:String){
  var name = inName
}

3、如果参数使用var关键字声明,那么那么Scala会将参数作为类的成员属性使用,并会提供属性对应的xxx()[类似getter]/xxx_$eq()[类似setter]方法,即这时的成员属性是私有的,但是可读写

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

6.5.2、Bean属性

JavaBeans规范了Java的属性是像getxxx()和setxxx()的方法。许多Java工具都依赖这个命名习惯。为了Java的互操作性。将Scala字段加@BeanProperty时,这样会自动**生成规范的setxxx/getxxx的方法。**这时可以使用对象.setxxx()和getxxx() 来调用属性。

注意:给某个属性加入@Beanproperty注解后,会生成getxxx和setxxx的方法,并且对原来底层自动生成类似xxx(),xxx——$eq()方法,没有冲突,二者可以共存

import scala.beans.BeanProperty
object BeanPropertyDemo01 {
  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对象创建的流程分析(面试题)

class Person{
    var age:Short = 90
    var name:String = _
    def this(n:String,a:Int){
        this()
        this.name = n
        this.age  = a
    }
var p : Person = new Person("小倩"20)

1、加载类的信息(属性信息,方法信息)

2、在内存中(堆)开辟空间

3、使用父类的构造器(主和辅助)进行初始

4、使用主构造器对属性进行初始化 【age:90, naem nul】

5、使用辅助构造器对属性进行初始化 【 age:20, naem 小倩 】

6、将开辟的对象的地址赋给 p这个引用

发布了16 篇原创文章 · 获赞 22 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_44258756/article/details/105271384