Scala高级语法入门 (三) Scala 面向对象编程 多图详解

本文详细介绍了Scala的面向对象编程,包括包、导入、类、属性、访问权限、方法、对象、构造方法、伴生类与伴生对象等基础概念,并深入探讨了继承、抽象、特质、类型检查和转换、枚举类和应用类等高阶主题。适合初学者和希望深入理解Scala面向对象编程的开发者阅读。

??♂??♂ 写在前面

?? 个人主页:csdn春和
?? 推荐专栏:更多专栏尽在主页!
JavaWeb专栏(从入门到实战超详细!!!)
SSM专栏 (更新中…)
?? 本期文章:Scala高级语法入门 (三) Scala 面向对象编程
如果对您有帮助还请三连支持,定会一 一回访!???♂


??本文目录


Scala面向对象编程

Scala 的面向对象思想和 Java 的面向对象思想和概念是一致的。

Scala 中语法和 Java 不同,补充了更多的功能

1、面向对象编程基础

1.1、package包

package 包名

包的三大作用(和java一样)

(1)区分相同名字的类

(2)当类很多时,可以很好的管理类

(3)控制访问范围

// TODO 面向对象编程 包
/* java中的包
*   1、区分相同名字的类
    2、当类很多时,可以很好的管理类
    3、控制访问范围
*/

/*Scala中的包
*   马丁发现package的语法过于简单 但是又不能省略 马丁给与包给更多的功能
*     1、可以让源码文件多次使用过package关键字
*     2、源码路径和包名没有关系
*     3、明确包的作用域可以在package后面加上{}
*     4、子包可以直接访问父包中的内容 无需导包
*     5、scala可以将包当成对象来使用,可以直接声明属性和方法
* */

包的命名规则

只能包含数字、字母、下划线、小圆点.,但不能用数字开头,也不要使用关键字

demo.class.exec1 //错误,因为 class 关键字
demo.12a //错误,数字开头

命名规则 一般是小写字母+小圆点

com.公司名.项目名.业务模块名 

com.itch.video

包说明(包语句)

Scala 有两种包的管理风格,一种方式和 Java 的包管理风格相同,每个源文件一个包(包 名和源文件所在路径不要求必须一致),包名用“.”进行分隔以表示包的层级关系,如 com.zhou.scala。
另一种风格,通过嵌套的风格表示层级关系,如下

package com{
	package zhou{
		package scala{
		} 
    } 
}

第二种风格有以下特点:

(1)一个源文件中可以声明多个 package

(2)子包中的类可以直接访问父包中的内容,而无需导包

包对象

在 Scala 中可以为每个包定义一个同名的包对象,定义在包对象中的成员,作为其对应包下所有 class 和 object 的共享变量,可以被直接访问。

在这里插入图片描述

在这里插入图片描述

1.2、import导入

1)和 Java 一样,可以在顶部使用 import 导入,在这个文件中的所有类都可以使用。

2)局部导入:什么时候使用,什么时候导入。在其作用范围内都可以使用

3)通配符导入:import java.util._

4)给类起别名:import java.util.{ArrayList=>JL}

5)导入相同包的多个类:import java.util.{HashSet, ArrayList}

6)屏蔽类:import java.util.{ArrayList =>,}

7)导入包的绝对路径:new root.java.util.HashMap

Scala中基本的import导入语法和Java完全一致

import java.util.List
import java.util._ // Scala中使用下划线代替Java中的星号

Scala中导入的扩展语法

1、Scala中的import语法可以在任意位置使用

object Scala_Object02_import {
  def main(args: Array[String]): Unit = {
    import java.util.ArrayList
    val list = new util.ArrayList()
  }
}

2、Scala中可以导包,而不是导类

object Scala_Object02_import {
  def main(args: Array[String]): Unit = {
   
    import java.util   // scala中可以只导入包 而不是具体的类
    new util.ArrayList()
  }
}

3、Scala中可以在同一行中导入相同包中的多个类,简化代码

import java.util.{List,ArrayList}

4、Scala中可以屏蔽某个包中的类

