一、类的定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Person { val id="9527" var age:Int =18 private var name : String = "唐伯虎" private [this ] var pet = "小强" } object Person { def main (args: Array [String ]): Unit = { val p=new Person println(p.id) println(p.age)0 println(p.name) } }
二、构造器
每个类都有主构造器,主构造器的参数直接放置类名后面,与类交织在一起
主构造器中的参数会成类中的字段
主构造器会执行类定义的所有语句,但是并不包括方法,只会执行语句
每个辅助构造器执行必须以主构造器或者其他辅助构造器的调用开始
类里面每个元素好像都得初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Student (val name:String ,var age:Int ) { println("执行主构造器" ) private var gender="male" def this (name:String ,age:Int ,gender:String ){ this (name,age) println("执行辅助构造器" ) this .gender=gender } } object Student { def main (args: Array [String ]): Unit = { val s1=new Student ("zhangsan" ,20 ) val s2=new Student ("zhangsan" ,20 ,"female" ) } }
三、面向对象 1)、单例模式 object 相当于 class 的单个实例,通常在里面放一些静态的 field 或者 method;
在Scala中没有静态方法和静态字段,但是可以使用object这个语法结构来达到同样的目的。 object作用:
1. 存放工具方法和常量
2. 高效共享单个不可变的实例
3. 单例模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Session {}object SessionFactory { val session=newSession def getSession ():Session ={ session } } object SingletonDemo { def main (args:Array [String ]){ val session1 = SessionFactory .getSession() val session2 = SessionFactory .session } }
2)、伴生对象
如果有一个class文件,还有一个与class同名的object文件,那么就称这个object是class的伴生对象,class是object的伴生类;
伴生类和伴生对象必须存放在一个.scala文件中;
伴生类和伴生对象的最大特点是,可以相互访问;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Dog { private var name = "itcast" def printName (): Unit ={ println(Dog .CONSTANT + name ) } } object Dog { private val CONSTANT = "汪汪汪 : " def main (args: Array [String ]) { val p = new Dog p.name = "123" p.printName() } }
9.2 private[this]访问权限 如果某个成员的权限设置为private[this],表示只能在当前类中访问。伴生对象也不可以访问.
示例
示例说明
定义一个Person类,包含一个name字段, 该字段用private[this]修饰
定义Person类的伴生对象,定义printPerson方法
测试伴生对象是否能访问private[this]权限的成员
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 object ClassDemo13 { class Person (private[this] var name: String ) object Person { def printPerson (p:Person ) = println(p.name) } def main (args: Array [String ]) = { val p = new Person ("张三" ) Person .printPerson(p) } }
注意: 上述代码,会编译报错。但移除掉[this]就可以访问了
3)、apply方法和main方法 一、apply方法 object 中非常重要的一个特殊方法,就是apply方法; apply方法通常是在伴生对象中实现的,其目的是,通过伴生类的构造函数功能,来实现伴生对象的构造函数功能; 通常我们会在类的伴生对象中定义apply方法,当遇到类名(参数1,…参数n)时apply方法会被调用; 在创建伴生对象或伴生类的对象时,通常不会使用new class/class() 的方式,而是直接使用 class(),隐式的调用伴生对象的 apply 方法,这样会让对象创建的更加简洁;
在Scala中, 支持创建对象的时候, 免new
的动作, 这种写法非常简便,优雅。要想实现免new
, 我们就要通过伴生对象的apply方法来实现。
定义apply方法的格式
1 2 3 object 伴生对象名 { def apply (参数名:参数类型, 参数名:参数类型...) = new 类(...) }
创建对象
1 val 对象名 = 伴生对象名(参数1 , 参数2. ..)
例如: val p = Person(“张三”, 23)
二、main方法 同Java一样,如果要运行一个程序,必须要编写一个包含 main 方法的类; 在 Scala 中,也必须要有一个 main 方法,作为入口; Scala 中的 main 方法定义为 def main(args: Array[String]),而且必须定义在 object 中; 除了自己实现 main 方法之外,还可以继承 App Trait,然后,将需要写在 main 方法中运行的代码,直接作为 object 的 constructor 代码即可,而且还可以使用 args 接收传入的参数;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 object Main_Demo1 { def main (args: Array [String ]) { if (args.length > 0 ){ println("Hello, " + args(0 )) }else { println("Hello World!" ) } } } object Main_Demo2 extends App { if (args.length > 0 ){ println("Hello, " + args(0 )) }else { println("Hello World!" ) } }
四、继承 1)、概念,字段以及方法
Scala 中,让子类继承父类,与 Java 一样,也是使用 extends 关键字;
继承就代表,子类可继承父类的 field 和 method ,然后子类还可以在自己的内部实现父类没有的,子类特有的 field 和method,使用继承可以有效复用代码;
子类可以覆盖父类的 field 和 method,但是如果父类用 final 修饰,或者 field 和 method 用 final 修饰,则该类是无法被继承的,或者 field 和 method 是无法被覆盖的。
private 修饰的 field 和 method 不可以被子类继承,只能在类的内部使用;
field 必须要被定义成 val 的形式才能被继承,并且还要使用 override 关键字。 因为 var 修饰的 field 是可变的,在子类中可直接引用被赋值,不需要被继承;即 val 修饰的才允许被继承,var 修饰的只允许被引用。继承就是改变、覆盖的意思。
Java 中的访问控制权限,同样适用于 Scala
1 2 3 4 5 scala里面有属性有四种情况 1. val name = "ss" 2. var name2 = "aa" 3. private val age1 = 12 4. private var age2 = 12
四种属性被继承后的情况如下:
1 2 3 4 5 6 7 1. 第一第二种子类可以直接使用父类定义的变量值2. val 不可以直接修改变量,需要把父类的覆盖,写法如下: override val name="sss" 3. var 可以直接修改父类的变量值 name2 = "aaa" 4. private 修饰的变量对于子类不可见,子类可以自由再定义同名变量没问题。
父类中定义了一个方法 两种情况,一种没有private修饰,一种用private修饰 1.没有用private修饰的方法,子类可以直接调用 2.没有用private修饰的方法,如果子类想重写父类的方法,需要在新方法上加入override参数,标明重写覆盖 3.如果父类的方法被private参数修饰,则子类无法调用,也无法得知该方法的存在,也可以任意自己再写同名方法。
2)、override 和 super
Scala中,如果子类要覆盖父类中的一个非抽象方法,必须要使用override
关键字;子类可以覆盖父类的val
修饰的field
,只要在子类中使用override
关键字即可。
override
关键字可以帮助开发者尽早的发现代码中的错误,比如,override
修饰的父类方法的方法名拼写错误。
此外,在子类覆盖父类方法后,如果在子类中要调用父类中被覆盖的方法,则必须要使用super
关键字,显示的指出要调用的父类方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Student extends Person { override val name="ss" override def getName :String ={ println("调用子类覆盖父类的方法" ) this .name } def getPersonName :String = { println("使用super关键字调用父类的方法,但是返回的name值还是子类的name值" ) super .getName } }
3)、isInstanceOf
和asInstanceOf
概述
isInstanceOf: 判断对象是否为指定类的对象
asInstanceOf: 将对象转换为指定类型
格式
1 2 3 4 5 val trueOrFalse:Boolean = 对象.isInstanceOf[类型]val 变量 = 对象.asInstanceOf[类型]
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 object ClassDemo05 { class Person class Student extends Person { def sayHello () = println("Hello, Scala!..." ) } def main (args: Array [String ]): Unit = { val p: Person = new Student if (p.isInstanceOf[Student ]) { val s = p.asInstanceOf[Student ] s.sayHello() } } }
scala
对象类型检查,判断该实例是否是某个类的子类,以及转换成该类:
如果实例化了子类的对象,但是将其赋予了父类类型的变量,在后续的过程中,又需要将父类类型的变量转换为子类类型的变量,应该如何做?
1 2 3 Class A Class B extends A B b=New B
首先,需要使用isInstanceOf
判断对象是否为指定类的对象,如果是的话,则可以使用asInstanceOf
将对象转换为指定类型; 注意:p.isInstanceOf[XX]
判断 p 是否为 XX 对象的实例;p.asInstanceOf[XX]
把 p 转换成 XX 对象的实例 注意:如果没有用 isInstanceOf 先判断对象是否为指定类的实例,就直接用 asInstanceOf 转换,则可能会抛出异常; 注意:如果对象是 null,则一定返回 false, asInstanceOf 一定返回 null; Scala与Java类型检查和转换 注意,子类对所有父类的isInstanceOf
[任意父类]返回的都是true
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Person3 {}class Student3 extends Person3 object Student3 { def main (args: Array [String ] ) { val p: Person3 = new Student3 var s: Student3 = null println (s.isInstanceOf[Student3 ]) if (p.isInstanceOf[Student3 ] ) { s = p.asInstanceOf[Student3 ] } println (s.isInstanceOf[Student3 ] ) } }
scala和java的获取类对象区别
Scala
Java
obj.isInstanceOf[C]
obj instanceof C
obj.asInstanceOf[C]
(C)obj
classOf[C]
C.class
4)、getClass 和 classOf isInstanceOf 只能判断对象是否为指定类以及其子类 的对象,而不能精确的判断出: 对象就是指定类的对象。如果要求精确地判断出对象的类型就是指定的数据类型,那么就只能使用 getClass 和 classOf 来实现.
用法
p.getClass可以精确获取对象的类型
classOf[类名]可以精确获取数据类型
使用==操作符可以直接比较类型
实际判断该类是哪个类型的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1. 对象.getClass() 返回类类型2. classOf[类名称] 返回类类型3. 把上面返回的结果用等号比较val person = new Student3 println(person.getClass) println( person.isInstanceOf[Student3 ] ) println( person.isInstanceOf[Person3 ] ) println( person.getClass == classOf[Student3 ] ) println( person.getClass == classOf[Person3 ] )
5)、使用模式匹配进行类型判断
在实际的开发中,比如 spark 源码中,大量的地方使用了模式匹配的语法进行类型的判断,这种方式更加地简洁明了,而且代码的可维护性和可扩展性也非常高;
使用模式匹配,功能性上来说,与 isInstanceOf 的作用一样,主要判断是否为该类或其子类的对象即可,不是精准判断。
等同于 Java 中的 switch case 语法;
1 2 3 4 5 6 7 val p:Person5 =new Student5 p match { case per:Person5 => println("This is a Person5's Object!" +per) case _ =>println("Unknown type!" ) }
6)、protected
跟Java一样,Scala中同样可使用protected关键字来修饰field和method。在子类中,可直接访问父类的field和method,而不需要使用super关键字;
还可以使用protected[this]关键字,访问权限的保护范围:只允许在当前子类中访问父类的field和method,不允许通过其他子类对象访问父类的field和method。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Person6 { protected var name:String ="tom" protected [this ] var hobby:String ="game" protected def sayBye=println ("再见..." ) } class Student6 extends Person6 { def sayHello =println ("Hello " +name) def sayByeBye=sayBye def makeFriends (s:Student6 )={ println("My hobby is " +hobby+", your hobby is UnKnown" ) } } object Student6 { def main (args: Array [String ]) { val s:Student6 =new Student6 s.sayHello s.makeFriends(s) s.sayByeBye } }
7)、调用父类构造函数
Scala中,每个类都可以有一个主constructor和任意多个辅助constructor,而且每个辅助constructor的第一行都必须调用其他辅助constructor或者主constructor代码;因此子类的辅助constructor是一定不可能直接调用父类的constructor的;
只能在子类的主constructor中调用父类的constructor。
如果父类的构造函数已经定义过的 field,比如name和age,子类再使用时,就不要用 val 或 var 来修饰了,否则会被认为,子类要覆盖父类的field,且要求一定要使用 override 关键字。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Person7 (val name:String ,val age:Int ) { var score :Double =0.0 var address:String ="beijing" def this (name:String ,score:Double )={ this (name,30 ) this .score=score } def this (name:String ,address:String )={ this (name,100.0 ) this .address=address } } class Student7 (name:String ,score:Double ) extends Person7 (name,score )
8)、抽象类 一、抽象类
如果在父类中,有某些方法无法立即实现,而需要依赖不同的子类来覆盖,重写实现不同的方法。此时,可以将父类中的这些方法编写成只含有方法签名,不含方法体的形式,这种形式就叫做抽象方法;
一个类中,如果含有一个抽象方法或抽象field,就必须使用abstract将类声明为抽象类,该类是不可以被实例化的;
在子类中覆盖抽象类的抽象方法时,可以不加override关键字;
抽象字段必须指出返回类型,要不然默认无返回值。
1 2 3 4 5 6 7 8 9 abstract class Person9 (val name:String ) { def sayHello :String def sayBye :String } class Student9 (name:String ) extends Person9 (name ) { def sayHello : String = "Hello," +name def sayBye : String ="Bye," +name }
二、抽象字段 如果在父类中,定义了field,但是没有给出初始值,则此field为抽象field;
1 2 3 4 5 6 7 abstract class Person10 (val name:String ) { val age:Int } class Student10 (name: String ) extends Person10 (name ) { val age: Int = 50 }
三、匿名内部类 匿名内部类是继承了类的匿名的子类对象,它可以直接用来创建实例对象。Spark的源代码中大量使用到匿名内部类。学完这个内容, 对我们查看Spark的底层源码非常有帮助.
需求
创建一个Person抽象类,并添加一个sayHello抽象方法
定义一个show()方法, 该方法需要传入一个Person类型的对象, 然后调用Person类中的sayHello()方法.
添加main方法,通过匿名内部类的方式来创建Person类的子类对象, 调用Person类的sayHello()方法.
调用show()方法.
参考代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 object ClassDemo09 { abstract class Person { def sayHello () } def show (p:Person ) = p.sayHello() def main (args: Array [String ]): Unit = { new Person { override def sayHello (): Unit = println("Hello, Scala, 当对成员方法仅调用一次的时候." ) }.sayHello() val p = new Person { override def sayHello (): Unit = println("Hello, Scala, 可以作为方法的实际参数进行传递" ) } show(p) } }
五、接口 1-trait
Scala中的trait是一种特殊的概念;
首先先将trait作为接口使用,此时的trait就与Java中的接口 (interface)非常类似;
在trait中可以定义抽象方法,就像抽象类中的抽象方法一样,只要不给出方法的方法体即可;
类可以使用extends关键字继承trait,注意,这里不是 implement,而是extends ,在Scala中没有 implement 的概念,无论继承类还是trait,统一都是 extends;
类继承后,必须实现其中的抽象方法,实现时,不需要使用 override 关键字;
Scala不支持对类进行多继承,但是支持多重继承 trait,使用 with 关键字即可。
trait中可以有四种东西,具体的接口,抽象的接口,具体的字段,抽象的字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 trait HelloTrait { def sayHello (): Unit } trait MakeFriendsTrait { def makeFriends (c: Children ): Unit } class Children (val name: String ) extends HelloTrait with MakeFriendsTrait { def sayHello () =println("Hello, " + this .name) def makeFriends (c: Children ) = println("Hello, my name is " + this .name + c.name) } object Children { def main (args: Array [String ]) { val c1=new Children ("tom" ) val c2=new Children ("jim" ) c1.sayHello() c1.makeFriends(c2) } }
2-四种数据
在trait中可以定义抽象方法,就像抽象类中的抽象方法一样,只要不给出方法的方法体即可;
Scala中的trait不仅可以定义抽象方法,还可以定义具体的方法,此时 trait 更像是包含了通用方法的工具,可以认为trait还包含了类的功能。
Scala 中的 trait 可以定义具体的 field,此时继承 trait 的子类就自动获得了 trait 中定义的 field; 但是这种获取 field 的方式与继承 class 的是不同的。 如果是继承 class 获取的 field ,实际上还是定义在父类中的;而继承 trait获取的 field,就直接被添加到子类中了。
Scala中的trait也能定义抽象field, 而trait中的具体方法也能基于抽象field编写; 继承trait的类,则必须覆盖抽象field,提供具体的值;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 trait PersonTrait { var name:String = "啦啦啦" ; var age:Int ; def getName :String = this .name def getAge :Int ; } class StudentTrait (nameTmp:String ,ageTmp:Int ) extends PersonTrait { this .name = nameTmp this .age = ageTmp override var age: Int = 99 override def getAge : Int = this .age } object StudentTrait { def main (args: Array [String ]): Unit = { val s = new StudentTrait ("豆豆" ,128 ) println(s.getName+" " +s.getAge) } }
3-指定混入某个trait 类原来继承一个父接口,可以混入某个父接口的子接口,没法混入爷爷接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 trait LoggedTrait { def log (msg: String ) = {println("LoggedTrait类中的方法:" +msg)} } trait MyLogger extends LoggedTrait { override def log (msg: String ) = println("MyLogger类中的方法: " + msg) } class StudentTrait extends LoggedTrait { def sayHello = { log("调用log方法!" ) } } object StudentTrait { def main (args: Array [String ]) { val s = new StudentTrait s.sayHello val s2 = new StudentTrait with MyLogger s2.sayHello } }
4-调用链
Scala中支持让类继承多个trait后,可依次调用多个trait中的同一个方法,只要让多个trait中的同一个方法,在最后都依次执行 super 关键字即可;
类中调用多个trait中都有的这个方法时,首先会从最右边的trait的方法开始执行,然后依次往左执行,形成一个调用链条;
这种特性非常强大,其实就是设计模式中责任链模式的一种具体实现;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 trait TraitList { def logger (name:String ) = println("traitList接口中的方法" +name) } trait TraitList2 extends TraitList { override def logger (name:String ) = { println("traitList2接口中的方法" +name) super .logger(name) } } trait TraitList3 extends TraitList { override def logger (name:String ): Unit = { println("traitList3接口中的方法" +name) super .logger(name) } } class TraitListc extends TraitList with TraitList2 with TraitList3 { def func (name:String ): Unit ={ this .logger(name) } } object TraitListc { def main (args: Array [String ]): Unit = { val listc = new TraitListc listc.func("aaa" ) } } traitList3接口中的方法aaa traitList2接口中的方法aaa traitList接口中的方法aaa