异常
异常体系图
java.lang.ArithmeticException: / by zero
System.out.println("出现异常的原因=" + e.getMessage());//输出异常信息
出现异常的原因=/ by zero
执行过程中所发生的异常事件可分为两大类
- Error(错误):Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如: StackOverflowError[栈溢出]和OOM(out of
memory).Error是严重错误,程序会崩溃。 - Exception:其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如空指针访问,试图读取不存在的文件,网络连接中断等等,
- Exception分为两大类:运行时异常[程序运行时,发生的异常]和
- 编译时异常[编程时,编译器检查出的异常]。
Serializable
0bject
-
Throwable
-
Exception
-
RuntimeException
-
NullPointerException
-
ClassCast Exception
-
ArrayIndex0ut0fBounds Exception
-
Arithmetic Exception
-
NumberFormat Exception
-
lllegalArgumentException
-
illegalstateException
-
NoSuchElementException
-
CloneNotsupportedException
-
ClassNotFoundException
-
IOEXception
- EOFException 文件结束符(end of file)
- FileNotFoundException
- MalformedURLException
- UnknownHostException
-
-
Error
-
OutOfMemoryError
-
StackOverflowError
-
蓝色:非受检(unchecked)异常(运行异常) RuntimeException
红色:受检(checked)异常(编译异常) IOException 和 ClassNotFound
异常测试
try {
FileInputStream fis;
fis = new FileInputStream("d:\\aa.jpg");
int len;
while ((len = fis.read()) != -1) {
System.out.println(len);
}
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
- ClassCastException类型转换异常
当试图将对象强制转换为不是实例的子类时,
抛出该异常。例如,以下代码
将生成一个ClassCastException
public class ClassCastException_ {
public static void main(String[] args) {
A b = new B(); //向上转型
B b2 = (B)b;//向下转型,这里是OK
C c2 = (C)b;//这里抛出ClassCastException
}
}
class A {}
class B extends A {}
class C extends A {}
- NumberFormatException数字格式不正确异常
当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常=>使用异常我们可以确保输入是满足条件数字.
String name="韩顺平教育";
int num = Integer.parselnt(name);
编译异常
编译异常是指在编译期间,就必须处理的异常否则代码不能通过编译。
常见的编译异常
√SQLException/操作数据库时,查询表可能发生异常
IOException//操作文件时,发生的异常
√FileNotFoundException//当操作一个不存在的文件时,发生异常
√ClassNotFoundException//加载类,而该类不存在时,异常
√EOFException/操作文件,到文件末尾,发生异常
√IllegalArguementException//参数异常
异常处理
·异常处理的方式
·1) try-catch-finally
程序员在代码中捕获发生的异常,自行处理
2) throws
将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是JVM
2.如果程序员,没有显示是处理异常,默认throws
5)可以进行 try-finally 配合使用,这种用法相当于没有捕获异常,因此程序会直接崩掉。
/*
可以进行 try-finally 配合使用, 这种用法相当于没有捕获异常,
因此程序会直接崩掉/退出。应用场景,就是执行一段代码,不管是否发生异常,
都必须执行某个业务逻辑
*/
try{
int n1 = 10;
int n2 = 0;
System.out.println(n1 / n2);
}finally {
System.out.println("执行了finally..");
}
System.out.println("程序继续执行..");//异常后,程序 不会继续执行。
例题输入一个整数
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);
}
}
class Father { //父类
public void method() throws RuntimeException {
}
}
class Son extends Father {//子类
//3. 子类重写父类的方法时,对抛出异常的规定:子类重写的方法,
// 所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常类型的子类型
//4. 在throws 过程中,如果有方法 try-catch , 就相当于处理异常,就可以不必throws
@Override
public void method() throws ArithmeticException {
}
}//运行时 异常,无需处理
自定义异常
2)如果继承Exception,属于编译异常
3)如果继承RuntimeException,属于运行有常(一般来说,继承RuntimeException)
public class CustomException {
public static void main(String[] args) /*throws AgeException*/ {
int age = 180;
//要求范围在 18 – 120 之间,否则抛出一个自定义异常
if(!(age >= 18 && age <= 120)) {
//这里我们可以通过构造器,设置信息
throw new AgeException("年龄需要在 18~120之间");
}
System.out.println("你的年龄范围正确.");
}
}
//自定义一个异常
//老韩解读
//1. 一般情况下,我们自定义异常是继承 RuntimeException
//2. 即把自定义异常做成 运行时异常,好处时,我们可以使用默认的处理机制
//3. 即比较方便
class AgeException extends RuntimeException {
public AgeException(String message) {//构造器
super(message);
}
}
包装类
基本数据类型
包装类
-
boolean
-
Boolean
-
char
- Character
-
byte 从这里开始,父类都是 Number
- Byte
-
short
- Short
-
int
- lnteger
-
long
- Long
-
float
- Float
-
double
- Double
3)自动装箱底层调用的是valueOf方法,比如Integer.valueOf)
Object obj1 = true? new Integer(1) : new Double(2.0)
答案为:1.0,三元运算符,会提升优先级。
常用方法
Integer.MAX_VALUE);//返回最大值
Character.isDigit('a');//判断是不是数字
Character.isLetter('a'))://判断是不是字母
Character.isUpperCase('a'));//判断是不是大写
Character.isLowerCase('a'))://判断是不是小写
Character.isWhitespace('a'))://判断是不是空格
Character.toUpperCase('a'));//转成大写
ValueOf
public static Integer valueOf(int i) {
//-128 to 127
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
//示例六
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
简介
String01.java
- String对象用于保存字符串,也就是一组字符序列
2)字符串常量对象是用双引号括起的字符序列。例如:“你好”、“12.97”."boy"等
3)字符串的字符使用Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节。
4)String类较常用构造器(其它看手册);
String s1 = new String0;1
String s2 = new String(String original);String s3 = new String(charl a);
String s4 = new String(charl a,int startlndex,int count)
String(byte[], Charset)
String(byte[]], int, int)
String(byte[])
String(StringBuffer)
String实现了Serializable,说明String可以串行化
可以网络传输
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];//底层是char数组
//一定要注意:value是一个final类型,不可以修改。是这个 char数组的地址 不可修改了。数组里的内容,还是可以改的
}
创建
方式一:直接赋值 String s = “hsp”;
方式二:调用构造器 String s2 = new String(“hsp”);
1.方式一:先从常量池查看是否有"hsp"数据空间,如果有,直接指向;如果
没有则重新创建,然后指向。S最终指向的是常量池的空间地址
2.方式二:先在堆中创建空间,里面维护了value属性,指向常量池的hsp空间。
如果常量池没有"hsp",重新创建,如果有,直接通过value指向。最终指向的是堆中的空间地址。
例题
String a = "abc";String b ="abc";
System.out.println(a.equals(b)):/T
System.out.println(a==b);//T
当调用intern方法时,如果池已经包含与equals(Object)方法确定的相当于此string对象的字符串,则返回来自池的字符串。否则,此string对象将添加到池中,并返回对此string对象的引用。
b.intern()方法最终返回的是常量池的地址(对象)
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
String s1 = "hspedu";
String s2 = "java";
String s4 = "java";
String s3 = new String("java");
System.out.println(s2 == s3);//false
System.out.println(s2 == s4);//true
System.out.println(s2.equals(s3));//true
System.out.println(s1 == s2);//false
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;
}
创建对象数量
String s1 = "hello";
s1="haha"; //创建2个对象
String a = "hello" +"abc"; //底层优化,创建一个对象。 helloabc
String a = "hello"; //创建 a对象
String b = "abc";//创建 b对象
//老韩解读
//1. 先 创建一个 StringBuilder sb = StringBuilder()
//2. 执行 sb.append("hello");
//3. sb.append("abc");
//4. String c= sb.toString()
//最后其实是 c 指向堆中的对象(String) value[] -> 池中 "helloabc"
String c = a + b;
String d = "helloabc";
System.out.println(c == d);//真还是假? 是false。因为C是 new 的,断点代码,可以看出。
//断点的代码 如下
/*public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}*/
String e = "hello" + "abc";//直接看池, e指向常量池
System.out.println(d == e);//真还是假? 是true。两个都是常量池。
老韩小结:底层是
StringBuilder sb = new StringBuilder();
sb.append(a);
sb是在堆中,并且append是在原来字符串的基础上追加的.
重要规则,String c1 = “ab” + “cd”;常量相加,看的是记。String c1=a+b;变量相加,是在堆中
class Test1 {
String str = new String("hsp");
final char[] ch = {'j', 'a', 'v', 'a'};
public void change(String str, char ch[]) {
//常量池中 新生成了 java 串
str = "java";
ch[0] = 'h';
}
public static void main(String[] args) {
Test1 ex = new Test1();
ex.change(ex.str, ex.ch);
//hsp and hava
System.out.print(ex.str + " and ");
System.out.println(ex.ch);
}
}
常用方法
equals
equalslgnoreCase
length
indexOf
lastlndexOf
substring
trim
charAt:获取某索引处的字符
toUpperCase
toLowerCase
concat
compareToto
CharArrayformat
常用方法1
indexOf //获取字符在字符串中第1次出现的索引,索引从0开始,如果找不到,返回-1
lastIndexOf//获取字符在字符串中最后1次出现的索引,索引从0开始,如找不到,返回-1
substring//截取指定范围的子串
trim//去前后空格
charAt:获取某索引处的字符,注意不能使用Str[index]这种方式.
- str.charAt(0);
// 4.indexOf 获取字符在字符串对象中第一次出现的索引,索引从0开始,如果找不到,返回-1
String s1 = "wer@terwe@g";
int index = s1.indexOf('@');
System.out.println(index);// 3
System.out.println("weIndex=" + s1.indexOf("we"));//0
// 5.lastIndexOf 获取字符在字符串中最后一次出现的索引,索引从0开始,如果找不到,返回-1
s1 = "wer@terwe@g@";
index = s1.lastIndexOf('@');
System.out.println(index);//11
System.out.println("ter的位置=" + s1.lastIndexOf("ter"));//4
// 6.substring 截取指定范围的子串
String name = "hello,张三";
//下面name.substring(6) 从索引6开始截取后面所有的内容
System.out.println(name.substring(6));//截取后面的字符
//name.substring(0,5)表示从索引0开始截取,截取到索引 5-1=4位置。就是截取5个数,从0开始。不包括 结束索引。
System.out.println(name.substring(2,5));//llo 。截取 2 3 4 三个字符。截取3个数,从2开始。
常用方法2
toUpperCase
toLowerCaseconcat
replace替换字符串中的字符
split 分割字符串,对于某些分割字符,我们需要转义比如|I等
案例: String poem=“锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦”;和文件路径.
compareTo 1/比较两个字符串的大小
toCharArray 1/转换成字符数组
format //格式字符串,%s字符串%c字符%d 整型%.2f 浮点型案例,将一个人的信息格式化输出.
// 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("宝玉", "jack");
System.out.println(s1);//宝玉 and 林黛玉 林黛玉 林黛玉
System.out.println(s11);//jack and 林黛玉 林黛玉 林黛玉
// 5.split 分割字符串, 对于某些分割字符,我们需要 转义比如 | \\等
String poem = "锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦";
//老韩解读:
// 1. 以 , 为标准对 poem 进行分割 , 返回一个数组
// 2. 在对字符串进行分割时,如果有特殊字符,需要加入 转义符 \
String[] split = poem.split(",");
poem = "E:\\aaa\\bbb"; //真实为 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";// len = 3
String b = "jack";// len = 4
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);
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
String a = "jcck";// len = 4
String b = "jack";// len = 4,长度相同时,某个不相同的字符 相减: c-a = 2
//长度不同的时候,a的长度 - b的长度。
StringBuffer
//1. StringBuffer 的直接父类 是 AbstractStringBuilder
//2. StringBuffer 实现了 Serializable, 即StringBuffer的对象可以串行化
//3. 在父类中 AbstractStringBuilder 有属性 char[] value,不是final
// 该 value 数组存放 字符串内容,引出存放在堆中的
//4. StringBuffer 是一个 final类,不能被继承
//5. 因为StringBuffer 字符内容是存在 char[] value, 所有在变化(增加/删除)
// 不用每次都更换地址(即不是每次创建新对象), 所以效率高于 String
StringBuffer stringBuffer = new StringBuffer("hello");
String VS StringBuffer
- String保存的是字符串常量,里面的值不能更改,每次String类的更新实际上就是更改地址,效率较低//private final char value[];
· - StringBuffer保存的是字符串变量,里面的值可以更改,每次
StringBuffer的更新实际上可以更新内容,不用每次更新地址,效率较高//char[] value;/这个放在堆.
- char value[] 数组的大小,默认16,不够了,才扩展
public StringBuffer() {
super(16);
}
//指定大小的
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
- StringBuffer 转为 String
//方式1 使用StringBuffer提供的toString方法
String s = stringBuffer3.toString();//方式2:使用构造器来搞定
String s1 = new String(stringBuffer3);
//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");
增删改插
StringBuffer s = new StringBuffer("hello");
//增
s.append(',');// "hello,"
s.append("张三丰");//"hello,张三丰"
s.append("赵敏").append(100).append(true).append(10.5);//"hello,张三丰赵敏100true10.5"
System.out.println(s);//"hello,张三丰赵敏100true10.5"
//删
/*
* 删除索引为>=start && <end 处的字符
* 解读: 删除 11~14的字符 [11, 14)
*/
s.delete(11, 14);
System.out.println(s);//"hello,张三丰赵敏true10.5"
//改
//老韩解读,使用 周芷若 替换 索引9-11的字符 [9,11)
s.replace(9, 11, "周芷若");
System.out.println(s);//"hello,张三丰周芷若true10.5"
//查找指定的子串在字符串第一次出现的索引,如果找不到返回-1
int indexOf = s.indexOf("张三丰");
System.out.println(indexOf);//6
//插
//老韩解读,在索引为9的位置插入 "赵敏",原来索引为9的内容自动后移
s.insert(9, "赵敏");
System.out.println(s);//"hello,张三丰赵敏周芷若true10.5"
//长度
System.out.println(s.length());//22
System.out.println(s);
插入 null
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
private AbstractStringBuilder appendNull() {
int c = count;
ensureCapacityInternal(c + 4);
final char[] value = this.value;
value[c++] = 'n';
value[c++] = 'u';
value[c++] = 'l';
value[c++] = 'l';
count = c;
return this;
}
String str = null;// ok
StringBuffer sb = new StringBuffer(); //ok
sb.append(str);//需要看源码 , 底层调用的是 AbstractStringBuilder 的 appendNull
System.out.println(sb.length());//4
System.out.println(sb);//null
//下面的构造器,会抛出NullpointerException
StringBuffer sb1 = new StringBuffer(str);//看底层源码 super(str.length() + 16);
System.out.println(sb1);
千分位练习
String s = "1234567890.59";
//前面有 7个长度,所以需要 添加2个。如果是6个,只要添加一个。
int count = s.indexOf(".");
String[] split = s.split("\\.");
//需要插入几个 , 逗号
int i = (count - 1) / 3;
StringBuffer sb = new StringBuffer(split[0]);
//比如1234567,长度为7,第一次插入索引的位置为4
int t = sb.length() - 3;
for (int j = 1; j <= i; j++) {
sb.insert(t, ",");
t = t - 3;
}
System.out.println(sb.append(".").append(split[1]));
String s = "1234567890.59";
//前面有 7个长度,所以需要 添加2个。如果是6个,只要添加一个。
int count = s.lastIndexOf(".");
//需要插入几个 , 逗号
int i = (count - 1) / 3;
StringBuffer sb = new StringBuffer(s);
int t = count - 3;
for (int j = 1; j <= i; j++) {
sb.insert(t, ",");
t = t - 3;
}
System.out.println(sb);
//new Scanner(System.in)
String price = "8123564.59";
StringBuffer sb = new StringBuffer(price);
//先完成一个最简单的实现123,564.59
//找到小数点的索引,然后在该位置的前3位,插入,即可
// int i = sb.lastIndexOf(".");
// sb = sb.insert(i - 3, ",");
//上面的两步需要做一个循环处理,才是正确的
for (int i = sb.lastIndexOf(".") - 3; i > 0; i -= 3) {
sb = sb.insert(i, ",");
}
System.out.println(sb);//8,123,564.59
DecimalFormat df = new DecimalFormat("###,###.00");
//12,345,678.12
System.out.println(df.format(12345678.123));
String 对比
String、StringBuffer 和StringBuilder的比较
- StringBuilder 和 StringBuffer非常类似,均代表可变的字符序列,而且方法也一样
- String:不可变字符序列,效率低,但是复用率高。
- StringBuffer:可变字符序列、效率较高(增删)、线程安全4) StringBuilder:可变字符序列、效率最高
线程不安全
· - String使用注意说明:
string s=“a”;//创建了一个字符串
S +=“b”;//实际上原来的"a"字符串对象已经丢弃了,现在又产生了一个字符串s+“b”(也就是"ab”)。如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能=>结论:如果我们对String 做大量修改,不要使用String
builder 是 buffer的2倍,buffer是 String的 1千倍。