1. 类的基本操作
1.1. 类的定义
object _01ClassOps {
def main(args: Array[String]): Unit = {
/*
scala中类对应对象的构建,和java一模一样 使用关键字new,
以及构造函数来完成实例的创建
scala可以使用默认的无参构造器完成实例创建,
同时一类如果有无参构造器,在创建对象的时候,可以省略掉()
*/
val p = new Person()
// val p1 = new Person
// p.name =
p.setName("刘嘉豪")
p.setAge(169)
p.show()
}
}
class Person {
/*
_的作用就相当于java中private String name代表默认值;
如果要使用_,前面的成员变量就应该指定数据类型
*/
private var name:String = _
private var age = 13
def show(): Unit = {
println(s"name=${name}\tage=${age}")
}
def setName(name:String):Unit = {
//成员变量和局部变量命名冲突之后使用this关键字来进行区分
this.name = name
}
def getName() = this.name
//单行函数
// def setAge(age:Int) = this.age = age
def setAge(age:Int): Unit = {
if(age < 0 || age > 150) {
throw new RuntimeException("地球不欢迎你~")
}
this.age = age
}
def getAge = this.age
}
1.2. 类的构造
scala中有两类构造器——主构造器和辅助构造器。
object _02ConstructorOps {
def main(args: Array[String]): Unit = {
val p = new Person("华勇", 23)
p.show()
}
}
class Person() {
private var name:String = _
private var age:Int = _
def Person(): Unit = {
println("--Person()--是默认的构造器吗?")
}
//自定义构造器 自定义构造器的第一句话必须要调用其它构造器
def this(name:String, age:Int) {
this()
this.name = name
this.age = age
println("---------this(name:String, age:Int)---------")
}
println("-------------构造器~----------------")
def show(): Unit = {
println(s"name=${name}\tage=${age}")
}
}
本例中的这个和类的定义交织在一起的默认的构造器,是scala类中最重要的一个构造器——主构造器(默认的构造器是无参主构造器,定义在类名和类的{之间,省略了())。
其余的自定义构造器,也就是在类中通过def this(xxx)定义的构造器称之为辅助构造器,同时辅助构造器的第一句话,必须要通过this来调用本类主构造器或者其它辅助构造器。
object _02ConstructorOps {
def main(args: Array[String]): Unit = {
val _3p = new Person(77)
_3p.show()
}
}
class Person(var name:String, var age:Int) {
def Person(): Unit = {
println("--Person()--是默认的构造器吗?")
}
def this() {
this("嘿嘿", 33)
println("---this() ---")
}
def this(name:String) {
this()
this.name = name
println("---this(name:String) ---")
}
def this(age:Int) {
this("龚天明")
this.age = age
println("---this(age:Int) ---")
}
println("-------------构造器~----------------")
def show(): Unit = {
println(s"name=${name}\tage=${age}")
}
}
-------------构造器~----------------
—this() —
—this(name:String) —
—this(age:Int) —
name=龚天明 age=77
1.3. 内部类
object _03InnerClassOps {
def main(args: Array[String]): Unit = {
val outer = new Outer
val inner = new outer.Inner()
inner.show()
}
}
class Outer { ooo =>
val x = 5
class Inner { iii =>
val x = 6
def show(): Unit = {
val x = 7
System.out.println("x = " + x)
System.out.println("inner class x = " + iii.x)
System.out.println("Outer class x = " + ooo.x)
}
}
}
总结:scala的内部类和java的内部类的主要区别就在内部类对象的创建方式不一样。其次scala为了灵活的在内部类的局部访问内部类成员或者外部类的成员,可以给内部类和外部类的引用提供一个别名,方便操作。
也就是说,上例中这iii.x <=> this.x, ooo.x <=> Outer.this.x
内部类使用蛮多的一个地方:就是匿名内部类。
1.4. object对象
1.给scala类提供程序运行的入口,静态的main函数
2.给scala类也来提供静态成员——scala类的伴生对象来实现。
object中的所有的成员都是static静态
1.4. 单例
object _05ObjectOps {
def main(args: Array[String]): Unit = {
val s1 = Singleton.getInstance()
val s2 = Singleton.getInstance()
println(s1 == s2)
}
}
//单例
class Singleton private () {
var x = 5
}
/* 饿汉式
object Singleton {
private val instance = new Singleton
def getInstance(): Singleton = {
instance
}
}
*/
//懒汉式
object Singleton {
private var instance:Singleton = _
def getInstance(): Singleton = {
if(instance == null) {
Singleton.synchronized {
if(instance == null) {
instance = new Singleton
}
}
}
instance
}
}
1.5. 伴生对象
scala伴生对象的作用
1、给伴生类提供了类似于java中的静态成员
2、可以给scala的class提供一种新的对象构造方式
3、 在伴生对象中创建一个名为apply的方法,该apply方法的参数列表为class的构造函数的参数列表,二者保持对应
4、 返回值为本类,通过该apply方法来完成对象的构造
5、伴生对象,不仅可以访问对应伴生类的非私有成员,同时还可以访问对应伴生类的私有成员
object _06ObjectOps {
def main(args: Array[String]): Unit = {
val worker = Worker()
worker.show()
println("--------------")
val w1 = Worker("柳泽宁", 15)
w1.show()
}
}
class Worker(name: String, age: Int) {
def this() {
this("old李", 35)
}
def this(name: String) {
this()
}
def show(): Unit = {
println(s"name: ${name}, age: ${age}")
}
}
object Worker {//伴生对象
def apply(): Worker = {
new Worker()
}
def apply(name: String, age: Int): Worker = {
new Worker(name, age)
}
}
2. 类的继承体系
类与类之间的一个很重要关系——继承/扩展(extends)。
2.1. 类的扩展
继承的特点:
- 子类可以继承父类的所有非私有(private),非静态的成员(变量和成员方法)。
- 可以对父类的相关方法进行覆盖/重写
- 也可以添加自己独有的成员
- 被final修饰的父类成员,子类不可以继承
- 被protected修饰的父类成员,子类可以继承
- 子类覆盖父类的方法的异常,必须要大于等于父类的异常
- 子类的访问权限必须要大于等于父类
class Person {
private var name:String = _
protected var age:Int = 0
def this(name:String, age:Int) {
this()
this.name = name
this.age = age
}
def show(): Unit = {
println(s"person's name is $name")
}
}
class Student extends Person {
age = 15
def this(age:Int) {
this()
this.age = age
}
override def show(): Unit = {
super.show()//子类调用父类的成员通过super关键字
println(s"Student's age is $age")
}
}
person’s name is null
Student’s age is 18
override在java中是一个注解,用来表示该方法是继承的,scala中是一个关键字,必须要添加在重写的方法前面,除非该方法是抽象的。
2.2. 类型检查和转换
使用isInstanceOf来进行类型判断,asInstanceOf进行类型转换。需要注意的是这两个操作都是对象的方法。
object _02EqualsOps {
def main(args: Array[String]): Unit = {
val w1 = new Worker("雷德言", 33)
val w2 = new Worker("雷德言", 33)
println(w1.equals(w2))
}
}
class Worker {
private var name:String = _
private var age:Int = _
def this(name:String, age:Int) {
this()
this.age = age
this.name = name
}
override def equals(obj: Any): Boolean = {
if(!obj.isInstanceOf[Worker]) //类型检查
false
else {//类型转换
val that = obj.asInstanceOf[Worker]
this.name.eq(that.name) && this.age == that.age
}
}
}
同时scala提供另一种更加简洁的写法,来处理这些类型检查和类型转换——模式匹配
override def equals(obj: Any): Boolean = {
obj match {//模式匹配
case that:Worker => {
this.name.eq(that.name) && this.age == that.age
}
case _ => false
}
}
2.3. 受保护字段和方法
被修饰为protected的age字段,不能在子类中被访问,但是通过scala的特定访问权限修饰设置,
- 在访问修饰符后面使用[]指定要访问的package,比如protected[extendz]——代表了该字段只能在extendz包及其子包下面被访问。
- 这实际上是更加精准的访问权限控制。
- private后面也可以加[extendz],和上面类似
object _01ProtectedOps {
def main(args: Array[String]): Unit = {
val stu = new Student
val person = new Person()
person.name = "清华学姐"
person.age = 23
stu.makeFriends(person)
}
}
class Person {
var name: String = "刘照路"
protected[extendz] var age: Int = 18
// protected[this] var age: Int = 18
private[extendz] var gender = "guy"
def show(): Unit = {
println(s"${name}, age: ${age}, gender: ${gender}")
}
}
class Student extends Person {
name = "刘照路2"
age = 19
def makeFriends(person: Person): Unit = {
println(s"姓名为:${this.name}, 年龄为${this.age}的小哥哥和${person.name}, 年龄为:${person.age}的小姐姐成为了红颜知己~")
}
}
被protected[this]所修饰的变量,只能在本类,及其子类中被调用,但不可以被子类对象调用。
2.4 访问权限修饰符
scala中的访问权限修饰符就只有这么两个:private、protected,缺省相当于public
2.5 子类调用父类构造
super.show()调用父类的方法
子类只能通过主构造器来调用父类的构造器。
object _01ExtendsOps {
def main(args: Array[String]): Unit = {
val stu = new Student("李烨培")
stu.show()
}
}
class Person {
private var name:String = _
protected var age:Int = 0
println("父类Person的主构造器-------")
def this(name:String, age:Int) {
this()
this.name = name
this.age = age
println("----父类Person的辅助构造器this(name:String, age:Int)------")
}
def this(name:String) {
this(name, 0)
this.name = name
this.age = age
println("----父类Person的辅助构造器this(name:String)------")
}
def show(): Unit = {
println(s"person's name is $name")
}
}
class Student(name:String) extends Person(name) {
println("子类Student的主构造器")
age = 15
def this(age:Int) {
this("zhangsan")
this.age = age
println("----子类Student的辅助构造器------")
}
override def show(): Unit = {
super.show()
println(s"Student's age is $age")
}
}
父类Person的主构造器-------
----父类Person的辅助构造器this(name:String, age:Int)------
----父类Person的辅助构造器this(name:String)------
子类Student的主构造器
person’s name is 李烨培
Student’s age is 15
2.5. 重写字段和方法
说白了就是在字段前面加上一个override关键字即可。最主要的作用就是父类中定义的非私有非protected的字段,子类如果想要定义个同名的字段,此时就需要使用override关键字完成覆盖,如果不用报错。
被覆盖的字段只能被val修饰。
方法的覆盖和java的覆盖是一样的。
2.6 匿名子类
匿名子类,其实说白了就是没有名字的类,通常匿名的类就是只调用一次的,没有必要进行定义,在运行时完成创建即可。
object _03AnonymousSubClassOps {
def main(args: Array[String]): Unit = {
fire(new Manager("黄世仁"))
println("---------------------------------------")
val m = new Manager("黄世仁"){
override def manage(): Unit = {
super.manage()
println("还有升级版:克扣工资")
}
def haha(): Unit = {
println("哈哈哈")
}
}
m.haha()
fire(m)
}
def fire(manage: Manager): Unit = {
manage.manage()
println("所以要炒了经理的鱿鱼~")
}
}
class Manager(name: String) {
def manage(): Unit = {
println(s"行为为${name}的经理正在对员工吆五喝六~")
}
}
2.7抽象类和抽象方法、字段
- scala中的抽象类 抽象方法 抽象字段(这在java中不存在)
- 一个类中的方法或者字段只有定义,没有实现,那么吧这些方法或者字段称之为抽象的方法和字段,
- 而又抽象方法或者字段的类,我们称之为抽象类,用abstract关键字来进行修饰
- 同样,抽象方法和字段的abstract关键字可以省略,而类上面abstract可不能省略。
- 子类如果复写了父类的抽象的成员,是可以省略掉override关键字的,但是复写非抽象成员该关键字必须要加。
object _04AbstractOps {
def main(args: Array[String]): Unit = {
val cat = new Cat()
cat.sleep()
println("------------------------------")
val fish = new Fish()
fish.sleep()
}
}
/**
* 定义一个抽象类
*/
abstract class Animal {
val leg: Int
def sleep():Unit
def dead(): Unit = {
println("动物固有一死,或清蒸,或红烧,或爆炒,这真的是太难了~")
}
}
class Cat extends Animal {
val leg: Int = 4
def sleep(): Unit = {
println("喵星人,蜷缩成一团睡觉~")
}
}
class Horse extends Animal {
override val leg: Int = 4
override def sleep(): Unit = {
println("站着睡觉的还有谁!")
}
}
class Fish extends Animal {
override val leg: Int = 0
override def sleep(): Unit = {
println("睁着眼睛睡觉,太难了我~")
}
}
2.8 trait特质
- trait特质:
- 不管是java还是scala的继承,有一个特点:只能单继承,不能多继承,在java中来弥补这个缺陷怎么解决:
- 1、可以多层继承
- 2、进行多实现(implements interface)
- 在多实现这个接口的时候,比较痛苦,如果接口方法比较少还好,但是如果方法很多,实现起来非常的麻烦
- 所以scala有了trait这样一个概念来弥补java中的interface只能由抽象方法的缺陷
- 所以scala中的trait比java中的接口interface内容更加的丰富,既可以拥有抽象,也可以有拥有非抽象,当trait特质中的所有
- 方法都抽象的时候,我们就可以理解其为java中的接口。
- trait在进行调用的时候和scala的类的调用一样,都要使用关键字extends,而java的接口实现用implements
- java的实现多接口的时候通过多个,来进行分割。很遗憾scala扩展多个trait的时候使用关键字with来进行分割。
- 比java中的接口还牛的一点是,可以做到动态的运行时的扩展特质————特质的混入
- 如果scala的一个类,既要扩展一个类,又要扩展特质,那么先写类,其次用with连接trait特质
object _05TraitOps {
def main(args: Array[String]): Unit = {
val logger = new ConsoleLogger
logger.log("roma was not built in one day~")
println("--------------------------")
val fileName = "E:/data/out/scala/fileLogger.txt"
val fLoger = new FileLogger(fileName) with MySerializable
fLoger.log("god help those who help themselves.")
fLoger.log("love is blind.")
fLoger.close()
fLoger.serialize("书到用时方恨少,绝知此事要躬行。")
}
}
trait Logger {
def log(msg: String): Unit
def show(): Unit = {
println("show~")
}
}
trait MySerializable {
//完成了序列化
def serialize(msg: String): Unit = {
println("serilaize: " + msg)
}
}
class ConsoleLogger extends Logger {
override def log(msg: String): Unit = {
println("consoleLogger: " + msg)
}
}
class FileLogger(fileName: String) extends Logger /*with MySerializable */{
private val bw = new FileWriter(fileName, true)
override def log(msg: String): Unit = {
bw.write(msg)
bw.write("\r\n")
bw.flush()
}
def close(): Unit = {
if(bw != null) {
bw.close()
}
}
}