Scala实战二

一、上次课回顾

二、Scala的变量Var、Val

三、函数、方法

四、面向对象编程

一、上次课回顾

1、注意源数据与元数据的区别,源数据对于Mapreduce来说的话就相当于是输入的数据;而元数据是描述数据的数据

2、Hive对应到MySQL元数据库中的一些表,tbls dbl表的关联关系

3、集群数据规模的评估,多少机器、多少业务线、几副本、数据要存几年,拿到磁盘的空间,需要预留30%-50%的空间,为了避免数据突增;比如系统OOM,mapreduce达到90%阈值时就会变成unhealthy nodes,节点不健康

二、Scala的变量var、val

在Linux的控制台上操作Scala:
1、打开进入Scala后,开始运行计算默认就是从res0开始递增
scala> 1+1
res0: Int = 2
var和val用来修饰对象:

1、使用var修饰对象

1、定义一个人名字为17
scala> var name:String="17"
name: String = 17

2、查看名字
scala> name
res2: String = 17

3、名字修改为17er
scala> name="17er"
name: String = 17er

4、查看已经修改成功
scala> name
res3: String = 17er

2、使用val修饰对象

1、使用val定义人名
scala> val name="17"
name: String = 17

2、修改人名,提示重新分配失败
scala> name="17er"
<console>:12: error: reassignment to val
       name="17er"
           ^

3、Scala不定义类型还能够自己识别类型:

scala> val name = "big tree"

name: String = big tree

小结:

  • val修饰的是不可变的,只能是值,var修饰的可以是变量
  • Scala中的类型是可以自动推导的

2.1、Scala的类型&&类型转换

Scala中的数据类型:

Byte、Char、Short、Int、Long、Float、Double、Boolean

类型转换和类型判断:

1、asInstanceOf表示类型转换:
scala> val b=10.asInstanceOf[Double]
b: Double = 10.0

2、isInstanceOf表示类型判断:
scala> val b=10.5f.isInstanceOf[Float]
b: Boolean = true

三、函数、方法

3.1、函数的定义

  • 通用框架:

      def main(x:Int,y:Int):Int= {
      	if (x>y){
      		x
      	}else{
      		y
      	}
      }
    

解析:

  1. main后面跟着的是 (x:Int,y:Int) 函数的入参,可能没有,可能有多个,入参之间使用逗号分隔;每一个参数的定义:参数名称:参数类型
  2. def:定义函数的关键字、max:函数的名称,需要见名知意
  3. 在两个大括号之间的叫做方法体/函数体,函数体的最后一行表示返回值,return xx
  4. :Int表示的是一个返回值类型
  • 定义sum方法,传入x/y两个参数,然后调用sum方法

      object Function {
        def main(args: Array[String]): Unit = {
            print(sum(2,3))
        }
        
          def sum(x:Int,y:Int):Int={
            x+y
          }
      }
    

注意:

基于IDEA开发要注意返回值,比如你自己定义方法的时候,直接回车的时候自动跳出Unit,如下:

 1、如下是取不到值的:因为返回值是Unit:
 def sum(x:Int,y:Int){
      x+y
    }

3.2、函数的调用

object Function {
  def main(args: Array[String]): Unit = {
 	//调用sayHello方法
       sayHello()
       sayHello	//函数没有入参,不加括号也可以
       sayWorld("john")			
       sayWorld		//不带括号编译都编译不过去
  }
	//定义一个sayHello方法
    def sayHello():Unit={
      println("hello")
    }

	//定义一个sayWorld方法
    def sayWorld(a:String): Unit = {
		println(a + "好开心")
}
}

3.3、函数循环表达式

在Linux上查看:

1、查看到它底层调用的是Range方法:
scala> 1 to 10
res4: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

2、直接使用Range方法:
scala> Range(1,10)
res6: scala.collection.immutable.Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9)

3、还可以加上步长:
scala> Range(1,15,4)
res2: scala.collection.immutable.Range = Range(1, 5, 9, 13)


4、写两个参数代表步长是1,需要步长是-3
scala> Range(10,1)
res4: scala.collection.immutable.Range = Range()

