您的位置:首页 > 编程语言 > Java开发

JAVA final 、super 关键字以及继承关系中父类与子类实例变量初始化的 理解

2015-10-20 22:27 567 查看
1,final 修饰符修饰变量、方法、类 时有什么作用?

①final 修饰变量:该变量被赋初值后,不能对它重新赋值

②final 修饰方法:此方法不能重写,即父类中某方法被final修饰,在子类中将不能定义一个与父类final 方法同名且有相同方法标识符(参数个数也相同,返回值类型相同)的方法

③final 修饰类:此类不能再派生子类

④final 修饰的实例变量在使用前必须显示地初始化。对于普通实例变量,可以默认初始化:如引用类型的实例变量,默认初始化值为null,但使用final修饰实例变量后,该变量可使用以下三种方式显示初始化: a)在定义final实例变量时指定初始值 b)在非静态初始化块中指定初始值 c)在构造器中初始化

⑤局部变量被final修饰时,a)局部变量的值不能再被改变 b)局部变量在使用前必须显示初始化(其实JAVA本来就要求局部变量在使用之前要先初始化)

❻当使用final修饰变量时(包括局部变量、实例变量、类变量),如果在定义该final变量时就指定了初始值,则该变量的值在编译阶段就已经被确定下来了,此时该变量类似于C中的宏变量。认识到这一点很重要!!!这样就能理解为什么有些变量值的初始化在奇怪,原来它们是在编译期就已经确定了。这也是为什么在《JAVA并发编程实践》一书中
为了保证线程的安全性,应尽量将变量定义成final的一个原因。另一个原因,我想应该是:带有final修饰的变量具有“不变性”,而不变性正是线程安全的一个特征。


⑦为什么要求内部类中访问的局部变量(在方法体中定义的变量)必须使用final修饰?

内部类访问局部变量,显然该内部类是一个局部内部类(在方法中定义的类)那为什么局部变量要用final修饰呢?答案是final修饰的局部变量不可变。对于局部变量而言,它的生命周期局限于方法体内,当方法结束时,局部变量的作用域也随之结束。但当在内部类中访问局部变量时,内部类会扩大局部变量作用域的生命周期,这时,保证该局部变量的不可变性对于程序的安全是很重要的。看下面示例:

public class Test {
public static void main(String[] args) {
final String str = "Hello";

new Thread(new Runnable() {

@Override
public void run() {
for(int i = 0; i < 100; i++){
System.out.println(str);
try{
Thread.sleep(100);
}catch(InterruptedException e){}
}
}
}).start();
}
}

局部变量str 在main方法中定义,当main方法执行到 .start() 时,main方法的主线程结束了,但是new Thread创建的线程还未结束,它继续在访问str变量的值。

2,super 关键字如何理解?

①借助super关键字,可以在子类中调用父类的变量或方法(假设访问权限允许)。但需要注意的是:super不是一个变量,不能像this那样,当做变量来用。如,return this; 是允许的,但是 return super; 则不行。super神奇的地方在于,super并不是一个父类对象的引用(变量),但是可以通过super.method()、super.field 
来访问调用父类中的方法以及访问父类中的变量。

3,子类继承了父类时,子类中的实例变量与父类中的实例变量有哪些关系?

①在继承中,当new 一个子类对象时,首先会去执行父类的构造器,初始化父类的实例变量。即相当于new 子类对象时,隐含生成了一个父类对象,但是貌似是无法直接操纵这个父类对象的,能对父类做一些操作都是通过super关键字来完成的,如super.method(),super.field。

那么,当子类中有与父类同名的实例变量时,子类的实例变量会隐藏父类的实例变量,若要访问父类的实例变量就是前面提到的使用super关键字。

②通过引用来调用方法和通过引用来访问变量,JAVA处理是不同的。如,假设有一个引用变量obj,obj.method() 调用的是obj实际指向的引用类型的方法,而obj.field 访问的是声明obj类型的属性。看下面示例:

class Base{
int count = 2;
void method(){
System.out.println("Base Method");
}
}

public class Sub extends Base{
int count = 20;

@Override
void method(){
System.out.println("Sub Method");
}
public static void main(String[] args) {
Sub s = new Sub();
Base b = s;
System.out.println(s.count);//20
System.out.println(b.count);//2
s.method();//Sub Method
b.method();//Sub Method
}
}b.count 为2,因为b声明成Base类型。b.method()输出: Sub Method,因为b实际指向的还是子类型Sub。
看一段解释:当变量的编译时类型和运行时类型不同时,通过该变量访问它引用的对象的实例变量时,该实例变量的值由声明该变量的类型决定。但是通过该变量调用它引用的对象的实例方法时,该方法行为将由它实际所引用的对象来决定。

③不要在父类的构造器中调用被子类重写的方法(说得更直白一点:不要在父类的构造器中调用子类的方法),因为当 new 子类对象时,会先执行父类构造器中的方法,在父类构造器中调用子类的方法,而此时子类的对象都还没有初始化完成!这也是为什么在JAVA多线程中所不允许的。这会造成很大的线程不安全性。比如,子类中重载的某方法会修改子类的实例变量,若在父类的构造器调用了该重载方法修改了子类的实例变量,而此时子类变量尚未初始化,将会造成子类实例变量的极大不确定性。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java final super 继承 变量