Scala基础【模板方法设计模式、面向对象编程】

本文深入探讨了模板方法设计模式,通过Java和Scala的实例展示了如何使用该模式实现算法骨架,强调子类只需关注具体细节。同时,文章详细阐述了Java和Scala中的包、导入、类加载器的工作原理,以及访问权限的差异,帮助读者更好地理解和应用面向对象编程概念。
摘要由CSDN通过智能技术生成

一 模板方法的设计模式

基本原理:根据一个模板做出一个东西,但是细节有轻微差别

逻辑没有变化,抽象类也没有变化,重点是用哪具体的类,这种代码的写法叫做模板方法的设计模式。父类将算法的骨架搭建好,子类只需要实现具体的细节即可。在执行时,只需要创建不同的子类就可以完成不同的功能。

以下代码用到的功能有:多态,方法的重写

package com.hike.bigdata.scala.test;

public class TestParent {
    public static void main(String[] args) {
        Parent parent = new QueryChild();
        parent.doExecute();

        Parent parent1 = getParent();
        parent1.doExecute();
    }
    private static Parent getParent(){
        return new OperChild();
    }
}

abstract class Parent{
    public void execute(){
        startTransaction();
        doExecute();
        endsTransaction();
    }
    public void startTransaction(){
        System.out.println("startTransaction");
    }
    public void endsTransaction(){
        System.out.println("endsTransaction");
    }
    //不同的sql语句使用不同的操作,所以将其设置为抽象方法
    abstract public void doExecute();
}

class QueryChild extends Parent{
    @Override
    public void doExecute() {
        System.out.println("do execute query");
    }
}
class OperChild extends Parent{
    @Override
    public void doExecute() {
        System.out.println("do execute insert/update/delete");
    }
}

使用scala实现模板方法的设计模式,无抽象类,无子类

package com.hike.bigdata.scala.test

object TestTemplatMethodScala {
  def main(args: Array[String]): Unit = {
    TM.execute{
      println("do execte insert")
    }
    TM.execute{
      println("do execte update")
    }
  }
  object TM{
    def execute(op : => Unit ): Unit = {
      startTransaction()
      op
      endsTransaction()
    }

    def startTransaction(): Unit = {
      System.out.println("startTransaction")
    }

    def endsTransaction(): Unit = {
      System.out.println("endsTransaction")
    }
  }
}

二 面向对象编程

java中面向对象代码的基本语法操作:package,import,创建类,类中创建属性和方法,如果与别的类有关系,可以将别的类当做本类的属性,构建对象,访问对象中的方法,如果静态方法,也可以通过类名访问。

同样,scala中面向对象的基本语法也大体是这些内容。

/**
     * package xxx.yyy.zzz
     * import java.util.List
     *
     * public class Test{
     *    private String name;
     *    public void setName(){};
     * }
     *
     * Test test = new Test();
     * test.setName();
     */

1 包

(1)java中package的作用

  • 分类管理(X)
    • 工具类 – util包 – util.StringUtil , util.DateUtil
      • 主要用.后面的,包名并不重要
    • 通用类 – common包
    • 实体类 – bean包
  • 区分相同名称的类(X)
    • 如java.util.Date 和 java.sql.Date
    • 但如果给这两个类重新起名,包名也就不重要了,java.util.UtilDate 和 java.sql.SqlDate
  • 包访问权限(X)
    • 大家都不用,好像不是很重要,可以去掉

(2)scala中package的作用

在java中package语法过于简单,但是scala基于java开发,不能够省略,所以scala中给package更加重要的功能

  • 可以在源码文件中多次使用package关键字

    package com.hike.bigdata.scala.chapter06
    

    等同于

    package com
    package hike
    package bigdata
    package scala
    package chapter06
    
  • 源码的物理路径和包名没有关系

    package com
    package hike
    package bigdata
    package scala
    package chapter07
    

    写错也没有关系,可以正常运行

  • 明确包的作用域,可以在package的后面添加大括号,体现包之间的父子关系,java中的包名之间的.表示调用,没有父子关系

    package com
    package hike
    package bigdata
    package scala {
      package chapter06 {
        object Scala01_Object_Package {
          def main(args: Array[String]): Unit = {
            println("anythings")
          }
        }
      }
    }
    
  • 同一个源码中,子包可以直接访问父包中的内容

    package com.hike.bigdata.scala.chapter06
    package com
    package hike
    package bigdata
    package scala {
          class Test{
            def test(): Unit ={
              println("test...")
            }
          }
      package chapter06 {
        object Scala01_Object_Package {
          def main(args: Array[String]): Unit = {
            new Test().test()
          }
        }
      }
    }
    
  • scala可以将包当成对象来用,可以直接声明属性和方法,在包对象中声明的属性和方法,在这个包下其他类中可以直接使用

