第 6 章 面向对象编程 (基础部分)

一、类与对象

1、Scala语言是面向对象的

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

面向对象解决养猫的问题示例代码:

package com.lj.scala.obj

/**
  * @author Administrator
  * @create 2020-03-10 9:26
  */
object ObjTest01 {

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

        val cat = new Cat
        cat.age = 2
        cat.name = "闪电"
        cat.color = "白色"
        printf("他家宠物猫的信息:
        \n年龄:%s,\n名字:%s,\n颜色:%s.\n", cat.age, cat.name, cat.color)

    }

}

class Cat {

    /**
      * 说明:
      * 1. 当我们声明了var name: String时,在底层对应的是private name;
      * 2. 同时会生成两个public方法name() 相当于Java中getter方法,另一个是name_$eq() 
      * 相当于Java中的setter方法。
      */
    
    // 定义/声明三个属性
    var name: String = ""  // 给初始值
    var age: Int = _    // "_" 下划线表示age一个默认值,如果Int默认是0
    var color: String = _   // "_" 表示给color默认值,如果是String,默认是""

}
========================运行结果=============================
他家宠物猫的信息:
年龄:2,
名字:闪电,
颜色:白色.
========================运行结果=============================

2、 类和对象的区别和联系

通过上面的案例可以得出结论

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

3、如何定义类

基本语法

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

定义类的注意事项

1. scala语法中,类并不声明为public,所有这些类都具有公有可见性(即默认就是public);
2. 一个Scala源文件可以包含多个类。

4、属性

介绍

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

注意事项和细节说明

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

属性的默认值
在这里插入图片描述

5、如何创建对象

基本语法

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

注意事项

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

示例代码:

package com.lj.scala.obj

/**
  * @author Administrator
  * @create 2020-03-10 10:10
  */
class ObjTest02 {

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

        val stu01 = new Student
        // 如果此时希望将子类对象交给父类对象的引用,这时就需要写上类型
        val stu02: Person = new Person

        /**
          * 可以发现,stu02只能访问父类对象Person的属性,stu01既能访问自己Student的属性也能访问父类Person的属性
          * 所以说,将子类对象交给父类对象的引用,这时就需要写上类型。
          * 即:当类型和后面new对象类型有继承关系即多态时,就必须写了。
          */

        stu01.ID = 10002
        stu01.age = 13
        stu01.name = "Jack"
        
        stu02.name = "Leo"
        stu02.age = 12
        
    }
}

class Person {

    var name: String = _
    var age: Int = _
    
}

class Student extends Person {
    
    var ID: Int = _

}

6、如何访问属性

基本语法

对象名.属性名; 

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

示例代码:

package com.lj.scala.obj

/**
  * @author Administrator
  * @create 2020-03-10 11:09
  */
object ObjTest03 {

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

        val p1 = new People
        p1.name = "Jack"
        p1.age = 23
        val p2 = p1

        println("p1与p2是否相等:" + (p1 == p2))  // true
        printf("p1的信息:name=%s, age=%s.\n", p1.name, p1.age)
        p1.name = "Tom"
        printf("p1的信息:name=%s, age=%s.\n", p1.name, p1.age)
        printf("p2的信息:name=%s, age=%s.\n", p2.name, p2.age)

    }

}

class People {

    var name: String = _
    var age: Int = _

}
========================运行结果=============================
p1与p2是否相等:true
p1的信息:name=Jack, age=23.
p1的信息:name=Tom, age=23.
p2的信息:name=Tom, age=23.
========================运行结果=============================

类与对象的内存分析示意图
类与对象的内存分析示意图

二、方法

基本说明

Scala中的方法其实就是函数,声明规则请参考函数式编程中的函数声明。

基本语法

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

方法的示例代码:

package com.lj.scala.obj

/**
  * @author Administrator
  * @create 2020-03-10 11:40
  */
object ObjTest04 {

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

        val dog = new Dog
        dog.name = "哮天犬"
        dog.color = "白色"
        dog.sex = "公"
        
        // 调用Dog对象中的方法dogInfo()
        dog.dogInfo()

    }

}

class Dog {

