1. final关键字1
final关键字可以用来修饰字段、方法或者类。
1.1 修饰字段
当final关键字用来修饰字段时,就表示这个字段是不可修改的。这也表明:被final关键字修饰的字段必须要在构造对象时初始化。
如果只是简单地构造一个类而不对被final关键字修饰的字段进行初始化,那么会报错:Variable might not have been initialized.
可以通过直接给该字段赋初始值或者在构造器中初始化它来解决这个问题。
例如:
class Test{
private final String name;
Test(String aName) {
name = aName;
}
}
或者:
class Test{
private final String name = "abc";
}
final可以用来修饰有些一旦设置就不会去修改的字段,这样可以保证它们确实不会被修改。
final这样的特性应该被用在基本类型和不可变类(例如String)中,对于可变的类,这样会造成一些令人困扰的地方:
class Test{
private final String NAME = "abc";
private final StringBuilder stringBuilder;
Test(StringBuilder stringBuilder) {
this.stringBuilder = stringBuilder;
}
public void setBuilder(){
stringBuilder.append("");//这样是正确的
//NAME = "";这样会报错
//stringBuilder = new StringBuilder();这样也会报错
}
}
final关键字只是阻止了这个变量的对象引用不能再指向另一个不同的对象,并没有阻止对原来这个对象本身的状态进行修改,因此,依然可以对这个stringBuilder执行append操作。
而对于String这样的不可变类,没有方法来修改String对象,就不会造成这样的困扰。
1.2 修饰方法
可以将特定的方法修饰成final,这样可以使得子类不能重写这个方法:
class Test2{
public final void print(String s){
System.out.println(s);
}
//可以进行重载
public final void print(){
System.out.println();
}
}
class Child extends Test2{
//可以重载
public void print(int a){
}
//!!以下部分会报错:'print()' cannot override 'print()' in 'Test2'; overridden method is final
//不能重写父类中的final方法
public void print(){
}
}
1.3 修饰类
如果想要更进一步,不想让整个类被继承,可以对类使用final修饰符,一旦将类修饰为final,里面的所有方法就自动被声明成final,但字段不会。
String类就是final类,任何人都不能定义其子类。
2. static关键字
static关键字可以用来修饰字段和方法,也可以和final关键字进行配合,定义一个静态常量。
2.1 静态字段
对于静态字段,每个类在内存中只有一个这样的字段,而非静态字段,每个对象都有一个这个字段的副本。
class Student{
private static int step = 1;
private int id;
}
对于这样一个student类,每个student对象都有一个属于自己的id字段,而共享一个step字段,也就意味着一旦将其更改,每个student类的对象都共享这个更改。
可以把静态字段理解成“类字段”,即被static修饰的字段属于类,而不属于某一个对象,即使没有为student类创建任何对象,step也存在。
2.2 静态常量
static和final一起使用,就可以定义一个静态常量。
例如,在Math类中有一个常量PI=3.14159265358979323846,就被声明为public static final这样的形式。由于static,不需要创建Math类的对象就可以直接使用它:Math.PI;由于final,不能对它进行修改,因此就可以安全地声明成public而不担心被修改。
System.out也是一个静态常量,在源码中声明如下:
2.3 静态方法
可以将一个方法声明成静态的,这样可以不需要通过对象来调用这个方法。例如常用的Math.pow方法,不需要构造一个Math类的对象,直接通过类名来调用。
什么时候可以声明一个静态方法?
当这个方法不需要隐式参数的时候。
由于静态方法不需要对象来调用,也就意味着它不需要也不能使用与对象有关的部分(例如上面student类中的id实例字段,这样的字段与对象有关)。
class Student{
private static int step = 1;
private int id;
static void print(){
//下面的代码是错误的,不能使用实例字段id
//Non-static field 'id' cannot be referenced from a static context
System.out.println(id);
//但是可以使用静态字段,因为静态字段与对象无关:
System.out.println(step);
}
}
所以,当方法不需要隐式参数(即不需要访问对象的状态,只需显式参数就可以完成功能;或者只需要访问静态字段时)可以将其声明为static。
文章内容参考自《Java核心技术》,网络以及自己的思考,主要作为自己整理的笔记使用。理解上表达上可能存在错误,欢迎指正。 ↩︎