final&static在java中的具体表现

final的目的是为了某些数据不要改变,减轻系统负担。

final可以修饰数据,方法,类。


一、数据

package test;

import java.util.Random;

public class FinalTest {
        private final String final_01 = "jason";    //编译期常量,必须要进行初始化,且不可更改
        private final String final_02;         //构造器常量,在实例化一个对象时被初始化
        
        private static Random random = new Random();
        private final int final_03 = random.nextInt(10); //使用随机数来进行初始化
        
        //引用变量
        public final Person final_04 = new Person("jason_nan"); //final指向引用数据类型
        
        FinalTest(String final_02){
            this.final_02 = final_02;
        }
        
        public String toString(){
            return "final_01 = " + final_01 +"   final_02 = " + final_02 + "   final_03 = " + final_03 +
                   "   final_04 = " + final_04.getName();
        }
        
        public static void main(String[] args) {
            System.out.println("------------第一次创建对象------------");
            FinalTest final1 = new FinalTest("cm");
            System.out.println(final1);
            System.out.println("------------第二次创建对象------------");
            FinalTest final2 = new FinalTest("zj");
            System.out.println(final2);
            System.out.println("------------修改引用对象--------------");
            final2.final_04.setName("jasonnan");
            System.out.println(final2);
        }
    
}
    class Person {
    private String name;

    Person(String name){
        this.name = name;
    }
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

输出结果:

------------第一次创建对象------------
final_01 = jason   final_02 = cm   final_03 = 0   final_04 = jason_nan
------------第二次创建对象------------
final_01 = jason   final_02 = zj   final_03 = 6   final_04 = jason_nan
------------修改引用对象--------------
final_01 = jason   final_02 = zj   final_03 = 6   final_04 = jasonnan
注:这里类重写了toString方法,引用变量根据toString输出已不是内存地址。

       1.private final String final_01 = "jason";//这里是在编译期就在线程私有栈中分配给final_01一个固定空间位置,指向的是String类型在方法区常量池中唯一的存有“jason”字符串的地址,final_01已经无法被重新赋值,也就是说这一状态一直持续到该线程结束。

       2.private final String final_02;//这里指编译期就在线程私有栈中分配给final_02一个固定空间位置,并没有指向对象地址,而需要在运行初始化期间,指向String类型在方法区常量池中唯一的存有“null”字符串的地址,这一状态可以在重新被赋值会有所改变,但引用变量在私有栈中的位置一直不变,指向的常量可以改变,这就是编译期赋值与运行期赋值的差别。final变量在定义的时候,可以先声明,而不给初值,这种变量也称为final空白,无论什么情况。编译器都确保空白final在使用之前必须被初始化,直接操作就是根据数据类型赋值,String为null,int为0等,因此final数据成员会依据不同对象而有所不同。

       3.private final int final_03 = random.nextInt(10); //这里指编译期就在线程私有栈中分配给final_03一个固定空间位置,然后在初始化时(这里的初始化指的是依附的类实例化对象)调用random.nextInt(10)得到一个int值,在线程私有栈中分配空间,由于是随机数,栈中空间也是不固定的,这里FinalTest类实例化了两次,final_03也是不同的,这说明创建一个实例对象,final_03就初始化一次。

       4.public final Person final_04 = new Person("jason_nan");///这里指编译期就在线程私有栈中分配给final_03一个固定空间位置,然后在初始化时在堆的空间创建了一个Person("jason_nan")实例化对象,这里初始化了两次,也就是说在堆空间中有两个Person("jason_nan")实例化对象,具体是不是这样,下面我们修改一下代码:

      

package test;

import java.util.Random;

public class FinalTest {
	    private final String final_01 = "jason";    //编译期常量,必须要进行初始化,且不可更改
	    private final String final_02;                //构造器常量,在实例化一个对象时被初始化
	    
	    private static Random random = new Random();
	    
	     private final  int final_03 = random.nextInt(10);    //使用随机数来进行初始化
	    //引用
	    public final Person final_04 = new Person("jason_nan");    //final指向引用数据类型
	    
	    FinalTest(String final_02){
	        this.final_02 = final_02;
	    }
   
	    
	 public String toString(){
	       return "final_01 = " + final_01 +"   final_02 = " + final_02 + "   final_03 = " + final_03 +
	             "   final_04 = " + final_04.getName();
	   }
	    
	    public static void main(String[] args) {
	    	
	        System.out.println("------------第一次创建对象------------");
	        FinalTest final1 = new FinalTest("cm");
	     	System.out.println(final1.final_04);
	        System.out.println(final1);
	        System.out.println("------------第二次创建对象------------");
	        FinalTest final2 = new FinalTest("zj");
	      	System.out.println(final1.final_04);
	        System.out.println(final2);
	        System.out.println("------------修改引用对象--------------");
	        final2.final_04.setName("jasonnan");
	        System.out.println(final2);
	    }
	
}
    class Person {
    private String name;

    Person(String name){
        this.name = name;
    }
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

结果:

------------第一次创建对象------------
test.Person@1f33675
final_01 = jason   final_02 = cm   final_03 = 2   final_04 = jason_nan
------------第二次创建对象------------
test.Person@1f33675
final_01 = jason   final_02 = zj   final_03 = 3   final_04 = jason_nan
------------修改引用对象--------------
final_01 = jason   final_02 = zj   final_03 = 3   final_04 = jasonnan

返回的两次

System.out.println(final1.final_04);
结果均是一样的。而且不管我怎么重复运行程序,结果还是这个地址。 这说明不管多少次创建实例对象(初始化),一旦一个对象引用被修饰为final后,它只能恒定指向一个对象,无法将其改变为另一个对象,也就是堆地址不变,而堆里面的内容可以改变。

一个既是static又是final的字段只占据一段不能改变的存储空间:

package test;

import java.util.Random;

public class FinalTest {
	    private final String final_01 = "jason";    //编译期常量,必须要进行初始化,且不可更改
	    private final String final_02;                //构造器常量,在实例化一个对象时被初始化
	    
	    private static Random random = new Random();
	    private static final  int final_03 = random.nextInt(10);    //使用随机数来进行初始化
	    //引用
	    public final Person final_04 = new Person("jason_nan");    //final指向引用数据类型
	    
	    FinalTest(String final_02){
	        this.final_02 = final_02;
	    }
   
	    
	 public String toString(){
	       return "final_01 = " + final_01 +"   final_02 = " + final_02 + "   final_03 = " + final_03 +
	             "   final_04 = " + final_04.getName();
	   }
	    
	    public static void main(String[] args) {
	    	
	        System.out.println("------------第一次创建对象------------");
	        FinalTest final1 = new FinalTest("cm");
	     	//System.out.println(final1.final_04);
	        System.out.println(final1);
	        System.out.println("------------第二次创建对象------------");
	        FinalTest final2 = new FinalTest("zj");
	      	//System.out.println(final1.final_04);
	        System.out.println(final2);
	        System.out.println("------------修改引用对象--------------");
	        final2.final_04.setName("jasonnan");
	        System.out.println(final2);
	    }
	
}
    class Person {
    private String name;

    Person(String name){
        this.name = name;
    }
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

这段代码我在只是修改了private static final  int final_03 = random.nextInt(10);

这样的结果为:

------------第一次创建对象------------
final_01 = jason   final_02 = cm   final_03 = 4   final_04 = jason_nan
------------第二次创建对象------------
final_01 = jason   final_02 = zj   final_03 = 4   final_04 = jason_nan
------------修改引用对象--------------
final_01 = jason   final_02 = zj   final_03 = 4   final_04 = jasonnan

这说明第一次初始化时也就是 FinalTest final1 = new FinalTest("cm");实例化时,如果只有 final 修饰 则final_03指向线程私有栈的字面值为4的空间,如果是static final组合修饰,则专门在内存中开劈一个空间给final_03来储存random.nextInt(10)的结果,在第二次初始化时(FinalTest final2 = new FinalTest("zj");),random.nextInt(10)的值可能是7,如果只有 final 修饰 则final_03指向线程私有栈的字面值为7的空间,(在栈中的指向与指向堆是不一样的,栈是一个一个对象塞进去,等要用的时候再从最上层开始一个一个取出来,这里需要知道栈是如何运作的),如果是static final组合修饰,则直接向已开辟的指定内存空间取出值,这个值在第一次赋值后就不会改变,这是针对基本数据类型的,但static final组合修饰时如果是在栈中的对象,不能改变的是对象的类名,但由于类的封装,就是高内聚,低耦合,内部的属性和方法是可以改变的,只是说外表还是这个人,但他可以慢慢跑,也可以快步跑。实例化后的对象也可以有各种不同的实际状态,每一个时间段也可以有不同的状态。

再来如下修改一下代码看看:

package test;

import java.util.Random;

public class FinalTest {
	    private final String final_01 = "jason";    //编译期常量,必须要进行初始化,且不可更改
	    private final String final_02;                //构造器常量,在实例化一个对象时被初始化
	    
	    private static Random random = new Random();
	    private static final  int final_03 = random.nextInt(10);    //使用随机数来进行初始化
	    //引用
	    public static final Person final_04 = new Person("jason_nan");    //final指向引用数据类型
	    
	    FinalTest(String final_02){
	        this.final_02 = final_02;
	    }
   
	    
	 public String toString(){
	       return "final_01 = " + final_01 +"   final_02 = " + final_02 + "   final_03 = " + final_03 +
	             "   final_04 = " + final_04.getName();
	   }
	    
	    public static void main(String[] args) {
	    	
	        System.out.println("------------第一次创建对象------------");
	        FinalTest final1 = new FinalTest("cm");
	     	//System.out.println(final1.final_04);
	        System.out.println(final1);
	        System.out.println("------------第二次创建对象------------");
	        FinalTest final2 = new FinalTest("zj");
	      	//System.out.println(final1.final_04);
	        System.out.println(final2);
	        System.out.println("------------修改引用对象--------------");
	        final2.final_04.setName("jasonnan");
	        System.out.println(final2);
	    }
	
}
    class Person {
    private String name;

    Person(String name){
        this.name = name;
    }
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

结果为:

------------第一次创建对象------------
final_01 = jason   final_02 = cm   final_03 = 9   final_04 = jason_nan
------------第二次创建对象------------
final_01 = jason   final_02 = zj   final_03 = 9   final_04 = jason_nan
------------修改引用对象--------------
final_01 = jason   final_02 = zj   final_03 = 9   final_04 = jasonnan


这里修改的是 public static final Person final_04 = new Person("jason_nan");

但结果与没有static一样,这是由于在堆中的对象可以有不同的状态,根据类的封装,就是高内聚,低耦合,内部的属性和方法是可以改变的,只是说外表还是这个人,但他可以慢慢跑,也可以快步跑。实例化后的对象也可以有各种不同的实际状态,每一个时间段也可以有不同的状态。而对于static final来说,第一次初始化后,引用变量的地址,对象的地址,对象均是没有变化的,而对于final来说,第一次初始化后,引用变量的地址,对象的地址,均没有变,但对象空间里的对象可以换成其他类。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值