scala简单基础 ---代码版

常识

常量和变量

变量 var 常量 val

var str1:String = "abc";
var str2 = "def";
 str2 = "xyz";
 
val str3:String = "abc";
val str4 = "def";
//str4 = "xy**z";//报错

数据类型

Scala中没有真正的基本数据类型,基本数据类型也是类,其指也是对象

Scala中的数据类型分为基本数据类型和复杂数据类型

基本数据类型有9中:Byte ,Short ,Int,Long , Float , Double ,Char ,boolean ,String

string类型有两种用法

val str1 = "abc\r\ndef\tgh\\i";

val str2 = """abc

        def gh\i"""

操作符

操作符即方法

 val num1 = 3 + 2;

  val num2 = 3.+(2);

方法即操作符 语法糖

val str1 = "abcdefg";

val c1 = str1.charAt(2);

val c2 = str1 charAt 2;

点都可以用空格代替

操作符优先级

 * / %      +-       =!        <>       &       ^     |

优先级相同从左到右从上到下

特殊运算符

:结尾的是特殊的函数 这种方法是由操作数调用传入左操作数

a +: b ==> b.:+(a)

操作符类型
中缀操作符

       3.+(2) --> 3 + 2

后缀操作符

      str1 toUpperCase --> str1.toUpperCase();

前缀操作符
前缀操作符 指 操作符在唯一的操作数之前
有四种 + - ! ~

                 val n1 = -3; -->  val n1 = 3.unary_-
		         val n2 = +5; -->  val n2 = 5.unary_+

内建控制结构

Scala内建结构以够用为主,因为可以自建控制结构

if判断

大体与Java相似但是可以携带返回值 r1

val num = 99;
val r1 = if(num<10){
   "哈哈"
}else if(num<100){
"呵呵"
 }else{
"嘻嘻"
 }
println(r1)

while do…while 循环

与Java相似

var i = 0;
var sum = 0;
while(i <= 10){
  sum += i;
  i += 1;
}
print(sum)

注意: 因为while与do while都会与外界数据接触,很有可能产生额外的影响,破坏函数封闭的特点,所以要尽量减少使用 通常使用递归来实现

def mx(i:Int,sum:Int):Int = {
        if(i>10) return sum;
        else mx(i+1,sum + i);
}
var r = mx(0,0)
println(r)

for循环

和Java使用上有点不同

for(i <- 1 to 10){     //普通循环
       println("abc"+i)
} 
val l1 = List(1,3,5,7,9);
for(n <- l1){          //增强版 -》循环遍历列表
       println(n)
}
//案例:遍历0到100的偶数
for(i <- 0 to 100; if i % 2==0){        // for里边加判断
       println(i)
}
//案例:遍历0到100的偶数 过滤出其中大于30小于80的数]
for(i <- 0 to 100; if i % 2==0;if i>30;if i<80){
       println(i)
}
//案例:实现九九乘法表
for(i <- 1 to 9){                //  嵌套
 for(j <- 1 to i){
   print(j +" * "+ i +" = "+i*j +" ")
 }
 println()
}

for(i <- 1 to 9; j <- 1 to i){
       print(j +" * "+ i +" = "+i*j +" ")
}

流量变量定义

//--流间变量定义
for(i <- 1 to 9; j <- 1 to i;val str = "\r\n"){     // 新嵌套
        print(j +" * "+ i +" = "+i*j + str) 
}
for(i <- 1 to 9; j <- 1 to i;val str = if (i==j) "\r\n" else " "){
        print(j +" * "+ i +" = "+i*j + str)
}

制造新的集合:通过yield 将for每一次的结果组合成集合

//--yield关键字创建新集合
val r = for(i <- 1 to 9;j <- 1 to i) yield {   
        j +" * "+ i +" = "+i*j
}
println(r)

try…catch…finally

val r = try{                        //可以拥有返回值
  val i = 1/0;
}catch{
  case t: NullPointerException => "空指针异常!"
  case t: IOException => "IO异常!"
  case t: RuntimeException => "其他运行时异常!"
  case t: Throwable  => "其他异常或错误!"
}finally{
  "无论如何都执行 finally"
}
println(r)

match

与Java中switch case相似

val str = "def"
val r = str match {
  case "abc" => "哈哈哈" 
  case "def" => "呵呵呵"
  case "ghi" => "嘻嘻嘻"
  case _ => "啦啦啦"
}
println(r)

类continue与break

因为Scala中没有这两者所以可以想办法使用布尔值来实现

