Scala:完全面向对象的语言
1、 面向对象编程1.1包 -package1.2 导入 - import1.3 类 -class1.4 属性1.5 权限修饰符(重点)1.6 方法1.7 构造方法1.8 创建对象的方法1.9 创建单例模式1.10 抽象类1.11 特质1.11.1java中接口和抽象类说明1.11.2 特质基本介绍1.11.3 特质的使用方式1.11.4 特质的初始化顺序1.11.5 特质功能执行的顺序
1.12 拓展
为什么scala号称万物皆对象?
1、 面向对象编程
scala面向对象的思想和java基本上是一致的。
1.1包 -package
java中的包
a、什么是域名:www
.baidu
.com
b、程序类型名称:比如util
(工具
)、core
(核心
)、test
(测试
)等包
c、但是在实际的使用过程中,我们基本上是不太关注包名,所以怎么简单怎么来,导致我们在看别人的代码时就会出现下面这种包名:
" c.a.b.s.bean.JavaUser06"
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 编译类:类的源码程序应该和所在的包名保持一致
Scala中的包
a、scala会按照package的声明编译指定路径的class
b、当前类编译后会在最后的package所在的包中生成class文件
a、识别时是自上而下的层级关系。
a、在package包名的后面增加大括号,设定作用域范围以及层级关系
b、子包可以直接访问父包中的内容,无需
import导入
a、package中为了和java兼容,默认不能声明属性和函数(方法),声明类是可以的。
b、scala提供了一种特殊的对象声明方式: 包对象,
"可以将一个包中共通性的方法或属性在包对象中声明。"
c、一个包只能创建一个包对象;
d、在包对象中的方法、属性和类可以被包中所有的类使用。
1.2 导入 - import
java中的导入
java语法中import操作功能比较单一。但是不能舍弃Scala语言在java语法基础上进行扩展。
scala中的导入
import java
.sql.Date
val
date = new
Date()
scala中默认导入的类:
2.1 java
.lang包中所有的类
2.2 scala包中的类
2.3 Predef
(类似于java中静态导入
)
import java
.sql
val
date = new
sql.Date();
a、scala中使用
"下划线"代替java中的星号
import java
.util
.*;
b、上面这种方式会将util包下所有的类加载到方法区吗?
不会,方法区的空间也是有限的,编译器会根据在类中使用了哪个类,会将导包的类进行转换,加载使用到的类。
import java
.util
._
import java
.util
.{ArrayList
, HashMap}
new ArrayList
()
new HashMap
()
import java
.sql.{
Date=>_
, Array
=>_
, _}
方式一:
import java
.util
.{
Date=>UtilDate}
new UtilDate
()
方式二:
type UtilDate
= java
.util
.Date
val a : UtilDate
= new UtilDate
()
a、委派机制
- 没起作用。
b、不想使用相对路径,那么可以采用特殊的路径(root)访问,则从最顶层开始查找
println
(new _root_
.java
.util
.HashMap
())
a、该对象只能是使用
"val"声明的
b、样式如下
val
user = new
user
import user._
login
()
test
()
1.3 类 -class
1. object 在编译时会产生两个类文件,一个是当前类的文件
, 还有一个是单例的类文件
如:
User.class
, User$
.class
2. class 在编译时只会产生当前类的class文件
如:Emp
.class
1. object:用于修饰伴随着普通的类所产生的一个单例对象,用于模仿java中的静态语法,object中的方法和属性都可以通过类名直接访问,类似于静态语法;
2. class: 用于修饰普通的类。
1. 使用class声明的类:伴生类
2. 使用object声明的类:伴生对象
1. class: 如果需要通过对象访问属性或方法,那么就使用
"class"声明
2. object: 如果需要通过类名就可以直接访问属性或方法,那么就使用
"object"声明
1. class: 需要使用new的方式
,由于构造方法无参的原因,小括号可以省略
;
2. object: 可以直接使用类名
,但是获取的是
"伴生对象",不能使用new
1. class: 使用class声明的类无法通过类名直接访问方法或属性,必须构建对象
2. object: 使用object声明的类可以通过类名直接访问属性或方法。
1. Scala中class可以继承(extends)父类
2. 多态 : 一个对象在不同场合下所表示的不同的状态。
1. Scala当省略类型,编译器会自动将构建对象的类型进行推断。
2. 如果需要使用多态操作,那么必须显示声明类型。
1. 只能有一个
public的类;
2. public声明的类名需要和文件名称保持一致;
3. 一个文件可以有多个类;
1. 类名与文件名可以不一致;
2. class可以声明在很多位置;
3. 一个文件可以有多个公共的类。
IDEA中,scala不同类型的文件对应不同的图标
图标1:表示这个文件中只有伴生对象和伴生类图标2:表示这个文件中只有伴生类图标3:表示文件内容很复杂图标4:表示这个文件中只有伴生对象图标5:表示这个文件中是包对
1.4 属性
1. 在类中声明属性就等同于在类中声明局部变量的感觉,可以使用var,val声明;
2. 可以通过对象在外部访问;
3. "变量需要显示的初始化"。
1. 如果想要像java中类的属性初始化一样,需要采用特殊的符号:
"下划线";
2. 如果属性使用
"val"来声明。那么初始值
"不能使用下划线",需要显示的赋值;
3. 使用val声明的属性也不能改。
从如下的源码和反编译可知:
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"
b、因为属性为私有的,所以获取属性值,其实等同于调用属性的get方法
val 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; }
}
}
要求属性的
set/get方法起名必须以
set,get开头
,原因是在框架中,会动态获取属性, 反射调用属性的get方法获取属性值。
如果想要scala中的属性符合bean规范,可以采用特殊的注解:
"@BeanProperty"
@BeanProperty
var name
: String
= _
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. 每个类都会将其父类所有的属性和方法加载到自己的类中;
2. 现在com
.atguigu
.java_object访问User05中的父类(object),那他们两个就不是父子类的关系。
3. 是java_object类在main方法中调用了user对象的
clone()方法
scala的权限
Scala :
4种访问权限
private : 私有权限
"同类"
private
[包名
] : 包私有权限,
"同类,同包"
protected : 受保护权限,
"同类,子类"
(default) : 公共权限,
"任意的地方", scala中没有
public关键字
a、如果属性声明为private,那么编译器生成
set,get方法时,也会使用private进行修饰。
b、使用
@BeanProperty注解后,属性不能声明为private
a、中括号中的包名可以向上使用
b、必须在当前包的作用域范围内
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. 自定义方法
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. 应用
数值类型,在重载的方法中会提升精度。
引用类型,在重载的方法如果找不到对应的类型。会从类树往上查找
1. 方法的重写一定要存在父子类。
2. 子类重写父类的方法
,子类重写父类相同方法的逻辑
3. 子类重写方法与父类的方法要求:方法名一致,参数列表保持一致,异常范围
"子类抛出的异常不能比父类大",访问权限
"子类的访问权限不能比父类低"
如下内容,其实就是虚方法调用的原则,对于
"成员方法"来说:
"编译看左边,运行看右边,但是对于属性来说,运行和编译都看左边。"
4. 既然父类和子类有相同的方法,形成了方法的重写。
5. 那么在调用时,无法确定到底执行哪一个方法,那么需要遵循 动态绑定机制
6. 动态绑定机制 :程序
"执行"过程,如果调用了对象的
"成员""方法"时, 会将方法和对象的实际内存进行绑定,然后调用。
7. 动态绑定机制和属性无关
package com
.atguigu
.Scala_chapter01
.scala_chapter06_add
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
: Char) ={
println
("CCCC")
}
def Method
( b
: Int) ={
println
("iiii")
}
}
package com
.atguigu
;
public class Java_Method {
public static void main(String
[] args
) {
Student student
= new Student();
System
.out
.println(student
.sum());
Person student1
= new Student();
System
.out
.println(student1
.sum());
int i
= student1
.i
;
System
.out
.println(i
);
}
}
class Person {
int i
= 10 ;
public int sum(){
return i
+ 10 ;
}
}
class Student extends Person {
int i
= 20 ;
}
1.7 构造方法
1. 使用new关键字创建的对象其实等同于调用类的构造方法。
2. Scala是一个完全面向对象的语言。
3. Scala还是一个完全面向函数的语言。
4. Scala中类其实也是一个函数
,类名其实就是函数名
,"类名后面可以增加括号",表示函数参数列表
5. 这个类名所代表的函数其实就是构造方法,构造函数
6. 构造方法执行时,会完成类的主体内容的初始化。
7. 如果在类名的后面增加private关键字,表示主构造方法私有化,无法在外部使用
1. 主构造方法:在类名后加一对小括号的构造方法,可以完成类的初始化
2. 辅助构造方法:为了完成类初始化的辅助功能而提供的构造方法
1.声明方式为:
"def this() = {}"
2.在使用辅助构造方法时,必须直接或间接地调用主构造方法
3.辅助构造方法也存在重载的概念
4.辅助构造方法间接调用主构造函数时,
"需要保证调用的其他辅助构造函数已经声明过"。
package com
.atguigu
.Scala_chapter01
.scala_chapter06_add
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. 给构造方法增加参数的目的是什么?
从外部将数据传递到对象的属性中
2. Scala中一般构造方法的参数用于属性的初始化,所以为了减少数据的冗余
可以使用关键字var
,val将构造参数当成类的属性来用。
"var可读可写,val可读不可写"
3. 辅助构造方法的参数不能作为属性
package com
.atguigu
.Scala_chapter01
.scala_chapter06_add
object Scala_Construct
{
def main
(args
: Array
[String]): Unit = {
val person2
= new emp
("scala", 10)
println
(person2
.age
)
val emp
= new emp
()
println
(emp
.name
)
println
(emp
.age
)
val emp1
= new emp
("hadoop")
println
(emp1
.name
)
}
}
class emp
(val name
: String, var age
: Int) {
def this() = {
this("java", 20)
}
def this(name
: String) = {
this(name
, 30)
}
}
构建子类对象时,应该首先构建父类对象,如果父类的构造方法有参
那么子类在构建父类对象也应该传递参数。
在父类名称的后面加括号就可以传参了
package com
.atguigu
.Scala_chapter01
.scala_chapter06_add
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){
}
class Worker
(var str
:String , var num
:Int) extends Creature
(str
, num
){
}
1.8 创建对象的方法
代码实现
package com
.atguigu
.Scala_chapter01
.scala_review2
object Scala1_CreateObject
extends Cloneable
{
def main
(args
: Array
[String]): Unit = {
val user
= new User1
println
(user
)
val user1
= User1
()
println
(user1
)
val uclass
: Class
[User1
] = classOf
[User1
]
val user2
: User1
= uclass
.newInstance
()
println
(user2
)
val value
= clone
()
println
(value
)
}
}
class User1
{
val age
= 10
}
object User1
{
def apply
(): User1
= new User1
()
}
1.9 创建单例模式
总结:声明在object中的属性和方法类似java中的静态方法和静态属性,类加载的时候就会被加载。
步骤:
第一步:私有化构造器
第二步:创建一个私有化的静态属性,并new对象
第三步:创建一个公共的静态方法,返回静态属性
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
)
}
}
class User2
{
}
object User2
{
val user
= new User2
def singObject
={
user
}
}
1.10 抽象类
抽象类 : 不完整的类
抽象方法 :不完整的方法
只有声明而没有实现的方法
无需使用abstract关键字修饰
抽象属性:不完整的属性,只声明而没有初始化的属性
抽象类可以没有抽象方法和抽象属性,有抽象方法或抽象属性的类一定是抽象类
a、抽象类无法直接构造对象,可通过子类构造对象
b、抽象类使用
"abstract"修饰。
c、抽象类中可以有完整的方法和完整的属性。
a、子类只有实现抽象类中的所有抽象方法和抽象属性,才能实例化,否则还是抽象类。
a、如果子类重写父类的
"完整方法",需要显示增加override关键字来修饰
b、如果子类重写父类的
"抽象方法",需要直接补充完整即可
, 或采用override关键字修饰
a、子类重写父类的
"完整属性",那么需要增加override关键字修饰
,"被重写的属性必须是val声明的"
b、子类重写父类的
"抽象属性",那么需要将属性补充完整即可
关于抽象属性的说明:
a、抽象属性在编译时,不会产生类的属性,而是产生属性的
set,get方法,并且方法为抽象的。
b、重写抽象属性,那么等同于普通属性的声明:属性,
set,get方法。
"问题:为什么子类重写父类完整的属性只能是val,不能是var"
解析:
1. Scala中类的属性在编译时,会自动生成私有属性和对应的
set,get方法。
2. 详情见下图分析
1.11 特质
1.11.1java中接口和抽象类说明
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 = {
val user
= new User4
() with MySQL
user
.select
}
}
trait Operator
{
def test
() : Unit
}
trait MySQL
{
def select
= {
println
("执行mysql的数据库操作")
}
}
class User4
extends Operator
{
override def test
(): Unit = {
println
("zzzz")
}
}
1.11.4 特质的初始化顺序
object Scala5_trait
{
def main
(args
: Array
[String]): Unit = {
new SubUser35
()
}
}
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.operData
()
}
}
class MySQL37
extends DB37
with Log37
{
}
1.12 拓展
枚举类
object Scala7_Trait
{
def main
(args
: Array
[String]): Unit = {
println
(User7
.RED
)
println
(User7
.YELLOW
)
println
(User7
.BLUE
)
println
(User7
.BLUE
.id
)
}
}
object User7
extends Enumeration
{
val RED
= Value
(1, "red")
val YELLOW
= Value
(2, "yellow")
val BLUE
= Value
(3, "blue")
}
应用类
object Scala6_app
extends App
{
println
("xxxx")
println
("yyyy")
}