《Java解惑》书摘

例子1:关于char数组的输出
 1 System.out.println("H" + "a");//输出:Ha
 2 System.out.println(‘H‘ + ‘a‘);//输出:169
 3 System.out.println("" + ‘H‘ + ‘a‘);//输出:Ha
 4 System.out.println("//////////////////");
 5 System.out.println("2 + 2 = " + 2 + 2);//输出:2 + 2 = 22
 6 System.out.println("2 + 2 = " + (2 + 2));//输出:2 + 2 = 4
 7 System.out.println("//////////////////");
 8 char[] char1 = {‘1‘, ‘2‘, ‘3‘};
 9 Object char2 = new char[] {‘1‘, ‘2‘, ‘3‘};
10 System.out.println(char1);//输出:123
11 System.out.println(char2);//输出:[[email protected]
12 System.out.println("test-" + char1);//输出:test-[[email protected]
13 System.out.println("test-" + char1.toString());//输出:test-[[email protected]
14 System.out.println("test-" + String.valueOf(char1));//输出:test-123
15 System.out.println("//////////////////");

例子2:
 1 Random random = new Random();
 2     StringBuffer sBuffer = null;
 3     switch(random.nextInt(2)) {
 4         case 1:sBuffer = new StringBuffer(‘A‘);
 5         case 2:sBuffer = new StringBuffer(‘B‘);
 6         default:sBuffer = new StringBuffer(‘C‘);
 7     }
 8     sBuffer.append(‘a‘);
 9     sBuffer.append(‘b‘);
10     sBuffer.append(‘c‘);
11     System.out.println(sBuffer);//输出:abc
12 }

