[修饰符] class 类名 {java
类体程序员
}编程
scala语法中,类并不声明为public,全部这些类都具备公有可见性(即默认就是public)app
一个Scala源文件能够包含多个类框架
定义一个最简单的类函数式编程
object Demo {
def main(args: Array[String]): Unit = {
var man = new Man
man.name = "cris"
man.age = 12
println(man.name + "----" + man.age) // cris----12
}
}
class Man {
var name = ""
var age = 0
}
复制代码
反编译对应的 class 文件函数
属性是类的一个组成部分,通常是值数据类型,也但是引用类型工具
def main(args: Array[String]): Unit = {
val man = new Man()
val pc = new PC
man.pc = pc
man.pc.brand = "惠普"
// man.pc().brand()
println(man.pc.brand) // 惠普
}
class Man {
var name = "" // 手动设置初始值,此时能够省略成员属性的数据类型声明
var age = 0
var pc: PC = _ // _ 表示让 Scala 自动赋默认值,此时声明带上成员属性的数据类型,不然编译器没法肯定默认值
}
class PC {
var brand: String = _
}
复制代码
针对 for(int i = 10;i>0;i--){System.out.println(i)} 翻译成 Scala 代码this
object Practice {
def main(args: Array[String]): Unit = {
for (i <- 0.to(10).reverse) {
print(i + "\t") // 10 9 8 7 6 5 4 3 2 1 0
}
}
}
复制代码
使用过程重写上面的 Scala 代码编码
def func(x: Int) {
for (i <- 0 to x reverse) {
print(i + "\t")
}
}
复制代码
编写一个for循环,计算字符串中全部字母的Unicode代码(toLong方法)的乘积。举例来讲,"Hello"中全部字符串的乘积为9415087488L
def cal(str:String): Unit ={
var result = 1L
for(x <- str){
result*=x.toLong
}
print(result)
}
复制代码
使用 StringOps 的 foreach 方法重写上面的代码
var r2 = 1L
// _ 能够理解为字符串的每个字符
"Hello".foreach(r2 *= _.toLong)
print(r2)
复制代码
使用递归解决上面求字符串每一个字符 Unicode 编码乘积的问题
def recursive(str: String): Long = {
if (str.length == 1) str.charAt(0).toLong
/*drop(n)从索引为 1 开始切片到结尾*/
else str.take(1).charAt(0).toLong * recursive(str.drop(1))
}
复制代码
编写函数计算 x^n,其中 n 是整数(负数,0,正数),请使用递归解决
def pow(x: Int, n: Int): Double = {
if (n == 0) 1
else if (n < 0) {
1.0 / x * pow(x, n + 1)
} else {
x * pow(x, n - 1)
}
}
复制代码
val | var 对象名 [:类型] = new 类型()
若是咱们不但愿改变对象的引用(即:内存地址), 应该声明为val 性质的,不然声明为var, scala设计者推荐使用val ,由于通常来讲,在程序中,咱们只是改变对象属性的值,而不是改变对象的引用
scala在声明对象变量时,能够根据建立对象的类型自动推断,因此类型声明能够省略,但当类型和后面new 对象类型有继承关系即多态时,就必须写了
Scala中的方法其实就是函数,只不过通常将对象中的函数称之为方法
def 方法名(参数列表) [:返回值类型] = {
方法体
}
练习
嵌套循环打印图形
def func1(): Unit ={
for (i <- 1 to 4; j <- 1 to 3) {
if (j == 3) println("*")
else print("*\t")
}
}
复制代码
计算矩形的面积
class Test {
def area(): Double = {
(this.width * this.length).formatted("%.2f").toDouble
}
var width: Double = _
var length: Double = _
复制代码
java 的构造器回顾
[修饰符] 方法名(参数列表){
构造方法体
}
在Java中一个类能够定义多个不一样的构造方法,构造方法重载
若是程序员没有定义构造方法,系统会自动给类生成一个默认无参构造方法(也叫默认构造器)
3)一旦定义了本身的构造方法,默认的构造方法就覆盖了,就不能再使用默认的无参构造方法,除非显示的定义一下,即: Person(){}
Scala 构造器
和Java同样,Scala构造对象也须要调用构造方法,而且能够有任意多个构造方法。
Scala类的构造器包括: 主构造器 和 辅助构造器
基础语法
class 类名(形参列表) { // 主构造器
// 类体
def this(形参列表) { // 辅助构造器
}
def this(形参列表) { //辅助构造器能够有多个...
}
}
简单示例
abstract class Dog {
var name = ""
var age = 0
val color: String
def this(name: String, age: Int) {
this()
this.name = name
this.age = age
}
def eat(): Unit = {
println("吃狗粮")
}
def run()
}
复制代码
class Cat(var name: String, val color: String) {
println("constructor is processing")
def describe: String = name + "--" + color
}
def main(args: Array[String]): Unit = {
var cat = new Cat("tom", "gray")
println(cat.describe)
var cat2 = new Cat("jack", "red")
println(cat2.describe)
}
复制代码
细节
Scala构造器做用是完成对新对象的初始化,构造器没有返回值。
主构造器的声明直接放置于类名以后 [反编译]
主构造器会执行类定义中的全部语句,这里能够体会到Scala的函数式编程和面向对象编程融合在一块儿,即:构造器也是方法(函数),传递参数和使用方法和前面的函数部份内容没有区别
若是主构造器无参数,小括号可省略,构建对象时调用的构造方法的小括号也能够省略
辅助构造器名称为this(这个和Java是不同的),多个辅助构造器经过不一样参数列表进行区分, 在底层就是java的构造器重载,辅助构造器第一行函数体必须为 this.主构造器
abstract class Dog {
var name = ""
var age = 0
val color: String
def this(name: String, age: Int) {
this()
this.name = name
this.age = age
}
def eat(): Unit = {
println("吃狗粮")
}
def run()
}
复制代码
6)) 若是想让主构造器变成私有的,能够在()以前加上private,这样用户只能经过辅助构造器来构造对象了,说明:由于Person3的主构造器是私有,所以就须要使用辅助构造器来建立对象
class Car private(){}
复制代码
Scala类的主构造器函数的形参未用任何修饰符修饰,那么这个参数是局部变量
若是参数使用val关键字声明,那么Scala会将参数做为类的私有的只读属性使用
若是参数使用var关键字声明,那么那么Scala会将参数做为类的成员属性使用,并会提供属性对应的xxx()[相似getter]/xxx_$eq()[相似setter]方法,即这时的成员属性是私有的,可是可读写
class Counter {
/*1. 有公开的 getter 和 setter 方法*/
var count = 0
/*2. 私有化 getter 和 setter,能够手动提供 setter 和 getter*/
private var number = 1
/*3. 只能被访问getter,没法修改setter,final 修饰的 age 属性*/
val age = 12
/*4. 对象级别的私有*/
private[this] var length = 12
def compare(other: Counter): Boolean = other.number > number
// def compareLength(other: Counter): Boolean = length > other.length
def increase(): Unit = {
number += 1
}
/*无参方法能够省略(),{}也能够省略*/
def current: Int = number
}
def main(args: Array[String]): Unit = {
var c = new Counter()
c.count = 3
println(c.count) // 3
c.increase()
println(c.current) // 2
println(c.age) // 12
}
复制代码
若是在主构造器中为属性设置了默认值,那么就没必要在函数体内再去声明属性以及赋值了,大大简化代码的书写
def main(args: Array[String]): Unit = {
val dog = new Dog()
println(dog.name) // cris
println(dog.age) // 10
}
}
class Dog(var name :String= "cris",var age:Int = 10){
}
复制代码
JavaBean 注解
JavaBeans规范定义了Java的属性是像getXxx()和setXxx()的方法。许多Java工具(框架)都依赖这个命名习惯。为了Java的互操做性。将Scala字段加@BeanProperty时,这样会自动生成规范的 setXxx/getXxx 方法。这时可使用 对象.setXxx() 和 对象.getXxx() 来调用属性
给某个属性加入@BeanPropetry注解后,会生成getXXX和setXXX的方法
而且对原来底层自动生成相似xxx(),xxx_$eq()方法,没有冲突,两者能够共存
请针对如下代码简述对象建立流程
class Bike {
var brand = ""
var color = ""
def this(brand: String, color: String) {
this
this.brand = brand
this.color = color
}
}
def main(args: Array[String]): Unit = {
var bike = new Bike("ofo", "黄色")
}
复制代码
加载类信息(属性信息,方法信息)
在堆中,给对象开辟空间
调用主构造器对属性进行初始化
使用辅助构造器对属性进行初始化
把对象空间的地址,返回给 bike 引用
回顾 Java 的包知识
做用
区分相同名字的类
当类不少时,能够很好的管理类
控制访问范围
打包基本语法
package com.cris;
打包的本质分析
实际上就是建立不一样的文件夹来保存类文件
示例代码
先在不一样的包下创建同名的类
若是想要在一个类中同时使用上面的两个 Pig,Java 的解决方式以下:
public static void main(String[] args) {
Pig pig1 = new Pig();
cris.package2.Pig pig2 = new cris.package2.Pig();
// pig1.getClass() = class cris.package1.Pig
System.out.println("pig1.getClass() = " + pig1.getClass());
// pig2.getClass() = class cris.package2.Pig
System.out.println("pig2.getClass() = " + pig2.getClass());
}
复制代码
再来看看咱们的源码所在路径和字节码文件所在路径,都是一一对应的
Java 要求源码所在路径和字节码文件所在路径必须保持一致,若是咱们此时去修改源码的打包路径
基本语法
import java.awt.* or import java.util.List
注意事项:java中包名和源码所在的系统文件目录结构要一致,而且编译后的字节码文件路径也和包名保持一致
接着看看 Scala 是如何处理的
咱们使用 Scala 重写上面的 Java 包案例
def main(args: Array[String]): Unit = {
var b1 = new cris.package1.Bird1
var b2 = new cris.package2.Bird2
// class cris.package1.Bird1
println(b1.getClass)
// class cris.package2.Bird2
println(b2.getClass)
}
复制代码
此时咱们若是修改了 Bird1 的打包路径
再看看源代码和字节码文件所在的路径
Scala 的包
和Java同样,Scala中管理项目可使用包,但Scala中的包的功能更增强大,使用也相对复杂些
基本语法 package 包名
Scala包的三大做用(和Java同样)
Scala中包名和源码所在的系统文件目录结构要能够不一致,可是编译后的字节码文件路径和包名会保持一致(这个工做由编译器完成)
图示
命名规范
只能包含数字、字母、下划线、小圆点.,但不能用数字开头, 也不要使用关键字
通常是小写字母+小圆点通常是 com.公司名.项目名.业务模块名
Scala 自动 import 的包有:java.lang.*,scala,Predef 包
Scala 打包细节(难点)
经常使用的两种打包形式
源代码的路径和字节码文件路径保持一致
源代码的路径和字节码文件路径不一致
上面的演示中已经很清楚的展现了 Scala 包的这一特色,咱们继续用下面代码演示 Scala 包的嵌套
咱们在 Detail 类文件中写入以上很是奇怪的代码,编译运行后再查看源代码和字节码文件的位置
进一步印证了 Scala 中源文件和字节码文件路径能够不一致
包也能够像嵌套类那样嵌套使用(包中有包), 见上面图示。好处是:程序员能够在同一个文件中,将类(class / object)、trait 建立在不一样的包中,很是灵活
做用域原则:能够直接向上访问。即: Scala中子包中直接访问父包中的内容, 大括号体现做用域。(提示:Java中子包使用父包的类,须要import)。在子包和父包 类重名时,默认采用就近原则,若是但愿指定使用某个类,则带上包名便可
示例代码
package com.cris {
class Apple {
}
package scala {
class Apple {
}
object Boy {
def main(args: Array[String]): Unit = {
/*1. Scala 中子包能够直接访问父包的内容;2. 子包和父包的类重名,默认采起就近原则;3. 能够带上类的路径名指定使用该类*/
val apple = new Apple
val apple2 = new com.cris.Apple
// class com.cris.scala.Apple
println(apple.getClass)
// class com.cris.Apple
println(apple2.getClass)
}
}
}
}
复制代码
父包要访问子包的内容时,须要import对应的类
package com.cris {
import com.cris.scala.Apple
object Apple{
def main(args: Array[String]): Unit = {
// 推荐只在使用的时候再引用,控制做用域
import com.cris.scala.Apple
val apple = new Apple()
// class com.cris.scala.Apple
println(apple.getClass)
}
}
package scala {
class Apple {
}
}
}-
复制代码
能够在同一个.scala文件中,声明多个并列的package(建议嵌套的pakage不要超过3层)
包对象
基本介绍:包能够包含类、对象和特质trait,但不能包含函数或变量的定义。这是Java虚拟机的局限。为了弥补这一点不足,scala提供了包对象的概念来解决这个问题
参见以下代码
package com.cris {
// 不能直接在 package 中定义函数和变量
// var name = "cris"
/** * 包对象的名字须要和包名一致 * package object emp 会在 com.cris.emp 包下生成 package.class 和 package$.class */
package object emp {
def eat(): Unit = {
println("eat")
}
val salary = 1000.0
}
package emp {
object test {
def main(args: Array[String]): Unit = {
eat() // eat=》等价于使用了 package$.class 中的 MODULE$.eat()
println(salary) // 1000.0=> 等价于使用了 package$.class 中的 MODULE$.salary()
}
}
}
}
复制代码
使用反编译工具打开瞧瞧
具体的执行流程第二章节已经解释过,这里再也不赘述
注意事项:
包的可见性
在Java中,访问权限分为: public,private,protected和默认。在Scala中,你能够经过相似的修饰符达到一样的效果。可是使用上有区别
当属性访问权限为默认时,从底层看属性是private的,可是由于提供了xxx_$eq()[相似setter]/xxx()[相似getter] 方法,所以从使用效果看是任何地方均可以访问)
当方法访问权限为默认时,默认为public访问权限
private为私有权限,只在类的内部和伴生对象中可用
示例:
protected为受保护权限,scala中受保护权限比Java中更严格,只能子类访问,同包没法访问
在scala中没有public关键字,即不能用public显式的修饰属性和方法。
包访问权限(表示属性有了限制。同时增长了包的访问权限),这点和Java不同,体现出Scala包使用的灵活性
包的引入
细节说明
在Scala中,import语句能够出如今任何地方,并不只限于文件顶部,import语句的做用一直延伸到包含该语句的块末尾。这种语法的好处是:在须要时在引入包,缩小import 包的做用范围,提升效率
示例以下:
Java中若是想要导入包中全部的类,能够经过通配符*,Scala中采用下 _
若是不想要某个包中所有的类,而是其中的几个类,能够采用选取器(大括号)
若是引入的多个包中含有相同的类,那么能够将不须要的类进行重命名进行区分,这个就是重命名
或者使用 import java.util.{HashMap => _ } 对冲突的包进行隐藏
编写一个Time类,加入只读属性hours和minutes,和一个检查某一时刻是否早于另外一时刻的方法before(other:Time):Boolean。Time对象应该以new Time(hrs,min)方式构建
object Practice {
def main(args: Array[String]): Unit = {
val time1 = new Time(4, 12)
val result = time1.before(new Time(4, 14))
println(result)
}
}
class Time(val hour: Int, val minute: Int) {
def before(other: Time) = {
if (this.hour < other.hour) true
else if (this.hour > other.hour) false
else if (this.hour == other.hour) {
if (this.minute < other.minute) true
else if (this.minute > other.minute) false
else false
}
}
}
复制代码
建立一个Student类,加入可读写的JavaBeans属性name(类型为String)和id(类型为Long)。有哪些方法被生产?(用javap查看。)你能够在Scala中调用JavaBeans的getter和setter方法吗?
object Practice {
def main(args: Array[String]): Unit = {
var s = new Student
println(s.getName)
println(s.age)
}
}
class Student {
@BeanProperty var name = "好学生"
@BeanProperty var age = 0
}
复制代码
编写一段程序,将Java哈希映射中的全部元素拷贝到Scala哈希映射。用引入语句重命名这两个类
object Ex extends App {
import java.util.{HashMap => JavaHashMap}
import scala.collection.mutable.{HashMap => ScalaHashMap}
var map1 = new JavaHashMap[Int, String]()
map1.put(1, "cris")
map1.put(2, "james")
map1.put(3, "simida")
var map2 = new ScalaHashMap[Int, String]()
for (key <- map1.keySet().toArray()) { // key 的数据类型是 AnyRef
// asInstanceOf 强制数据类型转换
map2 += (key.asInstanceOf[Int] -> map1.get(key))
}
println(map2.mkString("||")) // 2 -> james||1 -> cris||3 -> simida
}
复制代码
咱们在前面去定义一个类时候,实际上就是把一类事物的共有的属性和行为提取出来,造成一个物理模型(模板)。这种研究问题的方法称为抽象
示例代码
object Demo extends App {
var account = new Account("招行:888888", 200, "123456")
account.query("123456")
account.save("123456", 100)
account.query("123456")
account.withdraw("123456", 250)
account.query("123456")
}
class Account(val no: String, var balance: Double, var pwd: String) {
def query(pwd: String): Unit = {
if (pwd != this.pwd) {
println("密码错误!")
} else {
println(s"卡号:${this.no},余额还有:${this.balance}")
}
}
def save(pwd: String, money: Double): Unit = {
if (pwd != this.pwd) {
println("密码错误")
} else {
this.balance += money
println(s"卡号:${this.no},存入:${money},余额为:${this.balance}")
}
}
def withdraw(pwd: String, money: Double): Unit = {
if (pwd != this.pwd) {
println("密码错误")
} else if (money > this.balance) {
println("余额不足")
} else {
this.balance -= money
println(s"卡号:${this.no},取出:${money},余额为:${this.balance}")
}
}
}
复制代码