import java.util._
import java.sql.{ Date=>_, Array=>_, _ }  // 屏蔽类

5、Scala中可以给类起别名,简化使用

import java.util.{ArrayList=>AList}  // 起别名

val list1 = new AList()

6、Scala中可以使用类的绝对路径而不是相对路径

import _root_.java.util.ArrayList

7、默认情况下,Scala中会导入如下包和对象

// scala默认导入的
import java.lang._
import scala._
import scala.Predef._

1.3、类

面向对象编程中类可以看成一个模板,而对象可以看成是根据模板所创建的具体事物

基本语法

访问权限 class类名 {类主体内容}

class User {
    // 类的主体内容
}
// 对象:new 类名(参数列表)
new User()

Scala扩展语法

Scala中一个源文件中可以声明多个公共类

你知道类的加载顺序吗?

类加载的双亲委派机制

在这里插入图片描述

1.4、属性

属性其实就是类中的变量

object Scala_Object04_field {


  def main(args: Array[String]): Unit = {
    // TODO 面向对象编程 属性
    //  1、所谓的属性其实就是类中的变量
    //  2、编译时编译器会将变量编译为私有属性,同时提供了属性对应的公共方法 (set get)方法

    val test = new Test()
    // 给类的属性赋值,等同于调用对象属性的set方法
    test.name = "lisi"
    // 访问类的属性时,等同于调用对象属性的get方法
    println(test.name)

  }


  class Test{
    // 声明属性
//    private String name = "zs";
//    private final int age = 20;
    var name:String = "zs"

    // val 声明的属性在编译时会给属性添加final关键字,编译器不会提供属性的set方法
    val age:Int = 20

    // scala中变量必须显示的初始化 可以采用特殊符号使用默认初始化
    var email:String = _  // 默认初始化
  }

}

1.5、访问权限

Scala中的访问权限和Java中的访问权限类似,但是又有区别

private : 私有访问权限
private[包名]: 包访问权限
protected : 受保护权限,不能同包
            : 公共访问权限

 // TODO 面向对象编程 访问权限

 /*java中的访问权限
 *   private     本类
 *   缺省 default  本类 本包
 *   protected   本类 本包 子包
 *   public  任意
 * */

 /* Scala中的访问权限
 *   1、private  私有的 本类
 *   2、private[包名] 包私有 
 *   3、protected   受保护的  只能同类和子类使用 没有同包
 *   4、(default) 什么都不写就是公共的 没有public关键字
 * */

你会调用java中的clone方法吗?

在这里插入图片描述

这里我们需要在AA类中重写clone方法

改变clone方法的提供者
画图来表示其中的关系

在这里插入图片描述

public class JavaTest6_clone {
    public static void main(String[] args) throws CloneNotSupportedException {
        AA aa = new AA();

        // 方法的调用者和方法的提供者之间的关系
        // 方法的提供者 : java.lang.Object
        // 方法调用者: com.zhou.scala.test.JavaTest6_clone

        // 点不是调用的意思 点的含义是 "的",有从属关系
        aa.clone();
    }
}