scala> Range(10,1,-3)
res3: scala.collection.immutable.Range = Range(10, 7, 4)
小结:

1 to 10 --> 1.to(10)
1 until 10
Range(1,10)

scala> Range(1,10,0)
java.lang.IllegalArgumentException: step cannot be 0.
at scala.collection.immutable.Range.(Range.scala:86)
at scala.collection.immutable.Range$.apply(Range.scala:439)
… 32 elided

  • 步长不能为0

去到IDEA中的Range源码中寻找:

@param start      the start of this range.
 *  @param end        the end of the range.  For exclusive ranges, e.g. 
 *                    `Range(0,3)` or `(0 until 3)`, this is one
 *                    step past the last one in the range.  For inclusive
 *                    ranges, e.g. `Range.inclusive(0,3)` or `(0 to 3)`,
 *                    it may be in the range if it is not skipped by the step size.
 *                    To find the last element inside a non-empty range,
                      use `last` instead.
 *  @param step       the step for the range.

to的底层就是调用range的:

 def to(end: Int): Range.Inclusive = Range.inclusive(self, end)

四、面向对象编程

4.1、面向对象-从类开始

在Scala中,class来定义类,可以理解为某一种、某一类东西的一个抽象;

如下代码解析:

  1. 先class People,定义出People类,这个类有两个属性,name、city属性;

  2. 这个类有两个方法,sleep和py方法;sleep方法有返回值,无传入参数;py方法有传入参数,无返回值;

  3. 使用这个类的时候要先new一个人出来,val people = new People;然后people.name=“sail”,为new出来的对象进行赋值,sleep方法有返回值,可以直接print进行打印;

  4. people.py(这个方法需要传入一个参数),所有的操作都是基于new出来的这个people对象来的;

     object demo {
     def main(args: Array[String]): Unit={
       val people = new People   //先new出来一个人
       people.name="sail"
       println(people.sleep())
       people.py("sail")
    

    }

     class People{
       //定义了人的属性
       var name=""     //""表示这个人肯定是一个字符串类型
       var city=""
    
       def sleep(): String ={
         name + "正在睡觉ZZZ"
       }
    
       def py(pyname:String)={
         println(name + "正在和" + pyname + "做一些羞羞的事情,,,")
    
       }
     }
    

    }

补充:
1、val 属性=_,表示占位符,如下的都是可以这样使用的

var city = _
//上面这个定义会报错,unbound placeholder parameter,报错的原因,是不知道city的类型是什么,所以会报错

var city:String = _
var city1 = ""
var city2:String = ""

4.2、定义SparkConf

使用面向对象的方法定义SparkConf如下:
解析:
1、定义一个SparkConf类,有appName和master两个属性;
2、定义两个方法,setAppName/setMaster方法,使用this关键字:用于引用当前对象;
3、new一个sparkconf对象,为sparkconf对象赋值,this方法就是赋什么值就打印什么值;
4、定义一个printInfo方法,把传入的两个参数打印出来;

object SparkConfApp {

    def main(args: Array[String]): Unit = {
        val sparkconf = new SparkConf
        sparkconf.setAppName("SparkConfApp")
        sparkconf.setMaster("yarn")
        sparkconf.printInfo()
    }

    class SparkConf{
        var master:String= _
        var appName:String=_

        def setMaster(master:String)={
            this.master=master
        }

        def setAppName(name:String)={
            this.appName=name
        }
        def printInfo()={
            println(this.appName +":"+ this.master)
        }

    }

}

引申到Spark官网上去:

  • http://spark.apache.org/docs/latest/rdd-programming-guide.html#initializing-spark

注意到如下这段话:

val conf  = new SparkConf().setAppName(appName).setMaster(master)
new SparkContext(conf)

