1. 异常处理的方式二:throws
/**
* 异常处理章节内容:
* 1. 了解异常的体系结构
* 2. 常见的Error、Exception(编译时异常、运行时异常)的举例
* 3. 异常处理的方式一:try-catch-finally
* 4. 异常处理的方式二:throws
* 5. 手动的抛出一个异常类的对象
* 6. 用户自定义异常类
*
* 测试异常处理的方式二:throws + 异常类型
*
* 1. 声明位置:我们在方法的声明处,使用“throws + 异常类型”方式处理异常
* 2. 与try-catch-finally区别在于:throws方式将可能出现的异常对象抛给方法的调用者。由调用者继续考虑
* 该如何处理异常。
* 3. 与try-catch-finally相同的地方:只针对于编译时异常进行处理即可。运行时异常可不用处理。
*/
public class ExceptionTest {
public static void main(String[] args) {
// method1();
method4();
}
public static void method4(){
try {
method3();
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}
}
public static void method3() throws FileNotFoundException,IOException{
method2();
}
public static void method2() throws FileNotFoundException, IOException {
File file = new File("hello1.txt");
FileReader fr = new FileReader(file);
char[] buffer = new char[4];
int len;
while ((len = fr.read(buffer)) != -1) {
String str = new String(buffer, 0, len);
System.out.print(str);
}
fr.close();
}
public static void method1(){
Object obj = new String("hello");
Date date = (Date)obj;
}
@Test
public void test(){
method0();
}
public void method0() {
String str = "123";
str = "123a";
int num = Integer.parseInt(str);
System.out.println(num);
}
}
- 方法重写中的关于throws的规则
/**
* 方法重写的规则中,关于throws+异常类型的说明:
*
* 子类重写的方法throws的异常类型为A,父类被重写的方法throws的异常类型为B
* 要求:类型A可以与类型B相同,或者类型A是类型B的子类。
*
* >推论:如果父类被重写的方法在声明时,没有声明throws的结果,则子类重写父类的方法,也不能使用throws的
* 方式处理异常。
*
*/
public class OverrideTest {
public static void main(String[] args) {
SuperClass s = new SubClass();
try {
s.method();
}catch(IOException e){
e.printStackTrace();
}
}
}
class SuperClass{
public void method() throws IOException {
}
public void method1(){
}
}
class SubClass extends SuperClass{
public void method() throws FileNotFoundException {
}
//编译失败
// public void method1() throws FileNotFoundException{
//
// }
}
- 开发中如何选择try-catch-finally还是throws
开发中,该如何选择使用try-catch-finally还是throws的方式处理异常呢?
* ① 子类重写父类的方法,如果父类的方法中没有声明throws的结构,则子类方法中如果出现异常,
* 只能使用 try-catch-finally的方式处理异常
* ② 如果代码中涉及到相关的资源(IO流、Socket资源、数据库连接)必须手动关闭的话,
* 则只能使用try-catch-finally的方式处理异常。
* ③ 如果一个方法a中又调用了相关的数个方法,而此数个方法之间是递进调用的关系。
* 则通常,数个方法内如果有异常的话,选择使用throws的方式进行处理,而在方法a中统一使用try-catch-finally的方式进行处理。
*
2. 手动throw一个异常对象
/**
* Java提供的是异常处理的抓抛模型。
* 程序执行过程中,出现了异常,并处理异常,对应着两个过程。
* 过程一:“抛”:理解为异常对象生成的过程
* > 系统自动创建的指定异常类型的对象
* > 手动创建异常类的对象,并抛出。 使用 throw
* 过程二:“抓” 理解为异常处理的两种方式:① try-catch-finally ② throws + 异常类型
* 测试throw的使用:手动抛出异常类的对象
* 我们可以在方法内部,手动的使用"throw + 异常类的对象"的方式,显式的生成一个异常类的对象,并throw出去。
* 面试题:throw 、 throws 的区别
*/
- 举例1
public class ThrowTest {
public static void main(String[] args) {
Student s = new Student();
try {
s.regist(-10);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
class Student{
int id;
public void regist(int id) throws Exception{
if(id > 0){
this.id = id;
}else{
//手动抛出一个异常类的对象
// throw new RuntimeException("输入的id不合法");
throw new Exception("输入的id不合法");
}
}
}
- 举例2
public class ComparableCircle extends Circle implements CompareObject{
public ComparableCircle(double radius) {
super(radius);
}
public ComparableCircle() {
}
@Override
public int compareTo(Object o) throws Exception{
if(this == o){
return 0;
}
if(o instanceof ComparableCircle){
ComparableCircle c = (ComparableCircle)o;
// return (this.getRadius() > c.getRadius())? 1 : ((this.getRadius() == c.getRadius())? 0 : -1);
return Double.compare(this.getRadius(),c.getRadius());
}
// throw new RuntimeException("输入的类型不匹配");
throw new Exception("输入的类型不匹配");
}
}
3. 用于自定义异常类
/**
* 如何自定义异常类?
* 1. 需要继承于现有的异常体系结构:Exception
* 2. 提供一个序列化的版本号:serialVersionUID
* 3. 提供重载的方法
*/
public class MyException extends Exception{
static final long serialVersionUID = -7034897166939L;
public MyException(){
}
public MyException(String message){
super(message);
}
}
4. 异常处理_小结
- 课后练习
/**
* 编写应用程序EcmDef.java,接收命令行的两个参数,要求不能输入负数,计算两数相除。
* 对数据类型不一致(NumberFormatException)、缺少命令行参数(ArrayIndexOutOfBoundsException、
* 除0(ArithmeticException)及输入负数(EcDef 自定义的异常)进行异常处理。
* 提示:
* (1)在主类(EcmDef)中定义异常方法(ecm)完成两数相除功能。
* (2)在main()方法中使用异常处理语句进行异常处理。
* (3)在程序中,自定义对应输入负数的异常类(EcDef)。
* (4)运行时接受参数 java EcmDef 20 10 //args[0]=“20” args[1]=“10”
* (5)Interger类的static方法parseInt(String s)将s转换成对应的int值。
* 如:int a=Interger.parseInt(“314”); //a=314;
*
*/
public class EcmDef {
public static void main(String[] args) {
try {
int m = Integer.parseInt(args[0]);
int n = Integer.parseInt(args[1]);
int result = ecm(m, n);
System.out.println("结果为:" + result);
} catch (EcDef e) {
System.out.println(e.getMessage());
} catch (NumberFormatException e) {
System.out.println("数据类型不一致");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("缺少命令行参数");
} catch (ArithmeticException e) {
System.out.println("除0");
}
}
public static int ecm(int i, int j) throws EcDef {
if (i < 0 || j < 0) {
//手动抛出异常类的对象
throw new EcDef("输入负数了");
}
return i / j;
}
}
//自定义异常类
class EcDef extends Exception {
static final long serialVersionUID = -33875169929948L;
public EcDef() {
}
public EcDef(String message) {
super(message);
}
}
5. String的声明
public final class String
* implements java.io.Serializable, Comparable<String>, CharSequence
*
* final:String不可被继承
* Serializable : 表明String是可以序列化的
* Comparable:String的对象是可以比较大小的
* 内部存储数据,使用 private final char value[]; ---> jdk8
6. String的不可变性
String的不可变性
* 情况1:字符串通过字面量的方式进行了赋值,如果对此变量进行了重新赋值,
* 则新的值不能在原有字符串常量池字面量的位置进行修改。必须重新指定一个新的位置赋值。
* 情况2: 使用字符串拼接操作给字符串赋新的值,不能再原有字符串常量池的位置上进行拼接,
* 必须开辟新的空间存放拼接以后的字符串内容。
* 情况3: 调用String的replace()修改字符串内的指定字符时,也必须重新开辟空间保存修改以后的字符串。
* 原有的字符串不会被修改。
@Test
public void test1(){
String str1 = "hello";
String str2 = "hello";
System.out.println(str1 == str2);//true
str1 = "world";
System.out.println(str1);//world
System.out.println(str2);//hello
}
@Test
public void test2(){
String str1 = "hello";
String str2 = "hello";
str1 += "world";
System.out.println(str1);//helloworld
System.out.println(str2);//hello
}
@Test
public void test3(){
String str1 = "hello";
String str2 = str1.replace('l', 'w');
System.out.println(str1);//hello
System.out.println(str2);//hewwo
}
@Test
public void test4(){
//效率低
// String s1 = "a";
// for(int i = 0;i < 10;i++){
// s1 += "b";
// }
Person p1 = new Person("Tom",12);
Person p2 = new Person("Tom",12);
System.out.println(p1.name == p2.name);//true
p1.name = "Jerry";
System.out.println(p2.name);//Tom
}
7. String的实例化方式
/*
* String的实例化方式:
* 方式一:字面量的赋值方式
* 方式二:new + 构造器的方式
*
*
* 1. 以字面量的方式给String赋值,此时的字面量数据存储在String常量池中。
* 2. String常量的存放位置:
* jdk6 : 存放在方法区(永久代)
* jdk7及以后:存放到堆空间
*
* 3. 字符串常量池中不能存放相同字面量的两个字符串数据。
*
* 面试题:new String("hello")在内存中创建了几个对象? 两个
*
*
* */
@Test
public void test5(){
//方式一:字面量的赋值方式
String s1 = "hello";
//方式二:new + 构造器的方式
String s2 = new String("hello");
System.out.println(s1 == s2);//false
}
8. String的拼接操作
/**
* 常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。
* 只要其中有一个是变量,结果就在堆中,相当于新new了一个字符串对象。
* 如果拼接的结果调用intern()方法,返回值就在常量池中
*/
@Test
public void test6(){
String s1 = "hello";
String s2 = "world";
String s3 = "helloworld";
String s4 = "hello" + "world";
String s5 = s1 + "world";
String s6 = "hello" + s2;
String s7 = s1 + s2;
String s8 = s5.intern();
System.out.println(s3 == s4);
System.out.println(s3 == s5);
System.out.println(s3 == s6);
System.out.println(s3 == s7);
System.out.println(s5 == s6);
System.out.println(s5 == s7);
System.out.println(s6 == s7);
System.out.println(s3 == s8);
}
9. String的常用方法
/*
* int length():返回字符串的长度: return value.length
char charAt(int index): 返回某索引处的字符return value[index]
boolean isEmpty():判断是否是空字符串:return value.length == 0
String toLowerCase():使用默认语言环境,将 String 中的所有字符转换为小写
String toUpperCase():使用默认语言环境,将 String 中的所有字符转换为大写
String trim():返回字符串的副本,忽略前导空白和尾部空白
boolean equals(Object obj):比较字符串的内容是否相同
boolean equalsIgnoreCase(String anotherString):与equals方法类似,忽略大小写
String concat(String str):将指定字符串连接到此字符串的结尾。 等价于用“+”
int compareTo(String anotherString):比较两个字符串的大小
String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。
String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。
* */
@Test
public void test1(){
String str = "";
// str = null;
System.out.println(str.isEmpty());
String str1 = "abc123我ABC";
System.out.println(str1.toLowerCase());
System.out.println(str1.toUpperCase());
String str2 = " he llo world ";
System.out.println("***" + str2.trim() + "###");
String str3 = "abc";
String str4 = "abe";
System.out.println(str3.compareTo(str4));
String str5 = "helloworld";
String str6 = str5.substring(3);
System.out.println(str6);//loworld
System.out.println(str5);//helloworld
String str7 = str5.substring(3,6);
System.out.println(str7);//low
}
/*
* boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束
boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始
boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始
*
* boolean contains(CharSequence s):当且仅当此字符串包含指定的 char 值序列时,返回 true
int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引。如果没有此字符串,则返回-1
int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始
int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引
int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索
注:indexOf和lastIndexOf方法如果未找到都是返回-1
* */
@Test
public void test2(){
String str = "helloworldo";
System.out.println(str.endsWith("orld"));
System.out.println(true);
System.out.println(str.indexOf("owo"));//4
System.out.println(str.indexOf("o",5));//6
System.out.println(str.lastIndexOf("o"));//10
}
10. String与其他结构之间的转换
/*
* 基本数据类型、包装类 ---> String : ① + ② valueOf(Xxx xxx)
*
* String ----> 基本数据类型、包装类 : 调用包装类Xxx的parseXxx(String s)
* */
@Test
public void test1() {
String str1 = String.valueOf(123);
int i = Integer.parseInt(str1);
System.out.println(i);
}
/*
* String 与char[]之间的转换
* String ---> char[] : 调用字符串类的toCharArray()
* char[] ----> String : 调用构造器
* */
@Test
public void test2() {
String str = "hello我";
char[] arr = str.toCharArray();
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i] + "@");
}
char[] arr1 = new char[]{'a', 'b', 'c'};
String str1 = new String(arr1);
System.out.println(str1);//"abc"
}
/*
* String 与byte[]之间的转换:
*
*String--->byte[] : 调用字符串类的getBytes()
* byte[] ---> String:调用构造器
* */
@Test
public void test3() {
String str = "hello";
byte[] bytes = str.getBytes();
System.out.println(bytes.length);//5
for (int i = 0; i < bytes.length; i++) {
System.out.println((char) bytes[i]);
}
String str1 = new String(bytes);
System.out.println(str1);
}
/*
* 内存层面: char 相当于2个字节, byte就表示1个字节
*
* 存储层面:
* ascii: 只能保存a-z,A-Z,0-9,常用的标点符号等,每个字符使用一个字节存储即可。
*
* utf-8:向下兼容ascii,即每个ascii中包含的字符仍然使用一个字节存储。
* 每个汉字使用3个字节存储。
*
* gbk:向下兼容ascii,即每个ascii中包含的字符仍然使用一个字节存储。
* 每个汉字使用2个字节存储。
*
*
* */
@Test
public void test4() throws UnsupportedEncodingException {
String str = "hello我";
byte[] bytes = str.getBytes(); //默认的字符集,与IDEA设置的字符集相同
System.out.println(bytes.length);//UTF-8 : 8
for (int i = 0; i < bytes.length; i++) {
System.out.println((char) bytes[i]);
}
byte[] bytes1 = str.getBytes("utf-8");
System.out.println(bytes1.length);//8
byte[] bytes2 = str.getBytes("gbk");
System.out.println(bytes2.length);//7
}