关键字、基本类型与运算、字符串与数组
变量命名的规则
标识符
- 变量名
- 函数名
- 数组名
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 | // 如果 object 是 class 的一个实例,返回 true;反之,返回 false |
strictfp 的作用
是 strict float point 的缩写,指的是精确浮点。
当一个类被 strictfp 修饰时,所有方法都会自动被修饰。
Java 的基本数据类型
- 8 种原始数据类型:byte、short、int、long、float、double、char、boolean。声明后会立刻在栈上分配内存空间;
- 引用类型:类、接口、数组等,类似于 C++ 中的指针概念。声明后不会被分配内存空间,只是存储了一个内存地址而已。
Java 还提供了对这些原始数据类型的封装类,封装类型和原始类型的不同点:
- 原始类型按值传递,封装类型按引用传递;
- 具有不同的特征和用法。
默认声明的小数是 double
类型的,对 float 类型变量初始化时需要进行强制类型转换:
1 | float f = (float)3.4; |
不可变类
具有使用简单、线程安全、节省内存等优点。
不可变类的对象会因为值的不同而产生新的对象,从而导致无法预料的问题,切不可滥用。
- 类中所有成员变量被 private 所修饰。
- 类中没有写或者修改成员变量的方法。
- 确保类中所有方法不会被子类覆盖,用 final。
- 如果一个类成员不是不可变量,需要通过 clone 来确保类的不可变性。
- 如果有必要,可使用覆盖 Object 类的 equals() 方法、hashCode() 方法。
- 创建对象时,需要初始化所有成员变量。
类型转换的规则
自动类型转换
转换的规则为:从低精度向高精度转换,即优先级满足byte < short < char < int < long < float < double
。
强制类型转换
从高级数据类型转换为低级数据类型时,需要进行强制类型转换。
涉及 byte、short、char 类型的运算时,会自动转换为 int 类型:
1 | // 编译器会报错 |
移位运算
Java 中,为了保证移动位数的有效性(不超过 32bit),采用了取余操作
。
即:a >> n 等价于 a >> (n%32)。
- “>>”为有符号右移运算符,正数高位补 0,负数高位补 1;
- “>>>”为无符号右移运算符,都补 0;
- 左移运算,移除高位的同时在低位补 0。
new String(“abc”) 创建了几个对象?
一个或两个。如果字符串池中原来有“abc”,那么只创建一个对象;如果没有,就会创建两个。
1 | // 在常量区里面存放了一个“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() 方法
是针对泛型集合
而言的,用于查看泛型中有多少个元素。