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修饰的类不可以有子类
网友评论