scala面向对象总结。

  1. Java是面向对象语言,但存在着非面向对象内容:基本类型、null,静态方法等;
  2. scala是天生面向对象语言,一切皆对象

语法总结:

  1. scala类默认是public的,不必声明
  2. 一个源文件可以有多个类的声明

类的修饰符

查看简单示例:

object Demo01 {def main(args: Array[String]): Unit = {var p1 = new Personp1.name = "jimo"p1.age = 18println(p1.name, p1.age)}
}class Person {var name: String = _var age: Int = 0
}

查看编译后的class文件:

public class Person {private String name;public String name() {return this.name;}public void name_$eq(String x$1) {this.name = x$1;}private int age = 0;public int age() {return this.age;}public void age_$eq(int x$1) {this.age = x$1;}
}

也就是说,scala已经生成了get、set方法:

  1. get方法和属性名一样
  2. set方法为:属性_$eq()
  3. 并且是public的

创建对象

对象的内存分配

方法调用机制

构造器

Java里构造器重点就是通过方法表示;可以重载;有一个默认无参构造,自己声明了就没有了。

scala构造器分类:主构造器,辅助构造器(形参个数不同)

主构造器:

class 类名(形参列表) { // 主构造器 def this(形参列表1){} // 辅助构造器def this(形参列表2){} // 辅助构造器
}

实例:

  def main(args: Array[String]): Unit = {val jimo = new Student("jimo", 18)jimo.hello()}class Student(name: String, ageIn: Int) {var age: Int = ageInprintln("hello 1")def hello(): Unit = {println("hello,I am " + name + ",age = " + age)}println("hello 2")age += 10
}
// hello 1
// hello 2
// hello,I am jimo,age = 28

scala类里可以写任何语句,这是和java不同的地方,那原理是什么呢?
通过编译后的class文件可以看出:

public class Student {private final String name;private int age;public Student(String name, int ageIn) {this.age = ageIn;Predef$.MODULE$.println("hello 1");Predef$.MODULE$.println("hello 2");age_$eq(age() + 10);}public int age() {return this.age;}public void age_$eq(int x$1) {this.age = x$1;}public void hello() {Predef$.MODULE$.println((new StringBuilder()).append("hello,I am ").append(this.name).append(",age = ").append(BoxesRunTime.boxToInteger(age())).toString());}
}

这些语句实际上是放在构造方法里执行的。

构造器的重载

辅构造器必须调用主构造器(间接调用也可以)

class Student(name: String, ageIn: Int) {def this(name: String) {this(name, 0)}def this(age: Int) {this(null, age)}def this() {this("jimo")}
}

私有化主构造器

用于不让使用者new 一个实例

class Teacher private() {def this(name: String) {this()}
}

属性

先看以下声明:

class P1(name: String) {println(name)
}class P2(val name: String) {println(name)
}class P3(var name: String) {println(name)
}

生成的不同在于:是否会变成成员变量;
val和var变量代表是否可读

public class P1 {public P1(String name) {Predef$.MODULE$.println(name);}
}public class P2 {private final String name;public String name() {return this.name;}public P2(String name) {Predef$.MODULE$.println(name);}
}public class P3 {private String name;public String name() {return this.name;}public void name_$eq(String x$1) {this.name = x$1;}public P3(String name) {Predef$.MODULE$.println(name());}
}

Bean属性

由于javaBeans规范了getX()和setX()等规范方法,所以很多框架采用(mybatis),由于需要反射,所以
需要应用规范。

class P4 {var name: String = _@BeanPropertyvar age: Int = 0
}val p = new P4()
p.setAge(19)
println(p.getAge)

对象创建流程

  1. 加载类信息(属性、方法)
  2. 在堆中给对象开辟空间
  3. 调用主构造器对属性进行初始化
  4. 使用辅助构造器进行初始化
class P5 {var name: String = "hehe"def this(name: String) {this()println(this.name)this.name = nameprintln(this.name)}
}new P5("jimo")
输出:
hehe
jimo

继承

scala里重写父类非抽象方法必须加 override 修饰。

构造器的调用

class J {var name: String = _println("J...")
}class JI extends J {def this(name: String) {thisthis.name = nameprintln("JI...")}
}new JI()
println("=========")
new JI("jimo")

结果:

J...
=========
J...
JI...

scala里:子类的主类和辅助构造器不能直接调用父类的构造器;
只有通过类声明时直接调用。

class K(name: String) {println("K")
}class KI(name: String) extends K(name = name) {println("KI")def this() {this(null)println("KI:this")}
}new KI()
println("=========")
new KI("jimo")

结果:

K
KI
KI:this
=========
K
KI