在这里插入图片描述
所以上面的面向对象方法需要修改成通用形态:

  1. 在sparkconf中可以设置非常多的东西,做一个通用的方法

  2. 定义一个set方法,有key/value,返回一个SparkConf,之前是一个一个设置–>一批一批设置,key,value的底层结构是map,我们定义一个属性setting,可以支持并发(var setting = new ConcurrentHashMap

  3. setting.put(key,value) -->就可以这样传值;

  4. 此时需要修改printInfo方法,取值的时候也要在setting中去取值,setting.get(“spark.app.name”)

    import java.util.concurrent.ConcurrentHashMap

    object SparkConfApp {

     def main(args: Array[String]): Unit = {
         val sparkconf = new SparkConf
         sparkconf.setAppName("SparkConfApp").setMaster("yarn")
         sparkconf.printInfo()
     }
    
     class SparkConf{
    
         def setMaster(master:String): SparkConf={
             set("spark.master",master)
         }
    
         def setAppName(name:String): SparkConf={
            set("spark.app.name",name)
         }
         def printInfo()={
             val appName = setting.get("spark.app.name")
             val master = setting.get("spark.master")
             println(appName + ":"  + master)
         }
    
         //key,value --> map
         var setting = new ConcurrentHashMap[String,String]()
         def set(key:String,value:String):SparkConf = {
             setting.put(key,value)
             this
         }
    
     }
    

    }

这样就可以把值取出来了,还可以进行修改:

 def main(args: Array[String]): Unit = {
        val sparkconf = new SparkConf
//        sparkconf.setAppName("SparkConfApp").setMaster("yarn")
        sparkconf.set("spark.app.name","SparkConfApp").set("spark.master","yarn")

        sparkconf.printInfo()
    }
扩展语法:private[this]

private[this] def set(key:String,value:String): SparkConf = {
setting.put(key,value)
this
}

这样修改的话:sparkconf.set(“spark.app.name”,“SparkConfApp”)就会出现报错,所以只能那个使用sparkconf.setAppName().setMaster()

private修饰方法只有在class里面使用,外面是使用不了它的

IDEA中查看SparkConf.scala源码:

需要在pom.xml中添加相应的依赖:

<spark.version>2.4.2</spark.version>

<!--Spark Core的依赖-->
<dependency>
  <groupId>org.apache.spark</groupId>
  <artifactId>spark-core_2.11</artifactId>
  <version>${spark.version}</version>
</dependency>
  • 查看源码:
    1、ctrl+o,查看所有的方法
    2、SparkConf中的代码是怎么写的呢,也是先定义一个SparkConf类,有一个私有的setting,查看setMaser方法

    def setMaster(master: String): SparkConf = {
    set(“spark.master”, master)
    }

点击这个set,跳转到相应源码:

private[spark] def set(key: String, value: String, silent: Boolean): SparkConf = {
if (key == null) {
throw new NullPointerException(“null key”)
}
if (value == null) {
throw new NullPointerException("null value for " + key)
}
if (!silent) {
logDeprecationWarning(key)
}
settings.put(key, value)
this
}

4.3、构造方法(主构造器和附属构造器)

我们定义人名、城市的时候是把它放在属性中的,创建People类的时候,直接可以传入它的属性进去;

主构造器:

1、类后面可以直接跟上属性(叫做主构造器),然后new对象的时候属性也能直接跟在后面,打印的时候会先打印主构造器中的方法:

object test {
  def main(args: Array[String]): Unit = {
    val person = new Person("john","苏州")
    println(person.name + "" + person.city)
  }
  class Person(var name:String,var city:String){
    println("people 1")

    println("people 2")
  }

}

附属构造器:在主构造器中的:
object test {
  def main(args: Array[String]): Unit = {
    val person = new Person("john","苏州")
    println(person.name + "" + person.city)
    val person2 = new Person("sail","北京",30)
    println(person2.name + "" + person2.city + "" + person2.age)
  }

//如下的这个就是主构造器
  class Person(var name:String,var city:String){
    println("people 1")

//附属构造器的第一行必须要调用主构造器或者其它附属构造器
    var age:Int = _
    def this(name:String,city:String,age:Int){
      this(name,city)
      this.age=age
    }
    println("people 2")
  }

	打印结果:
people 1
people 2
john苏州
people 1
people 2
sail北京30

说明每执行一次都会调用一次主构造器中的方法
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值