`

Java解惑中的相关总结二

    博客分类:
  • Java
 
阅读更多
异常
1、finally与中断
Java代码  
//该方法返回false  
static boolean f() {  
 try {  
  return true;  
 } finally {  
  return false;  
 }  
}  
不要用return、break、continue或throw来退出finally语句块,并且千万不要允许受检查的异常传播到finally语句
块之外。也就是说不要在finally块内终止程序,而是执行完finally块后,要将控制权移交给try块,由try最终决定
怎样结束方法的调用。
 
对于任何在finally语句块中可能抛出的受检查异常都要进行处理,而不是任其传播,下面流拷贝程序在关闭流时没有
防止异常的传播,这会有问题:
Java代码  
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代码  
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");  
 }  
}  

2、catch捕获异常规则
捕获RuntimeException、Exception或Throwable的catch语句是合法,不管try块里是否抛出了这三个异常。但如果try
块没有抛出或不可能抛出检测性异常,则catch不能捕获这些异常,如IOException异常:
Java代码  
public class Test {  
 public static void main(String[] args) {  
  try{  
   //...  
  }catch (Exception e) {  
     
  }catch (Throwable e) {  
     
  }  
    
  /* !! 编译出错 
   try{ 
    //... 
   }catch (IOException e) { 
     
   } 
   */  
 }  
}  

3、重写时方法异常范围
重写或实现时不能扩大异常的范围,如果是多继承,则异常取所有父类方法异常的交集或不抛出异常:
Java代码  
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 {  
 }  
}  

4、静态与非静态final常量不能在catch块中初始化
静态与非静态块中如果抛出了异常,则一定要使用try-catch块来捕获。
Java代码  
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进行赋值是很困难的问题。语言规范在这一点上采用了保守的方式。

5、System.exit()与finally
Java代码  
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代码  
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类的相关方法来实现的。

6、递归构造
Java代码  
public class S  {  
 private S instance = new S();  
 public S() {}  
}  
如果在程序外面构造该类的实例,则会抛出java.lang.StackOverflowError错误。其原因是实例变量的初始化操作将
先于构造器的程序体而运行。

7、构造器中的异常
如果父类构造器抛出了检测异常,则子类也只能抛出,而不能采用try-catch来捕获:
Java代码  
public class P {  
 public P() throws Exception {}  
}  
  
class S extends P {  
 public S() throws Exception {  
  try {  
   // 不能在try块中明确调用父类构造器,因为构造的  
   // 明确调用只能放在第一行  
   // !! super();  
  //try-catch不能捕获到父类构造器所抛出的异常,子类只能抛出  
  } catch (Exception e) {  
  }  
 }  
}  
 
如果初使化实例属性时抛出了异常,则构造器只能抛出异常,在构造器中捕获不起作用:
Java代码  
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;  
    }  
}  

8、StackOverflowError
Java虚拟机对栈的深度限制到了某个值,当超过这个值时,VM就抛出StackOverflowError。一般VM都将栈的深度限制
为1024,即当方法调用方法的层次超过1024时就会产生StackOverflowError。异常

9、finally与中断
Java代码  
//该方法返回false  
static boolean f() {  
 try {  
  return true;  
 } finally {  
  return false;  
 }  
}  
不要用return、break、continue或throw来退出finally语句块,并且千万不要允许受检查的异常传播到finally语句
块之外。也就是说不要在finally块内终止程序,而是执行完finally块后,要将控制权移交给try块,由try最终决定
怎样结束方法的调用。
 
对于任何在finally语句块中可能抛出的受检查异常都要进行处理,而不是任其传播,下面流拷贝程序在关闭流时没有
防止异常的传播,这会有问题:
Java代码  
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代码  
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");  
 }  
}  

10、catch捕获异常规则
捕获RuntimeException、Exception或Throwable的catch语句是合法,不管try块里是否抛出了这三个异常。但如果try
块没有抛出或不可能抛出检测性异常,则catch不能捕获这些异常,如IOException异常:
Java代码  
public class Test {  
 public static void main(String[] args) {  
  try{  
   //...  
  }catch (Exception e) {  
     
  }catch (Throwable e) {  
     
  }  
    
  /* !! 编译出错 
   try{ 
    //... 
   }catch (IOException e) { 
     
   } 
   */  
 }  
}  

11、重写时方法异常范围
重写或实现时不能扩大异常的范围,如果是多继承,则异常取所有父类方法异常的交集或不抛出异常:
Java代码  
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 {  
 }  
}  

12、静态与非静态final常量不能在catch块中初始化
静态与非静态块中如果抛出了异常,则一定要使用try-catch块来捕获。
Java代码  
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进行赋值是很困难的问题。语言规范在这一点上采用了保守的方式。

13、System.exit()与finally
Java代码  
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代码  
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类的相关方法来实现的。

14、递归构造
Java代码  
public class S  {  
 private S instance = new S();  
 public S() {}  
}  
如果在程序外面构造该类的实例,则会抛出java.lang.StackOverflowError错误。其原因是实例变量的初始化操作将
先于构造器的程序体而运行。

15、构造器中的异常
如果父类构造器抛出了检测异常,则子类也只能抛出,而不能采用try-catch来捕获:
Java代码  
public class P {  
 public P() throws Exception {}  
}  
  
class S extends P {  
 public S() throws Exception {  
  try {  
   // 不能在try块中明确调用父类构造器,因为构造的  
   // 明确调用只能放在第一行  
   // !! super();  
  //try-catch不能捕获到父类构造器所抛出的异常,子类只能抛出  
  } catch (Exception e) {  
  }  
 }  
}  
 
如果初使化实例属性时抛出了异常,则构造器只能抛出异常,在构造器中捕获不起作用:
Java代码  
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。

 

分享到:
评论

相关推荐

    java解惑 PDF版

    文档《java解惑 PDF版》中列举了95个这样的谜题,每个谜题都旨在帮助开发者理解并纠正一些常见的错误理解。以下是根据提供的部分内容解析的几个相关知识点。 ### 表达式谜题与取余操作符(%)的行为 在Java中,...

    java解惑(包括pdf和答案)

    Java是一种广泛使用的面向对象的编程语言,以其跨平台性、高效性和丰富的类库而闻名。...在学习过程中,不断实践和总结是掌握任何编程语言的关键,"java解惑"提供的资源无疑是这样的一个实践和学习的优秀工具。

    JAVA解惑(JAVA谜题) 中文版(PDF)

    ### JAVA解惑中的关键知识点解析 #### 谜题1:奇数性的判断 在《JAVA解惑》一书中,作者通过一系列实例介绍了Java编程语言中的一些不易掌握的知识点。其中一个例子是关于如何正确判断一个整数是否为奇数。 **原始...

    java 解惑

    3. **java解惑.pdf**:这很可能与博客主题相呼应,详细解答了Java编程中的疑惑,比如异常处理、多线程、集合框架、内存管理等复杂话题。 4. **网络安全防护措施百分百.ppt**:网络安全是任何应用开发都不可忽视的...

    JAVA解惑 中文pdf版

    **谜题背景**: 在《JAVA解惑》这本书中提到了第一个谜题:如何判断一个整数是否为奇数。该谜题提供了一个看似合理的解决方案,但实际运行时会出现问题。 **原方法实现**: ```java public static boolean isOdd(int ...

    Java解惑中文

    标题《Java解惑中文》很可能是某本专注于解决Java编程中常见疑惑、误区或陷阱的书籍的中文版。这类书籍通常由经验丰富的Java开发者编写,目的是帮助其他开发者深入理解Java语言和相关类库的内在工作原理,同时学习...

    Java解惑PPT8

    Java编程语言中有许多微妙而有趣的细节,这些细节可能会在开发过程中造成困扰,这就是"Java解惑"系列试图解决的问题。本文将深入探讨PPT8中提及的三个Java谜题:Puzzle 76 乒乓、Puzzle 77 搞乱锁的妖怪和Puzzle 78 ...

    java谜题解惑 中文chm

    刚刚由本人收集整理,共享给各位java爱好者。 本书深入研究Java编程... 本书以轻松诙谐的语言,寓教于乐的方式,由浅入深、总结归纳Java编程语言的知识点,适合具有Java知识的学习者和有编程经验的Java程序员阅读。

    Java解惑中文版(带索引)

    ### Java解惑中文版(带索引)知识点详解 #### 一、理解Java中的奇数检测方法 在Java中,判断一个整数是否为奇数是常见的编程需求。本章节介绍了一个具体的例子来探讨如何正确地实现这一功能。 **原始方法实现**:...

    JAVA解惑.doc

    在《JAVA解惑》文档中的第一个谜题关注的是一个简单的逻辑判断:一个整数是否为奇数。虽然这个问题看似简单,但在Java语言中却隐藏着一个容易被忽视的陷阱。 **谜题描述** 代码片段提供了一个名为`isOdd`的方法,...

    Java解惑.pdf

    ### Java解惑:深入理解Java中的谜题与陷阱 #### 表达式谜题:奇数判断误区 在探讨Java编程中的谜题时,我们首先遇到的是关于奇数判断的一个常见陷阱。根据“Java谜题”一书的中文版描述,一个简单的函数`public ...

    JAVA解惑 大量java实例 Java程序员阅读

    在Java中,判断一个整数是否为奇数的常见方法是通过取余操作符(%)来实现。例如,判断方法`isOdd(int i)`使用`return i % 2 == 1;`来判断传入的整数`i`是否为奇数。这个方法看起来直观且正确,但在处理负数时会出现...

    Java解惑PPT6

    在"Java解惑PPT6"中,我们探讨了几个关键的Java特性,特别是关于不变性、equals()和hashCode()方法的约定以及它们在HashSet中的应用。 首先,让我们来看一下Library Puzzle(Puzzle 56)中的Big Problem。这个例子...

Global site tag (gtag.js) - Google Analytics