class AA{
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

1.6、方法

Scala中的类的方法其实就是函数,所以声明方式完全一样,但是必须通过使用对象进行调用

def main(args: Array[String]): Unit = {
  val user = new User
  user.sayHi()
}

class User {
  var name: String = _
  val age: Int = 30
  @BeanProperty var address: String = _
  
  
  def sayHi(): Unit ={
    println("hello world!")
  }
}

还记得方法的重写和重载吗? 你真的明白吗?

1、方法的重载

两个参数的名字相同如何区分,看名称(参数列表)

对应的类型没有查找到则会查找父类

2、方法的重写

如何区分父类 子类中相同的方法,需要采用动态绑定机制

什么是动态绑定机制:在调用对象的成员方法过程中将方法和对象的实际内存进行绑定然后调用

public class JavaTest7override {
    public static void main(String[] args) {

        A1 a1 = new A1();
//        System.out.println(a1.sum()); // 20

        B1 b1 = new B1();
//        System.out.println(b1.sum()); // 40

        A1 a2 = new B1();
//        System.out.println(a2.sum()); // 40

        A1 a3 = new B1();
        // 属性是不遵循动态绑定机制的,所以在哪里声明就在哪里使用
        System.out.println(a3.sum()); // 20  b1里面没有sum 找b1的父类有sum方法

    }
}


class A1{
    public int i = 10;
    public int sum(){
        return i + 10;
    }
}
class B1 extends A1{
    public int i = 20;
//    public int sum(){
//        return i + 20;
//    }
}

1.7、对象

Scala中的对象和Java是类似的

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

1.8、构造方法

和Java一样,Scala中构造对象也需要调用类的构造方法来创建。并且一个类中可以有任意多个不相同的构造方法。这些构造方法可以分为2大类:主构造函数辅助构造函数

基本语法:

class 类名(形参列表) { // 主构造器
 // 类体
 def this(形参列表) { // 辅助构造器
 }
 def this(形参列表) { //辅助构造器可以有多个...
 } 
}

(1)辅助构造器,函数的名称 this,可以有多个,编译器通过参数的个数及类型来区分。

(2)辅助构造方法不能直接构建对象,必须直接或者间接调用主构造方法。

(3)构造器调用其他另外的构造器,要求被调用构造器必须提前声明。

  class User() { // 主构造函数
    var username: String = _

    def this(name: String) { // 辅助构造函数,使用this关键字声明
      this() // 辅助构造函数应该直接或间接调用主构造函数
      username = name
    }

    def this(name: String, password: String) {
      this(name) // 构造器调用其他另外的构造器,要求被调用构造器必须提前声明
    }

(1)如果主构造器无参数,小括号可省略,构建对象时调用的构造方法的小括号也可以省略

构造器参数

Scala 类的主构造器函数的形参包括三种类型:未用任何修饰、var 修饰、val 修饰

(1)未用任何修饰符修饰,这个参数就是一个局部变量

(2)var 修饰参数,作为类的成员属性使用,可以修改

(3)val 修饰参数,作为类只读属性使用,不能修改

示例代码:

object Scala_Object05_class {

  // TODO 面向对象编程 构造方法
  private val person = new person("zs", 20, 99)

  // (1)未用任何修饰符修饰,这个参数就是一个局部变量
  // printf(person.name)
  // (2)var 修饰参数,作为类的成员属性使用,可以修改
  person.age = 19
  println(person.age)

  // (3)val 修饰参数,作为类的只读属性使用,不能修改
  //  person.score = 20 
  println(person.score)
  

}

// 主构造函数
class person(name: String, var age: Int, val score: Int) {

}

私有构造器

在java中我们一般用私有构造器创建单例对象

object Scala_Object07_instance {


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

    // 构造方法私有化

    val p = new Person()

  }

