微信
手机版
网站地图

圣女果,胃癌-瞭望远方,远方的海岛,旅行方式

2019-05-16 12:15:31 投稿人 : admin 围观 : 262 次 0 评论



作者:菜鸟逆袭链接:https://mp.weixin.qq.com/s/YfLH1MXEYedURMfNVEpAoQ

1.奇数性

看下面代码时分是否能判别参数 i 是奇数?

public static boolean isOdd(int i){ 
return i % 2 == 1;
}

答案是: No!

看似正确的判别奇数, 可是假如 i 是负数, 那么它回来值都是false

形成这种现象的是 => 从思想上固化, 以为奇数只在正数规模, 故判别负数将报错, 在C++中也是, 负数取余仍是负.

在Java中取余操作界说发生的成果都满意下面的恒等式:

int数值a, 与非零int数值b 都满意下面的等式:
(a / b) * b + (a % b) == a

从上面就能够看出, 当取余操作回来一个非零的成果时, 左右操作数具有相同的正负号, 所以当取余在处理负数的时分, 以及会考虑负号.

而上面的这个问题, 处理办法便是防止判别符号:

public static boolean isOdd(int i){ 
return i % 2 != 0;
}

让成果与0比较, 很简略防止正负号判别.

考虑:

1.在运用取余操作的时分要考虑符号对成果的影响

2.在运算中, 测验运用0处理符号问题, 在必定程度上防止符号对成果的影响

2.浮点数发生的差错

看下面代码会打印出什么样的成果?

public class Change{ 
public static void main(String args[]){
System.out.println(2.00 - 1.10);
}
}

从片面上看, 打印的成果必定是0.90, 然后这却是一个片面过错.

关于1.10这个数, 核算机只会运用近似的二进制浮点数表明, 发生精度影响.

从上面的比方中来看, 1,10在核算机中表明为1.099999, 这个1.10并没有在核算机中得到精确的表明.

针对这个精度问题, 咱们或许会挑选: System.out.printf("%.2f%n", 2.00 - 1.10);处理, 虽然打印出来的是正确答案, 可是仍旧会暴露出一个问题: 假如精度控制在2.00 - 1.0010; 圣女果,胃癌-眺望远方,远方的海岛,游览办法那么精度差错仍旧会呈现.

这儿也阐明晰: 运用printf, 核算机底层仍旧是运用二进制的办法来核算, 只不过这种核算供给了更好的近似值罢了.

那么应该怎样处理这个问题呢?

首要想到是运用int模仿小数每一位, 然后核算, 终究将成果又转化为小数;

以此想到的便是运用BigDecimal类, 它首要用于精确小数运算.

import java.math.BigDecimal; 
public class Change1{
public static void main(String args[]){
System.out.println(new BigDecimal("2.00").subtract(new BigDecimal("1.10")));
}
}

经过上面的代码就能得到一个精确的值.

注: 运用BigDecimal的时分, 不要运用BigDecimal(double d)的结构办法, 在double与double之间传值的时分仍旧会引起精度丢掉. 这是一个严峻的问题.

BigDecimal底层选用的便是int[], 运用String的时分, 会将String不断取每一位存入int[], 运用double的时分, 同理将数字的每一位存入int[], 可是double自身存在差错, 导致存入的数据会呈现差错,例: 0.1存入double就表明为0.1000000099999999, 因而不运用double类型的结构函数

考虑:

当然关于精确要求不高的当地, 完全能够运用float/double, 可是关于要求精度的核算, 比方钱银 必定要运用int, long, BigDecimal.

3.长整数形成数据溢出

看下面的代码会打印什么?

public class LongDivision{ 
public static void main(String args[]){
final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000;
final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
System.out.println(MICROS_PER_DAY/MILLIS_PER_DAY);
}
}

整个进程, 除数与被除数都是long型, 很简略保存这两个数, 成果必定是1000, 可是成果让你绝望了, 成果是5.

这又是怎样回事呢?

首要这个表达式: 24606010001000总是在int类型的基础上进行核算. 即表达式是依照int的规矩核算.

很简略看出这个表达式核算的规模早已超出int的取值规模, 纵然运用long去存储核算成果, 可是在核算的进程中就现已呈现核管用据溢出, 这是一个躲藏过错.

Java方针确认类型的特性 => 如上比方, 不同经过 long 去确认24606010001000依照long进行存储.

有必要指定数据类型, 才干依照指定的规矩进行运算.

就用前面这个比方来看, 当指定24为24L就能防止数据核算溢出, 在进行乘法运算的时分就现已是在long的基础上进行核算.

public class LongDivis国美榜首城邮编ion{ 
public static void main(String args[ ]){
final long MICROS_PER_DAY = 24L * 60 * 60 * 1000 * 1000;
final long MILLIS_PER_DAY = 24L * 60 * 60 * 1000;
System.out.println(MICROS_PER_DAY/MILLIS_PER_DAY);
}
}

考虑:

这个问题给了我一个深入的经验, 当操作数很大的时分, 要防备操作数溢出, 当无法确认核管用会不会溢出母亲和孩子, 所要做的便是用贮存规模最大的类型: long 来进行核算。

4.long的 "L" 与 "l" 所引发的过错

从上面 "长整数运算形成数据溢出" 引发又一个问题, 看下面比方:

public class Elem卢凡entary{ 
public static void main(String[] args){
System.out.println(12345+5432l);
}
}

乍一看, 这很简略, 核算成果时是 6666, 可是打印的成果是 17777, 我开端头晕了, 这很不合理.

考虑往后, 发现了一个问题:

我把 "l" 看作是 "1", "l" 仅仅用于标识5432是一个long类型, 这个视觉上的过错将会引发更严峻的问题.

考虑:圣女果,胃癌-眺望远方,远方的海岛,游览办法

小写字母 l 与 1 很简略形成混杂, 为了防止这种过错, 在表明long类型数据御天刀帝的, 要做的便是将 "l" 换做 "L", 掐断发生紊乱的源头.

5.多重类型转化引发的数值改变

看这样的一个比方:

public class Multicast{ 
public static void main (String[] args){
System.out.println((int)(char)(byte) -1);
}
}

看似成果是 -1, 可是运转之后, 成果变为 65535

剖析一下:

byte下的-1 => 变为: 
1111,1111,1111,1111,1111,1111,1111,1111
32位(4个字节) 首位1表明负号.
byte到char => 变为:
0000,0000,1111,1111
16位(2个字节),首位0, 就此负号变正号.
char到int => 变为:
0000,0000,0000,0000,0000,0000,1111,1111
32位(4个字节)

由此可见, 在byte到char的改变进程中呈现符号转化的问题. char首位总是0使得负号变正号.

类型转化的进程存在这样的简略规矩: 假如开始的数值类型是有符号的,那么就履行符号扩展;假如它是 char,那么不论它将要被转化成什么类型,都履行零扩展。因而这也就解说了为什么byte到char的进程存在负号变正号.

为了圣女果,胃癌-眺望远方,远方的海岛,游览办法在转化的进程中保存符号, 就运用位掩码进行约束, 例如:

char c = (char)(b & 0xff); 

这样就能确保符号具有保存

考虑:

在对有符号与无符号之间的转化, 必定要注意上面的转化规矩, 假如不能确认转化符号是否正确, 那么就防止呈现有符号到无符号之间的转化.

6.防止所谓聪明的编程技巧

关于交流两个变量的内容, 在C/C++中存在一种这样的编程技巧:圣女果,胃癌-眺望远方,远方的海岛,游览办法

int x = 1111;
int y = 2;
x^=y^=x^=y;
cout<<<" "<

这样一个简略的接连异或就能处理变量的交流问题, 这种写法在很久以前是为了削减暂时变量的运用, 所以这种做法在现在也得到了保存.

首要看这样一个问题, 表达式x^=y, 在C/C++的编译器中是先核算出y的值, 然后再获取x的值, 终究再核算表达式. 但在Java中的做法是先取得x的值, 再取得y的值, 终究再核算.

Java的言语标准描绘: 操作符的操作数是从左往右求值.

这使得在核算 x^ =y^ =x^ =y表达式中的第二个x的时分是在核算x^ =y之前的值( x的值仍旧是1111 ), 并不是x^=y后的值, 这就导致了核算上的过错.

所以在Java中精确的写法是:

y = ( x^=( y^=x ) )^y

考虑:

上面的这种写法极端简略引起过错, 程序的可读性遭到很大的影响, 所以在写代码的时分要考虑一个问题, 除非编译器能确炖肉记定操作数的运算次序, 不然不要让编译器去确认操作数的核算次序, 就比方这样的表达式: x=a[i]++-a[j]++. 很简略导致过错.

7.防止运用混合运算

看如下代码:

public class DosEquis{ 
public static void main(String[] args){
char x = 'X';
int i = 0;
System.out.println(true ? x : 0);
System.out.println(false ? i : x);
暴君的逃婚皇后}
}

看似将打印: XX, 可是成果却是X88.

这是一个出人意料的成果.

考虑之后, 将或许得出这样的定论: 呈现这样问题的原因是操作数的类型主动提高, char=>int.

可是又有一个问题便是为什么榜首个运算不是88. 找其根本原因仍是在于条件表达式的运算规矩:

A ? B : C
B, C为相同类型, 那么表达式的核算成果便是B, C的类型
B, C不是相同类型的时分, 那么核算成果就依照B的类型(此刻B有必要是式子中最高类型).
此刻C的类型就主动上升为式子中最高的类型, 例: false ? x : i, 输出是0, 而不是0对应的字符.

上面的规矩决议了, 将调用哪enthusiam一个print的重载函数.

这种条件表达式回来值, 很简略受B, C类型影响. 当依据返dissappear回值作条件判别的时分, 这种性质也将导致一个严峻的问题.

考虑:

上面的问题阐明晰, 在条件表达式中, 终究再后两个操作数运用相同类型的操作数, 以此防止回来值类型不确认的问题, 并且在其他的表达式核算中, 必定要理清楚数值之间的类型转化.

8.发现躲藏的类型转化

在这样的表达式: x += i; 依照往常的了解, 它必定是x = x + 安瑟十三i; 可是这样的运算表达式是建立在x与i具有相同的类型的基础上的, 假如当x, i类型不相同的时分, 将会引发一个问题便是精度丢掉.

就比方:

short x = 0;
int i = 99999;
x += i;

现在的x不是99999, 而是-31073.

当 x += i 的时分, 呈现的问题便是i主动转型为short, 此刻x的值就不再是99999. 而当你将表达式写为: x = x + i 的时分, 这是一种显式的转型, 天然需要强转操作. 然后防止了躲藏的类型转化.

考虑:

复合运算会躲藏呈现转型操作, 这种转型操作很有或许呈现精度丢掉.

所以在进行复合运算的时分, 防止两头的操作数是不同的类型, 防止编译器呈现风险的窄化类型, 或许不运用复合运算, 人为进行类型转化.

9.字符串的"+"运算符刘世龙和刘尚娴的婚姻

看如下代码:

public class LastLaugh{
public static void main(String[] args){
System.out.print("H"+"a");
System.out.print('H'+'a');
}
}

因为长时间受 "+" 运算符的影响, 上面的代码, 很简略把 'H'+'a' 也看作是字符串的衔接, 这是一种惯性的考虑办法.

在 'H'+'a' 表达式的运算中, 是将 'H', 'a', 上升为int, 进行数值运算.

假如想让两个字符衔接在一蔡雄英起, 能够选用:

