Scala的基本语法(四):面向对象基本
面向对象基本
类与对象
面试题
java scala的区别
1) Java是面向对象的编程语言,由于历史原因,Java中还存在着非面向对象的内容:基本类型(int,float…) ,null,静态方法等(object 替代)。
2) Scala语言来自于Java,所以天生就是面向对象的语言,而且Scala是纯粹的面向对象的语言,即在Scala中,一切皆为对象。
Scala对象创建对象流程分析
1) 加载类的信息(属性信息和方法信息)
2) 在内存中(堆)给对象开辟空间
3) 使用父类的构造器(主构造器/辅助构造器)完成父类的初始化 (多个父类)
4) 使用本类的主构造器完成初始化( age = 90 name = “null”)
5) 使用本类的辅助构造器继续初始化(age =20,name = “小倩”)
6) 将对象在内存中的地址赋给 p 这个引用
总结
- scala 中的object是单例对象,相当于java中的工具类,可以看成是定义静态的方法的类。object不可以传参数。另:Trait不可以传参数
- 类不能声明为public,默认就是public
- 属性的定义类型可以为任意类型,包含值类型或引用类型,属性必须显示的初始化。若暂时不赋值,也可以必须给定数据类型,使用符号_(下划线),让系统分配默认值
- 如果赋值为null,则一定要加类型,因为不加类型, 那么该属性的类型就是Null类型
- scala中的class类默认可以传参数,默认的传参数就是默认的构造函数。重写构造函数的时候,必须要调用默认的构造函数。
- 使用object时,不用new,使用class时要new ,并且new的时候,class中除了方法不执行,其他都执行。
- 如果在同一个文件中,object对象和class类的名称相同,则这个对象就是这个类的伴生对象,这个类就是这个对象的伴生类。可以互相访问私有变量。
- 伴生对象的功能特性并不在类的作用域内,如类无法直接使用伴生对象的方法 必须加类名
构造器
-
Scala构造器作用是完成对新对象的初始化,构造器没有返回值,会执行除方法外的所有语句
-
多个辅助构造器通过不同参数列表进行区分, 在底层就是构造器重载,辅助构造器无论是直接或间接,最终都一定要调用主构造器
-
如果想让主构造器变成私有的,可以在()之前加上private,这样用户不能直接通过主构造器来构造对象了
class C private(){}
-
<父类如果没有空参构造方法 子类继承时要传入参数,如自定义Spark数据源receiver
-
构造器
class Person2(name: String) { public Person2(String name) {} //在底层实现 }
-
可以把主构造器设置为有参 辅助构造器为def this()空参,在定义中调用有参主构造器,这样子类继承时可以继承空参的父类。
-
可以将子类参数传递给继承的父类构造器
class Emp (name:String) extends Person(name){}//class Person(name:String)是父类构造器,子类又可以继承父类的name
构造器参数
-
如果Scala类的主构造器的形参未用任何修饰符修饰,这个参数将升级为字段(或局部变量),如果没有方法使用,则没有改字段。
-
如果参数使用val关键字声明,那么Scala会将参数作为类的私有的只读属性使用
-
如果参数使用var关键字声明,那么那么Scala会将参数作为类的成员属性使用,并会提供属性对应的xxx()[类似getter]/xxx_$eq()[类似setter]方法,即这时的成员属性是私有的,但是可读写。
修饰符
-
没有修饰符的var变量,在编译时会被声明为private类型,但提供公共的get和set方法,即name,name_=
-
没有修饰符的val变量,在编译时会被声明为private类型,但只提供公共的get方法
-
使用private修饰的var变量.只有私有的get与set,如需访问,需要自定义get与set
-
使用private[this]修饰的var变量,为对象所私有,同一个类的不同对象不能相互访问该属性,也就是对象隔离级
-
@BeanProperty 生成java风格的get与set 以及默认的
scala 访问控制底层实际只用了两种修饰符: private public 。不管变量是否是private ,scala先将其定义成private属性,如果允许外界访问(如未定义private var),getter setter方法就会被设置成public,否则(如加了private var或者作为主构造器的参数)getter setter被设置为private 或者没有getter(private val)等。(protect例外 即便底层是public)
-
虽然scala的变量定义是底层都是private修饰的, 但如果父类private def修饰的的方法及public的getter setter方法,子类仍然可以通过调用父类的getter setter,来获取修改继承自父类的private修饰的属性。
val 变量
private final int val_val = 18;
public int val_val() { return this.val_val; }
var 变量
private String var_var = "coder";
public String var_var() { return this.var_var; }
public void var_var_$eq(String x$1) { this.var_var = x$1; }
private val 变量
private final int age; // final修饰
private int age() { return this.age; }
private var 变量
private String pri_var = "private var";
private String pri_var() { return this.pri_var; } //pri_var
private void pri_var_$eq(String x$1) { this.pri_var = x$1; } //pri_var._方法
@BeanProperty val
private final String BeanP_val = "@BeanProperty val"; //得到2个方法
public String BeanP_val() { return this.BeanP_val; }
public String getBeanP_val() { return BeanP_val(); }
@BeanProperty var
//@BeanProperty var adress="beijing" //得到4个方法
private String adress = "beijing";
public String adress() { return this.adress; }
public void adress_$eq(String x$1) { this.adress = x$1; }
public void setAdress(String x$1) { this.adress = x$1; } //setter
public String getAdress() { return adress(); } //getter
无任何修饰的参数 无方法引用 class Person(name:String,age:Int)
class Person(name:String,age:Int)
--------------------------------------------------
public Person(String name, int age){} //源码中升级为字段 如果没有方法使用,则没有该字段
无任何修饰的参数 有方法引用
class Person(name:String,age:Int) def sellHello: Unit ={println(name+Int) } //以默认public方法为例
--------------------------------------------------
private final String name; //因为是final 子类无法继承
public void sellHello() {
Predef..MODULE$.println(new StringBuilder().append(this.name).append(Int..MODULE$).toString());
}
包
包的使用
-
包也可以像嵌套类那样嵌套使用(包中有包), 这个在前面的第三种打包方式已经讲过了,在使用第三种方式时的好处是:程序员可以在同一个文件中,将类(class / object)、trait 创建在不同的包中,这样就非常灵活了。[案例+反编译] / 案例参考前面的即可。
-
作用域原则:可以直接向上访问。即: Scala中子包中直接访问父包中的内容, 大括号体现作用域。(提示:Java中子包使用父包的类,需要import)。在子包和父包 类重名时,默认采用就近原则,如果希望指定使用某个类,则带上包名即可。
-
父包要访问子包的内容时,需要import对应的类等
-
可以在同一个.scala文件中,声明多个并列的package(建议嵌套的pakage不要超过3层)
-
包名可以相对路径也可以绝对路径,比如,访问BeanProperty的绝对路径是:
root. scala.beans.BeanProperty ,在一般情况下:我们使用相对路径来引入包,只有当包名冲突时,使用绝对路径来处理
引入包
import语句可以出现在任何地方,import语句的作用一直延伸到包含该语句的块末尾。这种语法的好处是:在需要时在引入包,缩小import 包的作用范围,提高效率
-
Java中如果想要导入包中所有的类,可以通过通配符*,Scala中采用下 _
-
如果不想要某个包中全部的类,而是其中的几个类,可以采用选取器,使用{} 括起来即可。
-
如果引入的多个包中含有相同的类,那么可以将类进行重命名进行区分,这个就是重命名。
包对象
包可以包含类、对象和特质trait,但不能包含函数/方法或变量的定义。这是Java虚拟机的局限。为了弥补这一点不足,scala提供了包对象的概念来解决这个问题。
1)每个包都可以有一个包对象。你需要在父包中定义它。
-
包对象名称需要和包名一致,一般用来对包的功能补充
-
当属性访问权限为默认时,从底层看属性是private的,但是因为提供了xxx_$eq()[类似setter]/xxx()[类似getter] 方法,因此从使用效果看是任何地方都可以访问)
-
当方法访问权限为默认时,默认为public访问权限
-
private为私有权限,只在类的内部和伴生对象中可用 【案例演示】
-
protected为受保护权限,scala中受保护权限比Java****中更严格,只能子类访问,同包无法访问 (编译器从语法层面控制)
-
在scala中没有public关键字,即不能用public显式的修饰属性和方法。【案演】
-
scala设计者将访问的方式分成三大类:
(1) 处处可以访问public (2) 子类和伴生对象能访问protected (3) 本类和伴生对象访问 private
- 包访问权限(表示属性有了限制。同时包也有了限制),这点和Java不一样,体现出Scala包使用的灵活性。 当然,也可以将可见度延展到上层包private[atguigu] val description="zhangsan"说明:private也可以变化,比如protected[atguigu], 非常的灵活.