Scala 编程—第七节:类和对象(二)

前言:
        类和对象第二节,主要介绍:单例对象、伴生对象与伴生类、apply方法、抽象类

1.单例对象

Java语言中,如果想直接使用类名点的方式调用方法或属性,直接用static修饰即可。但Scala语言不支持静态成员,而提供了object对象,这个object对象类似于Java的静态类,object对象的成员、方法默认都是静态的。单例对象创建方式如下

object Student {
  private var studentNo: Int = 0
  def uniqueStudentNo() :Int = {
    studentNo = 1
    studentNo
  }

  def main(args: Array[String]): Unit = {
    println(Student.uniqueStudentNo())
  }
}

字节码文件

D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Student$
警告: 二进制文件Student$包含com.harvey.classandobject.Student$
Compiled from "Student.scala"
public final class com.harvey.classandobject.Student$ {
  public static final com.harvey.classandobject.Student$ MODULE$;
  private int studentNo;
  public static {};
  private int studentNo();
  private void studentNo_$eq(int);
  public int uniqueStudentNo();
  public void main(java.lang.String[]);
  private com.harvey.classandobject.Student$();
}
D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Student
警告: 二进制文件Student包含com.harvey.classandobject.Student
Compiled from "Student.scala"
public final class com.harvey.classandobject.Student {
  public static void main(java.lang.String[]);
  public static int uniqueStudentNo();
}

上述字节码文件中可以看到object Student 最终生成了两个类,分别是Student和Student$ ,它们都是 final 类型,且Student$ 的构造方法为私有,通过静态成员域 public static final com.harvey.classandobject.Student$ MODULE$ ; 对Student$ 进行引用,即java中的单例模式,私有化构造方法并提供静态方法,方法内初始化对象返回。

2.伴生对象与伴生类

在前面单例对象的基础上,在object student 所在的文件内定义一个class Student,此时,object student 被称为 class Studnet 的伴生对象,class student 被称为 object Student的伴生类,

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

object Student {
  private var studentNo:Int=0;
  def uniqueStudentNo()={
    studentNo+=1
    studentNo
  }
  def main(args: Array[String]): Unit = {
    println(Student.uniqueStudentNo())
  }
}

字节码文件

D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Student$
警告: 二进制文件Student$包含com.harvey.classandobject.Student$
Compiled from "Student.scala"
public final class com.harvey.classandobject.Student$ {
  public static final com.harvey.classandobject.Student$ MODULE$;
  private int studentNo;
  public static {};
  private int studentNo();
  private void studentNo_$eq(int);
  public int uniqueStudentNo();
  public void main(java.lang.String[]);
  private cn.scala.xtwy.Student$();
}
D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Student
警告: 二进制文件Student包含com.harvey.classandobject.Student
Compiled from "Student.scala"
public class com.harvey.classandobject.Student {
  private java.lang.String name;
  private int age;
  public static void main(java.lang.String[]);
  public static int uniqueStudentNo();
  public java.lang.String name();
  public void name_$eq(java.lang.String);
  public int age();
  public void age_$eq(int);
  public cn.scala.xtwy.Student(java.lang.String, int);
}

上述字节码文件中,可以看到伴生对象与伴生类本质上是两个不同的类,只不过伴生类与伴生对象之间可以相互访问到对方的成员(私有的成员变量和方法),如下

class Student(var name:String,age:Int) {
  private var sex:Int = 0
  // 访问伴生对象的私有成员 studentNo
  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())
  }
}

3.apply 方法

apply 方法可以直接使用类名创建对象,例如:前面第四节集合操作中,可以通过val list = (1, 2, 3)这种方式创建初始化一个列表对象,其实相当于调用了List的apply方法,即val list = List.apply(1, 2, 3),只不过val list = (1, 2, 3)这种方式更简洁一点,但要知道的是apply这种创建方式实际上使用的还是new的方式,只不过在操作的时候可以省去new的操作。下面我们来自己实现apply方法,代码如下:

// 伴生类
class Student(var name:String,age:Int) {
  private var sex:Int = 0
  // 访问伴生对象的私有成员 studentNo
  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())

    // 使用new创建对象
    val stu1 = new Student("tom", 18)
    println(stu1.sex)

    // 直接利用类名创建对象,实际上是调用上面的apply方法实现,好处是省去了new的操作
    val stu2 = Student("john", 20)
    println(stu2.name)
    println(stu2.sex)
  }
}

