scala基础

发布于:2025-03-01 ⋅ 阅读:(11) ⋅ 点赞:(0)

scala基础

Scala介绍

Scala是Scalable Language两个单词的缩写,表示可伸缩,是一门完整的计算机编程语言,作者马丁
Scala来自于Java语言
Java语言来自于C语言:跨平台
马丁也是javac的开发作者
Scala是一个完全面向对象的语言
Scala是基于Java语言开发的,所以运行环境也是基于JVM
在这里插入图片描述
Scala是一个面向函数式编程语言,更适合迭代式数据计算

后面的Spark,Fink计算框架底层都是使用Scala进行开发。Kafka也是采用Scala语言开发的。

第一个scala代码

package com.aaa
object HelloWorld01 {

  // 快捷键  main
  def main(args: Array[String]): Unit = {
    System.out.println("Hello World");
    println("Hello Scala");
  }

}

解析:

  • package:包,等同于java中的package
  • object: 声明对象(单例)通常用于封装静态方法和常量,不能实例化。
    • scala是一个完全面向对象的语言,但是java中的静态语法不是面向对象的。
  • def:声明方法的关键字。
  • main:Scala语言程序入口
  • main(…)小括号表示方法参数列表,可以有参数,也可以没有参数,由多个参数使用逗号隔开
    • args: Array[String]:方法的参数
    • java=》String[] arg
    • java语言是一个强类型语言,在编译时就需要明确类型,所以类型很重要。开发时,类型并不是那么重要
    • scala=》arg:String[]
    • scala语言是基于java开发的所以也是强类型语言
    • 作者认为参数名称更重要,开发程序时用的多,为了方便记忆使用,所以将名称放在前面,类型放在后面,为了使用方便,将参数名称和参数类型使用冒号分隔开。
  • Array[String]:表示参数类型
    • scala语言是一个完全面向对象的语言,所以数组也是对象,也有自己的类型 scala语言中括号中的String表示泛型
  • def main:Unit
    • scala语言中方法的声明也符合scala的原则
    • 方法名(参数列表):方法类型
    • scala语言是基于java开发,是一个完全面向对象的语言,方法的返回值也是对象,也应该有相应的类型,但是,没有返回值这个事本身也是一个返回,也需要有类型:Unit(void)
  • =:赋值
    • 将代码块逻辑赋值给一个方法名
  • {} :方法体
    • System.out.println(“Hello World”); :java代码,那么java的代码可以在scala代码中直接使用
    • scala也提供了简化的代码操作:println(“Hello World”);
  • 代码没有分号结尾
    • scala语言中认为一行代码最好完成一段逻辑,不要多个逻辑操作在一行完成,会比较乱,如果一行代码就是一段逻辑,那么就不要使用分号进行区分,不写的话,并不代表没有,而是编译时候,会进行补全,如果需要代码多行才能完成逻辑,需要使用分号进行隔开。

注意事项:
scala语言中没有静态语法,java语言中的静态操作在scala中如何使用呢?
scala采用新的关键字object来模拟静态语法,可以通过对象名称.方法/变量实现静态操作。
如果使用object关键字声明一个对象,那么在编译时也会编译出对应的class文件?
object关键字声明的对象类型和当前编译后的class文件的类型不一样,多了一个$,会生成两个class文件

1.HelloWorld01.class
2.HelloWorld01$.class(单例)
HelloWorld01:单例对象名称,同时也是类名

object和class的区别

在 Scala 中,object 和 class 都是定义类型的关键字,但它们有着不同的用途和特性。

  • class:
    • 定义类:class 用来定义类,是面向对象编程中的一个基本概念。类是对象的模板,可以包含属性(字段)和方法(函数)。
    • 实例化:类是模板,只有通过实例化类才能创建一个对象。使用 new 关键字来创建类的实例。
    • 支持继承和多态:class 支持继承、特质、方法覆盖等面向对象的特性。
class Person(val name: String, val age: Int) {
  def greet(): Unit = {
    println(s"Hello, my name is $name and I am $age years old.")
  }
}

