Scala:完全面向对象的语言
为什么scala号称万物皆对象?
1、 面向对象编程
scala面向对象的思想和java基本上是一致的。
1.1包 -package
- java中的包
-- 包
--1. 声明包(起名):域名反写 + 项目名称 + 模块名 + 程序类型名称
a、什么是域名:www.baidu.com --> baidu.com是域名,www不是域名,所以域名反写为:com.baidu
b、程序类型名称:比如util(工具)、core(核心)、test(测试)等包
c、但是在实际的使用过程中,我们基本上是不太关注包名,所以怎么简单怎么来,导致我们在看别人的代码时就会出现下面这种包名:" c.a.b.s.bean.JavaUser06"
-- 2. 包的作用
2.1 管理类:将声明的类分门别类的管理起来
-- 我们现在通过类名也可以看出来类的作用,所以管理类的作用就没那么重要,如下:
service.UserService => UserService
bean.JavaUser06 => BeanJavaUser06
2.2 区分类 :依靠包名进行区分,也没那么重要了,如Date
java.sql.Date => SQLDate
java.util.Date => UtilDate
2.3 类的访问权限:因为我们不能很好的使用4种管理权限,基本就是public private,所以也就没那么重要了;
2.4 编译类:类的源码程序应该和所在的包名保持一致 --> 这是java的限制
-- 3. 总结:Java语法中,package包语法的作用没有那么强大。所以Scala语言对package语法进行了功能的补充,更加强大。
- Scala中的包
-- 1. package和源码文件的物理路径没有关系
a、scala会按照package的声明编译指定路径的class
b、当前类编译后会在最后的package所在的包中生成class文件
-- 2. package关键字可以多次声明
a、识别时是自上而下的层级关系。
-- 3. package可以使用层级结构
a、在package包名的后面增加大括号,设定作用域范围以及层级关系
b、子包可以直接访问父包中的内容,无需import导入 -- 在同一源码文件中才有效
-- 4. 包也可以当成对象来使用
a、package中为了和java兼容,默认不能声明属性和函数(方法),声明类是可以的。
b、scala提供了一种特殊的对象声明方式: 包对象,"可以将一个包中共通性的方法或属性在包对象中声明。"
c、一个包只能创建一个包对象;
d、在包对象中的方法、属性和类可以被包中所有的类使用。
1.2 导入 - import
- java中的导入
--1. 导类,不是导包
--2. import static 静态导入,导入类的静态属性和静态方法
- java语法中import操作功能比较单一。但是不能舍弃
- Scala语言在java语法基础上进行扩展。
- scala中的导入
-- 1. import 可以声明在任意的位置
import java.sql.Date
val date = new Date()
-- 2. java中默认导入java.lang包中的类
scala中默认导入的类:
2.1 java.lang包中所有的类
2.2 scala包中的类
2.3 Predef (类似于java中静态导入)
-- 3. Scala中的import可以导包
import java.sql
val date = new sql.Date();
-- 4. 导入一个包中所有的类
a、scala中使用"下划线"代替java中的星号
import java.util.*; -- java方式
b、上面这种方式会将util包下所有的类加载到方法区吗?
不会,方法区的空间也是有限的,编译器会根据在类中使用了哪个类,会将导包的类进行转换,加载使用到的类。
import java.util._ -- scala中使用下划线代替java中星号,表示当前包下的所有类都要
-- 5. 可以在一行中导入同一个包的多个类。使用一对{}花括号,将需要导入的类写在里面
import java.util.{ArrayList, HashMap}
new ArrayList()
new HashMap()
-- 6. 使用import关键字将包中的类隐藏掉
import java.sql.{Date=>_, Array=>_, _}
-- 7. 使用import关键字将指定类起别名
方式一:
import java.util.{Date=>UtilDate}
new UtilDate()
方式二:
type UtilDate = java.util.Date
val a : UtilDate = new UtilDate()
-- 8. Scala默认import是按照包的相对路径进行导入的。
a、委派机制 - 没起作用。
b、不想使用相对路径,那么可以采用特殊的路径(root)访问,则从最顶层开始查找
println(new _root_.java.util.HashMap())
-- 9. import 可以导入对象。
a、该对象只能是使用"val"声明的
b、样式如下
val user = new user
import user._
login()
test()
1.3 类 -class
-- scala中 class 与object在编译时的区别
--1. 编译时情况:
1. object 在编译时会产生两个类文件,一个是当前类的文件, 还有一个是单例的类文件
如:User.class, User$.class
2. class 在编译时只会产生当前类的class文件
如:Emp.class
-- 2. 修饰区别
1. object:用于修饰伴随着普通的类所产生的一个单例对象,用于模仿java中的静态语法,object中的方法和属性都可以通过类名直接访问,类似于静态语法;
2. class: 用于修饰普通的类。
-- 3. 汇总:后来我们统一将“相同名称”的class,object声明的类做了区分:
1. 使用class声明的类:伴生类
2. 使用object声明的类:伴生对象
-- scala中 class 与 object 在使用上的区别
--1. 声明的方式:
1. class: 如果需要通过对象访问属性或方法,那么就使用"class"声明
2. object: 如果需要通过类名就可以直接访问属性或方法,那么就使用"object"声明
--2. 构建对象
1. class: 需要使用new的方式,由于构造方法无参的原因,小括号可以省略;
-- val u = new User()
-- val u = new User
2. object: 可以直接使用类名,但是获取的是"伴生对象",不能使用new
-- 3. 调用方法
1. class: 使用class声明的类无法通过类名直接访问方法或属性,必须构建对象
2. object: 使用object声明的类可以通过类名直接访问属性或方法。
-- 多态声明
1. Scala中class可以继承(extends)父类
2. 多态 : 一个对象在不同场合下所表示的不同的状态。
-- 多态使用
1. Scala当省略类型,编译器会自动将构建对象的类型进行推断。
2. 如果需要使用多态操作,那么必须显示声明类型。
-- java中class 与 scala中class的区别
--java中
1. 只能有一个public的类;
2. public声明的类名需要和文件名称保持一致;
3. 一个文件可以有多个类;
-- scala中
1. 类名与文件名可以不一致;
2. class可以声明在很多位置;
3. 一个文件可以有多个公共的类。
- IDEA中,scala不同类型的文件对应不同的图标
- 图标1:表示这个文件中只有伴生对象和伴生类
- 图标2:表示这个文件中只有伴生类
- 图标3:表示文件内容很复杂
- 图标4:表示这个文件中只有伴生对象
- 图标5:表示这个文件中是包对
1.4 属性
-- 属性
--说明
1. 在类中声明属性就等同于在类中声明局部变量的感觉,可以使用var,val声明;
2. 可以通过对象在外部访问;
3. "变量需要显示的初始化"。
-- 属性的初始化:
1. 如果想要像java中类的属性初始化一样,需要采用特殊的符号:"下划线";
2. 如果属性使用"val"来声明。那么初始值"不能使用下划线",需要显示的赋值;
3. 使用val声明的属性也不能改。
-- 属性 Scala类中声明的属性,不仅仅只有属性,还有其他的内容
从如下的源码和反编译可知:
1. 类中声明的属性,都编译为private权限的属性
a、"val"声明的属性:属性会被直接使用final修饰
private final int age = 10;
b、"var"声明的属性:属性只是一个普通的私用变量
private String name;
2. 使用var声明的属性:"提供属性set和get方法"
a、有类似于java中bean对象的get方法,用于返回属性的值
public String name() { return this.name }
b、有类似于java中bean对象的set方法, 用于设定属性的值
public void name_$eq(String x$1) { this.name = x$1; }
3. 使用val声明的属性:"只提供属性get方法",不可变的属性
a、只有类似于java中bean对象的get方法,用于返回属性的值
public int age() { return this.age; }
4. 在使用属性时:
a、因为属性为私有的,所以给属性赋值,其实等同于调用属性的set方法
person.name="zhangsan" --反编译--> person.name_$eq("zhangsan");
b、因为属性为私有的,所以获取属性值,其实等同于调用属性的get方法
val name = person.name --反编译--> String name = person.name();
- 源码
object Scala_Variable {
def main(args: Array[String]): Unit = {
val person = new Person
}
class Person {
val age = 10
var name: String = _
}
}
- 反编译代码
public final class Scala_Variable{
public static void main(String[] paramArrayOfString) { Scala_Variable$.MODULE$.main(paramArrayOfString);
}
public static class Person
{
private String name;
private final int age = 10;
public int age() { return this.age; }
public String name() { return this.name; }
public void name_$eq(String x$1) { this.name = x$1; }
}
}
-- scala 与java的bean的结合
--java Bean规范:
要求属性的set/get方法起名必须以set,get开头,原因是在框架中,会动态获取属性, 反射调用属性的get方法获取属性值。
--Scala编译生成属性的set,get方法并没有遵循bean规范,这样在很多框架中无法使用。
如果想要scala中的属性符合bean规范,可以采用特殊的注解:"@BeanProperty"
//源码:
@BeanProperty
var name: String = _
//反编译:发现生成满足JavaBean的set和get方法,但方法体内还是调用未加注解前类似java的set和get方法
public String getName() { return name(); }
public void setName(String x$1) { name_$eq(x$1); }
1.5 权限修饰符(重点)
- java中的四种权限符
1. private : 私有权限, "同类"
2. (default) : 包权限, "同类,同包"
3. protected : 受保护权限, "同类,同包,子类"
4. public : 公共权限, "任意的地方"
-- 但是你真的了解这四种权限吗?
1. 声明一个类,那么这个类直接或间接继承于Object类,同时继承object类的方法。
object类的方法权限有:public 、 private 、 protected 三种。
2. 什么是访问权限?
权力与限制
3. 确认是否具有访问权限,屡清楚这两个的关系:
a、方法的提供者
b、方法的调用者
4. user.clone(); --> 点不是调用关系,而是从属关系
- 案例1:
package com.atguigu;
public class java_object {
public static void main(String[] args) {
User05 user = new User05();
user.clone();
}
}
class User05 {
private String name ;
}
/*
深层解析:
1. user.clone(); 是java_object类在main方法中调用了user对象的clone()方法
2. a、方法的提供者: 因为Usero5未重写Clone()方法,所以该方法来自于父类object,java.lang.Object
b、方法的调用者: com.atguigu.java_object
3. clone()权限:protected,所以是同类、同包、子类可以调用
4. com.atguigu.java_object 与 java.lang.Object 不是子父类的关系
*/
"如何理解不是子父类的关系呢?"
1. 每个类都会将其父类所有的属性和方法加载到自己的类中;
2. 现在com.atguigu.java_object访问User05中的父类(object),那他们两个就不是父子类的关系。
3. 是java_object类在main方法中调用了user对象的clone()方法 //这是关键

