Scala

scala-07-包

2020-04-05  本文已影响0人  chen_666

1.在scala中一个文件可以同时创建多个包
2.scala中包名和源码所在的系统文件目录结构要可以不一致,但是编译后的字节码文件路径和包名会保持一致

package com.scalastudy{
  //表示在com.scalastudy包下创建了User的class
  class User

  package scala {
    //在com.scalastudy.scala这个包下创建了 class PkgDemo03[伴生类] 和 class PkgDemo3$  [伴生对象]
    object PkgDemo03 {
      def main(args: Array[String]): Unit = {
        println("执行ok~~")
      }
    }
    //表示在com.scalastudy.scala包下创建了Person类
    class Person
    //表示在com.scalastudy.scala包下创建了User类
    class User
  }
}

3.包也可以像嵌套类那样嵌套使用(包中有包), 这个在前面的第三种打包方式已经讲过了,在使用第三种方式时的好处是:程序员可以在同一个文件中,将类(class / object)、trait 创建在不同的包中,这样就非常灵活了
4.作用域原则:可以直接向上访问。即: Scala中子包中直接访问父包中的内容, 大括号体现作用域。(提示:Java中子包使用父包的类,需要import)。在子包和父包 类重名时,默认采用就近原则,如果希望指定使用某个类,则带上包名即可

package com.scalademo {

  //表示在com.scalademo 包下创建了User的class
  class User
  //表示在com.scalademo 包下创建了Sheep类
  class Sheep

  package scala {
    //在com.scalademo.scala这个包下创建了 class PkgDemo03[伴生类] 和 class PkgDemo3$  [伴生对象]
    object PkgDemo03 {
      def main(args: Array[String]): Unit = {
        //子包可以直接使用父包的内容
        val sheep = new Sheep
        //如果子包和父包有相同的类,采用就近原则
        val user = new User
        //如果需要使用父包的内容,则指定包的路径
        val user1 = new com.atguigu.User

        println("sheep=" + sheep)
        println("user=" + user)
        println("user2=" + user1)
        println("执行ok~~")
      }
    }
    //表示在com.scalademo.scala包下创建了Person类
    class Person
    //表示在com.scalademo.scala包下创建了User类
    class User

  }

4)父包要访问子包的内容时,需要import对应的类等

 object Test{
    def main(args: Array[String]): Unit = {
      //使用com.scalademo.scala.Person
      import com.scalademo.scala.Person
      val person = new Person
    }
  }

包对象

 package object scala {
    var name = "jack" //变量
    def sayHi(): Unit = { //方法
      println("package object scala sayHI()")
    }
}
package scala {
    object PkgDemo03 {
      def main(args: Array[String]): Unit = {
        //使用对应的包对象的内容
        println("name=" + name)
        sayHi()
      }
    }

伴生类和伴生对象

  1. 如果我们在同一个文件中,写了 class Clerk 和 object Clerk
    就把 class Clerk 称为 伴生类, object Clerk 称为伴生对象
  2. 如果我们在同一个文件中,只写了 class Clerk ,那么Clerk就是一个普通的类
  3. 如果我们在同一个文件中,只写了 object Clerk, 那么在底层就会自动生成对应的伴生类 class Clerk, 只是这个伴生类是空..
  4. 伴生对象,可以访问到伴生类的任何方法和属性
class Clerk {
  //var name , 底层 name是private ,但是会提供两个public方法 name name_$eq
  var name: String = "jack"
  //protected var job ,底层 job 是private , 会提供两个pulbic方法 job 和 job_$eq
  protected var job : String = "大数据工程师"
  //private var sal , 底层sal priate, 会提供两个private方法 sal 和 sal_$eq
  private var sal: Double = 9999.9
  //如果方法默认就是public
  def showInfo(): Unit = {
    println(" name " + name + " sal= " + sal)
  }
}

//伴生对象
object Clerk {
  def test(c: Clerk): Unit = {
    //这里体现出在伴生对象中,可以访问c.sal[sal是私有]
    println("test() name=" + c.name + " sal= " + c.sal) //c.sal()
  }
}

Scala中包的可见性和访问修饰符的使用

1.当属性访问权限为默认时,从底层看属性是private的,但是因为提供了xxx_$eq()[类似setter]/xxx()[类似getter] 方法,因此从使用效果看是任何地方都可以访问)
2.当方法访问权限为默认时,默认为public访问权限
3.private为私有权限,只在类的内部和伴生对象中可用 【案例演示】
4.protected为受保护权限,scala中受保护权限比Java中更严格,只能子类访问,同包无法访问 (编译器从语法层面控制)
5.在scala中没有public关键字,即不能用public显式的修饰属性和方法。
6.scala设计者将访问的方式分成三大类: (1) 处处可以访问public (2) 子类和伴生对象能访问protected (3) 本类和伴生对象访问 private
7.包访问权限(表示属性有了限制。同时包也有了限制),这点和Java不一样,体现出Scala包使用的灵活性。 当然,也可以将可见度延展到上层包private[atguigu] val description="zhangsan"说明:private也可以变化,比如protected[atguigu], 非常的灵活.