//案例:将0到100的奇数进行累加  但如果累加值超过了50,则不再进行累加,求最后的累加的值
var x = 0;
var sum = 0;
var  flag = true;
while(x < 100 && flag){
        if(sum>50){
                flag = false;
        }
        if(x % 2==0){
        //
        }else{
                sum += x;
        }
        x += 1;
}
print(sum)

注意:因为这样引入了外部数据破坏函数封闭的特点所以不推荐
官方推荐使用闭包的方式

def mx(i:Int,sum:Int):Int={
  if(sum>50 || i >100) sum;
  else if(i % 2 ==0) mx(i+1,sum)
  else mx(i+1,sum+i)
}
val r = mx(0,0)
println(r)

访问范围问题

val str = "abc";          
if(3>2){
        println(str)
        val str = "xyz";   //上边的str这里不能用
}
println(str)

Scala api 集合

Array数组

包含类型相同长度固定内容可变的数据的数据结构 底层由数组实现
定长数组:scala.collection.immutable.Array,一旦声明之后长度不可变
变长数组:scala.collection.mutable.ArrayBuffer,动态数组

定义+访问+遍历+修改+多维

//1.定义数组
val arr1 = new Array(3);
val arr2 = Array("aaa","bbb","ccc");

//2.访问Array
val arr3 = Array("aaa","bbb","ccc");
println(arr3(1))

//3.遍历Arrray
val arr4 = Array("aaa","bbb","ccc","ddd","eee");
for(str <- arr4){
  println(str)
}
for(i <- 0  to arr4.length-1){
  println(arr4(i))
}

//修改
val arr5 = Array("aaa","bbb","ccc","ddd","eee");
arr5(2) = "zzz"   //因为可变所以直接在原来上边改
arr5.foreach(println(_));

//多维  
val arr6 = Array.ofDim(3,4);

其他方法

def concat[T]( xss: Array[T]* ): Array[T]

        连接所有阵列成一个数组。
        
def copy( src: AnyRef, srcPos: Int, dest: AnyRef, destPos: Int, 
length: Int ): Unit

        复制一个数组到另一个。相当于Java的System.arraycopy(src, srcPos, dest, destPos, length).
        
def empty[T]: Array[T]

        返回长度为0的数组

def iterate[T]( start: T, len: Int)( f: (T) => T ): Array[T]

        返回一个包含一个函数的重复应用到初始值的数组。

def ofDim[T]( n1: Int ): Array[T]

        创建数组给出的尺寸。

def ofDim[T]( n1: Int, n2: Int ): Array[Array[T]]

        创建了一个2维数组

def ofDim[T]( n1: Int, n2: Int, n3: Int ): Array[Array[Array[T]]]

        创建3维数组

def range( start: Int, end: Int, step: Int ): Array[Int]

        返回包含一些整数间隔等间隔值的数组。

def range( start: Int, end: Int ): Array[Int]

        返回包含的范围内增加整数序列的数组。

def tabulate[T]( n: Int )(f: (Int)=> T): Array[T]

        返回包含一个给定的函数的值超过从0开始的范围内的整数值的数组。

def tabulate[T]( n1: Int, n2: Int )( f: (Int, Int ) => T): Array[Array[T]]

        返回一个包含给定函数的值超过整数值从0开始范围的二维数组

List

包含类型相同长度固定内容不可变的数据的数据结构 底层用链表实现
定长列表:scala.collection.immutable.List,一旦声明之后长度不可变
变长列表:scala.collection.mutable.ListBuffer,变长列表

定义+访问+遍历+连接+插入+删除+修改

//1.定义List
val l1 = List(1,3,5,7,9);
l1.foreach(println(_))
 
val l2 = 2 :: 4 :: 6 :: 8 :: Nil;
l2.foreach(println(_))

//2.访问List
val l3 = 2 :: 4 :: 6 :: 8 :: Nil;
println(l3(2))

//3.遍历List
for(n <- l3){
        println(n)
}
for(i <- 0 to l3.length-1){
        println(l3(i))
}

//4.连接List
val l4 = 1::3::5::7::9::Nil;
val l5 = 2::4::6::8::10::Nil;
val l6 = l4 ::: l5;
l6.foreach(println(_))

//5.插入元素
val l7 = 2::4::6::8::Nil;
val l8 = 0 +: l7 :+ 10;
l8.foreach(println(_))

//6.删除元素
val l9 = 2::4::6::8::10::12::14::Nil;
val l10 = l9.drop(2)          //左开始
l10.foreach(println(_))
val l11 = l9.dropRight(2);    //右开始
l11.foreach(println(_))

