一、系列文章导言

《不该被忽视的CoreJava细节》系列文章将会持续更新。我希望自己通过这一系列文章的写作,能与读者一起进步,逐步完善对Java体系结构的了解。

二、本期关注点

几乎翻看每一本与Java相关的入门书籍,教你跟着敲的第一个程序都会像下面这段代码一样。

 public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
}
}

这个程序貌似很简单,很多人都不以为意。但是,请大家扪心自问一下,你真的有花时间研究过吗或者说真的理解了吗?

不过,请你大可放心,本文不是教你编译器编译这段程序机制,也不是教你字节码反编译后如何解读,更不会教你JVM如何处理这段程序以及类的装载机制。

本期只是想让大家从最基础的角度读懂

  public static void main(String[] args) {  }

这段代码,仅此而已。

三、main访问限定词趣闻

根据Java语言规范,main方法必须声明为public。注意,Java语言是Sun公司对于Java语言的官方文档,在Java语言方面具有最高的权威性。

但是,Java虚拟机规范并没有要求main方法一定是public

两者的迥异导致,有些Java解释器能够执行非public修饰main方法所在的程序。

不过,这个bug最终在JDK1.4中得到了修复,其后的所有main方法必须使用public作为访问修饰符。

下面我尝试使用JDK1.7编写此方法,将main方法的public修改成private

上图说明,至少在JDK1.7版本,这个bug已经被修复,无法使用非public修饰词修饰main方法。而且,我可以明确地告诉大家,1.7的虚拟机规范仍然没有指明必须这样做。

四、static修饰词、void返回值对main意味什么

先拿void开刀。如果在学过Java之前学过C语言,那么你一定会想起每次写main方法的时候都会写上这么一段话。

 int main() {
...
return 0;
}

表示程序会给操作系统返回一个状态码0,操作系统收到状态码0表示程序正常退出。

但是,我们的Java语言并没有返回值。也就是说,理论上Java程序并不会主动返回退出代码。

那么到底Java程序有没有返回状态代码呢?事实上,如果Java程序正常退出,则会返回默认的退出码0。

 public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
}
} // 程序正常退出,退出码如下图。

通过操作系统的errorlevel变量我们可以得到程序所返回的退出码。关于Java退出码需要再了解四点:

1)Java程序的退出码并不会返回给JVM,这也就是方法用void的原因所在。

2)Java程序的退出码返回给JVM所在的操作系统,正常退出默认返回给操作系统状态码值为0。

3)退出码其实是约定俗成的。一般约定[0,99]内整数代表正常退出,[100-199]代表警告退出,大于等于200代表异常退出,但是不同的操作系统却不同。在Java中,默认的正常退出码为0,异常退出或警告码为1。

 public class HelloWorld {
public static void main(String[] args) throws ClassNotFoundException{
System.out.println("Hello World");
Class.forName("");
}
} // 不声明异常,声明异常,退出码均为 1

4)可以自己设置退出状态码。Java调用java.lang.System.exit(int status)方法结束程序执行,并向操作系统返回status值。

 public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
System.exit(1000);
}
} // 向JVM所在操作系统返回自定义状态码

顺带提一句,如果在eclipse做实验并不会有状态码,或者说在eclipse跑程序,在命令行中状态码始终未0。其中差别始末,暂时未考虑清楚。

五、参数列表--变态考题

如果要出考题,前面的内容或许都用不到,基本没有什么可以出的题目。但是一提到main方法的参数列表,就有意思了。下面我就几个问题解答一下诸位可能存在的疑问。

1)public static void main(String[] s) {}是否正确?

咦,貌似和原来的有点区别,好像原来的形参是args。不明真相的群众可能一口咬定这句话是错误的,不巧的是,这句话确是没有任何无法问题的。

 public class HelloWorld {
public static void main(String[] s) {
System.out.println("Hello World");
}
}

这段代码能够正确地输出结果。其中的原因在于:原来的args仅仅是参数,编译器是不会检查形参的变量名是否与args一致的,只要有一个合法的名字,编译器就认为没有问题,运行也不会出错。更进一步说,这个参数就是在控制台输入参数后用于获得参数用的,无非就是数组名字改了而已。

如果编译程序的时候加上参数,比如javac HelloWorld.java 1 2 3

 public class HelloWorld {
public static void main(String[] s) {
System.out.println("Hello World");
System.out.println(s[0]); // 输出结果为1
}
}

2)public static void main(String args)是否正确?

这句代码编译器不能通过编译,缺少程序入口。

3)public static void main(Char[] args)是否正确?

这句代码编译器不能通过编译,缺少程序入口。

上面三种类型说明,必须存在《code>public static void main(String[] 任意合法变量标识符) {}方法。这也是Java官方文档所规定的,必须遵守。

六、奇怪,怎么有2个main方法?

为什么就不可能存在多个main方法?请看下面的例子。

 public class HelloWorld {
public static void main(String[] args) {
main("Hello World");
} public static void main(String arg) {
System.out.println("Hello World");
}
}

本例是通过函数的重载实现的。其实很容易想到,在同一个类中有多个相同方法名存在很可能是使用了Java重载机制(编译器只检查方法名和参数列表,如果存在不同,则认为是两个不同的方法,编译器认为语法成立)。

如果,一不小心写成下面这种样子,则会出现StackOverflowError异常,本质上是虚拟机方法栈帧存不下无穷递归过程的数据量。

 public class HelloWorld {
public static void main(String[] args) {
main("Hello World");
}
// 无递归出口,导致无穷递归
public static void main(String arg) {
main("Hello World");
}
}

七、结束语