这段程序有三个BUG:

  1. Random.nextInt(int n)方法的使用错误:
    先看下这个方法的API:
    public int nextInt(int n)
    返回一个伪随机数,它是取自此随机数生成器序列的、在 0(包括)和指定值(不包括)之间均匀分布的 int 值。
    所以,此处应改为:switch(random.nextInt(3)) {
  2. case子句中没有break语句:
    导致前面的赋值被后面的赋值覆盖。
  3. StringBuffer类的构造方法使用错误:
    StringBuffer不支持传入参数为字符(char)的构造方法,应改为传入字符串(String),
    所以,应改为:sBuffer = new StringBuffer("A");

例子3:会死循环的代码
 1  //死循环1
 2         int start1  = Integer.MAX_VALUE-1;
 3         for(int start7=start1; start7<=start1+1; start7++) {
 4             System.out.println("loop..");
 5         }
 6         //死循环2
 7         double start2 = 1.0 / 0.0;
 8         double start3 = Double.POSITIVE_INFINITY;//与上句等价,可替代
 9         while(start2 == (start2+1)) {
10             System.out.println("loop..");
11         }
12         //死循环3
13         double start4 = 0.0 / 0.0;
14         double start5 = Double.NaN;//与上句等价,可替代
15         while(start4 != start4) {
16             System.out.println("loop..");
17         }
18         //死循环4
19         String start6 = "test";
20         while(start6 != (start6+0)) {
21             System.out.println("loop..");
22         }
23         //死循环5
24         byte start7 = -1;//死循环
25 //      short start7 = -1;//死循环
26 //      int start7 = -1;//循环32次
27 //      long start7 = -1;//循环64次
28         int count = 0;//循环次数
29         while(start7 != 0) {
30             start7 >>>= 1;
31             System.out.println("loop..");
32             count++;
33         }
34         System.out.println("循环次数 = " + count);
35         //死循环6
36         int start8 = Integer.MIN_VALUE;
37 //      Long start8 = Long.MIN_VALUE;
38         while(start8 != 0 && start8 == -start8) {
39             System.out.println("loop..");
40         }

补充说明:

  1. 关于Infinity和NaN(死循环2和3)

     1 double d = 1.0 / 0;
     2 System.out.println(d);             //输出:Infinity
     3 System.out.println(d + 1);         //输出:Infinity
     4 System.out.println(d == d + 1);    //输出:true
     5
     6 d = 0.0 / 0;
     7 System.out.println(d);             //输出:NaN
     8 System.out.println(d + 1);         //输出:NaN
     9 System.out.println(d == d + 1);    //输出:false
    10
    11 System.out.println(Double.NaN == Double.NaN);//输出:false
    12 Double a = new Double(Double.NaN);
    13 Double b = new Double(Double.NaN);
    14 System.out.println(a.equals(b));//输出:true
  2. 关于Java的自动窄化原生类型转换(死循环5)
    分析 short start7 = -1; 和 start7 >>>= 1;这两句代码:
    在执行移位操作时,第一步是将start7提升为start7nt类型,因为所有的算术操作都会对short,byte,char类型的操作数执行这样的提升;
    这种提升是拓宽了原生类型,没有信息的损失,执行的是符号扩展,start7的值提升后是0xffffffff,start7>>>1之后得到的是0x7fffffff;
    将这个值再存入start7中的时候,为了将int数值存入short变量,Java自动执行了可怕的窄化原生类型,直接把高16位截掉,剩下(short)0xffff,又回到了起点,从而导致了死循环。
    这提醒了我们,不要在short,byte,char类型上使用复合赋值操作符,很容易出问题,而且这种隐性的窄化类型转换,在丢失信息的同时,结果也可能是灾难性的。
  3. 关于Java中的特殊值(死循环6)
    Java使用2的补码的算术运算是不对称的。
    对于每一种有符号的整数类型(byte、short、int和long),负的数值总是比正的数值多一个,这个多出来的数值总是这个类型所能表示的最小值;
    对Integer.MIN_VALUE取负值并不会改变它的值,对这个值取负值将会产生溢出,但是Java自动忽略了这种溢出;同理对Long.MIN_VALUE等也是如此。
 
 
时间: 2024-11-06 20:27:21

《Java解惑》书摘的相关文章

【书摘】《Node.js从入门到精通》

1. npm 安装其他包 npm install  [module_name]     全局安装 npm install  -g  [module_name] 查看包文档   npm doc  [module_name] 查看bug      npm bug  [module_name] 查看源码      npm edit  [module_name] 2. 用package.json 来保存管理项目引用的包的依赖关系.避免一个一个的安装. 3. Npm : http://npmjs.org/

Node.js开发入门—语音合成示例

出于项目需要,搞了一个语音合成(TTS)的小示例,使用的是OKVoice. 我想在PC上测试,OKVoice的快速接入API可以实现我的目的,文档在这里:http://dev.okvoice.com/file.php. 直接上代码吧,okVoiceTts.js,内容如下: var http = require('http'); var fs =require('fs'); var crypto = require('crypto'); var util = require('util'); va

Node.js开发入门—使用对话框ngDialog

做网站经常会遇到弹出对话框获取用户输入或弹出对话框让用户确认某个操作之类的情景,有一个基于AngularJS的扩展模块可以帮我们优雅地完成这类事情:ngDialog. ngDialog在github上提供了一个示例网页,演示了它的各种用法,在这里:https://github.com/likeastore/ngDialog/blob/master/example/index.html.ngDialog的github主页的readme也对常用的指令和服务做了较为详细的介绍,可以参考.我这篇就纯粹是

Node.js开发入门—Stream用法详解

Stream是Node.js中非常重要的一个模块,应用广泛.一个流是一个具备了可读.可写或既可读又可写能力的接口,通过这些接口,我们可以和磁盘文件.套接字.HTTP请求来交互,实现数据从一个地方流动到另一个地方的功能. 所有的流都实现了EventEmitter的接口,具备事件能力,通过发射事件来反馈流的状态.比如有错误发生时会发射"error"事件,有数据可被读取时发射"data"事件.这样我们就可以注册监听器来处理某个事件,达到我们的目的. Node.js定义了R

Node.js开发入门—Angular简单示例

在"使用AngularJS"中,我们提到了如何在Node.js项目中引入AngularJS,这次提供一个非常简单的示例,演示AngularJS里的指令.数据绑定.服务等内容. 我准备做Web后台管理系统,不同的管理员会有不同的权限,管理员登录后看到的菜单和他的权限有关,能看到什么,是动态生成的(类似RBAC).本文的示例从这个项目而来,当然,现在还是最简单的. 如果没有特别说明,后面我们用到的示例都使用express generator生成. Angular小demo 先搞起来吧. 第

Node.js开发入门—UDP编程

Node.js也提供了UDP编程的能力,相关类库在"dgram"模块里. 与TCP不同,UDP是无连接的,不保障数据的可靠性,不过它的编程更为简单,有时候我们也需要它.比如做APP的统计或者日志或者流媒体,很多流媒体协议都会用到UDP,网上一搜一大堆. 使用UDP,如果你要发送数据,只需要知道对方的主机名(地址)和端口号,扔一消息过去即可.至于对方收不收得到,听天由命了.这就是数据报服务,类似快递或邮件. 我们这次来介绍一下Node.js里的UDP编程,我会提供一个UDP版本的echo

Node.js开发入门—使用http访问外部世界

Node.js的http模块,不但可以构建服务器,也可以作为客户端类库来访问别的服务器.关键就在两个方法: http.request(options[,callback]) http.get(path[,callback]) 除了http,还会用到FileSystem模块和Stream中的stream.Readable和stream.Writable. 先来大概介绍一下相关API吧. API解释 http.request()方法接受一个options参数,这个参数可以是对象,用来指明你要访问的网

Node.js开发入门—套接字(socket)编程

Node.js的net模块提供了socket编程接口,方便我们利用较为底层的套接字接口来实现应用协议.这次我们看一个简单的回显服务器示例,包括服务端和客户端的代码. 使用JavaScript也可以进行套接字编程,哈哈,这酸爽! 代码 分服务器和客户端两部分来说吧. echoServer代码分析 echoServer.js: var net = require("net"); // server is an instance of net.Server // sock is an ins

Node.js开发入门—notepad++ for Node.js

对于Node.js开发,论IDE的话,Webstorm是不二的选择,但它是收费的(可免费使用30天).一开始,我们先将就一下,使用notepad++来编写Node.js应用.这样做还有一大好处:没有关于Node.js的代码高亮和自动补全,可以更好地敦促我们使用在线API文档记忆各种类库API.死磕自己吧,enjoy it. notepad++的安装与配置 到"https://notepad-plus-plus.org/download/v6.8.2.html"这里下载吧,6.8.2版本

Node.js开发入门—使用AngularJS内置服务

在上一篇,"AngularJS简单示例"中演示了一个非常简单的使用Angular的小demo,那篇已经太长,原本要介绍的一些内容只好单另开篇了.这些内容,就是如何使用Angular服务. 我们还是基于"AngularJS简单示例"中的示例来改造一下.新的示例,能从Node.js+Express构造的服务器上获取管理菜单.为了实现这个,需要做几部分改造: 服务器提供adminMenu的下载功能,需要修改app.js,处理路由 修改Angular实现的控制器x-cont