类型检查与转换

classOf[T]

    println(classOf[String])println(classOf[P])val s = "s"println(s.getClass.getName)

isInstanceOf

    val s = "s"println(s.isInstanceOf[String])

asInstanceOf

class P {
}class PI extends P {
}val pi = new PI
val p: P = pi.asInstanceOf[P]
println(p.isInstanceOf[P], p.isInstanceOf[PI])

输出:

class java.lang.String
class com.jimo.scala.oop.P
java.lang.String
true
(true,true) // p既是子类,也是父类

字段隐藏与覆写

java的字段隐藏

java中没有字段的覆写,只有方法的覆写。

但是可以模拟覆写的效果,原因是隐藏机制。

public class JavaOverrideDemo {public static void main(String[] args) {Sub sub = new Sub();System.out.println(sub.s);Super sub2 = new Sub();System.out.println(sub2.s);System.out.println(((Super) sub).s);}
}class Super {String s = "super";
}class Sub extends Super {String s = "sub";
}
/*
sub
super
super
*/

scala字段覆写

object ScalaOverrideDemo {def main(args: Array[String]): Unit = {val a: AA = new BBval b: BB = new BBprintln("a.age=" + a.age)println("b.age=" + b.age)}
}class AA {val age: Int = 10
}class BB extends AA {override val age: Int = 20
}
/*
a.age=20
b.age=20
*/

原理很简单,看看生成的class文件:

public class AA {private final int age = 10;public int age() {return this.age;}
}public class BB extends AA {private final int age = 20;public int age() {return this.age;}
}

也就是java的方法重写。

val字段只可以重写父类的val字段,或者 同名的无参方法

为啥不能重写 var 修饰的字段?
因为,如果重写父类的var变量,那么就会生成 field_$eq的设值方法,
也就是说子类会继承这个方法,常量拥有set方法显然不行。

object ScalaOverrideDemo {def main(args: Array[String]): Unit = {val a: AA = new BBval b: BB = new BBprintln("a.name=" + b.name)println("b.name=" + b.name)}
}class AA {def name(): String = {"AA"}
}class BB extends AA {override val name: String = "BB"
}
/*
a.name=BB
b.name=BB
*/

那什么时候可以重写 var 修饰的字段呢?

在抽象类中的字段:

abstract class CC {var height: Int
}class DD extends CC {override var height: Int = 20
}

原理很简单,字段在抽象类里压根就不生成,只有抽象方法:

public abstract class CC {public abstract int height();public abstract void height_$eq(int paramInt);
}public class DD extends CC {private int height = 20;public int height() {return this.height;}public void height_$eq(int x$1) {this.height = x$1;}
}

抽象类

  1. abstract关键字修饰class
  2. 抽象方法不能用abstract关键字修饰
  3. 方法也可实现,字段也可初始化

scala类层级关系

  1. 所有类都是AnyRef的子类,类似java的Object
  2. AnyVal和AnyRef都是Any的子类,Any是根节点
  3. Any中定义了isInstanceOd, asInstanceOf以及hash
  4. Null类型的唯一实例就是null对象,可以将null赋给任何引用,但不能赋给值类型的变量
  5. Nothing类没有实例,对于泛型有用处,例如:空列表Nil和类型是List[Nothing],他是List[T]的子类型
    var n: Long = nullprintln(n)
Error:(5, 19) an expression of type Null is ineligible for implicit conversionvar n: Long = null
Error:(5, 19) type mismatch;found   : Null(null)required: Longvar n: Long = null

scala伴生对象–解决静态问题

按理说,scala是纯OOP的语言,不应该有静态对象和静态属性。

但是,必须要有静态概念,因为要和java打通。

于是,scala通过【伴生对象】来模拟类对象。

  def main(args: Array[String]): Unit = {println(CCC.name)}/*** 当类名和object名称一样,且放在同一个scala文件中* 1.object CCC 为伴生对象* 2.class CCC 为伴生类* 3.将静态内容写在伴生对象里,普通属性写在伴生类*/
object CCC {var name: String = "jimo"
}class CCC {var sex: Boolean = true
}

通过查看class文件可以理解实现原理:

public class CCC {private boolean sex = true;public static void name_$eq(String paramString) {CCC$.MODULE$.name_$eq(paramString);}public static String name() {return CCC$.MODULE$.name();}public boolean sex() {return this.sex;}public void sex_$eq(boolean x$1) {this.sex = x$1;}
}public final class CCC$ {public static final CCC$ MODULE$;private String name;public String name() {return this.name;}public void name_$eq(String x$1) {this.name = x$1;}private CCC$() {MODULE$ = this;this.name = "jimo";}
}

