##⒈this和super關鍵字的3種含義 ##
this關鍵字
this關鍵字有3個用途:一是引用隱式參數(即引用自己的成員變量);二是調用自己其它的構造器;三是代表本身類的一個對象。
###⑴ super關鍵字
super關鍵字也有3個用途:一是調用超類的方法;二是調用超類的構造器;三是代表超類的一個對象。
###⑵ 二者相似之處
在調用構造器時,兩個關鍵字的使用方式相似。調用構造器的語句只能作爲另一個構造器的第一條語句出現。
###⑶ 示例代碼
/**
* @author gao tianci
* @version $Id: Father.java, v 0.1 2017年6月1日 下午8:21:22 gao tianci Exp $
*/
public class Father {
private String name;
private int age;
/**
* 默認構造器
*/
public Father() {
}
public Father(String name) {
//調用本類中的其他構造器
this(name, 20);
}
public Father(String name, int age) {
//引用隱式參數
this.name = name;
this.age = age;
}
//父類中的方法
public void printInfo(String name, int age) {
System.out.println("name = " + name);
System.out.println("age = " + age);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
/**
*
* @author gao tianci
* @version $Id: Child.java, v 0.1 2017年6月1日 下午8:30:03 gao tianci Exp $
*/
public class Child extends Father {
private String sex;
public Child() {
}
public Child(String sex) {
//[super]調用超類構造器
super("tianci", 20);
//[super]調用超類方法;[super]代表超類對象
super.printInfo(super.getName(), super.getAge());
//[this]引用隱式參數
this.sex = sex;
//[this]代表本類的對象
this.printInfo(this.getSex());
}
//子類中的方法
public void printInfo(String sex) {
System.out.println("sex = " + sex);
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
##⒉方法調用[靜態綁定]和[動態綁定] ##
⑴靜態綁定
程序執行前方法已經被綁定(也就是說在編譯過程中就已經知道這個方法到底是哪個類中的方法)。針對Java簡單的可以理解爲程序編譯期的綁定.
⑵動態綁定
程序在運行期間判斷對象的類型,調用適當的方法(也就是說在編譯過程中並不知道這個方法是哪個類中的方法,但在程序運行期間通過一定的機制可以確定個方法是哪個類的方法,並正確的調用)。動態綁定的過程:❶虛擬機提取對象的實際類型的方法表;❷虛擬機搜索方法簽名;❸調用方法。
###⑶ Java中的靜態綁定和動態綁定體現
①動態綁定針對的範疇只是對象的方法。
②java當中的方法只有final,static,private和構造方法是靜態綁定,其它方法都是動態綁定。
③java中對屬性採取靜態綁定。
###⑷java的編譯與運行
①java的編譯過程是將java源文件編譯成字節碼(jvm可執行代碼,即.class文件)的過程,在這個過程中java是不與內存打交道的,在這個過程中編譯器會進行語法的分析,如果語法不正確就會報錯。
②Java的運行過程是指jvm(java虛擬機)裝載字節碼文件並解釋執行。在這個過程纔是真正的創立內存佈局,執行java程序。java字節碼的執行有兩種方式: ❶即時編譯方式:解釋器先將字節編譯成機器碼,然後再執行該機器碼;❷解釋執行方式:解釋器通過每次解釋並執行一小段代碼來完成java字節碼程序的所有操作。
⑸Java靜態綁定和動態綁定特點
①靜態綁定:可以讓我們在編譯期就發現程序中的錯誤,而不是在運行期。靜態綁定對系統開銷較小,提高了程序運行效率。
②動態綁定:而對方法採取動態綁定是爲了實現多態,以效率爲代價來實現多態。動態綁定可以讓我們對現存的代碼無需修改,就可以對其功能進行擴展。
3.equals與hashCode和toString方法
###⑴ equals
①’=='雙等號兩種應用
❶比較基本數據類型,如果兩個值相同,則結果爲true,否則false.
❷比較引用類型時,如果引用指向內存中的同一對象(即兩個變量引用同一個對象時),結果爲true,否則false。
②equals
㈠Object類中equals方法。
作用是判斷兩個變量是否具有相同的引用,如果是結果爲true,否則false,從這一點來說equals本質實現方式使用了==。
㈡覆寫equals方法。
一般我們有這樣的需求:只比較兩個對象的內容是否相同,如果兩個對象的內容都相同,就認爲這兩個對象相等。想達到這一目的,我們就需要自己覆寫equals方法。java常用的類基本都默認覆寫了equals方法來實現比較內容相同。
㈢覆寫equals方法建議。
在自己定義的類中編寫一個完美的equals方法的建議:
❶顯式參數命名爲otherObject,稍後將它轉換成另一個叫做other的的變量。
❷檢測this與otherObject是否引用同一個對象:
if(this==otherObject) return true;
❸檢測otherObject是否爲null,如果爲null,返回false:
if(null==otherObject) return false;
❹比較this與otherObject是否屬於同一類。如果equals的語義在每個子類中有所改變,就是用getClass檢測:
if(this.getClass()!=otherObject.getClass()) return false;
如果所有的子類都擁有統一的語義,就是用instanceof 檢測:
if(!(otherObject instanceof ClassName)) return false;
❺將otherObject 轉換爲相應的類類型變量:
ClassName other = (ClassName)otherObject;
❻接下來對所有需要比較的域進行比較。使用==比較基本類型域,使用equals比較對象域。如果所有的域都匹配,返回true,否則返回false。
return field1==other.field1&&Objects.equals(field2,other.field2)&&...;
如果子類中重新定義equals,就要在其中包含調用:
super.equals(other);
⑵hashCode
####① Object類中hashCode方法。
由於hashCode方法定義在Object類中,因此每個對象都有一個默認的散列碼,其值是一個整型數值,爲對象的存儲地址。
②覆寫hashCode方法。
❶覆寫hashCode方法原則。當我們在自定義的類覆寫equals方法的時候,就必須覆寫hashCode方法。
❷爲什麼要遵循覆寫hashCode原則。
1 HashCode存在的作用:當對象存儲在哈希表中(如Hashtable,HashMap)時,哈希表中查找對象時,先依據HashCode確定對象所在的鏈表,再依據equals方法在鏈表中確定對象是否存在。
2 覆寫equals()時,要覆寫hashCode()原因:
- <1>JavaSE規範約束:可查看Object的hashCode()方法,註釋文檔。歸納起來就是以下3條:
兩個對象相同,hashCode一定相同;
hashCode不同,兩個對象一定不相同;
hashCode相同,兩個對象不一定相同。 - <2>覆寫的本質意義:
若對象不會保存在哈希表結構中,那麼是不需要覆寫hashCode();
若在HashMap和Hashtable裏面如果是作爲value而不是作爲key的話,也是不必覆寫 hashCode()。至於HashSet,實際上它只是忽略value的HashMap,每次HashSet.add(obj)其實就是HashMap.put(obj, dummyObject)。
爲了保證對象做爲key時,每次get的時候HashMap(包括HashTable,HashSet)既要看equals是不是true,也要看hash code是不是true;put的時候也是要判斷equals()和hash code()是不是都是true,來進行添加和獲取操作。重寫了equals()改變了判等依據,所以要對應重寫hashCode()改變對應的判等依據,否則無法保持一致性造成錯誤。
⑶toString
####① Object類中toString方法。
Object類定義了toString方法,因此每個對象都有一個默認的toString方法,打印輸出的默認內容爲:對象所屬的類名和散列碼。
####② 覆寫toString方法。
一般對自定義的類,覆寫toString方法,方便打印我們需要看到的內容。
⑷equals與hashCode和toString用例
父類代碼:
import java.io.Serializable;
import java.time.LocalDate;
import java.util.Objects;
/**
*
* @author gao tianci
* @version $Id: Employee.java, v 0.1 2017年5月18日 下午6:32:19 gao tianci Exp $
*/
public class Employee implements Serializable {
/** */
private static final long serialVersionUID = -1213732035543208949L;
private String name;
private double salary;
private LocalDate hireDay;
public Employee(String name, double salary, int year, int month, int day) {
this.name = name;
this.salary = salary;
hireDay = LocalDate.of(year, month, day);
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
public LocalDate getHireDay() {
return hireDay;
}
public void raiseSalary(double byPrecent) {
double raise = salary * byPrecent / 100;
salary += raise;
}
//equals方法
/*編寫完整equals方法步驟:
* 檢測this 和 otherObject是否引用同一個對象
* 檢測otherObject是否爲null
* 檢測this 和 otherObject是否爲同一個類
* 將otherObject轉換爲相應的類類型變量
* 使用Objects.equals()比較域對象
*
*/
public boolean equals(Object otherObject) {
// 檢測this 和 otherObject是否引用同一個對象
if (this == otherObject)
return true;
// 檢測otherObject是否爲null
if (null == otherObject)
return false;
//檢測this 和 otherObject是否爲同一個類
if (this.getClass() != otherObject.getClass())
return false;
//將otherObject轉換爲相應的類類型變量
Employee other = (Employee) otherObject;
// 使用Objects.equals()比較域對象
return Objects.equals(name, other.name) && Objects.equals(salary, other.salary) && Objects.equals(hireDay, other.hireDay);
}
//hashCode方法
public int hashCode() {
//由所有對象的散列碼組合組合一個散列碼
return Objects.hash(name, salary, hireDay);
}
/**
* toString 方法
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Employee [name=" + name + ", salary=" + salary + ", hireDay=" + hireDay + "]";
}
}
子類代碼:
import java.util.Objects;
/**
* @author gao tianci
* @version $Id: Manager.java, v 0.1 2017年5月18日 下午6:58:53 gao tianci Exp $
*/
public class Manager extends Employee {
/** */
private static final long serialVersionUID = 5401329903146485794L;
/**
* @param name
* @param salary
* @param year
* @param month
* @param day
* 繼承中,當基類中沒有無參數的構造器時(換句話說,基類中只提供了有參數的構造器),
* 那麼子類也要提供對應的含參數構造器,否則編譯出錯。在子類 的構造器中我們使用super
* 關鍵字,實現對基類的構造器的調用。
*/
public Manager(String name, double salary, int year, int month, int day) {
//使用super關鍵字調用基類的構造器
super(name, salary, year, month, day);
bonus = 0;
}
private double bonus;
public double getSalary() {
//使用super關鍵字調用基類的方法
double baseSalary = super.getSalary();
return baseSalary + bonus;
}
public void setBonus(double bonus) {
//this 關鍵字引用隱式參數
this.bonus = bonus;
}
//equals方法
public boolean equals(Object otherObject) {
//如果子類中重新定義equals,就要在其中包含調用: super.equals(otherObject);
if (!super.equals(otherObject))
return false;
//調用父類的super.equals(otherObject)檢測通過,只需檢測子類中獨有的域值
Manager other = (Manager) otherObject;
return Objects.equals(bonus, other.bonus);
}
//hashCode方法
public int hashCode() {
return super.hashCode() + 17 * new Double(bonus).hashCode();
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return super.toString() + "[bonus=" + bonus + "]";
}
}
測試代碼:
/**
* @author gao tianci
* @version $Id: Test.java, v 0.1 2017年5月18日 下午7:10:04 gao tianci Exp $
*/
public class Test {
public static void main(String[] args) {
Employee alice1 = new Employee("Alice Adams", 7500, 1987, 12, 5);
Employee alice2 = alice1;
Employee alice3 = new Employee("Alice Adams", 7500, 1987, 12, 5);
Employee bob = new Employee("Bob Brandson", 5000, 1989, 10, 1);
System.out.println("--------==-------");
System.out.println(alice1 == alice2);
System.out.println(alice1 == alice3);
System.out.println("-----父類equals----");
System.out.println(alice1.equals(alice2));
System.out.println(alice1.equals(alice3));
System.out.println(alice1.equals(bob));
System.out.println("-----子類equals-----");
Manager carl = new Manager("Carl", 5000, 1987, 12, 10);
Manager boss = new Manager("Carl", 5000, 1987, 12, 10);
Manager boss2 = new Manager("Carl", 5000, 1987, 12, 10);
boss2.raiseSalary(5000);
System.out.println(carl.equals(boss));
System.out.println(boss.equals(boss2));
System.out.println("----hashCode值----");
//不同對象的hashCode值可能相同
System.out.println(alice1.hashCode());
System.out.println(alice2.hashCode());
System.out.println(alice3.hashCode());
System.out.println("----hashCode值----");
System.out.println(bob.hashCode());
System.out.println(carl.hashCode());
System.out.println(boss.hashCode());
System.out.println(boss2.hashCode());
}
}
測試結果:
--------==-------
true
false
-----父類equals----
true
true
false
-----子類equals-----
true
false
----hashCode值----
-916556984
-916556984
-916556984
------------------
-731457450
1306331297
1306331297
1492420577
###⑸自定義類覆寫equals與hashCode和toString方法時使用輔助工具類建議
1.java.util.Objects (jdk7)
static int hash(Object… objects)
返回一個散列碼,由提供的所有對象的散列碼組合得到。
static int hashCode(Object a)
如果a爲null,返回0;否則返回a.hashCode();
static boolean equals(Object a,Object b)
如果a和b都爲null,返回true;如果其中之一爲null,返回false;否則返回a.equals(b)
2.針對數組的一些工具類方法在java.util.Arrays類中。