  // 在参数列表前加上private关键字 就是私有构造器
  class Person private(){
    def getInstance(): Person ={
      new Person()
    }
  }
}

上述代码会报错:

在这里插入图片描述

1.9、伴生类&伴生对象

在java中我们知道需要 声明一个公共的 静态的返回本类型的方法 用来获取对象

Scala中没有静态语法,但是可以直接使用java中的静态操作

Scala采用了特殊的操作方式代替了静态语法:object

object Person{
  def getInstance(): Person ={
    new Person()
  }
}


// object关键字用于创建对对象 对象的名字就是声明的名字
val person = Person.getInstance()
println(person)

使用object关键字声明的类和对象是有关系的,这个对象等同于伴随着这个类创建时产生的,所以将这个对象称之为伴生对象,这个类称为伴生类

// 伴生类
class Person private(){

}

// 伴生对象
object Person{
  def getInstance(): Person ={
    new Person()
  }
}

伴生对象就是一个对象,可以访问半生类中的所有东西 包括私有的

伴生对象其实就是马丁模拟静态语法所产生的,一般写代码时,将静态语法操作的代码写在伴生对象中,将成员方法或属性写在伴生类中

在这里插入图片描述

伴生对象就是单例的

伴生对象只需要声明即可,无需构建,所以不需要构造参数列表

单例模式存在为一个问题:创建的对象不会被回收,需要显示的回收(设置为null)

如果伴生对象中构建对象的方法名为apply,编译器可以自动识别,所以这个方法名可以省略

在这里插入图片描述

注意区分下面三种情况:

val person1 = new Person()  // 调用类的构造方法
val person2 = Person()      // 调用的是伴生对象的apply方法
val person3 = Person        // 伴生对象个本身

2、面向对象高阶编程

2.1、继承

和Java一样,Scala中的继承也是单继承,且使用extends关键字。子类继承父类的属性和方法

class Person {

}

class User extends Person {

}

构造对象时需要考虑构造方法的执行顺序

(1)子类继承父类的属性和方法

(2)继承的调用顺序:父类构造器->子类构造器

2.2、抽象

抽象类使用abstract关键字声明
抽象方法:只有声明没有实现的方法

object Scala_Object08_abstract {
  def main(args: Array[String]): Unit = {
    // TODO 抽象
    // 所谓的抽象就是不完整  抽象类和抽象方法

    // 抽象类是没有办法被实例化的 需要有子类继承后完成实例化
    val user = new Child()


  }

  // 抽象类
  abstract class User {

    // 抽象方法:只有声明没有实现
    def test(): Unit
  }

  class Child extends User {
    override def test(): Unit = {
      println("hello world")
    }
  }

}

Scala中将一个不完整的类称之为抽象类

abstract class Person {
}

Scala中如果一个方法只有声明而没有实现,那么是抽象方法,因为它不完整。

abstract class Person {
   def test():Unit
}

Scala中如果一个属性只有声明没有初始化,那么是抽象属性,因为它不完整。

abstract class Person {
   var name:String
}

子类如果继承抽象类,必须实现抽象方法或补全抽象属性,否则也必须声明为抽象的,因为依然不完整。

abstract class Person {
   var name:String
}
class User extends Person {
   var name : String = "zhangsan"
}

2.3、特质

Scala将多个类的相同特征从类中剥离出来,形成一个独立的语法结构,称之为“特质”(特征)。这种方式在Java中称之为接口,但是Scala中没有接口的概念。所以scala中没有interface关键字,而是采用特殊的关键字trait来声明特质, 如果一个类符合某一个特征(特质),那么就可以将这个特征(特质)“混入”到类中。这种混入的操作可以在声明类时使用,也可以在创建类对象时动态使用

1、声明类的时候混入特质

基本语法:

trait 特质名称
class 类名 extends 父类(特质1) with 特质2 with特质3

trait Operator {

}
trait DB{

}
class MySQL extends Operator with DB{

}

1、如果一个类只有一个特征,采用extends关键字进行混入

trait Runnable {
  def run(): Unit
}

class Person extends Runnable {
  override def run(): Unit = {
    println("人run")
  }
}

2、如果一个类有多个特征,第一个特征使用extends进行混入,其他采用with进行混入

trait Eat {
  def eat(): Unit
}

trait Runnable {
  def run(): Unit
}

class Person extends Runnable with Eat {
  override def run(): Unit = {
    println("人run")
  }

  override def eat(): Unit = println("人吃饭")
}

3、如果一个类存在父类并同时兼备特征,需要使用extends来继承父类 使用with来混入特征

class Dog extends Object with Runnable with Eat {
  override def run(): Unit = {
    println("dog run...")
  }

  override def eat(): Unit = println("狗吃屎")
}
2、动态混入

这里提一点 OCP开发原则,即不改变源代码的情况下进行功能的扩展

在这里插入图片描述

所以动态混入就是在不改变构造器的情况下混入新的特质,使得对象具有新的功能

动态混入就是在创建对象的时候混入特质 基本语法为:

val user = new User() with [trait]
object Scala_Object09_trait1 {
  def main(args: Array[String]): Unit = {
    // TODO 特征 动态混入

    val user = new User
    user.insertUser()

    // 我们需要增加更改用户的功能
    val user1 = new User() with UpdateUser
    user1.updateUser()



  }

