12 我读Thinking in java 通过异常处理错误

Java的基本理念是“结构不佳的代码不能运行”

Java中异常处理的目的在于通过使用少于目前数量的代码来简化大型、可靠的程序的生成,并且通过这种方式可以使你更加自信:你的应用中并没有未处理的错误。

12.1 概念

“异常”这个词有“我对此感到意外”的意思。问题出现了,你也许不清楚该如何处理,但你的确知道不应该置之不理;你要停下来,看看是不是有别人或在别的地方,能够处理这个问题。只是在当前的环境中还没有足够的信息来解决这个问题,所以把这个问题提交到一个更高级别的环境中,在这里将作出正确的决定。
使用异常所带来的另一个相当明显的好处是,它往往能够降低错误代码的复杂度。如果不使用异常,那么就必须检查特定的错误,并在程序中的许多地方去处理它。而如果使用异常,那就不必在方法调用处进行检查,因为异常机制将保证能够捕获这个错误。并且,只需在一个地方处理错误,即所谓的异常处理程序中。这种方式不仅节省代码,而且把“描述在正常执行过程中做什么事”的代码和“出了问题怎么办”的代码分离。总之,与以前的错误处理方法相比,异常机制使代码的阅读、编写和调试工作更加井井有条。
其实简单的理解为,java的异常处理有两大主要用途:将当前无法处理的错误抛给其他可以处理这个错误的环境;将代码的逻辑与问题分离开。

12.2 基本异常

异常情形是指阻止当前方法或作用域继续执行的问题。把异常情形与普通问题相区分很重要,所谓的普通问题是指,在当前环境下能得到足够的信息,总能处理这个错误。而对于异常情形,就不能继续下去了,因为在当前环境下无法获得必要的信息来解决这问题。你所能做的就是从当天环境跳出,并且把问题提交给上一级环境。这就是抛出异常时所发生的事情。
异常使得我们可以将每件事都当作一个事务来考虑,而异常可以看护着这些事务的底线“... ...事务的基本保障是我们所需的在分布式计算中的异常处理。事务是计算机中的合同法,如果出了什么问题,我们只需要放弃整个计算。”我们还可以将异常看作是一种内建的恢复系统,因为(在细心的情况下)我们在程序中可以拥有各种不同的恢复点。如果程序的某部分失败了,异常将“恢复”到程序中某个已知的稳定点上。

12.2.1异常参数

与使用Java中的其他对象一样,我们总是用new在堆上创建异常对象,这也伴随着存储空间的分配和构造器的调用。所有标准异常类都有两个构造器:一个是默认构造器;另一个是接受字符串作为参数,以便能够把相关信息放入异常对象的构造器;

12.3 捕获异常

要明白异常时如何捕获的,必须首先理解监控区域(guarded region)的概念。它是一段可能产生异常的代码,并且后面跟着处理这些异常的代码。

12.3.1 try块

如果是在方法内部抛出了异常(或者在方法内部调用的其他方法抛出了异常),这个方法将在抛出异常的过程中结束。要是不希望方法就此结束,可以在方法内设置 一个特殊的块来捕获异常。因为在这个块里“尝试”各种(可能产生异常的)方法调用,所以成为try块。它是跟在try关键字之后的普通程序块:
    try {
       // Code that might generate exceptions 
    }

12.3.2 异常处理程序

当然,抛出的异常必须在某处得到处理。这个“地点”就是异常处理程序,而且针对每个要捕获的异常,得准备相应的处理程序。异常处理程序紧跟在try块之后,以关键字catch表示;
try {
       // Code that might generate exceptions
    } catch(Type1 id1) {
       // Handle exceptions of Type1
    } catch(Type2 id2) {
       // Handle exceptions of Type2
    }