class House {
  //说明:
  //1. 当我们在private[pkg] 表示(1) private 是生效 (2) 同时扩大属性的访问范围,就是在
  //   com.atguigu.chapter07.pkg  包下是可以访问的.
  //2. private[chapter07] 表示 com.atguigu.chapter07 包和它子包都可以访问master属性.
  //3. 其它方法和属性类推
  private[pkg] var master = "smith"

  private[chapter07] def sayHi() {
    println("say Hi")
  }
}

8.在Scala中,import语句可以出现在任何地方,并不仅限于文件顶部,import语句的作用一直延伸到包含该语句的块末尾。这种语法的好处是:在需要时在引入包,缩小import 包的作用范围,提高效率
9.Java中如果想要导入包中所有的类,可以通过通配符*,Scala中采用下 _
10.如果不想要某个包中全部的类,而是其中的几个类,可以采用选取器,使用{} 括起来即可。

class Cat{
  def test(): Unit = {
    //
    import scala.collection.mutable.{HashMap,HashSet}
    val hm = new HashMap()
    val hs = new HashSet()
  }
}

11.如果引入的多个包中含有相同的类,那么可以将类进行重命名进行区分,这个就是重命名。

class Car {
  def test(): Unit = {
    //如果有多个同名的类或者trait等,可以使用scala 重命名的机制来解决.
    import java.util.{ HashMap=>JavaHashMap, List}
    import scala.collection.mutable._
    var map = new HashMap() // 此时的HashMap指向的是scala中的HashMap
    var map1 = new JavaHashMap(); // 此时使用的java中hashMap的别名
  }
}

12.如果某个冲突的类根本就不会用到,那么这个类可以直接隐藏掉

class Student {
  def test(): Unit = {
    import java.util.{ HashMap=>_, _} // 含义为 引入java.util包的所有类,但是忽略 HahsMap类.
    import scala.collection.mutable.HashMap
    var map = new HashMap() // 此时的HashMap指向的是scala中的HashMap, 而且idea工具,的提示也不会显示java.util的HashMaple
  }
}

重写方法

scala明确规定,重写一个非抽象方法需要用override修饰符,调用超类的方法使用super关键字

class Person2 {
  var name: String = "tom"
  //父类方法
  def printName() {
    println("Person printName() " + name)
  }
  def sayHi(): Unit = {
    println("sayHi")
  }
}

class Emp extends Person2 {

  //想去重写Person-printName方法,必须显式的声明 override
  //
 override def printName() {
    println("Emp printName() " + name)
    sayHi()
    //如果希望调用父类的printName,则需要使用super.printName()
    super.printName()
  }
}

类型检查与转换

1.要测试某个对象是否属于某个给定的类,可以用isInstanceOf方法。用asInstanceOf方法将引用转换为子类的引用。classOf获取对象的类名。
2.classOf[String]就如同Java的 String.class 。
3.obj.isInstanceOf[T]就如同Java的obj instanceof T 判断obj是不是T类型。
4.obj.asInstanceOf[T]就如同Java的(T)obj 将obj强转成T类型

