JavaSE(4):java异常处理与常用类


四、java异常处理与常用类

 

一、java异常处理

 

、异常:就是不正常,是指程序在运行时出现的问题,错误

、异常才处理机制:java中为了分离错误处理代码与源代码,采用了面向对象的方式来处理异常,异常也被看做是对象,而且和一般的对象无区别,但是异常必须是Throwable类及其子类所产生的对象。

、抛出异常,指正在运行的方法或是java虚拟机生成的异常类对象的产生并提交给jre的过程称为抛出一个异常。

、捕获异常,jre在得到一个异常对象时,会寻找相应的处理方法,沿着生成异常的方法回溯调用栈,知道找到包含相应异常处理的方法为止,然后jre将当前异常对象交给这个方法处理,这一过程叫做捕获一个异常。

、异常体系 Throwable

Error:指JVM出现重大问题,如运行的类不存在或是内存溢出等,不需要编写代码做出对应处理,程序无法处理,出现则程序中断。

Exception在运行时运行出现的一些情况,可以通过trycatchfinally处理。



6 、异常分类与结构。

编译时被检查的异常,Checked异常,在程序中必须使用try…catch处理。

编译时不被检测的异常,Runtime异常,可以不用try…catch处理,一旦出现就由JVM处理。

Runtime异常:指程序设计或实现方法不当在程序运行期间产生的异常,所以说是一种可以完全避免的异常。而且这种异常编译器不会检测,即使不处理也没关系,但是一旦出现,程序就会异常终止。若是有异常处理代码,就会以异常抓抛模型进行处理

Checked异常:除了RuntimeException之外其他的Exception及其子类都是受检查的异常,Java编译器会去检查他们,必须通过try…catch或是throw语句声明抛异常,否则编译不通过,这种异常,要求程序必须处理。

 

7 、异常的处理方法

1、try…catch捕获处理异常:

try{可能会发生异常的代码}且try中一个语句发生了异常,则try中该 语句之后的语句不会在执行。

catch{异常类对象} … 可以多个catch块,但是范围小的异常类型居前

finally{可选代码块,一定会执行的代码块,除非在catch中有 System.exit(1)语句}

 Exception.java (1测试return和finally的先后顺序,2异常捕获的范围问题(原因,java中不能到达的代码是一个错误,若父类异常范围在前,则之后的子类异常catch代码块永远不会到达),3一个catch捕获多个异常类的实现)

package blog4;

/**
 * 本例程主要测试下列三点:
 *  1 测试return和finally的先后顺序
 *  2 异常捕获的范围问题
 *  3 一个catch捕获多个异常类的实现 (以数组下标越界和空指针以及数学异常为例)
 * 
 */
public class Exception {

	public static void test1() {
		String friends[] = { "kelly", "sandy", "Mike", "Chery" };
		try {// 不用try…catch捕捉也能编译通过,因为是运行时异常
			for (int i = 0; i <= 4; i++) {
				// 数组下标越界异常
				System.out.println(friends[i]);
			}
			// 数学异常
			System.out.println("the next is math exception"); // 未执行
			@SuppressWarnings("unused")
			int num = friends.length / 0;

			// 实际上上列try代码块在发生数组越界异常后就不执行数学异常的语句了,因为程序先捕获了该异常
		} catch (ArrayIndexOutOfBoundsException e) {
			e.printStackTrace();
		} catch (ArithmeticException e) {
			e.printStackTrace();
		}
	}

	/*
	public static void test2() {
		String friends[] = { "kelly", "sandy", "Mike", "Chery" };
		try {// 不用try…catch捕捉也能编译通过,因为是运行时异常
			for (int i = 0; i <= 4; i++) {
				// 数组下标越界异常
				System.out.println(friends[i]);
			}
		}catch (java.lang.Exception e){ 
			e.printStackTrace();
			//此处编译错误,因为Exception作为ArrayIndexOutOfBoundsException的父类
			//却优先捕获,则第二个catch块永远不会到达,这在java中是错误的(不能达到的代码)
		}catch (ArrayIndexOutOfBoundsException e){
			e.printStackTrace();
		}
	}*/
	