val person = new Person("Alice", 30)
person.greet()  // 输出:Hello, my name is Alice and I am 30 years old.

  • 特点:
    • 可以实例化。
    • 支持构造器(如上例的 name 和 age)。
    • 可以有成员变量和方法。
    • 支持继承。
  • object
  • 定义单例对象:object 用于定义一个 单例对象,它会自动创建一个类的唯一实例。它是 Scala 中的一个特殊结构,通常用于声明常量、工具类、伴生对象等。
  • 不能实例化:object 不能通过 new 创建实例,因为它本身就是一个类的单一实例。
  • 伴生对象:在 Scala 中,object 通常与 class 配对,称为 伴生对象和 伴生类。伴生对象通常用于封装静态方法和常量。
object MySingleton {
  val name = "Singleton"
  def greet(): Unit = {
    println(s"Hello from $name!")
  }
}

MySingleton.greet()  // 输出:Hello from Singleton!

  • 特点:
    • 只有一个实例。
    • 可以定义方法、字段和常量。
    • 常用于定义工具类、工厂方法等。
    • 常与类(class)搭配使用,作为伴生对象(companion object)。

关键区别

特性 class object
定义内容 定义一个类,作为模板 定义一个单例对象,只有一个实例
实例化 可以实例化多个对象 只能有一个实例,不能通过 new 创建
用途 用于创建实例化的对象,支持继承、方法、字段等 常用于工具类、常量、单例模式等
伴生关系 可以与 object 作为伴生类一起使用 与 class 作为伴生对象一起使用,封装类的静态方法
实例化方式 使用 new 创建实例 自动创建单例实例,不能使用 new 创建

伴生类和伴生对象:

在 Scala 中,类和对象常常作为伴生类(companion class)和伴生对象(companion object)成对出现。伴生对象和伴生类可以访问彼此的私有成员,并且它们必须位于同一个源文件中。

class MyClass(val name: String)

object MyClass {
  def apply(name: String): MyClass = new MyClass(name)  // 工厂方法
}

val obj = MyClass("Alice")  // 通过伴生对象的 apply 方法创建实例
println(obj.name)  // 输出:Alice

字节码解析

在java中创建三个类
public class Emp {

// 被final修饰
    public static final int age=30;

    static {
        System.out.println("emp....");
    }
}

public class User {

    public static int age=30;

    static {
        System.out.println("user....");
    }
}

public class Test {
    public static void main(String[] args) {
        System.out.println(User.age);
        System.out.println(Emp.age);
    }
}

运行结果

user....
30
30

Emp中的age被final修饰后,没有打印静态代码块中的内容。
这是为什么呢?

反编译代码

通过javap命令来进行反编译代码

/ 进入idea中target中的class文件夹下,打开Termail控制台。
使用命令
javap -v User
javap -v emp

编译User.class源码后的结果

警告: 二进制文件User包含com.test.User
Classfile /Users/lida/private/project/qmtools/out/production/scala/com/test/User.class
  Last modified 2024-4-24; size 499 bytes
  MD5 checksum 1d09d609093679118c6e95baffbcedf3
  Compiled from "User.java"
public class com.test.User
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #7.#20         // java/lang/Object."<init>":()V
   #2 = Fieldref           #6.#21         // com/test/User.age:I
   #3 = Fieldref           #22.#23        // java/lang/System.out:Ljava/io/PrintStream;
   #4 = String             #24            // user....
   #5 = Methodref          #25.#26        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #6 = Class              #27            // com/test/User
   #7 = Class              #28            // java/lang/Object
   #8 = Utf8               age
   #9 = Utf8               I
  #10 = Utf8               <init>
  #11 = Utf8               ()V
  #12 = Utf8               Code
  #13 = Utf8               LineNumberTable
  #14 = Utf8               LocalVariableTable
  #15 = Utf8               this
  #16 = Utf8               Lcom/test/User;
  #17 = Utf8               <clinit>
  #18 = Utf8               SourceFile
  #19 = Utf8               User.java
  #20 = NameAndType        #10:#11        // "<init>":()V
  #21 = NameAndType        #8:#9          // age:I
  #22 = Class              #29            // java/lang/System
  #23 = NameAndType        #30:#31        // out:Ljava/io/PrintStream;
  #24 = Utf8               user....
  #25 = Class              #32            // java/io/PrintStream
  #26 = NameAndType        #33:#34        // println:(Ljava/lang/String;)V
  #27 = Utf8               com/test/User
  #28 = Utf8               java/lang/Object
  #29 = Utf8               java/lang/System
  #30 = Utf8               out
  #31 = Utf8               Ljava/io/PrintStream;
  #32 = Utf8               java/io/PrintStream
  #33 = Utf8               println
  #34 = Utf8               (Ljava/lang/String;)V
{
  public static int age;
    descriptor: I
    flags: ACC_PUBLIC, ACC_STATIC

  public com.test.User();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 9: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/test/User;

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=2, locals=0, args_size=0
         0: bipush        30
         2: putstatic     #2                  // Field age:I
         5: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
         8: ldc           #4                  // String user....
        10: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        13: return
      LineNumberTable:
        line 11: 0
        line 14: 5
        line 15: 13
}
SourceFile: "User.java"