实际上,scala没有生成静态内容,只不过将伴生对象生成了一个新的类,实现属性的get方法调用。

单例对象

Java版

Java中的单例模式:饿汉式,懒汉式。

优化之后的版本:

public class Singleton {private Singleton() {}private static class SingletonInstance {private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance() {return SingletonInstance.INSTANCE;}
}

scala版本

scala直接用object就是单例的

  def main(args: Array[String]): Unit = {println(SSingleton.name)println(SSingleton.hello())}object SSingleton {var name: String = "hello"def hello(): Unit = {}
}

但底层如何实现呢?虚拟机里没有object的,编译查看class文件:

public final class SSingleton {public static void hello() {SSingleton$.MODULE$.hello();}public static void name_$eq(String paramString) {SSingleton$.MODULE$.name_$eq(paramString);}public static String name() {return SSingleton$.MODULE$.name();}
}public final class SSingleton$ {public static final SSingleton$ MODULE$;private String name;public String name() {return this.name;}public void name_$eq(String x$1) {this.name = x$1;}public void hello() {}private SSingleton$() {MODULE$ = this;this.name = "hello";}
}

特质与接口

scala里没有接口,使用特质来代替接口的概念。

多个类具有相同的特征,就可以抽象出来。

trait可看作对继承的一种补充

trait的使用

