Java面试题之基本类型1. 截尾和舍入先来看看面试题:
public class baseType {
public static void main(String[]args){
float floatNumber =29.7f;
int intNumber =(int)floatNumber;
System.out.println(intNumber);
double doubleNumber = 29.7;
intNumber =(int)doubleNumber;
System.out.println(intNumber);
     floatNumber =29.3f;
intNumber =(int)floatNumber;
System.out.println(intNumber);
 doubleNumber = 29.3;
intNumber =(int)doubleNumber;
System.out.println(intNumber); }
}
答案:29,29,29,29
在java中,这种基本类型中精度间的转换都遵从一种称为“窄化转换”的原则。
因此在float或者double转型为整型值时,总是对数字执行截尾。而不是想当然的执行四舍五入。Java的类型转换在下面会详细解释。
以下说说还有一个比较有常用的的截尾方法。这个方法经常出现在java面试题的综合帖里面的,经常泡论坛的估计都能看到。
相信下面这道面试题大家都会见过:
Public class Test{
   public static void main(String[]args){
          System.out.println(Math.round(12.5));
          System.out.println(Math.round(-12.5));
          
}
}
直接看看JDK的源码:
public static long round(double a) {
return (long)floor(a + 0.5d);
}
看到了没有在调用floor的时候无论 a 是否是正负数,都给a加上0.5.
floor这个方法最终的调用还是调用StrictMath.floor(a),
trictMath.floor(a)这个方法是jdk的本地方法,我没有查到他的具体代码,但是估计这个是通用的函数来的,类似于C++的函数库中的一样。根据C++的函数库floor的解释:
返回不大于改参数的最大整数(最接近正无穷)的double型值。
所以这个很容易解释上面的结果了。即使是
 System.out.println(Math.round(12.0));
 System.out.println(Math.round(12.4));