- scala的权限
Scala : 4种访问权限
private : 私有权限 "同类"
private[包名] : 包私有权限,"同类,同包"
protected : 受保护权限,"同类,子类"
(default) : 公共权限, "任意的地方", scala中没有public关键字
--1. private : 同类
a、如果属性声明为private,那么编译器生成set,get方法时,也会使用private进行修饰。
b、使用@BeanProperty注解后,属性不能声明为private
--2. private[包名] :同类,同包
a、中括号中的包名可以向上使用
b、必须在当前包的作用域范围内
--3. protected : 同类,子类
--4. (default) : 公共的
Scala中的源码文件中可以声明多个公共类
1.6 方法
-- 方法: 其实就是类中声明的函数
1. 默认方法
a、java.lang.Object的方法
user.toString
user.hashCode()
b、scala中提供的方法
user.asInstanceOf[User]
user.isInstanceOf[User]
获取对象的类型信息(方法区内存)
val clazz: Class[_ <: User] = user.getClass
val clazz: Class[User] = classOf[User]
c、Predef中的方法
2. 自定义方法
-- 方法 - apply(应用)
1. 作用:apply主要作用用于构建对象;
2. 使用场景:当类的构造方法为私有时,使用new"等同于调用对象的构造方法构建对象"的方式就行不通,那么就可以使用apply的方式;
3. 实现原理:一般用于object伴生对象中构建对象,伴生对象可以访问伴生类的"私有"属性和方法;
4. 细节:
a、Scala会自动识别apply方法,所以调用伴生对象的apply方法时,apply方法名可以省略;
b、如果不使用new来构建对象,那么等同于调用伴生对象的apply方法;
c、如果将伴生对象不使用小括号,那么等同于将伴生对象赋值给变量,而不是调用apply方法;
d、apply方法如果想要被编译器自动识别,那么不能省略小括号;
e、apply方法主要用于构建对象,但是这个构建对象不一定是当前类的对象;
f、apply方法可以重载。
-- 方法的重载
1. 概念: 多个方法名称相同,但是参数列表( 参数个数,参数类型,参数顺序 )不相同的方法之间构成重载
2. 应用
数值类型,在重载的方法中会提升精度。
引用类型,在重载的方法如果找不到对应的类型。会从类树往上查找
-- 方法的重写x`
1. 方法的重写一定要存在父子类。
2. 子类重写父类的方法,子类重写父类相同方法的逻辑
3. 子类重写方法与父类的方法要求:方法名一致,参数列表保持一致,异常范围"子类抛出的异常不能比父类大",访问权限"子类的访问权限不能比父类低"
如下内容,其实就是虚方法调用的原则,对于"成员方法"来说:"编译看左边,运行看右边,但是对于属性来说,运行和编译都看左边。"
4. 既然父类和子类有相同的方法,形成了方法的重写。
5. 那么在调用时,无法确定到底执行哪一个方法,那么需要遵循 动态绑定机制
6. 动态绑定机制 :程序"执行"过程,如果调用了对象的"成员""方法"时, 会将方法和对象的实际内存进行绑定,然后调用。
7. 动态绑定机制和属性无关
//方法的重载
package com.atguigu.Scala_chapter01.scala_chapter06_add
/**
* @Description
**
* @author lianzhipeng
* @create 2020-05-26 0:10:07
*/
object Scala_Method1 {
def main(args: Array[String]): Unit = {
val user = test.apply()
val b : Byte = 10
user.Method(b)
}
}
object test{
def apply(): test = new test()
}
class test {
// def Method ( b : Byte) ={
// println("bbbb")
// }
def Method ( b : Char) ={
println("CCCC")
}
def Method ( b : Int) ={
println("iiii")
}
// def Method ( b : Short) ={
// println("SSSS")
// }
}
package com.atguigu;
/**
* @author lianzhipeng
* @Description
* @create 2020-05-26 0:29:52
*/
public class Java_Method {
public static void main(String[] args) {
Student student = new Student();
//情况1:
System.out.println(student.sum());//40
//情况2:
Person student1 = new Student();
System.out.println(student1.sum());//40
int i = student1.i;
System.out.println(i); // 10
//情况3:注释子类重写的方法,再执行情况2的代码,结果为20
}
}
class Person {
int i = 10 ;
public int sum(){
return i + 10 ;
}
}
class Student extends Person {
int i = 20 ;
// public int sum(){
// return i + 20 ;
// }
}
1.7 构造方法
-- 构造方法
1. 使用new关键字创建的对象其实等同于调用类的构造方法。
2. Scala是一个完全面向对象的语言。
3. Scala还是一个完全面向函数的语言。
4. Scala中类其实也是一个函数,类名其实就是函数名,"类名后面可以增加括号",表示函数参数列表
5. 这个类名所代表的函数其实就是构造方法,构造函数
6. 构造方法执行时,会完成类的主体内容的初始化。
7. 如果在类名的后面增加private关键字,表示主构造方法私有化,无法在外部使用
-- Scala构造方法只有一个吗?
--不是。scala中提供了2种不同类型的构造方法
1. 主构造方法:在类名后加一对小括号的构造方法,可以完成类的初始化
2. 辅助构造方法:为了完成类初始化的辅助功能而提供的构造方法
1.声明方式为:"def this() = {}"
2.在使用辅助构造方法时,必须直接或间接地调用主构造方法
3.辅助构造方法也存在重载的概念
4.辅助构造方法间接调用主构造函数时,"需要保证调用的其他辅助构造函数已经声明过"。
package com.atguigu.Scala_chapter01.scala_chapter06_add
/**
* @Description
**
* @author lianzhipeng
* @create 2020-05-26 1:04:52
*/
object Scala_Construct {
def main(args: Array[String]): Unit = {
val person = new Person ("scala")
val person1 = new Person()
val person2 = new Person("scala",10)
}
}
class Person (name :String) {
def this() = {
this("scala")
}
def this (name: String,age:Int) ={
this()
}
}
-- 构造方法之参数
1. 给构造方法增加参数的目的是什么?
从外部将数据传递到对象的属性中
// java : public User( String name ) { this.username = name }
2. Scala中一般构造方法的参数用于属性的初始化,所以为了减少数据的冗余
可以使用关键字var,val将构造参数当成类的属性来用。
"var可读可写,val可读不可写"
3. 辅助构造方法的参数不能作为属性
package com.atguigu.Scala_chapter01.scala_chapter06_add
/**
* @Description
* *
* @author lianzhipeng
* @create 2020-05-26 1:04:52
*/
object Scala_Construct {
def main(args: Array[String]): Unit = {
// val person = new Person("scala")
// println(person.age)
// val person1 = new Person()
val person2 = new emp("scala", 10)
println(person2.age) //10
val emp = new emp()
println(emp.name) //java
println(emp.age) //20
val emp1 = new emp("hadoop")
println(emp1.name) //hadoop
}
}
class emp(val name: String, var age: Int) {
def this() = {
this("java", 20)
}
def this(name: String) = {
this(name, 30)
}
}
-- 如果父类有构造方法,怎么办?
构建子类对象时,应该首先构建父类对象,如果父类的构造方法有参
那么子类在构建父类对象也应该传递参数。
-- Scala中如何向父类的构造方法中传参?
在父类名称的后面加括号就可以传参了
package com.atguigu.Scala_chapter01.scala_chapter06_add
/**
* @Description
* *
* @author lianzhipeng
* @create 2020-05-26 1:04:52
*/
object Scala_Construct {
def main(args: Array[String]): Unit = {
val worker = new Worker("java",30)
println(worker.age)
println(worker.str)
}
}
class Creature ( var name : String ,val age : Int){
}
//方式1
//class Worker extends Creature ("java",20){
//
//}
//方式2
class Worker (var str :String , var num :Int) extends Creature (str , num){
}
1.8 创建对象的方法
-- 方式1: new
-- 方式2: apply
-- 方式3: classof
-- 方式4: clone 【这种方式的类需要实现cloneable接口】
-- 方式5: 反序列化
- 代码实现
package com.atguigu.Scala_chapter01.scala_review2
/**
* @Description
**
* @author lianzhipeng
* @create 2020-05-26 18:13:10
*/
object Scala1_CreateObject extends Cloneable{
def main(args: Array[String]): Unit = {
//创建对象的方法
//方式1 :new
val user = new User1
println(user)
//方式2 : apply
val user1 = User1()
println(user1)
//方式3 : classof
val uclass: Class[User1] = classOf[User1]
val user2: User1 = uclass.newInstance()
println(user2)
//方式4 :clone
val value = clone()
println(value)
//方式5 : 反序列化
}
}
class User1{
val age = 10
}
object User1 {
def apply(): User1 = new User1()
}
1.9 创建单例模式
总结:声明在object中的属性和方法类似java中的静态方法和静态属性,类加载的时候就会被加载。
步骤:
-- java中懒汉式
第一步:私有化构造器
第二步:创建一个私有化的静态属性,并new对象
第三步:创建一个公共的静态方法,返回静态属性
-- scala中
object类中{}的内容只会在初始化的时候加载一次,所以将构建的对象语句放在{}中,并声明为val.
object Scala2_SingObject {
def main(args: Array[String]): Unit = {
//调用获取对象的方法,获取两个对象
val singObject: User2 = User2.singObject
val singObject1: User2 = User2.singObject
//判断这两个对象的地址值是否相等
println(singObject eq singObject1) //true
}
}
class User2 {
}
object User2{
//创建一个单例对象,并设置为不可变的
val user = new User2
//创建一个方法,供外部进行调用,返回一个单例对象
def singObject ={
user
}
}
1.10 抽象类
-- 抽象类、抽象方法、抽象属性
--1. 抽象的理解:抽象的对象一般理解为不完整的对象
抽象类 : 不完整的类
抽象方法 :不完整的方法
只有声明而没有实现的方法
无需使用abstract关键字修饰
抽象属性:不完整的属性,只声明而没有初始化的属性
--2. 抽象类、抽象方法、抽象属性之间的关系
抽象类可以没有抽象方法和抽象属性,有抽象方法或抽象属性的类一定是抽象类
--3. 抽象类的说明
a、抽象类无法直接构造对象,可通过子类构造对象
b、抽象类使用"abstract"修饰。
c、抽象类中可以有完整的方法和完整的属性。
--4. 抽象类与其子类
a、子类只有实现抽象类中的所有抽象方法和抽象属性,才能实例化,否则还是抽象类。
--5. 实现抽象方式的说明
a、如果子类重写父类的"完整方法",需要显示增加override关键字来修饰
b、如果子类重写父类的"抽象方法",需要直接补充完整即可, 或采用override关键字修饰
--6. 实现抽象属性的说明
a、子类重写父类的"完整属性",那么需要增加override关键字修饰,"被重写的属性必须是val声明的"
b、子类重写父类的"抽象属性",那么需要将属性补充完整即可
关于抽象属性的说明:
a、抽象属性在编译时,不会产生类的属性,而是产生属性的set,get方法,并且方法为抽象的。
b、重写抽象属性,那么等同于普通属性的声明:属性,set,get方法。
"问题:为什么子类重写父类完整的属性只能是val,不能是var"
-- 回答:如果采用var声明的属性可以被重写,会产生歧义
解析: 1. Scala中类的属性在编译时,会自动生成私有属性和对应的set,get方法。
2. 详情见下图分析
1.11 特质
1.11.1java中接口和抽象类说明
-- java中:
-- 抽象类:将子类共通的内容给抽取出来,放置在父类中
a、父类不能直接构建对象,必须通过子类来创建
b、抽象类一般在"父子继承"和"方法重写"的时候用的比较多。
-- 接口 :和类不一样,不是一个体系,所以可以多继承
a、将接口理解为规范和规则。如果一个类符合指定的规则,就应该实现接口
1.11.2 特质基本介绍
1. Scala中没有接口的概念,也就没有interface关键字。
2. Scala可以将"多个类中相同的特征"从类里剥离出来,形成特殊的语法结构,称之为“特质”
3. 如果一个类,符合这个特征,那么就可以将这个特征(特质)混入到类中
1.11.3 特质的使用方式
1. 使用"trait"声明;
2. 特质中可以声明抽象方法、抽象属性、以及完整的方法和属性;
3. 特质既可以看做java中的接口,也可以看做java中的抽象类;
4. 如果一个类符合特质,那么可以将特质“混入”到类中, 采用extends关键字,如果这个类将抽象部分全部补全,那么可以实例化,否则属于抽象类。
-- 总结:
1. "trait"关键字 可以声明特质
2. 将trait理解为interface :特质之间可以多继承
3. 将trait理解为抽象类 : 特质也可以继承其他类,也可以被其他类继承,并采用with混入其他的特质
4. 子类继承特质,如果重写特质中的具体方法,需要使用override
5. java中所有的接口在Scala中都当成特质来使用
// 声明特质
trait Operate {
// 抽象方法 - 不完整方法
def oper():Unit
}
class MySQL extends Operate {
def oper():Unit = {
println("执行mysql数据操作...")
}
}
特质的应用:动态扩展功能,遵循 OCP 开发原则
1. 特质中不仅仅有抽象方法,还可以有具体方法
2. 如果对象声明后想要扩展功能,怎么办?
3. 特质 (混入with)
object Scala4_trait {
def main(args: Array[String]): Unit = {
//构建对象,并使用with “混入”特质2,则拥有特质2所有的属性和方法,如果特质2中也还有抽象方法,那么也需要进行实现
val user = new User4() with MySQL
user.select
}
}
//特质1:
trait Operator {
//抽象方法
def test () : Unit
}
//特质2:
trait MySQL {
//完整方法
def select = {
println("执行mysql的数据库操作")
}
}
//类“混入”特质1,并实现了特质1中的抽象方法
class User4 extends Operator {
override def test(): Unit = {
println("zzzz")
}
}
1.11.4 特质的初始化顺序
object Scala5_trait {
def main(args: Array[String]): Unit = {
new SubUser35()
// aaaaa
// bbbbb
// ddddd ==> parent class
// ccccc ==> trait
// eeeee ==> child class
}
}
trait Parent35 {
println("aaaaa")
}
trait Test35 extends Parent35 {
println("bbbbb")
}
trait SubTest35 extends Parent35 {
println("ccccc")
}
class User35 extends Test35 {
println("ddddd")
}
class SubUser35 extends User35 with SubTest35{
println("eeeee")
}
1.11.5 特质功能执行的顺序
1. 类混入多个特质的时候,功能的执行顺序"从右向左"
2. 特质中的super其实有特殊的含义,表示的"不是父特质,而是上级特质"。
3. 如果想要改变执行顺序,需要指定特质的类型
object Scala5_trait {
def main(args: Array[String]): Unit = {
new MySQL37().operData() //向日志中向数据库中操作数据
}
}
trait Operate37 {
def operData(): Unit = {
println("操作数据")
}
}
trait DB37 extends Operate37 {
override def operData(): Unit = {
print("向数据库中")
super.operData()
}
}
trait Log37 extends Operate37 {
override def operData(): Unit = {
print("向日志中")
//super[Operate37].operData()
super.operData()
}
}
class MySQL37 extends DB37 with Log37 {
}

1.12 拓展
- 枚举类
object Scala7_Trait {
def main(args: Array[String]): Unit = {
println(User7.RED) //red
println(User7.YELLOW) //yellow
println(User7.BLUE) //blue
println(User7.BLUE.id) //3
}
}
object User7 extends Enumeration {
val RED = Value(1, "red")
val YELLOW = Value(2, "yellow")
val BLUE = Value(3, "blue")
}
- 应用类
//在没有main方法中,通过继承App特质,使程序也可以执行
//在APP特质中有main方法,因为是继承了app特质,所以当前类则具有了main主方法
object Scala6_app extends App {
println("xxxx")
println("yyyy")
}