	public static int test3() {
		int a = 0;
		try {
			//程序先执行这一句,但是函数却没跳出,但是记住了需要返回的值是0
			return a;
		} catch (java.lang.Exception e) {
			a++;
			e.printStackTrace();
		} finally{
			//此处在return a;之后执行,虽然改变a值,但是不改变返回值
			a++;
			//return a,若在finally中添加return,则下面的return -1不可达到,会编译错误
			//此处return a;会返回1,而覆盖了try中记录的返回值0.
		}
		//此处没有执行到
		return -1;
	}

	public static void main(String[] args) {
		Exception.test1();
		System.out.println(Exception.test3());
	}

}

 



2、try嵌套,多异常处理:

为了实现上文所说的当try中一个语句发生异常后,try中该句后面的语句不能执行,所以,需要使用try语句嵌套解决这个问题。异常前后关系会被推入一个堆栈。

TryNest.java(try嵌套的实现)

package blog4;

/**
 * try语句嵌套: 前文Exception.java可见,try中有多个语句时,前面的语句发生了异常,后续语句就不执行了
 * 解决:1、后续语句放在finally中,2、嵌套try语句(若后续语句可能发生异常)
 * 
 */
public class TryNest {

	@SuppressWarnings("unused")
	public static void test() {
		String friends[] = { "kelly", "sandy", "Mike", "Chery" };
		try {
			try {
				int num = friends.length / 0;
			} catch (ArithmeticException e) {
				e.printStackTrace();
			}

			// 即使发生了ArithmeticException此处for语句块还是会执行
			for (int i = 0; i <= 4; i++) {
				// 数组下标越界异常
				System.out.println(friends[i]);
			}

		} catch (ArrayIndexOutOfBoundsException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		TryNest.test();
	}

}


 

3、throws声明异常:

有时不用在方法中处理异常,需要将异常向上传递,由调用它的方法来处理这些异常,这种传递可以逐层向上传递,知道main方法,此时需要throws字句声明。格式为:

returnType  methodName(paramList) throws ExceptionList(逗号隔开,为非运行时异常,运行时异常可以不声明)

一个方法,调用了一个本来就已经用throws声明的方法,那么它要么try…catch处理,要么也向上throws抛出。

重写方法不能抛出比被重写方法范围更大的异常类型

 

 

4、throw关键字

自行抛出一个异常对象

 

ThrowCompare.java(对比throw和throws)

package blog4;

/**
 * throw与throw的对比: throws用在方法声明上,后面跟的是异常类,可以多个异常类,逗号连接 
 * throw用于方法内,后面跟的是唯一一个异常类的对象
 * 
 */
public class ThrowCompare {

	public static void test1() throws java.lang.Exception {
		int i = 10;
		System.out.println(i / 0);
	}

	public static void test2() throws java.lang.Exception {
		int a = 10;
		if (a > 0) {
			// 通过throw抛出异常后,同样需要在方法中用throws声明会抛出异常,或者在当前方法中
			// try catch处理
			throw new java.lang.Exception("test2 a>0 exception...");
		}
	}

	public static void main(String[] args) {
		try {
			ThrowCompare.test1();
			// 实际上,test1发生异常,所以test2执行不到
			ThrowCompare.test2();
		} catch (java.lang.Exception e) {
			e.printStackTrace();
		}
	}
}

 



5、finally关键字

异常的统一出口,不管try块是否异常,也不管哪个catch执行,finally块总会被执行,除非在try或是到达的catch块中调用了退出JVM系统的的system.exit(1)语句。

finally不能单独出现,而且不要再finally中使用return或是throw语句,否则会导致try或是catch中的return或是throw失效。

 

6、throw和catch同时使用实例

异常出现在当前方法中,程序只对异常进行部分处理,剩下的处理在调用者中完成,此时应再次抛出异常,让调用者捕捉到,使用catch中throw的方法。

ThrowInCatch.java

package blog4;

/**
 * 抛出异常后,本方法中执行一部分,调用者中做剩下的处理 此时用这种分开处理的方法,在Catch中再抛出异常的方法
 * 
 */
public class ThrowInCatch {

	public static void buy(String price) throws java.lang.Exception {
		try {
			if (price != null)
				Double.parseDouble(price);
		} catch (java.lang.Exception e) {
			e.printStackTrace();
			throw new java.lang.Exception("价格不能只能是数字组成");
		}
	}
	
