当前位置: 首页 > news >正文

java解惑你知多少(四)

异常

26. finally与中断

Java代码   收藏代码
  1. //该方法返回false   
  2. static   boolean  f() {  
  3.  try  {  
  4.   return   true ;  
  5.  } finally  {  
  6.   return   false ;  
  7.  }  
  8. }  
//该方法返回false
static boolean f() {
 try {
  return true;
 } finally {
  return false;
 }
}

不要用return、break、continue或throw来退出finally语句块,并且千万不要允许受检查的异常传播到finally语句

块之外。也就是说不要在finally块内终止程序,而是执行完finally块后,要将控制权移交给try块,由try最终决定

怎样结束方法的调用。

 

对于任何在finally语句块中可能抛出的受检查异常都要进行处理,而不是任其传播,下面流拷贝程序在关闭流时没有

防止异常的传播,这会有问题:

Java代码   收藏代码
  1. static   void  copy(String src, String dest)  throws  IOException {  
  2.  InputStream in = null ;  
  3.  OutputStream out = null ;  
  4.  try  {  
  5.   in = new  FileInputStream(src);  
  6.   out = new  FileOutputStream(dest);  
  7.   byte [] buf =  new   byte [ 1024 ];  
  8.   int  n;  
  9.   while  ((n = in.read(buf)) >=  0 ) {  
  10.    out.write(buf, 0 , n);  
  11.   }  
  12.  } finally {  
  13.   //这里应该使用try-catch将每个close包装起来   
  14.   if (in !=  null ){in.close();}  
  15.   if (in !=  null ){out.close();}  
  16.  }  
  17. }  
static void copy(String src, String dest) throws IOException {
 InputStream in = null;
 OutputStream out = null;
 try {
  in = new FileInputStream(src);
  out = new FileOutputStream(dest);
  byte[] buf = new byte[1024];
  int n;
  while ((n = in.read(buf)) >= 0) {
   out.write(buf, 0, n);
  }
 } finally{
  //这里应该使用try-catch将每个close包装起来
  if(in != null){in.close();}
  if(in != null){out.close();}
 }
}

catch块中的return语句是不会阻止finally块执行的,那么catch块中的continue和break能否阻止?答案是不会的,

与return一样,finally语句块是在循环被跳过(continue)和中断(break)之前被执行的:

Java代码   收藏代码
  1. int  i =  0 ;  
  2. System.out.println("--continue--" );  
  3. while  (i++ <=  1 ) {  
  4.  try  {  
  5.   System.out.println("i="  + i);  
  6.   continue ;  
  7.  } catch  (Exception e) {  
  8.  } finally  {  
  9.   System.out.println("finally" );  
  10.  }  
  11. }  
  12. System.out.println("--break--" );  
  13. while  (i++ <=  3 ) {  
  14.  try  {  
  15.   System.out.println("i="  + i);  
  16.   break ;  
  17.  } catch  (Exception e) {  
  18.  } finally  {  
  19.   System.out.println("finally" );  
  20.  }  
  21. }  
int i = 0;
System.out.println("--continue--");
while (i++ <= 1) {
 try {
  System.out.println("i=" + i);
  continue;
 } catch (Exception e) {
 } finally {
  System.out.println("finally");
 }
}
System.out.println("--break--");
while (i++ <= 3) {
 try {
  System.out.println("i=" + i);
  break;
 } catch (Exception e) {
 } finally {
  System.out.println("finally");
 }
}

27. catch捕获异常规则

捕获RuntimeException、Exception或Throwable的catch语句是合法,不管try块里是否抛出了这三个异常。但如果try

块没有抛出或不可能抛出检测性异常,则catch不能捕获这些异常,如IOException异常:

Java代码   收藏代码
  1. public   class  Test {  
  2.  public   static   void  main(String[] args) {  
  3.   try {  
  4.    //...   
  5.   }catch  (Exception e) {  
  6.      
  7.   }catch  (Throwable e) {  
  8.      
  9.   }  
  10.     
  11.   /* !! 编译出错  
  12.    try{  
  13.     //...  
  14.    }catch (IOException e) {  
  15.       
  16.    }  
  17.    */   
  18.  }  
  19. }  
public class Test {
 public static void main(String[] args) {
  try{
   //...
  }catch (Exception e) {
   
  }catch (Throwable e) {
   
  }
  
  /* !! 编译出错
   try{
    //...
   }catch (IOException e) {
    
   }
   */
 }
}

28. 重写时方法异常范围

重写或实现时不能扩大异常的范围,如果是多继承,则异常取所有父类方法异常的交集或不抛出异常:

Java代码   收藏代码
  1. interface  I1 {  
  2.  void  f()  throws  Exception;  
  3. }  
  4.   
  5. interface  I2 {  
  6.  void  f()  throws  IOException;  
  7. }  
  8.   
  9. interface  I3  extends  I1, I2 {}  
  10.   
  11. class  Imp  implements  I3 {  
  12.  // 不能编译通过,多继承时只能取父类方法异常交集,这样就不会扩大异常范围   
  13.  // !! void f () throws Exception;   
  14.  // void f();// 能编译通过   
  15.  // 能编译通过,Exception与IOException的交集为IOException   
  16.  public   void  f()  throws  IOException {  
  17.  }  
  18. }  
interface I1 {
 void f() throws Exception;
}

interface I2 {
 void f() throws IOException;
}

interface I3 extends I1, I2 {}

class Imp implements I3 {
 // 不能编译通过,多继承时只能取父类方法异常交集,这样就不会扩大异常范围
 // !! void f () throws Exception;
 // void f();// 能编译通过
 // 能编译通过,Exception与IOException的交集为IOException
 public void f() throws IOException {
 }
}

29. 静态与非静态final常量不能在catch块中初始化

 

静态与非静态块中如果抛出了异常,则一定要使用try-catch块来捕获。

Java代码   收藏代码
  1. public   class  Test {  
  2.  static   final   int  i;  
  3.  static  {  
  4.   try  {  
  5.    i = f();  
  6.   } catch  (RuntimeException e) {  
  7.    i = 1 ;  
  8.   }  
  9.  }  
  10.   
  11.  static   int  f() {  
  12.   throw   new  RuntimeException();  
  13.  }  
  14. }  
public class Test {
 static final int i;
 static {
  try {
   i = f();
  } catch (RuntimeException e) {
   i = 1;
  }
 }

 static int f() {
  throw new RuntimeException();
 }
}

上面的程序编译不能通过。表面上是可以的,因为i第一次初始化时可能抛出异常,所以抛异常时可以在catch块中初

始化,最终还是只初始化一次,这正是空final所要求的,但为什么编译器不知道这些呢?

 

要确定一个程序是否不止一次地对一个空final进行赋值是很困难的问题。语言规范在这一点上采用了保守的方式。

30. System.exit()与finally

Java代码   收藏代码
  1. try  {  
  2.  System.out.println("Hello world" );  
  3.  System.exit(0 );  
  4.  // 或者使用Runtime退出系统   
  5.  // Runtime.getRuntime().exit(0);   
  6. finally  {  
  7.  System.out.println("Goodbyte world" );  
  8. }  
try {
 System.out.println("Hello world");
 System.exit(0);
 // 或者使用Runtime退出系统
 // Runtime.getRuntime().exit(0);
} finally {
 System.out.println("Goodbyte world");
}

上面的程序会打印出"Goodbyte world"吗?不会。

 

System.exit将立即停止所有的程序线程,它并不会使finally语句块得到调用,但是它在停止VM之前会执行关闭挂钩

操作(这此挂钩操作是注册到Runtime.addShutdownHook上的线程),这对于释放VM之外的资源很有帮助。使用挂钩程

序修改上面程序:

Java代码   收藏代码
  1. System.out.println( "Hello world" );  
  2. Runtime.getRuntime().addShutdownHook(new  Thread() {  
  3.  public   void  run() {  
  4.   System.out.println("Goodbyte world" );  
  5.  }  
  6. });  
  7. System.exit(0 );  
System.out.println("Hello world");
Runtime.getRuntime().addShutdownHook(new Thread() {
 public void run() {
  System.out.println("Goodbyte world");
 }
});
System.exit(0);

 

另外,对象回收时,使用VM调用对象的finalize()方法有两种:
System.runFinalization():该方法让虚拟机也只是尽最大努力去完成所有未执行的finalize()终止方法,但不一定

会执行。
System.runFinalizersOnExit(true):该方法一定会回收,但不安全,已被废弃。因为它可能对正在使用的对象调用

终结方法,而其他线程同时正在操作这些对象,从而导致不正确的行为或死锁。

 

为了加快垃圾回收,使用System.gc(),但不一定马上执行加收动作,由虚拟机决定,实质上是调用

Runtime.getRuntime().gc()。

 

System的很多方法都是调用Runtime类的相关方法来实现的。


31. 递归构造

Java代码   收藏代码
  1. public   class  S  {  
  2.  private  S instance =  new  S();  
  3.  public  S() {}  
  4. }  
public class S  {
 private S instance = new S();
 public S() {}
}

如果在程序外面构造该类的实例,则会抛出java.lang.StackOverflowError错误。其原因是实例变量的初始化操作将

先于构造器的程序体而运行。


32. 构造器中的异常

如果父类构造器抛出了检测异常,则子类也只能抛出,而不能采用try-catch来捕获:

Java代码   收藏代码
  1. public   class  P {  
  2.  public  P()  throws  Exception {}  
  3. }  
  4.   
  5. class  S  extends  P {  
  6.  public  S()  throws  Exception {  
  7.   try  {  
  8.    // 不能在try块中明确调用父类构造器,因为构造的   
  9.    // 明确调用只能放在第一行   
  10.    // !! super();   
  11.   //try-catch不能捕获到父类构造器所抛出的异常,子类只能抛出   
  12.   } catch  (Exception e) {  
  13.   }  
  14.  }  
  15. }  
public class P {
 public P() throws Exception {}
}

class S extends P {
 public S() throws Exception {
  try {
   // 不能在try块中明确调用父类构造器,因为构造的
   // 明确调用只能放在第一行
   // !! super();
  //try-catch不能捕获到父类构造器所抛出的异常,子类只能抛出
  } catch (Exception e) {
  }
 }
}

 
如果初使化实例属性时抛出了异常,则构造器只能抛出异常,在构造器中捕获不起作用:

Java代码   收藏代码
  1. public   class  A {  
  2.     private  String str = String. class .newInstance();  
  3.   
  4.     public  A()  throws  InstantiationException, IllegalAccessException {}  
  5.   
  6.     public  A( int  i)  throws  Exception {  
  7.         try  { //即使这里捕获了,方法签名还是得要抛出   
  8.   
  9.         } catch  (Exception e) {  
  10.   
  11.         }  
  12.     }  
  13.   
  14.     /*  
  15.      * !!编译不能通过,因为str2为静态的,他不能通过构造器来捕获,所以只  
  16.      * 能使用静态方法来捕获。即初始化静态成员时不能抛出捕获性异常。   
  17.      */   
  18.     //!!private static String str2 = String.class.newInstance();   
  19.       
  20.     // 只能使用静态方法来捕获异常,如果是抛出的运行时异常则不需要捕获   
  21.     private   static  String str2 = newInstance();  
  22.   
  23.     private   static  String newInstance()  throws  RuntimeException {  
  24.         try  {  
  25.             return  String. class .newInstance();  
  26.         } catch  (Exception e) {  
  27.             e.printStackTrace();  
  28.         }  
  29.         return   null ;  
  30.     }  
  31. }  
public class A {
	private String str = String.class.newInstance();

	public A() throws InstantiationException, IllegalAccessException {}

	public A(int i) throws Exception {
		try {//即使这里捕获了,方法签名还是得要抛出

		} catch (Exception e) {

		}
	}

	/*
	 * !!编译不能通过,因为str2为静态的,他不能通过构造器来捕获,所以只
	 * 能使用静态方法来捕获。即初始化静态成员时不能抛出捕获性异常。 
	 */
	//!!private static String str2 = String.class.newInstance();
	
	// 只能使用静态方法来捕获异常,如果是抛出的运行时异常则不需要捕获
	private static String str2 = newInstance();

	private static String newInstance() throws RuntimeException {
		try {
			return String.class.newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
}

33. StackOverflowError

Java虚拟机对栈的深度限制到了某个值,当超过这个值时,VM就抛出StackOverflowError。一般VM都将栈的深度限制

为1024,即当方法调用方法的层次超过1024时就会产生StackOverflowError。

 

相关文章:

  • Android selector的使用
  • java解惑你知多少(五)
  • 快速傅里叶变换FFT学习小记
  • java解惑你知多少(六)
  • ThinkPHP3.2.3扩展之生成PDF文件(MPDF)
  • codeforces 711E E. ZS and The Birthday Paradox(数学+概率)
  • java解惑你知多少(七)
  • css3 TransformZ() 3D缩放
  • java解惑你知多少(八)
  • 多线程总结之旅(8):线程同步之信号量
  • java类初始化顺序
  • bootstrap总结
  • java创建对象的四种方式
  • java基础之String
  • 为什么单例对象的并发调用需要同步?
  • Angular 2 DI - IoC DI - 1
  • C++11: atomic 头文件
  • React Native移动开发实战-3-实现页面间的数据传递
  • 搭建gitbook 和 访问权限认证
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 基于web的全景—— Pannellum小试
  • 马上搞懂 GeoJSON
  • 深入浅出Node.js
  • 问题之ssh中Host key verification failed的解决
  • 延迟脚本的方式
  • 一道面试题引发的“血案”
  • 移动端解决方案学习记录
  • 移动互联网+智能运营体系搭建=你家有金矿啊!
  • 你对linux中grep命令知道多少?
  • 专访Pony.ai 楼天城:自动驾驶已经走过了“从0到1”,“规模”是行业的分水岭| 自动驾驶这十年 ...
  • #QT(一种朴素的计算器实现方法)
  • #vue3 实现前端下载excel文件模板功能
  • #我与Java虚拟机的故事#连载16:打开Java世界大门的钥匙
  • #在 README.md 中生成项目目录结构
  • (51单片机)第五章-A/D和D/A工作原理-A/D
  • (Forward) Music Player: From UI Proposal to Code
  • (LeetCode 49)Anagrams
  • (亲测有效)解决windows11无法使用1500000波特率的问题
  • (四)搭建容器云管理平台笔记—安装ETCD(不使用证书)
  • (转)linux 命令大全
  • (转载)从 Java 代码到 Java 堆
  • .net 发送邮件
  • .NET6 命令行启动及发布单个Exe文件
  • .Net8 Blazor 尝鲜
  • .NET的数据绑定
  • .NET精简框架的“无法找到资源程序集”异常释疑
  • .net开发引用程序集提示没有强名称的解决办法
  • .NET上SQLite的连接
  • .NET设计模式(8):适配器模式(Adapter Pattern)
  • .NET中使用Protobuffer 实现序列化和反序列化
  • @RequestBody的使用
  • @德人合科技——天锐绿盾 | 图纸加密软件有哪些功能呢?
  • [20170705]lsnrctl status LISTENER_SCAN1
  • [2021]Zookeeper getAcl命令未授权访问漏洞概述与解决
  • [8481302]博弈论 斯坦福game theory stanford week 1