在Java中,final关键字可以用来修饰数据、方法、参数、类,下面我们来了解final的关键字的用法。
基本用法
final 数据
对于基本类型,final使数值恒定不变;而对用对象引用,final使引用恒定不变。
final修饰的基本类型,一旦被初始化后,不能再被赋值。
final修饰的对象引用,一旦引用被初始化指向一个对象,就无法再把它改为指向另外一个对象。
final 类
当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。
final 方法
“使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。
因此,如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。
注:类的private方法会隐式地被指定为final方法。
final 参数
很多人都说在JAVA中用final来修饰方法参数的原因是防止方法参数在调用时被篡改,其实也就是这个原因,但理解起来可能会有歧义,我们需要注意的是,在final修饰的方法参数中,如果修饰的是基本类型,那么在这个方法的内部,基本类型的值是不能够改变的,但是如果修饰的是引用类型的变量,那么就需要注意了,引用类型变量所指的引用是不能够改变的,但是引用类型变量的值是可以改变的。
如下面的代码:
- package com.jd.test;
- public class Test{
- public static void main(String hh[]){
- int i = 1;
- System.out.println(i);
- checkInt(i);
- System.out.println(i);
- }
- /**
- * 对于基本类型,基本类型的值在方法内部是不能够改变的
- * @param i
- */
- public static void checkInt(final int i)
- {
- //编译不通过,final修饰的局部变量i的值是不能够改变的
- // i=10;
- }
- }
对于引用类型的变量,请看下面的代码:
- package com.jd.test;
- import com.jd.domain.User;
- /**
- *
- * final修饰参数的测试例子
- *
- * @author zhanghao10@jd.com
- * @since 2015-10-8
- */
- public class Test{
- public static void main(String hh[]){
- User user=new User();
- user.setId(1);
- user.setUserName("校长");
- user.setPassword("123456");
- checkInt(user);
- }
- /**
- * 对于基本类型,基本类型的值在方法内部是不能够改变的
- * @param i
- */
- public static void checkInt(final User user)
- {
- //user变量的引用是不能够改变的,否则的话,编译会报错
- // user=new User();
- //user变量的值是能够修改的,但所指向的引用是不能够改变的
- user.setUserName("小王");
- }
- }
空final
- class Reapeat {
- private final int a = 10;
- private final int b;
-
- public Reapeat(int b) {
- this.b = b;
- }
- }
- 所谓的”空白final”是指被声明的为final但又为给定初值的对象引用或者基本数据。无论在什么情况下,编译器都会去确保final在使用前必须被初始化。若不进行初始化,会提示错误,如下:
-
- 意味着,必须在域的定义处或者每个构造器中使用表达式对final进行赋值,这正是final域在使用前被初始化的原因所在。
final和static
- public class FinalData {
- private static Random random = new Random(47);
- public final int v_1 = random.nextInt(20);
- public final static int v_2 = random.nextInt(20);
- }
- public class TestFinal {
- public static void main(String[] args) {
- FinalData fd_1 = new FinalData();
- FinalData fd_2 = new FinalData();
- System.out.println("v_1 = " + fd_1.v_1 + " | v_2 = " + FinalData.v_2);
- System.out.println("v_1 = " + fd_2.v_1 + " | v_2 = " + FinalData.v_2);
- }
- }
- // Log
- v_1 = 15 | v_2 = 18
- v_1 = 13 | v_2 = 18
从上述代码可以看出,final将数值定义为静态和非静态的区别。在fd_1和fd_2中,v_1的值都是唯一的,而v_2的值是一致的,并没有因为对象的创建而加以改变,因为其被static修饰,意味着在装载时已被初始化,而不是每次创建新对象时都初始化。
final和对象引用
我们已经了解到,如果使用final修饰了引用对象,引用对象被初始化后,不能再被指向另外一个对象,但是其内部的内容是否可以修改?
- final Person person = new Person("li", 20);
- person.setName("wang");
- person.setAge(40);
- System.out.println(person.toString());
- // Log打印
- Person{name='wang', age=40}
从上述代码,person对象被final修饰,同时初始化为name = “li”,age=”20”。然后调用其相应setXX()方法修改其值。从Log打印看出,person对象自身的成员值为修改后的值。意味着被final修饰的对象引用,只是对象的应用不能修改,但是其自身却是可以修改的。
final和private
类中所有的private方法都隐式的指定为final的,由于无法取用private方法,所以也就无法覆盖它,可以对private方法添加final修饰符,但并没有添加任何额外意义。
- private final void doSwim() {
- }
- “覆盖”即重写只有在某方法是基类的接口的一部分时才会出现,即必须将一个对象向上转型为它的基本类型并调用相同的方法。如果某方法为private,它就不是基类接口的一部分,只不过是具有相同名称的方法而已。
- final在spring源码中的应用
- AbstractBeanFactory.java doGetBean
- /**
- * Return an instance, which may be shared or independent, of the specified bean.
- * @param name the name of the bean to retrieve
- * @param requiredType the required type of the bean to retrieve
- * @param args arguments to use if creating a prototype using explicit arguments to a
- * static factory method. It is invalid to use a non-null args value in any other case.
- * @param typeCheckOnly whether the instance is obtained for a type check,
- * not for actual use
- * @return an instance of the bean
- * @throws BeansException if the bean could not be created
- */
- @SuppressWarnings("unchecked")
- protected <T> T doGetBean(
- final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
- throws BeansException {
- final String beanName = transformedBeanName(name);
- Object bean;
- // Eagerly check singleton cache for manually registered singletons.
- Object sharedInstance = getSingleton(beanName);
- if (sharedInstance != null && args == null) {
- if (logger.isDebugEnabled()) {
- if (isSingletonCurrentlyInCreation(beanName)) {
- logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
- "' that is not fully initialized yet - a consequence of a circular reference");
- }
- else {
- logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
- }
- }
- bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
- }
- else {
- // Fail if we're already creating this bean instance:
- // We're assumably within a circular reference.
- if (isPrototypeCurrentlyInCreation(beanName)) {
- throw new BeanCurrentlyInCreationException(beanName);
- }
- // Check if bean definition exists in this factory.
- BeanFactory parentBeanFactory = getParentBeanFactory();
- if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
- // Not found -> check parent.
- String nameToLookup = originalBeanName(name);
- if (args != null) {
- // Delegation to parent with explicit args.
- return (T) parentBeanFactory.getBean(nameToLookup, args);
- }
- else {
- // No args -> delegate to standard getBean method.
- return parentBeanFactory.getBean(nameToLookup, requiredType);
- }
- }
- if (!typeCheckOnly) {
- markBeanAsCreated(beanName);
- }
- try {
- final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
- checkMergedBeanDefinition(mbd, beanName, args);
- // Guarantee initialization of beans that the current bean depends on.
- String[] dependsOn = mbd.getDependsOn();
- if (dependsOn != null) {
- for (String dependsOnBean : dependsOn) {
- getBean(dependsOnBean);
- registerDependentBean(dependsOnBean, beanName);
- }
- }
- // Create bean instance.
- if (mbd.isSingleton()) {
- sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
- public Object getObject() throws BeansException {
- try {
- return createBean(beanName, mbd, args);
- }
- catch (BeansException ex) {
- // Explicitly remove instance from singleton cache: It might have been put there
- // eagerly by the creation process, to allow for circular reference resolution.
- // Also remove any beans that received a temporary reference to the bean.
- destroySingleton(beanName);
- throw ex;
- }
- }
- });
- bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
- }
- else if (mbd.isPrototype()) {
- // It's a prototype -> create a new instance.
- Object prototypeInstance = null;
- try {
- beforePrototypeCreation(beanName);
- prototypeInstance = createBean(beanName, mbd, args);
- }
- finally {
- afterPrototypeCreation(beanName);
- }
- bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
- }
- else {
- String scopeName = mbd.getScope();
- final Scope scope = this.scopes.get(scopeName);
- if (scope == null) {
- throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
- }
- try {
- Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
- public Object getObject() throws BeansException {
- beforePrototypeCreation(beanName);
- try {
- return createBean(beanName, mbd, args);
- }
- finally {
- afterPrototypeCreation(beanName);
- }
- }
- });
- bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
- }
- catch (IllegalStateException ex) {
- throw new BeanCreationException(beanName,
- "Scope '" + scopeName + "' is not active for the current thread; " +
- "consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
- ex);
- }
- }
- }
- catch (BeansException ex) {
- cleanupAfterBeanCreationFailure(beanName);
- throw ex;
- }
- }
- // Check if required type matches the type of the actual bean instance.
- if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
- try {
- return getTypeConverter().convertIfNecessary(bean, requiredType);
- }
- catch (TypeMismatchException ex) {
- if (logger.isDebugEnabled()) {
- logger.debug("Failed to convert bean '" + name + "' to required type [" +
- ClassUtils.getQualifiedName(requiredType) + "]", ex);
- }
- throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
- }
- }
- return (T) bean;
- }