Java学习笔记(第12章 P444-P459)(第13章P460-P498)(第14章P499-P553)
第12章 异常-Exception
异常介绍
-
基本概念
Java语言中,将程序执行中发生的不正常情况称为"异常"。(开发过程中的语法错位和逻辑错误不是异常) -
执行过程中所发生的异常事件可分为两大类
1)Error(错误):java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError【栈溢出】和OOM(out of memory),Error是严重错误,程序会崩溃
2)Exception:其它因编译错误或偶然的外在因素导致的一般性问题,可以使用针对的代码进行处理。例如空指针访问,试图读取不存在的文件,网络连接中断等等,Exception分为两大类:运行时异常[程序运行时,发生的异常]和编译时异常[编程时,编译器检查出的异常]。 -
异常体系图
-
异常体系图的小结
1.异常分为两大类,运行时异常和编译时异常
2.运行时异常,编译器检查不出来。一般是指编程时的逻辑错误,是程序员应该避免其出现的异常。java.lang.RuntimeException类及它的子类都是运行时异常
3.对于运行时异常,可以不做处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响
4.编译时异常,是编译器要求必须处置的异常。 -
常见的运行时异常
1)NullPointerException空指针异常
当应用程序试图在需要对象的地方使用null时,抛出该异常
2)ArithmeticException数字运算异常
当出现异常的运算条件时,抛出该异常(整数除以零)
3)ArrayIndexOutOfBoundsException数组下标越界异常
用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引
4)ClassCastException类型转换异常
当试图将对象强制转换为不是实例的子类时,抛出该异常。
5)NumberFormatException数字格式不正确异常
当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出异常=>使用异常我们可以确保输入是满足条件数字
编译异常
- 介绍
编译异常是指编译期间,就必须处理的异常,否则代码不能通过编译。
-常见的编译异常
√SQLException //操作数据库时,查询表可能发生异常
√IOException //操作文件时,发生的异常
√FileNotFoundException //当操作一个不存在的文件时,发生异常
√ClassNotFoundException //加载类,而该类不存在时,异常
√EOFException //操作文件,到文件末尾,发生异常
√IlleglArguementException //参数异常
异常处理
-
基本介绍
异常处理就是当异常发生时,对异常处理的方式 -
异常处理的方式
1)try- catch-finally
程序员在代码中捕获发生的异常,自行处理
2)throws
将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是JVM -
示意图
try-catch异常处理
- try-catch方式处理异常说明
1)Java提供try和catch块来处理异常。try块用于包含可能出错的代码。catch块用于处理try块中发生的异常。可以根据需要在程序中有多个try…catch块
2)基本语法
try{
//可疑代码
//将异常生成对应的异常对象,传递给catch块
}catch(异常){
//对异常处理
}
//如果没有finally,语法是可以通过的
- try- catch方式处理异常
1)如果异常发生了,则异常发生后面的代码不会执行,直接进入到catch块
2)如果异常没有发生,则顺序执行try的代码块,不会进入到catch
3)如果希望不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等)
则使用如下代码-finally{}
4)可以有多个catch语句,捕获不同的异常(进行不同的业务处理),要求父类异常在后,子类异常在前,比如(Exception在后,NullPointException在前),如果发生异常,只会匹配一个catch
5)可以进行try-finally配合使用,这种用法相当于没有捕获异常,因此程序会直接崩掉/退出。应用场景,就是执行一段代码,不管是否发生异常,都必须执行某个业务逻辑
try{
//代码...
}
finally{
//总是执行
}
-
try- catch- finally执行顺序小结
1)如果没有出现异常,则执行try块中所有语句,不执行catch块中语句,如果有finally里面的语句
2)如果出现异常,则try块中异常发生后,try块剩下的语句不再执行。将执行catch块中的语句,如果有finally,最后还需要执行finally里面的语句! -
try-catch异常处理案例
要求:如果用户输入的不是一个整数,就提示他反复输入,直到输入一个整数为止
实现代码:
package com.hspedu.try_;
import java.util.Scanner;
public class TryCatchExercise04 {
public static void main(String[] args) {
//如果用户输入的不是一个整数,就提示他反复输入,直到输入一个整数为止
//思路
//1.创建Scanner对象
//2.使用无限循环,去接收一个输入
//3.然后将该输入的值,转成一个int
//4.如果在转换时,抛出异常,说明输入的内容不是一个可以转成int的内容
//5.如果没有抛出异常,则break 该循环
Scanner scanner = new Scanner(System.in);
int num = 0;
String inputStr = "";
while(true){
System.out.println("请输入一个整数:");
inputStr = scanner.next();
try {
num = Integer.parseInt(inputStr); //这里是可能抛出异常
break;
} catch (NumberFormatException e) {
System.out.println("你输入的不是一个整数:");
}
}
System.out.println("你输入的值是=" + num);
}
}
throws异常处理
-
基本介绍
1)如果一个方法(中语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。
2)在方法声明中用throws语句可以声明抛出异常的列表(即多个异常),throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。 -
注意事项和使用细节
1)对于编译异常,程序中必须处理,比如try-catch或者throws
2)对于运行时异常,程序中如果没有处理,默认就是throws的方式处理
3)子类重写父类的方法时,对抛出异常的规定:子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常的类型的子类型
4)在throws过程中,如果有方法try-catch,就相当于处理异常,就可以不必throws
自定义异常
-
基本概念
当程序中出现了某些"错误",但该错误信息并没有在Throwable子类中描述处理,这个时候可以自己设计异常类,用于描述该错误信息。 -
自定义异常的步骤
1)定义类:自定义异常类名(程序员自己写)继承Exception或RuntimeException
2)如果继承Exception,属于编译异常
3)如果继承RuntimeException,属于运行异常(一般来说,继承RuntimeException)
补充:
1.一般情况下,我们自定义异常是继承RuntimeException
2.即把自定义异常做成运行时异常,好处是,我们可以使用默认的处理机制
throw 和 throws 的区别
- 一览表
第13章 常用类
包装类(Wrapper)
1)针对八种基本数据类型相应的引用类型–包装类
2)有了类的特点,就可以调用类中的方法
- 包装类和基本数据的转换
1)jdk5前的手动装箱和拆箱方式,装箱:基本类型->包装类型,反之,拆箱
2)jdk5以后(含jdk5)的自动装箱和拆箱方式
3)自动装箱底层调用的是valueOf方法,比如Integer.valueOf()
4)其他包装类的用法类似,不一一举例
public class Integer01 {
public static void main(String[] args) {
//演示int <--> Integer 的装箱和拆箱
//jdk5前是手动装箱和拆箱
//手动装箱
int n1 = 100;
Integer integer = new Integer(n1);
Integer integer1 = Integer.valueOf(n1);
//手动拆箱
//Integer -> int
int i = integer.intValue();
//jdk5后,就可以自动装箱和自动拆箱
int n2 = 200;
//自动装箱 int -> Integer
Integer integer2 = n2; //底层使用的是 Integer.valueOf(n2)
//自动拆箱 Integer -> int
int n3 = integer2; //底层仍然使用的是 intValue()方法
}
}
包装类类型和String类型的相互转换
public class WrapperVSString {
public static void main(String[] args) {
//包装类(Integer) -> String
Integer i = 100;
//方式1
String str1 = i + "";
//方式2
String str2 = i.toString();
//方式3
String str3 = String.valueOf(i);
//String -> 包装类(Integer)
String str4 = "12345";
Integer i2 = Integer.parseInt(str4); //使用到自动装箱
Integer i3 = new Integer(str4); //构造器
System.out.println("ok~~");
}
}
Integer类和Character类的常用方法
public class WrapperMethod {
public static void main(String[] args) {
System.out.println(Integer.MIN_VALUE); //返回最小值
System.out.println(Integer.MAX_VALUE);//返回最大值
System.out.println(Character.isDigit('a'));//判断是不是数字
System.out.println(Character.isLetter('a'));//判断是不是字母
System.out.println(Character.isUpperCase('a'));//判断是不是大写
System.out.println(Character.isLowerCase('a'));//判断是不是小写
System.out.println(Character.isWhitespace('a'));//判断是不是空格
System.out.println(Character.toUpperCase('a'));//转成大写
System.out.println(Character.toLowerCase('A'));//转成小写
}
}
Integer类面试题
题中System.out.println输出了什么
1.
public class WrapperExercise02 {
public static void main(String[] args) {
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j); //False
//所以,这里主要是看范围 -128 ~ 127 就是直接返回
/*
//1. 如果i 在 IntegerCache.low(-128)~IntegerCache.high(127),就直接从数组返回
//2. 如果不在 -128~127,就直接 new Integer(i)
//源码:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
*/
Integer m = 1; //底层 Integer.valueOf(1); -> 阅读源码
Integer n = 1;//底层 Integer.valueOf(1);
System.out.println(m == n); //T
//所以,这里主要是看范围 -128 ~ 127 就是直接返回
//,否则,就new Integer(xx);
Integer x = 128;//底层Integer.valueOf(1);
Integer y = 128;//底层Integer.valueOf(1);
System.out.println(x == y);//False
}
}
public class WrapperExercise03 {
public static void main(String[] args) {
//示例一
Integer i1 = new Integer(127);
Integer i2 = new Integer(127);
System.out.println(i1 == i2);//F
//示例二
Integer i3 = new Integer(128);
Integer i4 = new Integer(128);
System.out.println(i3 == i4);//F
//示例三
Integer i5 = 127;//底层Integer.valueOf(127)
Integer i6 = 127;//-128~127
System.out.println(i5 == i6); //T
//示例四
Integer i7 = 128;
Integer i8 = 128;
System.out.println(i7 == i8);//F
//示例五
Integer i9 = 127; //Integer.valueOf(127)
Integer i10 = new Integer(127);
System.out.println(i9 == i10);//F
//示例六
Integer i11=127;
int i12=127;
//只有有基本数据类型,判断的是
//值是否相同
System.out.println(i11==i12); //T
//示例七
Integer i13=128;
int i14=128;
System.out.println(i13==i14);//T
}
}
String类
- String类的理解和创建对象
- String对象用于保存字符串,也就是一组字符序列
- 字符串常量对象是用双引号括起的字符序列。例如:“你好”、“12.97”、"boy"等
- 字符串的字符使用Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节。
- String类较常用构造器(其它看手册):
String s1 = new String();
String s2 = new String(String original);
String s3 = new String(char[] a);
String s4 = new String(char[] a, int startIndex, int count)
- String 类实现了接口 Serializable【String 可以串行化:可以在网络传输】,接口 Comparable [String 对象可以比较大小]
- String 是 final类 ,不能被其他的类继承
- String 有属性 private final char value[];用于存放字符串内容
- 一定要注意: value 是一个final类型, 不可以修改(需要理解):即value不能指向新的地址,但是单个字符内容是可以变化
-
创建String对象的两种方式
1)方式一:直接赋值 String s = “hspedu”;
2)方式二:调用构造器String s = new String(“hspedu”); -
两种创建String对象的区别
1.方式一:先从常量池查看是否有"hsp"数据空间,如果有,直接指向;如果没有则重新创建,然后指向。s最终指向的是常量池的空间地址
2.方式二:现在堆中创建空间,里面维护了value属性,指向常量池的hsp空间。如果常量池没有"hsp",重新创建,如果有,直接通过value指向。最终指向的是堆中的空间地址。
3.两种方式的内存分布图
实例1:
public class StringExercise01 {
public static void main(String[] args) {
String a = "abc";
String b = "abc";
System.out.println(a.equals(b)); //T
System.out.println(a == b); //T
}
}
实例2:
public class StringExercise03 {
public static void main(String[] args) {
String a = "hsp"; //a 指向 常量池的 “hsp”
String b =new String("hsp");//b 指向堆中对象
System.out.println(a.equals(b)); //T
System.out.println(a==b); //F
//b.intern() 方法返回常量池地址
System.out.println(a==b.intern()); //T //intern方法自己先查看API
System.out.println(b==b.intern()); //F
}
}
- 知识点:
当调用intern方法时,如果池已经包含一个等于此String对象的字符串(用equals(Object)方法确定),则返回池中的字符串。否则,将此String对象添加到池中,并返回此对象的引用
解读:b.intern()方法最终返回的是常量池的地址(对象)
实例3:
public class StringExercise05 {
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "hspedu";
Person p2 = new Person();
p2.name = "hspedu";
System.out.println(p1.name.equals(p2.name));//比较内容: True
System.out.println(p1.name == p2.name); //T
System.out.println(p1.name == "hspedu"); //T
String s1 = new String("bcde");
String s2 = new String("bcde");
System.out.println(s1==s2); //False
}
}
class Person {
public String name;
}
字符串的特性
1)String是一个final类,代表不可变的字符序列
2)字符串是不可变的。一个字符串对象一旦分配,其内容是不可变的。
- 面试题
1)题1
图解:
2)题二
3)题三
- 题四
示意图
代码:
public class StringExercise10 {
}
class Test1 {
String str = new String("hsp");
final char[] ch = {
'j', 'a', 'v', 'a'};
public void change(String str, char ch[]) {
str = "java";
ch[0] = 'h';
}
public static void main(String[] args) {
Test1 ex = new Test1();
ex.change(ex.str, ex.ch);
System.out.print(ex.str + " and ");
System.out.println(ex.ch);
}
}
运行结果:
String类的常见方法
-
说明
String类是保存字符串常量的。每次更新都需要重新开辟空间,效率较低n因此java设计者还提供了StringBuilder和StringBuffer来增强String的功能,并提高效率。 -
String类的常见方法一览
- equals //区分大小写,判断内容是否相等
- equalsIgnoreCase //忽略大小写的判断内容是否相等
- length //获取字符的个数,字符串的长度
- indexOf //获取字符在字符串中第一次出现的索引,索引从0开始,如果找不到,返回-1
- lastIndexOf //获取字符在字符串最后1次出现的索引,索引从0开始,如找不到,返回-1
- substring //截取指定范围的子串
- trim //去前后空格
- charAt //获取某索引处的字符,注意不能使用Str[index]这种方式
String str = “hello”;
//str[0]不对
//str.charAt(0) => h
案例演示String另一组相关的方法:
public class StringMethod02 {
public static void main(String[] args) {
// 1.toUpperCase转换成大写
String s = "heLLo";
System.out.println(s.toUpperCase()); //HELLO
// 2.toLowerCase
System.out.println(s.toLowerCase()); //hello
// 3.concat拼接字符串
String s1 = "宝玉";
s1 = s1.concat("林黛玉").concat("薛宝钗").concat("together");
System.out.println(s1); //宝玉林黛玉薛宝钗together
// 4.replace 替换字符串中的字符
s1 = "宝玉 and 林黛玉 林黛玉 林黛玉";
//在s1中,将所有的 林黛玉 替换成薛宝钗
// s1.replace()方法执行后,返回的结果才是替换过的.
// 注意对 s1没有任何影响
String s11 = s1.replace("林黛玉", "薛宝钗");
System.out.println(s1); //宝玉 and 林黛玉 林黛玉 林黛玉
System.out.println(s11); //宝玉 and 薛宝钗 薛宝钗 薛宝钗
// 5.split 分割字符串,对于某些分割字符,我们需要转义比如| \\等
String poem = "锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦";
//1. 以 ,为标准对poem进行分割 , 返回一个数组
//2. 在对字符串进行分割时, 如果有特殊字符,需要加入 转义符 \
String[] split = poem.split(",");
poem = "E:\\aaa\\bbb";
split = poem.split("\\\\");
System.out.println("===分割后内容===");
for(int i = 0; i < split.length; i++){
System.out.println(split[i]);
}
// 6.toCharArray 转换成字符数组
s = "happy";
char[] chs = s.toCharArray();
for(int i = 0; i < chs.length; i++){
System.out.println(chs[i]);
}
// 7.compareTo 比较两个字符串的大小,如果前者大,
// 则返回正数,后者大,则返回负数, 如果相等, 返回0
// (1) 如果长度相同,并且每个字符也相同,就返回0
// (2) 如果长度相同或者不相同,但是在进行比较时,可以区分大小
// 就返回 if (c1 != c2) {
// return c1 - c2;
// }
// (3) 如果前面的部分都相同,就返回 str1.len - str2.len
String a = "jcck";
String b = "jack";
System.out.println(a.compareTo(b)); //返回值是 'c' - 'a' = 2的值
//8.format格式字符串
/* 占位符有:
* %s字符串 %c 字符 %d 整型 %.2f 浮点型
*
*/
String name = "john";
int age = 10;
double score = 56.857;
char gender = '男';
//将所有的信息都拼接在一个字符串。
String info =
"我的姓名是" + name + "年龄是" + age + ",成绩是" + score + "性别是" + gender + ".希望大家喜欢我!";
System.out.println(info);
//1. %s , %d, %.2f, %c 称为占位符
//2. 这些占位符由后面变量来替换
//3. %s 表示后面 字符串来替换
//4. %d 是正数来替换
//5. %.2f 表示使用小数来替换, 替换后, 只会保留小数点两位, 并且进行四舍五入的处理
//6. %c 使用char类型来替换
String formatStr = "我的名字是%s年龄是%d,成绩是%.2f性别是%c.希望大家喜欢我!";
String info2 = String.format(formatStr, name, age, score, gender);
System.out.println("info2=" + info2);
}
}
输出:
StringBuffer类
- 基本介绍
java.lang.StringBuffer代表可变的字符序列,可以对字符串内容进行增删。
很多方法与String相同,但StringBuffrt是可变长度的。
StringBuffer是一个容器。
- StringBuffer 的直接父类 是 AbstractStringBuilder
- StringBuffer 实现了 Serializable, 即StringBuffer的对象可以串行化
- 在父类中 AbstractStringBuilder 有属性 char[] value, 不是final
该value 数组存放 字符串内容 , 引出存放在堆中的 - StringBuffer 是一个final类,不能被继承
- 因为StringBuffer字符内容是存放在 char[] value, 所有在变化(增加/删除)不用每次都更换地址(既不是每次创建新对象),所以效率高于String
-
String VS StringBuffer
1)String保存的是字符串常量,里面的值不能更改,每次String类的更新实际上就是更改地址,效率较低(private final char value[];)
2)StringBuffer保存的是字符串变量,里面的值可以更改,每次StringBuffer的更新实际上可以更新内容,不用每次更新地址,效率较高(char[] value;//这个放在堆) -
StringBuffer的构造器
√构造器的使用
public class StringBuffer02 {
public static void main(String[] args) {
//构造器的使用
//1.创建一个大小为16的char[] ,用于存放字符内容
StringBuffer stringBuffer = new StringBuffer();
//2.通过构造器指定 char[] 大小
StringBuffer stringBuffer1 = new StringBuffer(100);
//3.通过给一个String创建StringBuffer, char[] 大小就是 str.length() + 16
StringBuffer hello = new StringBuffer("hello");
}
}
String和StringBuffer相互转换
public class StringAndStringBuffer {
public static void main(String[] args) {
//看 String-->StringBuffer
String str = "hello tom";
//方式1 使用构造器
//注意: 返回的才是StringBuffer对象.对str本身没有影响
StringBuffer stringBuffer = new StringBuffer(str);
//方式2 使用的是append方法
StringBuffer stringBuffer1 =