final修饰变量时,表示该变量一旦获得了初始值就不可被改变。final既可以修饰成员变量,也可以修饰局部变量,形参。
final成员变量
对于final修饰的成员变量而言,一旦有了初始值,就不能被重新赋值。 所以 final修饰的成员变量必须有程序员显式地指定初始值。系统不会对final成员变量进行隐式初始化。
类变量: 必须在静态初始化块中指定初始值 或者 声明该类变量的时候指定初始值。
类变量不能在非静态初始化块中指定初始值,因为类变量在类初始化阶段就已经被初始化了。
实例变量:必须在非静态初始化块、声明该实例变量、 或者 构造器中指定初始值。
如果非静态初始化块已经为某个实例变量指定了初始值,则不能再在构造器中为该实例变量指定初始值。
final 形参
public void test (final int a) {
//不能对final修饰的形参赋值,下面语句非法
//a = 5;
}
形参在调用该方法时,由系统根据传入的参数来完成初始化,因此使用final修饰的形参不能被赋值。
final修饰引用类型变量
使用final修饰基本类型变量时,不能对基本基本类型变量重新赋值。
对于引用类型而言,它保存的仅仅是一个引用,final只保证这个引用类型变量所引用的地址不会改变,既一直引用同一个对象,但这个对象完全可以发生改变。
class A {
public static void main(String[] args) {
final int[] iArr = {1,6,12,9}; //final修饰数组变量,iArr只是一个引用变量
System.out.println("Arrays.toString(iArr)");
Arrays.sort(iArr); //对数组元素进行排序,合法
System.out.println("Arrays.toString(iArr)");
iArr[2] = 10; //对数组元素赋值,合法
System.out.println("Arrays.toString(iArr)");
iArr = null; //对iArr重新赋值,非法。
final Person p = new Person(45); //final修饰Person变量,p是一个引用变量
p.setAge(23); //改变Person对象的age实例变量,合法
System.out.println(p.setAge());
p = null; // 对p重新赋值,非法
}
}
可执行“宏替换”的final变量
当一个final变量在编译之前就被指定好初始值且定下来,这时final变量就不再是一个变量,而是相当于一个直接量。final修饰符的一个重要用途就是定义“宏变量”,当定义这个final变量时久违该变量指定了初始值,那么该初始值在编译时就会确定下来,那么这个final变量实质上就是一个“宏变量”,编译器会把程序中所有用到该变量的地方直接替换成该变量的值。
final int a = 5;
System.out.println(a); //当程序执行System.out.println(a);代码时,实际转换成执行System.out.println(5)
java会使用常量池来管理曾经出现过的字符串直接量,例如执行String a = “java”;语句之后,常亮池汇总就会缓存一个字符串“java”; 如果程序再执行String b = “java”;系统将会让b直接指向常量池中的”java”字符串。 因此 a==b将会返回true。
class B {
public static void main(String[] args) {
String s1 = "学习java";
String s2 = "学习"+"java";
System.out.println(s1==s2); //输出true
String str1 = "学习";
String str2 = "java";
String s3 = str1 + str2;
System.out.println(s1 == s3); //输出false
}
}
s1是一个普通的字符串直接量“学习java” .s2直接指向常量池缓存池中的“学习java” 因此第一个输出true
str1和str2都是两个普通变量,编译器不会执行“宏替换“因此编译器无法在编译时确定s3的值。所以第二个输出false 。 想让第二个输出ture 只要将str1 str2使用final修饰即可,编译器就会在编译器对str1和str2执行“宏替换“”,这样在编译阶段就可以确定str3的值。
PS:对于实例变量来说,在定义该变量的时候赋值,在构造器和非静态初始化块、这三个地方赋值的效果基本一样,但是只有在定义该变量的时候指定初始值才会有“宏变量”的效果
final方法
final修饰的方法不可以被重写.
public class A {
public final void test();
}
class B extends A {
public void test(); //编译错误,无法被重写。
}
public class A {
private final void test();
}
class B extends A {
public void test(); //可以编译。
}
private修饰的方法只能在当前类中可见,其他子类无法访问。
因为子类中定义一个与父类private方法相同方法名 ,相同形参列表,相同返回值类型的方法,也不是方法重写,只是重新定义了一个新方法。
public class A {
private final void test(){};
public final void test(String arg){}; //对
}
不能重写,但是可以被重载。
final类
final修饰的类不可以有子类。比如String类。