	public static void main(String[] args) {
		try {
			ThrowInCatch.buy("abc");
		} catch (java.lang.Exception e) {
			e.printStackTrace();
		}
		//执行结果,打印两个异常
		//1、buy中的打印 2、main中的打印
		//其实对应的都是一个异常,但是它出现的第一次是程序自动产生的异常对象,在buy中捕获,第二次是
		//人工抛出的,在main中捕获
	}
}

 


7、自定义异常

自定义异常必须继承Exception或者其子类,可扩充自己的成员变量或是方法,反映更加丰富的异常信息以及对异常对象的处理功能。

UserException.java(自定义异常的实现)

 

package blog4;

public class UserException {
	public static void getScore(int x) throws TooHight,TooLow{
		if(x>100){
			throw new TooHight("score>100",x);
		}else if(x<0){
			throw new TooLow("score<0",x);
		}else{
			System.out.println("score is " + x);
		}
	}

	public static void main(String[] args) {
		try {
			UserException.getScore(10);
			UserException.getScore(120);
			UserException.getScore(-5); //未执行
		} catch (TooHight e) {
			e.printStackTrace();
			System.out.println(e.getMessage()+" score is "+e.score);
		} catch (TooLow e) {
			e.printStackTrace();
			System.out.println(e.getMessage()+" score is "+e.score);
		}
	}
}

@SuppressWarnings("serial")
class TooHight extends java.lang.Exception{
	int score; //在异常对象中保存异常的数据
	public TooHight(String mess,int score){
		super(mess);
		this.score = score;
	}
}

@SuppressWarnings("serial")
class TooLow extends java.lang.Exception{
	int score;
	public TooLow(String mess,int score){
		super(mess);
		this.score = score;
	}
}

 

二 、java常用类

 

1、String 类

String是不可变的字符序列,一旦创建内容不可变,直到对象销毁。

String类和StringBuffer类都是final声明的,没有子类。

注:常量池

JVM中一块独立的区域存放字符串常量和基本类型常量(public static final)。String使用private final char value[]来实现字符串的存储,也就是说String对象创建之后,就不能再修改此对象中储存的字符串内容,就是因为如此,才说String类型是不可变的。

单独使用“”引号创建的字符串都是常量,编译期就已经确定存储到常量池中,而使用new 创建的String对象,是运行期新创建的,会储存到堆内存中。使用只包含常量的字符串连接符如“aa”+“bb”创建的也是常量,编译期就能确定,已经确定存储到常量池中,使用包含变量的字符串连接符如“aa”+ s1创建的对象时运行期才创建的,储存在堆内存中。

使用“”引号创建字符串的过程:会现在缓冲池中查找相同的字符串,若有,则将其引用赋给字符串变量,若无,创建新的字符串,放到缓冲池中。

StringTest.java(equals和==的区别,+号的使用,缓冲池的理解,equals比较的是对象的内容,而==比较的是对象(引用型变量)的地址)

 

package blog4;


/**
 * 本例知识点:
 * 1 equals和==的区别,equals比较的是对象的内容,而==比较的是对象(引用型变量)的地址
 * 2 +号的使用
 * 3 缓冲池的理解
 *
 */
public class StringTest {

	public static void main(String[] args) {
		String s1 = "aabb";
		String s2 = "aabb";
		String s3 = new String("aabb");
		System.out.println(s1==s2); //true,s2和s1都是指向常量池中同一个string对象 
		System.out.println(s2==s3); //false,==比较的是对象是否相同,此处不同
		System.out.println(s2.equals(s3)); //true,equals比较的是内容,s2和s3相等
		System.out.println("**********分隔符***********");
		
		String s4 = "aabbcc";
		String s5 = "aabb" + "cc";//常量间用+号,还是在常量池中先搜索已存在的String
		String s6 = s1 + "cc";	//变量存在的+号,直接在堆中建立新的String对象
		System.out.println(s4==s5); //true
		System.out.println(s4==s6); //false
		System.out.println(s4.equals(s6)); // true
	}
}


String常用方法:

各种构造方法,length、charAt(int where)、getChars()、getBytes()、toCharArray()、equals(是否相等)、compareTo(大小对比)、subString(取子串)、startWith()、endWith()、indexOf()、lastIndexOf()、replace()、valueOf(将基本数据类型转成String)、大小写转换toLowCase\toUpperCase()、忽略前后空白trim()、以指定分隔标记拆分字符串split().

注:字符串为空有两种情况,1、null,2、“”;

 

2、StringBuffer和StringBuilder

StringBuffer和StringBuilder对象是可变的字符串,这样可以避免每次重新创建对象,提高了效率。

StringBuffer提供了字符串的动态添加、插入和替换操作。

StringBuffer是线程安全的,效率低,可变内容。StringBuilder是线程不安全的,性能高,推荐使用StringBuilder。

BufferBuilder.java(这两个类的用法,以及常用方法append、insert、delete、deleteCharAt、reverse(翻转字符串内容),实现将整数数组元素变成逗号连接的字符串)

package blog4;


/**
 * 本例测试StringBuffer与StringBuilder相关用法
 */
public class BufferBuilder {