//7.更新元素
val l12 = 2::4::6::8::10::Nil;   
val l13 = l12.updated(2, 9);   //在下标2出插个9
l13.foreach(println(_))

注意:结尾 Nil
List本身无法修改,这些修改数据操作都是创建了一个新List

ArrayBuffer ListBuffer

val buf = ArrayBuffer(1,3,5,7,9);
buf +=   11
val arr =  buf.toArray;
arr.foreach(println(_))
buf.clear();

Map

定义+访问+遍历+追加+删除与zip

//定义
val m1 =  Map(1->"aa",2->"bb",3->"cc",4->"dd",2->"ee",5->"ff");
m1.foreach(println(_))

//访问
val m2 =  Map(1->"aa",2->"bb",3->"cc",4->"dd",5->"ff");
println(m2(3))

//遍历
val m3 =  Map[Int,String](1->"aa",2->"bb",3->"cc",4->"dd",5->"ff");//[]泛型
for(kv  <- m3.iterator){
println(kv._1,kv._2)
}
for(k  <- m3.keys){
println(k,m3(k))
}
m3.foreach((t:(Int,String))=>{println(t._1+"~"+t._2)})

//追加
    val m4 =  Map[Int,String](1->"aa",2->"bb",3->"cc");
    val m5 = m4 + (4->"dd");
    m5.foreach((t:(Int,String))=>{println(t._1+"~"+t._2)})

//删除
    val m6 =  Map[Int,String](1->"aa",2->"bb",3->"cc");
    val m7 = m6 - 2;    //删除第二个
    m7.foreach((t:(Int,String))=>{println(t._1+"~"+t._2)})

//zip  把两个List组合成Map
val l1  = List(1,2,3,4,5)
val l2 = List("a","b","c","d","e")
val m8 = l1.zip(l2).toMap;
m8.foreach((t:(Int,String))=>{println(t._1+"~"+t._2)})

Tuple元组

所谓的tuple就是 一组值的集合,可以表示一条由多个值组成的数据结构,可以存储多种数据

创建+访问

val t1 =  (1,"zs","bj",19,123.56,new Date());
val t2 = 3->"xxx"->6     //多层
println(t1._2)

set

val s1 = Set(1,3,5,7,9,3,6,9,10)
s1.foreach(println(_))

Range

代表一个数据的范围
在Int类型上提供了to方法,通过该方法可以生成一个Range

val r1 = 10 to 100;
r1.foreach { println(_) }
 
for(i <- 0 to 10){
        println("a"+i)
}

vector

Scala2.8为了提高list的随机存取效率而引入的新集合类型(而list存取前部的元素快,越往后越慢)

val v1 = Vector(1,3,5)
v1.foreach(println(_))
val v2 = 0 +: v1 :+ 6;
v2.foreach(println(_))

集合类的通用方法

Exists

判断 返回布尔值

//1.exists 判断集合中是否存在符合指定条件的数据
val l1 = List(1,2,3,4,5,6,7,8,9);
val r1  = l1.exists(_>10);  
println(r1)

sorted排序

//2.Sorted sortWith 排序
val l2 = List(1,3,2,34,6,23,657,23,11);
val l3 = l2.sorted      //升
l3.foreach(println(_))
 
val l4 = l2.sortWith((n1:Int,n2:Int)=>{n1>n2})  //降
l4.foreach(println(_))

Distinct去重

//3.Distinct去重
val l5 = List(1,3,5,2,4,6,4,2,1);
val l6 = l5.distinct
l6.foreach(println(_))

Reverse、ReverseMap反转

//4.Reverse、ReverseMap反转
val l7 = List(1,2,3,4,5);
val l8 = l7.reverse
l8.foreach(println(_))
val l9 = l7.reverseMap(_*10) //反转之后再操作
l9.foreach(println(_))

Contains、startWith、endWith 判断是按条件包含

//5.Contains、startWith、endWith 判断是按条件包含
val l10 = List(1,3,5,7,9)
val r1 = l10.contains(4)
println(r1)
val r2 = l10.startsWith(List(1,3))
println(r2)
val r3 = l10.endsWith(List(9,10))
println(r3)

交集intersect 差集diff 并集union

val l1 = List(1,3,5,7,9);
val l2 = List(5,7,9,11,13);
 
val l3 = l1.intersect(l2);
l3.foreach(println(_))
 
val l4 = l1.diff(l2)
l4.foreach(println(_))
 
val l5 = l1.union(l2)
l5.foreach(println(_))

集合中的高阶函数

partition拆分

将一个集合按一个布尔值分成两个集合,满足条件的一个集合,其他另外一个集合

//1.partition拆分
val l1 = List(1,2,3,4,5,6,7,8,9);
val l2 = l1.partition(_%2==0)
println(l2)

map映射

通过给定函数映射,把一个集合转换为另外一个集合。集合中元素个数不变。

val l1 = List(1,2,3,4,5);
val l2 = l1.map { _*100 }
println(l2)

filter、filterNot过滤

用来过滤数据,要求函数返回布尔值,true留下,false丢弃。

//3.filter、filterNot过滤
val l1 = List(1,3,56,23,65567,234,23);
val l2 = l1.filter { _<100 }
val l3 = l1.filterNot { _<100 }
println(l2)
println(l3)

reduce化简

重复运算,将上次的运行结果和下一个值进行运算

//4.reduce化简
val l1 = List(1,2,3,4,5);
val r1 = l1.reduce(_*10+_)
println(r1)

par多线程并发

在scala中如果有大量重复的操作,且这些操作满足交换律和结合律,则可以通过par操作自动开启多线程,并发的执行处理

//5.par多线程并发
val l1 = (1 to 1000000)
val begin = System.currentTimeMillis()
val r1 = l1.par.reduce((x1:Int,x2:Int)=>{println(Thread.currentThread().getName);x1+x2;});
println(r1)
val end = System.currentTimeMillis()
println(end - begin)

groupBy分组

分组操作可以将集合中的数据按照指定方式分组

//6.groupBy分组
val l1 = List(1,2,34,6345,324,12,54,2345,3);
val r1 = l1.groupBy { _<100 }
println(r1)
 
val l2 = List(("bj"->"zs"),("sh"->"lisi"),("gz"->"ww"),("bj"->"zl"),("gz"->"qq"))
val r2 = l2.groupBy(_._1)
println(r2)

mapValues值映射

对map集合中的值做映射,操作

//7.mapValues值映射
val m1 = Map("zs"->19,"ls"->20,"ww"->18);
val m2 = m1.mapValues(_+1)
println(m2)

案例:利用scala集合类的高阶函数实现numberCount

l1 = List(1,2,3,4,5,2);
val l2=l1.map((_,1))   //组合成一个新的集合后边带个数字1  
.groupBy(_._1)         //按照集合中 第一个分组 成映射 相同的放在一个value中
.mapValues(_.map(_._2))  //只要value中的第二个组成新的集合
.mapValues(_.reduce(_+_)) //把value中的几个值相加
.foreach(println(_)); 

函数基础

函数的概念与定义

概念

函数其实是一段具有特定功能的代码的集合
函数修饰符 函数名 函数参数列表 函数返回值声明 函数体 组成
可以将一段代码 组合成函数 在后续需要时 调用函数 执行代码 实现相应功能

定义

格式
[override][final][public/private/protected] def 函数名(参数列表):返回值声明 = {函数体}

通过def 关键字声明这是一个函数
通过 private/protected 设定访问权限 默认public 也可跟上override重写与final最终修饰符

函数基本定义

正常
def sum1(n1:Int,n2:Int):Int={  
        return n1 + n2;
}
稍简

在函数体中return关键字在不造成歧义的情况下可以省略,如果省略,则函数会将最后一个表达式的值作为返回值返回

def sum1(n1:Int,n2:Int):Int={
    n1 + n2;
}
再简

大部分的情况下,函数可以自动推断返回值的类型,所以返回值类型的声明可以省略

def sum1(n1:Int,n2:Int)={
        n1 + n2;
}

如果函数体只有一行内容,则包裹函数体的大括号可以省略

def sum1(n1:Int,n2:Int)= n1 + n2;
或者

如果返回值的类型是Unit,则 :Unit= 可以省略

def sum1(n1:Int,n2:Int){
    println(n1 + n2)
}

函数进阶定义

格式:
(参数列表) => {函数体}

上代码
(n1:Int,n2:Int)=>{n1 + n2}

如果函数体只有一行内容,则包裹函数体的大括号 可以省略

(n1:Int,n2:Int)=>n1 + n2

如果函数的参数类型可以自动推断,则函数参数类型声明可以省略

(n1,n2)=>n1 + n2 //此处报错,并不是写法有问题,而是没有办法推断参数类型,所以报错
                 //详细请看高级用法

如果函数的参数列表中只有一个参数,则在不引起歧义的情况下,包裹参数类型声明的小括号可以省略

str:String=>str.toUpperCase()

函数的使用

