开发者学堂课程【Scala 核心编程-基础:Java 的覆写字段回顾】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/609/detail/8975
Java 的覆写字段回顾
内容介绍
一、覆写字段
二、Java 中为什么字段不能被重写
三、如何访问隐藏字段
四、小结
五、回顾-java 另一重要特性:动态绑定机制
六、总结
一、覆写字段
1.基本介绍
在 Scala 中,子类改写父类的字段,称为覆写/重写字段。
覆写字段需使用 override 修饰。
前面研究了方法的重写,在 java 中有方法的重写。
讲到 scala 的覆写字段有必要了解 Java 中的覆写。
2.回顾:
在 Java 中只有方法的重写,没有属性/字段的重写,准确的讲,是隐藏字段代替了重写。参考:Java 中为什么字段不能被重写.doc[字段隐藏案例]。
只有将 Java 中为什么字段不能被重写讲清楚了后,学习 scala 覆写字段才会特别深刻,而且会感觉 scala 的机制会解决很多容易混淆的地方。
二、Java 中为什么字段不能被重写
案例:新建两个 Sub 对象,一个指向 Sub 类型的引用,一个指向 Super 类型的引用。
class Super {
//超类
String s = "Super";
}
class Sub extends Super {
//子类 sub 继承了 super
String s = "Sub";
这里都有一个 s
}
public class Fie
l
doverriding {
//类 Fieldoverriding
public static void main(String[ ]args) {
//主函数 main
Sub c1=new Sub
()
;
//创建 c1为子类对象的引用
System out -print
l
n(c1.s);
//(c1.s)输出 sub
Super c2=new Sub
()
:
//新的子类对象 sub 给父类 c2引用
Syst em .out -print
l
n(c2.s);
//(c2.s)输出 super
}
这里体现出字段并没有被真正覆写 s,只是在子类里面隐藏了。
这里有几个重要的原则,如果以前 java 没学习到位的话,这一节要认真听。
创建一个 class 类型的文件名为 javaFieldoverride,写入:
package com. atguigu. chapter07. myextends ;
public class JavaFiledoverride {
public static void main(String[ ] args) {
//创建两个对象
Sub2 c1 = new Sub2();
System. out . println(c1.s);
//输出 sub
Super2 C2 = new Sub2( );
System. out. println(c2.s);
//输出 super
}
// 主方法
class Super2 {
String S = " super" ;
}
class Sub2 extends Super2 {
string S = "sub" ;
//子类
运行:
D: \program\jdk8\bin\java
sub
super
Process finished with exit code 0
通过这段简单的代码想说的问题是创建了两个 sub 对象,但是为什么第二个对象打印出来的结果是“super”呢?
在 java 官方提供的 tutorial 有一段关于隐藏字段的明确定义:
Within a class, a field that has the same name as a field in the superclass hides thesuperclass’s field, even if their types are different. Within the subclass, the field in the superclasscannot be referenced by its simple name. Instead, the field must be accessed through super.Generally speaking, we don’t recommend hiding fields as it makes code difficult to read.
从上面这段解释中可以看出:成员变量不能像方法一样被重写。
在 java 中当一个子类定义了一个跟父类相同名字的字段,子类就是定义了一个新的字段。
这个字段在父类中被隐藏的,是不可重写的。
也就是说在它的底层里,子类有一部分,父类有一部分,它们之间是分开的。
既然是隐藏的那么就能想办法访问到。
三、如何访问隐藏字段
1、采用父类的引用类型,这样隐藏的字段就能被访问了,像上面所给出的例子一样。
2、将子类强制类型转化为父类类型,也能访问到隐藏的字段。
比如在以上代码的基础上写入:
System. out. println(((Super2)c1).s);
通过强转,仍然可以访问到被隐藏的字段。
四、小结
主要涉及到 java 里面一个字段隐藏的概念,父类和子类定义了一个同名的字段,不会报错。
但对于同一个对象,用父类的引用去取值(字段),会取到父类的字段的值,用子类的引用去取值(字段),则取到子类字段的值。
在实际的开发中,要尽量避免子类和父类使用相同的字段名,否则很容易引入一些不容易法现的 bug。
结论:
1、对于同一个对象,用父类的引用去取值(字段),会取到父类的字段的值。
2、用子类的引用去取值(字段),则取到子类字段的值。
五、回顾-java 另一重要特性:动态绑定机制
创建一个 alcss 类型的文件名为 javadaynamicbind 写入:
package com. atguigu. chapter07. myextends;
public class JavaDaynamicBind {
public static void main(String[ ] args) {
}
class AA {
public int i= 10;
public int sum() {
return getI()+ 10;
}
public int sum1() {
return i+ 10;
public int getl() {
return i;
}
class BB extendsAA {
public inti= 20;
public int sum() {
return i+ 20;
}
public int getl(){
return i;
}
public int sum1() {
returni+ 10;
}
将代码:AA
obj
= new BB();
System.out.println(
obj
.sum());
System.out.println(
obj
.sum1());
放到主函数里面,在这个数据空间里,将一个子类的对象地址,交给了一个 AA(父类的)引用。
流程是先看一下 BB,因为现在 obj 在内存空间是个子类,所以在调用方法的时候会先找自己有没有方法,发现有,所以:
System.out.println(
obj
.sum());
应该返回40
System.out.println(
obj
.sum1());
应该返回30
运行:
D: \program\jdk8\bin\java
40
30
Process finished with exit code 0
没有问题。
改进:
将public int sum() {
return i+ 20;
}
注销,此时 System.out.println(obj.sum());会输出30
如果将 sum 注销,找的就是上面的方法,方法里又调用了 getI,它返回的应该是20
将public int sum1() {
returni+ 10;
}
注销,此时 System.out.println(obj.sum1());会输出20
调用的是上面的 sum1,这时I返回的是10
执行:
D: \program\jdk8\bin\java
30
20
Process finished with exit code 0
这道题的关键,只要知道两个结论就可以了,以前讲过 Java 的动态绑定机制的小结:
1、如果调用的是方法,则 jvm 机会将该方法和对象的内存地址绑定
2、如果调用的是一个属性,则没有动态绑定机制,在哪里调用就返回对应的值
所以刚才因为把 sum 去掉了,调用的时候是调用的上面 AA 的 sum。
调用 getI,是调用方法,调用方法就会与内存地址绑定。
因为是 BB,所以调用的是下面 BB 的 getI,返回的I又不是动态绑定的,就会返回20,所以整个结果就是20+10。
知道了 jvm 机底层运行机制后,学习 Scala 的覆写字段就会很轻松,很明白它的规则。
六、总结
本节主要讲解了覆写字段相关内容:
1、基本介绍:
在 Scala 中,子类改写父类的字段,我们称为覆写/重写字段。覆写字段需使用 override 修饰。
2、回顾:
在 Java 中只有方法的重写,没有属性/字段的重写,准确的讲,是隐藏字段代替了重写。参考: Java 中为什么字段不能被重写.doc [字段隐藏案例]。
3、回顾-Java 另一重要特性:动态绑定机制
动态绑定的机制:
java 的动态绑定机制的小结
1、如果调用的是方法,则 Jvm 机会将该方法和对象的内存地址绑定
2、如果调用的是一个属性,则没有动态绑定机制,在哪里调用,就返回对应值