包基础概念
现在有两个程序员共同开发一个项目,程序员小明希望定义一个类取名 Dog ,程序员小红也想定义一个类也叫Dog,我们知道同一个脚本里类名不可以一样。这时可以使用包进行区分,并且控制访问范围。一般大家会用IDEA开发scala代码,此时可以通过子包的方式,分别存放Dog类的文件。调用时,带上路径即可,比如:
val dog1 = new scala.xiaoming.Dog
val dog2 = new scala.xiaohong.Dog
包的命名
命名规则
- 只能包含数字、字母、下划线、小圆点
- 不能用数字开头, 也不要使用关键字
命名规范
一般是小写字母+小圆点一般是com.公司名.项目名.业务模块名,比如百度机器学习的ctr项目:com.baidu.ml.ctr
包的使用细节
使用细节视频上讲了很多,但是目前我自己用到的很少,所以简单列几个点:
- Scala中子包中直接访问父包中的内容, 大括号体现作用域
- 在子包和父包 类重名时,默认采用就近原则,如果希望指定使用某个类,则带上包名即可
- 父包要访问子包的内容时,需要import对应的类等
package com.atguigu{
//这个类就是在com.atguigu包下
class User{
}
//这个类对象就是在Monster$ , 也在com.atguigu包下
object Monster {
}
class Dog {
}
package scala {
//这个类就是在com.atguigu.scala包下
class User{
}
//这个Test 类对象
object Test {
def main(args: Array[String]): Unit = {
//子类可以直接访问父类的内容
var dog = new Dog()
println("dog=" + dog)
//在子包和父包 类重名时,默认采用就近原则.
var u = new User()
println("u=" + u)
//在子包和父包 类重名时,如果希望指定使用某个类,则带上包路径
var u2 = new com.atguigu.User()
println("u2=" + u2)
}
}
}
}
上面这个例子我都没遇到过,通常都是把不同的object脚本,放在一个子包下面,暂时先这样,后面实际工作中遇到再来填坑。
包对象
包可以包含类、对象和特质trait,但不能包含函数/方法或变量的定义。这是Java虚拟机的局限。为了弥补这一点不足,scala提供了包对象的概念来解决这个问题。在实际开发中,我也没有用过包对象,所以还是简单看下代码吧~
package com.atguigu {
//每个包都可以有一个包对象。你需要在父包(com.atguigu)中定义它,且名称与子包一样。
package object scala {
var name = "jack"
def sayOk(): Unit = {
println("package object sayOk!")
}
}
package scala {
class Test {
def test() : Unit ={
//这里的name就是包对象scala中声明的name
println(name)
sayOk()//这个sayOk 就是包对象scala中声明的sayOk
}
}
object TestObj {
def main(args: Array[String]): Unit = {
val t = new Test()
t.test()
//因为TestObje和scala这个包对象在同一包,因此也可以使用
println("name=" + name)
}}}}
如上面代码:
- com.atguigu下面有一个scala的子包
- scala子包下面有一个类Test,一个object TestObj
- 在子包scala同级处,定义了个包对象(package object scala)
- 每个包都可以有一个包对象,需要在父包中定义它,也就是和子包同级
- 包对象名称需要和包名一致,一般用来对包的功能补充,比如子包scala的包对象用package object scala{}定义
包的可见性
包的可见性听得也很蒙蔽,都是和java作比较进行讲解的,还是看代码:
package com.atguigu.chapter07.visit
object Testvisit {
def main(args: Array[String]): Unit = {
val c = new Clerk()
c.showInfo()
Clerk.test(c)
//创建一个Person对象
val p1 = new Person
println(p1.name)
}
}
//类
class Clerk {
var name: String = "jack" //
private var sal: Double = 9999.9
protected var age = 10
var job : String = "大数据工程师"
def showInfo(): Unit = {
//在本类可以使用私有的
println(" name " + name + " sal= " + sal)
}
}
//当一个文件中出现了 class Clerk 和 object Clerk
//1. class Clerk 称为伴生类
//2. object Clerk 的伴生对象
//3. 因为scala设计者将static拿掉, 他就是设计了 伴生类和伴生对象的概念
//4. 伴生类 写非静态的内容 伴生对象 就是静态内容
//5.
object Clerk {
def test(c: Clerk): Unit = {
//这里体现出在伴生对象中,可以访问c.sal
println("test() name=" + c.name + " sal= " + c.sal)
}
}
class Person {
//这里我们增加一个包访问权限
//下面private[visit] : 1,仍然是private 2. 在visit包(包括子包)下也可以使用name ,相当于扩大访问范围
protected[visit] val name = "jack"
}
- 当属性访问权限为默认时,从底层看属性是private的,但是因为提供了xxx_$eq()[类似setter]/xxx()[类似getter] 方法,因此从使用效果看是任何地方都可以访问)
- 当方法访问权限为默认时,默认为public访问权限
上面两点好像只是概念上的区别,用法上都可以直接访问 - private为私有权限,只在类的内部和伴生对象中可用
第3点需要注意,class Clerk为伴生类,object Clerk 为伴生对象,私有变量sal只能在伴生对象中使用 - protected为受保护权限,scala中受保护权限比Java中更严格,只能子类访问,同包无法访问 (编译器)
第4点,在Person类中,name变量有protected修饰,此时只能子类访问,但是加上[visit]之后,可以在visit包中使用 - 在scala中没有public关键字,即不能用public显式的修饰属性和方法
包的引入
- 在Scala中,import语句可以出现在任何地方,并不仅限于文件顶部,import语句的作用一直延伸到包含该语句的块末尾。这种语法的好处是:在需要时在引入包,缩小import 包的作用范围,提高效率
- 导入包中所有的类,Scala中采用下 _ ,如 import org.apache.spark.sql.functions._
- 导入包中部分类,import org.apache.log4j.{Level, Logger}
- 给导入的类重命名,import java.util.{ HashMap=>JavaHashMap, List},这里指把java.util中的HashMap重命名为JavaHashMap
- 如果某个冲突的类根本就不会用到,那么这个类可以直接隐藏掉,import java.util.{ HashMap=>_, _} // 含义为 引入java.util包的所有类,但是忽略 HahsMap类
2020-02-22 于南京市栖霞区