这个里面可以清晰的看到,age是在静态代码块中进行复制的。

编译Emp.class源码后的结果

警告: 二进制文件Emp包含com.test.Emp
Classfile /Users/lida/private/project/qmtools/out/production/scala/com/test/Emp.class
  Last modified 2024-4-24; size 505 bytes
  MD5 checksum a790a625f63a08555848173ef4d808a6
  Compiled from "Emp.java"
public class com.test.Emp
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#21         // java/lang/Object."<init>":()V
   #2 = Fieldref           #22.#23        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #24            // emp....
   #4 = Methodref          #25.#26        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #27            // com/test/Emp
   #6 = Class              #28            // java/lang/Object
   #7 = Utf8               age
   #8 = Utf8               I
   #9 = Utf8               ConstantValue
  #10 = Integer            30
  #11 = Utf8               <init>
  #12 = Utf8               ()V
  #13 = Utf8               Code
  #14 = Utf8               LineNumberTable
  #15 = Utf8               LocalVariableTable
  #16 = Utf8               this
  #17 = Utf8               Lcom/test/Emp;
  #18 = Utf8               <clinit>
  #19 = Utf8               SourceFile
  #20 = Utf8               Emp.java
  #21 = NameAndType        #11:#12        // "<init>":()V
  #22 = Class              #29            // java/lang/System
  #23 = NameAndType        #30:#31        // out:Ljava/io/PrintStream;
  #24 = Utf8               emp....
  #25 = Class              #32            // java/io/PrintStream
  #26 = NameAndType        #33:#34        // println:(Ljava/lang/String;)V
  #27 = Utf8               com/test/Emp
  #28 = Utf8               java/lang/Object
  #29 = Utf8               java/lang/System
  #30 = Utf8               out
  #31 = Utf8               Ljava/io/PrintStream;
  #32 = Utf8               java/io/PrintStream
  #33 = Utf8               println
  #34 = Utf8               (Ljava/lang/String;)V
{
  public static final int age;
    descriptor: I
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: int 30

  public com.test.Emp();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 9: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/test/Emp;

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=2, locals=0, args_size=0
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String emp....
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 14: 0
        line 15: 8
}
SourceFile: "Emp.java"

从Test.class的代码中就可以看到,获取User中的age时,是获取的静态代码中的数据。但是Emp中的age是常量

注释

object Scala01_Desc {

  def main(args: Array[String]): Unit = {
    // 注释,说明、描述代码的作用
    // 单行注释

    // TODO 注释

    /*
     多行注释
     可以将说明性的内容
     跨行
     */
    /**
     * 文档注释
     */
  }

}

Scala类型推断&至简原则

Scala语言的目的是为了简化代码的开发,让程序更加容易
提出至简原则:能简单就简单,能简洁就简洁,能省则省,这里的能省则省,并不是不写,而是编译器可以帮助我们补充完整
java和scala是一个强类型的语言,那么变量的取值也就确定了。
如果变量的值可以确定,那么变量的类型也就确定了。所以写程序时,类型可以省略,由编译器补充完整。

var name = "lisi";

注意:多态不适用这个至简原则

变量

变量是一种使用方便的占位符,用于饮用计算机内存地址,变量创建后会占用一定的内存空间,基于变量的数据类型,操作系统会进行内存分配并且决定什么将被储存在保留内存中。因此,通过给变量分配不同的数据类型,你可以在这些变量中存储整数、小数或者字母。

  • 声明

变量的类型在变量名之后等号之前声明。
详解:
// scala 中声明变量需要采用特殊的方式
// 1。采用关键字var|val关键字声明
// 2.变量类型放在变量名称后面
// 3。变量名称和类型之间使用冒号分割
// 4。scala中变量必须显示初始化

var name:String="张三";
    val email:String="zhangsan@111.com";

    name="李四";
//    email="lisi@111.com"; 

