1. Java的基本数据类型/引用类型有哪些?知道自动装箱和拆箱吗?
4类8种基本数据类型。4整数型,2浮点型,1布尔型,1字符型
类型/th> | 存储 | 取值范围 | 默认值 | 包装类 |
---|---|---|---|---|
byte | 8 | 最大存储数据量是255,最小-2(7),最大2(7)-1,即:[-128~127] | (byte) 0 | Byte |
short | 16 | 最大数据存储量是65536,[-2(15),2(15)-1],±3万 [-32768,32767] | (short) 0 | Short |
int | 32 | 最大数据存储容量是2(31)-1,范围:[-2(31),2(31)-1],±21亿[ -2147483648, 2147483647] | 0 | Integer |
long | 64 | 最大数据存储容量是2(64)-1,范围:[-2(63),2(63)-1], ±922亿亿(±(922+16个零)) | (byte) 0L | Long |
float | 32 | 数据范围在3.4e-45~1.4e38,直接赋值时必须在数字后加上f或F | 0.0f | Float |
double | 64 | 数据范围在4.9e-324~1.8e308,赋值时可以加d或D也可以不加 | 0.0d | Double |
boolean | 1 | false,true | false | Boolean |
char | 16 | 存储Unicode码,用单引号赋值 | ‘\u0000’ (null) | Character |
- 引用数据类型
类(class)、接口(interface)、数组
- 自动装箱和拆箱
基本数据类型和它对应的封装类型之间可以相互转换。自动拆装箱是jdk5.0提供的新特特性,它可以自动实现类型的转换
装箱:从基本数据类型到封装类型叫做装箱
拆箱:从封装类型到基本数据类型叫拆箱
jdk 1.5
public class TestDemo {
public static void main(String[] args) {
Integer m =10;
int i=m;
}
}
上面的代码在jdk1.4以后的版本都不会报错,它实现了自动拆装箱的功能,如果是jdk1.4,就得这样写了
jdk 1.4
public class TestDemo {
public static void main(String[] args) {
Integer b = new Integer(210);
int c = b.intValue();
}
}
2. ValueOf缓存池
new Integer(123)与Integer.valueOf(123)的区别在于,new Integer(123) 每次都会新建一个对象,而 Integer.valueOf(123) 可能会使用缓存对象,因此多次使用 Integer.valueOf(123) 会取得同一个对象的引用。
Integer x = new Integer(123);
Integer y = new Integer(123);
System.out.println(x == y); // false
Integer z = Integer.valueOf(123);
Integer k = Integer.valueOf(123);
System.out.println(z == k); // true
编译器会在自动装箱过程调用 valueOf() 方法,因此多个 Integer 实例使用自动装箱来创建并且值相同,那么就会引用相同的对象。
Integer m = 123;
Integer n = 123;
System.out.println(m == n); // true
valueOf() 方法的实现比较简单,就是先判断值是否在缓存池中,如果在的话就直接使用缓存池的内容。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
在 Java 8 中,Integer 缓存池的大小默认为 -128~127。
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
Java 还将一些其它基本类型的值放在缓冲池中,包含以下这些:
boolean values true and false
all byte values
short values between -128 and 127
int values between -128 and 127
char in the range \u0000 to \u007F
因此在使用这些基本类型对应的包装类型时,就可以直接使用缓冲池中的对象。
3.i++和++i有什么区别
i++是在程序执行完毕后进行自增,而++i是在程序开始执行前进行自增。
i++的操作分三步
- 栈中取出i
- i自增1
- 将i存到栈
所以i++不是原子操作,上面的三个步骤中任何一个步骤同时操作,都可能导致i的值不正确自增
++i
在多核的机器上,cpu在读取内存i时也会可能发生同时读取到同一值,这就导致两次自增,实际只增加了一次。
i++和++i都不是原子操作
原子性:指的是一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程打断。
4. 位运算符
Java定义了位运算符,应用于整数类型(int),长整型(long),短整型(short),字符型(char),和字节型(byte)等类型。
下表列出了位运算符的基本运算,假设整数变量A的值为60和变量B的值为13
A:0011 1100
B:0000 1101
5. 原码、补码、反码是什么
机器数
一个数在计算机中的二进制表示形式,叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号,正数为0,负数为1。
比如,十进制中的数 +3 ,计算机字长为8位,转换成二进制就是00000011。如果是 -3 ,就是 10000011 。那么,这里的 00000011 和 10000011 就是机器数。
真值
因为第一位是符号位,所以机器数的形式值就不等于真正的数值。例如上面的有符号数 10000011,其最高位1代表负,其真正数值是 -3 而不是形式值131(10000011转换成十进制等于131)。所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值。
例:0000 0001的真值 = +000 0001 = +1,1000 0001的真值 = –000 0001 = –1
原码
原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是8位二进制:
[+1]原 = 0000 0001
[-1]原 = 1000 0001
第一位是符号位. 因为第一位是符号位, 所以8位二进制数的取值范围就是:
[1111 1111 , 0111 1111],即:
[-127 , 127]
原码是人脑最容易理解和计算的表示方式.
反码
反码的表示方法是:
正数的反码是其本身;
负数的反码是在其原码的基础上, 符号位不变,其余各个位取反。
[+1] = [00000001]原 = [00000001]反
[-1] = [10000001]原 = [11111110]反
可见如果一个反码表示的是负数, 人脑无法直观的看出来它的数值. 通常要将其转换成原码再计算。
补码
补码的表示方法是:
正数的补码就是其本身;
负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)
[+1] = [00000001]原 = [00000001]反 = [00000001]补
[-1] = [10000001]原 = [11111110]反 = [11111111]补
对于负数, 补码表示方式也是人脑无法直观看出其数值的。 通常也需要转换成原码在计算其数值。
6. 不用额外变量交换两个整数的值
如果给定整数a和b,用以下三行代码即可交换a和b的值
a = a ^ b;
b = a ^ b;
a = a ^ b;
假设a异或b的结果记为c,c就是a整数位信息和b整数位信息的所有不同信息。比如:a=4=100,b=3=011,ab=c=111
a异或c的结果就是b,比如:a=4=100,c=111,a^c=011=3=b
b异或c的结果就是a,比如:b=3=011,c=111,b^c=100=4=a
说明:位运算的题目基本上都带有靠经验积累才会做的特征,也就是准备阶段需要做足够多的题,面试时才会有良好的感觉。
7. 不使用运算符进行a+b操作
a^b; 得到不含进位之和
(a&b)<<1; 进位
只要进位不为零,则迭代;否则返回
public class Solution {
/*
* @param : An integer
* @param : An integer
* @return: The sum of a and b
*/
public int aplusb(int a, int b) {
int sum_without_carry, carry;
sum_without_carry = a^b; //没有进位的和
carry = (a&b)<<1; //进位
if(carry==0)
return sum_without_carry;
else
return aplusb(sum_without_carry, carry);
}
};
8. &和&& 、|和||的区别?【阿里实习生面试】
(1)&&和&都是表示与,区别是&&只要第一个条件不满足,后面条件就不再判断。而&要对所有的条件都进行判断。
// 例如:
public static void main(String[] args) {
if((23!=23)&&(100/0==0)){
System.out.println("运算没有问题。");
}else{
System.out.println("没有报错");
}
}
// 输出的是“没有报错”。而将&&改为&就会如下错误:
// Exception in thread "main" java.lang.ArithmeticException: / by zero
原因:
&&时判断第一个条件为false,后面的100/0==0这个条件就没有进行判断。
&时要对所有的条件进行判断,所以会对后面的条件进行判断,所以会报错。
(2)||和|都是表示“或”,区别是||只要满足第一个条件,后面的条件就不再判断,而|要对所有的条件进行判断。 看下面的程序:
public static void main(String[] args) {
if((23==23)||(100/0==0)){
System.out.println("运算没有问题。");
}else{
System.out.println("没有报错");
}
}
// 此时输出“运算没有问题”。若将||改为|则会报错。
原因
||判断第一个条件为true,后面的条件就没有进行判断就执行了括号中的代码
而|要对所有的条件进行判断, 所以会报错