寫在前面
本文為那些年我們追過的語言之Java篇。Java是一門使用廣泛的向對象開發語言,用于開發應用程序的技術,通用高效安全。由于Java并非我的專長,本文由@Moonbal 主筆,我僅做完善補充。
學點Java
Java開發分為三個方向:
- J2SE(標準版):桌面應用程序開發/網絡管理/電信
- J2EE(企業版):Web開發/電子商務/安全網站
- J2ME(移動版):手機游戲
Java資源推薦:
-
Thinking in Java
經典教程1 -
Effective Java
經典教程2 -
Java 8 in Action: Lambdas, streams, and functional-style programming
Java從Java8也開始支持函數式編程了。對于函數式編程,它是一種編程思想,在Java中使用函數式編程是為了使代碼更簡潔,且更可讀。 -
深入理解Java虛擬機
JVM是Java的核心與基礎,有人說,沒有學習過JVM卻說自己“精通Java”的同學就是在耍流氓。 -
尚硅谷
網站視頻包括《Java 基礎》、《Web 開發》、《JavaEE 框架》以及《Android 開發》,并且都是可以免費下載的。如果要學習建服務器和做項目,這里的內容蠻齊全的。 -
ImportNew
這里有很多關于 Java 的優秀文章,基本每天都會有更新。
Mac中的Java開發
下面是OS X 10.10系統環境下,Java Web開發的環境配置與實現。
下載 Apache Tomcat(點擊
Binary Distributions
下面第一行的zip
)切換到解壓后的Tomcat目錄下,執行下列命令:
# 當前在 apache-tomcat 目錄下
cd bin
sudo chmod 755 *.sh
./startup.sh # 打開服務。使用 ./shutdown.sh 可關閉服務
在 eclipse 中配置 apache-tomcat 服務(這一步是讓 eclipse 知道有 apache-tomcat 服務,類似于創建類):快捷鍵
cmd + ,
-> 選擇左邊的Server
-> 選擇Runtime Environments
-> 點擊右上角Add...
-> 選擇安裝的 apache-tomcat 版本 -> 點擊next
-> 點擊Browse...
選擇 apache-tomcat 的根目錄 -> 點擊Installed JREs...
選擇正確的 JRE -> 點擊finish
參考eclipse中如何新建tomcat服務,在 eclipse 中新建 tomcat 服務(這一步是在 eclipse 中創建并打開服務,類似于創建實例)。如果服務沒有開啟(顯示 Stopped),右鍵單擊服務,選擇
Start
。如果在控制臺中打開了 apache 服務,則 eclipse 會提示:8080 端口被占用。需先在控制臺關閉 apache 服務,再回到 eclipse 打開服務。選擇
File
->New
->Dynamic Web Project
-> 輸入工程名,創建好工程后 -> 在工程目錄的WebContent
目錄下新建一個 html 文件 -> 右鍵該文件,選擇Run As
,點擊Run On Server
-> 選擇后點擊finish
。這樣,就能在 eclipse 自帶的瀏覽器中看到新建的 html 網頁了,網址的格式為:http://localhost:8080/工程名/文件名。所以說工程目錄的WebContent
目錄就是網站的所有網頁文件的保存目錄了。剛剛只是寫了個靜態 html 頁面。接下來就要開始編寫服務端,Servlet 客戶端與服務端交互的中樞。客戶端的請求要發給服務端的 Servlet,并在 Servlet 中處理后返回響應給客戶端。打開工程目錄下的
Java Resources
,該目錄下的src
目錄,它將保存工程的所有 Java 文件。雙擊src
-> 選擇New
-> 選擇Class
,輸入Name:
MyServlet
。復制下面代碼到該文件中,然后按快捷鍵cmd + shift + o
導入所需包。
@WebServlet(urlPatterns = {"/yogy.cc"})
// 它的作用是 HTTP 請求的 path 為 /yogy.cc 會把請求映射到這個 Servlet 做處理
// 最前面的 / 代表工程目錄
public class MyServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException
{
// 請求的相關信息都可在 request 里找到
// 要對客戶端做出的響應通過 response 實現
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
// 返回 JSON 數據直接返回 JSON 字符串
out.println("<html><head>");
out.println("<head>");
out.println("<title>MyServlet</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Servlet MyFirstServlet at " + request.getContextPath() + "</h1>");
out.println("</body></html>");
} finally {
out.close();
}
}
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
//Do some other work
}
}
雙擊該文件點 Run As
,點擊 Run On Server
-> 選擇后點擊 finish
。仔細觀察瀏覽器中鏈接的路徑,并修改上面類文件,嘗試不同結果。如果你有其他網絡編程的經驗,你應該很輕松就明白了客戶端和服務端是怎么交互的。如果想繼續學習 Java 網絡編程,請參考 尚硅谷視頻鏈接。
黑魔法
1. Hello World
So Easy?那可不一定,請看下面代碼。
import java.util.Random;
public class Yogy {
public static void main(String[] args) {
System.out.println(helloWorld());
}
public static String randomString(int s) {
Random ran = new Random(s);
StringBuilder sb = new StringBuilder();
for (int k; (k = ran.nextInt(27)) != 0; ) {
sb.append((char)('`' + k));
}
return sb.toString();
}
public static String helloWorld() {
return randomString(-229985452) + " " + randomString(-147909649);
}
}
明明是在程序里使用了java.util.Random()
函數產生隨機數,為什么每次打出的結果都是Hello World?請看Stackflow上有趣的討論。
2. Integer 與 int
import java.lang.reflect.Method;
import java.util.Date;
public class Yogy {
private static int MAXNUM = 1000000000;
public static void main(String[] args) {
getRunningTime("useAutoboxing"); // useAutoboxing 執行10.65s
getRunningTime("notAutoboxing"); // notAutoboxing 執行 1.471s
}
public static void getRunningTime(String methodName) {
try {
Method method = Yogy.class.getMethod(methodName);
long stTime = new Date().getTime();
method.invoke(null);
System.out.println(methodName + " 執行" + (new Date().getTime() - stTime) / 1000. + "s");
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static void useAutoboxing() {
Integer sum = 0;
for (int i = 1000; i < MAXNUM; ++i) {
sum += i;
}
}
public static void notAutoboxing() {
int sum = 0;
for (int i = 0; i < MAXNUM; ++i) {
sum += i;
}
}
}
我們發現使用Integer的確比int慢了很多,因為在useAutoboxing
中,sum + = i
;實際上執行的是 int result = sum.intValue() + i; sum = new Integer(result);
,生成了很多臨時Integer對象。不僅Integer要轉換成int執行操作,還要增加GC垃圾回收的代價,所以在含有大量操作的時候,盡量使用基本數據類型代替包裝類。
3. hashCode()和equals()
import java.util.HashSet;
class Monkey {
private String nickName;
private String language;
public Monkey(String name, String lang) {
this.nickName = name;
this.language = lang;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((language == null) ? 0 : language.hashCode());
result = prime * result
+ ((nickName == null) ? 0 : nickName.hashCode());
return result;
}
}
public class Yogy {
public static void main(String[] args) {
HashSet<Monkey> myFriends = new HashSet<Monkey>();
// 當我和Yogy成為朋友時,她使用的還是C++
Monkey yogy = new Monkey("Yogy", "C++");
myFriends.add(yogy);
// 現在Python已成為她的最愛
yogy.setLanguage("Python");
// 但當我再次遇到Yogy時,我卻不能從我的腦海中找到她,我發現我失憶了
System.out.println(myFriends.contains(yogy)); // 輸出false
}
}
到底發什么什么呢?在Java中,對象都是引用類型的數據,myFriends
中保存的是yogy
對象的引用,所以當yogy
改變時,myFriends
中的對象也會改變,但我卻為什么不能想起她?理由是Monkey
中重寫了hashCode()
方法,并且字段language
也參與了hashCode
的生成。打印出yogy
使用C++時的hashCode
為4800998,而yogy
使用Python時的hashCode
為1563076845,在哈希表的實現中如果兩元素hashCode
不等,則直接認為兩元素不等。
在上例中,刪除Monkey
中對hashCode
重載,我能想起yogy。但是如果重新new Monkey(“Yogy”, “Python”)
,我還是會失憶。因為默認的hashCode
是對象在內存中地址,重新new的對象和以前對象的地址不同,hashCode
也會不同。因此要重寫hashCode
方法,使得只有字段nickName
參與hashCode
的生成,代碼如下。
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((nickName == null) ? 0 : nickName.hashCode());
return result;
}
按照開始的版本,我能想起yogy。但對于new Monkey(“Yogy”, “Python”)
還是不行,還需要重寫equals()
方法,默認的equals()
也是比較兩個對象的地址是否相等。重寫equals()
使得只有nickName
參與相等類型的比較。這樣我就能通過看到Yogy想起我的朋友了。
結束語
JAVA都有對象,但是經常找不到對象。
轉載請注明出處