2 导入

(1)java中import的功能

  • 导入其他包中的类
    • 若没有包,则不需要导类了
  • 静态导入
    • 当父类和子类存在同名方法,静态导入会引起歧义

(2)scala中import的功能

在java中,import功能比较单一,但是不能省略,所以import在scala中被赋予了更多的功能

  • 星号在scala中有特殊用途,所以不能使用在import语法中,需要用特殊的符号下划线代替星号

    import java.util._
    
  • import关键字可以在任何地方使用

    import java.util.Date
    new Date()
    
  • 可以在一行中导入同一个包中的多个类

    import java.util.{ArrayList,List,LinkedList}
    new ArrayList()
    
  • 导包,对应于package中的包对象,则以下语句可以理解为把一个util对象导入了进来

    import java.util
    new util.ArratList()
    
        import java.util._
        import java.sql._
        new Date()
        new util.ArrayList()
        new Timestamp(111)
    

    出现错误,一个类被导入了两次,则有了隐藏类

    it is imported twice in the same scope by
    import java.sql._
    and import java.util._
        new Date()
    
  • 隐藏类,遇到Date类屏蔽它,不运行

        import java.util._
        import java.sql.{Date=>_,_}
        new Date()
        new util.ArrayList()
        new Timestamp(111)
    
  • scala中导入类的操作,是以当前包路径的相对路径方式导入的,如果想使用绝对路径的方式,需要增加特殊操作,前面添加下划线root下划线.

    ​ 在java中,HashMap打印出来是{k = v, k = v},ArrayList是[a, b, c]

    package com.hike.bigdata.scala.chapter06
    
    object Scala01_Object_import {
      def main(args: Array[String]): Unit = {
        println(new _root_.java.util.HashMap())
      }
    }
    package java{
      package util{
        class HashMap{
        }
      }
    }
    
    
  • 给类起别名

        import java.util.{HashMap=>JavaHashMap}
        println(new JavaHashMap())
    

(3)类加载的双亲委派机制

java中的三个类加载器:

  • 启动类加载器(java的核心类库)由c语言开发
  • 扩展类加载器(java的扩展类库)由java开发
  • 应用类加载器(java的classpath中的类)由java开发

当自定义一个类,包名和类名都和java原生的类相同,如java.lang.String,当需要导入这个类时会导入java的String类,而不会导入自定义的类。因为String类中有其他类使用的各种方法,但是自定义的类并没有,会使其他的类也不能执行,称为“污染”,如果自定义的类和java的类产生冲突,选择哪一个,这时就会启动双亲委派机制。

首先,需要判断当前的类属于什么类,如为classpath中的类,则需要使用应用类加载器去加载,为了防止冲突,不会马上由应用类加载器去加载,会委派上一层的扩展类加载器加载,观察是否可以加载到这个类,当扩展类加载器收到请求时,为了防止冲突,同样会委派启动类加载器去加载,之后,它会在java的核心类库中查找这个类,如果找到会将其放到方法区内存当中,可以直接使用;找不到,会向下一级返回null,表示没有找到,之后扩展类加载器会在java的扩展类库中查找,加载到直接使用,加载不到,会抛出异常,应用类加载器会尝试捕获这个异常,尝试在类路径中查找这个类,找到可以直接使用,找不到将异常抛给java虚拟机,异常叫做ClassNotFoundException。

3 类

使用class关键字可以声明类,通过new的方式构造类的对象

java中一个源码文件中的公共类(类名和文件名完全相同)只能有一个,scala中没有这种约束

scala中的源码可以声明多个类,且可以声明多个公共类,名称可以和文件不一样