    var name: String = ""
    var color = ""
    var sex: String = _

    def dogInfo(): Unit = {
        println(s"Dog Info: name=$name, color=$color, sex=$sex")
    }

}
========================运行结果=============================
Dog Info: name=哮天犬, color=白色, sex=========================运行结果=============================

三、构造器

说明

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

基本介绍

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

回顾-Java构造器基本语法

	[修饰符] 方法名(参数列表){
		构造方法体
	}

回顾-Java构造器的特点

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

回顾-Java构造器的示例代码:

package com.lj.scala.obj;

/**
 * @author Administrator
 * @create 2020-03-10 12:07
 */
public class ObjTest05 {

    public static void main(String[] args) {

        People p1 = new People("Jack", 12);
        String info = p1.getInfo();
        System.out.println("info:" + info);

    }

}

class People {

    String name;
    int age;

    public People() {}
    public People(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getInfo() {
        return name + " -- " + age;
    }
    
}
========================运行结果=============================
info:Jack -- 12
========================运行结果=============================

1、Scala构造器的介绍

1. 和Java一样,Scala构造对象也需要调用构造方法,并且可以有任意多个构造方法(即scala中构造器
也支持重载);
2. Scala类的构造器包括: 主构造器和辅助构造器;
3. Scala构造器的基本语法如下:
		class 类名(形参列表) {  // 主构造器
			// 类体
			//1. 辅助构造器 函数的名称this, 可以有多个,编译器通过不同参数来区分.
			def  this(形参列表) {  // 辅助构造器
			}
			def  this(形参列表) {  //辅助构造器可以有多个...
			}
		} 

2、Scala构造器的快速入门

示例代码:

package com.lj.scala.obj

/**
  * @author Administrator
  * @create 2020-03-10 12:21
  */
object ObjTest06 {

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

        val p1 = new People("Leo", 13)
        println("p1 info:" + p1.toString)

        val p2 = new People("Tom")
        println("p2 info:" + p2.toString)

    }

}

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

class People(inPutName: String, inPutAge: Int) {

    var name: String = inPutName
    var age: Int = inPutAge

    age += 10

    println("~~~~~~~~~~~~~~~~~~~~~~~~~~")

    // 重写toString,便于输出对象信息。
    override def toString: String = {
        "name=" + this.name + " <--> age=" + this.age
    }

    println("ok~~~~~~")
    println("age=" + age)


    def this(name: String) {
        // 辅助构造器,必须在第一行显示调用主构造器(可以是直接,也可以是间接)
        this("Leo", 11)
        this.name = name   // 重新赋值
    }
}
========================运行结果=============================
~~~~~~~~~~~~~~~~~~~~~~~~~~
ok~~~~~~
age=23
p1 info:name=Leo <--> age=23
~~~~~~~~~~~~~~~~~~~~~~~~~~
ok~~~~~~
age=21
p2 info:name=Tom <--> age=21
========================运行结果=============================

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

1. Scala构造器作用是完成对新对象的初始化,构造器没有返回值;
2. 主构造器的声明直接放置于类名之后;
3. 主构造器会执行类定义中的所有语句,这里可以体会到Scala的函数式编程和面向对象编程融合
在一起,即:构造器也是方法(函数),传递参数和使用方法和前面的函数部分内容没有区别;
4. 如果主构造器无参数,小括号可省略,构建对象时调用的构造方法的小括号也可以省略;
5. 辅助构造器名称为this(这个和Java是不一样的),多个辅助构造器通过不同参数列表进行区分,
在底层就是方法构造器重载;
6. 如果想让主构造器变成私有的,可以在()之前加上private,这样用户只能通过辅助构造器来构造对象了;
7. 辅助构造器的声明不能和主构造器的声明一致,会发生错误(即构造器名重复)

辅助构造器示例代码:

package com.lj.scala.obj

/**
  * @author Administrator
  * @create 2020-03-10 12:47
  */
object ObjTest07 {

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

        val a1 = new Animal("二哈", 2)
        println("动物:name=" + a1.name + " <--> age=" + a1.age)

    }

}

class Animal() {

    var name: String = _
    var age: Int = _
    var color: String = _

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

