Scala学习笔记-入门(1)-Scala简介
Scala简介
Scala是一门以JVM为运行环境,并将面向对象和函数式编程的最佳特性结合在一起的一种静态类型编程语言。
Scala是一门多范式的编程语言,支持面向对象和函数式编程
多范式指多种编程方式,如面向过程、面向对象、泛型、函数式编程等
Scala的源代码会被编译成字节码,然后运行在JVM上,并且可以调用现有的Java类库,实现两种语言的无缝对接
在学习Scala的过程中,应当多加注意Scala与Java的区别。
在编译运行过程中,Java使用javac
和java
命令。由于同样是运行在JVM之上,Scala也有对应的过程。Scala的源代码文件后缀为.scala
,通过scalac
命令可以将源代码编译成字节码文件.class
,之后使用scala
命令进行运行。但是具体细节有所不同,这点在后续的HelloWorld原理对比中会进行说明。
环境搭建
本地环境搭建
- 确保JDK 1.8安装成功
- 下载对应的Scala安装文件,这里选择的是scala2.12版本
- 之后解压到某个路径
- 将对应路径配置为
SCALA_HOME
,并将对应的bin目录配置到path环境变量中
完成上面步骤之后,可以进入命令行输入scala
,之后就可以看到Scala的版本并进入交互式命令行模式,在里面可以尝试简单的指令。通过:quit
命令退出。
IDEA开发环境配置
- 正常创建一个Maven项目
- 默认情况下IDEA不支持Scala的开发,需要安装Scala插件
- 之后需要关联Scala的依赖包,具体路径:
File
->Project Structure
->Platfrom Settings
->Global Libraries
->+
,在其中关联对应路径,应选择SCALA_HOME
- 为项目添加框架依赖,右键项目目录,选择
Add Framework Support
,之后添加其中的Scala即可
如果在Global Libraries
中已经关联了对应的依赖,可以右键选择添加到对应的Module上。
目前我们的项目中已经可以书写Scala代码了。在Maven项目中,我们的Java源代码写在java目录下,为了规范,我们也可以创建对应的源文件目录。在main目录下新建一个文件夹,命名为scala,然后将这个文件夹标记为Sources Root
。原本的java目录可以删除,但是建议保留。因为Scala可以和Java代码无缝连接,后续项目中的Java代码可以写在对应的文件夹中,便于管理。这样就可以进入HelloWorld的编写了。
关联源码
- 首先在官网下载源码包
scala-sources-2.12.15.tar.gz
- 然后将其拷贝到对应
SCALA_HOME/lib
目录下,并解压为scala-source-2.12.15
文件夹 - 之后Attach Sources选择对应文件夹即可关联源码
HelloWorld以及原理对比
HelloWorld
下面是经典HelloWrold程序,Scala源代码如下:
1 |
|
简要说明:
- 类名和文件名保持一致
- args为参数,后面Array为类型,[]中的String为泛型
- :后面为返回值 Unit表示返回值为空
- = {} 存放函数体
;
句尾分号可有可无
可以使用scalac
和scala
进行运行,也可以在IDEA中直接运行。
与Java混合
前面我们说到,Scala代码中可以调用Java类库,所以我们也可以在Scala代码中直接插入Java代码,如下:
1 |
|
可以正确输出结果。
原理分析
观察scalac
命令得到的结果,发现得到了两个字节码文件,分别是HelloWorld.class
和HelloWorld$.class
。
使用
jad
反编译命令(需要额外下载.exe
,为了方便使用可以直接放入$JAVA_HOME$/bin
目录下)
通过反编译软件可以将这两个字节码文件反编译成Java的两个类,结果如下:
HelloWorld.class
:
1 |
|
HelloWorld$.class
:
1 |
|
观察反编译后的代码,可以发现,我们利用scala运行的HelloWorld类实际上是一个入口类。在其中,我们调用了HelloWorld$这个类中的一个对象,来执行对应的方法。而这个对象采用了单例模式构造,整个系统中只有这一个对象。
scala源文件中的HelloWorld对象编译后成为一个类,对象本身编译后是生成的另一个类HelloWorld$
的单例对象HelloWorld$.MODULE$
,称之为单例对象。在HelloWrold$
中有一个main的实例方法,HelloWrold
类的静态方法通过单例对象来调用HelloWorld$
中的main实例方法,完成方法的调用。
这种逻辑实际上是Scala为了做到完全的面向对象而采用的。在Java中存在static关键字,被static关键字修饰的属性通过类名直接调用。但是这并不符合面向对象中一切皆对象的逻辑,如果是一切皆对象,应该所有的情况都是通过对象来调用,但是这里还是存在通过类来调用。因此Scala中改进了这种方式,任何调用都是通过对象来完成的。但是这样的话就需要以某种方式来实现原来通过static实现的功能,于是引入了伴生对象。伴生对象通过单例模式实现,在整个系统中只有一个,即Scala中每个类只有一个伴生对象。static完成的静态功能通过单例对象的实例方法和属性来完成,做到了更加纯粹的面向对象。
我们可以通过一个等价的Java和Scala实现来更加深入的了解这一机制:
实现如下Java代码:
1 |
|
其中static实现的静态属性需要使用类名来调用。同样的功能通过Scala代码来实现:
1 |
|
将生成的两个字节码文件Student.class
和Student$.class
进行反编译得到代码,如下:
Student.class
:
1 |
|
Student$.class
:
1 |
|
可以看到,这里的Student类依然是一个入口类,其中有name和age的定义,用于Student$
中单例对象的生成。利用单例对象的属性school来达到static的功能。
与Java HelloWorld对比
由于Java和Scala的源代码都是编译成字节码文件然后在JVM上运行,那么它们的命令之间是否可以相互调用呢?这里首先构建如下的测试代码:
HelloJava.java
:
1 |
|
HelloScala.scala
:
1 |
|
之后分别通过javac
和scalac
来生成对应字节码文件,可以分别得到HelloJava.class
、HelloScala.class
和HelloScala$.class
- 通过scala调用javac生成的字节码文件
可以直接调用:
1 |
|
- 通过java调用scalac生成的字节码文件
不可以直接调用:
1 |
|
发现报错Class无法找到,原因是没有引入Scala的库,添加classpath既可以只能执行:
(下面的命令如果使用PowerShell无法执行?)
1 |
|