	//1 将”ABCDE“变成”A,B,C,D“
	public static void test1(){
		/* 1 插入实现 利用insert方法
		StringBuffer sb = new StringBuffer("ABCDE");
		sb.deleteCharAt(sb.length()-1);
		System.out.println(sb);
		for(int i=1;i<sb.length();i+=2){
			sb.insert(i, ',');			
		}
		System.out.println(sb);
		*/
		
		// 2 追加方法,转成字符数组逐个添加形成新的字符串
		StringBuffer sb = new StringBuffer("ABCDE");
		char [] charArray = sb.toString().toCharArray();
		StringBuffer sb2 = new StringBuffer();
		for(int i=0;i<charArray.length-1;i++){
			sb2.append(charArray[i]).append(',');
		}
		sb2.deleteCharAt(sb2.length()-1);
		System.out.println(sb2);
	}
	
	
	// 2 将字符串前后翻转
	public static void test2(){
		StringBuilder sb = new StringBuilder("abcde");
		System.out.println(sb);
		sb.reverse();
		System.out.println(sb);
	}
	
	// 3 将一个整数数组的元素用逗号相连,形成一个字符串
	public static void test3(){
		StringBuilder sb = new StringBuilder();
		int [] intArray = {2,4,6,8,10};
		for(int a:intArray){
			sb.append(a).append(',');
		}
		sb.deleteCharAt(sb.length()-1);
		System.out.println(sb);
	}
	
	// 4 replace方法与append连缀
	public static void test4(){
		StringBuilder sBuilder = new StringBuilder();
		//若用new StringBuilder(5)此时参数5代表sBuilder容量,
		//但是若append追加字符串使它超出了原有容量,会继续扩大容量,不会溢出字符
		sBuilder.append("<hello>").append("<goodbye>").append("<cat>");
		System.out.println(sBuilder);
		
		// 若是1,2,则会把h替换成laolao
		sBuilder.replace(1, 1, "laolao");
		// 可见replace方法用“laolao”字符串替代了原字符串中一个字符位置,是插入操作
		System.out.println(sBuilder);
	}
	
	public static void main(String[] args) {
		BufferBuilder.test1();
		BufferBuilder.test2();
		BufferBuilder.test3();
		BufferBuilder.test4();
	}
}

 



3、Math和Random以及UUID

Math类:abs(绝对值)、max(最大值)、min(最小值)、PI(静态常量)、rint(返回最接近参数的整数值)、random(返回大于0.0小于1.0的double)、round(返回只入不舍的最近整数值)。

Random类:生成伪随机数。Next(下一个随机数)、nextInt(int n)产生一个0到n之间的随机数,java中的区间都是前开后闭的。

注:不指定种子的构造函数时系统根据当前时间生成种子,每个种子对应一个数列,相同的种子会得到相同数列,而不是数值。所以如果在构造函数中指定种子,会得到同一个数列。

UUIDUUID表示一个128位的值,经过特殊算法保证全网唯一,叫全局唯一标识符。它保证同一时空中所有机器都是唯一独立的。用到了以太网卡地址、纳秒级时间、芯片ID和随机数。

OtherClass.java(以上三个类的使用方法)

 

package blog4;

import java.util.Random;
import java.util.UUID;

/**
 * Math和Random以及UUID的使用
 *
 */
public class OtherClass {
	
