6、面向对象编程
6.1、类与对象
->
张老太养了只猫猫:一只名字叫小白,今年3岁,白色。还有一只叫小花,今年10岁,花色。请编写一个程序,当用户输入小猫的名字时,就显示该猫的名字,年龄,颜色。如果用户输入的小猫名错误,则显示 张老太没有这只猫猫。
//问题
猫有三个属性,类型不一样.
如果使用普通的变量就不好管理
使用一种新的数据类型((1) 可以管理多个不同类型的数据 [属性]) (2) 可以对属性进行操作-方法
因此类与对象
6.1.1、Scala语言是面向对象的
1、Java是面向对象的编程语言,由于历史原因,Java中还存在着非面向对象的内容:基本类型 ,null,静态方法等。
2、Scala语言来自于Java,所以天生就是面向对象的语言,而且Scala是纯粹的面向对象的语言,即在Scala中,一切皆为对象。
3、在面向对象的学习过程中可以对比着Java语言学习
6.1.2、快速入门-面向对象的方式解决养猫问题
object CatDemo {
def main(args: Array[String]): Unit = {
// 创建一个猫
val cat = new Cat
// 给猫的属性赋值
// 说明
// 1. cat.name=“小白” 其实不是直接访问属性,而是 cat.name_$eq("小白")
// 2. cat.name 等价于 cat.name()
cat.name = "小白" // 等价
cat.age = 10
cat.color = "白色"
println("ok···")
printf("\n小猫的信息如下:%s,%d,%s",cat.name,cat.age,cat.color)
}
}
// 定义一个类
// 一个class Cat 对应的字节码文件只有一个Cat.class,默认是public
class Cat {
// 声明3个属性
// 说明
// 1.当我们声明了 var name :String 时,在底层对应private name
// 2.同时会生成 两个public方法 name() <=类似=> getter name_$et() => setter
var name:String = "" // 给初始值
var age:Int = _ // _ 表示给age 一个默认的值,如果是Int,默认就是0
var color:String = _ // _给 color 默认值,如果时String,默认就是""
}
6.1.3、类与对象的区别和联系
通过上面的案例和讲解我们可以看出:
1、类是抽象的,概念的,代表一类事物,比如人类,猫类…
2、对象是具体的,实际的,代表一个具体事物
3、类是对象的模板,对象是类的一个个体,对应一个实例
4、Scala中类和对象的区别和联系 和 Java是一样的。
6.1.4、如何定义类
->基本语法
[修饰符] class 类名 {
类体
}
定义类的注意事项
scala语法中,类并不声明为public,所有这些类都具有公有可见性(即默认就是public),[修饰符在后面再详解].
一个Scala源文件可以包含多个类.
6.1.5、属性
基本介绍
案例演示:
class Dog{
var name = "jack"
var lover = new Fish
}
属性是类的一个组成部分,一般是值数据类型,也可是引用类型。比如我们前面定义猫类 的 age 就是属性
6.1.6、属性/成员变量
注意事项和细节说明
1)属性的定义语法同变量,示例:[访问修饰符]var 属性名称[:类型] = 属性值
2)属性的定义类型可以为任意类型,包括值类型或引用类型[案例演示]
3)Scala中声明一个属性,必须显示的初始化,然后根据初始化数据的类型自动推断,属性类型可以省略(这点和Java不同)。
4)如果赋值为null则一定要加类型,因为不加类型,那么该属性的类型就是Null类型.
object PropertyDemo {
def main(args: Array[String]): Unit = {
val p1 = new Person
println(p1.Name) // Null
println(p1.address) // String
}
}
class Person {
var age: Int = 10 // 给属性赋初始值,省略类型,会自动推导
var sal = 8090.9
var Name = null // Name 是什么类型
var address: String = null // ok
}
5)如果在定义属性时,暂时不赋值,也可以使用符号**_(下划线),**让下同分配默认值
6)不同对象的属性是独立,互不影响,一个对象对属性的更改,不影响另外一个。
案列演示+图(Monster)//这点和Java完全一样
def main(args: Array[String]): Unit = {
var worker1 = new Worker
worker1.name = "jack"
var worker2 = new Worker
worker2.name = "tom"
}
class Worker {
var name = ""
}
6.1.7、属性的高级部分
说明:属性的高级部分和构造器(构造方法/函数) 相关,我们把属性高级部分放到构造器那里讲解
6.1.8、如何创建对象
基本语法
val | var 对象名[:类型] = new 类型 ()
说明:
1) 如果我们不希望改变对象的引用(即:内存地址),应该声明为val性质的,否则声明为var,scala设计者推荐使用val,因为一般来说,在程序中,我们只是改变对象的属性值,而不是改变对象的引用
2)scala在声明对象变量时,可以根据创建对象的类型自动推断,所以类型声明可以省略,但当类型和后面new对象类型有继承关系即多态时,就必须写了
object CreateObj {
def main(args: Array[String]): Unit = {
val emp = new Emp // emp 类型就是Emp
// 如果我们希望将子类对象,交给父类的引用,这时就需要写上类型
val emp2:Person = new Emp
}
}
class Person{
}
class Emp extends Person{
}
6.1.9、类和对象的内存分配机制
object MemState {
def main(args: Array[String]): Unit = {
val p1 = new Person2
p1.name = "jack"
p1.age = 10
val p2 = p1
println(p1 == p2) // true
p1.name = "tom"
println(p2.name) // tom
}
}
class Person2 {
var name = ""
var age: Int = _ // 如果是以_ 的方式给默认值,则属性必须指定类型
}
6.2、方法
6.2.1、基本说明
Scala中的方法就是函数
6.2.2、基本语法
def 方法名(参数列表)[:返回值类型]={
方法体
}
6.2.3、方法案例演示
object MethodDemo01 {
def main(args: Array[String]): Unit = {
// 使用一下
val dog = new Dog
println(dog.cal(10,30))
}
}
class Dog {
private var sal: Double = _
var food:String = _
def cal(n1: Int, n2: Int): Int = {
n1 + n2
}
}
6.2.4、方法的调用机制原理
- 当我们scala开始执行时,先在栈区开辟一个main栈。main栈是最后被销毁
- 当scala程序在执行到一个方法时,总会开一个新的栈。
- 每个栈是独立的空间,变量(基本数据类型)是独立的,相互不影响(引用类型除外)
- 当方法执行完毕后,该方法开辟的栈就会被jvm机回收。
6.2.5、方法的相关练习题
1、编写类(MethodExec),编程一个方法,方法不需要参数,在方法中打印一个10*8 的矩形,在main方法中调用该方法。
object MethodDemo02 {
def main(args: Array[String]): Unit = {
val m = new MethodExec
m.printRect()
}
}
class MethodExec {
def printRect(): Unit = {
for (i <- 0 until (10)) {
for (j <- 0 until (8)) {
print("*")
}
println()
}
}
}
2、修改上一个程序,编写一个方法中,方法不需要参数,计算该矩形的面积,并将其作为方法返回值。在main方法中调用该方法,接收返回的面积值并打印(结果保留小数点2位)。
m.len = 2.1
m.width = 3.4
println("面积等于="+m.area())
def area() ={
(this.len * this.width).formatted("%.2f")
}
3、修改上一个程序,编写一个方法,提供m和n两个参数,方法中打印一个m*n的矩形,再编写一个方法算该矩形的面积(可以接收长len,和宽width), 将其作为方法返回值。在main方法中调用该方法,接收返回的面积值并打印。
object MethodDemo02 {
def main(args: Array[String]): Unit = {
val m = new MethodExec
m.len
m.width
m.printRect()
println(m.area())
}
}
class MethodExec {
// 属性
println("请输入矩形的长:")
var len = StdIn.readInt()
println("请输入矩形的宽")
var width = StdIn.readInt()
def printRect(): Unit = {
for (i <- 0 until (len)) {
for (j <- 0 until (width)) {
print("*")
}
println()
}
}
// 计算面积的方法
def area(): Int = {
this.len * this.width
}
}
4、编写方法:判断一个数是奇数odd还是偶数
object MethodDemo03 {
def main(args: Array[String]): Unit = {
val o = new ood
o.n
o.decide()
}
}
class ood {
println("请输入一个数字:")
var n = StdIn.readInt()
def decide() = {
if (n % 2 == 0) {
println("你输入的数字为偶数")
} else {
println("你输入的数字为奇数")
}
}
}
5、小小计算器(Calcuator),实现加减乘除四个功能
1、分四个方法完成
2、分一个方法完成
object MethodDemo04 {
def main(args: Array[String]): Unit = {
val c = new Calcuator
c.n1
c.n2
c.symbol
c.cal()
}
}
class Calcuator {
println("请输入两个数字:")
var n1 = StdIn.readDouble()
var n2 = StdIn.readDouble()
println("请输入运算符:")
var symbol = StdIn.readChar()
def cal()= {
if (symbol == '+') {
println(n1 + n2)
} else if (symbol == '-') {
println(n1 - n2)
} else if (symbol == '*') {
println(n1 * n2)
} else if (symbol == '/') {
println(n1 / n2)
}else{
println("你的输入有误,请重新输入···")
}
}
}
6.3、类与对象应用案例
1、小狗案列
1、编写一个Dog类,包含name(String),age(Int),weight(Double)属性
2、类中声明一个say方法 ,返回String两类型,方法返回信息中包含所有属性值
3、在另一个TestDog类中的main方法中,创建Dog对象,并反问say方法和所有属性,将调用结果打印输出。
object DogCaseTest {
def main(args: Array[String]): Unit = {
val dog = new Dog
dog.name = "tom"
dog.age = 2
dog.weight = 6
println(dog.say())
}
}
class Dog {
var name: String = ""
var age: Int = 0
var weight: Double = 0.0
def say(): String = {
"小狗的信息如下:\n name = " + this.name +
"\n age =" + this.age +
"\n weight = " + this.weight
}
}
2、盒子案例
1、编程创建一个Box类,在其中定义三个变量表示一个立方体的长、宽和高,长宽高可以通过控制台输入
2、定义一个方法获取立方体的体积(volumn)长 * 宽 * 高
3、创建一个对象,打印给定尺寸的立方体的体积
object BoxCaseTest {
def main(args: Array[String]): Unit = {
val b = new Box
b.len
b.width
b.hieght
b.volumn()
}
}
class Box{
println("请输入立方体的长、宽和高:")
var len = StdIn.readDouble()
var width = StdIn.readDouble()
var hieght = StdIn.readDouble()
def volumn(){
println("立方体的体积:"+(this.len * this.width * this.hieght))
}
}
3、景区门票案列
-
1、一个景区根据有人的年龄收取不通价格的门票
-
2、请编写游人类,根据年龄段决定能够购买的门票价格并输出
-
3、规则:年龄>18,门票为20,其他情况免费
-
4、可以循环从控制台输入名字和年龄,打印门票收费情况,如果名字输入n,则退出程序。
-
object TicketsTestDemo { def main(args: Array[String]): Unit = { val t = new Tourist t.name t.age t.rule() } } class Tourist { println("请输入你的姓名和年龄:") var name = StdIn.readChar() var age = StdIn.readInt() def rule() = { if (name == 'n') { println("退出程序") } else if (age > 18) { println("%s先生您好,您的年龄为%d,门票为20元".format(name, age)) } else if (age <= 18) { println("%s先生您好,您的年龄为%d,门票免费".format(name, age)) } } }
6.4、构造器
6.4.1、看一个需求
我们来看一个需求:前面我们在创建Person的对象时,是先把一个对象创建好后,再给他的年龄和姓名属性赋值,如果现在我要求,在创建人类的对象时,就直接指定这个对象的年龄和姓名,该怎么做? 这时就可以使用构造方法/构造器。
6.4.2、基本介绍
构造器(constructor)又叫构造方法,是类的一种特殊的方法,它的主要作用是完成对新对象的初始化。
6.4.3、Scala构造器的介绍
和Java一样,Scala构造对象也需要调用构造方法,并且可以有任意多个构造方法(即scala中构造器也支持重载)。
Scala类的构造器包括: 主构造器 和 辅助构造器
6.4.4、Scala构造器的基本语法
class 类名(形参列表) { // 主构造器
// 类体
def this(形参列表) { // 辅助构造器
}
def this(形参列表) { //辅助构造器可以有多个…
}
}
6.4.5、Scala构造器的快速入门
创建Person对象的同时初始化对象的age属性值和name属性值
object ConDemo01 {
def main(args: Array[String]): Unit = {
val p = new Person("jack", 20)
println(p)
}
}
// 构造器的快速入门
//创建Person对象的同时初始化对象age属性值和name属性值
class Person(inName: String, inAge: Int) {
var name: String = inName
var age: Int = inAge
// 重写toString,便于输出对象的信息
override def toString: String = {
"name = "+this.name +"\n"+ "age = "+this.age
}
}
6.4.6、Scala构造器注意事项和细节
1、Scala构造器作用是完成对新对象的初始化,构造器没有返回值
2、主构造器的声明直接放置于类名之后[反编译]
3、主构造器会执行类定义中的所有语句,这里可以体会到Scala的函数式编程和面对对象编程融合在一起**,即:构造器也是方法(函数),传递参数和使用方法和前面的函数部分内容没有区别[案列演示+反编译](主构造器其实就是将类体里面除了函数部分其他都执行)**
4、如果主构造器五参数,小括号可省略,构建对象时调用的构造方法的小括号也可以省略
5、辅助构造器名称为this(这个和Java是不一样的),多个辅助构造器通过不同参数列表进行区分,在底层就是java的构造器重载
object ConDemo02 {
def main(args: Array[String]): Unit = {
val a = new AA("")
// 输出的顺序是
// 1、b··· 父类
// 2、AA() 构造器
// 3、A this(name:String) 辅助构造器
}
}
class BB() {
println("b···")
}
class AA() extends BB() {
println("AA()")
def this(name: String) {
this // 调用A的柱构造器
println("A this(name:String)")
}
}
object ConDemo03 {
def main(args: Array[String]): Unit = {
val p1 = new Person2()
println(p1)
}
}
// 定义了一个Person类
// 运行后Person 有几个构造器 4
class Person2(){
var name:String = _
var age:Int = _
def this(name:String){
/*辅助构造器无论是直接还是间接,最终都一定艺调用主构造器,执行
主构造器的逻辑,而且需要放在辅助构造器的第一行[这点和Java一样,java
中一个构造器要调用同类的其他构造器,也需要放在第一行]
*/
this() // 直接调用主构造器
this.name = name
}
// 辅助构造器
def this(name:String,age:Int){
this()
this.name = name
this.age = age
}
def this(age:Int){
this("匿名") // 间接调用主构造器,因为 def this(name :String)中调用了主构造器
this.age = age
}
def showinfo():Unit={
println("person的信息如下:")
println("name="+this.name)
println("age="+this.age)
}
override def toString: String = {
this.name+this.age
}
}
6、如果想让主构造器变成私有的,可以再()之前加上private,这样用户只能通过辅助构造器来构造对象了
class Person2 private(){}
7、辅助构造器的声明不能和主构造器的声明一致,会发生错误(即构造器名重复)
6.5、属性高级
6.5.1、构造器参数
1、Scala类的主构造器的形参未用任何修饰符修饰,那么这个参数是局部变量。
// 1.如果 主构造器是Worker(inName:String), 那么 inName就是一个局部变量
class Worker(inName:String){
var name = inName
}
2、如果参数使用val关键字声明,那么Scala会将参数作为类的私有的只读属性使用
// 2.主构造器是Worker(val inName:String),那么 inName就是Worker2的一个private的只读属性
class Worker2(val inName:String){
var name = inName
}
3、如果参数使用var关键字声明,那么那么Scala会将参数作为类的成员属性使用,并会提供属性对应的xxx()[类似getter]/xxx_$eq()[类似setter]方法,即这时的成员属性是私有的,但是可读写。
// 3.主构造器是Worker(var inName:String), 那么 inName就是Worker2的一个private的可读写属性
class Worker3(var inName:String){
var name = inName
}
6.5.2、Bean属性
JavaBeans规范了Java的属性是像getxxx()和setxxx()的方法。许多Java工具都依赖这个命名习惯。为了Java的互操作性。将Scala字段加@BeanProperty时,这样会自动**生成规范的setxxx/getxxx的方法。**这时可以使用对象.setxxx()和getxxx() 来调用属性。
注意:给某个属性加入@Beanproperty注解后,会生成getxxx和setxxx的方法,并且对原来底层自动生成类似xxx(),xxx——$eq()方法,没有冲突,二者可以共存
import scala.beans.BeanProperty
object BeanPropertyDemo01 {
def main(args: Array[String]): Unit = {
val car = new Car
car.name = "宝马"
println(car.name)
// 使用 @BeanProperty 自动生成getXxx 和 setXxx
car.setName("奔驰")
println(car.getName)
}
}
class Car{
@BeanProperty var name:String = null
}
6.6、scala对象创建的流程分析(面试题)
class Person{
var age:Short = 90
var name:String = _
def this(n:String,a:Int){
this()
this.name = n
this.age = a
}
var p : Person = new Person("小倩",20)
1、加载类的信息(属性信息,方法信息)
2、在内存中(堆)开辟空间
3、使用父类的构造器(主和辅助)进行初始
4、使用主构造器对属性进行初始化 【age:90, naem nul】
5、使用辅助构造器对属性进行初始化 【 age:20, naem 小倩 】
6、将开辟的对象的地址赋给 p这个引用