package com.hike.bigdata.scala.chapter06

object Scala04_Object_Class {
  def main(args: Array[String]): Unit = {
    val test = new Test()
  }
  class Test{

  }
}
class A{

}
class B{

}
class C{

}

4 属性

所谓的属性就是类中的变量,在编译时,编译器会将变量编译为类的私有的属性,同时编译出公共的方法,功能就是对应的set,get方法

给类的属性赋值,等同于调用对象属性的set方法

访问类的属性时,等同于调用对象属性的get方法

def main(args: Array[String]): Unit = {
    val test = new Test()
    print(test.name)
    test.name = "lisi"
    println(test.name)
  }
  class Test{
    var name : String = "zhangsan"
    val age : Int = 30
  }

使用val声明的类的变量,在编译时会给属性添加final关键字,编译器不会提供属性的set方法,取值不能修改,以上两句反编译为

private String name = "zhangsan"
private final int age = 30

scala中变量必须显式的初始化,如果希望类的属性和java一样由系统进行初始化,而不是手动赋值,所以采用特殊符号,下划线。

var email : String = _

java bean规范:属性私有化,提供set,get方法,两方法必须以set或get开头

这种要求主要与反射有关,反射一般用于架构设计,架构设计的目的就是通用化

比如想使用sql语句获得某一类的id,name,age值,如果方法没有规则,不方便取出这些值

sql => cols => [id,name,age] => getId, getName, getAge

scala中给属性提供的set,get方法不遵循bean规范,那么就不能再架构中使用,所以scala做了一些妥协,添加注解就会出现set,get方法

@BeanProperty var email : String = _

test.setEmail("XXX")
test.getEmail()

5 访问权限

所谓的访问权限,其实就是权利和限制,指的是方法的调用者和方法的提供者的关系

(1)java中的访问权限

  • private: 本类
  • default: 本类,本包
  • protected: 本类,本包,子类
  • public: 任意
package com.hike.bigdata.scala.test;

public class TestAccess {
    public static void main(String[] args) {
        Object aa = new AA();
        aa.clone()
    }
}
class AA{

}

Object类中clone方法的权限为protected,但在上述代码中却不能够使用

方法的提供者:java.lang.Object

方法的调用者:是com.hike.bigdata.scala.Test.TestAccess而不是com.hike.bigdata.scala.Test.AA

.的含义就是“的”,英语为“with”,表示从属关系

两者不同类,不同包,不属于父子类

这里要注意:TestAccess与Object有父子关系,但TestAccess与AA的Object没有父子关系,aa.clone()表示TestAccess想调用AA的Object的clone(),当然不能使用!

note:super在编译时存在,在运行时不存在且由多态可知,子类对象可以指向父类,意味着每一个子类对象的父类应该在自己的那块内存中,不同类的同名父类在不同类内部都存在

在AA类中重写clone方法

package com.hike.bigdata.scala.test;

public class TestAccess {
    public static void main(String[] args) throws CloneNotSupportedException {
        AA aa = new AA();
        aa.clone();
    }
}
class AA{
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

此时,方法的提供者为com.hike.bigdata.scala.Test.AA,方法的调用者为com.hike.bigdata.scala.Test.TestAccess,不同类,同包,所以aa可以使用clone

(2)scala中的访问权限

  • private:同类
  • private[包名]:同包,包私有
  • protected:受保护的,同类和子类,没有同包
  • (default) :什么都不写就是公共的,没有public关键字

本类都可以使用

package com.hike.bigdata.scala.chapter06

object Scala06_Object_Access {
  def main(args: Array[String]): Unit = {

    class Test{
      private val name1:String = "zhangsan"
      private[chapter06] val name2:String = "zhangsan"
      protected val name3:String = "zhangsan"
      val name4:String = "zhangsan"
      
      def test(): Unit ={
        println(name1)
        println(name2)
        println(name3)
        println(name4)
      }
    }
  }
}

在本报外部定义类,1,3 不能使用

class OuterTest{
      def test(): Unit ={
        val t = new Test();
        println(t.name1)
        println(t.name2)
        println(t.name3)
        println(t.name4)
      }

在别的包下定义类1,2,3 不能使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

OneTenTwo76

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值