异常处理程序必须紧跟在try块之后。当异常被抛出时,异常处理机制将负责搜寻参数与异常类型相匹配的第一个处理程序。然后进入catch子句执行,此时认为异常得到了处理。一但catch子句结束,则处理程序的查找过程结束。注意,只有匹配的catch子句才能得到执行;这与switch语句不同,switch语句需要在每一个case后面跟一个break,以避免执行后续的case子句。
注意try块的内部,许多不同的方法调用可能会产生类型相同的异常,而你只需要提供一个针对此类型的异常处理程序。
中止与恢复
异常处理理论上有两种基本模型。Java支持终止模型。在这种模型中,将假设错误非常关键,以至于程序无法返回到异常发生的地方继续执行,一旦异常被抛出,就表明错误已无法挽回,也不能回来继续执行。
另一种称为恢复模型。如果想要用Java实现类似恢复的行为,那么在遇见错误时就不能抛出异常,而是调用方法来修正该错误。或者,把try块放在while循环里,知道得到满意的结果。
长久以来,尽管程序员们使用的操作系统支持恢复模型的异常处理,但他们最终还是转向使用类似“终止模型”的代码,并且忽略恢复行为。

12.4 创建自定义异常

不必拘泥于Java中已有的异常类型。Java提供的异常体系不可能遇见所有的希望加以报告的错误,所以可以自己定义异常类来表示程序中可能会遇到特定问题。
class SimpleException  extends Exception {}

public class InheritingExceptions {

public void f() throws SimpleException{
        System.err.println("Throw SimpleException from f()");
        throw new SimpleException();
}

public static void main(String[] args) {
        InheritingExceptions sed = new InheritingExceptions();
        try {
            sed.f();
} catch (SimpleException e) {
            System.out.println("Caught it!");
}
    }
}
也可以为异常类定义一个接受字符串参数的构造器:
class MyException extends Exception{
public MyException() {}
public MyException(String msg) { super(msg); }
}

public class FullConstructors {

public static void f() throws MyException{
        System.err.println("Throwing MyException from f()");
        throw new MyException();
}

public static void g() throws MyException{
        System.err.println("Throwing MyException from g()");
        throw new MyException("Originated in g()");
}

public static void main(String[] args) {
try {
f();
}catch (MyException e) {
            e.printStackTrace(System.out);
}
try {
g();
}catch (MyException e) {
            e.printStackTrace(System.out);
}
    }
}
在异常处理程序中,调用了在Throwable类声明(Exception即从此类继承)的printStackTrace()方法。就像从输出中看到的,它将打印“从方法调用处直到异常抛出处”的方法调用序列。这里,信息被发送到Sysout.out,并自动地被捕获和显示在输出中。但是,如果调用默认版本:

e.printStackTrace();
则信息将被输出到标准错误流。

12.4.1 异常与记录日志

你可能还想使用java.util.logging工具将输出记录到日志中。
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.logging.Logger;

class LoggingException extends Exception {

private static Logger logger = Logger.getLogger("LoggingException");

public LoggingException() {
        StringWriter trace = new StringWriter();
        printStackTrace(new PrintWriter(trace));
logger.severe(trace.toString());
    }
}

public class LoggingExceptions {
public static void main(String[] args) {
try {
throw new LoggingException();
        }catch (LoggingException e) {
            System.err.println("Caught " + e);
        }
try {
throw new LoggingException();
        }catch (LoggingException e) {
            System.err.println("Caught " + e);
        }
    }
}
尽管由于LoggingException将所有记录日志的基础设施都构建在异常自身中,使得它所使用的方式非常方便,并因此不需要客户端程序员的干预就可以自动运行,但是更常见的情形是我们需要捕获和记录其他人编写的异常,因此我们必须在异常处理程序中生成日志消息:
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.logging.Logger;

public class LoggingExceptions2 {
private static Logger logger = Logger.getLogger("LoggingExceptions2");
static void logException(Exception e) {
        StringWriter trace = new StringWriter();
        e.printStackTrace(new PrintWriter(trace));
logger.severe(trace.toString());
    }

public static void main(String[] args) {
try {
throw new NullPointerException();
        } catch (NullPointerException e) {
logException(e);
        }
    }
}

