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

轉(zhuǎn)載請(qǐng)注明出處