文章目录

  • ​​封装​​
  • ​​注意事项​​
  • ​​继承​​
  • ​​方法重写​​
  • ​​覆写字段​​
  • ​​java 中动态绑定机制​​
  • ​​scala覆写字段注意事项​​
  • ​​抽象类​​
  • ​​注意事项​​
  • ​​scala中的类型检查和转换​​
  • ​​构造器说明​​


面向对象三大特征:封装、继承和多态

封装

封装的好处
1)隐藏实现细节
2)可以对数据进行验证,保证数据安全合理
3)封装的同时可以加入业务逻辑
封装的步骤
1)属性私有化
2)提供公共的 get set 方法 用于赋值和读取值

注意事项

1)Scala中为了简化代码开发,当属性声明为 var【非private】时会自动生成 public 的 xxx 和 xxx$seq方法, 但是如果属性声明为private 那么这两个方法也会变成private .
因此 一般情况下 我们只是对一个属性简单的set 和get 那么 只需要声明非private 的var属性即可。
2)从形式上看如果 p.name 是直接访问的属性,但实际上是调用的 name方法
3)因为上述特性 很多框架在进行反射时,支持对属性的直接反射
4)BeanProperty 注解不能用于 private属性
示例:

class Person{
var name:String=_
private var age:Int=_
//@BeanProperty //BeanProperty 注解不能用于 private属性
private var salary:Float=_
def getAge:Int={
return this.age
}
def setAge(age:Int)={
if(age>120){
this.age=120;
}else if(age<0){
this.age=0
}else{
this.age=age
}
}
}

调用示例:

def test={
var p = new Person()
p.setAge(1000)
println(p.getAge)
}

示例反编译结果(底层实现):

public class class06$Person
{
private String name;
private int age;

public String name()
{
return this.name; }
public void name_$eq(String x$1) { this.name = x$1; }
private int age() { return this.age; }
private void age_$eq(int x$1) { this.age = x$1; }

public int getAge() { return age(); }

public void setAge(int age) {
if (age > 120)
age_$eq(120);
else if (age < 0)
age_$eq(0);
else
age_$eq(age);
}
}

继承

继承可以提高代码复用性、扩展性及可维护性
java 中的继承实现方式
class 子类名 extends 父类名{} 子类继承父类的 属性和方法
scala 中继承语法 和 java相似,子类继承了父类的所有属性,只是私有属性不能直接访问,需要通过公共的方法去访问。

方法重写

scala明确规定,重写一个非抽象方法需要使用override修饰符,重写时可以通过super关键字调用父类的方法
示例如下:

class Base{
var n1:Int=1;
protected var n2:Int=2;
private var n3:Int=3;

def f1={
println("基类-----f1")
}
protected def f2={
println("基类-----f2")
}
private def f3={
println("基类-----f3")
}
}
class Sub extends Base{
override def f1: Unit = {
super.f1
println("子类==========f1")
}
def showInfo={
println("修改前:n1="+n1+" n2="+n2+" n3=")//n3 无法访问
this.n1=10
this.n2=20
//this.n3 //n3无法直接访问
println("修改后:n1="+n1+" n2="+n2+" n3=")
f1
f2
//f3 //f3 无法访问
}
}

调用

def test={
var s=new Sub
s.showInfo
}

覆写字段

scala 中子类改写父类的字段 我们称之为 覆写字段。 java 中只有方法的重写没有字段的重写(准确讲是隐藏字段代替重写)
( java 中为什么不能重写字段?)

java 中动态绑定机制

1)如果调用的是方法,那么jvm会将方法和对象的内存地址绑定
2)如果调用的是属性,则没有动态绑定机制,在哪里调用就返回哪里的值
示例

class AA {
public int i =10;
public String flag ="父类";
public int sum1(){
return (getI()+10);
}
public int sum(){
return (i+10);
}
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
}
class BB extends AA{
public int i=20;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
public String getFlag(){
return this.flag;
}
}
public static void main(String[] args) {
//将一个子类的对象地址,交给一个AA 父类的引用
//如果调用的是方法,则jvm 会吧方法和 对象的内存绑定
//如果是调用属性,没有动态绑定机制,在哪里调用就返回哪里的值
AA a = new BB();
BB b = new BB();
System.out.println(a.i);//输出 10
System.out.println(b.i);//输出 20
System.out.println(a.sum());//输出 20
System.out.println(a.sum1());//输出 30
}

scala覆写字段注意事项