读完文本,是否觉得自己原来自己还有很多漏洞没有弥补。别慌,我想说的是:Java只是一种工具,一种为我们服务的编程工具。人与工具之间的关系不应该主次颠倒,应该尽量让工具为我们写出高质量应用而服务,而不是在这已经发展了20多年的工具面前如同乞讨者一般盲目仰慕前辈做出的巨大贡献。

其实Java也就那样,掌握语言的核心知识,学点大牛写的API,那么随着时间的积累,代码量的增多,可能大家也可以作为第三方提供高质量的类库。鸡汤到此结束!

不该被忽视的CoreJava细节(一)的更多相关文章

  1. 不该被忽视的CoreJava细节(四)

    令人纳闷的数组初始化细节 这个细节问题我很久以前就想深入研究一下,但是一直没有能够抽出时间,借这系列文章的东风,尽量解决掉这个"心头病". 下面以一维int数组为例,对数组初始化方 ...

  2. 不该被忽视的CoreJava细节(三)

    一.不该被遗忘的移位位运算 本文主要介绍移位运算(Shift Operation), 适当介绍一下其它相关的位运算. 甭说计算机刚发明那会,就连21世纪初那段日子,计算机内存都是KB/MB计算的.编写 ...

  3. ASP.NET常被忽视的一些细节

    原文:ASP.NET常被忽视的一些细节 前段时间碰到一个问题:为什么在ASP.NET程序中定时器有时候会不工作? 这个问题看起来很奇怪,代码好像也没错,但就是结果与预期不一致. 其实这里是ASP.NE ...

  4. C语言里面关于数组的一个容易忽视的小细节

    ginobili@VM_44_28_sles10sp1:~/code> cat test3.cpp #include <stdio.h> int main(){ char a[5] ...

  5. 【原创】构建高性能ASP.NET站点之三 细节决定成败

    原文:[原创]构建高性能ASP.NET站点之三 细节决定成败 构建高性能ASP.NET站点之三 细节决定成败 前言:曾经就因为一个小小的疏忽,从而导致了服务器崩溃了,后来才发现:原来就是因为一个循环而 ...

  6. 谈谈一些有趣的CSS题目(十二)-- 你该知道的字体 font-family

    开本系列,谈谈一些有趣的 CSS 题目,题目类型天马行空,想到什么说什么,不仅为了拓宽一下解决问题的思路,更涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题 ...

  7. 谈谈一些有趣的CSS题目(十一)-- reset.css 知多少?

    开本系列,谈谈一些有趣的 CSS 题目,题目类型天马行空,想到什么说什么,不仅为了拓宽一下解决问题的思路,更涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题 ...

  8. 谈谈一些有趣的CSS题目(三)-- 层叠顺序与堆栈上下文知多少

    开本系列,讨论一些有趣的 CSS 题目,抛开实用性而言,一些题目为了拓宽一下解决问题的思路,此外,涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题中有你感觉 ...

  9. 谈谈一些有趣的CSS题目(一)-- 左边竖条的实现方法

    开本系列,讨论一些有趣的 CSS 题目,抛开实用性而言,一些题目为了拓宽一下解决问题的思路,此外,涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题中有你感觉 ...

随机推荐

  1. 一个使用MVC3+NHibernate “增删改查” 的项目(修正版)

      前言: 谈到NHibernate大伙并不陌生,搞Java的更是清楚,Hibernate是一个目前应用的最广泛的开放源代码的对象关系映射框架,它对Java的JDBC(类似于ADO.Net)进行了非常 ...

  2. vue + eCharts 实现图表展示

    一.首先安装 eCharts 依赖 npm install echarts -S 二.main.js 引入 eCharts 依赖 2.1)在 main.js 中引入 import echarts fr ...

  3. git 仓库拆分方案对比

    此文已由作者张磊授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 前言 git 拆分仓库在网上已有的案例上来看,分为 submodule 和 subtree. 还有基于这两个方 ...

  4. 项目接入apm后错误报警总结

    此文已由作者张磊授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 前言: 后端服务一般都有监控措施,一般可以及时发现线上错误,但是很多项目的前端却没有线上报警服务,即使有错误, ...

  5. filter、map、reduce区别

    1.filter filter(function,sequence)-->list,tuple or string 1)       参数func是自定义的过滤函数,在函数func(item)中 ...

  6. ZOJ - 3777(状压dp)

    The 11th Zhejiang Provincial Collegiate Programming Contest is coming! As a problem setter, Edward i ...

  7. 某人视频中提到的 Spark Streaming 优化的几点事项

    某人,并未提他的名字,是因为看的视频是1年前的,视频里他吹得厉害.我看视频时,查了一下他在视频里说的要做到的东西,结果上网一查,就看到了很多人说他骗了钱后,就不管交了学费的人了.真假无从查起.但是无风 ...

  8. cogs 915. 隐藏口令

    915. 隐藏口令 ★★☆   输入文件:hidden.in   输出文件:hidden.out   简单对比时间限制:1 s   内存限制:128 MB USACO/hidden(译 by Feli ...

  9. cropper.js裁剪图片的使用

    这两天难得有时间可以整理一下最近学习的东西,这两天项目中用到了头像上传裁剪的功能,这里只介绍头像的裁剪吧. 单独实现图片剪裁的功能还是挺容易的,入门级别的.看一遍官方给的文档,基本上就明白了.大家如果 ...

  10. jmeter-CSV Data Set Config

    在使用Jemeter测试的时候,往往需要参数化用户名,密码以到达到多用户使用不同的用户名密码登录的目的.这个时候我们就可以使用CSV Data Set Config实现参数化登录: 首先通过Test ...