文章目录
1. 定义类
// Java 代码
public class Test {
public static void main(String[] args){
......
}
}
// Scala代码
object Test {
def main(args:Array[String]):Unit = {
......
}
}
细节:
-
Java文件:一个
.java
中可以有多个类,但是必须存在一个类用public
修饰,并且用public
修饰的类的 类名 必须 与 文件名 一样。 -
Scala文件:一个
.scala
中可以包含多个类,且所有的类都不需要用public
修饰,不要求 类名 与 文件名一致。因为所有的类默认加上了
public
关键字,如果再写上public
就重复了,反而就会报错 -
.scala
文件,需要先编译成.class
文件才能运行。
-
定义类名:以
字母
开头,后面接字母、数字、下划线、$
。在Scala中我一般采用首字母大写 + 下划线分隔
,没有采用驼峰命名法。比如Search_c_video_na.scala
2. 属性
-
Java封装操作如下:
- 将属性进行私有化
- 提供一个公共的set方法,用于对属性赋值
- 提供一个公共的get方法,用于获取属性的值
-
在scala中所有的属性,不管是用
public
还是private
修饰,在底层都是private
,并通过get方法(obj.field())
和set方法(obj.field_=(value))
访问该属性。因此,那个修饰符只用于定义 访问权限 的,没有起到封装的效果(Java是借助修饰符达到封装的效果)。 -
属性的定义:
class Student{ // 1. 类的属性一般用 var 修饰,因为一般要修改属性值 // 2. 如果赋null值,用 _ 代替 null var name: String = _ // 3. 由于一些场景下Java框架要利用反射调用getXXX和setXXX方法,有时候为了和这些框架兼容,需要显示生成getXXX和setXXX方法 // 如果需要显示的生成getter、setter方法,用 @BeanProperty 注解即可 @BeanProperty var age: Int = _ } class Student{ def main(args: Array[String]): Unit = { val st = new Student() st.age = 50 } }
3. 访问权限
private
私有权限,只在类的内部和伴生对象中可用。protected
为受保护权限,同类、子类可以访问,同包无法访问。private[包名]
为包访问权限,指定包名下的类可以访问。- 默认(什么都不写),就是public,任何类都可以访问
4. 方法
4.1 定义方法与调用
- 方法的定义:
比如:class或object 类名 { def 方法名([变量:变量类型,变量:变量类型]):返回值类型 = { 方法体 } }
注意:如果方法是递归方法,则必须指名方法的返回值类型,不能不写。
- 方法的调用:有两种方式
object Test { def sayTwoParams(name:String, age:Int):Unit = { println(s"name = $name, age = $age") } def sayOneParams(name:String):Unit = { println(s"name = $name") } def main(args: Array[String]): Unit = { val name = "zhangsan" val age = 18 // 1. 方式一:像Java一样通过.调用方法 Test.sayTwoParams(name, age) // 2. 方式二:直接调用,省略. Test sayTwoParams(name,age) Test sayOneParams name // 如果只有一个参数,那么小括号可以省略 } }
4.2 方法重写
在def
关键字前加上override
关键字:
class Person {
def run(): Unit = {
println("person run")
}
}
class Student extends Person {
// 在 def 关键字前加上 override 关键字。源码是下面这种格式
override
def run():Unit = {
println("student run")
}
}
4.3 方法重载
不用加关键字,直接写
class Person {
def run(): Unit = {
println("person run")
}
def run(name: String): Unit = {
println(s"${name} run")
}
}
4.4 构造方法
(1) 构造器定义
Scala的构造方法分为两类:
- 主构造器:必须有
- 辅助构造器:必须调用主构造器
- 主构造器必须有,如果主构造器没有参数,则括号可以省略。如:
class Person { ...... }
- 主构造器的方法体在哪里???
- 辅助构造方法不能直接构建对象,必须直接或者间接调用主构造方法
- 构造器调用其他另外的构造器,要求被调用构造器必须提前声明。也就是说,存在代码的先后顺序。
(2) 构造器的参数列表
- 未用var或val修饰,这个参数就是一个局部变量,底层有属性的特性
- var修饰参数,作为类的成员属性,可以修改
- val修饰参数,作为类的成员属性,不能修改
package chapter06
object Test07_ConstructorArgs {
def main(args: Array[String]): Unit = {
val person0 = new Person("zhangsan",11,"男")
println(person0.name)
println(person0.age)
println(person0.sex)
}
}
// 主构造器参数 分为3类:
// 没有修饰符 : 作为构造方法中的传入参数使用
// val 修饰 : 会自动生产同名的属性 并且定义为val
// var 修饰 : 会自动生产同名的属性 并且定义为var
class Person (name1:String,val age:Int,var sex:String){
val name = name1
// val age = age
// var sex = sex
}
(3) 构造器私有化
实现单例模式时,需要将构造器私有化。那么如何将构造器私有化,直接在主构造器前上加上private
关键字:
class Student private (var name: String, var sage: Int){
}
object Student {
private var instance:Student = _
def getInstance(name:String, age:Int): Student = {
if (instance == null) {
instance = new Student("zhang", 18)
return instance
}
instance
}
}
4.5 默认参数
- 定义方法时,在参数的类型后面加上
='默认值'
,这个参数就就被赋予了默认参数。object Test { def main(args: Array[String]): Unit = { this.defaultParam() } def defaultParam(path: String = "/user/home"): Unit = { print(path) } } // 输出:/user/home
4.6 可变参数
- 定义方法时,在参数的类型后面加上
*
,这个参数就变成了可变参数。(可变参数需放在最后,否则报错)object Main { def main(args: Array[String]): Unit = { def sum(x:Int*):Int = { // 在参数x后加上 * ,则x变成了可变参数 var value = 0 for(i <- x) value += i value } println(sum(1, 2, 3)) // 输出:6 } }
- 传入可变参数时,如果参数是集合,可通过
:_*
的方式传入:object Test { def main(args: Array[String]): Unit = { def printAll(x:Int*):Unit = { x.foreach(println) } val list = List(1, 2, 3) printAll(list:_*) } }
4.7 查看类/对象的所有方法
5. 继承
Scala和Java一样都是单继承机制:
6. 伴生对象 & 伴生类
6.1 伴生对象的由来
在Java的类中,用 static 关键字修饰的内部类、方法、属性,可以通过类名访问。比如:
class Student{
public static String school;
}
class Main {
public static void main(String[] args) {
System.out.printf(Student.school); // 通过类名直接访问。
}
}
这里的Student.school
是通过类名来访问,并不是通过对象访问,Java号称面向对象,这里就相当与面向对象的理念冲突了。
所以,在Scala中,没有 static 关键字。为了实现与 static
关键字相同的功能,于是引入了伴生对象。
-
问:删除
static
关键字容易,如何实现static
关键字的功能呢? -
思路:static 修饰的就是当前类所共享的,而
Student.school
,中的Student应该是一个对象,而不是类。自然的想到为每个类创建第一个同名字的对象,然后将 static 修饰的东西都放入该对象中即可。 -
例子:
class Student(var name:String, var age:Int) { // 定义与类相关的一些信息 def printerInfo():Unit = { println(s"姓名:${name}, 年龄;${age}") } } object Student{ // 所有Student在同一所学校 var school:String = "XXX高中" // main方法本来应该用static修饰,所以应该写在这里 def main(args: Array[String]): Unit = { var stu = new Student("张三", 18) stu.printerInfo() } }
6.2 伴生对象 & 伴生类
-
伴生对象用
object
关键字修饰,伴生类用class
关键字修饰。 -
他俩的名字必须相同,
-
且必须放在同一个scala文件中。
class Student{ .... } object Student{ .... }
因为伴生对象相当于是存放
static
的地方,所以所有类的对象共享伴生对象中的数据。
伴生对象与伴生类可以相互访问对方中的数据
7. case 类
case class Person(name:String, age:Int)
object Test {
def main(args: Array[String]): Unit = {
val list: List[Person] = List(Person("zhangsan", 18), Person("wangwu", 20))
list.foreach(println)
}
}
8. 对象的复制
-
样例类使用
copy()
方法复制,普通类需要自己编写复制方法。case class Person(name: String, age: Int) val john = Person("John", 30) val johnCopy = john.copy()
-
对于RDD或DataSet里面的Row对象,可以使用
fromSeq()
方法复制。比如:import org.apache.spark.sql.Row val newDf = df.rdd.map { row => if (row.getAs[String]("chuilei_type") == "1") { // 创建新的 Row,并复制原始 Row 的值 val newRow = Row.fromSeq(row.toSeq) newRow } else { // 如果不满足条件,保持原始 Row 不变 row } }.toDF()
注意:这是浅复制,并不是深复制,如果 Row 包含对其他对象的引用,这些引用将被共享,而不是复制。
9. 异常
override def remove(n: Int, count: Int) {
if (count < 0) throw new IllegalArgumentException("removing negative number of elements: " + count.toString)
else if (count == 0) return // Did nothing
if (n < 0 || n > size0 - count) throw new IndexOutOfBoundsException("at " + n.toString + " deleting " + count.toString)
copy(n + count, n, size0 - (n + count))
reduceToSize(size0 - count)
}