StringBuilder
在进行大量字符串改动时(比如拼接、替换),使用String会非常消耗内存,降低程序性能,使用StringBuilder可以节省内存,提高程序性能
String s1 = "";
s1 += "123";
s1 += "456";
StringBuilder s2 = new StringBuilder();
s2.append("123").append("456");
由于String的不可变性,所以每次给s1赋值的时候,都是新创建了一个新的对象,s1指向这个新的对象。所以导致String的性能较差。
- 注意, StringBuilder并不是String的子类。不过StringBuilder和String都实现了
CharSequence
接口
StringBuilder的常用方法有append
,insert
,delete
,replace
,reverse
等
StringBuilder的 append原理
StringBuilder的append内部使用的是一个动态数组。默认容量是16,当容量满时,会对数组进行扩容。扩容后的新容量是原来容量的2倍加2
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = value.length >> coder;
// 扩容 左移一位加上2就是新的容量
int newCapacity = (oldCapacity << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
int SAFE_BOUND = MAX_ARRAY_SIZE >> coder;
return (newCapacity <= 0 || SAFE_BOUND - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
异常(Exception)
java中所有的异常最终都继承自java.lang.Throwable
检查型异常(Checked Exception)
- 这类异常一般难以避免,编译器会进行检查
- 如果开发者没有处理这类异常,编译器会报错
- 哪些异常是检查型异常?
- 除Error、RuntimeException以外的异常
非检查型异常(Uncheck Exception)
- 这类异常一般可以避免,编译器不会进行检查
- 如果开发者没有处理这种异常,编译器不会报错
- 非检查型异常:Error,RuntimeException
异常的处理
不管是检查型异常,还是非检查型异常,只有没有主动处理它,都会导致java程序终止运行
异常的处理有两种方式
- try-catch 捕获异常
- throws 将异常往上抛
public static void main(String[] args) throws FileNotFoundException {
// 如果没有处理异常 就会报错
FileOutputStream fos = new FileOutputStream("F:/1.txt");
}
public static void main(String[] args) {
Integer[] nums = {11, null, 22};
for (Integer i : nums) {
System.out.println(i);
}
// 11 null 22
// 这里打印会抛出异常
for (int i : nums) {
System.out.println(i);
}
// 11
// Exception in thread "main" java.lang.NullPointerException
}
-
Integer
在自动拆箱为int
时,会调用Integer
对象的intValue()
方法,由于nums[1]为null,使用null调用方法会抛出异常:java.lang.NullPointerException
finally
finally语句中的代码一定会执行
细节: 如果在执行try
catch
时,如果jvm推出或者当前线程被中断、杀死,finally可能不会执行
- 如果
try
、catch
中有return
、continue
、break
等结束语句,finally
会在结束语句之前执行
try {
System.out.println("1");
return;
} finally {
// 会在return之前执行
System.out.println("2");
}
// 1 2
for (int i = 0; i < 3; i++) {
try {
System.out.println(i + "_try_1");
if (i == 2) {
continue;
}
System.out.println(i+ "_try_2");
} finally {
System.out.println(i+ "_finally");
}
}
打印
0_try_1
0_try_2
0_finally
1_try_1
1_try_2
1_finally
2_try_1
2_finally
throws处理异常
我们还可以使用throws来抛出异常,让上层来处理异常,如果上层不愿意处理,也可以继续往上抛。
当抛出到jvm还没有处理时,程序就会退出
public static void main(String[] args) throws ClassNotFoundException {
test1();
}
static void test1() throws ClassNotFoundException {
test2();
}
// 抛给上层来处理异常
static void test2() throws ClassNotFoundException {
Class.forName("Abc");
}
也可以一部分异常使用try-catch
处理,一部分使用throws
来处理
细节:
-
当父类的方法没有
throws
异常- 子类的重写方法也不能
throws
异常
- 子类的重写方法也不能
-
当父类的方法有
throws
异常- 子类方法可以没有
throws
异常 - 子类型
throws
跟父类相同类型的异常 - 也可以
throws
父类异常的子类型
- 子类方法可以没有
throw
使用throw
可以抛出一个新建的异常
public class Person {
private int age;
public void setAge(int age) throws Exception {
if (age <= 0) {
throw new Exception("age 必须大于0");
} else {
this.age = age;
}
}
}
自定义异常
开发中自定义异常,基本都是以下两种做法
- 继承自
Exception
(检查型异常)- 使用代码复杂一些
- 希望开发者重视这个异常,认真处理这个异常
- 继承自
RuntimeException
(非检查型异常)- 使用起来比较简洁
- 不严格要求开发者去处理这个异常
public class EmptyNameException extends RuntimeException {
public EmptyNameException(){
super("name must be not empty");
}
}