12.5 异常说明
Java鼓励人们把方法可能会抛出的异常告知使用此方法的客户端程序员。这是种优雅的做法,它使得调用者能确切的知道写什么样的代码可以捕获所有潜在的异常。当然,如果提供了源代码,客户端程序员可以在源代码中查找throw语句来获知相关信息,然而程序库通常并不与源代码一块发布。为了预防这样的问题,Java提供了相应的语法(并强制使用这个语法),使你能以礼貌的方式告知客户端程序员某个方法可能会抛出异常类型,然后客户端程序员就可以进行相应的处理。这就是异常说明,它属于方法声明的一部分,紧跟在形式参数列表之后。
异常说明使用了附加的关键字throws,后面接一个所有潜在异常类型的列表,所以方法定义可能看起来像这样:
void f() throws TooBig, TooSmall{ //…
void f() 就表示此方法不抛出任何异常

在接口或者抽象类中声明的抽象方法可以声明抛出异常,实际上却不抛出。编译器相信了这个声明,并强制此方法的用户像真的抛出异常那样使用这个方法。这样做的好处是,为异常先占个位置,以后就可以抛出这种异常那样使用这个方法。

12.6 捕获所有异常

可以只写一个异常处理程序来捕获所有类型的异常。通过捕获异常类型的基类Exception,就可以做到这一点:这将捕获所有异常,所以最好把它放在程序列表的末尾,以防它抢在其他处理程序之前先把异常捕获了。

12.6.1 栈轨迹

printStackTrace()方法所提供的信息可以通过getStackTrace()方法来直接访问,这个方法将返回一个由栈轨迹中的元素所构成的数组,其中每一个元素都表示栈中的一帧。元素0是栈顶元素,并且是调用序列中的最后一个方法调用(这个Throwable被创建和抛出之处)。数组中的最后一个元素和栈底是调用序列中的第一个方法调用。
import sun.management.StackTraceElementCompositeData;
import sun.org.mozilla.javascript.internal.EcmaError;

public class WhoCalled {
static void f() {
try {
throw new Exception();
        } catch (Exception e) {
for (StackTraceElement ste : e.getStackTrace()){
                System.out.println(ste.getMethodName());
            }
        }
    }
static void g() {f();}
static void h() {g();}

public static void main(String[] args) {
f();
        System.out.println("-------------------------------");
g();
        System.out.println("-------------------------------");
h();
    }
}

12.6.2 重新抛出异常
有时希望把刚捕获的异常重新抛出,尤其是在使用Exception捕获所有异常的时候。既然已经得到了对当前异常对象的引用,可以直接把它重新抛出:
重新抛出异常会把异常抛给上一级环境中的异常处理程序,同一个try块的后续catch子句 将被忽略。此外,异常对象的所有信息将得以保持,所以高一级环境中捕获此异常的处理程序可以从这个异常对象中得到所有信息。
想要通过栈轨迹跟踪异常,需要使用fillInStackTrace()方法,这将返回一个Throwable对象,它是通过把当前调用栈信息填入原来那个异常对象而建立的。

12.6.3 异常链
常常会想要在捕获一个异常后抛出另一个异常,并且希望把原始异常的信息保存下来,这被称为异常链。在JDK1.4以前,程序员必须自己编写代码来保存原始异常信息。现在所有Throwable的子类在构造器中都可以接受一个cause(因由)对象作为参数。这个cause就用来表示原始异常,这样通过把原始异常传递给新的异常,使得即使在当前位置创建并抛出了新的异常,也能通过这个异常链追踪到异常最初发生的位置。

12.7 Java标准异常

Throwable这个Java类被用来表示任何可以作为异常被抛出的类。Throwable对象可分为两种类型(指从Throwable继承而得到的类型);Error用来表示编译时和系统错误(除特殊情况外,一般不用你关心);Exception是可以被抛出的基本类型,在Java类库、用户方法以及运行时故障中都可能抛出Exception型异常。所以Java程序员关心的基类型通常是Exception。

12.7.1 特例:RuntimeException
属于运行时异常的类型有很多,他们会自动被Java虚拟机抛出,所以不必在异常说明中把它们列出来。这些异常都是从RuntimeException类继承而来,所以既体现了继承的优点,使用起来也很方便。这构成了一组具有相同特征的行为和异常类型。并且,也不再需要在异常说明中声明方法将抛出RuntimeException类型的异常(或者任何从RuntimeException继承的异常),它们也被称为“不受检查异常”。这种异常属于错误,将被自动捕获,就不用你亲自动手了。
请务必记住:只能在代码中忽略RuntimeException(及其子类)类型的异常,其他类型异常的处理都是由编译器强制实施的。究其原因,RuntimeException代表的是变成错误:
1)无法预料的错误。比如从你控制范围之外的传递进来的null引用
2)作为程序员,应该在代码中进行检查的错误。在一个地方发生的异常,常常会在另一个地方导致错误。