函数在scala中共有四种用法:成员方法 本地方法 函数值 高阶函数

成员方法

scala中将函数作为类的一个成员,就称为函数称为了类的成员方法

/**
 * 函数功能1 : 作为类的成员方法使用
 */
class Person{
  private val name:String = "";
  private val age:Int  = 0;
  def say(){
    println("说...")
  }
  def eat(food:String){
          println("吃..."+food)
  }
}

本地方法

cala中可以在函数内部再定义函数 这种用法称之为 函数 用作 本地方法

def eat(food:String){
  def cook(food:String)={   //嵌套方法又称(小递归)哈哈哈瞎说的
     "熟的"+food;
  }
  println("吃..."+cook(food))
}

函数值

函数本身可以理解为是一个对象,而函数名是指向这个对象的一个引用,所以可以将函数任意赋值给其他引用.

def sum(n1:Int,n2:Int):Int={
  return n1 + n2;
}
val sum2 = sum(_,_);    
val r = sum2(2,3)
print(r)

****************************************
****************************************
val sum3 = (n1:Int,n2:Int)=>{n1+n2}
val r = sum3(2,3)
println(r)

高阶函数

在scala中 函数可以作为另一个函数参数传递 或 作为另一个函数的返回值返回

//高阶函数 - 将一个作为另一个方法的参数进行传递
def cook(food:String,howtocook:(String)=>String)={
      howtocook(food);
    }
    def how(food:String)={
      "烤熟的"+food
    }
    val r1=cook("猪肉串",how)
//    println(r1)
    val r2=cook("韭菜",(food:String)=>{"壮阳的"+food})
//    print(r2)
    val r3=cook("馒头",food=>{"蒸好的"+food})  //参数类型可以直接推断所以不用写
//    print(r3)                           
    val r4=cook("刀削面",sx=>"大同的"+sx)
    print(r4)

//高阶函数 - 将一个作为另一个方法的返回值进行返回
def look(cook:String):(String)=>String={     //: 后边的是一个方法返回值它的参数是String返回值也是String
    if(cook=="烤串"){
      def cookf(food:String) = {
        "烤熟的" + food
      }
      cookf          
    }else if(cook=="方便面") {
      (food:String)=>{
        "统一"+food}      
      }else{
        food=>"热的"+food;
      }

    }

理解

在java语言中,函数只能作为成员方法存在,不能用作局部属性 或方法参数 或方法 返回值,功能更不完整,是二等公民
在scala中,函数可以 用作成员方法 可以用作局部属性 可以只用作 方法参数 和方法返回值,具有完整的功能,是一等公民
在这里插入图片描述

函数的使用升级

占位符

下划线 _ 是一个功能非常强大的特殊符号,可以在很多场景下实现非常好的占位符效果
使用占位符,替代函数直接量中的参数声明
当函数直接量中的所有参数 都只在函数体中使用过一次 且严格按声明顺序使用,则可以使用_替代这些参数

def cook(food1:String,food2:String,how2Cook:(String,String)=>String) = {
   how2Cook(food1,food2)
 }
 val r = cook("羊肉串","孜然","烤熟"+_+"撒"+_)
 val r2 = cook("羊肉串","孜然",(f1,f2)=>{"撒"+f2+"烤"+f1+"撒"+f2})
 //此处无法使用_替代函数直接量中的参数
 println(r)

使用占位符,替代函数值用法中的部分或全部参数列表

def sum(n1:Int,n2:Int,n3:Int):Int={
return n1 + n2 * n3;
}
val sum2 = sum(_,_,_);
val r1 = sum2(2,3,4)
println(r1)
val sum3 = sum(_:Int,_:Int,10);
val r2 = sum3(2,3)
println(r2)

重复参数

重复参数类似与java中的可变参数,可以将最后一个参数设置为重复参数,来接收0到多个该类型的参数值,在方法内部可以按照类似数组方式访问这些参数。

def sumx(nums:Int*):Int={
        var sum = 0;
        for(n <- nums){
          sum += n;
        }
        return sum;
}

尾递归

如果在递归时,保证函数每次递归最后一步操作都是在递归调用自己 则称这样的递归为尾递归 。
尾递归效率更高 ,应该尽力的取实现尾递归。

//案例:将0到100的奇数进行累加  但如果累加值超过了50,则不再进行累加,求最后的累加的值
def sumx(i:Int,sum:Int):Int={
  if(sum>50 || i > 100) sum;
  else if(i % 2 != 0){
          println("本次递归")
          sumx(i+1,sum+i)
  }
  else {
          println("本次递归")
    sumx(i+1,sum)
  }
}
val r  = sumx(0,0)
println(r)

