static、final、abstract修飾符
一、static修飾符
1.1、static變量
在類中,使用static修飾的成員變量,就是靜態變量,反之為非靜態變量。
靜態變量和非靜態變量的區別
靜態變量屬於類的,"可以"使用類名來訪問,非靜態變量是屬於對象的,"必須"使用對象來訪問.
public class Student{ private static int age; private double score; public static voidstudent.javamain(String[] args) { Student s = new Student(); //推薦使用類名訪問靜態成員 System.out.println(Student.age); System.out.println(s.age); System.out.println(s.score); } }
靜態變量對於類而言在內存中只有一個,能被類的所有實例所共享。實例變量對於類的每個實例都有一份,它們之間互不影響.
例如:
public class Student{ private static int count; private int num; public Student() { count++; numtest++; } public static void main(String[] args) { Student s1 = new Student(); Student s2 = new Student(); Student s3 = new Student(); Student s4 = new Student(); //因為還是在類中,所以可以直接訪問私有屬性 System.out.println(s1.num); System.out.println(s2.num); System.out.println(s3.num); System.out.println(s4.num); System.out.println(Student.count); System.out.println(s1.count); System.out.println(s2.count); System.out.println(s3.count); System.out.println(s4.count); } }
在加載類的過程中為靜態變量分配內存,實例變量在創建對象時分配內存
所以靜態變量可以使用類名來直接訪問,而不需要使用對象來訪問.
1.2、static方法
在類中,使用static修飾的成員方法,就是靜態方法,反之為非靜態方法。
靜態方法和非靜態方法的區別
靜態方法數屬於類的,"可以"使用類名來調用,非靜態方法是屬於對象的,"必須"使用對象來調用.
靜態方法"不可以"直接訪問類中的非靜態變量和非靜態方法,但是"可以"直接訪問類中的靜態變量和靜態方法
註意:this和super在類中屬於非靜態的變量.(靜態方法中不能使用)
例如:
public class Student{ private static int count; private int num; public void run(){} public static void go(){} public static void test(){ //編譯通過 System.out.println(count); go(); //編譯報錯 System.out.println(num); run(); } }View Code
非靜態方法"可以"直接訪問類中的非靜態變量和非靜態方法,也"可以"直接訪問類中的靜態變量和靜態方法
例如:
public class Student{ private static int count; private int num; public void run(){} public static void go(){} public void test(){ //編譯通過 System.out.println(count); go(); //編譯通過 System.out.println(num); run(); } }View Code
思考:為什麽靜態方法和非靜態方法不能直接相互訪問?
父類的靜態方法可以被子類繼承,但是不能被子類重寫
例如:
public class Person { public static void method() {} } //編譯報錯 public class Student extends Person { public void method(){} } 例如: public class Person { public static void test() { System.out.println("Person"); } } //編譯通過,但不是重寫 public class Student extends Person { public static void test(){ System.out.println("Student"); } } main: Perosn p = new Student(); p.test();//輸出Person p = new Person(); p.test();//輸出Perosn 和非靜態方法重寫後的效果不一樣test
父類的非靜態方法不能被子類重寫為靜態方法
例如:
public class Person { public void test() { System.out.println("Person"); } } //編譯報錯 public class Student extends Person { public static void test(){ System.out.println("Student"); } }test
1.3、代碼塊和靜態代碼塊
1)類中可以編寫代碼塊和靜態代碼塊
例如:
public class Person {
{
//代碼塊(匿名代碼塊)
}
static{
//靜態代碼塊
}
}
2)匿名代碼塊和靜態代碼塊的執行
因為沒有名字,在程序並不能主動調用這些代碼塊。
匿名代碼塊是在創建對象的時候自動執行的,並且在構造器執行之前。同時匿名代碼塊在每次創建對象的時候都會自動執行.
靜態代碼塊是在類加載完成之後就自動執行,並且只執行一次.
註:每個類在第一次被使用的時候就會被加載,並且一般只會加載一次.
例如:
1 public class Person { 2 { 3 System.out.println("匿名代碼塊"); 4 } 5 6 static{ 7 System.out.println("靜態代碼塊"); 8 } 9 public Person(){ 10 System.out.println("構造器"); 11 } 12 } 13 main: 14 Student s1 = new Student(); 15 Student s2 = new Student(); 16 Student s3 = new Student();test
//輸出結果:
靜態代碼塊
匿名代碼塊
構造器
匿名代碼塊
構造器
匿名代碼塊
構造器
3)匿名代碼塊和靜態代碼塊的作用
匿名代碼塊的作用是給對象的成員變量初始化賦值,但是因為構造器也能完成這項工作,所以匿名代碼塊使用的並不多。
靜態代碼塊的作用是給類中的靜態成員變量初始化賦值。
例如:
1 public class Person { 2 public static String name; 3 static{ 4 name = "tom"; 5 } 6 public Person(){ 7 name = "zs"; 8 } 9 } 10 main: 11 System.out.println(Person.name);//tomtest
註:在構造器中給靜態變量賦值,並不能保證能賦值成功,因為構造器是在創建對象的時候才指向,但是靜態變量可以不創建對象而直接使用類名來訪問.
1.4、創建和初始化對象的過程
Student s = new Student();
4.1)Student類之前沒有進行類加載
1、類加載,同時初始化類中靜態的屬性
2、執行靜態代碼塊
3、分配內存空間,同時初始化非靜態的屬性(賦默認值,0/false/null)
4、調用Student的父類構造器
5、對Student中的屬性進行顯示賦值(如果有的話)
6、執行匿名代碼塊
7、執行構造器
8、返回內存地址
註:子類中非靜態屬性的顯示賦值是在父類構造器執行完之後和子類中的匿名代碼塊執行之前的時候
例如:
1 public class Person{ 2 private String name = "zs"; 3 public Person() { 4 System.out.println("Person構造器"); 5 print(); 6 } 7 public void print(){ 8 System.out.println("Person print方法: name = "+name); 9 } 10 } 11 12 public class Student extends Person{ 13 private String name = "tom"; 14 { 15 System.out.println("Student匿名代碼塊"); 16 } 17 18 static{ 19 System.out.println("Student靜態代碼塊"); 20 } 21 public Student(){ 22 System.out.println("Student構造器"); 23 } 24 public void print(){ 25 System.out.println("student print方法: name = "+name); 26 } 27 public static void main(String[] args) { 28 new Student(); 29 } 30 }test
//輸出結果:
Student靜態代碼塊
Person構造器
student print方法: name = null
Student匿名代碼塊
Student構造器
Student s = new Student();
4. 2)Student類之前已經進行了類加載
1.分配內存空間,同時初始化非靜態的屬性(賦默認值,0/false/null)
2.調用Student的父類構造器
3.對Student中的屬性進行顯示賦值(如果有的話)
4.執行匿名代碼塊
5.執行構造器
6.返回內存地址
5)靜態導入
靜態導入是JDK5.0引入的新特性。
例如:
1 import static java.lang.Math.random; 2 import static java.lang.Math.PI; 3 4 public class Test { 5 public static void main(String[] args) { 6 //之前是需要Math.random()調用的 7 System.out.println(random()); 8 System.out.println(PI); 9 } 10 }test
二、final修飾符
2.1、修飾類
用final修飾的類不能被繼承,沒有子類。
例如:我們是無法寫一個類去繼承String類,然後對String類型擴展的,因為API中已經被String類定義為final的了.
我們也可以定義final修飾的類:
1 public final class Action{ 2 3 } 4 5 //編譯報錯 6 public class Go extends Action{ 7 8 }View Code
2.2、修飾方法
用final修飾的方法可以被繼承,但是不能被子類的重寫。
例如:每個類都是Object類的子類,繼承了Object中的眾多方法,在子類中可以重寫toString方法、equals方法等,但是不能重寫getClass方法 wait方法等,因為這些方法都是使用fianl修飾的。
我們也可以定義final修飾的方法:
1 public class Person{ 2 public final void print(){} 3 } 4 5 //編譯報錯 6 public class Student extends Person{ 7 public void print(){ 8 9 } 10 }View Code
2.3、修飾變量
用final修飾的變量表示常量,只能被賦一次值.其實使用final修飾的變量也就成了常量了,因為值不會再變了。
1)修飾局部變量:
例如:
1 public class Person{ 2 public void print(final int a){ 3 //編譯報錯,不能再次賦值,傳參的時候已經賦過了 4 a = 1; 5 } 6 } 7 8 public class Person{ 9 public void print(){ 10 final int a; 11 a = 1; 12 //編譯報錯,不能再次賦值 13 a = 2; 14 } 15 }View Code
2)修飾成員變量:
非靜態成員變量:
public class Person{
private final int a;
}
只有一次機會,可以給此變量a賦值的位置:
聲明的同時賦值
匿名代碼塊中賦值
構造器中賦值(類中出現的所有構造器都要寫)
靜態成員變量:
public class Person{
private static final int a;
}
只有一次機會,可以給此變量a賦值的位置:
聲明的同時賦值
靜態代碼塊中賦值
3)修飾引用變量
1 main: 2 final Student s = new Student(); 3 //編譯通過 4 s.setName("tom"); 5 s.setName("zs"); 6 7 //編譯報錯,不能修改引用s指向的內存地址 8 s = new Student();View Code
三、abstract修飾符
abstract修飾符可以用來修飾方法也可以修飾類,如果修飾方法,那麽該方法就是抽象方法;如果修飾類,那麽該類就是抽象類。
3.1、抽象類和抽象方法的關系
抽象類中可以沒有抽象方法,但是有抽象方法的類一定要聲明為抽象類。
3.2、語法
public abstract class Action{
public abstract void doSomething();
}
public void doSomething(){...}
對於這個普通方法來講:
"public void doSomething()"這部分是方法的聲明
"{...}"這部分是方法的實現,如果大括號中什麽都沒寫,就叫方法的空實現
聲明類的同時,加上abstract修飾符就是抽象類
聲明方法的時候,加上abstract修飾符,並且去掉方法的大口號,同時結尾加上分號,該方法就是抽象方法。
3.3、特點及作用
抽象類,不能使用new關鍵在來創建對象,它是用來讓子類繼承的。
抽象方法,只有方法的聲明,沒有方法的實現,它是用來讓子類實現的。
註:子類繼承抽象類後,需要實現抽象類中沒有實現的抽象方法,否則這個子類也要聲明為抽象類。
例如:
public abstract class Action{
public abstract void doSomething();
}
main:
//編譯報錯,抽象類不能new對象
Action a = new Action();
//子類繼承抽象類
public class Eat extends Action{
//實現父類中沒有實現的抽象方法
public void doSomething(){
//code
}
}
main:
Action a = new Eat();
a.doSomething();
註:子類繼承抽象類,那麽就必須要實現抽象類沒有實現的抽象方法,否則該子類也要聲明為抽象類。
3.4、思考
思考1:抽象類不能new對象,那麽抽象類中有沒有構造器?
思考2:抽象類和抽象方法意義(為什麽要編寫抽象類、抽象方法)
有抽象方法的抽象類
無抽象方法的抽象類
static、final、abstract修飾符