項目html |
內容java |
這個做業屬於哪一個課程算法 |
https://www.cnblogs.com/nwnu-daizh/數組 |
這個做業的要求在哪裏安全 |
https://edu.cnblogs.com/campus/xbsf/2018CST1/homework/8690併發 |
做業學習目標ide |
|
隨筆博文正文內容包括:函數
第一部分:總結第五章理論知識工具
第5章 繼承學習
5.1 類、超類、子類
5.1.1 定義子類
不一樣於C++的冒號繼承法,關鍵字extends表示繼承。
public class Manager extends Employee
{
private double bonus;
public Manager(String name,double salary,int year,int month,int day)
{
super(name, salary, year, month, day);
bonus=0;
}
public double getSalary()
{
double baseSalary=super.getSalary();
return baseSalary + bonus;
}
public void setBonus(double b)
{
bonus=b;
}
}
注:java中全部的繼承都是公有繼承。
子類比超類封裝了更多的數據,子類擁有超類的全部域和方法,除此以外,上面的例子中Manager就比超類Employee多了bonus這個數據域,和setBonus這一方法。Manager有本身的構造器,但Manager類不能訪問超類的私有域。構造器傳遞參數給超類構造器用來初始化超類的數據域,要使用關鍵字super。
而C++中使用冒號帶上超類的構造函數來構造超類私有域的。
5.1.2.覆蓋方法
由於經理的工資確定和員工的工資不同,因此在getSalary上也不能用一致的方法,容易發現上述例子中Manager類提供了一個和超類同名的getSalary方法,叫作覆蓋。
注:Manager類的方法不能直接訪問超類私有域,具體方法詳見super的解釋,已經整理
因此在覆蓋方法中必需要獲得基礎工資,不可避免要訪問超類的salary,就要用super關鍵字去調用超類的方法訪問超類的私有域。
5.1.3.多態
Employee類對象變量能夠引用Employee對象,也能夠引用Manager類對象。可是反過來,Manager類對象變量並不能引用Employee對象。道理很簡單,通俗的講,Manager必定是Employee,可是Employee必定是Manager嗎?不必定吧。
而從類內部的數據角度分析,由於子類必定有更多的方法或者數據域,而若是用子類對象變量去引用一個超類對象,超類對象並不存在那些子類多出來的方法或數據域,這明顯是有問題的引用。但反過來,超類對象變量去引用子類對象時候,當把對象看作超類,並不存在這個變量不能調用的方法。
多態:一個對象能夠指示多種實際類型的現象稱爲多態。
以下:
Manager boss = new Manager(...);
Employee[] staff = new Employee[3];
staff[0] = boss;
staff[1] = new Employee(...);
staff[2] = new Employee(...);
上述例子中 staff是一個Employee類的數組,可是staff[0]引用了一個Manager類的boss對象,而staff[1]和staff[2]則引用了Employee對象。這就是典型的多態。
與多態相關的概念還有動態綁定。
動態綁定:在運行時,上述對象變量能自動地選擇調用哪一個方法的現象稱爲動態綁定。
以下:
for(Employee e:staff)
system.out.println(e.getName()+" "+e.getSalary());
在這個例子裏面,注意到getSalary是被覆蓋了的,那麼e.getSalary()會調用超類還子類的方法呢?答案是根據其實際類型來自動選擇。java虛擬機知道e引用對象的實際類型,於是可以正確調用。
重點關注:重載和覆蓋的區別
覆蓋是子類的方法與超類方法同名,同參數列表即同簽名,當時用超類對象變量引用子類對象時,調用被覆蓋的方法,儘管是超類的對象變量,可是調用的方法是子類中被覆蓋的方法。在覆蓋時,對應方法在子類的可見性必須不低於超類的可見性。
重載僅僅同名,可是參數列表不一樣。
注意:
- 超類對象變量能夠引用子類對象,可是不能使用超類對象變量去調用子類方法,下面這樣是不合法的:
staff[0].setBonus(1000);
只有
boss.setBonus(1000);
可是同時注意,因爲動態綁定,對於覆蓋的方法,使用超類對象變量去調用,是合法的。這看上去和上述概念是矛盾的,但能夠以爲是偷樑換柱「暗中」調用了子類方法。
- 子類數組的引用能夠轉換成超類數組的引用,而不須要強制類型轉換。也就是:
Manager[] managers = new Manager[10];
Employee[] staff = managers;
這樣作是合法的,至關於staff數組的每個Employee對象變量都引用了一個Manager對象。可是回憶一下數組的引用,須要注意的問題是,managers和staff引用的是同一個數組!而比較恐怖的是,且看以下操做:
staff[0] = new Employee(...);
編譯器接受了這句操做(這看上去是難以想象的,由於managers和staff引用的是同一個數組,等於說是managers[0]=new Employee(...),但編譯器這種狀況下就是接受了)因爲managers和staff引用同一個數組,當用managers[0].setBonus(1000)時會出現錯誤,由於那個實際的對象根本就不是Manager類,從而不存在setBonus這個方法,這是一個有趣的問題。必定要避免這樣的事情發生,因此全部數組都要牢記建立他們的元素類型。
5.1.4.理解方法調用
另外還要注意,static方法不存在繼承和覆寫,它與聲明時候的類相關,假設有Shape類,Cirlce類繼承了Shape,這兩個類裏面各自聲明瞭一個static void draw()方法,當有Shape s = new Circle(),s.draw()調用的不是Circle的draw方法,而Circle c = new Circle(),c.draw()纔會調用Circle的draw方法。
5.2 object:全部類的超類
Object 類是Java中全部類的始祖,在Java中每一個類都是由它擴展來的。
可使用Object類型的變量引用任何類型的對象。
全部的數組類型,不論是對象數組仍是基本類型的數組都擴展了Object類。
一、equals方法
在Object類中,equals方法用於判斷兩個對象是否具備相同的引用。Object類中源碼以下:
public boolean equals(Object obj) { return (this == obj); }
若是兩個對象具備相同的引用,它們必定是相等的。可是對於大多數類來講,這種判斷沒有什麼意義,由於常常會須要判斷兩個對象的狀態是否相等,狀態相等就認爲相等。下面給出編寫一個完美equals方法的建議:
1)顯示參數命名爲 otherObject
2)檢測 this 和 otherObject 是否引用同一個對象:if ( this == otherObject ) return true; 起到優化的做用
3)檢測 otherObject 是否爲 null,若是爲 null,返回 false:if (otherObject == null) return false;
4)比較 this 與 otherObject 是否屬於同一個類,若是 equals 的語義在每一個子類中有所改變,即由子類的屬性狀態決定相等性,就使用 getClass 檢測:if (getClass() != otherObject.getClass()) return false;若是全部的子類都擁有統一的語義,即由超類決定相等的概念,那麼就使用 instanceof 檢測:if (!(otherObject instanceof ClassName)) return false;
5)將 otherObject 轉換成相應的類類型變量:ClassName other = (ClassName) otherObject
6)如今開始對全部須要比較的域進行比較了。使用==比較基本類型域,使用equals比較對象域。 return field1 == other.field1 && Objects.equals(field2,other.field2) && ...;若是在子類中從新定義equals,就要在其中包含調用super.equals(other)。
提示:對於數組類型的域,可使用Arrays.equals方法檢測相應的數據元素是否相等。
順便說一下:
在Java中,instanceof 運算符的前一個操做符是一個引用變量,該變量爲空則返回 false,後一個操做數一般是一個類(能夠是接口),用於判斷前面的對象是不是後面的類,或者其子類、實現類的實例。若是是返回 true,不然返回 false 。也就是說在使用 instanceof 關鍵字作判斷時, instanceof 操做符的左右操做數必須有繼承或實現關係。
使用 Objects 類的 equals 方法 Objects.equals(field2,other.field2) 避免若是 field2 爲 null 時,field2.equals(other.field2) 這種比較方法會產生異常,Objects 類在 java.util 包中,源碼以下:
public static boolean equals(Object a, Object b) { return (a == b) || (a != null && a.equals(b)); }
舉個栗子:
運行結果:
1
2
|
empOne
equals
empTwo:
false
empOne
equals
empTwo:
true
|
子類中定義equals方法:
到這裏基本說完了,還有Java語言規範要求的 equals 方法的 5 條特性,能夠在API文檔中 Object 類中 equals 方法查看。
二、hashCode方法
散列碼是由對象導出的一個整形值。Object類中定義的hashCode方法源碼以下:
1
|
public
native
int
hashCode();
|
使用了native關鍵字,我看不懂具體方法實現,有時間看完 native關鍵字 這篇博客應該能明白一些,具體之後再說,暫時不經常使用。
String 類使用下列算法計算散列碼:
1
2
3
|
int
hash =
0
;
for
(
int
i =
0
; i < length(); i++)
hash =
31
* hash + charAt(i);
|
因爲 hashCode 方法定義在 Object 類中,所以每一個對象都有一個默認的散列碼,其值爲對象的存儲地址。 使用 Object 類的這個方法能夠用 Objects 的 hashCode 方法保證 null 安全。
若是從新定義 equals 方法,就必須從新定義 hashCode 方法,以便用戶能夠將對象插入到散列表中(散列表在覈心技術I第9章討論)。
hashCode方法應該返回一個整形數值,併合理地組合實例域的散列碼,以便可以讓各個不一樣的對象產生的散列碼更加均勻。例如,下面是 Employee 類的 hashCode 方法。
1
2
3
4
|
@Override
//組合多個散列值,若是存在數組類型的域,使用靜態的Arrays.hashCode方法計算一個散列碼,equals與hashCode的定義必須一致
public
int
hashCode() {
return
Objects.hash(name, salary, hireDay);
}
|
涉及的源碼以下:
equals 與 hashCode 的定義必須一致:若是 x.equals(y) 返回 true,那麼 x.hashCode() 就必須與 y.hashCode() 具備相同的值。例如,若是用定義的 Employee.equals 比較僱員的ID,那麼 hashCode 方法就須要散列ID,而不是僱員的姓名或存儲地址。
先說到這吧,後面看了散列表再理解。
三、toString方法
Object 中的一個重要方法,用於返回表示對象值的字符串。
下面是 Employee 類中的 toString 方法的實現:
1
2
3
4
5
6
7
8
|
@Override
public
String toString() {
return
getClass().getName()
+
"[name="
+ name
+
",salary="
+ salary
+
",hireDay="
+ hireDay
+
"]"
;
}
|
下面是 Manager 類中的 toString 方法:
1
2
3
4
|
@Override
public
String toString() {
return
super
.toString() +
"[bonus="
+ bonus +
"]"
;
}
|
隨處可見toString方法的主要緣由是:只要對象與一個字符串經過操做符 「+」 鏈接起來,Java 編譯就會自動地調用 toString 方法,以便得到這個對象的字符串描述。
提示:在調用 x.toString() 的地方能夠用 ""+x 替代。這裏的 x 就是 x.toString() 。與 toString 不一樣的是,若是 x 是基本類型,這條語句照樣可以執行。
System.out.println(x); println 方法就會直接調用 x.toString(x) ,並打印輸出獲得的字符串。
Object 類定義了 toString 方法,用來打印輸出對象所屬的類名和散列碼。例如 System.out.println(System.out); 語句輸出 java.io.PrintStream@154617c ,由於 PrintStream 類沒有覆蓋 toString 方法。Object 類的toString方法源碼以下:
1
2
3
|
public
String toString() {
return
getClass().getName() +
"@"
+ Integer.toHexString(hashCode());
}
|
警告:使人煩惱的是,數組繼承了 object 類的 toString 方法,數組類型將按照舊的格式打印,生成字符串如 「[I@1a46...」(前綴 [I 代表是一個整形數組)。修正的方式是調用靜態方法 Arrays.toString,多維數組調用 Arrays.deepToString 方法。
toString 方法是一種很是有用的調試工具。在標準類庫中,許多類都定義了 toString方法, 以便用戶可以得到一些有關對象狀態的必要信息。強烈建議爲自定義的每個類增長 toString 方法。
到這裏基本把 Object 類的重要方法介紹了,後續有新東西會繼續添加。
程序清單:
運行結果:
empOne equals empTwo:false empOne equals empTwo:true -1893166707 -1893166707 com.song.Employee[name=song,salary=10.0,hireDay=2019-05-25] com.song.Employee[name=song,salary=10.0,hireDay=2019-05-25] com.song.Employee[name=song,salary=10.0,hireDay=2019-05-25] empOne equals managerOne:false managerOne equals managerTwo:false managerOne equals managerThree:true -1893166707 -1683903746 com.song.Manager[name=song,salary=10.0,hireDay=2019-05-25][bonus=0.0] com.song.Manager[name=song,salary=10.0,hireDay=2019-05-25][bonus=0.0] com.song.Manager[name=wang,salary=100.0,hireDay=2019-05-28][bonus=0.0] java.io.PrintStream@154617c Process finished with exit code 0
至於結果是兩個不一樣類的對象生成的散列碼是相同的這個狀況,是否違背了上面說的 equals 與 hashCode 的定義必須一致的原則。
5.3 泛型數組列表
java.util.ArrayList<T>
ArrayList<>() 構造一個空數組列表
ArrayList<T>(int initialCapacity) 用指定容量構造一個空數組列表
boolean add(T obj) 在數組列表的尾端添加一個元素
int size() 返回存儲在數組列表中的當前元素數量。(這個值小於或等於數組列表的容量)
void ensureCapacity(int capacity) 確保數組列表在不從新分配存儲空間的狀況下就可以保存給定數量的元素。
void trimToSize() 將數組列表的存儲容量消減到當前尺寸
使用set()和get()方法實現訪問或改變數組元素的操做,而不使用[]
例如,要設置第i個元素,可使用:
staff.set(i, harry);
等價於對數組a的元素賦值(數組的下標從0開始):
a[i] = harry;
注意:
只有i小於或等於數組列表的大小時,纔可以調用list.set(i,x)。例如,下面這段代碼是錯誤的:
ArrayList<Employee> list = new ArrayList<>(100); //capacity 100,size 0
list.set(0,x); // no element 0 yet
使用add方法爲數組添加新元素,而不要使用set方法, 它只能替換數組中已經存在的元素內容。
Employee e = staff.get(i);
等價於:
Employee e = a[i];
下面這個既能夠靈活擴展數組(對小型數組),又能夠方便地訪問數組元素。
首先,建立一個數組,並添加全部的元素
ArrayList<X> list = new ArrayList<>();
while(...)
{
x = ...;
list.add(x);
}
執行完以後,使用toArray方法將數組元素拷貝到另外一個數組中
X[] a = new [list.size()];
list.toArray(x);
5.4 對象包裝類與自動裝箱
5.4.1 對象型包裝類
對象型(Object的子類):Boolean、Character(char)
5.4.2 裝箱與拆箱
裝箱:將基本數據類型變爲包裝類對象,經過每一個包裝類的構造方法實現裝箱處理
拆箱:將包裝類中包裝的基本數據類型取出,利用的是××Value()方法
eg:Integer提供的intValue()
public class Test {
public static void main(String[] args) {
//裝箱
Integer integer = new Integer(10);
//拆箱
int data = integer.intValue();
System.out.println(data+10);
}
}
5.4.3 自動拆裝箱(語法糖)
JDK1.5新特性
public class Test {
public static void main(String[] args) {
//自動裝箱 將基本數據類型變成對象
Integer integer = 10;
//自動拆箱
System.out.println(integer+10);
}
}
5.5 參數數量可變的方法
5.5.1,概述
在java SE 5.0 以前版本,每一個java方法都是固定參數的。然而,如今提供了可變參數的方法調用。
5.5.2,定義
舉例:
public class PrintStream{
……
public PrintStream printf(String fmt,Object . . . args){ //三個英文句號 表示多參數參數
return format(fmt,args);
}
}
5.5.3 使用
System.out.print("d% %s",n,"hello");
實際上 args 等價於args[] ,因此在程序中看成數據類型處理。在這個例子中參數類型是任意的。也能夠是某個類型的。好比
//多參數入參
public int max(int... ints){
int maxi=0;
if(ints.length>0){
maxi=ints[0];
for(int i:ints){
if(i>maxi){maxi=i;}
}
}
System.out.println(maxi);
return maxi;
}
5.6 枚舉類
5.6.1 定義
從JDK1.5以後,出現了枚舉類這麼一個概念,就是使用enum關鍵字來定義枚舉類,枚舉類enum是引用數據類型,能夠簡單地理解爲一種特殊的java類。
枚舉類的對象個數是有限的且固定的,能夠將每個對象一一列舉出來。
5.6.2.枚舉類的繼承、被繼承、實現接口問題
枚舉類能夠實現其餘接口,可是不能繼承其餘的類,由於全部枚舉類在編譯後的字節碼中都繼承自 java.lang.Enum,由於Java沒有多繼承,因此不能繼承其餘的類;
枚舉類也不能被其餘類繼承,由於全部枚舉類在編譯後的字節碼中都是繼承自 java.lang.Enum(由編譯器添加)的 final class 類,final 的類是不容許被派生繼承的。
5.6.3.Java枚舉如何保證線程安全
由於 Java 類加載與初始化是 JVM 保證線程安全,而 Java enum 枚舉在編譯器編譯後的字節碼實質是一個 final 類,每一個枚舉類型是這個 final 類中的一個靜態常量屬性,其屬性初始化是在該 final 類的 static 塊中進行,而 static 的常量屬性和代碼塊都是在類加載時初始化完成的,因此天然就是 JVM 保證了併發安全。
5.7 反射
5.7.1 使用反射的三種方式
{ 1, 類名.class
2.對象.getClass()
3.使用Class.forName("路徑+類名")}
5.7.2 定義
在程序運行狀態中,對於任意一個類,都可以知道它的全部屬性和方法,對於任意一個對象,都可以調用它的任意一個方法和屬性。這種動態獲取信息以及動態調用對象方法的功能稱爲java語言的反射機制。
通俗點講:有一個類,我想訪問這個類中的成員變量、構造方法和成員方法,正常狀況下,咱們拿到的是這個類的.java文件,那麼我直接經過new關鍵字建立一個類的實例對象,而後經過這個實例對象就能夠訪問這個類的成員變量、構造方法、成員方法了。可是如今我沒有這個類的.java文件,而是隻能拿到了這個類的.class文件,那麼如何去訪問到這個類中的成員變量、構造方法和成員方法呢?這就是反射機制解決的問題了。
第二部分:實驗部分
一、實驗目的與要求
(1) 理解繼承的定義;
(2) 掌握子類的定義要求
(3) 掌握多態性的概念及用法;
(4) 掌握抽象類的定義及用途。
二、實驗內容和步驟
實驗1:測試程序1
Ÿ 在elipse IDE中編輯、調試、運行程序5-1 —5-3(教材152頁-153頁) ;
Ÿ 掌握子類的定義及用法;
Ÿ 結合程序運行結果,理解並總結OO風格程序構造特色,理解Employee和Manager類的關係子類的用途,並在代碼中添加註釋;
Ÿ 刪除程序中Manager類、ManagerTest類,背錄刪除類的程序代碼,在代碼錄入中理解父類與子類的關係和使用特色。
例題5.1程序代碼以下:
package inheritance; /** * This program demonstrates inheritance. * @version 1.21 2004-02-21 * @author Cay Horstmann */ public class ManagerTest { public static void main(String[] args) { // construct a Manager object var boss = new Manager("Carl Cracker", 80000, 1987, 12, 15); boss.setBonus(5000);//建立一個新經理,並設置他的獎金,setBonus屬於manager的特有方法 var staff = new Employee[3];//定義一個包含三個僱員的數組 // fill the staff array with Manager and Employee objects staff[0] = boss; staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1); staff[2] = new Employee("Tommy Tester", 40000, 1990, 3, 15); //父類引用子類對象;並將經理和僱員放入數組中 // print out information about all Employee objects for (Employee e : staff) System.out.println("name=" + e.getName() + ",salary=" + e.getSalary()); //輸出每一個人的姓名及薪水 } }
輸出截圖以下:
例題5.2程序代碼以下:
package inheritance; import java.time.*; public class Employee { 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()//取得name這個屬性的值 { return name; } public double getSalary()//取得Salary這個屬性的值 { return salary; } public LocalDate getHireDay() { return hireDay; } public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; salary += raise; } }
輸出截圖以下:
例題5.3程序代碼以下:
package inheritance; public class Manager extends Employee { private double bonus; /** * @param name the employee's name * @param salary the salary * @param year the hire year * @param month the hire month * @param day the hire day */ //構造器 public Manager(String name, double salary, int year, int month, int day) { super(name, salary, year, month, day);//調用超類構造器 bonus = 0; } public double getSalary() { double baseSalary = super.getSalary();//進行重定義 return baseSalary + bonus; } public void setBonus(double b) { bonus = b; } }
實驗總結:
大體明白了什麼是類、子類、超類,掌握了父類與子類的部分用法,學會了如何定義抽象類,使用super關鍵字等。初步的理解了繼承的結構層次和多態性的概念。瞭解了子類和父類的關係。也學到了不少有用的知識,