上述我们允许程序都需要指定main方法入口,Scala 中提供了一个App类,集成该类后,不用main方法,直接运行即可,如下

object AppDemo extends App{
  println("App")
}

App 源码

package scala
trait App extends scala.AnyRef with scala.DelayedInit {
  @scala.deprecatedOverriding("executionStart should not be overridden")
  val executionStart : scala.Long = { /* compiled code */ }
  @scala.deprecatedOverriding("args should not be overridden")
  protected def args : scala.Array[scala.Predef.String] = { /* compiled code */ }
  @scala.deprecated("The delayedInit mechanism will disappear.")
  override def delayedInit(body : => scala.Unit) : scala.Unit = { /* compiled code */ }
  @scala.deprecatedOverriding("main should not be overridden")
  def main(args : scala.Array[scala.Predef.String]) : scala.Unit = { /* compiled code */ }
}

上述代码可以看到App是一种trait,帮我们定义了main

4.抽象类

抽象类是一种不能被实例化的类,抽象类中包括了若干没有方法体的方法,这些方法由子类继承父类重写。定义抽象类Scala中同Java一样使用abstract关键字,如下定义抽象类Animal

abstract class Animal {
  def eat: Unit
}

字节码文件:

D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Animal
警告: 二进制文件Animal包含com.harvey.classandobject.Animal
Compiled from "Animal.scala"
public abstract class com.harvey.classandobject.Animal {
  public abstract void eat();
  public com.harvey.classandobject.Animal();
}

抽象类中也可以有抽象字段

// 抽象类
abstract class Animal {
  // 抽象字段,抽象类中的字段可以不用初始化
  var age: Int

  def eat: Unit
}

// Dog 集成 Animal,重写eat方法
class Dog(var age: Int) extends Animal {
  override def eat() = {
    println("dog eat food!")
  }
}

// 通过扩展App创建程序入口
object Dog extends App{
  new Dog(5).eat()
}

上述代码,会生成四个字节码文件,Animal.class、Dog$.class、Dog$delayedInit$body.class、Dog.class

// Animal 对应的字节码文件,字段对应的get/set方法均使用abstract修饰,即抽象
D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Animal
警告: 二进制文件Animal包含com.harvey.classandobject.Animal
Compiled from "Animal.scala"
public abstract class com.harvey.classandobject.Animal {
  public abstract int age();
  public abstract void age_$eq(int);
  public abstract void eat();
  public com.harvey.classandobject.Animal();
}
// Dog 伴生类对应字节码文件
D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Dog
警告: 二进制文件Dog包含com.harvey.classandobject.Dog
Compiled from "Animal.scala"
public class com.harvey.classandobject.Dog extends com.harvey.classandobject.Animal {
  private int age;
  public static void main(java.lang.String[]);
  public static void delayedInit(scala.Function0);
  public static java.lang.String[] args();
  public static void scala$App$_setter_$executionStart_$eq(long);
  public static long executionStart();
  public static void delayedEndpoint$com$harvey$classandobject$Dog$1();
  public int age();
  public void age_$eq(int);
  public void eat();
  public com.harvey.classandobject.Dog(int);
}
// Dog 伴生对象对应字节码文件
D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Dog$
警告: 二进制文件Dog$包含com.harvey.classandobject.Dog$
Compiled from "Animal.scala"
public final class com.harvey.classandobject.Dog$ implements scala.App {
  public static final com.harvey.classandobject.Dog$ MODULE$;
  private final long executionStart;
  private java.lang.String[] scala$App$$_args;
  private final scala.collection.mutable.ListBuffer> scala$App$$initCode;
  public static {};
  public long executionStart();
  public java.lang.String[] scala$App$$_args();
  public void scala$App$$_args_$eq(java.lang.String[]);
  public scala.collection.mutable.ListBuffer> scala$App$$initCode();
  public void scala$App$_setter_$executionStart_$eq(long);
  public void scala$App$_setter_$scala$App$$initCode_$eq(scala.collection.mutable.ListBuffer);
  public java.lang.String[] args();
  public void delayedInit(scala.Function0);
  public void main(java.lang.String[]);
  public final void delayedEndpoint$com$harvey$classandobject$Dog$1();
  private com.harvey.classandobject.Dog$();
}

你可能感兴趣的:(Scala,Scala,编程)