关于java中final关键字的总结
final是java中一个非常重要的关键字,final关键字在java中可以用于类、方法、变量,java中的String类、System类、StringBuffer类都不可被继承,是final的典型例子。下面就来总结我所学过的final的用法。
1. 成员变量:
简述: 就是在类里面定义的变量,成员变量随着类的初始化而初始话或者对象的初始化而初始化。成员变量中包含类变量和实例变量。对于类变量,按我个人理解,就是可以通过类直接调用的变量,随着类的加载而加载,因此是静态static的。而实例变量,就是在类中定义的非静态变量。执行普通初始化块、构造器时候可以对实例变量进行赋初值。还有一点值得注意的是:final修饰的成员变量要程序员显示赋初值。
①. final对类变量的初始化:两种方法:显式初始化,代码块初始化。
class Student{
//实例变量
String name;
int age;
//类变量:显式初始化
final static int id = 10;
//类变量:代码块初始化
final static int level;
static{
//代码块初始化,注意:代码块也要是静态的
//static修饰的变量随着类的加载而加载,早于方法,对象
level = 4;
}
}
②. final对实例变量的初始化:代码块赋值,构造器赋值,和非静态代码块赋值。
其实很好理解为什么是非静态代码块,因为如果是静态代码块,在类加载的时候就已经赋值,但是此时的实例变量需要创建对象才开始出现,所以相当于静态代码块在对一个不存在的变量赋值。
class Student{
//显式初始化
final int age = 10;
//构造器初始化:
final int level;
final String name;
public Student(int level, String name) {
this.level = level;
this.name = name;
}
//非静态代码块初始化
final int score;
{
score = 100;
}
}
③. final对变量赋值的一些注意点:
2.1:如果在定义final变量的时候已经赋初值,那么就不可以重新在代码块中和在构造器中重新赋值了。相当于对一个常量赋值,是不可以的。
2.2:如果一开始就打算在代码块中或者构造器中对实例变量赋值,那么就不要显式初始化。对于类变量也一样。
2.3:不能在方法中对final变量进行赋值。我的理解就是final的变量无论是类变量还是实例变量,都要在属性形成后才赋值。而方法需要被调用生效开始赋值,考虑到方法不一定会被调用,也有可能导致final的变量得不到初值。当然这是个人理解了。
2. 局部变量:
简述:局部变量就是方法中定义的变量,包括形参以及代码块中定义的变量
public class finalTest {
public static void main(String[] args) {
{
//对代码块中的变量赋值
final int b = 10;
}
}
}
class Student {
int age;
public void test(final int a){
//java不能对形参赋值,因为传进参数那一刻就已经赋值了,这样做是修改了。
//a = 10; 报错
final String name = "Mike";
//已经赋值了的不能再改变
//name = "john";
}
}
3. final修饰基本数据类型和引用数据类型的不同:
说明:对于基本数据类型,final修饰变量之后,就变成了一个常量,值是不可改变的。而对于引用数据类型,用final修饰过后,只能保证该引用数据类型所引用的地址不会变,就是说,执行的地址不会改变,但是属性还是可以变的。类似于数组、对象。而对于String本身就是final的,字符串也是存放在常量池中,本身就是不可变。
下面就来演示对引用数据类型的修改
public class finalTest {
public static void main(String[] args) {
//1. 对数组
final int[] arr = new int[]{
1,2,3,7,8,9,4,2,7};
System.out.println(Arrays.toString(arr));//[1, 2, 3, 7, 8, 9, 4, 2, 7]
arr[0] = 10;
arr[1] = 9;
arr[2] = 8;
arr[3] = 7;
System.out.println(Arrays.toString(arr));//[10, 9, 8, 7, 8, 9, 4, 2, 7]
//可以看到就是说,final过后还可以进行修改。
//2. 对对象
//①
final Student s = new Student(18, 101, "John");
System.out.println(s);//Student{age=18, number=101, name='John'}
s.age = 10;
s.number = 102;
s.name = "Mary";
System.out.println(s);//Student{age=10, number=102, name='Mary'}
//②.
Student s1 = new Student(22, 103, "Mike");
s1 = null;
System.out.println(s1);//null,可以修改为null
final Student s2 = new Student(22, 103, "Mike");
s2 = null;//加入final之后再修改会报错,因为地址是不可以改的
}
}
class Student {
int age;
int number;
String name;
public Student(int age, int number, String name) {
this.age = age;
this.number = number;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", number=" + number +
", name='" + name + '\'' +
'}';
}
}
final用来修饰一个方法,表明此方法不可以被重写,比如Object中的getClass()方法。如果在一个程序中有些方法不想要被重写,就可以使用final。
但是值得注意的是,对于final修饰的方法,如果是私有的方法,对于java而言,子类本身就是不可以重写的,如此一来,即使是父类中用private修饰的方法,子类也会因为访问权限不够而无法实现重写。所以这时候子类可以定义和父类同名同参的方法,因为这里不是重写的关系,二者没有关联。
class People{
private final void eat(){
System.out.println("吃饭");
}
}
class Student extends People{
public void eat(){
//这里的eat和上面的eat是没有关系的
System.out.println("学生应该好好吃饭");
}
}
当然,对于java而言,重载也是没有问题的
class People{
public final void eat(){
System.out.println("吃饭");
}
public void eat(String food){
System.out.println("吃饭" + food);
}
public void eat(String food, String food2){
System.out.println("吃饭" + food + food2);
}
}
对于用final修饰的类,意思就是不可继承了,所以这也就是为什么不把final和abstract放在一起了。
①. final + static : static final
修饰变量:即是静态的又是final的,表明该属性是全局常量
修饰方法:表明该方法是静态的,而且在整个程序中只有一个.
②. final 和 abstract不能出现在一个方法中,abstract的方法本身就是抽象的,要被子类继承重写,而final方法是不可被继承的,二者本身就矛盾了。