1)def只能重写另一个def(方法只能重写另一个方法)
2)val 只能重写另一个val属性 或者重写不带参数的def
3) var 只能重写另一个抽象的var属性
*一个属性没有初始化,那么这个属性就是个抽象属性(只能存在于抽象类中)
*抽象属性在编译成字节码文件时,属性并不会声明,但是会自动生成抽象的属性操作方法
*如果是腹泻一个父类的抽象属性,override关键字可以省略
示例:

abstract class AA{
val age:Int =10;
var name:String ="AA"
def sal():Int={
return 10;
}
var sex:String
}
class BB extends AA{
override val age:Int =20;
//override var name:String ="BB"//var 属性重写是 执行会报错 提示补鞥呢重写var属性
override val sal:Int=30;//由于scala会自动生成 sal()方法,该方法会覆盖 父类中的 sal方法
var sex:String="男"
}
def test={
var a:AA = new BB()
var b:BB = new BB()
//可以看到和java的不同 感觉父类的属性被覆盖了
//原理就是 我们这里调用属性 在底层都是调用子类的java 方法 ,由于java的动态绑定机制,所以都返回的 子类的值
println("a.age="+a.age+" b.age="+b.age)//a.age=20 b.age=20
println("a.name="+a.name+" b.name="+b.name)// a.name=AA b.name=AA
println("a.sal="+a.sal+" b.sal="+b.sal)//a.sal=30 b.sal=30
println("a.sex="+a.sex+" b.sex="+b.sex)//a.sex=男 b.sex=男
}

抽象类

scala中通过abstract关键字标记不能被实例化的类。抽象方法不用abstract标记,只要省略方法体即可
抽象类可以拥有抽象字段(没有被初始化的字段)

注意事项

1)抽象类不能被示例化,默认情况下不允许实例化抽象类,
但是如果实例化时动态实现抽象类的所有先后向方法也可以.(这种情况下实际上是创建了匿名子类实例)
2)抽象类不一定包含抽象方法和抽象属性
3)一旦类重有抽象方法或抽象属性 那么类必须是抽象的
4)如果一个类继承了抽象类,那么它必须实现所有的抽象方法和属性,除非他自己也是抽象类
5)抽象方法一定不能使用 abstract 修饰否则执行报错
6)抽象方法不能使用 private final 修饰,因为这些关键字和重写相违背
7)抽象类中可以有 已完成实现的方法
8)子类重写抽象方法可以不加 override 加了也不报错
示例:

abstract class Animal{
var name:String
//final var age:Int //会提示final 不能用于未实现的属性
var color:String="黑"
def cry()
//abstract def eat() //抽象方法不能使用 abstract 修饰
}
def test: Unit ={
var a1:Animal = new Animal {
override var name: String = _
override def cry(): Unit = {
println(" a1")
}
}
var a2:Animal = new Animal {
override var name: String = _
override def cry(): Unit = {
println(" a2")
}
}
a1.cry()
a2.cry()
}

scala中的类型检查和转换

scala 中的类型检查和转换
classOf[String] 相当于java 中的 String.class 返回对象类型
obj.isInstanceOf[T] 相当于 java中的 obj instanceof T 判断对象是否为T类型
obj.asInstanceOf[T] 相当于java中的(T)obj 强制将obj转换为T 类型

示例:

class Person{
var name:String ="tom"
def printName={
println("Person print name " + name)
}

}
class Emp extends Person{
override def printName={
println("Emp print name " + name)
super.printName
}

}
def test={
var p:Person = new Emp
println(classOf[Person])
println(p.getClass.getName)
if(p.isInstanceOf[Emp]){
var p2:Emp = p.asInstanceOf[Emp]
p2.printName
}
}

输出结果:

class com.test.class06$Person
com.test.class06$Emp
Emp print name tom
Person print name tom

构造器说明

构造器说明:
1)每个类有一个主构造器和任意多个辅助构造器,且每个辅助构造器必须先调用主构造器
2)只有主构造器才可以调用父类构造器,辅助构造器不能直接调用父类构造器
测试

class Base {
var name:String=_;
def this(inName:String){
this
println("base 辅助构造器"+inName)
}
println("Base 主构造器"+name)
}
class Sub(inName:String ,inAge:Int) extends Base(inName){// 对 父类构造的调用应该是在这行定义的 这里
//super(inName) 在子类中不能通过这种形式调用父类构造
println("SUb 主构造器"+name)
def this(){
this("张三",19);
println("SUb 辅助构造器" +name)
}
def this(name:String){
this(name,19);
println("SUb 辅助构造器"+name)
}
}
def test={
new Sub("tom")
}

输出结果:

Base 主构造器null
base 辅助构造器tom
SUb 主构造器null
SUb 辅助构造器tom