####1 引言
我们知道java中面向对象的三大特性:封装(可以封装属性,方法等,只能在本类中访问),继承(子类继承父类),多态(父类引用指向之类对象),同样scala中也是面向对象的,下面让我们进行学习。
2 类
/*
1. 如果定义的属性是private的,scala会自动为其生成对应的set和get方法
private var stuAge:Int = 20
get方法:stuAge s2.stuAge()
set方法: stuAge_= s2.stuAge() = 25
2. 对于某些属性,只希望有get方法,不希望有set方法:可将属性申明成val(常量)
3. 定义 private[this] 属性,表明该属性只属于对象私有,只有本对象内可以访问到
*/
class People{
//定义属性
var name="";
val age=18;
//定义一个私有属性
private[this] val gender="male"
//定义一个方法
def eat(): String ={
name+" "+"eat foods"
}
//打印gender
def printInfo(): Unit = {
println("gender: " + gender)
}
}
object People{
def main(args: Array[String]): Unit = {
//创建一个people
val people = new People
// 为name赋值
people.name = "Messi"
//打印name和age
println(people.name + " : " + people.age)
println(people.eat)
//调用printInfo
people.printInfo()
//这里并不能获取人的gender,因为使用了peivate修饰,只能在本类中访问
//println(people.name + " : " + people.age + " : " + people.gender)
}
}
结果:
Messi : 18
Messi eat foods
gender: male
####3 构造器
- Scala中,主构造器(constructor)是与类名放在一起的,与java不同
- 在scala中,如果不指定主构造函数,编译器将创建一个主构造函数的构造函数。 所有类的主体的声明都被视为构造函数的一部分。它也被称为默认构造函数。
- 主构造器是直接定义在类名后面,主构造器中的参数最终会被编译成字段/属性
- 构造方法中参数不加var class Person(name: String, age: Int) 那么name age为私有的,则会声明为private[this]修饰
- 主构造器会执行类定义中的所有语句。如下,println语句是主构造器的一部分,当类被实例化时,println语句会立即执行。
- 构造器中如果定义了方法,那么会编译其方法,当类实例化时不会执行,只有当调用该方法的时候才执行。
- 附属构造器
- 语法: def this(…)
- 第一行代码:必须要调用已经存在的主构造器或者其他附属构造器
- Scala中,可以给类定义多个辅助constructor,类似于java中的构造函数重载
- 辅助constructor之间可以互相调用,而且必须第一行调用主constructor
- 每个辅助构造器都必须以一个对先前已定义的其他辅助构造器或主构造器的调用开始。
class Student(var name:String,var age:Int) {
println("Student constructor enter...")
//在主构造器中定义一个gender属性
//其中下画线代表占位符
/*
* scala> var a:String = _
* a: String = null
* scala> var b:Boolean = _
* b: Boolean = false
* scala> var c:Int = _
* c: Int = 0
* scala> var d:Double = _
* d: Double = 0.0
*/
var gender:String = _
//在构造器中定义一个方法
def printInfo(): Unit = {
println("printInfo invoked....")
}
/**
* 附属构造器
* 语法: def this(......)
* 第一行代码:必须要调用已经存在的主构造器或者其他附属构造器
* Scala中,可以给类定义多个辅助constructor,类似于java中的构造函数重载
* 辅助constructor之间可以互相调用,而且必须第一行调用主constructor
*/
def this(name:String,age:Int,gender:String){
this(name,age)
this.gender=gender
}
val school = "ustc"
println("Student constructor leave...")
}
object Student{
def main(args: Array[String]): Unit = {
val student1=new Student("zhangan",18)
print(student1.name+" "+student1.age+" "+student1.gender+" "+student1.school)
val student2=new Student("lisi",20,"male")
println(student2.gender)
}
}
结果:
Student constructor enter...
Student constructor leave...
zhangan 18 null ustc
Student constructor enter...
Student constructor leave...
male
####4 继承
- Scala中,让子类继承父类,与Java一样,也是使用extends关键字
- 继承:子类可以从父类继承父类的属性和方法;然后子类可以在自己内部放入父类所没有的属性和方法;
- 子类可以覆盖父类的**属性(val)**和方法;但是如果父类用final修饰,则该类是无法被继承的;如果父类中属性或者方法使用final修饰,不能被重写;
- 重写一个非抽象方法或者属性时必须使用override修饰符;
- 在子类中重写超类的抽象方法时,你不需要使用override关键字;
- 父类中已有的字段就不需要使用val/var修饰
class Animal {
//属性
val name:String = "name"
val color:String = "color"
final var str1:String="private String1"
//方法
def eat():Unit={
println(color+"的"+name+"在吃东西")
}
//父类特有的方法,不能被重写
final def printInfo():Unit={
println("我是父类...")
}
}
class Dog extends Animal{
//子类可以覆盖父类的val field
override val name:String="哈皮狗"
override val color:String="白色"
override def eat(): Unit = super.eat()
}
object Test{
def main(args: Array[String]): Unit = {
val dog =new Dog
//str1使用final修饰,不能使用
//dog.str1
dog.printInfo()
dog.eat()
}
}
查看结果:
我是父类...
白色的哈皮狗在吃东西
- 子类构造方法触发之前要先触发其父类的构造方法
/**
* 子类构造方法触发之前要先触发其父类的构造方法
*
* 父类中已有的字段就不需要使用val/var修饰
*/
class Animal(var name:String,var age:Int){
println("Animal constructor enter...")
//方法
def eat():Unit={
println(name+"在吃东西")
}
//父类特有的方法,不能被重写
final def printInfo():Unit={
println("我是父类...")
}
println("Animal constructor leave...")
}
class Dog(name:String,age:Int,var color:String) extends Animal(name,age){
println(s"$name constructor enter...")
override def eat(): Unit = {
println(
s"""
|my name is $name
|I am $age years old
|my color is $color
""".stripMargin)
}
println(s"$name constructor leave...")
}
object Test{
def main(args: Array[String]): Unit = {
//创建dog实例对象
var dog = new Dog("哮天犬",18,"黑色")
//调用重写的eat方法
dog.eat()
//调用父类的特有方法
dog.printInfo()
}
}
查看结果:
Animal constructor enter...
Animal constructor leave...
哮天犬 constructor enter...
哮天犬 constructor leave...
my name is 哮天犬
I am 18 years old
my color is 黑色
我是父类...
####5 伴生类,伴生对象
- 在 Scala 中,是没有 static 这个东西的,但是它也为我们提供了单例模式的实现方法,那就是使用关键字 object。
- Scala 中使用单例模式时,除了定义的类之外,还要定义一个同名的 object 对象,它和类的区别是,object对象不能带参数。
- 如果有一个class,还有一个与class同名的object,那么就称这个object是class的伴生对象,class是object的伴生类
- 伴生类和伴生对象必须存放在一个.scala文件之中
- 伴生类和伴生对象,最大的特点就在于,互相可以访问private field
/**
* 伴生类和伴生对象
*/
//伴生类
class Work(var name:String,var address: String) {
//半生类中的私有属性
private var phone="110"
//访问伴生对象中的私有属性
def classWork() = println("伴生类中访问伴生对象的私有属性:" + Work.id)
//伴生类中的test方法
def test(): Unit ={
println("class test")
}
}
//伴生对象
object Work{
//伴生类中的私有属性
private var id:String="01"
var count:Int=1
//add方法
def add()={
count += 1 //加1
count //返回sno
}
//半生对象中的static方法
def static(): Unit ={
println("object static....")
}
}
object Test1{
def main(args: Array[String]): Unit = {
/*
*伴生对象(object)中访问属性或者方法时直接用该对象调用即可,不需要new实例
*伴生类(class)需要new实例进行访问
*/
//伴生类中的static方法,直接使用Work.方法名
Work.static()
//演示单例模式
for (i<- 1 to 10){
Work.add()
}
//查看打印的结果证明是不是单利模式,如果是结果是11,不是结果为每一个对象加1
println("我是单利模式吗:"+Work.count)
//半生对象,需要new
var a = new Work("学生","家里蹲")
a.test()
//访问私有属性
a.classWork()
}
}
结果:
object static....
我是单利模式吗:11
class test
伴生类中访问伴生对象的私有属性:01
- 解读apply方法
- object中非常重要的一个特殊方法,就是apply方法
- 通常在伴生对象中实现apply方法,并在其中实现构造伴生类的对象的功能
- 而创建伴生类的对象时,通常不会使用new Class的方式,而是使用Class()的方式,隐式地调用伴生对象得apply方法,这样会让对象创建更加简洁。
//伴生类
class Work(var name:String,var address: String) {
//伴生类中的test方法
def test(): Unit ={
println("class test")
}
def apply() = {
println("class apply.....")
}
}
//伴生对象
object Work{
//半生对象中的static方法
def static(): Unit ={
println("object static....")
}
//实现apply方法,实例化伴生类
def apply(name: String,address: String)={
println("object apply.....")
new Work(name,address)
}
}
object Test1{
def main(args: Array[String]): Unit = {
/*
*伴生对象(object)中访问属性或者方法时直接用该对象调用即可,不需要new实例
*伴生类(class)需要new实例进行访问,但是如果伴生对象中实现apply方法也不需要new
*/
//伴生类中的static方法,直接使用Work.方法名
Work.static()
//实际是通过伴生对象中的apply方法进行了对象实例化,避免了手动new对象
// 伴生对象() ==> 调用的是伴生对象的apply的方法
val a=Work("程序猿","家里蹲")
//调用伴生类中的test方法
a.test()
println(a.name+" "+a.address)
//实例化伴生类
val b=new Work("架构师","阿里")
//调用伴生类中的test方法
b.test()
b()
/***
* 类名() ==> object apply ( val a=Work("程序猿","家里蹲"))
* new出来的对象() ==> class apply (b())
*/
}
}
结果:
object static....
object apply.....
class test
程序猿 家里蹲
class test
class apply.....
####6 抽象类
- 如果在父类中,有某些方法无法立即实现,而需要依赖不同的子来来覆盖,重写实现自己不同的方法实现。此时可以将父类中的这些方法不给出具体的实现,只有方法签名,这种方法就是抽象方法。
- 而一个类中如果有一个抽象方法,那么类就必须用abstract来声明为抽象类,此时抽象类是不可以实例化的
- 在子类中覆盖抽象类的抽象方法时,不需要使用override关键字
/**
* 抽象类
*
*/
abstract class Person2 {
def speak
val name:String
var age:Int
}
class Student2 extends Person2 {
def speak(): Unit ={
println("Student2 speak.....")
}
val name = "zhangsan"
var age = 18
}
object AbstractApp extends App {
val s = new Student2()
s.speak()
println(s.name + " : " + s.age)
}
结果:
Student2 speak.....
zhangsan : 18
####7 多态
- 一句话概括多态:父类引用指向子类对象
//抽象Person类
abstract class Person(var name:String,var age:Int){
def walk():Unit
//talkTo方法,参数为Person类型
def talkTo(p:Person):Unit
}
class Student(name:String,age:Int) extends Person(name,age){
private var studentNo:Int=0
def walk()=println("walk like a elegant swan")
//重写父类的talkTo方法
def talkTo(p:Person)={
println("talkTo() method in Student")
println(this.name+" is talking to "+p.name)
}
}
class Teacher(name:String,age:Int) extends Person(name,age){
private var teacherNo:Int=0
def walk()=println("walk like a elegant swan")
//重写父类的talkTo方法
def talkTo(p:Person)={
println("talkTo() method in Teacher")
println(this.name+" is talking to "+p.name)
}
}
object demo{
def main(args: Array[String]): Unit = {
//下面的两行代码演示了多态的使用
//Person类的引用可以指向Person类的任何子类
val p1:Person=new Teacher("albert",38)
val p2:Person=new Student("john",38)
//下面的两行代码演示了动态绑定
//talkTo方法参数类型为Person类型
//p1.talkTo(p2)传入的实际类型是Student
//p2.talkTo(p1)传入的实际类型是Teacher
//程序会根据实际类型调用对应的不同子类中的talkTo()方法
p1.talkTo(p2)
p2.talkTo(p1)
}
}
结果:
talkTo() method in Teacher
albert is talking to john
talkTo() method in Student
john is talking to albert