zoukankan      html  css  js  c++  java
  • final的深入理解

    先通过例子看一看:

     

    package com.sotaof.testfinal;
    
    public class Value {
    	int i;
    	public Value(int i){
    		this.i = i;
    	}
    }
    
    package com.sotaof.testfinal;
    
    import java.util.Random;
    
    public class FinalData {
    
    	private static Random rand = new Random(47);
    	private String id;
    	public FinalData(String id) {
    		this.id = id;
    	}
    	private final int valueOne = 9;
    	private static final int VALUE_TWO = 99;
    	public static final int VALUE_THREE = 39;
    	
    	private final int i4 = rand.nextInt(20);
    	static final int INT_5 = rand.nextInt(20);
    	private Value v1 = new Value(11);
    	private final Value v2 = new Value(22);
    	private static final Value VAL_3 = new Value(33);
    	
    	private final int[] a = {1,2,3,4,5,6};
    	public String toString() {
    		return id + ": " + "i4 = " + i4 + ", INT_5 = " + INT_5;
    	}
    	public static void main(String[] args) {
    		FinalData fd1 = new FinalData("fd1");
    		//fd1.valueOne++;   这句代码会报错,因为final修饰的基本类型数值是不可以变化的
    		fd1.v2.i++; //这句代码不会报错,因为final修饰的v2对象自身是可以变化的
    		fd1.v1 = new Value(9); //这个代码是不会报错的,就是创建一个对象,v1没有用final修饰
    		for(int i = 0; i < fd1.a.length; i++) {
    			fd1.a[i]++;  //这句代码也是不会报错的,因为数组也是对象,而final修饰的对象自身可以变化
    		}
    		//fd1.v2 = new Value(0); 这句代码会报错 ,因为v2是用final修饰的,final修饰的对象自身是可以变化的,
    		//但是final修饰的对象的引用是不可以变化的,所以再用v2创建对象,就说明v2的引用变化了,所以报错
    		//fd1.VAL_3 = new Value(1);和上边一样的道理
    		//fd1.a = new int[3];和上边一样的道理
    		//刚才那三段代码都是一个意思,就是说final修饰的对象,这个对象的引用是不可以变化的
    		System.out.println(fd1);
    		System.out.println("Creating new FinalData");
    		FinalData fd2 = new FinalData("fd2");
    		System.out.println(fd1);
    		System.out.println(fd2);
    	}
    
    }


    1、我们不能因为某数据是final的就认为在编译时可以知道它的值。在运行时使用随机生成的数值来初始化i4和INT_5就说明了这一点。

    2、这个实例也向我们展示了将final数值定义为静态和非静态的区别,这个区别只有当数值 在运行时内 被初始化时 才会显示(这句话不容易理解,我将其用" "分开了,以方便阅读理解,细细品读),这是因为编译器对编译时数值一视同仁,当运行程序时就会看到这个区别。

    3、在fd1对象1中i4的值是唯一的,在fd2对象中i4的值也是唯一的,但是fd1和fd2中的i4值就不一定相同了,因为如果只有final修饰的话,在每次创建对象时都会被初始化(网友解释:i4仅仅是final类型的,分别在fd1和fd2两个对象中各开辟一块空间);

    4、不管是在fd1中,还是在fd2中,INT_5的值都是一样的,是不可以通过创建第二个FinalData对象而加以改变的,这是因为它是static的,在装载的时候,就被初始化,而不是每次创建新对象时都初始化(网友解释:static final 仅使用一段不能改变的空间,所以INT_5值不变)。

    在这里为了方便大家更多的了解final的数据,这里附上一位网友的例子,觉得很不错(http://chihom.iteye.com/blog/233035)

    下面的两个初始化例子

     

    class InitalizedClass {
    	static {
    		System.out.println("You have initalized InitalizedClass!");
    	}
    	public static int inititalize_varible = 1;
    
    }
    
    public class TestInitializeClass {
    	public static void main(String[] args) {
    		System.out.println(InitalizedClass.inititalize_varible);
    
    	}
    
    }
    


    上面的结果是: 
    You have initalized nitalizedClass! 


    再看下面的例子 

     

    class InitalizedClass {
    	static {
    		System.out.println("You have initalized InitalizedClass!");
    	}
           //和上面的例子唯一的差异就是此处的变量INITIALIZED_VARIBLE被声明为final
    	public final static int INITIALIZED_VARIBLE = 1;
    
    }
    
    public class TestInitializeClass {
    	public static void main(String[] args) {
    		System.out.println(InitalizedClass.INITIALIZED_VARIBLE);
    
    	}
    
    }
    


    上面的结果是: 

    这里大家会问:为什么两个例子执行结果不一样?

    原因是第二个例子中的INITIALIZED_VARIBLE为编译期常量,它不会导致类的初始化,也就是说,我第一个例子中,"InitalizedClass.inititalize_varible"这句代码会进入InitalizedClass这个类中,并执行static那段儿代码;而第二个例子中,"InitalizedClass.INITIALIZED_VARIBLE"这句代码并不会进入InitalizedClass这个类中,那有人会说,不进入这个类中怎么可能调用到INITIALIZED_VARIBLE这个值呢?因为我这个程序在编译的时候就初始化了,内存中已经存在INITIALIZED_VARIBLE这个值了,所以不再需要调用InitalizedClass类,这样大家应该理解了吧。

    我也是今天才有了一点儿的理解,不过还是有一个疑问,希望各位网友能帮忙解决:

    private final int valueOne = 9;

    private final int i = rand.nextInt(20);

    这两个代码,分别是在什么时候初始化的,我的理解是,valueOne和 i 都是编译时常量,但valueOne在编译的时候就初始化了,编译的时候就知道valueOne的值了,而 i 则是在运行时进行初始化的,运行的时候才会知道它的值。

  • 相关阅读:
    【JavaP6大纲】SpringCould篇:什么是微服务
    【JavaP6大纲】SpringCould篇: Spring Boot 和 Spring Cloud,谈谈你对它们的理解?
    【JavaP6大纲】分布式会话篇:集群部署时的分布式 Session 如何实现?
    【JavaP6大纲】SpringCould篇:Spring Cloud 和 Dubbo 有哪些区别?
    【JavaP6大纲】SpringCould篇:服务发现组件 Eureka 的主要调用过程?Eureka 和 Zookeeper 都可以提供服务注册与发现的功能,它们有什么区别?
    【JavaP6大纲】SpringCould篇:熔断框架如何做技术选型?选用 Sentinel 还是 Hystrix?
    【JavaP6大纲】SpringCould篇:如何限流?在工作中是怎么做的?说一下具体的实现?
    【JavaP6大纲】SpringCould篇:常用组件底层实现
    【JavaP6大纲】MySQL篇:SQL的整个解析、执行过程原理、SQL行转列?
    如何将百度搜索嵌入到站点中!
  • 原文地址:https://www.cnblogs.com/aukle/p/3223792.html
Copyright © 2011-2022 走看看