深入理解final关键字

在Java中,final关键字可以用来修饰数据、方法、参数、类,下面我们来了解final的关键字的用法。

基本用法

final 数据

对于基本类型,final使数值恒定不变;而对用对象引用,final使引用恒定不变。
 这里写图片描述
 final修饰的基本类型,一旦被初始化后,不能再被赋值。 
这里写图片描述

final修饰的对象引用,一旦引用被初始化指向一个对象,就无法再把它改为指向另外一个对象。

final 类

当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。 
 这里写图片描述

final 方法

“使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。
 因此,如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。

注:类的private方法会隐式地被指定为final方法。
 

final 参数

很多人都说在JAVA中用final来修饰方法参数的原因是防止方法参数在调用时被篡改,其实也就是这个原因,但理解起来可能会有歧义,我们需要注意的是,在final修饰的方法参数中,如果修饰的是基本类型,那么在这个方法的内部,基本类型的值是不能够改变的,但是如果修饰的是引用类型的变量,那么就需要注意了,引用类型变量所指的引用是不能够改变的,但是引用类型变量的值是可以改变的。

  如下面的代码: 

  1. package com.jd.test;  
  2.   
  3.   
  4. public class Test{  
  5.       public static void main(String hh[]){  
  6.       int i = 1;  
  7.       System.out.println(i);  
  8.       checkInt(i);  
  9.       System.out.println(i);  
  10. }  
  11.   
  12. /** 
  13.  * 对于基本类型,基本类型的值在方法内部是不能够改变的 
  14.  * @param i 
  15.  */  
  16. public static void checkInt(final int i)  
  17. {  
  18.   
  19.        //编译不通过,final修饰的局部变量i的值是不能够改变的         
  20.         //    i=10;  
  21.   
  22.     }  
  23. }  

 对于引用类型的变量,请看下面的代码:

  1. package com.jd.test;  
  2.   
  3. import com.jd.domain.User;  
  4.   
  5. /** 
  6.  *  
  7.  * final修饰参数的测试例子 
  8.  *  
  9.  * @author  zhanghao10@jd.com 
  10.  * @since    2015-10-8 
  11.  */  
  12. public class Test{  
  13.       public static void main(String hh[]){  
  14.       User user=new User();  
  15.       user.setId(1);  
  16.       user.setUserName("校长");  
  17.       user.setPassword("123456");  
  18.       checkInt(user);  
  19. }  
  20.   
  21. /** 
  22.  * 对于基本类型,基本类型的值在方法内部是不能够改变的 
  23.  * @param i 
  24.  */  
  25. public static void checkInt(final User user)  
  26. {  
  27.       //user变量的引用是不能够改变的,否则的话,编译会报错  
  28.       //    user=new User();  
  29.       //user变量的值是能够修改的,但所指向的引用是不能够改变的  
  30.       user.setUserName("小王");  
  31.   
  32.     }  
  33.  

空final
  1. class Reapeat {  
  2.       private final int a = 10;  
  3.       private final int b;  
  4.   
  5.       public Reapeat(int b) {  
  6.           this.b = b;  
  7.       }  
  8.   } 
  9.  所谓的”空白final”是指被声明的为final但又为给定初值的对象引用或者基本数据。无论在什么情况下,编译器都会去确保final在使用前必须被初始化。若不进行初始化,会提示错误,如下:  
  10. 这里写图片描述 
  11. 意味着,必须在域的定义处或者每个构造器中使用表达式对final进行赋值,这正是final域在使用前被初始化的原因所在。 

final和static

  1. public class FinalData {  
  2.     private static Random random = new Random(47);  
  3.   
  4.     public final int v_1 = random.nextInt(20);  
  5.     public final static int v_2 = random.nextInt(20);  
  6. }  
  7.   
  8. public class TestFinal {  
  9.   
  10.     public static void main(String[] args) {  
  11.   
  12.         FinalData fd_1 = new FinalData();  
  13.         FinalData fd_2 = new FinalData();  
  14.   
  15.         System.out.println("v_1 = " + fd_1.v_1 + " | v_2 = " + FinalData.v_2);  
  16.         System.out.println("v_1 = " + fd_2.v_1 + " | v_2 = " + FinalData.v_2);  
  17.     }  
  18. }  
  19.   
  20. // Log  
  21. v_1 = 15 | v_2 = 18  
  22. v_1 = 13 | v_2 = 18 
    从上述代码可以看出,final将数值定义为静态和非静态的区别。在fd_1和fd_2中,v_1的值都是唯一的,而v_2的值是一致的,并没有因为对象的创建而加以改变,因为其被static修饰,意味着在装载时已被初始化,而不是每次创建新对象时都初始化。
     

    final和对象引用

    我们已经了解到,如果使用final修饰了引用对象,引用对象被初始化后,不能再被指向另外一个对象,但是其内部的内容是否可以修改?
     

  23. final Person person = new Person("li"20);  
  24. person.setName("wang");  
  25. person.setAge(40);  
  26. System.out.println(person.toString());  
  27.   
  28. // Log打印  
  29. Person{name='wang', age=40
    从上述代码,person对象被final修饰,同时初始化为name = “li”,age=”20”。然后调用其相应setXX()方法修改其值。从Log打印看出,person对象自身的成员值为修改后的值。意味着被final修饰的对象引用,只是对象的应用不能修改,但是其自身却是可以修改的。

    final和private

    类中所有的private方法都隐式的指定为final的,由于无法取用private方法,所以也就无法覆盖它,可以对private方法添加final修饰符,但并没有添加任何额外意义。

  30. private final void doSwim() {  
  31.   
  32. }  
  33.  “覆盖”即重写只有在某方法是基类的接口的一部分时才会出现,即必须将一个对象向上转型为它的基本类型并调用相同的方法。如果某方法为private,它就不是基类接口的一部分,只不过是具有相同名称的方法而已。 
  34. final在spring源码中的应用
  35.  AbstractBeanFactory.java doGetBean
  36. /** 
  37.      * Return an instance, which may be shared or independent, of the specified bean. 
  38.      * @param name the name of the bean to retrieve 
  39.      * @param requiredType the required type of the bean to retrieve 
  40.      * @param args arguments to use if creating a prototype using explicit arguments to a 
  41.      * static factory method. It is invalid to use a non-null args value in any other case. 
  42.      * @param typeCheckOnly whether the instance is obtained for a type check, 
  43.      * not for actual use 
  44.      * @return an instance of the bean 
  45.      * @throws BeansException if the bean could not be created 
  46.      */  
  47.     @SuppressWarnings("unchecked")  
  48.     protected <T> T doGetBean(  
  49.             final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)  
  50.             throws BeansException {  
  51.   
  52.         final String beanName = transformedBeanName(name);  
  53.         Object bean;  
  54.   
  55.         // Eagerly check singleton cache for manually registered singletons.  
  56.         Object sharedInstance = getSingleton(beanName);  
  57.         if (sharedInstance != null && args == null) {  
  58.             if (logger.isDebugEnabled()) {  
  59.                 if (isSingletonCurrentlyInCreation(beanName)) {  
  60.                     logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +  
  61.                             "' that is not fully initialized yet - a consequence of a circular reference");  
  62.                 }  
  63.                 else {  
  64.                     logger.debug("Returning cached instance of singleton bean '" + beanName + "'");  
  65.                 }  
  66.             }  
  67.             bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);  
  68.         }  
  69.   
  70.         else {  
  71.             // Fail if we're already creating this bean instance:  
  72.             // We're assumably within a circular reference.  
  73.             if (isPrototypeCurrentlyInCreation(beanName)) {  
  74.                 throw new BeanCurrentlyInCreationException(beanName);  
  75.             }  
  76.   
  77.             // Check if bean definition exists in this factory.  
  78.             BeanFactory parentBeanFactory = getParentBeanFactory();  
  79.             if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {  
  80.                 // Not found -> check parent.  
  81.                 String nameToLookup = originalBeanName(name);  
  82.                 if (args != null) {  
  83.                     // Delegation to parent with explicit args.  
  84.                     return (T) parentBeanFactory.getBean(nameToLookup, args);  
  85.                 }  
  86.                 else {  
  87.                     // No args -> delegate to standard getBean method.  
  88.                     return parentBeanFactory.getBean(nameToLookup, requiredType);  
  89.                 }  
  90.             }  
  91.   
  92.             if (!typeCheckOnly) {  
  93.                 markBeanAsCreated(beanName);  
  94.             }  
  95.   
  96.             try {  
  97.                 final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);  
  98.                 checkMergedBeanDefinition(mbd, beanName, args);  
  99.   
  100.                 // Guarantee initialization of beans that the current bean depends on.  
  101.                 String[] dependsOn = mbd.getDependsOn();  
  102.                 if (dependsOn != null) {  
  103.                     for (String dependsOnBean : dependsOn) {  
  104.                         getBean(dependsOnBean);  
  105.                         registerDependentBean(dependsOnBean, beanName);  
  106.                     }  
  107.                 }  
  108.   
  109.                 // Create bean instance.  
  110.                 if (mbd.isSingleton()) {  
  111.                     sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {  
  112.                         public Object getObject() throws BeansException {  
  113.                             try {  
  114.                                 return createBean(beanName, mbd, args);  
  115.                             }  
  116.                             catch (BeansException ex) {  
  117.                                 // Explicitly remove instance from singleton cache: It might have been put there  
  118.                                 // eagerly by the creation process, to allow for circular reference resolution.  
  119.                                 // Also remove any beans that received a temporary reference to the bean.  
  120.                                 destroySingleton(beanName);  
  121.                                 throw ex;  
  122.                             }  
  123.                         }  
  124.                     });  
  125.                     bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);  
  126.                 }  
  127.   
  128.                 else if (mbd.isPrototype()) {  
  129.                     // It's a prototype -> create a new instance.  
  130.                     Object prototypeInstance = null;  
  131.                     try {  
  132.                         beforePrototypeCreation(beanName);  
  133.                         prototypeInstance = createBean(beanName, mbd, args);  
  134.                     }  
  135.                     finally {  
  136.                         afterPrototypeCreation(beanName);  
  137.                     }  
  138.                     bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);  
  139.                 }  
  140.   
  141.                 else {  
  142.                     String scopeName = mbd.getScope();  
  143.                     final Scope scope = this.scopes.get(scopeName);  
  144.                     if (scope == null) {  
  145.                         throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");  
  146.                     }  
  147.                     try {  
  148.                         Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {  
  149.                             public Object getObject() throws BeansException {  
  150.                                 beforePrototypeCreation(beanName);  
  151.                                 try {  
  152.                                     return createBean(beanName, mbd, args);  
  153.                                 }  
  154.                                 finally {  
  155.                                     afterPrototypeCreation(beanName);  
  156.                                 }  
  157.                             }  
  158.                         });  
  159.                         bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);  
  160.                     }  
  161.                     catch (IllegalStateException ex) {  
  162.                         throw new BeanCreationException(beanName,  
  163.                                 "Scope '" + scopeName + "' is not active for the current thread; " +  
  164.                                 "consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",  
  165.                                 ex);  
  166.                     }  
  167.                 }  
  168.             }  
  169.             catch (BeansException ex) {  
  170.                 cleanupAfterBeanCreationFailure(beanName);  
  171.                 throw ex;  
  172.             }  
  173.         }  
  174.   
  175.         // Check if required type matches the type of the actual bean instance.  
  176.         if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {  
  177.             try {  
  178.                 return getTypeConverter().convertIfNecessary(bean, requiredType);  
  179.             }  
  180.             catch (TypeMismatchException ex) {  
  181.                 if (logger.isDebugEnabled()) {  
  182.                     logger.debug("Failed to convert bean '" + name + "' to required type [" +  
  183.                             ClassUtils.getQualifiedName(requiredType) + "]", ex);  
  184.                 }  
  185.                 throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());  
  186.             }  
  187.         }  
  188.         return (T) bean;  
  189.     }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值