var和val之间的区别

var 关键字修饰的变量,值可以改变,称之为可变变量
val 关键字修饰的变量,值不可以改变,称之为不可变变量
var和val关键字声明的变量,在编译后是没有区别的,但是在编译时会有约束

可变变量

值可以改变的变量,称之为可变变量,但是变量类型无法发生改变,scala中可变变量使用关键字var进行声明

var username:String="lisi"

不可变变量

值一旦初始化后无法改变的变量,称之为不可变变量。Scala中不可变变量使用关键字val进行声明,类似于Java语言中的final关键字。

val username:String="lisi"

val使用的更加频繁一些。

标识符

基本规则

一般情况下,所谓的标识符就是起名
类名、对象名、方法名、参数名

java中的标识符规则:
1.数字、字符、下划线、美元符号
2.数字不能开头
3.长度没有限制
4.不鞥识关键字或者保留子
5.区分大小写
6.可以转换为unicode编码的文件都可以声明为标识符。

scala中的标识符的基本规则和java中识一致的。
如果非要使用和关键字同名的标识符,可以增加使用反引号

val `private`="123";

特殊符号

scala是一个完全面向函数式编程语言,所以可以声明特殊符号作为标识符。

val * ="zhangsan";

使用特殊符号作为Scala标识符,规则不要记,错了改就完了。如果非要记,常用在代码中的符号不要使用:=,{},[],(),‘’,“”
如果采用特殊符号形成特殊文字,标识特殊的含义,称之为颜文字。
因为特殊符号在编译后会被编译器进行转换,所以可以使用

// 在定义的时候两种方式都可以进行定义
val :-> = "zhangsan";
// 编译后的格式
val 
$colon$minus$greater = "zhangsan";

注意:不适用$开头

数据类型

任意值类型

scala与java有着相同的数据类型,但是又有不一样的地方

  • java的数据类型
    • ava的数据类型有基本数据类型和引用类型
      基本数据类型:byte、short、int、long、float、double、char、boolean
      引用类型:Object、数组、字符串、包装类、集合、POJO对象等
  • scala的数据类型
    • scala中没有基本数据类型
      所有的基本数据类型在scala中都有专门的类型
      任意值类型
      任意引用类型

在这里插入图片描述

Unit的返回值是一个()
StringOps是一个和字符串有关联的数据类型

object Scala01_DataType {

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

  def test():Unit={

  }
}

在这里插入图片描述

任意引用类型

所有的java类型,所有的scala类型,scala类型都是引用类型
引用类型的取值为空时,一般会赋值为null,但是null本身也应该有类型NULL,也表示对象

val name:Null=null;

Nothing
一般常用于异常处理

def test1():Nothing={
    throw new Exception("")
  }

不同类型的数据转换

val i:Int=10;
  val a:String="10";
  println(i)
  println(a)
  // 赋值值类型
  val c:AnyVal=i;
  // 赋值引用类型
  val d:AnyRef=a;
  // 赋值任意类型
  val f:Any=i;
  println(c)
  println(d)
  println(f)

// 不能进行赋值,因为数据类型不正确
//  val j:Int=null;
//  println(j)

自动(隐式)转换

// Byte和Int两个类型之间没有任何的关系,所以不应该能够转换
  // scala底层对类型进行了转换,所以代码上看不出来,所以称之为隐式(自动)转换
  val b1:Byte=10;
  val b2:Int=b1;
  println(b2)

强制类型转换

// 可以进行显式转换
  val b1:Byte=10;
  val b2:Int=b1.toInt;
  println(b2)

循环控制

for循环

var array = 1 to 5;
    var array1 = 1 until 5;
    var array2 = Range(1, 5);
    for (i: Int <- array) {
      println(i)
    }
    for (i: Int <- array1) {
      println(i)
    }
    for (i: Int <- array2) {
      println(i)
    }
    for (i <- array2) {
      println(i)
    }

循环守卫
 var array2 = Range(1, 5);

for (i <- array2 if i!=3) {
      println(i)
    }
设置步长
var array3 = 1 to 5 by 2;
    for (i <- array3) {
      println(i)
    }


 for (i <- Range(1,6,2)) {
      println(i )
    }
双层for循环
for (i <- 1 to 5; j <- 1 to 5) {
      println(i + " " + j)
    }