	public static void main(String[] args) {
		//产生一个1—50的随机数,abs绝对值,Math也提供了random方法。
		int randomNumber = Math.abs(new Random().nextInt()%50)+1;
		int randomNumber2 = Math.abs(new Random().nextInt(51));
		// 上例可见nextInt与nextInt(int range)的区别
		int randomNumber4 = Math.round((float)Math.random()*100)%50+1;
		// round是取最接近整形(只入不舍)
		System.out.println(randomNumber + "..." + randomNumber2 + "..." + randomNumber4);
		
		UUID uuid = UUID.randomUUID();
		System.out.println(uuid.toString()); //全网唯一的随机数
	}
}

 

4、DateCalendarDateFormatSimpleDateFormat

 

Date类:逐渐已被Calendar取代。

 

Calendar类:抽象类,没有共有的构造方法,不能外部new得到对象,调用其静态方法getInstance获取Calendar对象。

常用方法:int get(int field)返回字段指定的值,年月日等、

 

DateFormat和SimpleDateFormat类:前者是对时间日期格式化的抽象类,不能new对象,只能通过工厂类方法返回DateFormat实例,后者是前者的子类。

Parse方法与format方法的使用是关键。

DataFormatTest.java(时间类及其格式化的综合运用)

 

package blog4;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

/**
 * 时间的综合运用
 * data类、Calendar类、DateFormat类、SimpleDateFormat类
 */
public class DateFormatTest {
	
	// Data与Calendar类的运用
	public static void test1(){
		Date date = new Date();
		System.out.println(date);
		System.out.println("1970-1-1 到现在的毫秒数为:" + date.getTime());
		
		// 抽象类,静态方法获取实例
		Calendar cDate = Calendar.getInstance();
		// 注:Calendar中的month从0开始,到11。
		System.out.println(cDate.get(Calendar.YEAR) + "-" + cDate.get(Calendar.MONTH));
	}
	
	// DateFormat类的使用
	public static void test2(){
		// 抽象类,只能用过工厂类方法返回实例
		// 获取日期格式实例
		DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT,Locale.CHINA);
		// 获取时间格式实例
		DateFormat dTime = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG);
		Date date = new Date();
		// 以两种格式输出当前时间
		String s1 = df.format(date);
		String s2 = dTime.format(date);
		System.out.println(s1);
		System.out.println(s2);
		System.out.println(date);
		
		// 逆操作,将当前格式的字符串转成一个date类型对象
		try {
			// 将相应格式的字符串转成date对象
			Date dd = df.parse("1991-6-21");
			// 将date对象以相应的DateFormat格式输出
			System.out.println(dTime.format(dd));
		} catch (ParseException e) {
			e.printStackTrace();
			System.out.println("不能转换");
		}
		
	}
	
	// SimpleDateFormat类的使用
	public static void test3(){
		// 指定一种格式,注意格式中的大小写问题
		SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy.MM.dd - HH:mm:ss");
		try {
			// 将该格式的字符串转成date对象
			Date date = sDateFormat.parse("1992.8.3 - 12:23:20");
			System.out.println(date); // 以date默认格式输出
			System.out.println(sDateFormat.format(date)); // 以指定格式输出		
		} catch (ParseException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		
		DateFormatTest.test1();
		System.out.println();
		
		DateFormatTest.test2();
		System.out.println();
		
		DateFormatTest.test3();
	}
}


5、System类与Runtime类

System类包含一些与系统有关的类字段和方法,不能被实例化,类中所有方法都是static的,可以直接被System调用,其三个静态属性:in、out、error,对应标准输入流和标准输出流及标准错误流。

常用方法:arraycopy()快速的复制数组、exit(int status)终止虚拟机、currentTimeMillis(返回当前时间,毫秒单位,通常用来记录程序各部分的运行时间)、gc(运行垃圾回收器)、get\setProgerties(获取\设置系统属性)

 

Runtime类:封装了java运行时环境,每一个java应用程序都有一个Runtime类实例,使程序与其运行时的环境相连接,通常不实例化该对象,都是通过调用静态方法getRunTime获取当前RunTime对象的引用,然后调用方法对java虚拟机状态进行控制。

常用方法:gc(运行垃圾回收器)。totalMemory、freeMemory查看堆内存空间的大小及剩余情况。


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值