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
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。