scala系列(五)类和对象 有更新!

  |   0 评论   |   1,674 浏览

37套精品Java架构师高并发高性能高可用分布式集群电商缓存性能调优设计项目实战视教程 置顶! 有更新!

(一)类定义、创建对象

class Person {

  var name:String =null;

  val sex:Int = 1;

  private[thisvar name2:String="john"

}

 

虽然我们只在Person类中定义了一个类成员(域)name,类型为String,但Scala会默认帮我们生成name()与name_=()及构造函数Person()。其中name()对应Java中的getter方法,name_=()对应java中的setter方法(由于JVM中不允许出现=,所以用$eq代替。值得注意的是定义的是公有成员,但生成的字节码中却是以私有的方式实现的,生成的getter、setter方法是公有的因此,可以直接new操作创建Person对象

从代码执行产生的结果,我们可以知道:通过p.name=“john”这种方式进行赋值,调用者并不需要知道是其通过方法调用还是字段访问来进行操作的,这便是著名的统一访问原则

val变量对应的是java中的final类型变量

如果将成员域定义为private[this],则不会生成getter、setter方法

java语言当中,在定义JavaBean的时候生成的都是setXxx()、getXxx()方法,但scala语言生成的getter方法和setter方法并不是这样的,如果也需要程序自动会生成getter方法和setter方法,则需要引入 scala.reflect.BeanProperty 
然后采用注解的方式修饰变量

  var name:String =null;

  val sex:Int = 1;

  private[thisvar name2:String="john";

  @BeanProperty var name3:String="john";

    p1.name3 = "chen.mulin";

    println(p1.getName3());

    p1.setName3("chen.jincheng");

下图给出的是getter、setter方法产生的规则 

(二)类主构造器

主构造器的定义与类的定义交织在一直,将构造器参数直接放在类名称之后,如下代码:

//下列代码不但定义了一个类Person,还定义了主构造器,主构造器的参数为String、Int类型

class Person2(val name:String,val age:Int)

//上面的代码与下列java语言编写的代码等同

public class Person{

  private final String name;

  private final int age;

  public Person(String name,int age){

       this.name=name;

       this.age=age;

  }

  public String getName(){ return name}

  public int getAge() {return age}

}

具体使用如下

    val p2 = new Person2("mulin",30);

类初始化过程如下:

//当在创建对象时,需要进行相关初始化操作时,可以将初始化语句放在类体中,同样也可以在类中添加或重写相关方法

class Person3(val name:String,val age:Int){ 

  //println将作为主构建器中的一部分,在创建对象时被执行

  println("constructing Person ........"

  //重写toString()方法 

  override def toString()= name + ":"age 

  

}

    val p3 = new Person3("C.MuLin",32);

    println(p3)

constructing Person ........

C.MuLin:32

前面我们定义的Person类是一种无参主构建器主构建器还可以使用默认参数

class Person4(val name:String="",val age:Int=18){

  println("constructing Person ........")

  override def toString()= name + ":"age

}

主构造器中的参数还可以加访问控制符

//默认参数的主构建器,参数带访问控制符号

//age变成私有成员,其getter方法是私有的,外部不能访问

class Person5(val name:String="",private val age:Int=18){

  println("constructing Person ........")

  override def toString()= name + ":"age

}

当主构造器的参数不用var或val修饰的时候,参数会生成类的私有val成员,并且不会产生getter和setter方法

//不加变量修饰符

class Person6(name:String,age:Int){

  println("constructing Person ........")

  override def toString()= name + ":"+ age

}

//与下面类定义等同

class Person61(private[thisval name:String,private[thisval age:Int){

  println("constructing Person ........")

  override def toString()= name + ":"+ age

}

值得注意的是,将上述Person类中的toString()方法去掉,则类中无任何地方使用了主构造器的参数,此时主构造器参数不会生成类成员

//不加变量修饰符

class Person(name:String,age:Int){

  println("constructing Person ........")

  override def toString()= name + ":"+ age

}

改成:

class Person( val name:String,age:Int){

  println("constructing Person ........")

}

下面图给出了Scala中主构建器参数生成类成员和方法时的规则 
 在某些情况下,可能需要禁用主构建器,代码如下:

//类名后面紧跟private关键字可以将主构建器设为私有,不允许外部使用

class Person private(var name:String,var age:Int){

  println("constructing Person ........")

}

(三)辅助构造函数

前面讲了,如果禁用掉了主构建器,则必须使用辅助构造函数来创建对象。辅助构造函数具有两个特点:(1)辅助构建器的名称为this,java中的辅助构造函数与类名相同,这常常会导致修改类名时出现不少问题,scala语言避免了这样的问题;(2)调用辅助构造函数时,必须先调用主构造函数或其它已经定义好的构造函数

n 只有辅助构造函数的Person类

在定义辅助构造函数时,需要注意构造函数的顺序否则编译不通过

//只有辅助构造函数的类

class Person{

  //类成员

  private var name:String=null

  private var age:Int=18

  private var sex:Int=0

 

  //辅助构造器

  def this(name:String){

    this()

    this.name=name

  }

  def this(name:String,age:Int){

    this(name)

    this.age=age

  }

   def this(name:String,age:Int,sex:Int){

    this(name,age)

    this.sex=sex

  }

}

n 带主构造函数、辅助构造函数的Person类

//具有主构建函数和辅助构建函数的Person类

class Person(var name:String,var age:Int){

  //类成员

  private var sex:Int=0

 

  //辅助构造器

   def this(name:String,age:Int,sex:Int){

    this(name,age)

    this.sex=sex

  }

}

在主构造函数小节当中我们提到,有时候可能会禁用掉主构造函数,此时只能通过辅助构造函数来创建对象

//禁用主构造函数

class Person private(var name:String,var age:Int){

  //类成员

  private var sex:Int=0

 

  //辅助构造器

   def this(name:String,age:Int,sex:Int){

    this(name,age)

    this.sex=sex

   }

 

}

 

(四)单例对象

在某些应用场景下,我们可能不需要创建对象,而是想直接调用方法,但是Scala语言并不支持静态成员,scala通过单例对象来解决该问题。单例对象的创建方式如下:

object Student {

  private var studentNo:Int=0;

  def uniqueStudentNo()={

    studentNo+=1

    studentNo

  }

  def main(args: Array[String]): Unit = {

    println(Student.uniqueStudentNo())

  }

}

object Student最终生成了两个类,分别是Student与Student,它们都是final类型的,而且Student的构造方法是私有的,通过静态成员域 public static final cn.scala.xtwy.Student$ MODULE$; 对Student$进行引用,这其实是Java语言中单例实现方式。 单例对象的使用方式同Java语言类引用静态成员是一样的。

(五)伴生对象与伴生类

在前面单例对象的基础之上,我们在object Student所在的文件内定义了一个class Student,此时object Student被称为class Student的伴生对象,而class Student被称为object Student的伴生类:

 

class Student(var name:String,age:Int)

 

object Student {

  private var studentNo:Int=0;

  def uniqueStudentNo()={

    studentNo+=1

    studentNo

  }

 &nbsnbsp;def main(args: Array[String]): Unit = {

    println(Student.uniqueStudentNo())

  }

}

 

其实伴生对象与伴生类本质上是不同的两个类,只不过伴生类与伴生对象之间可以相互访问到对主的成员包括私有的成员变量或方法,例如:

class Student(var name:String,var age:Int){

  private var sex:Int=0

  //直接访问伴生对象的私有成员

  def printCompanionObject()=println(Student.studentNo)

}

 

object Student {

  private var studentNo:Int=0;

  def uniqueStudentNo()={

    studentNo+=1

    studentNo

  }

  def main(args: Array[String]): Unit = {

    println(Student.uniqueStudentNo())

    val s=new Student("john",29)

    //直接访问伴生类Student中的私有成员

    println(s.sex)

  }

}

1

0

(六)apply方法

在前几节中我们提到,通过利用apply方法可以直接利用类名创建对象,例如前面在讲集合的时候,可以通过val intList=List(1,2,3)这种方式创建初始化一个列表对象,其实它相当于调用val intList=List.apply(1,2,3),只不过val intList=List(1,2,3)这种创建方式更简洁一点,但我们必须明确的是这种创建方式仍然避免不了new,它后面的实现机制仍然是new的方式,只不过我们自己在使用的时候可以省去new的操作。下面就让我们来自己实现apply方法,代码如下:

//定义Student类,该类称为伴生类,因为在同一个源文件里面,我们还定义了object Student

class Student(var name:String,var age:Int){

  private var sex:Int=0

  //直接访问伴生对象的私有成员

  def printCompanionObject()=println(Student.studentNo)

 

}

 

//伴生对象

object Student {

  private var studentNo:Int=0;

  def uniqueStudentNo()={

    studentNo+=1

    studentNo

  }

  //定义自己的apply方法

  def apply(name:String,age:Int)=new Student(name,age)

  def main(args: Array[String]): Unit = {

    println(Student.uniqueStudentNo())

    val s=new Student("john",29)

    //直接访问伴生类Student中的私有成员

    println(s.sex)

 

    //直接利用类名进行对象的创建,这种方式实际上是调用前面的apply方法进行实现,这种方式的好处是避免了自己手动new去创建对象

    val s1=Student("john",29)

    println(s1.name)

    println(s1.age)

  }

}

如果没有伴生对象的apply方法将会有如下编译错误:

 

 

(七)应用程序对象

利用IDE开发scala应用程序时,在运行程序时必须指定main方法作为程序的入口,例如:

object ObjectDemo {

  

  def main(args: Array[String]): Unit = {

     println("constructing Person ........")

  }

  

}

除了这种方式之外,scala还提供了一种机制,即通过扩展App,在Scala IDE for Eclipse里是通过new->scala app方式创建的

 

也可在代码直接指定:

object AppDeom extends App {

  println("hello app");

}

App其实是一种trait,它帮助我们定义了main方法。

(八)抽象类

抽象类是一种不能被实例化的类,抽象类中包括了若干不能完整定义的方法,这些方法由子类去扩展定义自己的实现。

abstract class Animal {

  def eat:Unit // Unit 就是 void

  def drink:Boolean

}

除抽象方法外,抽象类中还可以有抽象字段:

abstract class Animal {

  //抽象字段(域)

  //前面我们提到,一般类中定义字段的话必须初始化,而抽象类中则没有这要求

  var height:Int

  //抽象方法

  def eat:Unit

  

  def isHungry:Boolean

}

 

//Person继承Animal,对eat方法进行了实现

//通过主构造器对height参数进行了初始化

class Dog(var height:Int) extends Animal{

  //对父类中的方法进行实现,注意这里面可以不加override关键字

  def eat()={

    println("eat by mouth")

  }

  

  def isHungry():Boolean ={

    true

  }

  

}

 

//通过扩展App创建程序的入口

object DogMain extends App{

  new Dog(10).eat()

}

评论

发表评论

validate