Scala编程学习之六-面向对象基础

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

//问题
1.因为猫有多个属性[年龄,姓名,颜色],属性的类型不一样.
2.猫有自己的行为(cry, run, cal)
3.需要一种新的数据类型,这种数据类型可以存储不同类型的数据,并且可以对这些数据进行操作和封装.
4.提出 类-对象

6.1.2Scala语言是面向对象的

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

6.1.2Scala语言是面向对象的

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

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

object CatDemo {
  def main(args: Array[String]): Unit = {
    //使用
    /*
    cat.name_$eq("小白");
    cat.age_$eq(3);
    cat.color_$eq("白色");
     */
    val cat = new Cat
    cat.name = "小白" //底层 cat.name_$eq("小白")
    cat.age = 3
    cat.color = "白色" //底层 cat.color_$eq("白色")

    println("小猫的属性=" + cat.name ) //cat.name()
    println("小猫的age=" + cat.age ) //cat.age()
    println("小猫的color=" + cat.color ) //cat.color()
  }
}

//创建一个猫类
/*
//反编译后得到的Scala的Cat类的源码
public class Cat
{
  private String name = "";
  private int age = 0;
  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; }

}
 */
class Cat {
  //属性
  //说明
  //1 var name 而言,在底层会对应生成 private String name
  //2.会生成一对public 方法 (setter和getter) getter: name() , setter name_$eq()
  //3.就可以通过方法来操作name
  //age , color 也是一样

  var name: String = ""
  var age : Int = 0
  var color :String = ""
}

6.1.4如何定义类

基本语法

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

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

属性注意事项和细节说明

1)属性的定义语法同变量,示例:[访问修饰符] var 属性名称 [:类型] = 属性值
2)属性的定义类型可以为任意类型,包含值类型或引用类型[案例演示]
3)Scala中声明一个属性,必须显示的初始化,然后根据初始化数据的类型自动推断,属性类型可以省略(这点和Java不同)。[案例演示]
4)如果赋值为null,则一定要加类型,因为不加类型, 那么该属性的类型就是Null类型.
5)如果在定义属性时,暂时不赋值,也可以使用符号_(下划线),让系统分配默认值, 这时要求属性必须给定数据类型。

6.1.5如何创建对象

基本语法
val | var 对象名 [:类型] = new 类型()

说明
1)如果我们不希望改变对象的引用(即:内存地址), 应该声明为val 性质的,否则声明为var, scala设计者推荐使用val ,因为一般来说,在程序中,我们只是改变对象属性的值,而不是改变对象的引用。
2)scala在声明对象变量时,可以根据创建对象的类型自动推断,所以类型声明可以省略,但当类型和后面new 对象类型有继承关系即多态时,就必须写了
3)代码

object CreateObjectDemo {
  def main(args: Array[String]): Unit = {
    //创建Worker
    val worker = new Worker // worker 类型就是Worker
    val worker2: Emp = new Worker //worker2编译类型就是Emp

  }
}

//父类
class Emp

//子类
class Worker extends Emp

6.1.6类和对象的内存分配机制
看一个思考题
我们定义一个Person类(包括 名字,年龄) ,创建一个对象(ObjectDemo.scala)
在这里插入图片描述
6.2方法
6.2.1案例演示

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

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

6.2.2代码

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

    //对象
    val methodExec = new MethodExec
    methodExec.printRect()

    //面积
    methodExec.width = 9.1
    methodExec.len = 8.2
    println("面积=" + methodExec.area())
  }
  /*
  1)编写类(MethodExec),编程一个方法,方法不需要参数,在方法中打印一个10*8 的矩形,在main方法中调用该方法。【案例演示】
   */

}

class MethodExec {

  var width = 0.0 //setter getter
  var len = 0.0 //setter getter

  def printRect(): Unit = {
    for (i<-1 to 10) {
      for (j<-1 to 8){
        print("*")
      }
      println()
    }
  }
  /*
  修改上一个程序,编写一个方法中,方法不需要参数,计算该矩形的面积,并将其作为方法返回值。在main方法中调用该方法,接收返回的面积值并打印(结果保留小数点2位)。【案例演示】
  分析
  1. 没有参数
  2. 设置width, len的属性
   */
  def area() = {
    (this.len * this.width).formatted("%.2f").toDouble
  }
}

6.3类与对象应用实例
6.3.1小狗案例

1)编写一个Dog类,包含name(String)、age(Int)、weight(Double)属性
2)类中声明一个say方法,返回String类型,方法返回信息中包含所有属性值。
3)在另一个TestDog类中的main方法中,创建Dog对象,并访问say方法和所有
属性,将调用结果打印输出。
4)代码:
class Dog {
  //编写一个Dog类,包含name(String)、age(Int)、weight(Double)属性
  //类中声明一个say方法
  var name:String = ""
  var age :Short = 0
  var weight:Float = 0.0f
  def say(): String = {
    s"小狗的信息如下\t ${name} \t ${age} \t ${weight}"
  }
}


package com.atguigu.chapter06

object TestDog {
  def main(args: Array[String]): Unit = {
    //创建一只狗
    val dog = new Dog
    dog.name = "tom"
    dog.age = 10
    dog.weight = 50
    println(dog.say())
  }
}

6.4构造器
6.4.1看一个需求

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

6.4.2回顾-Java构造器基本语法

[修饰符] 方法名(参数列表){
构造方法体
}
6.4.3基本介绍
构造器(constructor)又叫构造方法,是类的一种特殊的方法,它的主要作用是完成对新对象的初始化。

