《面试宝典》读书笔记 Ⅱ

关键字、基本类型与运算、字符串与数组

变量命名的规则

标识符

  • 变量名
  • 函数名
  • 数组名

break、continue、return 的区别

break

用于直接强行跳出当前循环,不再执行剩余代码。
仅仅终止了内层循环的执行,而不影响外层循环的执行。

continue

用于停止当次循环,回到循环的起始处,进入下一次循环操作。
continue 只是中断了一次循环的执行而已。

return

是一个跳转语句,可以使程序控制返回到调用该方法的地方。

final、finally、finalize 的区别

final

  • final 属性:属性不可变
  • final 方法:方法不可覆盖
  • final 类:类不可被继承

finally

作为异常处理的一部分,只能用在 try/catch 语句中,并且附带一个语句块。
表示这段语句最终一定被执行,经常被用在需要释放资源的情况下。

finalize

是 Object 类的一个方法,在垃圾回收器执行时会调用被回收对象的 finalize() 方法,可以覆盖此方法来实现对其他资源的回收。

JDK 中不能继承的类

  • 用 final 关键字修饰的类。
  • 基本类型:String、StringBuffer 等。

assert 的作用

断言(assert)作为一种软件调试的方法,提供了一种在代码中进行正确性检查的机制。
为了提高程序运行的效率,在软件发布后,assert 检查默认是被关闭的。

assert 与 if 的区别

  • assert 一般在调试程序时使用;
  • if 判断是逻辑判断,本身就是用来控制程序流程的。

Java 与 C++ 中,assert 的不同

  • Java 中使用 assert 关键字去实现其功能,而 C 中使用的是库函数;
  • C 中的 assert 是在编译时开启,而 Java 中是在运行时开启。

static 的作用

static 成员变量

虽然 Java 中没有全局的概念,但可以通过 static 关键字来达到全局的效果。

  • 静态变量属于类,只要静态变量所在的类被加载,这个静态变量就会被分配空间,因此就可以被使用了,它在内存中只有一个复制;
  • 实例变量属于对象,只有对象被创建后,实例变量才会被分配空间,才能被使用,它在内存中存在多个复制。

static 成员方法

static 方法中不能使用 this 和 super 关键字,不能调用非 static 方法。
一个很重要的用途是实现单例模式。

static 代码块

在类中是独立于成员变量和成员函数的代码块,只会被执行一次。

static 内部类

可以不依赖于外部类实例对象而被实例化。
只能访问外部类中的静态成员和静态方法(包括私有类型)。

volatile 的作用

一个类型修饰符,用来修饰被不同线程访问和修改的变量,使所有线程在任何时候所看到变量的值都是相同的。
不能保证操作的原子性,不能代替 synchronized;会阻止编译器对代码的优化,会降低程序的执行效率,尽量不要使用。

instanceof 的作用

一个二元运算符,判断一个引用类型的变量所指向的对象是否是一个类的实例:

1
2
// 如果 object 是 class 的一个实例,返回 true;反之,返回 false
result = object instanceof class;

strictfp 的作用

是 strict float point 的缩写,指的是精确浮点
当一个类被 strictfp 修饰时,所有方法都会自动被修饰。


Java 的基本数据类型

  • 8 种原始数据类型:byte、short、int、long、float、double、char、boolean。声明后会立刻在栈上分配内存空间;
  • 引用类型:类、接口、数组等,类似于 C++ 中的指针概念。声明后不会被分配内存空间,只是存储了一个内存地址而已。

Java 还提供了对这些原始数据类型的封装类,封装类型和原始类型的不同点:

  • 原始类型按值传递,封装类型按引用传递;
  • 具有不同的特征和用法。

默认声明的小数是 double 类型的,对 float 类型变量初始化时需要进行强制类型转换:

1
2
float f = (float)3.4;
float f = 3.4f;

不可变类

具有使用简单、线程安全、节省内存等优点。
不可变类的对象会因为值的不同而产生新的对象,从而导致无法预料的问题,切不可滥用。

  • 类中所有成员变量被 private 所修饰。
  • 类中没有写或者修改成员变量的方法。
  • 确保类中所有方法不会被子类覆盖,用 final。
  • 如果一个类成员不是不可变量,需要通过 clone 来确保类的不可变性。
  • 如果有必要,可使用覆盖 Object 类的 equals() 方法、hashCode() 方法。
  • 创建对象时,需要初始化所有成员变量。

