Scala的面向对象
Scala 的面向对象思想和 Java 的面向对象思想和概念是一致的。
Scala 中语法和 Java 不同,补充了更多的功能。
1.Scala包
Scala包与Java类似,有两种管理方式。第一种与Java一毛一样。
但是在Scala中,还有另一种,即通过嵌套的方式。
package com{
package zc{
package hadoop{
}
}
}
按照嵌套的方式:
(1)一个源文件中可以声明多个 package
(2)子包中的类可以直接访问父包中的内容,而无需导包,父包访问子包需要导包。
1.1Scala中的包对象
在 Scala 中可以为每个包定义一个同名的包对象,定义在包对象中的成员,作为其对应包下所有 class 和 object 的共享变量,可以被直接访问。类似于class的伴生对象。
package object Chap02 {
//定义当前包共享的属性和方法
val commonValue = "zc"
def commonMethod()={
println(commonValue+"在学习")
}
}
1.2导包
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.lang._ import scala._ import scala.Predef._
2.Scala 类
在Scala中,没有public(即默认就是 public)
其次,一个Scala源文件可以包含多个类
类的属性(封装)都在下面实现:
object Test1BeanClass {
def main(args: Array[String]): Unit = {
val student = new Student
student.setAge(1)
student.setSex("ada")
println(student)
}
}
/* 修饰符默认public*/class Student{
private var name :String = _
//使用 _ 就是默认为该类型的空值
//在Scala中@BeanProperty我们可以直接获取getset方法
//但是此时使用@ 的话,我们需要其是private ,那么就很离谱 设计来干嘛。。
//Scala 中的 public 属性,底层实际为 private,并通过 get 方法(obj.field())和 set 方法
//(obj.field_=(value))对其进行操作。所以 Scala 并不推荐将属性设为 private,再为其设置
//public 的 get 和 set 方法的做法。但由于很多 Java 框架都利用反射调用 getXXX 和 setXXX 方
//法,有时候为了和这些框架兼容,也会为 Scala 的属性设置 getXXX 和 setXXX 方法(通过
//@BeanProperty 注解实现)。
@BeanProperty
var age:Int= _
@BeanProperty
var sex:String= _
override def toString = name+"—"+age+"-"+sex
}
定义方法与定义函数类似。
2.1创建对象
(1)val 修饰对象,不能改变对象的引用(即:内存地址),可以改变对象属性的值。
(2)var 修饰对象,可以修改对象的引用和修改对象的属性值
(3)自动推导变量类型不能多态,所以多态需要显示声明
object ReviewAAA {
def main(args: Array[String]): Unit = {
val c = new testClass
c.name = "a"
println(c.name)
}
}
class testClass{
var name:String =_
}
2.2构造器与构造器参数
和 Java 一样,Scala 构造对象也需要调用构造方法,并且可以有任意多个构造方法。 Scala 类的构造器包括:主构造器和辅助构造器。
注意:使用辅助构造器时,一定会先使用主构造器。
Scala 类的主构造器函数的形参包括三种类型:未用任何修饰、var 修饰、val 修饰
(1)未用任何修饰符修饰,这个参数就是一个局部变量 (下述demo中的ss对象)
(2)var 修饰参数,作为类的成员属性使用,可以修改
(3)val 修饰参数,作为类只读属性使用,不能修改
object TestConstructor {
def main(args: Array[String]): Unit = {
new ss("aaa",12).printerInfo()
val aa = new ss("aa", 111)
//println(s"aa: name:"+aa.name) 错误 拿不到
val ab = new ss1("ab",111)
println(ab.name)//可以访问到
//那么为什么上面的aa访问不到,因为其只是一个形参,没有实际参数。
}
}
class Stu {
//用 _替代初始值
var name: String = _
var age: Int = _
println("主构造器被调用1")
//辅助构造方法
def this(name: String) {
this()
println("辅助构造器调用2")
this.name = name
}
def this(name: String, age: Int) {
this(name)
println("辅助构造器调用3")
this.age = age
}
}
class ss1(var name:String,var age:Int)
class ss(name: String, age: Int){
def printerInfo(): Unit ={
println(s"name:$name-age:$age")
}
}
2.3继承和多态
class 子类名 extends 父类名 { 类体 } (1)子类继承父类的属性和方法 (2)scala 是单继承
继承的调用顺序:父类构造器->子类构造器
与Java最大的区别在于,Scala中的继承是动态绑定的。即不仅会继承父类的方法,还要继承父类的属性。
//package Chap02
//
//object DTBinding {
下面这一段用java重写 最后输出的结果是不一样的
///*
//scala的结果中 都是teacher - helloteacher
//java中是 person - helloteacher
//说明java的动态绑定是指绑定了方法,但是scala属性和方法都绑定,都算在继承里面、
// */
//
//}
//class Person1 {
// val name: String = "person"
// def hello(): Unit = {
// println("hello person")
// }
//}
//class Teacher extends Person1 {
// override val name: String = "teacher"
// override def hello(): Unit = {
// println("hello teacher")
// }
//}
//object Test {
// def main(args: Array[String]): Unit = {
// val teacher: Teacher = new Teacher()
// println(teacher.name)
// teacher.hello()
// val teacher1: Person1 = new Teacher
// println(teacher1.name)
// teacher1.hello()
// }
//}
///*
//Java
//class Person {
// public String name = "person";
// public void hello() {
// System.out.println("hello person");
// }
//}
//class Teacher extends Person {
//public String name = "teacher";
// @Override
// public void hello() {
// System.out.println("hello teacher");
// }
//}
//public class TestDynamic {
//public static void main(String[] args) {
// Teacher teacher = new Teacher();
// Person teacher1 = new Teacher();
// System.out.println(teacher.name);
// teacher.hello();
// System.out.println(teacher1.name);
// teacher1.hello();
// }
//}
// */
2.4抽象类
/*
(1)定义抽象类:abstract class Person{} //通过 abstract 关键字标记抽象类
(2)定义抽象属性:val|var name:String //一个属性没有初始化,就是抽象属性
(3)定义抽象方法:def hello():String //只声明而没有实现的方法,就是抽象方法
(1)如果父类为抽象类,那么子类需要将抽象的属性和方法实现,否则子类也需声明
为抽象类
(2)重写非抽象方法需要用 override 修饰,重写抽象方法则可以不加 override。
(3)子类中调用父类的方法使用 super 关键字
(4)子类对抽象属性进行实现,父类抽象属性可以用 var 修饰;
子类对非抽象属性重写,父类非抽象属性只支持 val 类型,而不支持 var。
因为 var 修饰的为可变变量,子类继承之后就可以直接使用,没有必要重写
*/
object AbsClass {
}
abstract class Person {
val name: String
def hello(): Unit
}
class Teacher extends Person {
val name: String = "teacher"
override def hello(): Unit = {
println("hello teacher")
}
}
2.5匿名子类
与Java基本一致。
2.6单例对象
Scala语言是完全面向对象的语言,所以并没有静态的操作。但是为了能够和Java语言交互(因为Java中有静态概念),就产生了一种特殊的对象来模拟类对象,该对象为单例对象。若单例对象名与类名一致,则称该单例对象这个类的伴生对象,这个类的所有“静态”内容都可以放置在它的伴生对象中声明。
package Chap02
/*
(1)单例对象采用 object 关键字声明
(2)单例对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
(3)单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。
此时为了保护我们的对象实例,我们可以将其置为private,让所有的别的代码
必须访问我们的伴生对象中的apply方法用来返回对象。
即与Java中的getInstance一致。
*/
object SingletonScala {
//此时 school属性是静态的
var school :String = "zcStudy"
def apply(ename:String, age:Int):SingletonScala=new SingletonScala(ename,age)
}
class SingletonScala private(var ename:String ,var age:Int){
}
object maintest{
def main(args: Array[String]): Unit = {
println(SingletonScala.school)
//没有创建直接调用,相当于就是这个类的所有“静态”内容都可以放置在它的伴生对象中声明
val ss = SingletonScala.apply("a", 1)
println(ss.age +" "+ ss.ename)
//有了apply之后可以这样
var sss = SingletonScala("aoisdh",1231)
println(sss.age +" "+ sss.ename)
}
}
扩展单例模式:在Java中实现单例模式有许多种方法。详情见其他文章。
在Scala中则按照我们的需求,将getInstance放到“静态”即伴生对象中。
/*//饿汉式
object singleton {
private val single = new singleton("a",11)
def getInstance():singleton = single
}*/
//懒汉式
object singleton{
private var ss:singleton =_
def getInstance():singleton ={
if(ss == null){
ss = new singleton("a",11)
}
ss
}
}
class singleton private(var ename:String,var age:Int){
def printInfo(): Unit ={
println(ename +" "+ age)
}
}
3.Trait特质
scala中我们使用Trait特质来替代接口的概念。Scala 中的 trait 中即可以有抽象属性和方法,也可以有具体的属性和方法,一个类可以混入(mixin)多个特质。这种感觉类似于 Java 中的抽象类。
一个类具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所有要素, 所以在使用时,也采用了 extends 关键字,如果有多个特质或存在父类,那么需要采用 with 关键字连接。 基本语法:
没有父类:class 类名 extends 特质 1 with 特质 2 with 特质 3 …
有父类:class 类名 extends 父类 with 特质 1 with 特质 2 with 特质 3…
package Chap02
/*
此时出现了菱形继承。 归属的顺序 输出的顺序如下
student -> knowledge -> talent -> Person
在继承最末端的先出现
即 student的super是knowledge 。在demo中也予以证明。
同时当我们需要再附加属性时,只需要在创建时在with新的trait即可。
*/
object TestTraitMixin {
def main(args: Array[String]): Unit = {
val s = new student001
s.abcde()
s.increase()
s.increase()
println(s.selfInfo())
val sbad = new student001 with Bad {
override def fighting(): Unit = println("I am a badBoy")
override val name: String = "badguy"
}
println(sbad.name)
sbad.fighting()
}
}
class Person001{
val name =" person "
val age = -1
def sayHello(): Unit ={
println("hello"+name)
}
def selfInfo():String={
"Person"
}
}
object student001{
def study(): Unit ={
println("学习")
}
}
trait Bad{
def fighting()
val name = "badBoy"
}
trait Talent extends Person001 {
def singing()
def dancing()
def abcde(): Unit ={
println("talent-abcde")
}
override def selfInfo() : String ={
"talent"+" "+super.selfInfo()
}
}
trait Knowledge extends Person001 {
var amount = 0;
def increase()
def abcde(): Unit ={
println("Knowledge-abcde")
}
override def selfInfo() : String ={
"knowledge"+" "+super.selfInfo()
}
}
//此时的super,会继承最后拿一个trait,谁在最后跟谁
class student001 extends Person001 with Talent with Knowledge {
override def singing(): Unit = println("I can singing")
override def dancing(): Unit = println("I am dancing")
override def abcde(): Unit = super.abcde()
override def increase(): Unit = {
amount += 1
println(this.name +"-Knowledge"+" "+ amount)
}
override def selfInfo(): String = "Student has "+super.selfInfo()
}
3.1依赖注入)(自身注入)
scala中直接将 _:(类名) => 写入到我们需要的代码块中,此时我们可以在该代码块中拿到该类的一系列属性。
//实现了依赖注入的功能
object TestZishen {
def main(args: Array[String]): Unit = {
val user = new regis("a", "123")
user.insert()
}
}
class User(val name:String,val password: String)
trait Dao{
_:User =>
def insert(): Unit ={
println(s"insert into user values(${this.name},${this.password})")
}
}
class regis( name:String,password: String)extends User(name,password) with Dao
4.扩展
枚举类与Java基本一致。
应用类,即在里面的代码不需要写main方法可以直接执行。
Type定义新类型
object Test {
def main(args: Array[String]): Unit = {
type S=String //创建了一个新类型 S 和String一样
var v:S="abc"
def test():S="xyz"
}
}
类型转换
obj.isInstanceOf[T]:判断 obj 是不是 T 类型。
obj.asInstanceOf[T]:将 obj 强转成 T 类型