闭包

函数应该是一个尽量封闭 独立 可靠 的结构,而当函数使用了外部变量时,会造成函数本身没有完成封闭,直到函数被调用,真正要执行时,才取将外部变量的值获取到,以用来封闭函数,执行代码。这个过程称之为函数完成了闭包的过程。
闭包不是一种技术 而是一种现象。
闭包这种现象在很多时候都是有害,体现在,外部变量的变化造成程序执行时的不稳定 。以及 在使用高阶函数时,引用外部 环境变量,造成整个环境无法被释放,造成内存泄漏的等问题。
所以在函数编程开发过程中 ,要充分考虑闭包可能带来的危害,尽力去避免。

柯里化 - 自定义控制结构

柯里化

所谓的柯里化就是将一个函数的参数列表 拆分成多个参数列表的过程

def addAndPrint(x:Int,y:Int,z:(Int)=>Unit) = {
        val sum = x+y
        z(sum)
}
def addAndPrint2(x:Int,y:Int)(z:(Int)=>Unit) = {
        val sum = x+y
        z(sum)
}
def addAndPrint3(x:Int)(y:Int)(z:(Int)=>Unit) = {
        val sum = x+y
        z(sum)
}
自定义控制结构

在scala中并没有提供太多的内置控制结构,而是以够用为标准提供了最基本的控制结构。
可以在scala中通过 高阶函数 + 柯里化 来实现自定义控制结构
在函数的参数列表中的最后 一项为高阶函数,通过柯里化,将最后 一个参数独立为一个单独的参数列表。在调用时,单独传入高阶函数,而最后一个参数列表中只有一个参数,可以将包裹它的小阔号写成大括号,则整个函数调用看起来就像一个自定义的控制结构一样。

def  addAndPrint2(x:Int,y:Int)(z:(Int)=>Unit){
        val r = x+y;
        z(r)
}
addAndPrint2(2, 3){
        println(_)
}

面向对象

基础

类的声明

Scala的类和Java中的类基本相同,用class关键字来声明类,同样拥有成员属性成员方法,可以通过new来创建对象,同样可以限定访问权限,不同之处只有privateprotected关键字,默认public

class Person4{
   private val name:String =  "";
   private val age:Int = 0;
   def say(){
     println("说...")
   }
   def eat(food:String){
           println("吃..."+food)
   }
}

Scala中没有static关键字,也即,Scala中的类不可以包含静态成员
Scala中去掉静态后,针对于类似静态的使用需求,提出了单例对象的概念,模拟实现静态的效果

单例对象

单例对象就是一个独立存在的对象,直接由程序人员编写而成,直接就是一个对象,不需要通过类来创建。
通过object关键字就可以声明出一个单例对象。
单例对象是一个独立存在的对象,不需要new,直接就可以使用,来访问其中的属性和方法

object Car{
 val name:String = "BMW";
 val age:Int =  0;
 def run(){
   println("跑..")
 }
 def stop(){
   println("停..")
 }
}
 
object Demo05 {
  def main(args: Array[String]): Unit = {
    Car.name
    Car.age
    Car.run()
    Car.stop();
  }
}

单例对象可以单独存在,也可以和一个类产生伴生的关系。
只要将单例对象和要伴生的类声明在同一个scala文件中,且单例对象的名字和类名相同,就可以将单例对象和类伴生在一起。
类称之为该单例对象的伴生类,单例对象称之为该类的伴生对象
伴生类和伴生对象之间可以互相访问对方的私有成员。
通过将单例对象和类产生伴生关系,可以在单例对象中增加方法和属性,用起来感觉就像类具有了静态成员 。

/**
 * 单利对象 - 伴生
 */
class Person6{
  private val age = 19
  def eat(){
    println(Person6.name + "吃。。。")
  }
  def say(){
          println("说。。。")
  }
}
object Person6{
        private val name =  "zhang"
  def run(){
    println("跑。。。")
  }
}
 
object Demo06 {
  def main(args: Array[String]): Unit = {
    Person6.run();
    val p = new Person6();
    p.eat();
    p.say();
  }
}

类的构造器

主构造器

scala中可以在类的声明过程中在类名后跟上小阔号,指定类的构造函数参数。
而在类的内部,不归属于任何成员属性和成员方法的内容都会被收集起来称为类的构造函数的体。

class Person7(name:String,age:Int){
  def say(){}
  def eat(){}
  println("对象被创建了。。。name:"+name+"age:"+age+"..")  //体
}
辅助构造器