类型转换的规则

自动类型转换

转换的规则为:从低精度向高精度转换,即优先级满足byte < short < char < int < long < float < double

强制类型转换

从高级数据类型转换为低级数据类型时,需要进行强制类型转换。
涉及 byte、short、char 类型的运算时,会自动转换为 int 类型:

1
2
3
4
5
6
// 编译器会报错
short s1 = 1; s1 = s1 + 1;
// 正确
short s1 = 1; s1 = (short)(s1 + 1);
// Java编译器会对“+=”进行特殊处理,正确
short s1 = 1; s1 += 1;

移位运算

Java 中,为了保证移动位数的有效性(不超过 32bit),采用了取余操作
即:a >> n 等价于 a >> (n%32)。

  • “>>”为有符号右移运算符,正数高位补 0,负数高位补 1
  • “>>>”为无符号右移运算符,都补 0;
  • 左移运算,移除高位的同时在低位补 0。

new String(“abc”) 创建了几个对象?

一个或两个。如果字符串池中原来有“abc”,那么只创建一个对象;如果没有,就会创建两个。

1
2
3
4
5
6
7
8
// 在常量区里面存放了一个“abc”字符串对象
String s1 = "abc";
// s2 引用常量区中的对象,因此不会创建新对象
String s2 = "abc";
// 在堆中创建新的对象
String s3 = new String("abc");
// 在堆中又创建一个新的对象
String s4 = new String("abc");

==、equals、hashCode 的区别

==

用来比较两个变量的值是否相等。

equals

用于比较两个对象的内容是否相同,即堆中的内容是否相同。
与“==”的不同:equals() 方法可以被覆盖,可以通过覆盖的方法让它不是比较引用而是比较数据内容。
返回的是 boolean 型。

hashCode

从 Object 类中继承过来,用来鉴定两个对象是否相等。
如果没有重写 hashCode() 方法,任何对象都不相等。
与“equals”的不同:用户一般不会去调用 hashCode() 方法,它相当于是一个对象的编码,就好像文件中的 md5。
返回的是 int 型,比较起来不直观。

String、StringBuffer、StringBuilder、StringTokenizer 的区别

String

不可变类,适合在需要被共享的场合中使用。
可以利用构造函数的方式来对其进行初始化,也可以用赋值的方式来初始化;而 StringBuffer 只能使用构造函数的方式来初始化。

StringBuffer

可变类,当一个字符串经常需要被修改时,最好使用 StringBuffer 来实现。
若使用 String,会多很多附加的操作,同时也生成了一些临时的对象,从而导致程序的执行效率降低。

StringBuilder

与 StringBuffer 类似,都是字符串缓冲区,但 StringBuilder 不是线程安全的。
若只是在单线程中使用字符串缓冲区,那么 StringBuilder 的效率会更高些。

执行效率:StringBuilder > StringBuffer > String。

StringTokenizer

用来分割字符串的工具类。

总结

  • 如果操作的数据量比较小,应优先使用 String 类;
  • 如果是在单线程下操作大量数据,应优先使用 StringBuilder 类;
  • 如果是在多线程下操作大量数据,应优先考虑 StringBuffer 类。

Java 中数组是对象

数组不仅有其自己的属性(length 属性),也有一些方法可以被调用(clone 方法),从这个角度来讲,数组是对象。
每个数组类型都有其对应的类型,可以通过 instanceof 来判断数据的类型。

数组初始化与 C++ 的不同

  • Java 数组被创建后会根据数组存放的数据类型初始化成对应的初始值,int 会初始化为 0,对象会初始化为 null;
  • Java 数组在定义时,并不会给数组元素分配存储空间,因此[]中不需要指定数组的长度。

length 属性、length() 方法、size() 方法的区别

length 属性

数组提供了 length 属性来获取数组的长度。

length() 方法

是针对字符串而言的,String 提供了 length() 方法来计算字符串的长度。

size() 方法

是针对泛型集合而言的,用于查看泛型中有多少个元素。