前言:
类和对象第二节,主要介绍:单例对象、伴生对象与伴生类、apply方法、抽象类
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中的单例模式,私有化构造方法并提供静态方法,方法内初始化对象返回。
在前面单例对象的基础上,在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())
}
}
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
抽象类是一种不能被实例化的类,抽象类中包括了若干没有方法体的方法,这些方法由子类继承父类重写。定义抽象类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$();
}