Java学习篇之---修饰符final详解
最近又翻看了一遍Java的基础知识,发现当初自己关于修饰符final的理解就曾经迷茫过,为了帮助Java初学者更好度过迷茫期,特此献上此文(此文将重点讲解final修饰的变量,至于final修饰的类和方法,由于比较简单,就不再下面列出了)。
一、概述:
final关键字可用于修饰类、变量和方法。final修饰变量时,表示该变量一旦获得了初始值就不可被改变(可以赋初值,但是不可以被改变),final既可以修饰成员变量(包括类变量和实例变量),也可以修饰局部变量、形参。
二、final成员变量:
1) 类变量:
final修饰类变量,要么在定义该类变量时指定初始值;要么在静态初始化块中指定初始值。
/*正确代码演示*/
public class FinalClassTest{
final static int a=210;//在定义该类变量时指定初始值;
final static int b;
static{
b=211;//在静态初始化块中指定初始值;
}
}
/*错误代码演示*/
public class FinalClassErrorTest{
final static int aa=110;
final static int bb;
//系统不会为final成员变量(类变量和实例变量)进行隐形初始化;而此类变量既没有在定义时指定初始值,又没有在静态初始化块中指定初始值,所以非法。
final static int cc;
static{
aa=111;//类变量已经定义了初始值,不能再次赋值,因此此语句非法;
}
public void finalClassChange(){
bb=112;//在普通方法中为类变量赋值,因此此语句非法;
}
}
2) 实例变量:
final修饰实例变量,要么在定义该实例变量时指定初始值;要么在普通初始化块中指定初始值;要么在构造器中指定初始值。
/*正确代码演示*/
public class FinalInstanceTest{
final int c=310;//在定义该实例变量时指定初始值;
final int d;
final int e;
{
d=311;//在普通初始化块中指定初始值;
}
public FinalInstanceTest(){
e=312;//在构造器中指定初始值;
}
}
/*错误代码演示*/
public class FinalInstanceErrorTest{
final int cc=410;
final int dd;
//系统不会为final成员变量(类变量和实例变量)进行隐形初始化;而此实例变量既没有在定义时指定初始值,又没有在普通初始化块中指定初始值,又没有在构造器中赋值,所以非法。
final int ee;
{
cc=411;//实例变量已经定义了初始值,不能再次赋值,因此此语句非法;
}
public void finalInstanceChange(){
dd=412;//在普通方法中为实例变量赋值,因此此语句非法;
}
public FinalInstanceErrorTest(){
}
}
三、final局部变量:
系统不会对局部变量进行隐形初始化,需要程序员显性初始化。
public class FinalLocalTest{
public void invokTest(final int f){
f=510;//对final修饰的形参变量赋值,非法;
//该方法被调用时,由系统根据传入的参数来对形参完成初始化;
}
public void mainTest(){
final int g=511;//定义final变量时指定初始值,合法;
final int h;
h=512;//第一次赋初始值,合法;
}
}
四、final修饰基本类型变量和引用类型变量的区别:
final修饰基本类型变量时,不能对基本类型变量重新赋值;对于引用类型而言,它保存的只是一个引用,final只保证这个引用类型变量所引用的地址不变,即一直引用同一个对象,但是这个对象的成员可以改变;
class Student{
privte int age;
public Student(int age){
this.age=age;
}
//此处省略age的setter和getter方法;
}
public class FinalReferenceTest{
public static void main(String[] args){
//final修饰数组变量,iArray是一个引用变量;
final int[] iArray={5,6,7,8};
Arrays.sort(iArray);//对数组元素进行排序,合法;
iArray[3]=9;//对数组元素赋值,合法;
iArray=null;//对iArray重新赋值,非法;
final Student st=new Student(21);
st.setAge(23);//改变Student对象的age实例变量,合法;
st=null;//对st重新赋值,非法;
}
}
五、可执行"宏替换"的final变量:
对于一个final变量而言,不管是类变量、实例变量还是局部变量,只要该变量满足以下三条,则final变量就不再是一个变量,而是相当于是一个直接值:
1、使用final修饰符修饰;
2、在定义该final变量时指定了初始值;
3、该初始值可以在编译时就确定下来。
public class MacroFinalTest{
public static void main(String[] args){
final String domainName="pplns.com";
final String domain="pplns"+".com";
System.out.println(domainName==domain);//返回值为true;
final String domainPrefix="pplns";
final String domainSuffix=".com";
final String myDomain=domainPrefix+domainSuffix;
System.out.println(domainName==myDomain);//返回值为true;
}
}
public class MacroTest{
public static void main(String[] args){
String domainName="pplns.com";
String domain="pplns"+".com";
System.out.println(domainName==domain);//返回值为true;
String domainPrefix="pplns";
String domainSuffix=".com";
String myDomain=domainPrefix+domainSuffix;
System.out.println(domainName==myDomain);//返回值为false;
}
}
以上特意用final修饰的变量和无final修饰的变量进行对比,从而发现不同之处。
Java通过一个常量池来管理曾经用过的字符串常量,例如执行String domain="pplns.com";语句之后,常量池中就缓存了一个字符串“pplns.com”;如果程序再执行String domainName="pplns.com";,系统将会让domainName直接指向常量池中“pplns”字符串,因此domain==domainName将返回true;但是在上例MacroTest类中,myDomain是由两个变量拼接而成,而变量在编译期间是不会确定下来,只有在运行期间才会确定下来,所以domainName==myDomain返回的是false;而在上例MacroFinalTest类中,domainPrefix和domainSuffix是由final修饰的变量,所以这两个变量在编译期间就会当作“宏变量”,即常量来处理,所以myDomain在编译期间实际是由"pplns"和".com"拼接而成,所以domainName==myDomain返回的是true。