        this()
        this.name = name
        this.age = age
    }

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

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

}
========================运行结果=============================
动物:name=二哈 <--> age=2
========================运行结果=============================

让主构造器变成私有示例代码:

package com.lj.scala.obj

/**
  * @author Administrator
  * @create 2020-03-10 13:04
  */
object ObjTest08 {

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

        val a2 = new Animal
        
    }

}

// 在()之前加上private,让主构造器变成私有
class Animal02 private() {

    var name: String = _
    var age: Int = _
    var color: String = _

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

		// 说明,由于主构造器变成私有,再通过主构造器调用就会报错
        this()     // error: ambiguous reference to overloader definition
        this.name = name
        this.age = age
    }

    def this() {
        this()
    }

}

四、属性高级

1、构造器参数

	1. Scala类的主构造器的形参未用任何修饰符修饰,那么这个参数是局部变量;
	2. 如果参数使用val关键字声明,那么Scala会将参数作为类的私有的只读属性使用;
	3. 如果参数使用var关键字声明,那么Scala会将参数作为类的成员属性使用,并会提供属性对应
	的xxx()[类似getter]和xxx_$eq()[类似setter]方法,即这时的成员属性是私有的,但是可读写。

示例代码:

package com.lj.scala.obj

/**
  * @author Administrator
  * @create 2020-03-10 13:34
  */
object ObjTest09 {

    def main(args: Array[String]): Unit = {
        
        val w1 = new Worker("Jack")
        w1.name  // 不能访问inPutName
        val w2 = new Worker2("Jack")
        w2.name
        w2.inPutName  // 可以访问inPutName
        // w2.inPutName = "Tom"  // 不可以修改inPutName的值
        val w3 = new Worker3("Jack")
        w3.name
        w3.inPutName // 可以访问inPutName
        w3.inPutName = "Tom"  // 可以修改inPutName的值
        
    }

}

// 此时的主构造器里的变量inPutName就是一个局部变量
class Worker(inPutName: String) {
    var name = inPutName
}

// 此时主构造器里的变量inPutName就是一个private的只读属性
class Worker2(val inPutName: String) {
    var name = inPutName
}

// 此时主构造器里的变量inPutName就是一个private的可以读写属性
class Worker3(var inPutName: String) {
    var name = inPutName
}

2、Bean属性

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

注意:

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

示例代码:

package com.lj.scala.obj

import scala.beans.BeanProperty

/**
  * @author Administrator
  * @create 2020-03-10 13:48
  */
object ObjTest10 {

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

        // 测试加了“@BeanProperty”注解的getter与setter方法
        val c1= new Company
        c1.setName("华为")
        println("name:" + c1.getName)

        // 测试自带的getter[对象.xxx()]与setter[对象.xxx_$eq()]方法
        val c2 = new Company
        c2.name_$eq("阿里巴巴")
        println("name:" + c2.name)

    }

}

class Company() {

    @BeanProperty var name: String = _

}
========================运行结果=============================
name:华为
name:阿里巴巴
========================运行结果=============================

五、对象创建的流程分析

看一个案例

package com.lj.scala.obj

import scala.beans.BeanProperty

/**
  * @author Administrator
  * @create 2020-03-10 13:48
  */
object ObjTest10 {

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

        val p = new Person("Leo", 20)

    }

}

class Person() {
    var age: Int = 13
    var name: String = _
    
    def this(name: String, age: Int) {
        this()
        this.name = name
        this.age = age
    }
}

上面的流程分析

1. 加载类的信息(属性信息,方法信息);
2. 在内存中(堆)开辟空间;
3. 使用父类的构造器(主和辅助)进行初始;
4. 使用主构造器对属性进行初始化 【age:13, name:""】;
5. 使用辅助构造器对属性进行初始化 【 age:20, naem:Jack 】;
6. 将开辟的对象的地址赋给p这个引用。

学习来自:北京尚学堂韩顺平老师—尚硅谷大数据技术之Scala
每天进步一点点,也许某一天你也会变得那么渺小!!!

发布了24 篇原创文章 · 获赞 6 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/XuanAlex/article/details/104770762