1.运用 StringBuffer/StringBuild 做 append 运算.
StringBuild s = "".app村医闯全国end('H');
2.运用String s = "" + 'H' +'a'; 运用字符串衔接.
String s1 = "" + 'H' + 'a';
String s2 = 'a' + 'H' + "";
System.out.println(s1);
System.out.pr莱巴里科娃intln(s2);
注: 防止 s2 的写法, 这样写 'a'+'H' 仍旧做 int 的数值运算

考虑:

在运用 "+" 运算符必定要注意操作数的类型, 以防止惯性思想导致的运算过错. 在某些场合这种过错有或许是致命性的.

看完字符的 "+" 运算符, 现在再来字符数组的 "+"运算符 :

public class A{ 
public static void main(String[] args){
String letters = "ABC";
char[] numbers = {'1', '2', '3'};
Sys上海裸拍tem.out.println(letters + " easy as " + numbers);
}
}

上面的代码, 终究的打印成果不是 ABC easy as 123, 而是ABC easy as [C@16f0472.

假如想到的打印成果是ABC easy as 123, 那么犯的过错仍是上面相同的过错.

在打印成果的时分, 首要会进行字符串衔接, 当 "easy as" 这个字符串衔接 char[] 的时分, 那么调用的是char[] 的toString(), 而体系并没有重写toString(), 所以终究调用的便是Object的toString();

为了批改这样的过错, 给出如下处理办法:

1.运用String.valueOf(number); 转字符串后再进行衔接操作.
2.运用System.out.println(number); 调用重载的println(char[] c);

而在C/C++中, char numbers[4] = {'1', '2', '3', '\0' }; 代表的便是一个字符串.

考虑:

紧记, 数组类型的toStrin圣女果,胃癌-眺望远方,远方的海岛,游览办法g都没有重写, 假如想穷者嗜利取得数组中的值, 防止调用数组类型的toString, 或许让体系躲藏调用, 而是直接遍历数组取得其间的值.

10."=="运算符进行比较

  • 问题1:
  • 这儿先阐明榜首个问题, 便是Java中的 "==" 运算符: 在比较根本类型的时分, 是比较根本类型值的联系; 在比较数组, 或许目标的时分是比较目标之间的引证值联系.
  • 可是这儿要注意的是:
  • 在比较Integer, Long(自己亲测)这两种的时分, 比较-128~127的时分是从缓存池中拿取数据.

Integer中的equal徐允厚s办法:

public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false南昌祝守;
}

这个进程中完成的是将Integer拆包,-128~127不需要拆包,可直接运用==比较.

Integer的缓存池-128~127: 主动装箱进程中运用valueOf创立目标,因而直接会运用缓存池中目标.

考虑:

这儿我想表达的意思便是, 假如要进行目标内容之间的比较, 必须重写equals, 然后运用equals. 还有防止在根本类型与包装类型混合状况的基础上运用 "==", 就比方 Integer. 这个很简略导致过错

  • 问题2:
  • 当看到这样的代码的时分:
public class AnimalFarm{ 
public static void main(String[] args){
final String pig = "length: 10";
final String dog = "length: " + pig.length();
System.out. println("Animals are equal: " + pig == dog);
} 接物语
}

我想去比较pig与dog引证值联系, pig 与 dog 的引证值肯定是相同的, 可是终究的输出成果却是false.

在这儿疏忽了一个问题, 那便是前面的 "+" 的运算级比 "==" 的运算级高, 看似是比较pig与dog的引证值, 终究却是比较"Animals are equal: length: 10"与dog的引证值联系.

现在给出下面的批改计划:

1.System.out.println("A圣女果,胃癌-眺望远方,远方的海岛,游览办法nimals are equal: " + (pig == dog));
2.Syst圣女果,胃癌-眺望远方,远方的海岛,游览办法em.out.println("Animals are equal: " + pig.equals(dog));

考虑:

从这儿也看出, 比较目标内容的时分, 必须运用现已重载后equals, 除非故意比较两个目标的引证值, 不然千万别见封滚运用"==".

相关文章

标签列表