6.4.4回顾-Java构造器的特点

1)在Java中一个类可以定义多个不同的构造方法,构造方法重载
2)如果程序员没有定义构造方法,系统会自动给类生成一个默认无参构造方法(也叫默认构造器),比如 Person (){}
3)一旦定义了自己的构造方法(构造器),默认的构造方法就覆盖了,就不能再使用默认的无参构造方法,除非显示的定义一下,即: Person(){};

6.4.5回顾-Java构造器的案例
在前面定义的Person类中添加两个构造器:
第一个无参构造器:利用构造器设置所有人的age属性初始值都为18
第二个带name和age两个参数的构造器:使得每次创建Person对象的同时初始化对象的age属性 值和name属性值。

代码:

class Person{
    public String name;
    public int age;
    public String getInfo(){
        return name+"\t"+age;
    }
    public Person(){ //无参构造器
        age = 18;
    }
    public Person(String name,int age){  //两个形参的构造器
        this.name = name;//名字
        this.age = age;//年龄
    }}

6.4.6Scala构造器的介绍

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

6.4.7Scala构造器的基本语法

class 类名(形参列表) { // 主构造器
// 类体
def this(形参列表) { // 辅助构造器
}
def this(形参列表) { //辅助构造器可以有多个…
}
}
//1. 辅助构造器 函数的名称this, 可以有多个,编译器通过不同参数(个数或类型)来区分.
代码演示案例

object ScalaConstructor {
  def main(args: Array[String]): Unit = {
    println("ok~~")
  }
}


//分析
//1.当前这个Person会生成四个构造器
//2. class Person 对应的底层的.class
/*
package com.atguigu.chapter06

object ScalaConstructor {
  def main(args: Array[String]): Unit = {
    println("ok~~")
  }
}


//分析
//1.当前这个Person会生成四个构造器
//2.对应的底层的.class 文件
class Person { // 会生成一个默认的无参构造器
  def this(name: String) {
    //所有的辅助构造器必须调用主构造器 => 一会解释为什么必须调用主构造器
    //==> 为了底层能够实现对父类的初始化
    this
  }

  def this(name: String, age: Int) {
    this
  }

  def this(age: Int, name: String) {
    this
  }
}

class Person2(name: String) {
  //在底层就看到 public Person2(String name) {}

}

*/
class Person { // 会生成一个默认的无参构造器
  def this(name: String) {
    //所有的辅助构造器必须调用主构造器 => 一会解释为什么必须调用主构造器
    //==> 为了底层能够实现对父类的初始化
    this
  }

  def this(name: String, age: Int) {
    this
  }

  def this(age: Int, name: String) {
    this
  }
}

class Person2(name: String) {
  //在底层就看到 public Person2(String name) {}

}

6.4.8Scala构造器的快速入门

创建Person对象的同时初始化对象的age属性值和name属性值,案例演示。

object ConstractorDemo {
  def main(args: Array[String]): Unit = {
    val person = new Person("jack", 18)
    person.show()
  }
}

class Person(pName: String, pAge: Int) {
  var name = pName
  var age = pAge

  def show(): Unit = {
    println("name=" + this.name + " age=" + this.age)
  }
}

6.4.9Scala构造器注意事项和细节
1)Scala构造器作用是完成对新对象的初始化,构造器没有返回值。
2)主构造器的声明直接放置于类名之后

class Person2(name: String) {
  //在底层就看到 public Person2(String name) {}

}

3)主构造器会执行类定义中的所有语句(把类中写的语句放入到主构造器),这里可以体会到Scala的函数式编程和面向对象编程融合在一起,即:构造器也是方法(函数),传递参数和使用方法和前面的函数部分内容没有区别【案例演示+反编译】

class Person(pName: String, pAge: Int) {
  var name = pName
  var age = pAge
  age += 10
  println("age=" + age)
  def show(): Unit = {
    println("name=" + this.name + " age=" + this.age)
  }

  name += " say ok"
  println("name=" + name)
}

4)如果主构造器无参数,小括号可省略,构建对象时调用的构造方法的小括号也可以省略
// //如果主构造器无参数,小括号可省略,构建对象时调用的构造方法的小括号也可以省略
// val dog = new Dog

5)辅助构造器名称为this(这个和Java是不一样的),多个辅助构造器通过不同参数列表进行区分, 在底层就是构造器重载,辅助构造器无论是直接或间接,最终都一定要调用主构造器,执行主构造器的逻辑, 而且调用主构造器语句一定要放在辅助构造器的第一行

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)
  }
}

6)如果想让主构造器变成私有的,可以在()之前加上private,这样用户不能直接通过主构造器来构造对象了
7)辅助构造器的声明不能和主构造器的声 明一致,会发生错误(即构造器名重复)

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

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

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

注意:给某个属性加入@BeanPropetry注解后,会生成getXXX和setXXX的方法
并且对原来底层自动生成类似xxx(),xxx_$eq()方法,没有冲突,二者可以共存。
在这里插入图片描述

对象创建的流程分析
案例

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)

流程分析
加载类的信息(属性信息,方法信息)
在内存中(堆)开辟空间
使用父类的构造器(主和辅助)进行初始
使用主构造器对属性进行初始化 【age:90, naem nul】
使用辅助构造器对属性进行初始化 【 age:20, naem 小倩 】
将开辟的对象的地址赋给 p这个引用

猜你喜欢

转载自blog.csdn.net/smsmtiger/article/details/84726639