Java 第三周總結
第三周的作業。
- Java 第三周總結
- 1.本章學習總結
- 2.Java Q&A
-
- 1.代碼閱讀
- 2.構造函數有什么用?其編寫格式是什么?如果一個類不寫構造函數,它有構造函數嗎?如果一個類中只有帶參的構造函數,它有沒有不帶參數的默認構造函數?
- 3.使用java.lang.Math類的函數時,為什么不需要new?如果new Math()會產生什么錯誤?
- 4.什么是重載?什么是構造函數重載?在java.lang.Math類中舉出函數重載的例子?怎么才能區分兩個重載函數?
- 5.final修飾的變量不能改變,為什么如下代碼可以正常運行?
- 6.閱讀代碼EmployeeTest.java,回答:
- 7.編寫函數public static boolean isMondayToFriday()
-
- 3.使用碼雲管理Java代碼
- 4.PTA實驗
1.本章學習總結
2.Java Q&A
1.代碼閱讀
public class Test1 {
private int i = 1;//這行不能修改
private static int j = 2;
public static void main(String[] args) {
geti();
Test1getj();
}
public static void geti() {
System.out.println(i);
}
public static void getj() {
System.out.println(j);
}
}
以上代碼可否編譯通過?哪里會出錯?為什么?嘗試改正?
如果創建3個Test1對象,有內存中有幾個i,幾個j?
為了解決兩種情形,static關鍵字應運而生:
> 只為某特定域分配單一存儲空間
> 希望即使沒有創建對象,也能夠調用某方法
接下來回答問題:不能編譯通過,(首先Test1getj())出錯如圖所示,即靜態方法不能調用一個非靜態的成員變量。修改的話在這個靜態方法里面創建其自身的對象。
public static void geti() {
System.out.println((new Test1()).i);
}
那么我們來想想這是為什么。首先靜態方法可以不通過對象進行調用,而且我們被推薦使用類名去引用static變量(或者方法),它強調了其static結構。由於在用static方法前不需要創建任何對象,所以對於static方法來說,不能直接訪問非static的成員變量或者是方法,因為他們很有可能都不存在!
如果創建3個Test1變量,內存中有3個i,1個j。因為一個static字段對於每個類來說只有一份存儲空間,而非static字段對於每個對象都會有一個存儲空間。
2.構造函數有什么用?其編寫格式是什么?如果一個類不寫構造函數,它有構造函數嗎?如果一個類中只有帶參的構造函數,它有沒有不帶參數的默認構造函數?
構造函數(Constructor)的用處:
> 提醒我們要對變量進行初始化,C語言編程告訴我么一個慘痛教訓,不初始化變量而去使用它是一件相!當!糟!糕!的事情。
> 減少重復的初始化代碼,並可以使代碼更加易於閱讀。
編寫格式:
class Rock {
int i;
Rock() {
//do something
}
}
或者是
class Rock2 {
int i;
Rock2(int i) {
/* do something
* such as...
* this.i = i;
*/
}
}
通過上述兩個樣例,我們可以看到構造函數的一些基本格式。比如可以帶參或者不帶參,有了形式參數,我們就可以在初始化對象時提供實際參數,so當然可以帶形式參數。然后方法名和類名相同,還有,不指定返回類型。
如果我們不寫構造函數呢?那么這個類里面會不會有構造函數呢?顯然是應該有的,我們剛才說了,不初始化是一個非常不安全的行為,so肯定是要有的啦。然而我們並看不見,不過它確實是有,它大概長成這個樣子,這樣,這樣……那到底是怎樣,首先它沒有參數,然后這個方法不干任何事情(??),可能是的。好像還是不大懂,那我們舉個栗子吧。
public class Rock {
//好蠢的代碼哦……
}
再次啟用我們的反匯編!來看看到底發生了什么?
public class Rock {
public Rock();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
}
我們可以看到編譯的時候自動給我們加上了一個構造函數,然后確實什么都沒有干……
這在Java當中被稱為默認構造器,同時它也是無參的構造器,一般我們不寫任何構造函數的時候,它就會在編譯的時候被自動調用。
然后我們再來扯扯剛才的編寫格式:
①有無形式參數都可以,解釋過了;
②構造函數與類同名,沿用C++的做法,這樣不會和類的任何成員名稱起沖突。而且我們一般在寫方法名的時候,首字母一般小寫,然而構造函數它可管不着你這種約定俗成啦~
③不指定返回類型,連void都不可以。
……
那Rock rock = new Rock();
呢?嗯……臉好疼,這個只是new表達式去返回對新建對象的引用,而不是構造函數去返回什么。不信往下看
For purposes other than simple initialization, classes can have constructors. Constructors are blocks of statements that can be used to initialize an object before the reference to the object is returned by new. Constructors have the same name as the class they initialize. Like methods, they take zero or more arguments, but constructors are not methods and thus have no return type. Arguments, if any, are provided between the parentheses that follow the type name when the object is created with new. Constructors are invoked after the instance variables of a newly created object of the class have been assigned their default initial values and after their explicit initializers are executed.
The Java Programming Language傳送門到參考網站
我們可以從這段英文中看到,constructor不被當做一個常見的方法,我們可以認為它是特殊的方法。而且constructor在調用之前,對象就已經存在了,何談是它來返回引用呢?
對於這個最后一小問,那就敲個代碼吧,就拿上面的Rock2類來看看吧!
public class Rock2 {
int i;
Rock2(int);
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: iload_1
6: putfield #2 // Field i:I
9: return
}
從反匯編結果來看,編譯的時候就不會再自動建立默認構造函數了。
而且你看,的確沒有無參的構造函數給你用了。真的想用,看到第三個fix了嗎?Create constructor 'Rock2()',自己去搞一個啊!
3.使用java.lang.Math
類的函數時,為什么不需要new?如果new Math()會產生什么錯誤?
為什么不需要要new,因為Math類里面的所有成員變量和成員方法都是靜態的,我們前面有說到過,這種用static修飾的都可以直接用類名去調用而且不需要任何對象在提前創建。
而且還不是不需要的問題,問題是你如果想new一個,會報錯啊!報了個啥錯?The constructor Math() is not visible
,也就是說它根本就不讓你new一個,這就很尷尬了,所以讓我們來看看Math類的源碼。
/**
* Don't let anyone instantiate this class.
*/
private Math() {}
說的很明確了,根本就不想讓你去實例化,死心吧。
如何正確的去看待Math類的這個做法,即只是把類當做一個名字空間而已,滿足我們上面所說的,方法的調用和對象是否創建不需要建立聯系。
4.什么是重載?什么是構造函數重載?在java.lang.Math類中舉出函數重載的例子?怎么才能區分兩個重載函數?
重載在英文維基中是個多義詞,其中和計算機科學相關的就有:
我們這邊就來談談方法的重載!
方法重載在英文wiki中的定義:
In some programming languages, function overloading or method overloading is the ability to create multiple methods of the same name with different implementations.Function overloading
就是創建同名的但是有不同實現的多個方法就是重載!
我們日常生活中也可以隨處看到重載的影子,比如學習!學習語文、學習數學、學習英語、學習dota……
但是像下面這樣說,別人會覺得很蠢
“以學習語文的方法學習語文”;
“以學習數學的方法學習數學”
……
很顯然,我即使做一點省略,意思還是能夠很好的表達出來。相反我要是完整地進行表述,就會讓別人覺得很冗余。
構造函數重載,就是允許方法名相同而形式參數不同的構造器同時存在。因為在不同的情境下,我可能需要用不同的方式去初始化一個類,但是類名只有一個,而構造函數的名字只能是類名,所以構造函數也需要函數的重載!
Math類中函數重載的例子下面就有,寫了四個abs,針對四個類型,double、float、int、 long:
怎樣才能區分兩個重載函數,這兩個函數都具有相同的名字,Java如何知道我到底指的是哪一個嘞?
答案是參數列表!
不僅是參數類型的差異,而且參數順序的不同都可以區分出不同的方法。
那么返回值可不可以,比如:
void f() {}
int f() {return 1;}
那這樣怎么區分呢,或許可以搞一個int x = f();
這樣。那如果有賦值那就是第二個,沒有賦值的就是第一個。可是我們經常會這么編程:即使是一個返回值不為void,我們可能也不會把這個方法的返回值賦給任何變量,就像f();
這樣做沒有任何毛病,如果它本身只是一個返回值類型為int的函數,那最多編譯器就是給個warning而已。那么如果我們寫了這么兩個重載函數,那Java到底該如何確定用哪一個函數呢?(能怎么辦呢,我也很絕望啊!)所以根據返回值類型來區分重載方法顯然是不行的。
5.final修飾的變量不能改變,為什么如下代碼可以正常運行?
final int[] NUMBS= {1,2,3,4,5};
NUMBS[2] = 1;
這邊貼出一段話來回答
當對對象引用而不是基本類型運用final時,其含義會有一點令人迷惑。對於基本類型final使數值恆定不變;而用於對象引用,final使引用恆定不變。一旦引用被初始化指向一個對象,就無法再把它改為指向另一個對象。然而,對象其自身卻是可以被修改的,Java並未提供使任何對象恆定不變的途徑(但可以自己編寫類以取得使對象恆定不變的效果)。這一限制同樣適用數組,它也是對象。
——《Java編程思想》
這邊舉個栗子來類比一下
——“無論她將來是富有還是貧窮,或無論她將來身體健康或不適,你都願意和她永遠在一起嗎?”
——“是的,我願意。”
大概就是這么個感覺。
6.閱讀代碼EmployeeTest.java,回答:
1. 為什么其屬性均為private?這樣設計有什么好處?
2. 為Employee類增加一個無參構造函數,調用其有參構造函數,設置name為雇員1, salary為3000, hireDay的年月日為2017年3月5日。(粘貼代碼)
3. 為Employee類再增加一個有參構造函數,參數只有name與salary,hideDay為當前日期。(粘貼代碼)
- 屬性設置為private,是不希望用戶能夠隨意操作類的成員變量,或者希望用戶可以通過自己所指定的方法來操作類成員變量。尤其是對這個employee類來說,要是隨意加工資就算了,要是工資突然就被扣了,那不跟你急?
- 增加無參構造函數
直接調用現成的帶參數列表的構造函數就好了
public Employee(String n, double s, int year, int month, int day) {
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
// GregorianCalendar uses 0 for January
hireDay = calendar.getTime();
}
public Employee() {
this("雇員1", 3000.0, 2017, 3, 5);
}
再增加一個有參構造函數
//唯一的問題就是構造函數必須得放在第一行,所以這邊代碼就只能丑陋一點了
public Employee(String n, double s, int year, int month, int day) {
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
// GregorianCalendar uses 0 for January
hireDay = calendar.getTime();
}
public Employee(String n, double s) {
this(n, s, Calendar.getInstance().get(Calendar.YEAR),
Calendar.getInstance().get(Calendar.MONTH) + 1,
Calendar.getInstance().get(Calendar.DAY_OF_MONTH));
}
7.編寫函數public static boolean isMondayToFriday()
功能:判斷今天如果是周一到周五,直接輸出如“上班時間”並返回true,否則輸出“休息時間”。
提示:使用LocalDateTime, DayOfWeek
/*按着提示往下做就是了,沒什么好說的,
*就是這個getDayOfWeek()的方法不是int
*不過只要在用這個getValue()方法就好了
*所以沒什么差
*/
public static boolean isMondayToFriday() {
LocalDateTime localDateTime = LocalDateTime.now();
int day = localDateTime.getDayOfWeek().getValue();
if (day >= 1 && day <= 5) {
return true;
}
return false;
}
3.使用碼雲管理Java代碼
4.PTA實驗
- 第一題:
- this關鍵字是為了在方法的內部獲得對當前對象的引用。表示對調用方法的那個對象的引用。如果在方法內部調用同一個類中的另一個方法,則不需要使用this。
- toString()方法是用字符串來表示對象的一種方法,這個方法就是會返回一個用文本方式表達對象的字符串。推薦在所有的子類里面重載toString()方法。如果我們什么都不寫,那么默認就會返回這么一個字符串
getClass().getName() + '@' + Integer.toHexString(hashCode())
- setter/getter,設置器和訪問器。我們之前說到了,通常將成員變量聲明為private,是為了防止直接訪問成員變量而引起的惡意操作。但是,這並不是不允許訪問,我們可以通過setter和getter方法來完成。Eclipse可以自動生成getter和setter方法,在左上角Source菜單欄里。
- 第二題
- 這道題希望每一個雇員都有個不同的ID,那么我們可以設置在Person類里面設置一個靜態變量,然后每招來一個雇員,我們都把當前這個靜態變量的值賦給這個雇員,然后靜態變量再自增1。因為這個靜態變量是這個類所持有的,而不屬於任何一個雇員,作用其實就相當於全局變量。
- 如果有些事情要在.class加載后執行,那么就可以定義static塊,這樣只要.class加載進JVM中,默認就會執行該static塊。
第三題,這邊就簡要地講兩點
toString()和deepToString()的區別
Returns a string representation of the contents of the specified array.If the array contains other arrays as elements, they are converted to strings by the {@link Object#toString} method inherited from Object, which describes their identities rather than their contents.
toString()的實現
public static String toString(Object[] a) {
if (a == null)
return "null";
int iMax = a.length - 1;
if (iMax == -1)
return "[]";
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) {
b.append(String.valueOf(a[i]));
if (i == iMax)
return b.append(']').toString();
b.append(", ");
}
}
測試代碼:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int [][] a = {{1, 2}, {2, 3}};
int [] b = {1, 2};
System.out.println(Arrays.toString(a));
System.out.println(Arrays.toString(b));
}
}
然后運行結果是這樣的:
[[I@15db9742, [I@6d06d69c]
[1, 2]
對於一維數組打印出了我們所希望得到的東西,而對於一個多維數組(這邊是二維數組),我們也如注釋中所說的打印了從相應的類里所繼承的toString()方法,那么我們之前也說了,默認就是getClass().getName() + '@' + Integer.toHexString(hashCode())
這樣的格式,那這邊也是。
為了解決這樣的問題,另一個方法應運而生,它叫做deepToString()方法,方法如其名,它可以一直深入這個(多維)數組的內部,即便其維度會很高。
Returns a string representation of the "deep contents" of the specified array. If the array contains other arrays as elements, the string representation contains their contents and so on. This method is designed for converting multidimensional arrays to strings.
deepToString()的實現
//這邊就上一個簡單的,這個方法調用的那個同名方法其實挺復雜的
if (a == null)
return "null";
int bufLen = 20 * a.length;
if (a.length != 0 && bufLen <= 0)
bufLen = Integer.MAX_VALUE;
StringBuilder buf = new StringBuilder(bufLen);
deepToString(a, buf, new HashSet<Object[]>());
return buf.toString();
測試代碼:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int [][] a = {{1, 2}, {2, 3}};
System.out.println(Arrays.toString(a));
System.out.println(Arrays.deepToString(a));
}
}
然后運行結果是這樣的:
[[I@15db9742, [I@6d06d69c]
[[1, 2], [2, 3]]
當然就不一樣咯,deepToString()方法專門針對多維數組的。
要不我試試直接貼html的代碼?
import java.time.LocalDate;
public class Main {
public static boolean isMondayToFriday(String string) {
String[] strings = string.split("-");
int year = Integer.valueOf(strings[0]);
int month = Integer.valueOf(strings[1]);
int dayOfMonth = Integer.valueOf(strings[2]);
LocalDate localDate = LocalDate.of(year, month, dayOfMonth);
int day = localDate.getDayOfWeek().getValue();
if (day >= 1 && day <= 5) {
return true;
}
return false;
}
public static void main(String[] args) {
if (args.length > 0) {
String string = args[0];
if (isMondayToFriday(string)) {
System.out.println("上班時間");
} else {
System.out.println("休息時間");
}
}
}
}
import java.time.LocalDate;
public class Main {
public static boolean isMondayToFriday(String string) {
String[] strings = string.split("-");
int year = Integer.valueOf(strings[0]);
int month = Integer.valueOf(strings[1]);
int dayOfMonth = Integer.valueOf(strings[2]);
LocalDate localDate = LocalDate.of(year, month, dayOfMonth);
int day = localDate.getDayOfWeek().getValue();
if (day >= 1 && day <= 5) {
return true;
}
return false;
}
public static void main(String[] args) {
if (args.length > 0) {
String string = args[0];
if (isMondayToFriday(string)) {
System.out.println("上班時間");
} else {
System.out.println("休息時間");
}
}
}
}
運行結果如截圖所示:
