4.1Java提供了哪些基本数据类型
byte(8bit)short(16bit)int(32bit)long(64bit)
char(16bit unicode[0,65535] 默认值’\u0000’)float(32bit)double(64bit)true&false(1bit)这些基本类型的数据变量在声明之后就立刻在栈上被分配内存空间。
Java语言中,默认小数点是double类型的(意味着float=3.4不正确),float初始化两种方式:1.float f = 1.0f或float f = (float)1.0
Java中的null值是什么?在内存中null是什么?
null是一个不合法的Object实例,所以编译器没有为其分配内存,仅仅用于表明该引用目前没有指向任何对象。
如何理解String x = null;?
Java语言中,变量被分为两大类型:原始值(primitive)与引用值(reference)。原始类型变量存储的是实际的值;声明为引用类型,存储的是实际对象的地址。String x = null 定义一个变量x,x中存放的是String引用,此处为null。
4.2 什么是不可变类?
不可变类是指当前创建了这个类的实例后,就不允许修改它的值了。也就是说,一个对象一旦被创建出来,在其整个生命周期中,它的成员变量就不能被修改了。有点类似于常量(const),即只允许别的程序读,不允许别的程序修改。
Java类库中,所有基本类型的包装类都是不可变类。
public class Test1 {
public static void main(String[] args) {
String s = "Hello";
s += " world";
System.out.println(s);
}
}
运行结果:Hello world
String s = “Hello” 声明了一个可以指向String类型对象的引用,s指向"Hello"。
s += " world" 没有改变s所指向的对象,s指向了另一个String类型的对象,该对象内容为"Hello world"。原来的"Hello"还在内存中,并没有被改变。
创建一个不可变类,遵循四原则:
- 类中所有成员变量被 private 修饰。
- 类中没有 写/修改 成员变量的方法。
- 类中所有方法不会被子类覆盖。
- 如果一个类成员是可变量,那么在成员初始化或者使用get方法获取该成员变量时,需要通过clone方法来确保类的不可变性。
- 如果有必要,可使用覆盖Object类的equals()方法和hashCode()方法。在equals()方法中,根据对象的属性值来比较两个对象是否相等,并且保证用equals()方法判断相等的两个对象的hashCode()方法的返回值也相等,这可以保证这些对象能被正确地放到HashMap或HashSet集合中。
由于类的不可变性,在创建对象时就需要初始化所有成员变量,因此提供一个带参数的构造函数来初始化这些成员变量。
错误实例:
package org.base;
import java.util.Date;
class ImmutableClass{
private Date d;
public ImmutableClass(Date d){
this.d = d;
}
public void printState(){
System.out.println(d);
}
}
public class TestImmutable {
public static void main(String[] args) {
Date d = new Date();
ImmutableClass immuC = new ImmutableClass(d);
immuC.printState();
d.setMonth(5);
immuC.printState();
}
}
结果运行不一致。
由于Date对象的状态是可以被改变的,而ImmutableClass保存了Date类型对象的引用,当被引用的对象的状态改变时会导致ImmutableClass对象状态的改变。
package org.base;
import java.util.Date;
class ImmutableClass{
private Date d;
public ImmutableClass(Date d){
this.d = (Date)d.clone();// 解除了引用关系
}
public void printState(){
System.out.println(d);
}
}
public class TestImmutable {
public static void main(String[] args) {
Date d = new Date();
ImmutableClass immuC = new ImmutableClass(d);
immuC.printState();
d.setMonth(5);
immuC.printState();
}
}
结果运行一致。
4.3 值传递和引用传递有哪些区别?
这个我专门有写一篇文章,这里不在描述。
4.4 不同数据类型的转换有哪些规则?
隐式数据类型的转换:低精度向高精度转换,优先级满足byte < short 、char < int < long < float、 double,反之要强制转换。
原操作数的类型 | 转换后操作数的类型 |
---|---|
byte | char |
char | byte、char、short |
short | byte、char |
float | byte、short、char、int、long |
double | byte、short、char、int、long、double |
package org.base;
public class Test1 {
public static void main(String[] args) {
short a = 128;
byte b = (byte) a;
System.out.println(a +" " + b);
}
}
运行结果:128 -128
a对应二进制:0000 0000 1000 0000 b强制转换取低字节:1000 0000,是-128的补码,所以a为128,b为-128。
4.5 强制类型转换的注意事项有哪些?
Java在涉及byte、short和char类型的运算时,首先把这些类型的变量强转为int类型,然后对int类型的值进行计算,结果也为int类型。因此,两个short类型或者两个byte类型相加,结果为int类型。如果需要得到short类型,就必须显式的把结果转换为short类型。
short a = 1;
a = a + 1; // 在运算时会将a转为int类型,所以会编译错误
short a = 1;
a = (short) (a + 1);// 正确写法
short a = 1;
a += 1; // += 左右类型不同会自动类型强转
4.6 运算符优先级是什么?
优先级 | 运算符 |
---|---|
1 | . () [] |
2 | +(正) -(负) ++ – ~ ! |
3 | * / % |
4 | +(加) -(减) |
5 | << >>(无符号右移) >>>(有符号右移) |
6 | < <= > >= instanceof |
7 | == != |
8 | & |
9 | l |
10 | ^ |
11 | && |
12 | ll |
13 | ?: |
14 | = += -= *= /= %= &= =l= ^= ~= <<= >>= >>>= |
public class Test1 {
public static void main(String[] args) {
byte a = 5;
int b = 10;
int c = a >> 2 + b >> 2;// + 优先级大些
System.out.println(c);
}
}
运行结果:0
4.7 Math类中round、ceil和floor方法的功能?
java.lang包下,能自动导入,Math类是一个不可变类,里面的方法全是静态方法。
- round(“环绕”)方法表示四舍五入。在原来数字的基础上先增加0.5 再向下取整。返回值为int类型。
- ceil(“天花板”)方法表示向上取整。对操作数取顶。返回类型是double类型。
- floor(“地板”)方法表示向下取整。对操作数取底。返回值类型是double类型。
数字 | Math floor方法 | Math.round方法 | Math.ceil方法 |
---|---|---|---|
1.4 | 1.0 | 1 | 2.0 |
1.5 | 1.0 | 2 | 2.0 |
1.6 | 1.0 | 2 | 2.0 |
-1.4 | -2.0 | -1 | -1.0 |
-1.5 | -2.0 | -1 | -1.0 |
-1.6 | -2.0 | -2 | -1.0 |
4.8 ++i和i++有什么区别?
不同点在于i++是在程序执行完毕后进行自增,而++i是在程序开始执行前进行自增。
package org.base;
public class Test1 {
public static void main(String[] args) {
int i = 1;
System.out.println(i++ + i++);// 1(i=1) + 2(i=2)
System.out.println("i=" + i); // 3
System.out.println(i++ + ++i);// 3(i=3) + 5(i=5)
System.out.println("i=" + i); // 5
System.out.println(i++ + i++ + i++); // 5(i=5) + 6(i=6) + 7(i=7)
System.out.println("i=" + i); // 8
}
}
运行结果:3
i=3
8
i=5
18
i=8
4.9 如何实现无符号数的右移操作?
“>>” :有符号右移运算符,若参与运算的数字为正数,则在高位补0;若为负数,则在高位补1
“>>>”:无符号右移运算符,无论参与运算数字为正数或负数,都会在高位补0
对char、byte、short进行移位操作前,编译器都会自动地将数值转化为int类型,再进行移位操作。 为了保证移位的有效性,移位不超过32位,采用取余操作,即a >> n(n % 32)
package org.base;
public class Test {
public static void main(String[] args) {
int i = -4;
System.out.println("----int>>:" + i);
System.out.println("移位前二进制:" + Integer.toBinaryString(i));
i >>= 1;
System.out.println("移位后二进制:" + Integer.toBinaryString(i));
System.out.println("----int>>:" + i);
i = -4;
System.out.println("----int>>>:" + i);
System.out.println("移位前二进制:" + Integer.toBinaryString(i));
i >>>= 1;
System.out.println("移位后二进制:" + Integer.toBinaryString(i));
System.out.println("----int>>>:" + i);
short j = -4;
System.out.println("----short>>>:" + j);
System.out.println("移位前二进制:" + Integer.toBinaryString(j));
j >>>= 1;
System.out.println("移位后二进制:" + Integer.toBinaryString(j));
System.out.println("----short>>>:" + j);
i = 5;
System.out.println("----int>>>:" + i);
System.out.println("移位前二进制:" + Integer.toBinaryString(i));
i >>= 32;
System.out.println("移位后二进制:" + Integer.toBinaryString(i));
System.out.println("----int>>>:" + i);
}
}
运行结果:----int>>:-4
移位前二进制:11111111111111111111111111111100
移位后二进制:11111111111111111111111111111110
----int>>:-2
----int>>>:-4
移位前二进制:11111111111111111111111111111100
移位后二进制:1111111111111111111111111111110
----int>>>:2147483646
----short>>>:-4
移位前二进制:11111111111111111111111111111100
移位后二进制:11111111111111111111111111111110
----short>>>:-2
----int>>>:5
移位前二进制:101
移位后二进制:101
----int>>>:5
需要特别说明的是short,由于short只占2Byte,在移位操作时会先转换为int类型,虽然在进行无符号右移时会先在高位补1,但当把运算结果再赋值给short类型变量时,只会取其中低位的两个字节,因此,高位无论补0还是补1对运算结果无影响。上例中,-4的二进制表示为11111111 11111100,在转换为二进制时会以4Byte方式输出,高位会补1,因此输出11111111 11111111 11111111 11111100,无符号右移后二进制为01111111 11111111 11111111 11111100,当把结果再复制给i时取低位的两个字节,因此,运算结果的二进制为11111111 11111110对应的十进制值是-2,当把-2以二进制形式输出时,同理会以4Byte方式输出,高位补1,因此输出11111111 11111111 11111111 11111110
"<<"与“>>”有什么异同?
<<,左移n位表示原来的值乘以2的n次方,经常用来代替乘法操作,因为CPU直接支持位运算,效率比乘法高。
<<与右移不同的是,左移运算不看符号。
<<与右移相同的是,进行位运算时,如果移动的位数超过了该类型的最大位数,编译器会对移动的位数取模,如对int型移动33位,实际上只移动了33%32=1位。
4.10 char型变量中是否可以存储一个中文汉字?
Java默认使用Unicode编码,即每个字符占用两个字节,因此可以存储汉字。
public class Test {
public static void main(String[] args) {
String s1 = "Hello";
String s2 = "你好";
System.out.println(s1 +" 的长度:" + s1.length() +" 所占字节数:" + s1.getBytes().length);
System.out.println(s2 +" 的长度:" + s2.length() +" 所占字节数:" + s2.getBytes().length);
}
}
运行结果:Hello 的长度:5 所占字节数:5
你好 的长度:2 所占字节数:4
判断一个字符串中是否包含中文字符 ?
首先通过字节长度和字符串长度判断字符串是否包含中文字符,若包含,则用正则表达式匹配的方式找出字符串中的所有中文字符。
package org.base;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Test {
public static void judgeChineseCharactor(String str){
String regEx = "[\u4e00-\u9fa5]"; // 匹配汉字
if(str.getBytes().length == str.length()) {
System.out.println("无汉字!");
}else{
Pattern p = Pattern.compile(regEx);
Matcher m = p.matcher(str);
while (m.find()) {
System.out.print(m.group(0) + " ");
}
}
}
public static void main(String[] args) {
judgeChineseCharactor("Hello world!");
judgeChineseCharactor("你好!");
}
}
运行结果:无汉字!
你 好
为什么国际化简称为i18n呢?
i18n,18意味着在国际化(internationalization)这个单词中,i和n之间有18个字母