| final | 类 | 接口 | 方法 | 变量 | 形参 |
|---|---|---|---|---|---|
| 使用范围 | √ | × | √ | √ | √ |
final的作用范围介绍
在Java中,final可以修饰类、方法、变量(成员变量以及局部变量)、还有方法的形参。
修饰类
当用final修饰一个类,表示这个类不可以被其他类继承,也称之为:终态类。
final修饰在类上一般出于安全用于不需要被继承的类中,例如String类
修饰方法
使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。 –《Java编程思想》第四版
被final修饰后的方法的差异:
- 被final修饰后的方法,子类不能重写此方法
- 被final修饰后的方法,子类可以重载此方法
- 被final修饰后的private方法,子类可以声明相同的方法(同名、同参),与父类无关
注:类中被private修饰的方法会隐式指定为final方法
修饰变量
当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。
被final修饰的成员变量表示常量,只能被赋值一次,赋值后不能再改变。
final修饰成员变量时的初始方式
- 在变量声明时赋值
- 在构造方法中赋值
public class A {
//在变量声明时赋值
final String str = "Hello World";
public void test() {
//被final修饰的成员变量不能在方法中赋值
//str = "Other"; //Cannot assign a value to final variable "str"
}
}
public class A {
final String str;
public A() {
//在构造方法中赋值
str = "Hello World";
}
}
public class A {
final String str;
public A() {
str = "Hello World";
}
public A(String str2) {
//不能存在未赋值的构造函数,构造函数中必须为final常量修饰
str = "Hello World2"; //必须
}
}
修饰方法的形参
public class A {
void test(final int i) {
//i = 10; //当形参被final修饰时,表示此形参为只读的 只能使用,不能修改
System.out.println(i);
}
void invoke() {
int a = 10;
test(a); //形参被final修饰 实参可以传入普通变量
}
}
被final修饰的变量和普通变量的区别
public class A {
public static void main(String[] args) {
String a = "123";
String b = "12";
final String c = "12";
String x = b + "3";
String y = c + "3";
System.out.println(a == y); //true
System.out.println(a == x); //false
//由于变量c被final修饰,因此会被当做编译器常量,所以在使用到c的地方会直接将变量c替换为它的值。而对于变量b的访问却需要在运行时通过链接来进行。
}
}
只有在编译期间能确切知道final变量值的情况下,编译器才会进行这样的优化。
public class A {
public static void main(String[] args) {
String a = "123";
String b = "12";
final String c = getC();
String x = b + "3";
String y = c + "3";
System.out.println(a == y); //false
System.out.println(a == x); //false
//不要以为某些数据是final就可以在编译期知道其值,通过变量c我们就知道了,在这里是使用getC()方法对其进行初始化,他要在运行期才能知道其值。
}
static String getC() {
return "12";
}
}
example:
public class A {
public static void main(String[] args) {
StringBuilder str = new StringBuilder("hello");
test(str);
str.append("xixi");
System.out.println(str.toString());
}
static void test(final StringBuilder str) {
//str = new StringBuilder("123"); //final修饰引用类型的参数,不能再让其指向其他对象,但是对其所指向的内容是可以更改的。
str.append("456");
System.out.println(str.toString());
}
}
结果:
hello456
hello456xixi
example2:
public class A {
public static void main(String[] args) {
StringBuilder str = new StringBuilder("hello");
test(str);
System.out.println(str.toString());
}
static void test(StringBuilder str) {
str = new StringBuilder("123");
str.append("456");
System.out.println(str.toString());
}
}
结果:
123456
hello
java采用的是值传递,对于引用变量,传递的是引用的值,也就是说让实参和形参同时指向了同一个对象,因此让形参重新指向另一个对象对实参并没有任何影响。