  • 没有父类:class 类名 extends 特质1 with 特质2 with …
  • 有父类:class 类名 extends 父类 with 特质1 with 特质2 with …
trait Move {def fly(): Unit
}abstract class Plane {}class Airliner extends Plane with Move {override def fly(): Unit = {println("客机使用翅膀飞行")}
}class Helicopter extends Move {override def fly(): Unit = {println("直升机使用螺旋桨飞行")}
}

查看编译后的class文件:可以看到变成了接口

public interface Move {void fly();
}public class Helicopter implements Move {public void fly() {Predef$.MODULE$.println(");}
}public class Airliner extends Plane implements Move {public void fly() {Predef$.MODULE$.println(");}
}

trait里实现方法

trait既可以定义抽象方法,也可以实现默认方法。

trait TA {def hello(): Unit = {println("TA hello()")}def go(): Unit
}class TAI extends TA {override def go(): Unit = {println("TAI go()")}
}

看看怎么实现的:

public interface TA {void hello();void go();
}
public abstract class TA$class {public static void $init$(TA $this) {}public static void hello(TA $this) {Predef$.MODULE$.println("TA hello()");}
}public class TAI implements TA {public void hello() {TA$class.hello(this);}public TAI() {TA$class.$init$(this);}public void go() {Predef$.MODULE$.println("TAI go()");}
}

可以看出同时生成了一个接口,还有一个抽象类,抽象类负责实现默认方法,
然后被调用。

java中的接口都可以当作trait使用

经过上面本质的分析,trait就是接口和抽象类的组合,所以当然可以使用java的接口。

trait Serializable extends Any with java.io.Serializable

trait的动态混入(mixin)

  • 不继承特质,但又想使用特质的功能:可以在创建对象时(抽象类也可以)混入特质
  def main(args: Array[String]): Unit = {val b = new MysqlDB with TBb.insert(1)}trait TB {def insert(id: Int): Unit = {println("插入数据=" + id)}
}class MysqlDB {}

看看如何实现:

  public void main(String[] args) {MysqlDB b = new TraitDemo03$$anon$1();((TB)b).insert(1);}public final class TraitDemo03$$anon$1 extends MysqlDB implements TB {public void insert(int id) {TB$class.insert(this, id);}public TraitDemo03$$anon$1() {TB$class.$init$(this);}}

看来是生成了一个匿名类。

  • 动态混入的优点:可以在不影响原始类的情况下加功能,耦合性很低
new BB with TB {override def haha(): Unit = {}
}trait TB {def insert(id: Int): Unit = {println("插入数据=" + id)}
}abstract class BB {def haha(): Unit
}

叠加特质

看一个多层次的继承

class Mysql {}trait Op {println("Op...")def insert(id: Int): Unit
}trait Data extends Op {println("Data...")override def insert(id: Int): Unit = {println("插入数据+" + id)}
}trait DB extends Data {println("DB...")override def insert(id: Int): Unit = {println("向数据库 ")super.insert(id)}
}trait File extends Data {println("File...")override def insert(id: Int): Unit = {println("向文件 ")super.insert(id)}
}

然后看看调用:

  def main(args: Array[String]): Unit = {val mysql = new Mysql with DB with File
//    mysql.insert(1)}

先来看看构建顺序:

  • 构建顺序与声明的顺序一样(从左至右)

看看结果:

Op...
Data...
DB...
File...

接下来看看执行顺序:

mysql.insert(1)

执行顺序:

  • 刚好是相反的,只要查找到一个父类的方法就执行
向文件
向数据库
插入数据+1

这里我就很好奇,为什么 insert方法只执行了一遍?

  • 解释:scala特质中如果使用了super,并不表示调用方法,而是向前面(左边)继续查找特质,如果找不到,才会去父特质查找

看一下class:

  public void main(String[] args) {Mysql mysql = new TraitDemo04$$anon$1();((File)mysql).insert(1);}public final class TraitDemo04$$anon$1 extends Mysql implements DB, File {public void insert(int id) {File$class.insert(this, id);}public TraitDemo04$$anon$1() {Op$class.$init$(this);Data$class.$init$(this);DB$class.$init$(this);File$class.$init$(this);}}public abstract class File$class {public static void insert(File $this, int id) {Predef$.MODULE$.println("向文件 ");$this.com$jimo$scala$oop2$File$$super$insert(id);}
}

其实class也没有透露更多细节,发现mysql实际上是最右边的特质File的实例,当File的insert
执行到 super.insert时,会继续找到 DB的insert执行,再执行 super.insert 发现左边已经没有
特质了,所以执行父特质 Data 的insert方法。

指定父类执行

叠加特质时可以指定调用父类的特质方法

trait File extends Data {println("File...")override def insert(id: Int): Unit = {println("向文件 ")super[Data].insert(id)//    super.insert(id)}
}

这时候发现结果就只剩2句话了:

向文件
插入数据+1

特质的抽象方法实现

注意abstract, 不加会报错

trait Op {println("Op...")def insert(id: Int): Unit
}
trait File2 extends Op {abstract override def insert(id: Int): Unit = {println("File2...")super.insert(id)}
}

富接口

特质中既有抽象方法,又有已经实现的方法。

trait中字段的继承

object TraitDemo05 {def main(args: Array[String]): Unit = {val mysql = new Mysql5 with Op5 {override var opType: String = "mysql-insert"}println(mysql.opType)val mysql1 = new Mysql5 with DB5println(mysql1.opType)}
}trait Op5 {var opType: String
}trait DB5 extends Op5 {override var opType: String = "db-insert"
}class Mysql5 {}

实际上是作为了类的一个属性。

 public void main(String[] args) {Mysql5 mysql = new TraitDemo05$$anon$2();scala.Predef$.MODULE$.println(((Op5)mysql).opType());Mysql5 mysql1 = new TraitDemo05$$anon$1();scala.Predef$.MODULE$.println(((DB5)mysql1).opType());}public final class TraitDemo05$$anon$2 extends Mysql5 implements Op5 {private String opType = "mysql-insert";public String opType() {return this.opType;}public void opType_$eq(String x$1) {this.opType = x$1;}}public final class TraitDemo05$$anon$1 extends Mysql5 implements DB5 {private String opType;public String opType() {return this.opType;}@TraitSetterpublic void opType_$eq(String x$1) {this.opType = x$1;}public TraitDemo05$$anon$1() {DB5$class.$init$(this);}}private TraitDemo05$() {MODULE$ = this;}
}

trait构造的执行顺序

首先看看继承关系:

trait A6 {println("A6...")
}trait B6 extends A6 {println("B6...")
}trait C6 extends B6 {println("C6...")
}trait D6 extends B6 {println("D6...")
}class E6 {println("E6...")
}
  1. 声明类时同时混入特质
class F6 extends E6 with C6 with D6 {println("F6...")
}val f1 = new F6

输出:

E6...
A6...
B6...
C6...
D6...
F6...
  1. 构建对象时动态混入
class K6 extends E6 {println("K6...")
}val k = new K6 with C6 with D6
println(k)

输出:

E6...
K6...
A6...
B6...
C6...
D6...

可以理解为:第二种在混入特质时,对象已经创建了。

trait继承类

可以扩展类的功能。

object TraitDemo07 {def main(args: Array[String]): Unit = {val log = new LogExp {}log.log()}
}trait LogExp extends Exception {def log(): Unit = {println(getMessage)}
}

总结

创建对象的方式有几种

  • new
  • apply
  • 动态方式
  • 匿名子类

说说apply的方式:通过伴生对象实现,为什么要伴生对象呢?

因为,伴生对象可以访问类的所有属性,包括私有的。

  def main(args: Array[String]): Unit = {val a = AAA.apply()println(a)}class AAA {}object AAA {def apply(): AAA = new AAA()
}

scala面向对象总结相关推荐

  1. 2021年大数据常用语言Scala(二十八):scala面向对象 MAVEN依赖和类

    目录 scala面向对象 MAVEN依赖 类 - 掌握 创建类和对象 - 掌握 getter/setter - 了解 类的构造器 - 掌握 scala面向对象 MAVEN依赖 <?xml ver ...

  2. educoder Scala面向对象编程

    第一关: 编程要求     本关的编程任务是补全huawei类中从 Phone 类继承的函数,以使得程序运行结果如预期输出.根据提示,在右侧编辑器补充代码. 测试说明 下面是对平台如何评测你所实现功能 ...

  3. Scala核心编程 第六章—Scala面向对象编程(一)

    佛家有云:一花一世界,一叶一菩提,而我们所学的Java就是一个以面向对象为基础的程序语言,在Java中是万物皆为对象,但是在Scala中万事万物皆为对象,由此引出了Scala面向对象编程,接下来,让我 ...

  4. 3000门徒内部训练绝密视频(泄密版)第2课:Scala面向对象彻底精通及Spark源码阅读

    Scala面向对象彻底精通及Spark源码阅读 不用写public class中的public class Person {private var myName = "flink" ...

  5. Scala 面向对象编程

    一 定义一个简单的类 1.1定义类,包含field以及方法 classHelloWorld { private var name = "leo" def sayHello() { ...

  6. 重学scala:scala面向对象编程之类与对象

    文章目录 一.类 1.类的定义 2.类的构造器 二.对象 1.scala中的object 2.scala中的伴生对象 3.scala中object的apply方法 4.scala中object的mai ...

  7. Scala面向对象基础--类和对象

    一.类和对象介绍 在Scala里,类是用关键字"class"开头的代码定义,它是用于创建对象的蓝图.一个类就是一个类型,不同的类就是不同的类型,一个对象的类型就是创建它用的那个类. ...

  8. 2021年大数据常用语言Scala(三十一):scala面向对象 特质(trait)

    目录 特质(trait) 作为接口使用 定义具体的方法 定义具体方法和抽象方法 定义具体的字段和抽象的字段 实例对象混入trait trait调用链 trait的构造机制 trait继承class 特 ...

  9. 2021年大数据常用语言Scala(二十九):scala面向对象 单例对象

    目录 单例对象 定义object - 掌握 伴生对象 - 掌握 apply方法 - 掌握 main方法 单例对象 Scala中没有static关键字,但是它支持静态 如果要定义静态的东西,统统定义到o ...

最新文章

  1. 使用java进行erlang字符解析
  2. 虚机如果要访问SAN中的多个LUN,如何实现高可用
  3. 基于jQuery的表单验证插件:jValidate
  4. 跨平台2D/3D游戏开发框架libGDX发布1.2.0更新
  5. inux系统中如何进入退出vim编辑器
  6. 【Tensorflow】卷积神经网络实现艺术风格化通过Vgg16实现
  7. 洛谷P2089 烤鸡 题解
  8. 老铁,邀请你来免费学习人工智能!!!
  9. 大数据技术之kafka (第 3 章 Kafka 架构深入 ) 消费者组案例
  10. 给年份year,定义一个宏,以判别该年份是否闰年。提示:宏名可以定义为LEAP_YEAR,形参为y,既定义宏的形式为 #define LEAP_YEAR(y) (读者设计的字符串)
  11. python取列表中位数_Python如何获取列表(List)的中位数
  12. TensorFlow在美团外卖推荐场景的GPU训练优化实践
  13. 如何列出引用SQL Server中给定表的所有外键?
  14. 计算机组成原理 陈泽,计算机组成原理 课程设计计算机组成原理 课程设计.doc...
  15. QT学习之做一个简易图片处理器(后续补全)
  16. 信奥赛一本通 C++题解 2041【例5.9】新矩阵
  17. @Cacheable和@CacheEvict的学习使用
  18. 在网站的地址栏中的显示个性图标
  19. Java HashSet
  20. 香港途径巴黎转机至波哥大

热门文章

  1. 2018.8.18梦中的凶杀案
  2. 注册微信小程序的操作步骤
  3. Bzoj4698: [Sdoi2008]Sandy的卡片
  4. deny后加to do还是doing_常见的后面只能接to do的词组(不能接doing)有哪些
  5. vs本地调试dll文件
  6. 如何选择SCI期刊并能顺利发表
  7. 程序员爬取 5000+ 口红商品数据,差点比女朋友更懂口红?
  8. 快速选择(QuickSelect)的平均时间复杂度分析
  9. VLAN中SVL和IVL的区别
  10. 网点分布图怎么做,用地图制作客户分布图