九、Scala的包和样例类


1. 包

实际开发中,如果遇到同名的类(比如两个Person类),怎么在不改变类名的情况下区分它们呢?就需要使用包(package)

1.1 概述

包就是文件夹,用关键字package修饰,它可以区分重名类,且功能相似的代码可以放到同一个包中,便于维护和管理代码

注意:

  • 编写scala源代码时,包名和源码所在的目录结构可以不一致
  • 编译后,字节码文件和包名路径一致(由编译器自动完成)
  • 包名由数字,大小写英文字母,_(下划线),$(美元符)组成。多级包之间用.隔开,一般是公司域名反写(例如:com.itheima.demo01)

1.2 格式

格式一:文件顶部标记法,合并版

package 包名1.包名2.包名3 //根据实际需求,可以写多级包名
//这里可以写类,特质

格式二:文件顶部标记法,分解版

package 包名1.包名2 
package 包名3
//这里可以写类,特质

格式三:串联式包语句(包名嵌套最好不要超过3级)

package 包名1.包名2 {
	//包名1的内容在这里不可见
	
	package 包名3 {
		//这里可以写类,特质
	}
}

示例:

package com.itheima.scala
//人类
class Person
//测试类
object ClassDemo01 {
	def main(args:Array[String]):Unit={
	}
}

找到当前字节码文件的位置:

  • 方式一:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 方式二:
    在这里插入图片描述
    在这里插入图片描述

1.3 包的作用域

Scala中的包和其他作用域一样,也支持嵌套,具体访问规则为:

  1. 下层可以直接访问上层的内容(即子包可以直接访问父包中的内容)
  2. 上层访问下层内容时,可通过导包(import)写全包名的形式实现
  3. 如果上下层有相同的类,使用时将采用就近原则来访问(即上下层的类重名时,优先使用下层的类,如果明确要访问上层的类,可通过上层路径+类名的形式实现)

示例:
在这里插入图片描述

//1.创建com.itheima包,并在其中定义Person类,Teacher类,及子包scala
package com.itheima{
	class Person  //路径:com.itheima.Person
	class Teacher  //路径:com.itheima.Teacher

	//测试类
	object ClassDemo {
		def main(args:Array[String]):Unit={
			//3.4上层访问下层内容时,要先导包
			import com.itheima.scala.Student //可以简化成import scala.Student
			val s = new Student
			println(s) //com.itheima.scala.Student@47f37ef1
		}
	}

	package scala {  //路径:com.itheima.scala
		//2.在com.itheima.scala包中定义Person类,Student类
		class Person  //路径:com.itheima.scala.Person
		class Student  //路径:com.itheima.scala.Student
	}
}

//3.测试类中测试
object ClassDemo {
	def main(args:Array[String]):Unit={
	//3.1测试下层可以直接访问上层的内容
	val t = new Teacher
	println(t) //com.itheima.Teacher@47f37ef1
	
	//3.2如果上下层有重名类,会采用就近原则来访问
	val p1 = new Person
	println(p1) //com.itheima.scala.Person@5a01ccaa
	
	//3.3如果明确需求访问上层的类,可以通过包名+类名的形式实现
	val p2 = new com.itheima.Person
	println(p2) //com.itheima.Person@71c7db30
	}
}

1.4 包对象

包中可以定义子包,也可以定义类或特质,但是Scala中不允许直接在包中定义变量或方法。这是因为JVM的局限性导致的,要想解决此问题,需要用到包对象。
Scala中每个包都有一个包对象,包对象的名字和包名必须一致,且它们之间是平级关系,不能嵌套定义
注意:

  • 包对象也要定义到父包中,这样才能实现包对象和包的评级关系
  • 包对象一般用于对包的功能进行补充,增强等

格式:

package 包名1 { //父包
	package 包名2 { //子包
		
	}
	
	package object 包名2 { //包名2的包对象

	}
}

示例:
在这里插入图片描述