object TypeConvert {
  def main(args: Array[String]): Unit = {
    // 获取对象类型
    println(classOf[String])
    val s = "zhangsan"
    println(s.getClass.getName) //这种是Java中反射方式得到类型

    println(s.isInstanceOf[String]) //判断类型 true
    println(s.asInstanceOf[String]) //将s 显示转换成String
    //看一个简单应用
    val p: Person3 = new Emp3 //子类对象给了一个父类的引用
    p.printName()
    //如果希望使用的子类的方法say
    p.asInstanceOf[Emp3].say()
  }
}
class Person3 {
  var name = "king"
  def printName(): Unit = {
    println("name=" + name)
  }
}
class Emp3 extends Person3 {
  def say(): Unit = {
    println("Emp3=" + name)
  }
}

超类

类有一个主构器和任意数量的辅助构造器,而每个辅助构造器都必须先调用主构造器(也可以是间接调用.)
只有主构造器可以调用父类的构造器。辅助构造器不能直接调用父类的构造器。在Scala的构造器中,你不
能调用super(params)

object SuperDemo {
  def main(args: Array[String]): Unit = {
    new Emp5()
  }
}
class Person5(name: String) { //父类的构造器
  println("Person5 主构造器" + name)
}
class Emp5 (name: String) extends Person5(name) {// 将子类参数传递给父类构造器,这种写法√
  println("子类的主构造器=" + name)
  //super(name), 错误不能在主构造器中显式的调用super
  def this() {
    this("xx")
    //super("abc") // (×)不能在辅助构造器中调用显式父类的构造器
  }
}

覆写字段

object ScalaFieldOverride {
  def main(args: Array[String]): Unit = {
    val obj : AA = new BB()
    val obj2 : BB = new BB()
    println("obj.age=" + obj.age + " obj2.age=" + obj2.age) //本质 obj.age => obj.age() obj2.age=>obj2.age()
    println(obj.asInstanceOf[AA].age) // 20
  }
}

class AA {
  val age: Int = 10
}

class BB extends AA {
  override val age: Int = 20
}

注意事项
1)def只能重写另一个def(即:方法只能重写另一个方法)
2)val只能重写另一个val 属性 或 重写不带参数的def
3)var只能重写另一个抽象的var属性

抽象属性

抽象属性:声明未初始化的变量就是抽象的属性,抽象属性在抽象类
var重写抽象的var属性小结
1.一个属性没有初始化,那么这个属性就是抽象属性
2.抽象属性在编译成字节码文件时,属性并不会声明,但是会自动生成抽象方法,所以类必须声明为抽象类
3.如果是覆写一个父类的抽象属性,那么override 关键字可省略 [原因:父类的抽象属性,生成的是抽象方法,因此就不涉及到方法重写的概念,因此override可省略]

抽象类

在Scala中,通过abstract关键字标记不能被实例化的类。方法不用标记abstract,只要省掉方法体即可。抽象类可以拥有抽象字段,抽象字段/属性就是没有初始值的字段

object AbstractClassDemo01 {
  def main(args: Array[String]): Unit = {
    println("ok~~")
  }
}

//抽象类
abstract class Animal {
  var name: String //抽象的字段
  var age: Int // 抽象的字段
  var color: String = "black" //普通字段
  def cry() //抽象方法
  def sayOk(): Unit = {
    println("ok")
  }
}

//快捷键 alt+enter
class Mouse extends Animal {
  //以后再scala中,override 有两层含义 1. 重写(override 必须保留) 2. 实现 (可以省略)
  override var name: String = _
  override var age: Int = _
  override def cry(): Unit = {
  }
}

注意事项及细节

1)抽象类不能被实例
2)抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法
3)一旦类包含了抽象方法或者抽象属性,则这个类必须声明为abstract
4)抽象方法不能有主体,不允许使用abstract修饰。
5)如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法和抽象属性,除非它自己也声明为abstract类。
6)抽象方法和抽象属性不能使用private、final 来修饰,因为这些关键字都是和重写/实现相违背的。
7)抽象类中可以有实现的方法.
8)子类重写抽象方法不需要override,写上也不会错.

上一篇 下一篇

猜你喜欢

热点阅读