可以通过 def this() 来声明一个辅助构造器
所有的辅助构造器的第一行代码 都要来调用在其之前声明的其他构造器,所以任意的辅助构造器,其实最终都会调用主构造器上。

class Person7(name:String,age:Int){
  def this(name:String){
    this(name,0);
    println("辅助构造器1号 。。。")
  }
  def this(){
    this("x");
    println("辅助构造器2号 。。。")
  }
  def say(){}
  def eat(){}
  println("对象被创建了。。。name:"+name+"age:"+age+"..")
}
 
object Demo07 {
  def main(args: Array[String]): Unit = {
    val p1 =  new Person7("w",20);
    val p2 =  new Person7("w");
    val p3 =  new Person7();
  }
}

属性即无参方法 无参方法即属性 - 万物皆函数(有那味儿了)

在类中成员属性可以想象成一个无参的方法 而类中无参的方法只要没有用到外界变量也没有产生任何其他副作用 因为他的执行等价于一个固定的值 也可以想象成一个属性
也即 属性即无参方法 无参方法即属性
而scala面向对象,万物皆对象,而对象包含属性和方法,而方法即函数,属性又可以理解为无参函数,所以可以说,scala中,万物皆函数。

那么在声明一个参数为空的成员方法时要不要加上括号呢?
这会影响为来使用这个方法的人的感受
官方建议是:
如果这个方法没有使用其他外部变量 也没有产生任何副作用 也就是说完全可以把他想象成一个属性 那么这种情况下 不要写括号 让外界使用者感觉这就像一个属性一样 屏蔽掉底层其实是个方法的细节
如果这个方法用到了其他外部变量或会产生任何副作用 那么在声明的时候加上括号 防止用户在使用这个方法时 误解其为一个成员属性

进阶

scala中也有包的概念,和java中相同,可以用来组织代码结构,实现分包存放代码。

package cn.tedu.scala.xxx
 
object Demo09 {
}
package com{
        package tarena{
                class Person{}
                package zzz{
                  class Student{}
                }
        }
}

引入

scala类似于java,可以通过import关键字引入外部包中的类
scala中的import可以出现在代码任何地方
scala中的import时可以指的是类和包
scala中的import可以重命名或隐藏一些被引用的成员

package cn.tedu.scala
import java.io._;
import java.util.{Date,HashMap};
import java.text.{SimpleDateFormat=>SDF};
 
object Demo10 {
        import java.util.ArrayList;
        def main(args: Array[String]): Unit = {
          val sdf  = new SDF();
        }
}

访问权限

scala类似于Java提供了访问权限控制机制 。
不同于java,只有private protected,如果不写默认public,但public不是关键字,不可以直接使用。
也可以在访问权限控制符之后通过中括号来提供额外的访问权限支持,实现 更精细的访问权限控制。

private [额外允许的包名] xxx
protected [额外允许的包名] xxx

再进阶

继承

类似于java,scala同样支持继承,且只支持单继承。
可以通过extends关键字来实现继承。

class Person11{
  val name = "zs"
  val age = 19
  def eat(){
    println("吃。。。")
  }
}
 
class Student extends Person11{
  def study(){
    println("学。。。")
  }
}

当父类没有无参构造时,需要在子类继承的过程中,明确的传递父类构造函数需要的参数

class Person11(name:String,age:Int){
  def eat(){
    println("吃。。。")
  }
}
 
class Student extends Person11("zs",19){
  def study(){
    println("学。。。")
  }
}

如果不希望写死父类构造参数,也可以基于子类构造参数进行传递

class Person11(name:String,age:Int){
  def eat(){
    println("吃。。。")
  }
}
 
class Student(name:String,age:Int) extends Person11(name,age){
  def study(){
    println("学。。。")
  }
}

抽象类

类似于java,scala同样支持抽象类的使用。
抽象类可以包含普通方法和抽象方法。
抽象类无法直接实例化,通常用来继承。

abstract class Demo9 {
        def eat()
        def say(){
                 println("哈哈")
        }
}

重写和重载

子类中可以覆盖父类的同名同参数的方法,这个过程称之为对父类方法实现重写。
另外,一个类的内部存在多个同名但参数不同的方法,这称之为方法的重载。
重写和java中不同,需要通过override关键字来进行
重载基本和java相同 。

class Person11(name:String,age:Int){
  def eat(){
    println("吃。。。")
  }
}
 
