不该被忽视的CoreJava细节(一)
一、系列文章导言
《不该被忽视的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细节(一)的更多相关文章
- 不该被忽视的CoreJava细节(四)
令人纳闷的数组初始化细节 这个细节问题我很久以前就想深入研究一下,但是一直没有能够抽出时间,借这系列文章的东风,尽量解决掉这个"心头病". 下面以一维int数组为例,对数组初始化方 ...
- 不该被忽视的CoreJava细节(三)
一.不该被遗忘的移位位运算 本文主要介绍移位运算(Shift Operation), 适当介绍一下其它相关的位运算. 甭说计算机刚发明那会,就连21世纪初那段日子,计算机内存都是KB/MB计算的.编写 ...
- ASP.NET常被忽视的一些细节
原文:ASP.NET常被忽视的一些细节 前段时间碰到一个问题:为什么在ASP.NET程序中定时器有时候会不工作? 这个问题看起来很奇怪,代码好像也没错,但就是结果与预期不一致. 其实这里是ASP.NE ...
- C语言里面关于数组的一个容易忽视的小细节
ginobili@VM_44_28_sles10sp1:~/code> cat test3.cpp #include <stdio.h> int main(){ char a[5] ...
- 【原创】构建高性能ASP.NET站点之三 细节决定成败
原文:[原创]构建高性能ASP.NET站点之三 细节决定成败 构建高性能ASP.NET站点之三 细节决定成败 前言:曾经就因为一个小小的疏忽,从而导致了服务器崩溃了,后来才发现:原来就是因为一个循环而 ...
- 谈谈一些有趣的CSS题目(十二)-- 你该知道的字体 font-family
开本系列,谈谈一些有趣的 CSS 题目,题目类型天马行空,想到什么说什么,不仅为了拓宽一下解决问题的思路,更涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题 ...
- 谈谈一些有趣的CSS题目(十一)-- reset.css 知多少?
开本系列,谈谈一些有趣的 CSS 题目,题目类型天马行空,想到什么说什么,不仅为了拓宽一下解决问题的思路,更涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题 ...
- 谈谈一些有趣的CSS题目(三)-- 层叠顺序与堆栈上下文知多少
开本系列,讨论一些有趣的 CSS 题目,抛开实用性而言,一些题目为了拓宽一下解决问题的思路,此外,涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题中有你感觉 ...
- 谈谈一些有趣的CSS题目(一)-- 左边竖条的实现方法
开本系列,讨论一些有趣的 CSS 题目,抛开实用性而言,一些题目为了拓宽一下解决问题的思路,此外,涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题中有你感觉 ...
随机推荐
- Java Synchronized的原理
我们先通过反编译下面的代码来看看Synchronized是如何实现对代码块进行同步的: public class SynchronizedDemo{ public void method(){ syn ...
- Linux包管理
1.yum(Yellow dog Updater, Modified) yum是一个在Fedora(基于Linux的操作系统)和RedHat(基于Linux的操作系统)以及SUSE(基于Linux的操 ...
- 1.jQuery入口函数 与javaScript入口函数
1.jQuery入口函数 与javaScript入口函数 JQ入口函数: $(document).ready(function(){ }); 或者 $(function(){ }) Js入口函数: w ...
- .net core关于跨域及Cookie的部分问题
一.如何跨域 1.情景描述 目前有A站点和B站点.A站点有一个API接口为UserData接口,B站点希望可以通过ajax请求来获取A站点该接口数据. 2.后端修改 首先在ConfigureServi ...
- Spark 中的 RPC 的几个类
Spark 中 RPC 部分的涉及了几个类,有点晕,在此记录一下 1. RpcEndpoint: RPC的一个端点.给定了相应消息的触发函数.保证 `onStart`, `receive` and ...
- Codeforces Round #501 (Div. 3) 1015D Walking Between Houses
D. Walking Between Houses time limit per test 2 seconds memory limit per test 256 megabytes input st ...
- idea中使用Git对项目进行版本控制
- Restful 3 -- 序列化组件(GET/PUT/DELETE接口设计)、视图优化组件
一.序列化组件 基于上篇随笔的表结构,通过序列化组件的ModelSerializer设计如下三个接口: GET 127.0.0.1:8000/books/{id} # 获取一条数据,返回值:{} PU ...
- 01.Spring Ioc 容器
基本概念 Spring 的 Ioc 容器,通常也称应用上下文.它包含了两个概念 Ioc 和 容器: 容器:顾名思义就是用来装东西的,在 Spring 中容器里盛放的就是各种各样的 Bean.既然装了东 ...
- Washing Plates 贪心
https://www.hackerrank.com/contests/101hack41/challenges/washing-plates 给定n个物品,选这个物品,贡献 + p, 不选的话,贡献 ...