Java-Final关键字

Java-Final关键字

Final成员变量

final修饰的成员变量必须由程序员显式的指定初始值

final修饰的类变量:

  • 静态初始块中指定初始值或者声明该变量时指定初始值

  • 而且只能在两个地方的其中之一指定

final修饰的实例变量:

  • 非静态初始块,声明该实例变量或构造函数中指定初始值

  • 只能在三个中的一个指定

实例变量不能在静态初始块中指定初始值,因为静态初始块是静态成员,不可访问实例变量—非静态成员

类变量不能在普通初始块中指定初始值,因为类变量在类初始化阶段已经被初始化了

public class FinalVariableTest {

    // 定义成员变量时指定默认值,合法。
    final int a = 6;
    // 下面变量将在构造器或初始化块中分配初始值
    final String str;
    final int c;
    final static double d;
    // 既没有指定默认值,又没有在初始化块、构造器中指定初始值,
    // 下面定义的ch实例变量是不合法的。
    // final char ch;
    // 初始化块,可对没有指定默认值的实例变量指定初始值
    {
        //在初始化块中为实例变量指定初始值,合法
        str = "Hello";
        // 定义a实例变量时已经指定了默认值,
        // 不能为a重新赋值,因此下面赋值语句非法
        // a = 9;
    }
    // 静态初始化块,可对没有指定默认值的类变量指定初始值
    static
    {
        // 在静态初始化块中为类变量指定初始值,合法
        d = 5.6;
    }
    // 构造器,可对既没有指定默认值、有没有在初始化块中
    // 指定初始值的实例变量指定初始值
    public FinalVariableTest()
    {
        // 如果在初始化块中已经对str指定了初始化值,
        // 构造器中不能对final变量重新赋值,下面赋值语句非法
        // str = "java";
        c = 5;
    }
    public void changeFinal()
    {
        // 普通方法不能为final修饰的成员变量赋值
        // d = 1.2;
        // 不能在普通方法中为final成员变量指定初始值
        // ch = 'a';
    }
    public static void main(String[] args)
    {
        FinalVariableTest ft = new FinalVariableTest();
        System.out.println(ft.a);
        System.out.println(ft.c);
        System.out.println(ft.d);
    }


}


假如打算在构造函数中,初始块中对final成员变量进行初始化,则不应在初始化之前直接访问final成员变量

但Java又允许通过方法来访问final成员变量,此时系统就会帮你将final成员变量进行默认初始化

public class FinalErrorTest {

    // 定义一个final修饰的实例变量
    // 系统不会对final成员变量进行默认初始化
    final int age;
    {
        // age没有初始化,所以此处代码将引起错误。
//		System.out.println(age);
        printAge();
        age = 6;
        System.out.println(age);
    }
    public void printAge(){
        System.out.println(age);
    }
    public static void main(String[] args)
    {
        new FinalErrorTest();
    }

    
}

final成员变量在显式初始化之前不能直接访问,但又可以通过方法来访问,这可能是Java设计的一个缺陷

final局部变量

系统不会对局部变量进行初始化,必须程序员自己进行显式初始化

所以用final修饰局部变量,可以在定义同时指定初始值,也可以不指定

  • 如果定义时没有指定初始值,可以在后面进行初始化,但只能进行一次

  • 如果定义时已经指定初始值,后面就不能再赋值


public class FinalLocalVariableTest {

    public void test(final int a)
    {
        // 不能对final修饰的形参赋值,下面语句非法
        // a = 5;
    }
    public static void main(String[] args)
    {
        // 定义final局部变量时指定默认值,则str变量无法重新赋值
        final String str = "hello";
        // 下面赋值语句非法
        // str = "Java";
        // 定义final局部变量时没有指定默认值,则d变量可被赋值一次
        final double d;
        // 第一次赋初始值,成功
        d = 5.6;
        // 对final变量重复赋值,下面语句非法
        // d = 3.4;
    }

    
}

final修饰基本类型变量和引用类型变量

  • final修饰基本类型变量,不能对该变量重新赋值

  • final修饰引用类型变量,final只保证这个变量所引用的地址不变,即一直引用同一个对象,但是这个对象是可以改变的

class Person
{
    private int age;
    public Person(){}
    // 有参数的构造器
    public Person(int age)
    {
        this.age = age;
    }
    // 省略age的setter和getter方法
    // age的setter和getter方法
    public void setAge(int age)
    {
        this.age = age;
    }
    public int getAge()
    {
        return this.age;
    }
}
public class FinalReferenceTest
{
    public static void main(String[] args)
    {
        // final修饰数组变量,iArr是一个引用变量
        final int[] iArr = {5, 6, 12, 9};
        System.out.println(Arrays.toString(iArr));
        // 对数组元素进行排序,合法
        Arrays.sort(iArr);
        System.out.println(Arrays.toString(iArr));
        // 对数组元素赋值,合法
        iArr[2] = -8;
        System.out.println(Arrays.toString(iArr));
        // 下面语句对iArr重新赋值,非法
        // iArr = null;
        // final修饰Person变量,p是一个引用变量
        final Person p = new Person(45);
        // 改变Person对象的age实例变量,合法
        p.setAge(23);
        System.out.println(p.getAge());
        // 下面语句对p重新赋值,非法
        // p = null;
    }
}


"宏替换"final变量

当变量满足三个条件时,这个final变量就不再是一个变量,而是一个直接量:

  • final修饰符修饰

  • 在定义时就指定了初始值

  • 该初始值可以在编译时就被确定

public class FinalLocalTest {

    public static void main(String[] args) {
        final int a = 5;

        System.out.println(a);
    }
}

System.out.println(a)语句实际上是直接执行 System.out.println(5)

如果被赋值的表达式只是基本的算术表达式或字符串连接操作,没有访问普通变量以及调用方法,Java编译器同样将这种final变量当做“宏变量”处理


public class FinalReplaceTest {


    public static void main(String[] args)
    {
        // 下面定义了4个final“宏变量”
        final int a = 5 + 2;
        final double b = 1.2 / 3;
        final String str = "leran" + "Java";
        final String book = "Java" + 99.0;
        // 下面的book2变量的值因为调用了方法,所以无法在编译时被确定下来
        final String book2 = "Java" + String.valueOf(99.0);  //①
        System.out.println(book == "Java99.0");
        System.out.println(book2 == "Java99.0");
    }


}

再看一个例子:

public class StringJoinTest {

    public static void main(String[] args)
    {
        String s1 = "LJava";
        // s2变量引用的字符串可以编译时就确定出来,
        // 因此s2直接引用常量池中已有的"LJava"字符串
        String s2 = "L" + "Java";
        System.out.println(s1 == s2); // 输出true
        // 定义2个字符串直接量
        String str1 = "L";     //①
        String str2 = "Java";     //②
        // 将str1和str2进行连接运算
        String s3 = str1 + str2;
        System.out.println(s1 == s3); // 输出false
    }
}

final方法

final修饰的方法不可重写

public class FinalMethodTest
{
    public final void test(){}
}
class Sub extends FinalMethodTest
{
    // 下面方法定义将出现编译错误,不能重写final方法
//    public void test(){}
}

final类

final修饰的类不可以有子类

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值