问题及答案来源自《Java程序员面试笔试宝典》第四章 Java基础知识 4.4基本类型与运算
1、Java提供了哪些基本数据类型?
Java一共提高了八种原始的数据类型:byte、short、int、long、float、double、char、boolean
基本数据类型不是对象,而是基本数据类型,这些数据类型变量在声明之后就会立刻在栈上被分配内存空间
八种基本类型可以分为以下四类:
- int相关:short(2字节)、int(4字节)、long(8字节)
- float相关:float(4字节)、double(8字节)
- boolean相关:boolean(1字节)、byte(1字节)
- char相关:char(2字节)(unicode字符)
另外Java还提供了对这些原始数据类型的封装类(字符类型Character、布尔类型Boolean、数值类型Byte、Short、
Integer、Long、Float、Double)
封装类型和原始类型的不同之处:
- 原始类型传递参数按值传递,封装类型传递参数传的是引用
- 类实例化时,封装类型默认值是null,原始类型默认值与它们的类型有关(比如int默认值为0)
精度损失:
当把高精度的变量赋值给低精度的变量时会造成精度损失,例如将double类型的数值赋值给float类型的变量
eg:float f = 0.93; 这样会造成精度损失,正确的方法是采用强制类型转换,eg:float f = (float)0.93;
2、什么是不可变类?
不可变类:
不可变类是指当创建了这个类的实例后,就不允许修改它的值,不可变类类似于常量,即只允许别的程序读,
但是不允许别的程序进行修改
常见的不可变类:
基本类型的封装类(Integer、Float等)、String
String类是不可变类,那么下面这段代码为什么可行?
1 String s = "Hello"; 2 s += " World"; 3 System.out.println(s);
解释:
第2行代码中修改的只是s的引用,s由原来的指向"Hello"改成指向"Hello World",
原来的"Hello"还存在于内存中并没有被改变
3、值传递与引用传递有哪些区别?
Java语言中参数传递有两种方式:值传递和引用传递
值传递:
在方法调用中,实参把值传递给形参,形参只是用实参的值初始化一个临时的存储单元,因此形参与
实参虽然值一样,但是有着不同的存储单元,对形参的改变不会影响实参的改变
引用传递:
在方法调用中,传递的是对象(也可以看作是对象的地址),这是形参和实参的对象指向同一块存储单元,
因此对形参的修改就会影响实参的值
在Java中,原始数据类型在传递参数时按值传递,而普通对象在传递参数时按引用传递
实例如下:
1 public class test { 2 public static void testPassParameter(StringBuffer ss1, int n){ 3 ss1.append(" World"); // 引用 4 n = 8; // 值 5 } 6 7 public static void main(String[] args) { 8 int i = 1; 9 StringBuffer s1 = new StringBuffer("Hello"); 10 testPassParameter(s1, i); 11 System.out.println(s1); // Hello World 12 System.out.println(i); // 1 13 } 14 }
Java中八种基本数据类型都是按值传递,其他所有类型都是按引用传递,由于Java中八种基本数据类型的包装类型
都是不可变类型,因此增加了对按引用传递的理解难度,详细情况看下面的实例:
1 public class test { 2 public static void changeStringBuffer(StringBuffer ss1, StringBuffer ss2) { 3 ss1.append(" World"); 4 ss2 = ss1; 5 } 6 7 public static void main(String[] args) { 8 Integer a = new Integer(1); 9 Integer b = a; 10 b = new Integer(b.intValue() + 1); 11 System.out.println(a + " " + b); // 1 2 第10行代码实际上是创建了一个新的Integer对象 12 StringBuffer s1 = new StringBuffer("Hello"); 13 StringBuffer s2 = new StringBuffer("Hello"); 14 changeStringBuffer(s1, s2); 15 System.out.println(s1 + " " + s2); // Hello World Hello 16 } 17 }
由上面的实例可以看出,按引用传递实际上当修改形参中地址对应的内容时,实参会发生相应变化,但是当
直接改变形参地址时实参不会改变,这说明了按引用传递实际上也是一种按值传递(传的是地址)
也就是说引用传递不会改变实际参数指向的地址,但是能改变实际参数指向的地址中的内容
4、不同数据类型的转换有哪些规则?
背景描述:
在Java中,当参与运算的两个变量的数据类型不同时,就需要进行隐式的数据转换,规则为从低精度向高精度转换,
即优先级满足:byte < short < char < long < float < double,例如不同数据类型的值在进行运算时,short类型数据
能够自动转换成int类型,int类型能够自动转换成float类型等,反之则需要通过强类型转换
类型自动转换:
低级数据类型可以自动转换为高级数据类型,转换规则如下:
自动转换的注意事项:
- char类型转换时会自动转换成其对应的ASCII码
- byte、char、short类型的数据在参与运算时会自动转换成int型,但使用+=运算时不会产生类型转换
- boolean类型是不能和其他数据类型相互转换的
强制类型转换:
当需要从高级数据类型转换成低级数据类型时,就需要进行强制类型转换,强制类型转换如下:
- byte =》char
- char =》byte
- short =》byte、char
- int =》byte、short、char
- long =》byte、short、char、int
- float =》byte、short、char、int、long
- double =》byte、short、char、int、long、float
另外要注意进行强制类型转换可能会损失精度(高精度转换成低精度)
5、强制类型转换的注意事项有哪些?
Java中的+=是Java中规定的运算法,Java编译器会对其进行特殊处理,因此语句short s1 = 1; s1 += 1; 能够编译通过
6、运算符优先级是什么?
7、Math类中round、ceil和floor方法的功能各是什么?
- round:对数四舍五入
- ceil:对数向上取整
- floor:对数向下取整
下面是测试代码:
1 public class test { 2 public static void main(String[] args) { 3 float m = 6.4f; 4 float n = -6.4f; 5 System.out.println(Math.round(m)); // 6 6 System.out.println(Math.round(n)); // -6 7 System.out.println("==============="); 8 System.out.println(Math.ceil(m)); //7.0 9 System.out.println(Math.ceil(n)); // -6.0 10 System.out.println("==============="); 11 System.out.println(Math.floor(m)); // 6.0 12 System.out.println(Math.floor(n)); // -7.0 13 System.out.println("==============="); 14 } 15 }
8、++i和i++有什么区别?
在java的执行过程中,i++和++i都直接对i进行了i=i+1的操作,
但是不同的是i++得到的是i未进行加法操作的前的值的副本,而++i直接得到计算后的值
比如a = i++;是将i的值赋值给a然后i的值再+1,而a=++i是将i的值+1然后再将+1后的i的值赋值给a
示例代码:
1 public class test { 2 public static void main(String[] args) { 3 int i = 1; 4 System.out.println((i++)+(i++)); // 3 5 System.out.println(i); // 3 6 System.out.println((i++)+(++i)); // 8 7 System.out.println(i); // 5 8 System.out.println((i++)+(i++)+(i++)); // 18 9 System.out.println(i); // 8 10 } 11 }
9、无符号数的右移操作
Java提供了两种右移运算符:>>和>>>,>>被称为有符号右移运算符,>>>被称为无符号右移运算符
两者的区别:
- >>在右移时,若参与运算的数为正数则在高位补0,若为负数则在高位补1
- >>>则不同,不论参与运算的数字为正数或负数,均会在高位补0
引申 - <<运算符和>>运算符有什么异同?
<<运算符标识左移,左移n位表示原来的值乘2的n次方,经常用来代替乘法操作
例如,一个数m乘以16可以表示为将这个数左移4位(m<<4)
由于CPU直接支持位运算,因此位运算比乘法运算的效率更高
与右移运算不同的是左移运算没有有符号和无符号的区别,在左移时,移除高位的同时在低位补0
10、char型变量中是否可以存储一个中文汉字?
在Java中,默认使用unicode编码方式,即每个字符(char)占用两个字节,因此char可以存储中文汉字
String是由char组成的,但它采用了更灵活的方式存储,即英文占用一个字节,而中文一般占用两个字节
示例代码:
1 public class test { 2 public static void getLen(String s){ 3 System.out.println(s + "的长度是: " + s.length() + " 所占字节数是: " + s.getBytes().length); 4 } 5 6 public static void main(String[] args) { 7 String s1 = "hello"; 8 String s2 = "你好"; 9 getLen(s1); 10 getLen(s2); 11 } 12 }
注:在Java中,中文字符所占的字节数取决于字符的编码方式,一般情况下,采用ISO8859-1编码方式时,
一个中文字符与一个英文字符一样只占1个字节;采用GB2312或GBK编码方式时,一个中文字符占2个字节;
而采用UTF-8编码方式时,一个中文字符会占3个字节