scala面向对象总结
scala面向对象总结。
- Java是面向对象语言,但存在着非面向对象内容:基本类型、null,静态方法等;
- scala是天生面向对象语言,一切皆对象
语法总结:
- scala类默认是public的,不必声明
- 一个源文件可以有多个类的声明
类的修饰符
查看简单示例:
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方法:
- get方法和属性名一样
- set方法为:属性_$eq()
- 并且是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)
对象创建流程
- 加载类信息(属性、方法)
- 在堆中给对象开辟空间
- 调用主构造器对属性进行初始化
- 使用辅助构造器进行初始化
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;}
}
抽象类
- abstract关键字修饰class
- 抽象方法不能用abstract关键字修饰
- 方法也可实现,字段也可初始化
scala类层级关系
- 所有类都是AnyRef的子类,类似java的Object
- AnyVal和AnyRef都是Any的子类,Any是根节点
- Any中定义了isInstanceOd, asInstanceOf以及hash
- Null类型的唯一实例就是null对象,可以将null赋给任何引用,但不能赋给值类型的变量
- 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...")
}
- 声明类时同时混入特质
class F6 extends E6 with C6 with D6 {println("F6...")
}val f1 = new F6
输出:
E6...
A6...
B6...
C6...
D6...
F6...
- 构建对象时动态混入
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面向对象总结相关推荐
- 2021年大数据常用语言Scala(二十八):scala面向对象 MAVEN依赖和类
目录 scala面向对象 MAVEN依赖 类 - 掌握 创建类和对象 - 掌握 getter/setter - 了解 类的构造器 - 掌握 scala面向对象 MAVEN依赖 <?xml ver ...
- educoder Scala面向对象编程
第一关: 编程要求 本关的编程任务是补全huawei类中从 Phone 类继承的函数,以使得程序运行结果如预期输出.根据提示,在右侧编辑器补充代码. 测试说明 下面是对平台如何评测你所实现功能 ...
- Scala核心编程 第六章—Scala面向对象编程(一)
佛家有云:一花一世界,一叶一菩提,而我们所学的Java就是一个以面向对象为基础的程序语言,在Java中是万物皆为对象,但是在Scala中万事万物皆为对象,由此引出了Scala面向对象编程,接下来,让我 ...
- 3000门徒内部训练绝密视频(泄密版)第2课:Scala面向对象彻底精通及Spark源码阅读
Scala面向对象彻底精通及Spark源码阅读 不用写public class中的public class Person {private var myName = "flink" ...
- Scala 面向对象编程
一 定义一个简单的类 1.1定义类,包含field以及方法 classHelloWorld { private var name = "leo" def sayHello() { ...
- 重学scala:scala面向对象编程之类与对象
文章目录 一.类 1.类的定义 2.类的构造器 二.对象 1.scala中的object 2.scala中的伴生对象 3.scala中object的apply方法 4.scala中object的mai ...
- Scala面向对象基础--类和对象
一.类和对象介绍 在Scala里,类是用关键字"class"开头的代码定义,它是用于创建对象的蓝图.一个类就是一个类型,不同的类就是不同的类型,一个对象的类型就是创建它用的那个类. ...
- 2021年大数据常用语言Scala(三十一):scala面向对象 特质(trait)
目录 特质(trait) 作为接口使用 定义具体的方法 定义具体方法和抽象方法 定义具体的字段和抽象的字段 实例对象混入trait trait调用链 trait的构造机制 trait继承class 特 ...
- 2021年大数据常用语言Scala(二十九):scala面向对象 单例对象
目录 单例对象 定义object - 掌握 伴生对象 - 掌握 apply方法 - 掌握 main方法 单例对象 Scala中没有static关键字,但是它支持静态 如果要定义静态的东西,统统定义到o ...
最新文章
- 使用java进行erlang字符解析
- 虚机如果要访问SAN中的多个LUN,如何实现高可用
- 基于jQuery的表单验证插件:jValidate
- 跨平台2D/3D游戏开发框架libGDX发布1.2.0更新
- inux系统中如何进入退出vim编辑器
- 【Tensorflow】卷积神经网络实现艺术风格化通过Vgg16实现
- 洛谷P2089 烤鸡 题解
- 老铁,邀请你来免费学习人工智能!!!
- 大数据技术之kafka (第 3 章 Kafka 架构深入 ) 消费者组案例
- 给年份year,定义一个宏,以判别该年份是否闰年。提示:宏名可以定义为LEAP_YEAR,形参为y,既定义宏的形式为 #define LEAP_YEAR(y) (读者设计的字符串)
- python取列表中位数_Python如何获取列表(List)的中位数
- TensorFlow在美团外卖推荐场景的GPU训练优化实践
- 如何列出引用SQL Server中给定表的所有外键?
- 计算机组成原理 陈泽,计算机组成原理 课程设计计算机组成原理 课程设计.doc...
- QT学习之做一个简易图片处理器(后续补全)
- 信奥赛一本通 C++题解 2041【例5.9】新矩阵
- @Cacheable和@CacheEvict的学习使用
- 在网站的地址栏中的显示个性图标
- Java HashSet
- 香港途径巴黎转机至波哥大