final和static的理解

488 阅读4分钟

1. final

1)被final关键字修饰的变量不能改变 (变量的引用不能改变,变量的内容是可以改变的)

2)被final关键字修饰的方法不能被重写

3)被final关键字修饰的类不能被继承

1.1 final变量

1.当final修饰基本数据类型变量,变量值不可改变

2.当修饰引用数据类型变量,变量的引用不可以被改变,但是变量本身指向的对象或者数组的值可以改变

package org.yunqing.myDemo;

public class FinalTest {
    public static void main(String[] args) {
        //基本数据类型
        final int i = 1;
        i = 2;//直接报错The final local variable i cannot be assigned. 
        //It must be blank and not using a compound assignment
        
        //引用类型,引用test类
        final Test t = new Test("66");
        System.out.println(t.getStr());
        t.setStr("77");//重新setStr的值,没问题,说明变量本身指向的对象的值可以改变
        System.out.println(t.getStr());
        t = new Test("99");//试着重新new一个test给t对象,
        //报错The final local variable i cannot be assigned. 
        //说明变量的引用不可以改变
    }
}
class Test{
    private String str;
    
    public test(String str) {
        this.str = str;
    }
    public String getStr() {
        return str;
    }
    
    public void setStr(String a) {
        this.str = a;
    }

}

1.2 final方法

方法前面加上 final 关键字,代表这个方法不可以被子类重写。如果你认为一个方法的功能已经足够完整了,子类中不需要改变的话,你可以声明此方法为 final。final 方法比非 final 方法要快,因为在编译的时候已经静态绑定了,不需要在运行时再动态绑定。

class PersonalLoan{
  public final String getName(){
    return "personal loan";
  }
}

class CheapPersonalLoan extends PersonalLoan{
  @Override
  public final String getName(){
    return "Chean personal loan"; // Compilation error:overridden method is final
  }
}

1.3 final类

final 类通常功能是完整的,他们不能被继承。Java 中有许多类是 final 的,譬如 String、Integer 以及其他包装类。

final class PersonalLoan{ }

class CheanPersonalLoan extends PersonalLoan { // Compilation error:cannot inherit form final class  
}

1.4 final关键字好处

1)final 关键字提高了性能。JVM 和 Java 应用都会缓存 final 变量。

2)final 变量可以安全的在多线程环境下进行共享,而不需要额外的同步开销。

3)使用 final 关键字,JVM 会对方法、变量及类进行优化。

2. static

关于 static 关键字的使用,它可以用来修饰的成员变量和成员方法,被修饰的成员是属于类的,而不是单单是属于某个对象的。也就是说,既然属于类,就可以不靠创建对象来调用了。

2.1 类变量

当 static 修饰成员变量时,该变量称为类变量。该类的每个对象都共享同一个类变量的值。任何对象都可以更改该类变量的值,但也可以在不创建该类的对象的情况下对类变量进行操作。

public class Student {
  private String name;
  private int age;
  // 学生的id
  private int sid;
  // 类变量,记录学生数量,分配学号
  public static int numberOfStudent = 0;
  public Student(String name, int age){
    this.name = name;
    this.age = age;
    // 通过 numberOfStudent 给学生分配学号
    this.sid = ++numberOfStudent;
  }
  // 打印属性值
  public void show() {
    System.out.println("Student : name=" + name + ", age=" + age + ", sid=" + sid );
  }
}
public class StuDemo {
  public static void main(String[] args) {
    Student s1 = new Student("张三", 23);
    Student s2 = new Student("李四", 24);
    Student s3 = new Student("王五", 25);
    Student s4 = new Student("赵六", 26);
    s1.show(); // Student : name=张三, age=23, sid=1
    s2.show(); // Student : name=李四, age=24, sid=2
    s3.show(); // Student : name=王五, age=25, sid=3
    s4.show(); // Student : name=赵六, age=26, sid=4
  }
}

2.2 静态方法

当 static 修饰成员方法时,该方法称为类方法 。静态方法在声明中有 static ,建议使用类名来调用,而不需要创建类的对象。静态方法在类加载的时候就存在了,他不依赖于任何实例。所以静态方法必须有实现,也就是说它不能是抽象方法。

public class StuDemo2 {
  public static void main(String[] args) {     
    // 访问类变量
    System.out.println(Student.numberOfStudent);
    // 调用静态方法
    Student.showNum();
  }
}

注意事项:

1) 静态方法可以直接访问类变量和静态方法。

2) 静态方法 不能直接访问普通成员变量或成员方法。反之,成员方法可以直接访问类变量或静态方法。

3) 静态方法中,不能使用 this和super关键字。

4) 静态方法只能访问静态成员

2.3 静态语句块

特点:当第一次用到本类时,静态代码块执行唯一的一次。 静态内容总是优先于非静态,所以静态代码块比构造方法先执行。

public class Game {
  public static int number;
  public static ArrayList<String> list;
  static {
    // 给类变量赋值
    number = 2;
    list = new ArrayList<String>();
    // 添加元素到集合中
    list.add("张三");
    list.add("李四");
  }
}

2.4 初始化顺序

静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序

存在继承的情况下,初始化顺序:
父类(静态变量、静态代码块)
子类(静态变量、静态代码块)
父类(实例变量、普通语句块)
父类(构造函数)
子类(实例变量、普通语句块)
子类(构造函数)