  trait UpdateUser{
    def updateUser(): Unit ={
      println("更改用户!")
    }
  }

  class User{
    def insertUser(): Unit ={
      println("添加用户!")
    }
  }

}

在这里插入图片描述

可以将特质trait理解为接口和抽象类的结合体

3、初始化顺序
object Scala_Object09_trait2 {
  def main(args: Array[String]): Unit = {
    // TODO 特征 初始化顺序

    // 初始化问题 谁先初始化
    // 父类的特质  > 父类 > 特质1 > 特质2 > 当前类

    new User() // p a b u


  }

  trait A{
    println("aaa")
  }
  trait B{
    println("bbb")
  }
  class Person{
    println("ppp")
  }

  class User extends Person with A with B{
    println("uuu")
  }


}
5、功能叠加
object ScalaTrait {
    def main(args: Array[String]): Unit = {
        val mysql: MySQL = new MySQL
        mysql.operData()
    }
}

trait Operate{
    def operData():Unit={
        println("操作数据。。")
    }
}
trait DB extends Operate{
    override def operData(): Unit = {
        print("向数据库中。。")
        super.operData()
    }
}
trait Log extends Operate{

    override def operData(): Unit = {
        super.operData()
    }
}
class MySQL extends DB with Log {

}
6、特质和抽象类的区别

1.优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类。

2.如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数, 而特质不行(有无参构造)

2.4、类型检查和转换

(1)obj.isInstanceOf[T]:判断 obj 是不是 T 类型。

(2)obj.asInstanceOf[T]:将 obj 强转成 T 类型。

(3)classOf 获取对象的类名

class Person{
}
object Person {
 def main(args: Array[String]): Unit = {
 val person = new Person
 //(1)判断对象是否为某个类型的实例
 val bool: Boolean = person.isInstanceOf[Person]
 if ( bool ) {
 //(2)将对象转换为某个类型的实例
 val p1: Person = person.asInstanceOf[Person]
 println(p1)
 }
 //(3)获取类的信息
 val pClass: Class[Person] = classOf[Person]
 println(pClass)
 }
}

字符串真的不可变吗?

字符串是底层char数组的内存地址不可变,它的值可以通过反射进行修改

object Scala_Object10_string {
  def main(args: Array[String]): Unit = {
    // TODO 特征 不可变字符串?
    val s = " a b "

    // 反射
    val stringClass: Class[String] = classOf[String] // 拿到String的类型信息
    val field = stringClass.getDeclaredField("value")  // 拿到属性 封装了char数组
    field.setAccessible(true) // 设置可用 绕过处理机制
    val obj = field.get(s)    // 拿到真正的char数组
    val chars: Array[Char] = obj.asInstanceOf[Array[Char]] // 强转为char数组
    chars(2) = 'M'  // 修改char数组中的值

    println(s)  // 打印字符串s

  }
}

在这里插入图片描述

2.5、枚举类和应用类

1、枚举类
object Scala_Object11_Enum {
  def main(args: Array[String]): Unit = {
    // TODO 枚举类
    println(Color.RED)

  }

}
// 枚举类
object Color extends Enumeration {
  val RED = Value(1, "red")
  val YELLOW = Value(2, "yellow")
  val BLUE = Value(3, "blue")
}
2、应用类
object Scala_Object12_app {
  println("aaa")

  def main(args: Array[String]): Unit = {
    // TODO 应用类
    println("bbb")
  }

  println("ccc")
}

上述代码的打印顺序是什么?

在这里插入图片描述

为了避免不好理解,马丁提出使用应用类 里面的顺序按顺序执行

object Scala_Object12_app extends App {
  println("aaa")

  println("bbb")

  println("ccc")
}

在这里插入图片描述

2.6、Type定义新类型

使用type关键字可以定义新的数据数据类型名称,本质上就是类型的一个别名

object Test {
    def main(args: Array[String]): Unit = {
        type S = String
        var v : S = "abc"
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值