JAVA学习记录 – final和static
1. final
在JAVA中,final关键字被使用在不同的场景中。一旦一个被final修饰的变量被定义,此变量所指向的值将保持不变;如果一个final变量指向一个对象,那么此变量将一直指向此对象1;如果一个final变量指向一个array,那么此array的内容是可以被改变的,而这个final变量的指向一直不变。
1.1 final classes
用final修饰的类是不能被继承2的,用final修饰的好处是安全提升,很多JAVA标准库就是使用final修饰,比如java.lang.System和java.lang.String。
示例如下:
public final class MyFinalClass {...}
public class ThisIsWrong extends MyFinalClass {...} // forbidden
1.2 final methods
当一个类的方法定位为final时,这个方法是不能被子类重载或者隐藏3,Oracle在线指导。
示例如下:
public class Base
{
public void m1() {...}
public final void m2() {...}
public static void m3() {...}
public static final void m4() {...}
}
public class Derived extends Base
{
public void m1() {...} // Ok, overriding Base#m1()
public void m2() {...} // forbidden
public static void m3() {...} // OK, hiding Base#m3()
public static void m4() {...} // forbidden
}
A common misconception is that declaring a class or method as final improves efficiency by allowing the compiler to directly insert the method wherever it is called (see inline expansion). But because the method is loaded at runtime, compilers are unable to do this. Only the runtime environment and JIT compiler know exactly which classes have been loaded, and so only they are able to make decisions about when to inline, whether or not the method is final.
从这段描述中可以看出将class or method声明为final并不会转换成inline而提升性能。
1.3 final variables
一个final变量只能被初始化一次。但是final变量不需要在声明时就一定要被初始化,这种没有在声明时初始化的final变量叫做blank final
变量。这种blank final变量的初始化必须在其类的构造函数中完成;类似的,一个blank final static变量必须在静态初始化器4中完成初始化。对于对象类型的变量,则变量指向不会被改变。
示例如下:
public class Sphere {
// pi is a universal constant, about as constant as anything can be.
public static final double PI = 3.141592653589793;
public final double radius;
public final double xPos;
public final double yPos;
public final double zPos;
Sphere(double x, double y, double z, double r) {
radius = r;
xPos = x;
yPos = y;
zPos = z;
}
[...]
}
final也可以在for循环中使用,这样就可以避免在循环里面对循环变量做出修改。如下例:
for (final SomeObject obj : someList) {
// do something with obj
}
1.4 final和inner classes
在WIKI上有如下内容5:
When an anonymous inner class is defined within the body of a method, all variables declared final in the scope of that method are accessible from within the inner class. For scalar values, once it has been assigned, the value of the final variable cannot change. For object values, the reference cannot change. This allows the Java compiler to “capture” the value of the variable at run-time and store a copy as a field in the inner class. Once the outer method has terminated and its stack frame has been removed, the original variable is gone but the inner class’s private copy persists in the class’s own memory.
示例代码如下:
import javax.swing.*;
public class FooGUI {
public static void main(String[] args) {
//initialize GUI components
final JFrame jf = new JFrame("Hello world!"); //allows jf to be accessed from inner class body
jf.add(new JButton("Click me"));
// pack and make visible on the Event-Dispatch Thread
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
jf.pack(); //this would be a compile-time error if jf were not final
jf.setLocationRelativeTo(null);
jf.setVisible(true);
}
});
}
}
此说法只在JDK1.7及以下试用,在JDK1.8中已经可以不使用final就能在inner class中进行访问。
2.static
static表示全局或者静态,可以用来修饰成员变量或者方法,同时还可以编写static代码块来优化程序性能。
2.1 static method
static方法一般叫做静态方法。静态方法不依赖于任何对象就可以进行访问,是没有this的,所以是不能直接访问类的非static成员变量和static方法,需通过对象来访问类的非static变量和非static方法。最常见的static方法就是main方法,因为在没有创建任何对象之前main已经开始执行。
2.2 static variables
static变量叫做静态变量。静态变量和非静态变量的区别是:静态变量是被所有的对象共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是被对象所拥有,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
2.3 static代码块
static{
和}
可以用来形成静态代码块,可以优化程序性能,同时可以用来初始化blank final static变量。这种static代码块可以放在类中的任何地方。在类初次加载时,会按照static代码块的顺序来执行每个static代码块。
示例代码:
public static void main(String[] args) {
for(final String a : b)
{
System.out.println(a);
}
}
private static final List<String> b = new ArrayList<>();
static {
b.add("1");
b.add("2");
b.add("3");
}
在JDK1.8可以如下表示上述示例代码:
public static void main(String[] args) {
Stream<String> stream = b.stream();
stream.forEach(System.out::println);
}
private static final List<String> b = new ArrayList<>();
static {
b.add("1");
b.add("2");
b.add("3");
}
- 一个final变量指向一个对象时,此对象的状态是可以被改变的。 ↩
- JAVA中,extends是继承,implements是对接口的实现。 ↩
- 覆盖和隐藏的区别涉及到RTTI(Run-Time Type Identification,通过运行时类型信息程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型),RTTI会在运行时自动确定该引用的真实类型,当子类覆盖了父类的方法时,会直接调用子类的方法,而静态的方法被子类重写的话就是隐藏,同时静态变量和成员变量也是隐藏,而RTTI只是识别覆盖,而不识别覆盖。 ↩
static{
和}
之间的代码就是静态初始化器。它只在第一次加载类时运行。只有静态变量才可以在静态初始化器中进行访问。 ↩- 百度了一下,很多博客上也是有类似的写法。 ↩