引入变量
for (i <- 1 to 5) {
      var j = i - 1;
      println(i + " " + j)
    }
    // 简化
    for (i <- 1 to 5; j = i - 1) {
      println(i + " " + j)
    }
for循环的返回值

如果想要将集合中的数据进行处理后返回,可以采用特殊的关键字:yield

var result=for(i<-1 to 5) yield{
      i*2
    }
    println(result);
java中线程对象有yeild方法,在scala中如何调用
Thread.`yield`()

while循环

 while (true){
      println("*")
    }
    
循环中断

scala中没有break,continue

import scala.util.control.Breaks
import scala.util.control.Breaks._


   Breaks.breakable {
      for (i <- 1 to 5) {
        if (i == 3) {
          Breaks.break()
        }
        println("i=" + i)
      }
    }

    breakable {
      for (i <- 1 to 5) {
        if (i == 3) {
          break()
        }
        println("i=" + i)
      }
    }

    println("结束")

流程控制

分支判断

  • 1.if可以独立使用,称为单分支
val age=30;
 if(age==30){
   println("年龄等于30")
 }
  • 2.if可以和else联合使用,称为双分支
val age=3;
      if(age==30){
          println("年龄等于30")
      }else{
          println("年龄不等于30")
      }
  • 3.与elseif,else联合使用,称为多分支
val age=40;
      if(age==30){
          println("年龄等于30")
      }else if(age>30){
          println("年龄大于30")
      }else{
          println("年龄不等于30")
      }

分支控制都是有返回值的

// 这里的返回结果其实就是满足条件后的最后一行代码的执行结果
      // 返回结果是所有结果的通用类型
      val result=if(age==30){
          println("年龄等于30");
          "壮年";
      }

      println(result)

注意:scala中没有三元运算符,也没有switch语法

运算符

关系运算符

  • 双等号

scala 中的双等号可以理解为非空的equals操作。
比较内存地址值 使用eq方法

 val name1=new String("123");
//    val name1=null;
    val name2=new String("123");

    println(name1==name2)
    println(name1.equals(name2))
    // 比较内存地址值
    println(name1 eq name2 )

等号原理说明

在java中使用++运算(使用临时变量将值进行暂存)有歧义,容易理解错误,所以在scala中没有这样的语法,所以采用+=的方式来进行替代。

加法运算

    var r=1.+(1);
    var r1=1+1+2;
    println(r)
    println(r1)

字符串

scala中没有字符串,使用的是java中的字符串,它本身没有字符串
在这里插入图片描述

拼接方式

    // + 拼接
    println("name="+name1);
    // 传值字符串拼接
    printf("name=%s\n",name1);
    // 插值字符串拼接
    println(s"name=${name1}");

    // 多行字符串  |顶格字符串
    // 常用于json和sql字符串
    println(
      """
        |hello
        |scala
        |""".stripMargin);
    println(
      """
        #hello
        #scala
        #""".stripMargin('#'));

输入输出

输入

从控制台获取数据
val age:Int = scala.io.StdIn.readInt()
    println(age);

在控制台输入完成后,需要进行回车

scala获取文件中的数据,需要采用特殊的对象
val source:BufferedSource = Source.fromFile("");
    val iter = source.getLines()
    while (iter.hasNext){
      println(iter.next())
    }
    
    source.close();

输出

scala中没有输出,采用的是java中的输出

val writer = new PrintWriter("")
    writer.println("test")
    writer.println("scala")
    writer.flush()
    
    writer.close()

网络

Scala中进行网络交互,也是采用的java中的

  • server端代码
object Scala03_Net_Server {

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

    val server = new ServerSocket(9999)
    println("服务器启动成功,等待客户端的链接。。。")

    val client = server.accept()

    val stream = client.getInputStream
    val i = stream.read()
    println(i)

    stream.close()
    client.close()
    server.close()

  }
}

  • client端代码
object Scala03_Net_Client {

  def main(array: Array[String]):Unit={
    // 创建服务器
    val socket = new Socket("localhost",9999)
    println("链接成功,向服务器发送数据")
    val stream = socket.getOutputStream
    stream.write(100)
    stream.close()
    println("向服务器发送给数据,100")

    socket.close();

  }
}

序列化

网络中传输的是256之间的ASCALL码,也可以理解为就是传输的数字
在这里插入图片描述

可以在网络中传输字节码,可以将对象转换为字节码,这个叫做序列化。
将字节码转换为对象,称之为反序列化