类
//定义类
class helloworld{
private var name="Tom"
def say(){ print("hello,"+ name)}
def getname()=name
}
//创建类的对象
val aa=new helloworld
//调用方法
aa.say()
print(aa.getname()) //可以不加(),如果定义方法是不带括号,调用时也不能带()
//
getter和setter
定义不带private的var字段,scala生成面向JVM的类时,会定义为private的var字段,但是getter和setter是public
定义private的var字段,生成private的getter和setter
(无论字段是不是private修饰的,字段都会是private的,区别只是在getter和setter)
如果定义val字段,只会生成getter
如果不想生成getter和setter方法,可以将字段声明为private[this]
class person{
var age =0
}
//scala 中getter 和setter 方法分别叫age和age_
//自定义getter,setter方法
class Student{
private var myName="tom"
//自定义getter方法
def name="hello,"+myName
//自定义setter方法,注意签名、=、参数三者之间不能有空格
def name_=(newName:String){
println("you do not edite your name!")
}
}
object GetterAndSetter {
def main(args: Array[String]): Unit = {
val aa=new Student()
println(aa.name)
aa.name_=("jack")
}
}
仅暴露字段的getter方法
class student{
private var myName="Tom"
def updataname(newName:String){
if(newName=="Amy") myName=newName
else print("NO")
}
def name="you name is "+myName
}
private[this]的使用
class student{
private[this] var myage=0
def age_=(nameValue:Int){
if (newValue>0)myage = newValue
else print("No")
}
def age=myage
def older(s:Student)={
myage>s.myage //报错,[this]
}
}
辅助constructor(构造函数)
//可以给类定义多个辅助constructor,辅助constructor之间可以互相调用
class Stu{
private var name="default"
private var age=0
//第一个辅助构造器
def this(name:String){
this() //调用主构造器
this.name=name
}
//第二个辅助构造器
def this(name:String,age:Int){
this(name) //调用第一个辅助构造器
this.age=age
}
def sayHello=println(name+" "+age)
}
object ConstructorDemo {
def main(args: Array[String]): Unit = {
val stu1=new Stu //调用默认的主构造器
stu1.sayHello
val stu2=new Stu("张三") //调用第一个辅助构造器
stu2.sayHello
val stu3=new Stu("李四",20) //调用第二个辅助构造器
stu3.sayHello
}
}
主constructor
主constructor是与类名放在一起的,类中没有定义任何方法或者代码块之中的代码,就是主constructor的代码
class Per(val name:String,val age:Int){
println("my name is "+name+",my age is "+age)
println("hello,main constructor!")
}
object MainConstructor {
def main(args: Array[String]): Unit = {
val per=new Per("张三",20)
}
}
对象
object
相当于class的单个实例,第一次调用object的是后会执行object的constructor(只会在第一次调用时执行一次)
但是object不能定义接受参数的constructor
伴生对象
如果有一个class还有一个与class同名的object,那么这个object是class的伴生对象,class是object的伴生类
两者必须在同一个.scala文件中()
最大特点就是可以互相访问private字段
class Account{
// val array=Array()
//伴生类中的私有字段
private var num=10
//通过Account.num2调用伴生对象中的私有字段num2
def getNum2=println(Account.num2)
//通过Account.getResult调用伴生对象中的私有函数
num=Account.getResult
def getInfo=println(num)
}
//伴生对象
object Account {
//伴生对象中的私有字段
private var num2=20
//伴生对象中的私有函数
private def getResult={
num2+=10
num2
}
//创建apply()方法,用于创建伴生类的对象
def apply()={
println("这是伴生对象中的apply方法!")
new Account
}
def main(args: Array[String]): Unit = {
val account=Account()//调用的是伴生对象中的apply方法
account.getInfo
// val account=new Account
//在伴生对象中,通过伴生类的对象访问类中的私有字段
// println(account.num)
}
}
//类的伴生对象可以被访问但不在作用域中
//Account类必须通过Account.newUniqueNumber()类调用伴生对象的方法
- apply方法
通常在伴生对象中实现apply方法,并在其中实现构造伴生类的对象功能
在创建伴生类的对象是使用Class()的方式,隐式的调用伴生对象的apply方法
class person(val name:String )
object person{
def apply(name:String)=new persom(name)
}
//伴生对象
object Account {
//伴生对象中的私有字段
private var num2=20
//伴生对象中的私有函数
private def getResult={
num2+=10
num2
}
//创建apply()方法,用于创建伴生类的对象
def apply()={
println("这是伴生对象中的apply方法!")
new Account
}
def main(args: Array[String]): Unit = {
val account=Account()//调用的是伴生对象中的apply方法
account.getInfo
// val account=new Account
//在伴生对象中,通过伴生类的对象访问类中的私有字段
// println(account.num)
}
}
- 用object来实现枚举功能
scala中没有类似于java中enum的枚举特性
object继承Enumeration,并调用Value放发来初始化枚举值
val aa extends Enumeration{
val spring,summer,autumn,winter=Value
}
或
object aa extends Enumeration{
val spring = Value(0,"spring")
val summer = Value(1,"summer")
val autumn = Value(2,"autumn")
val winter = Value(3,"winter")
}
aa(0)
aa.withName("winter")
//使用枚举object.values可以遍历枚举值
for (ele<-aa.Value)print(ele)
- mian方法
scala的main方法必须定义在object中
除了自己实现mian方法外,还可以继承App trait,将要运行的代码自己而作为object的constructor代码,而且用args可以接受传入的参数 - object 继承抽象类
object的功能和class类似,除了不能定义接受参数的constructor
object可以继承抽象类,并覆盖抽象类的方法
abstract class Hello (var message:String){
def say(name:String):Unit
}
object Hello1 extends Hello("aa"){
override def say(name:String)={
println(message+" "+name)
}
}
继承
使用关键字extends
继承代表子类可以继承父类的字段和方法,子类可以覆盖父类的字段和方法,
如果父类用final 修饰,是无法继承的,字段或方法用final修饰,字段和方法是无法被覆盖的。
如果子类要覆盖一个父类中的飞抽象方法,必须使用override
在覆盖父类方法后,在子类中调用父类被覆盖哦的方法,使用super,显式的指定要调用父类的方法
class person{
private var name="leo"
def getName=name
}
class student extends person{
private var score="92"
def getScore=score
override def getName="hi,i am"+super.getName
}
子类可以覆盖父类的val字段,只要使用override关键字即可
- isInstanceOf 和asInstanceOf
isInstanceOf :判断对象是否是指定类的对象
asInstanceOf:将对象转换为指定类型
如果对象是null,则isInstanceOf一定返回false,asInstanceOf一定返回null
如果没有进行判断直接转换类型,可能会抛出异常
if(p.isInstanceOf[Studnet]) s=p.asInstanceOf[Studnet]
isInstanceOf不能进行精确的判断
对象.getClass可以精确的获取对象的类,classOf[类] 可以精确的获取类,然后使用==连接即可
class person
class studnet extends person
val p: person= new studnet
p.isInstanceOf[person]
p.isInstanceOf[studnet]
p.getClass==classOf[person]
p.getClass==classOf[student]
- 模式匹配
功能上与isInstanceOf一样,主要是判断是该类以及该类的子类对象即可,是不精确判断
class person
class studnet extends person
val p: person= new studnet
p match{
case per:person=>println("asdad")
case _ => println("NO") //注意case与下划线之间有一个空格
}
- protectd 关键字
使用protectd 修饰字段或方法,子类就不需要super关键字就能直接访问字段和方法了
protectd[this] ,只能在当前子类中党文父类的字段和方法 - 调用父类的constructor
每个类都有一个主constructor和多个辅助constructor
每个辅助constructor的第一行必须是调用其他辅助constructor或者是主constructor,所以子类的辅助constructor一定不能直接调用父类的constructor的
只能在子类的主constructor中调用父类的constructor
class person(val name:String ,val age:Int)
class student(name:String,age:Int,var score:Double) extends person(name,age){
def this(name:String){
this(name,0,0)
}
def this(age:Int){
this("tom",age,0)
}
}
- 抽象类
父类中的那些不给出具体实现的方法,只有方法签名,称为抽象方法
如果类中有抽象方法,,类必须使用abstract声明为抽象类,是不能实例化的
在子类中覆盖抽象类的抽象方法时,不需要使用override关键字
abstract class person(val name:String)
class student(name:String) extends person(name){
def say:Unit=println("hello"+name)
}
- 抽象字段
在父类中定义了字段,没有给出初始值,为抽象字段
scala会为val 和var类型的字段生成对应的个getter和setter方法,子类必须覆盖字段以定义自己的具体字段,不用override关键字
trait特质
可以将trait作为借口来使用,在trait中定义抽象方法,和抽象类中的抽象方法一样,只要不给出具体的实现即可
类可以使用extends继承trait,继承后必须实现其中的抽象方法,实现时不需要override
scala不支持对类进行多继承,但支持多重继承trait,还用with关键字
//将trait作为接口使用
trait hello{
def say(name:String)
}
traitmakefriend{
def makefriends(p:person)
}
class person (val name:String)extends hello with makefriends with Serializable{
def say(name:String)=println("hello"+name)
def makefriend(p:person)=print(name+" "+p.name)
}
- trait定义具体方法
trait logger{
def log(message:String)=println(message)
}
- trait中定义具体字段
trait中可以定义具体字段,此时继承trait的类就自动获得了trait中的字段
与继承class获取的字段是不同的,实际是定义在父类中的,而继承trait获取的字段是直接添加到类中的
//实例混入trait
traid Logged{
def log(msg:String)
}
trait MyLogger extends Logged{
override def log(msg:String){
println("log:"+msg)
}
}
class person(val name:String) extends Logged{
def say("hi-----I am"+ name);
log("sayHellomydear")
}
val aa = new person("sam")
aa.say
val bb = new person("Tony") with MyLogger
bb.say
调用链问题总结
- 调用链必须是类继承多个trait
- 多个trait中必须包含同一个方法,并多个trait有一个共同的父trait
- 调用的多个trait的同一个方法中,最后的语句必须是super.方法名,通过这种形式trait调用链中下一个trait的方法
- 满足以上条件才能构成trait调用链,在调用链中按照从右到左的顺序依次调用一个方法
-
混合使用trait的具体方法和抽象方法
可以让抽象方法方法哦继承trait的类中实现,这就是模板设计模式 -
trait的构造机制
- 父类的构造函数执行
- trait的构造代码执行,多个trait从右放左执行
- 构造trait是会先构造父类trait,若多个trait继承同一个父类,父类只会构造一次
- 所有的trait构造完毕后,子类的构造函数执行