结果都是一样的:12。
因为返回的结果都是不大于输入参数加上0.5的最大整数而已,关键词是不大于。
2. 精度转换1.float型float f=3.4是否正确? 答案:很明显是不正确。精度不准确,应该用强制类型转换,如下所示:float f=(float)3.4 或float f = 3.4f。在java里面,对于语句中出现的常数,没小数点的默认是int,有小数点的默认是 double;
2.short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 += 1;有什么错?
答案:short s1 = 1; s1 = s1 + 1;有错,s1是short型,s1+1是int型,不能显式转化为short型。可修改为s1 =(short)(s1 + 1)。short s1 = 1; s1 += 1正确。
在JAVA规范上,e1+=e2 实际上是 e1=(T1)(e1+e2),其中T1是e1的数据类型s1+=1等效于   s1=(short)(s1+1),所以是正确的。
还有一道题,来自深入虚拟机的。
class BadArithmetic{
static byte addOneAndOne(){
   byte a = 1;
   byte b = 1;
   btye  c = a+b;
    Return c;
}} 初看,好像没有什么错误,其实会报一下错误的。 Incompatible type for declation·····错误的。在虚拟机内部,涉及到byte,short和char类型的运算操作首先会把这些值转换为int类型值的,然后对int类型的值进行运算,最后得到的结果也是int类型的结果。因此如果两个byte相加,得到的结果是int类型的,如果需要得到byte的结果,就必须显示的进行类型转换:byte c =(int)a+b;如果将正确的代码产生class文件并分析这些文件,就会发现: 有以下的 虚拟机汇编语句:iadd---------i2b.iadd语句意义为执行int类型的加法,因为 a和b在压入方法栈的时候,就变成了int类型的值了。i2b的意思正如语句一样就是讲int类型的值转换为byte类型。每一种类型转换都有对应的汇编指令。Java虚拟机中不能把long,float,double类型的值直接转换为比int类型更小的类型,原因在于
虚拟机中没有对应的操作指令。因此,把foat转为byte需要两个步骤。一,先将float转为int类型的,然后将int类型转为byte类型。
再看看下面的一道题:
public class TestRound {/**
 * @param args
 */
   public static void main(String[]args){
   
   System.out.println(3.3f-2.4f); 
   float c = 0.9; 
   System.out.println(c);
          
}
 
答案是否觉得很奇怪呢?
有人会问,为什么
Java代码  
1 System.out.println(0.9f);  输出的还是0.9呢?
因为在源代码里面println调用的是Float#toString(float),最终的实现是在
Java代码  
        public static String toString(float f) {   
    return new FloatingDecimal(f).toJavaFormatString();   
}  下面实战计算一把:
3、浮点型的减法运算
浮点加减运算过程比定点运算过程复杂。完成浮点加减运算的操作过程大体分为四步:
  (1) 0操作数的检查;
如果判断两个需要加减的浮点数有一个为0,即可得知运算结果而没有必要再进行有序的一些列操
作。
   (2) 比较阶码(指数位)大小并完成对阶;
两浮点数进行加减,首先要看两数的 指数位 是否相同,即小数点位置是否对齐。若两数 指数位 相同,表示小数点是对齐的,就可以进行尾数的加减运算。反之,若两数阶码不同,表示小数点位置没有对齐,此时必须使两数的阶码相
同,这个过程叫做对阶 。
如何对 阶(假设两浮点数的指数位为 Ex 和 Ey ):
通过尾数的移位以改变 Ex 或 Ey ,使之相等。 由 于浮点表示的数多是规格化的,尾数左移会引起最高有位的丢失,造
成很大误差;而尾数右移虽引起最低有效位的丢失,但造成的误差较小,因此,对阶操作规定 使尾数右移,尾数右移后使阶
码作相应增加,其数值保持不变。很显然,一个增加后的阶码与另一个相等,所增加的阶码一定是小阶。因此在对阶时,总是
使小阶向大阶看齐 ,即小阶的尾数向右移位 ( 相当于小数点左移 ) ,每右移一位,其阶码加 1 ,直到两数的阶码相等为止,
右移的位数等于阶差 △ E。
   (3) 尾数(有效数位)进行加或减运算;对阶完毕后就可 有效数位 求和。 不论是加法运算还是减法运算,都按加法进行操作,其方法与定点
加减运算完全一样。
   (4) 结果规格化并进行舍入处理
4、 计算12.0f-11.9f
12.0f 的内存存储格式为: 0 1 0000010 10000000000000000000000
11.9f 的内存存储格式为:  0 1 0000010 01111100110011001100110
可见两数的指数位完全相同,只要对有效数位进行减法即可。
12.0f-11.9f 结果: 0 1 0000010 00000011001100110011010
将结果还原为十进制为: 0.000 11001100110011010= 0.10000038
还有一道容易出错的题目,来自java解惑这本书的,我也摘录出来给大家看看。
谜题1:奇数性
下面的方法意图确定它那唯一的参数是否是一个奇数。这个方法能够正确运转吗? 
public static boolean isOdd(int i){
return i % 2 == 1;
}
奇数可以被定义为被2整除余数为1的整数。表达式 i % 2 计算的是 i 整除 2 时所产生的余数,因此看起来这个程序应该能够正确运转。遗憾的是,它不能;它在四分之一的时间里返回的都是错误的答案。 
为什么是四分之一?因为在所有的 int 数值中,有一半都是负数,而 isOdd 方法对于对所有负奇数的判断都会失败。在任何负整数上调用该方法都回返回 false ,不管该整数是偶数还是奇数。 
这是 Java 对取余操作符(%)的定义所产生的后果。该操作符被定义为对于所有的 int 数值 a 和所有的非零 int 数值 b,都满足下面的恒等式: 
(a / b) * b + (a % b) == a
换句话说,如果你用b整除a,将商乘以b,然后加上余数,那么你就得到了最初的值 a 。该恒等式具有正确的含义,但是当与 Java 的截尾整数整除操作符相结合时,它就意味着:当取余操作返回一个非零的结果时,它与左操作数具有相同的正负符号。 
当 i 是一个负奇数时,i % 2 等于-1而不是1, 因此 isOdd 方法将错误地返回 false。为了防止这种意外,请测试你的方法在为每一个数值型参数传递负数、零和正数数值时,其行为是否正确。 
这个问题很容易订正。只需将 i % 2 与0而不是与1比较,并且反转比较的含义即可: 
public static boolean isOdd(int i){
return i % 2 != 0;
}
如果你正在在一个性能临界(performance-critical)环境中使用isOdd方法,那么用位操作符AND(&)来替代取余操作符会显得更好: 
public static boolean isOdd(int i){
return (i & 1) != 0;
}
总之,无论你何时使用到了取余操作符,都要考虑到操作数和结果的符号。该操作符的行为在其操作数非负时是一目了然的,但是当一个或两个操作数都是负数时,它的行为就不那么显而易见了。 5. 窄数字类型提升至宽类型时使用符号位扩展还是零扩展
Java代码 
1 System.out.println((int)(char)(byte)-1);// 65535  
结果为什么是65535而不是-1?
 
窄的整型转换成较宽的整型时符号扩展规则:如果最初的数值类型是有符号的,那么就执行符号扩展(即如果符号位
为1,则扩展为1,如果为零,则扩展为0);如果它是char,那么不管它将要被提升成什么类型,都执行零扩展。
 
了解上面的规则后,我们再来看看迷题:因为byte是有符号的类型,所以在将byte数值-1(二进制为:11111111)提
升到char时,会发生符号位扩展,又符号位为1,所以就补8个1,最后为16个1;然后从char到int的提升时,由于是
char型提升到其他类型,所以采用零扩展而不是符号扩展,结果int数值就成了65535。
 
如果将一个char数值c转型为一个宽度更宽的类型时,只是以零来扩展,但如果清晰表达以零扩展的意图,则可以考虑
使用一个位掩码:
Java代码 
2 int i = c & 0xffff;//实质上等同于:int i = c ;  
 
如果将一个char数值c转型为一个宽度更宽的整型,并且希望有符号扩展,那么就先将char转型为一个short,它与
char上个具有同样的宽度,但是它是有符号的:
Java代码 
3 int i = (short)c;  
 
如果将一个byte数值b转型为一个char,并且不希望有符号扩展,那么必须使用一个位掩码来限制它:
Java代码 
4 char c = (char)(b & 0xff);// char c = (char) b;为有符号扩展  
 以上很多的内容是对java常见的一些基本类型问题进行了汇总。如有错误,欢迎指出!