Cris 的 Scala 笔记整理(七):面向对象
7. 面向对象(重点)
7.1 Scala 面向对象基础
类
[修饰符] class 类名 {
类体
}
scala语法中,类并不声明为public,所有这些类都具有公有可见性(即默认就是public)
一个Scala源文件可以包含多个类
定义一个最简单的类
object Demo {def main(args: Array[String]): Unit = {var man = new Manman.name = "cris"man.age = 12println(man.name + "----" + man.age) // cris----12}}class Man {var name = ""var age = 0
}
复制代码
反编译对应的 class 文件
属性
属性是类的一个组成部分,一般是值数据类型,也可是引用类型
def main(args: Array[String]): Unit = {val man = new Man()val pc = new PCman.pc = pcman.pc.brand = "惠普"// man.pc().brand()println(man.pc.brand) // 惠普}class Man {var name = "" // 手动设置初始值,此时可以省略成员属性的数据类型声明var age = 0var pc: PC = _ // _ 表示让 Scala 自动赋默认值,此时声明带上成员属性的数据类型,否则编译器无法确定默认值
}class PC {var brand: String = _
}
复制代码
练习
针对 for(int i = 10;i>0;i--){System.out.println(i)} 翻译成 Scala 代码
object Practice {def main(args: Array[String]): Unit = {for (i <- 0.to(10).reverse) {print(i + "\t") // 10 9 8 7 6 5 4 3 2 1 0 }} } 复制代码
使用过程重写上面的 Scala 代码
def func(x: Int) {for (i <- 0 to x reverse) {print(i + "\t")} } 复制代码
编写一个for循环,计算字符串中所有字母的Unicode代码(toLong方法)的乘积。举例来说,"Hello"中所有字符串的乘积为9415087488L
def cal(str:String): Unit ={var result = 1Lfor(x <- str){result*=x.toLong}print(result) } 复制代码
使用 StringOps 的 foreach 方法重写上面的代码
var r2 = 1L // _ 可以理解为字符串的每一个字符 "Hello".foreach(r2 *= _.toLong) print(r2) 复制代码
使用递归解决上面求字符串每个字符 Unicode 编码乘积的问题
def recursive(str: String): Long = {if (str.length == 1) str.charAt(0).toLong/*drop(n)从索引为 1 开始切片到结尾*/else str.take(1).charAt(0).toLong * recursive(str.drop(1)) } 复制代码
编写函数计算 x^n,其中 n 是整数(负数,0,正数),请使用递归解决
def pow(x: Int, n: Int): Double = {if (n == 0) 1else if (n < 0) {1.0 / x * pow(x, n + 1)} else {x * pow(x, n - 1)} } 复制代码
对象
val | var 对象名 [:类型] = new 类型()
如果我们不希望改变对象的引用(即:内存地址), 应该声明为val 性质的,否则声明为var, scala设计者推荐使用val ,因为一般来说,在程序中,我们只是改变对象属性的值,而不是改变对象的引用
scala在声明对象变量时,可以根据创建对象的类型自动推断,所以类型声明可以省略,但当类型和后面new 对象类型有继承关系即多态时,就必须写了
方法
Scala中的方法其实就是函数,只不过一般将对象中的函数称之为方法
def 方法名(参数列表) [:返回值类型] = {
方法体
}
练习
嵌套循环打印图形
def func1(): Unit ={for (i <- 1 to 4; j <- 1 to 3) {if (j == 3) println("*")else print("*\t")} } 复制代码
计算矩形的面积
class Test {def area(): Double = {(this.width * this.length).formatted("%.2f").toDouble}var width: Double = _var length: Double = _ 复制代码
构造器
java 的构造器回顾
[修饰符] 方法名(参数列表){
构造方法体
}
在Java中一个类可以定义多个不同的构造方法,构造方法重载
如果程序员没有定义构造方法,系统会自动给类生成一个默认无参构造方法(也叫默认构造器)
3)一旦定义了自己的构造方法,默认的构造方法就覆盖了,就不能再使用默认的无参构造方法,除非显示的定义一下,即: Person(){}
Scala 构造器
和Java一样,Scala构造对象也需要调用构造方法,并且可以有任意多个构造方法。
Scala类的构造器包括: 主构造器 和 辅助构造器
基础语法
class 类名(形参列表) { // 主构造器
// 类体
def this(形参列表) { // 辅助构造器
}
def this(形参列表) { //辅助构造器可以有多个...
}
}
简单示例
abstract class Dog {var name = ""var age = 0val color: Stringdef this(name: String, age: Int) {this()this.name = namethis.age = age}def eat(): Unit = {println("吃狗粮")}def run()
}
复制代码
class Cat(var name: String, val color: String) {println("constructor is processing")def describe: String = name + "--" + color
}def main(args: Array[String]): Unit = {var cat = new Cat("tom", "gray")println(cat.describe)var cat2 = new Cat("jack", "red")println(cat2.describe)}
复制代码
细节
Scala构造器作用是完成对新对象的初始化,构造器没有返回值。
主构造器的声明直接放置于类名之后 [反编译]
主构造器会执行类定义中的所有语句,这里可以体会到Scala的函数式编程和面向对象编程融合在一起,即:构造器也是方法(函数),传递参数和使用方法和前面的函数部分内容没有区别
如果主构造器无参数,小括号可省略,构建对象时调用的构造方法的小括号也可以省略
辅助构造器名称为this(这个和Java是不一样的),多个辅助构造器通过不同参数列表进行区分, 在底层就是java的构造器重载,辅助构造器第一行函数体必须为 this.主构造器
abstract class Dog {var name = ""var age = 0val color: Stringdef this(name: String, age: Int) {this()this.name = namethis.age = age}def eat(): Unit = {println("吃狗粮")}def run()
}
复制代码
6)) 如果想让主构造器变成私有的,可以在()之前加上private,这样用户只能通过辅助构造器来构造对象了,说明:因为Person3的主构造器是私有,因此就需要使用辅助构造器来创建对象
class Car private(){}
复制代码
- 辅助构造器的声明不能和主构造器的声明一致,会发生错误
属性高级
Scala类的主构造器函数的形参未用任何修饰符修饰,那么这个参数是局部变量
如果参数使用val关键字声明,那么Scala会将参数作为类的私有的只读属性使用
如果参数使用var关键字声明,那么那么Scala会将参数作为类的成员属性使用,并会提供属性对应的xxx()[类似getter]/xxx_$eq()[类似setter]方法,即这时的成员属性是私有的,但是可读写
class Counter {/*1. 有公开的 getter 和 setter 方法*/var count = 0/*2. 私有化 getter 和 setter,可以手动提供 setter 和 getter*/private var number = 1/*3. 只能被访问getter,无法修改setter,final 修饰的 age 属性*/val age = 12/*4. 对象级别的私有*/private[this] var length = 12def compare(other: Counter): Boolean = other.number > number// def compareLength(other: Counter): Boolean = length > other.lengthdef increase(): Unit = {number += 1}/*无参方法可以省略(),{}也可以省略*/def current: Int = number
}def main(args: Array[String]): Unit = {var c = new Counter()c.count = 3println(c.count) // 3c.increase()println(c.current) // 2println(c.age) // 12
}
复制代码
如果在主构造器中为属性设置了默认值,那么就不必在函数体内再去声明属性以及赋值了,大大简化代码的书写
def main(args: Array[String]): Unit = {val dog = new Dog()println(dog.name) // crisprintln(dog.age) // 10}
}class Dog(var name :String= "cris",var age:Int = 10){}
复制代码
JavaBean 注解
JavaBeans规范定义了Java的属性是像getXxx()和setXxx()的方法。许多Java工具(框架)都依赖这个命名习惯。为了Java的互操作性。将Scala字段加@BeanProperty时,这样会自动生成规范的 setXxx/getXxx 方法。这时可以使用 对象.setXxx() 和 对象.getXxx() 来调用属性
给某个属性加入@BeanPropetry注解后,会生成getXXX和setXXX的方法
并且对原来底层自动生成类似xxx(),xxx_$eq()方法,没有冲突,二者可以共存
对象创建流程分析
请针对以下代码简述对象创建流程
class Bike {var brand = ""var color = ""def this(brand: String, color: String) {thisthis.brand = brandthis.color = color}
}def main(args: Array[String]): Unit = {var bike = new Bike("ofo", "黄色")
}
复制代码
加载类信息(属性信息,方法信息)
在堆中,给对象开辟空间
调用主构造器对属性进行初始化
使用辅助构造器对属性进行初始化
把对象空间的地址,返回给 bike 引用
7.2 面向对象进阶
包(难点)
回顾 Java 的包知识
作用
区分相同名字的类
当类很多时,可以很好的管理类
控制访问范围
打包基本语法
package com.cris;
打包的本质分析
实际上就是创建不同的文件夹来保存类文件
示例代码
先在不同的包下建立同名的类
如果想要在一个类中同时使用上面的两个 Pig,Java 的解决方式如下:
public static void main(String[] args) {Pig pig1 = new Pig();cris.package2.Pig pig2 = new cris.package2.Pig(); // pig1.getClass() = class cris.package1.PigSystem.out.println("pig1.getClass() = " + pig1.getClass()); // pig2.getClass() = class cris.package2.PigSystem.out.println("pig2.getClass() = " + pig2.getClass());} 复制代码
再来看看我们的源码所在路径和字节码文件所在路径,都是一一对应的
Java 要求源码所在路径和字节码文件所在路径必须保持一致,如果我们此时去修改源码的打包路径
基本语法
import java.awt.* or import java.util.List
注意事项:java中包名和源码所在的系统文件目录结构要一致,并且编译后的字节码文件路径也和包名保持一致
接着看看 Scala 是如何处理的
我们使用 Scala 重写上面的 Java 包案例
def main(args: Array[String]): Unit = {var b1 = new cris.package1.Bird1var b2 = new cris.package2.Bird2// class cris.package1.Bird1println(b1.getClass)// class cris.package2.Bird2println(b2.getClass)
}
复制代码
此时我们如果修改了 Bird1 的打包路径
再看看源代码和字节码文件所在的路径
Scala 的包
和Java一样,Scala中管理项目可以使用包,但Scala中的包的功能更加强大,使用也相对复杂些
基本语法 package 包名
Scala包的三大作用(和Java一样)
- 区分相同名字的类
- 当类很多时,可以很好的管理类
- 控制访问范围
Scala中包名和源码所在的系统文件目录结构要可以不一致,但是编译后的字节码文件路径和包名会保持一致(这个工作由编译器完成)
图示
命名规范
只能包含数字、字母、下划线、小圆点.,但不能用数字开头, 也不要使用关键字
一般是小写字母+小圆点一般是
com.公司名.项目名.业务模块名
Scala 自动 import 的包有:java.lang.*,scala,Predef 包
Scala 打包细节(难点)
常用的两种打包形式
源代码的路径和字节码文件路径保持一致
源代码的路径和字节码文件路径不一致
上面的演示中已经很清楚的展示了 Scala 包的这一特点,我们继续用下面代码演示 Scala 包的嵌套
我们在 Detail 类文件中写入以上非常奇怪的代码,编译运行后再查看源代码和字节码文件的位置
进一步印证了 Scala 中源文件和字节码文件路径可以不一致
包也可以像嵌套类那样嵌套使用(包中有包), 见上面图示。好处是:程序员可以在同一个文件中,将类(class / object)、trait 创建在不同的包中,非常灵活
作用域原则:可以直接向上访问。即: Scala中子包中直接访问父包中的内容, 大括号体现作用域。(提示:Java中子包使用父包的类,需要import)。在子包和父包 类重名时,默认采用就近原则,如果希望指定使用某个类,则带上包名即可
示例代码
package com.cris {class Apple {}package scala {class Apple {}object Boy {def main(args: Array[String]): Unit = {/*1. Scala 中子包可以直接访问父包的内容;2. 子包和父包的类重名,默认采取就近原则;3. 可以带上类的路径名指定使用该类*/val apple = new Appleval apple2 = new com.cris.Apple// class com.cris.scala.Appleprintln(apple.getClass)// class com.cris.Appleprintln(apple2.getClass)}}} } 复制代码
父包要访问子包的内容时,需要import对应的类
package com.cris {import com.cris.scala.Appleobject Apple{def main(args: Array[String]): Unit = {// 推荐只在使用的时候再引用,控制作用域import com.cris.scala.Appleval apple = new Apple() // class com.cris.scala.Appleprintln(apple.getClass)}}package scala {class Apple {}} }- 复制代码
可以在同一个.scala文件中,声明多个并列的package(建议嵌套的pakage不要超过3层)
包对象
基本介绍:包可以包含类、对象和特质trait,但不能包含函数或变量的定义。这是Java虚拟机的局限。为了弥补这一点不足,scala提供了包对象的概念来解决这个问题
参见如下代码
package com.cris {// 不能直接在 package 中定义函数和变量// var name = "cris"/*** 包对象的名字需要和包名一致* package object emp 会在 com.cris.emp 包下生成 package.class 和 package$.class*/package object emp {def eat(): Unit = {println("eat")}val salary = 1000.0}package emp {object test {def main(args: Array[String]): Unit = {eat() // eat=》等价于使用了 package$.class 中的 MODULE$.eat()println(salary) // 1000.0=> 等价于使用了 package$.class 中的 MODULE$.salary()}}}
}
复制代码
使用反编译工具打开瞧瞧
具体的执行流程第二章节已经解释过,这里不再赘述
注意事项:
- 每个包都可以有一个包对象,但是需要在父包中定义它
- 包对象名称需要和包名一致,一般用来对包(里面的类)的功能做补充
包的可见性
在Java中,访问权限分为: public,private,protected和默认。在Scala中,你可以通过类似的修饰符达到同样的效果。但是使用上有区别
当属性访问权限为默认时,从底层看属性是private的,但是因为提供了xxx_$eq()[类似setter]/xxx()[类似getter] 方法,因此从使用效果看是任何地方都可以访问)
当方法访问权限为默认时,默认为public访问权限
private为私有权限,只在类的内部和伴生对象中可用
示例:
protected为受保护权限,scala中受保护权限比Java中更严格,只能子类访问,同包无法访问
在scala中没有public关键字,即不能用public显式的修饰属性和方法。
包访问权限(表示属性有了限制。同时增加了包的访问权限),这点和Java不一样,体现出Scala包使用的灵活性
包的引入
细节说明
在Scala中,import语句可以出现在任何地方,并不仅限于文件顶部,import语句的作用一直延伸到包含该语句的块末尾。这种语法的好处是:在需要时在引入包,缩小import 包的作用范围,提高效率
示例如下:
Java中如果想要导入包中所有的类,可以通过通配符*,Scala中采用下 _
如果不想要某个包中全部的类,而是其中的几个类,可以采用选取器(大括号)
如果引入的多个包中含有相同的类,那么可以将不需要的类进行重命名进行区分,这个就是重命名
或者使用 import java.util.{HashMap => _ } 对冲突的包进行隐藏
练习
编写一个Time类,加入只读属性hours和minutes,和一个检查某一时刻是否早于另一时刻的方法before(other:Time):Boolean。Time对象应该以new Time(hrs,min)方式构建
object Practice {def main(args: Array[String]): Unit = {val time1 = new Time(4, 12)val result = time1.before(new Time(4, 14))println(result)} }class Time(val hour: Int, val minute: Int) {def before(other: Time) = {if (this.hour < other.hour) trueelse if (this.hour > other.hour) falseelse if (this.hour == other.hour) {if (this.minute < other.minute) trueelse if (this.minute > other.minute) falseelse false}} } 复制代码
创建一个Student类,加入可读写的JavaBeans属性name(类型为String)和id(类型为Long)。有哪些方法被生产?(用javap查看。)你可以在Scala中调用JavaBeans的getter和setter方法吗?
object Practice {def main(args: Array[String]): Unit = {var s = new Studentprintln(s.getName)println(s.age)} }class Student {@BeanProperty var name = "好学生"@BeanProperty var age = 0} 复制代码
编写一段程序,将Java哈希映射中的所有元素拷贝到Scala哈希映射。用引入语句重命名这两个类
object Ex extends App {import java.util.{HashMap => JavaHashMap}import scala.collection.mutable.{HashMap => ScalaHashMap}var map1 = new JavaHashMap[Int, String]()map1.put(1, "cris")map1.put(2, "james")map1.put(3, "simida")var map2 = new ScalaHashMap[Int, String]()for (key <- map1.keySet().toArray()) { // key 的数据类型是 AnyRef// asInstanceOf 强制数据类型转换map2 += (key.asInstanceOf[Int] -> map1.get(key))}println(map2.mkString("||")) // 2 -> james||1 -> cris||3 -> simida} 复制代码
抽象
我们在前面去定义一个类时候,实际上就是把一类事物的共有的属性和行为提取出来,形成一个物理模型(模板)。这种研究问题的方法称为抽象
示例代码
object Demo extends App {var account = new Account("招行:888888", 200, "123456")account.query("123456")account.save("123456", 100)account.query("123456")account.withdraw("123456", 250)account.query("123456")}class Account(val no: String, var balance: Double, var pwd: String) {def query(pwd: String): Unit = {if (pwd != this.pwd) {println("密码错误!")} else {println(s"卡号:${this.no},余额还有:${this.balance}")}}def save(pwd: String, money: Double): Unit = {if (pwd != this.pwd) {println("密码错误")} else {this.balance += moneyprintln(s"卡号:${this.no},存入:${money},余额为:${this.balance}")}}def withdraw(pwd: String, money: Double): Unit = {if (pwd != this.pwd) {println("密码错误")} else if (money > this.balance) {println("余额不足")} else {this.balance -= moneyprintln(s"卡号:${this.no},取出:${money},余额为:${this.balance}")}}
}
复制代码
Cris 的 Scala 笔记整理(七):面向对象相关推荐
- Cris 的 Scala 笔记整理(九):面向对象高级
9. 面向对象高级 9.1 静态属性和静态方法 ① 回顾 Java 的静态概念 public static 返回值类型 方法名(参数列表) {方法体} Java 中静态方法并不是通过对象调用的,而是通 ...
- Cris 的 Scala 笔记整理(八):面向对象中级-封装
封装 从数据的角度:封装 (encapsulation) 就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作 从模 ...
- Cris 的 Scala 笔记(三):变量
文章目录 3.变量 3.1 基本概念 3.2 数据类型 3.3 数据类型体系图 3.4 整数类型 3.5 浮点数据类型 3.6 字符类型(Char) 3.7 布尔类型 3.8 Unit类型.Null类 ...
- Cris 的 Scala 笔记(五):流程控制
文章目录 5. 流程控制 5.1 分支控制 单分支 双分支 多分支 分支控制if-else 注意事项 5.2 for循环控制 范围数据循环方式1 范围数据循环方式2 循环守卫 引入变量 嵌套循环 循环 ...
- 【Beetl笔记整理七】格式化
版权声明:本文为 小异常 原创文章,非商用自由转载-保持署名-注明出处,谢谢! 本文网址:https://sunkuan.blog.csdn.net/article/details/114743052 ...
- Java笔记整理七(网络编程,TCP通信程序,函数式接口,方法引用)
网络编程入门 1.软件结构 C/S结构 :全称为Client/Server结构,是指客户端和服务器结构.常见程序有QQ.迅雷等软件. B/S结构 :全称为Browser/Server结构,是指浏览器和 ...
- JavaScript之面向对象与原型笔记整理--------创建对象之原型(2)
4.原型 每个函数都有一个prototype属性,这个属性是一个对象,用途是包含可以由特定类型的所有实例共享的属性和方法. 逻辑上可以这么理解:prototype通过调用构造函数而创建的那个对象的原型 ...
- Deep Learning(深度学习)学习笔记整理系列之(七)
Deep Learning(深度学习)学习笔记整理系列 zouxy09@qq.com http://blog.csdn.net/zouxy09 作者:Zouxy version 1.0 2013-04 ...
- 慕课Java第三季学习及笔记整理
学习网址及截图和部分图片来源:https://www.imooc.com/learn/110 慕课Java第三季学习及笔记整理 一.异常与异常处理 1-1 java异常简介 概念 异常体系结构 1-2 ...
最新文章
- 第四章:Spring项目文件上传两种方式(全解析)
- excel pandas 空格_Pandas写入excel:1)索引中的空白行;2)每天保存索引
- python爬虫案例-Python爬虫案例集合
- vbs复制自己到tmp目录
- 【NLP】Transformer大家庭简介!
- 从操作系统层面分析Java IO演进之路
- 米莱狄机器人怎么那么多_米莱狄究竟应该怎么玩?
- 公安人像(证照)比对接口简介
- 同学使用计算机存在的问题,计算机应用基础教学问题及应对策略
- 学生如何提高专业英文阅读能力(转自施一公博客)
- 苹果查看电池实际容量
- 2022ICPC预选赛 A Yet Another Remainder(数学)(构造)
- 动漫线稿怎么画才流畅
- 函数对称性常见公式_函数的各种对称性
- 聚点 内部 内点 导集
- 前端/后端(FE / BE)
- 使用background 的url 引入背景图片失效的问题
- 电子数据取证之网站分析和重构基础
- 关于AI视觉-百度大脑EdgBoard边缘计算盒
- STM32移植BME680传感器输出IAQ(室内空气质量)
热门文章
- 插入(insert)
- 我的 2020,总结与告别
- Android实现文字垂直滚动
- 测试项目经理推荐的Java 并发测试神器
- Java内存分析工具MAT(Memory Analyzer Tool)的介绍与使用
- dnf7月7日服务器维护,dnf7月7日早7点/8点半停机更新公告
- [计算机]防止电脑进入锁屏
- linux su 不能输密码错误,su - root正确输入密码但是登录不了系统,报错su: Permission denied...
- 什么是Pyc?Pyc的作用是什么?
- NONMEN软件概览及数据文件