12.8 使用finally进行清理

对于一些代码,可能会希望无论try块中的异常是否抛出,它们都能得到执行。这通常适用于内存回收之外的情况(因为回收由垃圾回收器完成)。为了达到这个效果,可以在异常处理程序后面加上finally子句。完整的异常处理程序看起来像这样:
try {

} catch () {

} finally {

}

12.8.1 finally用来做什么
对于没有垃圾回收和析构函数自动调用机制的语言来说,finally非常重要。它能使程序员保证:无论try块里发生了什么,内存总能得到释放。但Java有垃圾回收机制,所以内存释放不再是问题。而且,Java也没有析构函数可供调用。那么,Java在什么情况下才能用到finally呢?
当要把除内存外的资源恢复到它们的初始状态时,就要用到finally子句。这种需要清理的资源包括:已经打开的文件或网络连接,在屏幕上画的图形,甚至可以是外部世界的某个开关。
个人理解:其实我们在编写代码的时候,一般会简单的理解为finally块是一定会执行的代码块,至于其关键字的设计之初的意义我们并不会关心,作者之所以会列出具体业务场景,很大一部分是因为Java是基于C++实现的变成语言,同时提供了丰富的类库。在我们长时间使用其丰富的类库的时候,我们往往就会忽略其设计之初的意义,这就是产品发展的强大之处,我们已经不用关心那么多就能完成我们的工作。
当涉及break和continue语句的时候,finally子句也会得到执行。请注意,如果把finally子句和带标签的break及continue配合使用,在Java里就没必要使用goto语句了。

12.8.2 在return中使用finally
因为finally子句总是会执行的,所以在一个方法中,可以从多个点返回,并且可以保证重要的清理工作仍旧会执行:

public class Test {

public static String f(){
try {
return "123";
        }catch (Exception e){
            System.out.println("caught");
        }finally {
            System.out.println("--------");
        }
return "";
    }

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

12.8.3 缺憾:异常丢失
遗憾的是,Java的异常实现也有瑕疵。异常作为程序出错的标志,绝不应该被忽略,但它还是有可能被轻易地忽略。用某些特殊的方式使用finally子句,就会发生这种情况:

class VeryImportantException extends Exception{
public String toString(){
return "A very important exception!";
    }
}

class HoHumException extends Exception{
public String toString(){
return "A trivial exception";
    }
}

public class LostMessage {

void f() throws VeryImportantException {
throw new VeryImportantException();
    }

void dispose() throws  HoHumException {
throw new HoHumException();
    }

public static void main(String[] args) {
try {
            LostMessage lm = new LostMessage();
try {
                lm.f();
            } finally {
                lm.dispose();
            }
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}

一种更加简单的丢失异常的方式是从finally子句中返回。

12.9 异常的限制

当覆盖方法的时候,只能抛出在基类方法的异常说明里列出的那些异常。这个限制很有用,因为这意味着,当基类使用的代码应用到其派生类对象的时候,一样能够工作(当然,这是面向对象的基本概念),异常也不例外。
class BaseballException extends Exception{}
class Foul extends BaseballException{}
class Strike extends BaseballException{}
abstract class Inning{
public Inning() throws BaseballException{}
public void event() throws BaseballException{}
public abstract void atBat() throws Strike,Foul;
public void walk(){}
}
class StormException extends Exception{}
class RainedOut extends StormException{}
class PopFoul extends Foul{}

interface Storm {
public void event() throws RainedOut;
public void rainHard() throws RainedOut;
}

public class StormyInning extends Inning implements Storm{
//为构造器添加新的异常是允许的,但是你必须处理基类的异常
public StormyInning() throws RainedOut,BaseballException{}
public StormyInning(String s)throws Foul,BaseballException{}
//正则方法必须符合基类(regular methods must conform to base class)
//! void walk() throws PopFoul {} //Compile error
    //接口不能给从基类继承的方法添加异常
//! public void event() throws RainedOut {} //Compile error
    //如果基类中没有这个方法,这个异常是允许的(此处由于继承的类中有event方法)
public void rainHard() throws RainedOut{}

public void event(){}

public void atBat()throws PopFoul{}

public static void main(String[] args) {
try {
            StormyInning si = new StormyInning();
            si.atBat();
        } catch (PopFoul e) {
            System.out.println("Pop foul");
        } catch (RainedOut e) {
            System.out.println("Rained out");
        } catch (BaseballException e) {
            System.out.println("Generic baseball exception");
        }

try {
            Inning i = new StormyInning();
            i.atBat();
        } catch (Strike e) {
            System.out.println("Strike");
        } catch (Foul e) {
            System.out.println("Foul");
        } catch (RainedOut e) {
            System.out.println("RainedOut");
        } catch (BaseballException e) {
            System.out.println("Generic baseball exception");
        }
    }
}
作者整理很多内容,但个人理解
总结如下:
1:**子类的方法抛出的异常范围不能超过父类的方法抛出的异常范围,子类也可以不抛出异常**;
2:**接口的实现类可以不抛异常,也可以抛出与接口不一样的异常. 但是必须是接口定义的异常或是该异常的子类**;

12.10 构造器

有一点很重要,即你要时刻询问自己“如果异常发生了,所有的东西能被正确的清理吗?”尽管大多数情况下是非常安全的,但涉及构造器时,问题就出现了。构造器会把对象设置成安全的初始状态,但还会有别的动作,比如打开一个文件,这样的动作只有在对象使用完毕并且用户调用了特殊的清理方式之后才能得以清理。如果在构造器内抛出了异常,这些清理行为也许就不能正常工作了。这意味着在编写构造器时要格外小心。
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class InputFile {
private BufferedReader in;
public InputFile(String fname) throws Exception {
try {
in = new BufferedReader(new FileReader(fname));
        } catch (FileNotFoundException e) {
            System.out.println("Could not open " + fname);
throw e;
        } catch (Exception e) {
try {
in.close();
            } catch (IOException e2) {
                System.out.println("in.close() unsucessful");
            }
throw e;
        } finally {
// Don't close it here!!!
}
    }
public String getLine() {
        String s;
try {
            s = in.readLine();
        } catch (IOException e) {
throw new RuntimeException("readLine() failed");
        }
return s;
    }
public void dispose() {
try {
in.close();
            System.out.println("dispose() successful");
        } catch (IOException e) {
throw new RuntimeException("in.close() failed");
        }
    }
}
对于在构造阶段可能会抛出异常,并且要求清理的类,最安全的使用方式是使用嵌套的try子句:
public class Cleanup {
public static void main(String[] args) {
try {
            InputFile in = new InputFile("Cleanup.java");
try {
                String s;
int i = 1;
while((s = in.getLine()) != null)
                    System.out.println();
            } catch (Exception e){
                System.out.println("Caught Exception in main");
                e.printStackTrace(System.out);
            } finally {
                in.dispose();
            }
        } catch (Exception e) {
            System.out.println("InputFile construction failed");
        }
    }
}
请仔细观察这里的逻辑:对InputFile对象的构造在其自己的try语句块中有效,如果构造失败,将进入外部的catch子句,而dispose()方法不会被调用。但是,如果构造成功,我们肯定想确保对象能够被清理,因此在构造之后立即创建了一个新的try语句块。执行清理的finally与内部的try语句块相关联。在这种方式中,finally子句在构造失败时是不会执行的,而在构成成功时将总是执行。
这种通用的清理惯用法在构造器不抛出任何异常时也应该运用,其基本规则是:在创建需要清理的对象之后,立即进入一个try-finally语句块:
class NeedsCleanup{
    private static long counter = 1;
    private static final long id = counter++;
    public void dispose(){
        System.out.println("NeedsCleanup "+ id + " dispose");
    }
}

class ConstructionException extends Exception{}

class NeedsCleanup2 extends NeedsCleanup{
    public NeedsCleanup2() throws ConstructionException{}
}

public class CleanupIdiom {
    public static void main(String[] args) {
        // Section 1
        NeedsCleanup nc1 = new NeedsCleanup();
        try {
            //...
        } finally {
            nc1.dispose();
        }

        // Section 2
        // If construction cannot fail you can group objects
        NeedsCleanup nc2 = new NeedsCleanup();
        NeedsCleanup nc3 = new NeedsCleanup();

        try {
            //...
        } finally {
            nc3.dispose();
            nc2.dispose();
        }

        // Section 3
        // If construction can fail you must guard each one
        try {
            NeedsCleanup2 nc4 = new NeedsCleanup2();
            try {
                NeedsCleanup2 nc5 = new NeedsCleanup2();
                try {
                    //...
                } finally {
                    nc5.dispose();
                }
            } catch (ConstructionException e) {
                System.out.println(e);
            } finally {
                nc4.dispose();
            }
        } catch (ConstructionException e) {
            System.out.println(e);
        }
    }
}
本例中的异常处理的棘手程度,对于应该创建不能失败的构造器是一个有力的论据,尽管这么做并非总是可行。
注意,如果dispose()可以抛出异常,那么你可能需要额外的try语句块。基本上,你应该仔细考虑所有可能性,并确保正确处理每一种情况。

12.11 异常匹配

跑出异常的时候,异常处理系统会按照代码的书写顺序找出“最近”的处理程序。找到匹配的处理程序之后,它就认为异常将得到处理,然后就不再继续查找。
查找的时候并不要求抛出的异常同处理程序所声明的异常完全匹配。派生类的对象也可以匹配其基类的处理程序,就像这样:
class Annoyance extends Exception{}
class Sneeze extends Annoyance{}

public class Human {
    public static void main(String[] args) {
        try {
            throw new Sneeze();
        } catch (Sneeze e) {
            System.out.println("Caught Sneeze");
        } catch (Annoyance e) {
            System.out.println("Caught Annoyance");
        }
        //Caught the base type
        try {
            throw new Sneeze();
        } catch (Annoyance e) {
            System.out.println("Caught Annoyance");
        }
    }
}
Sneeze异常会被第一个匹配的catch子句捕获,也就是程序里的第一个。然而如果将这个catch子句删掉,只留下Annoyance的catch子句,改程序仍然能运行,因为这次捕获的事Sneeze的基类。换句话说,catch(Annoyance e)会捕获Annoyance以及所有从它派生的异常。这一点非常有用,因为如果决定在方法里加上更多派生异常的话,只要客户端程序员捕获的是基类的异常,那么他们的代码就无需更改。

12.12 其他可选方式

异常处理系统就像一个活门(trap door),是你能放弃程序的正常执行顺序。当“异常情形”发生的时候,正常的执行已变得不可能或者不需要了,这时候就要用到这个“活门”了。异常代表了当前方法不能继续执行的情形。开发异常处理系统的原因是,如果为每个方法所有可能发生的错误都进行处理的话,任务就显得过于繁重了,程序员也不愿意这么做。结果常常是将错误忽略。应该注意到,开发异常处理的初衷是为了方便程序员处理错误。
异常处理的一个重要原则是“只有在你知道如何处理的情况下才捕获异常”。实际上,异常处理的一个重要目标就是把错误的代码同错误的发生的地点相分离。这使你能在一段代码中专注于要完成的事情,至于如何处理错误,则放在另一段代码中完成。这样一来,主干代码就不会与错误处理逻辑混在一起,也更容易理解和维护。通过允许一个处理程序去处理多个出错点,异常处理还使得错误处理代码的数量趋向于减少。
Java中的异常处理是“被检查的异常”,作者对于这块有一些其独到的理解。个人觉得对于学习基础知识的我,目前不需要给予太多的关注。

12.13 异常使用指南

应该在下列情况下使用异常:
1)在恰当的级别处理问题
2)解决问题并且重新调用产生异常的方法。
3)进行少许修补,然后绕过异常发生的地方继续执行
4)用别的数据进行计算,以代替方法预计会返回的值
5)把当前运行环境下能做的事情尽量昨晚,然后把相同的异常重新抛到更高层。   
6)把当前运行环境下能做的事情尽量昨晚,然后把不同的异常抛到更高层。
7)终止程序
8)进行简化
9)让类库和程序更安全

