面向对象编程
面向对象编程
包
语法
package com.pac1.pac2.pac3
package com
package pac1 {
package pac2 {
package pac3 {
object ScalaPackage {
def test(): Unit = {
println("test...")
}
}
}
}
}
注意
-
包和类的物理路径没有关系
-
同一个源码文件中子包可以直接访问父包中的内容,而无需import
-
package也可以看作对象,并声明属性和函数
package big { class Test{ def test() :Unit = { printf("test...") } } package object ob_pack{ val name : String = "ob_pack" def getname(): String = { name } } package ob { object ob01 { def main(args: Array[String]): Unit = { new Test().test() printf(ob_pack.getname()) } } } }
导入
语法
import java.util.List
import java.util._ // Scala中使用下划线代替Java中的星号
注意
-
import语法可以在任意位置使用
-
可以导包,而不是导类
object ScalaImport{ def main(args: Array[String]): Unit = { import java.util new util.ArrayList() } }
-
可以在同一行中导入相同包中的多个类,简化代码
import java.util.{List, ArrayList}
-
可以屏蔽某个包中的类
import java.sql.{ Date=>_, Array=>_, _ }
-
可以给类起别名,简化使用
import java.util.{ArrayList=>AList}
-
可以使用类的绝对路径而不是相对路径
import _root_.java.util.ArrayList
-
默认情况下,Scala中会导入如下包和对象
import java.lang._ import scala._ import scala.Predef._
类
语法
// 声明类:访问权限 class 类名 { 类主体内容 }
class User {
// 类的主体内容
}
// 对象:new 类名(参数列表)
new User()
注意
- 一个源文件中可以声明多个公共类
属性
语法
class User {
var name : String = _ // 类属性其实就是类变量
var age : Int = _ // 下划线表示类的属性默认初始化
}
注意
-
属性其实在编译后也会生成方法
public static class User { private String name; private int age; public String name() { return this.name; } public void name_$eq(final String x$1) { this.name = x$1; } public int age() { return this.age; } public void age_$eq(final int x$1) { this.age = x$1; } }
访问权限
语法
private : 私有访问权限 => 同类
private[包名]: 包访问权限 => 同包,包私有
protected : 受保护权限,不能同包 => 受保护的, 同类,子类,没有同包
: 公共访问权限
方法
类的方法其实就是函数,所以声明方式完全一样,但是必须通过使用对象进行调用
object ScalaMethod{
def main(args: Array[String]): Unit = {
val user = new User // new 对象 才可以使用方法
user.login("zhangsan", "000000")
}
}
class User {
def login( name:String, password:String ): Boolean = {
false
}
}
对象
语法
val / var 对象名 [:类型] = new 类型()
var user : User = new User()
补充
生成对象的操作
- 反射
- new
- 反序列化
- clone
构造方法(class)
语法
构造对象也需要调用类的构造方法来创建。并且一个类中可以有任意多个不相同的构造方法。这些构造方法可以分为2大类:主构造函数与辅助构造函数
class User() { // 主构造函数 ----用于完成类的初始化操作的构造方法称之为主构造方法
var username : String = _ // TODO 在构造参数前使用var或val声明
def this( name:String ) { // 辅助构造函数,使用this关键字声明 ----其他的构造方法就称之为辅助构造方法
this() // 辅助构造方法在执行之前,应该首先调用主构造方法完成类的初始化
username = name
}
def this( name:String, password:String ) {
this(name) // 构造器调用其他另外的构造器,要求被调用构造器必须提前声明
}
}
// 辅助构造方法可以重载的,并且可以互相调用,但是调用的辅助构造方法应该提前声明
def main(args: Array[String]): Unit = {
new User("zhangsan"); // 先执行父类构造方法
// * 子类所有的 构造函数 默认调用父类的无参构造函数(其实是默认省略掉了一行代码:super(););省略掉的这行super()代码可以自行添加到构造函数的第一行(必须是第一行,否则报错)
// aaaaa
// ccccc
new User()
//aaaaa
//ccccc
//ddddd
}
class Person(s:String) {
println("aaaaa")
def this() {
this("lisi")
println("bbbbb")
}
}
class User(var name:String) extends Person(name){
println("ccccc")
def this() {
this("wangwu")
println("ddddd")
}
}
伴生类与伴生对象(object)
构造方法私有化: 在参数列表前增加private关键字
声明一个公共的,静态的,返回本类型的方法,用于获取对象
scala中没有静态语法,但是可以直接使用java中的静态操作
scala采用了一种特殊的处理方式来代替静态语法 :object
object关键字可以用于创建对象,对象的名字就是声明的名字
使用object关键字声明的类和对象有关系的。这个对象等同于伴随着这个类创建时所产生的,所以将这个对象称之为:伴生对象,这个类称之为伴生类,伴生对象就是一个对象,可以访问伴生类中的所有东西,包括私有的。伴生对象其实就是马丁模拟静态语法所产生的。一般写代码时,将静态语法操作的代码写在伴生对象中,将成员方法或属性写在伴生类中
// 伴生类
class Person private () {
}
// 伴生对象
// Person.class
// Person$.class
object Person {
def getInstance() : Person = {
new Person()
}
}
- scala中伴生对象就是单例的
- 伴生对象只需要声明即可,无需构建,所以不需要构造参数列表
- 单例模式存在一个问题:创建的对象不会被回收,需要显示地回收(设置为null)
- 如果伴生对象中构建对象的方法名成为apply,编译器可以自动识别的,所以这个方法名可以省略的
//val test1: Test = Test.apply()
//val test1 = new Test() // 调用类的构造方法
//val test2 = Test() // 调用的是伴生对象的apply方法
//val test3 = Test // 伴生对象本体
class Test() {
println("ttttt")
}
object Test {
def apply() = {
println("apply")
//new Test()
}
}
继承
class Person {
}
class User extends Person { // 单继承
}
封装
封装就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行访问。
- 将属性进行私有化
- 提供一个公共的set方法,用于对属性赋值
- 提供一个公共的get方法,用于获取属性的值
抽象
-
所谓的抽象其实就是不完整
-
抽象类,抽象方法
-
抽象类没有办法直接实例化,需要由子类继承后完成实例化操作
-
子类继承抽象类后,可以声明为抽象类,也可以将父类的抽象方法补充完整。
-
scala中不完整的方法就是抽象,所以无需增加abstract关键字。
-
将一个不完整的类称之为抽象类。
abstract class Person { }
-
如果一个方法只有声明而没有实现,那么是抽象方法,因为它不完整。
abstract class Person { def test():Unit }
-
如果一个属性只有声明没有初始化,那么是抽象属性,因为它不完整。
abstract class Person { var name:String }
-
子类如果继承抽象类,必须实现抽象方法或补全抽象属性,否则也必须声明为抽象的,因为依然不完整。
abstract class Person { // 抽象属性:只有声明,没有初始化 // 编译时,不会在类中声明属性,而是会声明属性的set,get方法,并且是抽象的。 var name:String } class User extends Person { // 属性:编译时,会在类中声明私有属性,同时提供属性的set,get方法,并且为公共的。 var name : String = "zhangsan" }
-
子类重写父类的抽象方法,直接补充完整即可
-
子类重写父类的完整方法,必须添加override关键字
-
开发时,推荐,只要重写,都添加override
abstract class User { def test(): Unit = { } def fun():Unit } class Child extends User { override def test(): Unit = { } override def fun():Unit = { } }
-
子类可以重写父类的抽象属性
def main(args: Array[String]): Unit = { // TODO 面向对象 - 抽象 // 子类可以重写父类的抽象属性,补充完整即可 // 子类可以重写父类的完整属性,那么必须要添加override关键字 // println(new Child().age) println(new Child().test) } abstract class User { var name : String val age : Int = 10 def test(): Unit = { //age = 30 // 对属性的赋值其实等同于调用属性的set方法 println(age) // 对属性的方法其实等同于调用属性的get方法 } } class Child extends User { var name : String = "zhangsan" // 重写 override val age : Int = 20 }
特质
语法
- 将多个对象中相同的特征,从对象中剥离出来,形成独立的一个结构,称之为trait(特征)
- 如果一个对象符合这个特征,那么可以将这个特征加入到这个对象,这个加入的过程,称之为混入(extends)
- 如果一个类只有一个特征时,采用extends关键字进行混入
- 但是一个类如果有多个特征,这个时候,第一个特征采用extends,后续采用with
- 如果类存在父类的场合,并同时具备了某个特征,需要使用extends关键字继承父类,使用with关键字来混入特征
trait 特质名称
class 类名 extends 父类(特质1) with 特质2 with特质3
trait eat {
def eat():Unit
}
trait Runnable {
def run():Unit
}
class Person extends Object with Runnable {
override def run(): Unit = {
println("run...")
}
}
class Dog extends Runnable {
override def run(): Unit = {
println("run...")
}
}
注意
-
动态混入
object ScalaTrait{ def main(args: Array[String]): Unit = { val mysql = new MySQL with Operator //使用中混入 mysql.insert() } } trait Operator { def insert(): Unit = { println("insert data...") } } class MySQL { }
-
初始化叠加
object ScalaTrait{ def main(args: Array[String]): Unit = { val mysql = new MySQL } } trait Operator { println("operator...") } trait DB { println("db...") } class MySQL extends DB with Operator{ println("mysql...") }
-
功能叠加
-
java中不能类的多继承 : 砖石问题
scala采用了一种功能叠加的方式解决砖石问题
super不是父特质的意思,是上一级(上一个)的意思object ScalaTrait { def main(args: Array[String]): Unit = { val mysql: MySQL = new MySQL mysql.operData() } } trait Operate{ def operData():Unit={ println("操作数据。。") } } trait DB extends Operate{ override def operData(): Unit = { print("向数据库中。。") super.operData() } } trait Log extends Operate{ override def operData(): Unit = { super.operData() } } class MySQL extends DB with Log { }
-
初始化问题
父类的特质 > 父类 > 特质1, 特质2 > 当前类new User() // bbbb // dddd // aaaa // cccc trait Test extends Test1{ println("aaaa") } trait Test1 { println("dddd") } class Person{ println("bbbb") } class User extends Person with Test with Test1 { println("cccc") }
补充
-
类型检查和转换
class Person{ } object Person { def main(args: Array[String]): Unit = { val person = new Person //(1)判断对象是否为某个类型的实例 val bool: Boolean = person.isInstanceOf[Person] if ( bool ) { //(2)将对象转换为某个类型的实例 val p1: Person = person.asInstanceOf[Person] println(p1) } //(3)获取类的信息 val pClass: Class[Person] = classOf[Person] println(pClass) } }
-
枚举类和应用类
object Test { def main(args: Array[String]): Unit = { println(Color.RED) } } // 枚举类 object Color extends Enumeration { val RED = Value(1, "red") val YELLOW = Value(2, "yellow") val BLUE = Value(3, "blue") } // 应用类 object AppTest extends App { println("application"); }
-
Type定义新类型
object Test { def main(args: Array[String]): Unit = { type S = String var v : S = "abc" } }
-
修改不可变字符串内容 — 不可变指的是地址
val s = " a b " // 反射:类型信息 // 镜子 val stringClass: Class[String] = classOf[String] val field: Field = stringClass.getDeclaredField("value") field.setAccessible(true) val obj = field.get(s) val chars: Array[Char] = obj.asInstanceOf[Array[Char]] chars(2) = "D" println(s)