final关键字和成员变量,构造方法,成员方法在继承的深入思考

final关键字

1.final可以修饰类,方法,变量
final修饰类,被final修饰的类不可以被继承。
final修饰方法,被final修饰的方法不能被子类override方法重写
final修饰变量,被final修饰的变量不能重新赋值,因为变量被final修饰后就变成了常量。

常量知识点:
字面型常量:”hello“,20,ture
自定义常量:final int x = 20,x就是一个自定义常量
2.final修饰的变量的初始化时机
同样的也符合成员变量的初始化时机,在成员变量初始化——构造方法初始化这个阶段,在构造方法完毕之前。
如果被静态修饰,在加载类的时候就被初始化。
值得注意的是,final修饰的成员变量只能被赋值一次。
可以在成员变量区定义成员变量,然后再成员方法内给变量赋值,只要注意只能赋值一次,就不会报错。

class test{
    final int x;
    public(){

        x = 20;
    }
}

3.final在继承中的体现
被final修饰的方法,可以被继承但是不能被重写,所以,当我们不想子类去重写父类中的某些功能的时候,可以在父类中将这个方法用final修饰,这样子类就不能对父类的这个方法进行方法重写。

4.final修饰局部变量的问题
A:变量是基本类型:基本类型的值不能发生改变
B:变量是引用类型:引用变量的地址值不能发生改变,但是该对象的堆内存可以发生改变。

class Student{
    int age = 10;
    //final int age2 = 20;
    final int age2;
    {
        //age2 = 30;这里会报错,原因是被final修饰的变量不能重新赋值
    }
    public Student(){
        age = 30;//没有被fanal修饰的成员变量,可以在根据先后顺序在构造方法中被重新赋值
        age2 = 40;//成员变量的初始化可以在构造方法中完成,但如果是被final修饰的成员变量,无论在哪里初始化都只能被赋值一次。
    }

}
class TestFinal{
    public static void main(String[] args){
        final Student s = new Student();
        System.out.println(s.age);
        s.age = 100;
        System.out.println(s.age);
        // s = new Student();这样才会产生编译错误,因为final修饰引用类型型时是限定了这个引用类型s的地址值。这一步相当于重新分配内存空间
        //而被final修饰的引用类型变量是无法为其重新分配内存空间的
        Student ss = new Student();
        System.out.println(ss.age+","+ss.age2);
    }
}

成员变量,构造方法,成员方法在继承中的深入思考

1.给变量赋值,和定义一个变量的误区

我们先从一个例子说起

class Animal{
    int num = 10;
    final int num2 = 20;
    public void show(){};
    public void method(){
        System.out.println("method");
    }
}
class Cat extends Animal{
    int num2 = 20;//被final定义的变量不是不能重新赋值吗?
    public void show(){
        num2 = 40;//被final定义的变量不是不能重新赋值吗?
        System.out.println(num2);
    }
}

这是我自己在写代码的时候发出的疑问,这个问题产生的原因是我长期以来对给变量赋值:x = 10;定义一个变量 int x = 10;混淆。
要明确的是,子类在这里不是给父类中被final定义的变量重新赋值,而是重新定义了这个变量,而这个误区的产生可能是因为这两个原因:
A:在同一个类中的时候,我们是见不到对同一个变量名进行重复定义的,原因是会报错。

class A{
    int x = 20;
    //int x = 30;//报错,已经在类A中定义了x。
    x = 30;
}
class B extends A{
    //x = 30;//报错。
    int x = 30;
}

B:但是在继承中,子类想要改变从父类中继承过来的成员变量的值时,就会出现,因为子类不能对父类的成员变量直接赋值(报错,其实这里就可以看出来为什么说变量没有重写,与成员方法重写不同,子类中的同名成员变量和父类中的同名成员变量根本是不同的东西。),所以要对父类的成员变量重新定义,其实这里已经变成子类的成员变量了。
下面分两种情况:
a:当子类不对父类的成员变量重新定义时,那个子类会继承来自父类的成员变量,如果这个成员变量在父类中被final修饰,那么子类是不能对这个变量重新赋值的。
b:当子类对父类的成员变量重新定义是,那么子类中的这个同名变量和父类中的同名变量,不再是一个变量赋值的关系,这两个变量在栈内存中的位置不同,即使父类中这个变量被final修饰,也不会影响子类中的这个变量被赋值。

2.成员变量,构造方法和成员方法在继承中的思考
我在继承中,常常这样考虑继承:
比如子类继承父类的成员变量,那就相当于子类中直接添上了这个成员变量;
又比如子类继承父类的方法后,我也会把父类中的方法也直接改成子类中被重写后的方法。
这样想在大部分时候是对的,但是,这两种的想法都是不严谨的,有时会带来麻烦,我们来看下面两个例子
A:

class Fu{
    int x = 10;
}
class Zi extends Fu{
    x = 30;//报错。如果这里想成给子类直接添上int x = 10;就不会报错
}

B:

class Fu{
    //int age = 10;//即使在这里加上这句,输出的结果还是20。去掉这句后,Fu类中根本没有age,为何能编译运行,加上这句,为何结果是10。
    public void show(){

    }
}
class Zi extends Fu{
    int age = 20;
    public void show(){
        System.out.println(age);
    }
}
class TestDemo{
    public static void main(String[] args){
        Fu f = new Zi();
        f.show();
    }
}
输出结果 20

由上面两个例子我们知道这种脑内的相当于,有的时候是不灵的,那么有没有一种理解方法是可以对继承中的成员变量和成员方法有最佳的理解呢?
我的回答是有的
方法就是:只要发生了继承,就不要把子类和父类割裂开来看待,而是应该将子类和父类看作一个整体,然后用到那个变量或者方法要搞清楚这个变量,这个变量是子类还是父类的,方法是父类的还是子类的,然后去相应的类中调用这个变量或者方法。这种思想在尤其在多态,和方法重写中能够帮助理解
比如这个案例B。这里是一个向上转型,fu f = newZi();f.show();首先是去到父类中的show(),由于父类中的show()被子类重写,所以要去子类中才能调用这个show(),那么既然是在子类中,当然可以使用子类的age,而且也只能使用子类的age。

3.方法重写的特殊性
由上面的例子和思考,我们会发现,在继承中子类可以继承父类的成员方法和成员变量,但是对于声明相同的方法和变量,在继承中的原理是完全不同的,对于方法,有着方法重写,父类中的方法会变成子类的方法,在栈中的地址值相同。而对于变量,在栈中的地址值不同,是不同的两个变量,只是名字相同。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值