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"还在内存中,并没有被改变。

创建一个不可变类,遵循四原则:

  1. 类中所有成员变量被 private 修饰。
  2. 类中没有 写/修改 成员变量的方法。
  3. 类中所有方法不会被子类覆盖。
  4. 如果一个类成员是可变量,那么在成员初始化或者使用get方法获取该成员变量时,需要通过clone方法来确保类的不可变性。
  5. 如果有必要,可使用覆盖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类是一个不可变类,里面的方法全是静态方法。

  1. round(“环绕”)方法表示四舍五入。在原来数字的基础上先增加0.5 再向下取整。返回值为int类型。
  2. ceil(“天花板”)方法表示向上取整。对操作数取顶。返回类型是double类型。
  3. 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个字母