許可權修飾符 許可權修飾符包括public、private、protected和不加任何修飾符的default,它們都可以修飾方法和變數。其中public和預設的default(不加任何修飾符)這兩個還可以修飾class。private和protected修飾類的情況只能在使用內部類時修飾,正常情況下不 ...
許可權修飾符
許可權修飾符包括public、private、protected和不加任何修飾符的default,它們都可以修飾方法和變數。其中public和預設的default(不加任何修飾符)這兩個還可以修飾class。private和protected修飾類的情況只能在使用內部類時修飾,正常情況下不能使用這兩個修飾符修飾類。
(1).public
:用public修飾的變數及方法,包內及包外的任何類(包括子類和普通類)均可以訪問;
(2).protected
:用protected修飾的變數及方法,包內的任何類及包外那些繼承了該類的子類才能訪問,protected重點突出繼承;
(3).default
:沒有用public、protected及private中任何一種修飾,其訪問許可權為default預設許可權。預設訪問許可權的類、類屬變數及方法,包內的任何類(包括繼承了此類的子類)都可以訪問它,而對於包外的任何類都不能訪問它(包括包外繼承了此類的子類)。default重點突出包;
(4)private
: 用private修飾的變數及方法,只有本類可以訪問,而包內包外的任何類均不能訪問它。
就一句話:protected修飾符所修飾的變數和方法,只可以被子類訪問,而不管子類是不是和父類位於同一個包中。default修飾符所修飾的變數和方法,只可被同一個包中的其他類訪問,而不管其他類是不是該類的子類。protected屬於包修飾符,還是子類修飾符,而default屬於包修飾符。
從許可權嚴格角度來說,private < default < protected < public
在考慮default修飾的許可權時,它是包修飾符,其中沒有加入到包中的"裸體類"屬於同一個隱式的包中,因此可以互相訪問。
例如,前面的person和student的繼承關係中,將父類成員變數name加上private修飾符,於是下麵的代碼將編譯出錯。因為new子類對象時,構造方法中賦值給this.name,而這個name是繼承自父類的,它是private的。因此對於子類來說,這個成員變數屬於能看到,不能引用、不能操作的擺設屬性。
class Person {
private String name;
int age;
}
class Student extends Person {
int studentID;
Student(int id,String name,int age) {
this.name = name;
this.age = age;
this.studentID = id;
}
}
public class Inherit {
public static void main(String[] args) {
Student s1 = new Student(1,"Malongshuai",23);
}
}
方法的重寫(overwrite/override)
父類定義的成員相對來說都比較粗糙,當子類繼承時,難免無法適當地描述子類。因此當子類對從父類繼承的方法不滿意時,可以重寫方法。
例如Person類能eat(),但girl類吃飯是淑女的吃,boy類吃飯是粗魯的吃。girl類很不滿意,因為父類的eat()只能描述吃,不能描述怎麼吃。於是girl類就重寫eat()方法,讓吃這個方法符合自身的淑女形象。
重寫方法必須和被重寫的方法具有相同的方法名稱、參數列表和返回類型。重寫的方法不能比被重寫的方法許可權更嚴格。從方法訪問的角度來說,父類的方法都能被訪問,子類重寫後的方法卻不能被訪問,這顯然是不合理的,且即使這是能訪問父類方法,但重寫的意義就丟失了。
重寫方法時,最佳實踐方式是copy整個被重寫的方法的定義語句。因為即使重寫方法的名稱改變了,編譯也不會出錯。例如重寫eat()結果寫成了Eat(),編譯是不會有任何錯誤出現的,此時它沒有重寫,而是新定義了一個Eat()方法。
class Person {
String name;
int age;
void eat() { System.out.println("eating...");}
}
class Student extends Person {
int studentID;
Student(int id,String name,int age) {
this.name = name;
this.age = age;
this.studentID = id;
}
void eat() { System.out.println("graceful eating");} //重寫
void study() {System.out.println("studing...");}
}
public class Inherit {
public static void main(String[] args) {
Student s1 = new Student(1,"Malongshuai",23);
System.out.println(s1.studentID+","+s1.name+","+s1.age);
s1.eat(); //調用重寫後的eat方法
}
}
super關鍵字
this關鍵字指向對象自身,而super關鍵字則指向對象中的父對象。如下圖:
super既可以用來引用父對象的成員變數,也可以用來調用父對象的方法。例如下麵的代碼:
class FatherClass {
public int value;
public void f(){
value = 100;
System.out.println("FatherClass.value="+value);
}
}
class ChildClass extends FatherClass {
public int value;
public void f() {
super.f(); //雖然f()要重寫,但父對象的f()函數還有一些用武之地來發揮餘熱
value = 200;
System.out.println("ChildClass.value="+value);
System.out.println(value);
System.out.println(super.value);
}
}
public class TestInherit {
public static void main(String[] args) {
ChildClass cc = new ChildClass();
cc.f();
}
}
new出子對象時,父類和子類中都有value屬性,它們都採用的初始化值0。當執行cc.f()時,調用子類的f()方法,該方法首先調用父對象的f()方法,父f()方法先將value賦值為100,這個value是父對象的屬性,然後回到子f()中賦值value為200,這個value是子對象自身的value,隨後輸出的兩個value都是子對象中的value屬性,最後的super.value是父對象中的value屬性。
雖然在圖中看上去super和this的地位是相同的,但實際上它們之間很不公平,不公平之處在於有引用變數(上圖中的cc)指向子對象,所以能夠使用"return this"代碼來返回一個子對象,但卻不能使用"return super"來返回子對象中的父對象,因為沒有引用變數指向父對象。
關於super調用的成員變數,需要區分清楚是子對象中的屬性還是父對象中的屬性。如果子對象和父對象中有同名屬性var,在沒有指定"this.var"和"super.var"時,僅模糊地指定var時將優先取子對象的屬性,如果子對象中沒有某屬性,則var表示的是父對象中的屬性。例如:
class Student extends Person {
int studentID;
int age = 33;
Student(int id) {
this.name = super.name + "x";
this.age = age + 2; //右邊的age是子對象的屬性,但如果將"int age = 33;"註釋,則age是父對象的屬性
this.studentID = id;
}
}
繼承時構造方法的重寫super()
子對象中總是包含父對象,這個父對象是怎麼來的?對象都是通過構造方法構造出來的,因此在new子類對象的時候,會調用對應的子類構造方法構造子對象,正是這個時候使用super()方法表示調用父類構造方法將父對象構造出來的。
在寫構造父對象的代碼時有以下幾個規則:
- 使用子類構造方法構造子對象時,必須要構造父對象。
- 子類可以在自己的構造方法中使用super(args)來調用父類的構造方法。同理,可以使用this(args)來調用本類其他的構造方法。
- super(args)必須寫在子類構造方法中的第一行,因為要先構造出父對象,再慢慢填補子對象自身。如果沒有顯式書寫super(args),則預設在第一行處調用父類無參數的構造方法,等價於super()。
- 如果子類構造方法中調用的super(args)在父類中不存在對應參數列表的構造方法,則編譯錯處。這包括沒有顯式指定super()時,且父類又重載了構造方法使得父類中沒有了無參數的構造方法時。
class Person {
String name;
int age;
Person() {
System.out.println("Person()");
}
Person(String name,int age) {
this.name = name;
this.age = age;
System.out.println("Person(arg1,arg2)");
}
Person(String name) {
this.name = name;
age = 20;
System.out.println("Person(arg1)");
}
}
class Student extends Person {
int studentID;
Student(int id) {
super("Malongshuai",23); //第一行調用父類構造方法構造父對象,且是含有兩個參數的Person(arg1,arg2)
this.name = super.name + "X"; //調用父對象中的name屬性
this.age = age + 2; //也是調用父對象中的屬性age
this.studentID = id;
}
}
public class TestSuper {
public static void main(String[] args) {
Student s1 = new Student(1);
System.out.println(s1.studentID+", "+s1.name+", "+s1.age);
}
}
如果將"super("Malongshuai",23);"修改為super("Malongshuai"),則表示調用父類的Person(arg1)構造方法。如果改為super(),則表示調用父類的Person()構造方法。如果省略不寫super,則等價於super()。
Object類
除了明確定義了從某個父類繼承的子類,java中的所有類都是從java.lang包中的Object類繼承來的。也就是說,Object類是所有類繼承的根,也就是它們的祖宗。一級繼承一級,最終的根總是Object類。
這個類里提供了幾個方法,但基本上所有方法都建議重寫,因為它的級別太高,抽象化的太嚴重,它的提供的那些方法也就太大眾化。
toString()
在和對象做數據連接時,將自動調用該類的toString()方法。例如System.out.println("Hello" + Person)
時,等價於System.out.println("Hello" + Person.toString())
例如:
public class TTString {
public static void main(String [] args) {
Person p = new Person();
System.out.println(p);
System.out.println(p.toString());
}
}
class Person {}
編譯並運行,查看toString()的運行結果。
D:\myjava
λ javac TTString.java
D:\myjava
λ java TTString
Person@15db9742
Person@15db9742
toString()的結果是"類名@hex(hashcode)"。官方建議,任何子類都應該重寫該方法。例如:
public class TTString {
public static void main(String [] args) {
Person p = new Person();
System.out.println(p);
}
}
class Person {
public String toString() { //重寫toString()
return "Hello World";
}
}
對象的比較"=="和equals()
對象與對象之間是否有相等關係?一般可以認為,如果兩個對象的對象內容完全相同,將認為是相等的對象。
在進行對象比較時,"=="比較的是兩個對象的引用地址,因此兩個對象使用"=="比較時是絕對不會相等的。Object類中的equals(),基本等價於"==",因此也無法正確比較對象是否相等。所以官方手冊建議重寫equals()方法。在String類中已經重寫好了。
例如,使用String類中的equals()。
public class TTequals {
public static void main(String [] args) {
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2);
System.out.println(s1.equals(s2));
}
}
final關鍵字
final表示最終的意思,它可以修飾變數、方法和類。
- final變數的值不能被改變。
- (1).final的成員變數不可改變。
- (2).final的局部變數和形參不可改變。
- final的方法不能被重寫。
- final的類不能被繼承。
也就是說,要想讓變數只讀、方法不可改變或類的繼承到此結束,就用final進行修飾。
註:若您覺得這篇文章還不錯請點擊右下角推薦,您的支持能激發作者更大的寫作熱情,非常感謝!