class Student(name:String,age:Int) extends Person11(name,age){
  //重写
  override def eat(){
    println("边学边吃。。。")
  }
  //重载
  def study(){
    println("学。。。")
  }
  def study(subject:String){
          println("学。。。"+subject)
  }
}

多态 和final

scala中同样多态特性,即,子类可以当做父类来使用。

val stu:Person11 = new Student11("zs",19);

final可以用在成员方法、类本身上 作用和java中相同

scala内置继承结构

scala中所有的类都直接或间接的继承自Any。
Any类中内置了如下方法,由于所有的类都直接或间接的继承自Any,所以所有类都有这些方法:
final def ==(that:Any):Boolean
final def !=(that:Any):Boolean
def equals(that:Any):Boolean
def hashCode:Int
def toString:String
Any类有两个直接子类,分别是AnyVal和AnyRef
其中AnyVal下又有九个子类以下九个类型 都无法通过new来创建对象。
Byte Short Int Long Float Double Char Boolean Unit
而其他类型都继承自AnyRef,其实AnyRef就是java中的Object
存在一个特殊的类型,scala.Null,它只有一个值就是null。这个scala.Null时所有AnyRef类型是子孙。
存在一个 特殊的类型,scala.Nothing,这个类型没有任何的值,表示当某个方法向上抛出异常时,当前方法的返回值类型。这个scala.Nothing,是有类公共的子孙。
在这里插入图片描述

特质 - trait

scala中的特质,类似于java中的接口
但是scala中的特质和java中的接口也有一定的区别。
scala中的特质既可以 包含抽象方法也可以包含非抽象方法
一个类可以混入多种特质。
和抽象类相比,抽象类和特质都可以包含抽象方法和非抽象方法,但抽象类只能继承一个,而特质可以混入多个。但这并不意味着特质可以替代抽象类。在真正的开发中,抽象类通常用于满足继承结构,而特质用户在当前类中增加新的特性。

abstract class Person{
  def eat(){
    println("吃。。。。")
  }
  
  def say();
}
 
abstract class Student extends Person {
  def say(){
    println("学生叽叽喳喳的说...")
  }
  def study(){
          println("学生闷头学...")
  }
}
 
trait codeSkill{
  def code();
  def run(){
    println("运行代码。。。")
  }
}
 
trait  driveSkill{
  def drive(){
    println("开车狂飙。。。")
  }
}
 
class Big_Student extends Student with codeSkill with driveSkill{
  def code() {
    println("写代码。。。")
  }
}
 
 
object Demoo12 {
  def main(args: Array[String]): Unit = {
     val s  =  new Big_Student();
     s.eat();
     s.say();
     s.study();
     s.code();
     s.drive();
  }
}

其他语法

lazy

懒加载 懒执行
正常情况下通过val 和 var定义的属性都会直接分配空间 即使这个属性要在很久以后才使用,这样就会造成内存空间白白被占用
这种情况下可以加上lazy关键字 延后变量/常量赋值的位置 这样直到后续真正用到这个量时才真正开辟空间赋值 减少了内存的浪费

lazy val str = "abc";

Option

在Scala中用来表示一个结果,它可能有值,也可能没有值,它有两个子Option
Some(x)
None
通过将函数的返回值设定为Option,可以通过Some带回正常值 ,通过None表示非正常的值,从而减少异常机制的使用,简化程序

def div(n1:Int,n2:Int):Option[Int] = {
  if(n2 ==0){
    None   
  }else{
    Some(n1/n2)   
  }
}
val r = div(1,0).getOrElse(0)
println(r)

caseclass 样例类

只要在声明类时 在class关键字前加上case关键字 这个类就成为了样例类
样例类和普通的区别在于:
默认实现序列化接口
默认自动覆盖 toString equals hashCode方法
不需要new可以直接生成对象
通常用在声明javabean

case class Person13(){
}

泛型

scala中的泛型和java中的泛型基本一致。

使用

val l1:List[String] = List[String]("a","b","c")  //对List的value确定一个类型
val m1:Map[Int,String] = Map[Int,String](1->"a",2->"b",3->"c")

自定义

class God1{  
  def kill[T](t:T):T={    //方法上的 
    return t;
  }
}

class God2[T]{   //类上的
        def kill(t:T):T={
                        return t;
        }
        def save(t:T):T={
                        return t;
        }
}

class God3[T <: Ani]{   //泛型的上边界
        def kill(t:T):T={
                        return t;
        }
        def save(t:T):T={
                        return t;
        }
}

class God4[T>:Person]{  //下边界
  def kill(t:T):T ={
    return t;
  }
  def save(t:T):T={
    return t;
  }
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值