Java - 软件界的擎天柱
写在前面
本文为那些年我们追过的语言之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都有对象,但是经常找不到对象。
Java转载请注明出处