问题:final修饰的变量能否在定义时不初始化?

问题:final修饰的变量能否在定义时不初始化?

首先,先看一下final可以修饰哪些东西:

final可以修饰类、变量、方法。

  • final修饰的类不能被继承
  • final修饰的方法不能被重写
  • final修饰的变量是一个常量,只能被赋值一次

修饰的类不可继承,所以不能修饰抽象类。

那么final是否可以修饰内部类呢?

package com.newre.demo2;

public class OuterClass {
	/**
	 * 用final修饰成员内部类
	 * @author newre
	 *
	 */
	final class InnerClass1 {
		{
			System.out.println(
                "InnerClass1:I can"
            );
		}
	}
	/**
	 * 用final修饰静态内部类
	 * @author newre
	 *
	 */
	static final class InnerClass2 {
		{
			System.out.println(
                "InnerClass2:I can"
            );
		}
	}
	public static void main(String[] args) {
		/**
		 * 用final修饰方法内部类
		 * @author newre
		 *
		 */
		final class InnerClass3 {
			{
				System.out.println(
                    "InnerClass3:I can"
                );
			}
		}
		new OuterClass().new InnerClass1();
		new InnerClass2();
		new InnerClass3();
	}
}
########################################
执行结果:
InnerClass1:I can
InnerClass2:I can
InnerClass3:I can

无论是成员内部类、静态内部类、还是方法内部类,我们可以看到三者均可以用final修饰。

让我们再看看final修饰的方法。

众所周知的是:普通方法是能被final修饰的,抽象方法不能被final修饰。

那么,静态方法是否能被final修饰?

静态方法可以存在于类中,也可以存在于方法中,所以分别试一下。

package com.newre.demo2;

public class TestClass {
	public final static void test() {
		System.out.println("Class FinalMethod:I can");
	}
	public static void main(String[] args) {
		TestClass.test();
	}
}
#######################################
执行结果:
Class FinalMethod:I can
package com.newre.demo2;

public interface TestInferface {
	/**
	 * 此处出现了编译时异常:
	 * Illegal modifier for the interface method test; 
	 * only public, abstract, default, static and strictfp are permitted
	 */
	 final static void test() {
		 System.out.println("Interface FinalMethod:I can't"); 
	 }
}

结论就是,类中的静态方法可以被final修饰,而接口中的静态方法不能被final修饰。

看到这,也许有人会问:

类中的静态方法可以被final修饰,可是……这有啥用?静态方法本身就是可继承,不可重写的呀?

的确是的,所以,这样写的作用似乎只剩下了一个:

一起使用时的主要目的是使代码规范化,便于以后阅读更改……

啰里啰唆说了一大堆,现在进入正题,final修饰的变量能否在定义时不被初始化?

答案是,可以。

通常情况,我们定义常量共有三种常见形式:

package com.newre.demo2;
public class FinalField {
	// final修饰实例变量并初始化
	private final String A = "1";
	// final修饰类变量并初始化
	private static final String B = "2";
	/**
	 * final修饰形参,此时不初始化,
	 * 但内部不能改变形参的值
	 * 
	 * @param A final修饰的形参
	 */
	public void test(final String A) {
		// 此句不成立,编译时异常:
		// The final local variable A cannot be assigned.
		// It must be blank and not using a compound assignment
		// A = "aaa";
	}
	public static void test2(final String A) {
	}
}

通常情况这三种足以应付大多数的问题,但是事实上,我们定义的实例变量与类变量,不一定非要在被定义时就初始化,这一点其实是很好用的,我们稍后再议,先进行代码测试。

在这里插入图片描述

此时,我们没有赋值,然后报错……

此时,不用心慌,因为代码还没写完。

package com.newre.demo2;

public class FinalField {
	// final修饰实例变量并初始化
	private final String A1;
	private final String A2;
	// final修饰类变量并初始化
	private static final String B1;
	private static final String B2;
	
	{
		A1 = "a";
		// 此处编译报错:
		// The final field FinalField.B2 cannot be assigned
		// 因为B2是final修饰的类变量
		// 又因为在实例化时才会触发代码块内容
		// 所以在代码块内部不能初始化静态常量
		// B2 = "b";
	}
	static {
		// 此处编译报错:
		// Cannot make a static reference to the non-static field A2
		// 有编程基础的朋友们应该明白,
		// 此处出错是因为在静态代码块中,
		// 不能有实例变量(包括实例常量)
		// A2 = "a";
		B1 = "b";
		B2 = "b";
	}
	public FinalField() {
		A2 = "a";
		// 此处仍旧编译报错,
		// 原因不多说,
		// 与代码块中报错原因相同
		// B2 = "b";
	}
	public FinalField(String A) {
		A2 = "";
	}
}

此时代码编译通过。

所以结论就是:

  • 实例常量可以在代码块或构造器中被初始化(如果是构造器中初始化,那么必须保证每个构造器都能够有实例常量的初始化步骤。
  • 类常量可以在静态代码块中被初始化,不能在代码块或构造器中被初始化。

这样设计的好处在哪儿呢?

好处之一就是,能体现配置的灵活性。

package com.newre.demo2;

import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

public class TestJDBC {
	private static final String DRIVER;
	private static final String URL;
	private static final String USERNAME;
	private static final String PASSWORD;
	
	static {
		Properties properties = new Properties();
		try {
			properties.load(TestJDBC.class.getClassLoader().getResourceAsStream("jdbc-config.properties"));
		} catch (IOException e) {
			e.printStackTrace();
		}
		DRIVER = properties.getProperty("DRIVER");
		URL = properties.getProperty("URL");
		USERNAME = properties.getProperty("USERNAME");
		PASSWORD = properties.getProperty("PASSWORD");			
	}
	
	public static Connection getConnection() throws SQLException, ClassNotFoundException {
		Class.forName(DRIVER);
		return DriverManager.getConnection(URL,USERNAME,PASSWORD);
	}
	public static void main(String[] args) {
		try {
			Connection connection = getConnection();
			System.out.println(connection);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}
jdbc-config.properties配置文件内容如下:

DRIVER=com.mysql.jdbc.Driver
URL=http://localhost:3306/test
USERNAME=root
PASSWORD=123456

由于新装的电脑上暂时没有安装MySQL,故而无法测试是否运行正确。

总之,编译能通过。

而这,就是final延迟一点初始化的一个小小的应用。

如果没有学过JDBC的朋友们也可以看下面的一个例子:

package com.newre.demo2;

public class SimpleFinalField {
	private final String A;
	private SimpleFinalField(String A) {
		this.A = A;
	}
	public String getA() {
		return A;
	}
	public static SimpleFinalField A() {
		return new SimpleFinalField("A");
	}
	public static SimpleFinalField B() {
		return new SimpleFinalField("B");
	}
	public static SimpleFinalField C() {
		return new SimpleFinalField("C");
	}
	public static void main(String[] args) {
		String a = SimpleFinalField.A().getA();
		String b = SimpleFinalField.B().getA();
		String c = SimpleFinalField.C().getA();
		System.out.println("a = " + a);
		System.out.println("b = " + b);
		System.out.println("c = " + c);
	}
}
##########################################
执行结果:
a = A
b = B
c = C

当然,这串代码的实用性不是很高,更多的应用场景需要由各位摸索下去。

最后,请记住:

final修饰的变量可以在定义时不被初始化!!


版权声明:本文为NewReErWen原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。