12.14 总结

异常处理的优点之一就是它使得你可以在某处集中经理处理你要解决的问题,而在另一处处理你编写的这段代码中产生的错误。尽管异常通常被认为是一种工具,使得你可以在运行时报告错误并从错误中恢复...我一直相信“报告”功能是异常的精髓所在...

省略号是作者对“恢复”的怀疑和与C++的比较,在此不做赘述。通读过整章之后,我对异常语法及使用有了基本的了解,并对异常的限制、异常匹配这两节有了印象,对于大多数程序员而言,我们需要关注的是在何时应用合适的技术。这就需要我们首先自身应该具有一定的技术广度,基于这个广度,我们再做深入的深度学习和实际与理论验证。因为我们没有那么多时间亲身验证一个理论(因为我们浪费了太多时间于玩耍),只能于工作中多来验证。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《Thinking in Java》是一本经典的Java编程入门教材,通过阅读该书可以系统地学习Java编程的基础知识和高级概念。阅读过程中,我结合自己的学习体验和实践,进行了详细的学习笔记。 首先,该书从基础语法开始介绍,包括数据类型、控制语句、数组等。对于初学者来说,这些基础知识是最基本的,通过书中的示例代码和解析,我能够更好地理解这些概念和语法规则。 其次,书中对面向对象编程进行了深入的讲解。通过学习面向对象的思想,我明白了类、对象、继承、多态等概念的含义和使用方法。同时,书中还讲解了接口、内部类、异常处理等较为高级的概念,极大地拓宽了我的Java知识面。 另外,该书还介绍了Java的常见类库和工具,如字符串操作、集合框架、输入输出、线程等。这些内容对于实际开发非常有用,而且书中的示例代码也能帮助我更好地理解和应用这些类库和工具。 此外,该书通过大量的实例和案例,生动地展示了Java编程的实战应用。这些实例涵盖了各种不同的应用场景,从简单的程序到复杂的项目,都有所涉及。通过分析这些实例,我不仅可以学习到实际编程的技巧,还能提高自己的解决问题的能力。 总的来说,读完《Thinking in Java》后,我对Java编程有了更深入的理解和掌握。通过学习笔记的整理,我不仅复习了各个知识点,还加深了对这些概念的理解。希望这份学习笔记能够帮助到其他想要学习Java的同学,让他们能够更快地入门和掌握这门编程语言。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值