final关键字 之 final数据

final数据

许多程序设计语言都有自己的办法告诉编译器某个数据是“常数”。常数主要应用于下述两个方面:

    (1)编译期常数,它永远不会改变

    (2)在运行期初始化的一个值,我们不希望它发生变化

    对于编译期的常数,编译器(程序)可将常数值“封装”到需要的计算过程里。也就是说,计算可在编译期间提前执行,从而节省运行时的一些开销。在java中,这些形式的常数必须属于基本数据类型(Primitives),而且要用final关键字进行表达。在对这样的一个常数进行定义的时候,必须给出一个值

    无论static还是final字段,都只能存储一个数据,而且不得改变。

    若随对象句柄使用final,而不是基本数据类型,它的含义就稍微让人有点迷糊了。对于基本数据类型,final会将值变成一个常数;但对于对象句柄,final会将句柄变成一个常数。进行声明时,必须将句柄初始化到一个具体的对象(就是 只爱一人心,白首不分离)。而且永远不能将句柄变成指向另一个对象(不能喜欢其他的女人)。然而,对象本身是可以修改的(自己可以让别人更换自己的发型)。Java对此未提供任何手段,可将一个对象直接变成一个常数(但是,我们可自己编写一个类,使其中的对象具有“常数”效果)。这一限制也适用于数组,它也属于对象。

class Value{
	int i = 1;
}

public class FinalDemo {
	//可以在编译时常量
	final int i1 = 9;
	static final int I2 = 99;
	
	//典型的公共常量:
	public static final int I3 = 39;
	
	//不能是编译时常量:
	final int i4 = (int)(Math.random()*20);
	static final int i5 = (int)(Math.random()*20); //注意i5 在 编译期间是未知的,所以它没有大写。
	
	Value v1 = new Value();
	final Value v2 = new Value();
	static final Value V3 = new Value();
	//! final Value v4; // Pre-Java 1.1 Error:
	// no initializer
	
	//Arrays;
	final int[] a = {1,2,3,4,5,6};
	
	public void print(String id){
		System.out.println(
		id+":"+"i4:"+i4+",i5="+i5);		
	}
	
	public static void main(String[] args) {
		FinalDemo fd1 = new FinalDemo();
		//!fd1.i1++;	//不能改成的值
		//fd1.v2.i++;		//Object isn't constant 不是常数!
		fd1.v1 = new Value(); //ok not final
		//!fd1.V3 = new Value();  fd1.v2 = new Value();  change handle
		fd1.V3.i++;
		//System.out.println(fd1.V3.i); //i可以改成
		
		//! fd1.a = new int[3];
		fd1.a[0]=5;//值可变,对象不可变
		System.out.print("int [] a=");
		for (int i = 0; i < fd1.a.length; i++) {
			if(fd1.a.length-i==1){
				System.out.println(fd1.a[i]+"]");
			}else if(i==0){
				System.out.print("["+fd1.a[i]+",");
			}else{
				System.out.print(fd1.a[i]+",");
			}
		}
		
		
		fd1.print("fd1");
		
		//创建新的FinalDemo对象
		FinalDemo fd2 = new FinalDemo();
		fd1.print("fd1"); //fd1:i4:16,i5=18 
		fd2.print("fd2"); //fd2:i4:3,i5=18 
		/**
		 * 注意对于fd1和 fd2来说,i4的值是唯一的,但 i5的值不会由于创建了另一个FinalData 对象而发生改
		变。那是因为它的属性是static,而且在载入时初始化,而非每创建一个对象时初始化。
		 * */
		
		System.out.println("i5:"+FinalDemo.i5);
	}
}

输出结果:

int [] a=[5,2,3,4,5,6]
fd1:i4:18,i5=11
fd1:i4:18,i5=11
fd2:i4:17,i5=11
i5:11

由于i1和I2都是具有final属性的基本数据类型,并含有编译期的值,所以它们除了能作为编译期的常数使用外,在任何导入方式中也不会出现任何不同。I3是我们体验此类常数定义时更典型的一种方式:

    (1)public表示可在包外使用;   

    (2)static强调它们只有一个;

    (3) final表明它是一个常数。

    注意:含有固定初始化值(即编译期常数)的final static基本数据类型,它们的名字根据规则要全部采用大写

/**
 * 男
 */
public static final byte SEX_MALE = 1;

    不能由于某样东西的属性是final,就认定它的值能在编译时期知道。i4 和 i5 向大家证明了这一点。它们在运行期间使用随机生成的数字。例子的这一部分也向大家揭示出将final 值设为static和非static之间的差异。只有当值在运行期间初始化的前提下,这种差异才会解释出来。因为编译期间的值被编译器认为是相同的。这种差异可从输出结果中看出:

fd1:i4:18,i5=11
fd2:i4:17,i5=11

    注意对于fd1和fd2来说,i4的值是唯一的,但i5的值不会由于创建了另一个FinalDemo对象而发生改变。那是因为它的属性是static,而且在载入时初始化,而非每创建一个对象时初始化。

    从v1到v4的变量向我们揭示出final句柄的含义。正如大家在main()中看到的那样,并不能认为由于v2属于final,所有就不能再改变它的值。然而,我们确实不能再将v2绑定到一个新对象,因为它的属性是final。这边与final对于一个句柄的确切含义。我们会发现同样的含义亦适用于数组,后者只不过是另一种类型的句柄而已。将句柄变成final看起来似乎不如将基本数据类型变成final那么有用。

    摘自:《Java编程思想第四版》

Java对于修改Integer变量值的问题?

摘自知乎:

java中Integer传参是无法改变原值的,如
Integer i = new Integer(-1);
void chang(Integer i){
  i = 1;
}
甚至在chang函数中new Integer(1)都无法改变 i 的值,如i = new Integer(1);
那么问题是:为什么可以改变Object中的Integer属性呢?如:
public class ObjectName{
  private Integer id;
  public ObjectName(Integer id){
    this.id = id;
  }
  public void setId(Integer id){
    this.id = id;
  }
}
ObjectName实例调用setId函数是可以改变Integer id的,这是为什么?

值传递与引用比较清楚。Integer不可变的,在java官方中明确指出了,所以有i = new Integer(1)都无法改变i原来的值。
我的问题就在这里:Integer变量既然是不可变的,那为什么用Object封装后就可变了呢?

最佳答案:

这个可以看下Integer类的源码,因为Integer类中的value值是final的。
/**
 * The value of the {@code Integer}.
 *
 * @serial
 */
private final int value;
更改一个Integer对象的值,会新创建一个Integer对象的。

作者:向南
链接:https://www.zhihu.com/question/34904653/answer/129044196
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

猜你喜欢

转载自my.oschina.net/u/3568600/blog/1802038