//1.定义父包com.itheima,并在其中定义子包scala
package com.itheima{ // 父包

	package scala {  //子包
		class A
		trait B

		//3.在scala包中定义测试类,并测试
		object ClassDemo {
			def main(args:Array[String]):Unit={
				//测试包对象中的成员
				println(scala.name)
				scala.sayHello()
			}
		}
	}

	//2.定义scala包的包对象,并在其中定义变量和方法
	package object scala { //scala包的包对象
		var name = 'lee'
		def sayHello() = println('hi')
	}
}
//lee
//hi

1.5 包的可见性

Scala中,我们可以通过访问权限修饰符(private, protected, 默认)来限定包中一些成员的访问权限

格式:

访问权限修饰符[包名]   //例如:private[com] var name = ""

示例:
在这里插入图片描述

//1.定义父包com.itheima,并在其中定义子包scala
package com.itheima{ // 父包
	class Employee { //父包中的员工类
		//2.在Employee类中定义变量和方法
		//private var name = 'lee' //报错,因为private使name这个成员变量只能在本类中被直接访问
		private[com] var name = 'lee' //[com]使得name成员变量可以在com包下的任意类中被访问
		var age = 23
		
		private[com] sayHello() = println('hi')
	}

	package scala {  //子包
		//3.在scala包中定义测试类,创建Employee类对象,并访问其成员
		object ClassDemo {
			def main(args:Array[String]):Unit={
				val e - new Employee
				println(e)
				println(e.name, e.age)
				e.sayHello()
			}
		}
	}
//com.itheima.Employee@47f37ef1
//(lee,23)
//hi

1.6 包的引入

Scala中导入包也是通过关键字import来实现,但它不局限于编写到scala文件的顶部,而是可以写到scala文件中任何你需要用的地方,且scala默认引入了java.lang包,scala包,及Predef包

注意:

  • Scala中并不是完全引入scala包和Predef包中的所有内容,它们的部分内容在使用时仍需要先导包(如:import scala.io.StdIn

  • Scala中如果要导入某个包中所有的类和特质,要通过_(下划线)来实现;如果只需要某几个类或特质,则通过选取器(即一对大括号)来实现

    import scala._ //导入scala包下所有的内容
    import scala.collection.mutable.{HashSet, TreeSet} //只引入HashSet和TreeSet两个类
    
  • 如果引入的多个包中含有相同的类,则通过重命名或隐藏的方式解决

    • 重命名的格式:

      import 包名1.包名2.{原始类名=>新类名,原始类名=>新类名}
      //例如:import java.util.{HashSet => JavaHashSet}
      

      隐藏的格式:

      import 包名1.包名2.{原始类名=>_, _}
      	//例如:import java.util.{HashSet => _, _}表示java.util包下除了HashSet类之外所有的类
      

示例:
在这里插入图片描述

object ClassDemo {
	def main(args:Array[String]):Unit={
		//1.创建测试类,并在main方法中测试

		//2.需求1:导入java.util.HashSet类
		import java.util.HashSet()
		val hs = new HashSet()
		println(hs.getClass) //class java.util.HashSet

		//3.需求2:导入java.util包下所有的内容
		import java.util._
		val list = new ArrayList()
		val hs = new HashSet()
		println(list.getClass(), hs.getClass()) //(class java.util.ArrayList, class java.util.HashSet)

		//4.需求3:导入java.util包下所有的内容
		import java.util.{ArrayList, HashSet}
		val list = new ArrayList()
		val hs = new HashSet()
		println(list.getClass(), hs.getClass()) //(class java.util.ArrayList, class java.util.HashSet)

		//5.需求4:通过重命名的方式,解决多个包中类名重复的问题
		import java.util.{HashSet=>JavaHashSet}
		import scala.collection.mutable.HashSet
		val hs = new HashSet()
		println(hs.getClass()) //class scala.collection.mutable.HashSet
		val jhs = new JavaHashSet()
		println(jhs.getClass()) //class java.util.HashSet

		//6.需求5:导入时隐藏某些不需要用到的类,即:导入java.util包下HashSet和TreeSet之外所有的了类
		import java.util.{HashSet=>_, TreeSet=>_, _}
		import scala.collection.mutable.HashSet
		val hs = new HashSet()
		println(hs.getClass()) //class scala.collection.mutable.HashSet
		
	}
}

2. 样例类

Scala中样例类时一种特殊类,一般用于保存数据的(类似Java POJO类),在并发编程以及Spark、Flink这些框架中会经常用到。

格式:

case class 样例类名([var/val] 成员变量名1:类型1,成员变量名2:类型2,成员变量名3:类型3){}

注意:

  • 如果不写,则变量的默认修饰符是val,即val是可以省略不写的
  • 如果要实现某个成员变量值是可以被修改的,则需手动添加var来修饰此变量

示例:
在这里插入图片描述

object ClassDemo {
	//1.定义样例类Person
	case class Person(name:String = 'lee', var age:Int = 23){} //val是可以省略不写的

	//2.定义main方法,作为程序的主入口
	def main(args:Array[String]):Unit={
		//3.创建Person类对象
		val p = new Person
		println(p.name, p.age) //lee, 23

		//4.尝试修改Person类中的属性值
		//p.name = 'mike' //报错,因为在定义成员变量时未用val或var修饰,系统默认时val
		p.age = 30
		println(p.name, p.age) //lee, 30
		
	}
}

当我们定义一个样例类后,编译器会自动帮我们生成一些方法,常用如下:
在这里插入图片描述
在这里插入图片描述
示例:
在这里插入图片描述

object ClassDemo {
	//1.定义样例类Person
	case class Person(name:String, var age:Int){}

	//2.定义main方法,作为程序的主入口
	def main(args:Array[String]):Unit={
		//3.创建Person类对象
		//apply():快速创建对象,省去new
		val p1 = Person('lee', 23)
		//toString():输出语句打印对象,默认调用了对象的toString方法,重写它可以方便快速输出对象的各个属性
		println(p1) //Person('lee',23)
		//equals():比较两个对象的各个属性值是否相同
		val p2 = Person('lee', 23)
		println(p1 == p2) //true
		//hashCode():获取对象的哈希值
		//同一个对象哈希值一定相同,不同对象哈希值一般不同
		println(p1.hashCode()) //2077529024
		println(p2.hashCode()) //2077529024
		//copy():基于某一个对象,快速构建一个和它类似的对象
		val p3 = p2.copy(age = 30)
		println(p3) //Person(lee, 30)
		

		//4.尝试修改Person类中的属性值
		//p.name = 'mike' //报错,因为在定义成员变量时未用val或var修饰,系统默认时val
		p.age = 30
		println(p.name, p.age) //lee, 30
		
	}
}

3. 样例对象

Scala中,用case修饰的单例对象叫样例对象,而且它没主构造器,主要用在两个地方:

  • 当作枚举值用(一些固定值,用来统一项目规范的)
  • 作为没有任何参数的消息传递(后续Akka并发编程会讲)

格式:

case object 样例对象名

示例:
在这里插入图片描述

object ClassDemo {
	//1.定义特质Sex,表示性别
	trait Sex

	//2.定义样例对象Male,表示男,继承Sex特质
	case object Male extends Sex
	//3.定义样例对象Female,表示女,继承Sex特质
	case object Female extends Sex
	
	//4.定义样例类Person
	case class Person(var name:String, var sex:Sex) {}
	//5.定义main方法,作为程序的主入口
	def main(args:Array[String]):Unit={
		//6.创建Person样例类对象
		val p = Person("lee", Male)
		println(p.name, p.age)
	}
}
//(lee, Male)

4. 案例:计算器

需求:
在这里插入图片描述

object ClassDemo {
	//1.定义样例类Calculate
	case class Calculate(a:int, b:Int) {
		//2.定义四个方法,加减乘除
		def add() = a + b
		def subtract() = a - b
		def multiply() = a * b
		def divide() = a / b
	}
	//3.定义main方法,作为程序的主入口
	def main(args:Array[String]):Unit={
		//4.创建Calculate样例类对象
		val c = Calculate(10, 3)
		println(c.add()) //13
		println(c.subtract()) //7
		println(c.multiply()) //30
		println(c.divide()) //3
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值