Scala day03
递归和尾递归
- 一个函数/方法在函数/方法体内又调用了本身,我们称之为递归调用
- 在递归的时候,只有递归,没有任何运算,这就是尾递归
- 阶乘
递归算法
1) 方法调用自身
2) 方法必须要有跳出的逻辑
3) 方法调用自身时,传递的参数应该有规律
4) scala中的递归必须声明函数返回值类型
object TestFunction {
def main(args: Array[String]): Unit = {
println(test(5))
}
def test( i : Int ) : Int = {
if ( i == 1 ) {
1
} else {
i * test(i-1)
}
}
}
/**
* guo chao
* Date 2021/5/1
*/
object RecursiveDemo1 {
def main(args: Array[String]): Unit = {
println(factorial(3))
println(factorial1(5,1))
}
//累加器 用来储存中间结果的
@tailrec
def factorial1(n:Int,acc:Int):Int={
if(n==1 ) acc
else factorial1(n-1,n * acc)
}
//计算阶乘
def factorial(n:Long):Long={
if (n==1) 1
else n*factorial(n-1)
}//可能栈溢出
}
/*
递归
1.找到递归的算法
2.在递归函数的内部,一定要有结束条件
3,随着递归的深入,要有机会到达结束条件
尾递归:
Scala会对尾递归做一个优化,尾递归优化,就不会出现StackOverflowError
*/
惰性求值
- 当函数返回值被声明为lazy时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行。这种函数我们称之为惰性函数。
- 案例实操
def main(args: Array[String]): Unit = {
lazy val res = sum(10, 30)
println("----------------")
println("res=" + res)
}
def sum(n1: Int, n2: Int): Int = {
println("sum被执行。。。")
return n1 + n2
}
输出结果
----------------
sum被执行。。。
res=40
/**
* guo chao
* Date 2021/5/1
*/
object LazyDemo {
val a1:Int={println("a1");10}//第一次使用a的时候才会求值
lazy val a2:Int={println("a2");10}
def a3:Int={println("a3");10}
def main(args: Array[String]): Unit = {
println(a1)//第一次,先判断a是否有值,如果没有则去计算,反之直接使用。
println(a1)
println(a1)
println()
println(a2)
println(a2)
println(a2)
println()
println(a3)
println(a3)
println(a3)
/*
惰性求值:
只能用val
主动求值:
系统一旦启动,所有的值都计算出来
好处:后面使用的时候速度快
坏处:拖慢系统的启动速度,浪费内存
*/
}
}
面对对象
定义类
- 基本语法
[修饰符] class 类名 {
类体
}
说明
(1)Scala语法中,类并不声明为public,所有这些类都具有公有可见性(即默认就是public)
(2)一个Scala源文件可以包含多个类 - 案例实操
package com.atguigu.chapter07
//(1)Scala语法中,类并不声明为public,
//所有这些类都具有公有可见性(即默认就是public)
class Person {
}
//(2)一个Scala源文件可以包含多个类
class Teacher{
}
属性
属性是类的一个组成部分
- 基本语法
[修饰符] var 属性名称 [:类型] = 属性值
注:Bean属性(@BeanPropetry),可以自动生成规范的setXxx/getXxx方法 - 案例实操
package com.atguigu.scala.test
import scala.beans.BeanProperty
class Person {
var name: String = "bobo" //定义属性
var age: Int = _ // _表示给属性一个默认值
//Bean属性(@BeanProperty)
@BeanProperty var sex: String = "男"
}
object Person {
def main(args: Array[String]): Unit = {
var person = new Person()
println(person.name)
person.setSex("女")
println(person.getSex)
}
}
/**
* guo chao
* Date 2021/5/1
*/
object objDemo1 {
def main(args: Array[String]): Unit = {
val user:User=new User()
println(user.age)// println(user.name)不能访问
user.age=100
println(user.age)
user.eat()
}
}
class User {
//定义属性,和定义普通的变量完全一样
//@BeanProperty var age: Int = 10
@beanGetter var age: Int = 10
private var name: String = "lisi"
def eat(): Unit = { //给类定义方法
println("eat:" + name)
}
}
构造器
- 基本语法
class 类名(形参列表) { // 主构造器
// 类体
def this(形参列表) { // 辅助构造器
}
def this(形参列表) { //辅助构造器可以有多个…
}
}
说明:
(1)辅助构造器,函数的名称this,可以有多个,编译器通过参数的个数来区分。
(2)辅助构造方法不能直接构建对象,必须直接或者间接调用主构造方法。 - 案例实操
(1)如果主构造器无参数,小括号可省略,构建对象时调用的构造方法的小括号也可以省略。
//(1)如果主构造器无参数,小括号可省略
//class Person (){
class Person {
var name: String = _
var age: Int = _
def this(age: Int) {
this()
this.age = age
println("辅助构造器")
}
def this(age: Int, name: String) {
this(age)
this.name = name
}
println("主构造器")
}
object Person {
def main(args: Array[String]): Unit = {
val person2 = new Person(18)
}
}
构造器参数
-
说明
Scala类的主构造器函数的形参包括三种类型:未用任何修饰、var修饰、val修饰
(1)未用任何修饰符修饰,这个参数就是一个局部变量
(2)var修饰参数,作为类的成员属性使用,可以修改
(3)val修饰参数,作为类只读属性使用,不能修改 -
案例实操
class Person(name: String, var age: Int, val sex: String) {
}
object Test {
def main(args: Array[String]): Unit = {
var person = new Person("bobo", 18, "男")
// (1)未用任何修饰符修饰,这个参数就是一个局部变量
// printf(person.name)
// (2)var修饰参数,作为类的成员属性使用,可以修改
person.age = 19
println(person.age)
// (3)val修饰参数,作为类的只读属性使用,不能修改
// person.sex = "女"
println(person.sex)
}
}
/**
* guo chao
* Date 2021/5/1
*/
object obj3 {
def main(args: Array[String]): Unit = {
val chengwei:User3=new User3(10,"chengwei","male")
println(chengwei.age)
//给类起别名
type U=User3
val u:U=new User3(10,"a","female")
println(u.age)
println(u.getClass.getName)
}
}
class User3(val age:Int,var name:String,sex:String){
//这种构造器:辅构造器
def this(){
this(10,"abc","male") //首行必须是调用主构造器
}
def this(c:Int){
this(10,"abc","male")//构造器内调用构造器
println(c)
}
def eat={
println(sex)
println(this.age)//站在面向
}
}
导包
Scala中也有包,包的命名使用规则和Java一样
包的声明
1)package com.syf.scala1015day03.pack
2)包语句
在Scala中,一个.Scala文件中一般会写多个类,所有的类会默认在一个包里
package aa{
class A
}
2.包的使用
如果导包和导类
1)在文件的最顶层导
import java.util.HashMap
2)在代码的任何需要的地方导包
比如可以在方法的内部
3)通配符导入
import java.util._
import java.util.{TreeMap,HashMap}
//导包的时候给类起别名
import java.util.{HashMap=>jHashMap}
//不想用HashMap,其他的都想用
import java.util.{HashMap=>,}
import java.util.HashMap
package com.syf.scala1015day03.pack
//不想用HashMap,其他的都想用
import java.util.{HashMap=>_,_}
import java.util.{TreeMap,HashMap}
//导包的时候给类起别名
import java.util.{HashMap=>jHashMap}
/**
* guo chao
* Date 2021/5/1
*/
object Pack1 {
def main(args: Array[String]): Unit = {
// new util.HashMap[String,String]()
}
}
class A{
// new util.HashMap[]()
}
package aa{
class A
}
权限修饰符
在Java中,访问权限分为:public,private,protected和默认。在Scala中,你可以通过类似的修饰符达到同样的效果。但是使用上有区别。
(1)scala 中属性和方法的默认访问权限为public,但scala中无public关键字。
(2)private为私有权限,只在类的内部和伴生对象中可用。
(3)protected为受保护权限,Scala中受保护权限比Java中更严格,同类、子类可以访问,同包无法访问。
(4)private[包名]增加包访问权限,包名下的其他类也可以使用
继承
- 基本语法
class 子类名 extends 父类名 { 类体 }
(1)子类继承父类的属性和方法
(2)scala是单继承 - 案例实操
(1)子类继承父类的属性和方法
(2)继承的调用顺序:父类构造器->子类构造器
class Person(nameParam: String) {
var name = nameParam
var age: Int = _
def this(nameParam: String, ageParam: Int) {
this(nameParam)
this.age = ageParam
println("父类辅助构造器")
}
println("父类主构造器")
}
class Emp(nameParam: String, ageParam: Int) extends Person(nameParam, ageParam) {
var empNo: Int = _
def this(nameParam: String, ageParam: Int, empNoParam: Int) {
this(nameParam, ageParam)
this.empNo = empNoParam
println("子类的辅助构造器")
}
println("子类主构造器")
}
object Test {
def main(args: Array[String]): Unit = {
new Emp("z3", 11,1001)
}
}
/**
* guo chao
* Date 2021/5/1
*/
class Extends1 {
def main(args: Array[String]): Unit = {
val b:B=new B
b.foo()
}
}
class A(val a:Int){
println("A 主构造内的代码")
def foo()={
println("A foo...")
}
}
class B (val b:Int)extends A(b){
override def foo(): Unit = {
println("B foo...")
}
}
抽象类
定义抽象类:abstract class Person{} //通过abstract关键字标记抽象类
/**
* guo chao
* Date 2021/5/1
*/
object abs1 {
def main(args: Array[String]): Unit = {
// val b:B=new B
// b.foo()
val a:A=new A(20){
var b:Int=30
def foo:Int= {
println("abc")
100
}
}
a.foo()
}
}
abstract class A(var a:Int){
var b:Int //属性只声明,不初始化,这就是抽象字段
def foo():Int//方法只声明,没有实现,就是抽象方法
}
class B extends A(20){
override var b: Int = _
override def foo(): Int = {
println("foo...")
}
}
单例对象及伴生对象
- 基本语法
object Person{
val country:String=“China”
} - 说明
(1)单例对象采用object关键字声明
(2)单例对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
(3)单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。 - 案例实操
//(1)伴生对象采用object关键字声明
object Person {
var country: String = "China"
}
//(2)伴生对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
class Person {
var name: String = "bobo"
}
object Test {
def main(args: Array[String]): Unit = {
//(3)伴生对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。
println(Person.country)
}
}
/**
* guo chao
* Date 2021/5/1
*/
object Single1 {
def main(args: Array[String]): Unit = {
A.foo
A()//相当于A.apply()
a()//相当于a.apply()
val a:A=new A
}
}
object A{
def foo= println("foo...")
def apply(): Int = {
println("apply")
10
}
}
class A{
def apply()={
println("class A apply....")
}
}
/*
单例对象:
Java实现单例:
1.饿汉式
2.懒汉式
scala中单例:
使用object关键字,声明出来的对象就是一个单例对象
独立对象
*/