Java核心技术 卷1 Ch.7

暂时跳过的部分:

  • 7.2.6 堆栈轨迹, All

Ch.VII 异常, 断言与日志:

7.1 处理错误:

Java的异常处理与C++非常的相似

首先, 是Java内置的异常类的继承结构:

img

其中:

  • Error 类描述了 Java 运行时系统内部错误和资源耗尽的错误 (注意这玩意不是异常, 是错误)

    出现这种情况, 通常无力回天, 只能通告给用户, 并尽力使程序安全的终止

    异常是可以被处理的,而错误是没法处理的

  • Exception规定的异常是程序本身可以处理的异常, 其中的两个大类:

    • RuntimeException:
      程序错误导致的异常
    • IOException
      由于I/O 错误这类问题导致的异常

Java语言规范将派生于Error类或RuntimeException类的所有异常称为非受查(unchecked)异常, 无法预先捕捉处理,而所有其他的异常称为受查(checked)异常, 可以预先捕捉处理

  • Checked Exception

    可检查的异常,这是编码时非常常用的,所有checked exception都是需要在代码中处理的
    它们的发生是可以预测的,正常的一种情况,可以合理的处理

    比如IOException,或者一些自定义的异常

  • Unchecked Exception

    RuntimeException及其子类都是unchecked exception。这种异常是运行时发生,无法预先捕捉处理

    比如NPE空指针异常,除数为0的算数异常ArithmeticException等等

类比一下C++中的异常继承结构:

img

与C++异常类的比较:

  • RuntimeException相当于C++中的logic_error, 表示程序中的逻辑错误
  • 而其他所有的非RuntimeException异常, 都相当于C++中的runtime_error异常, 是所有由于不可预测的原因所引发的异常

所以这俩是相反的…

声明受查(checked)异常:

其实前头已经见识过了如何声明一个方法可能抛出的异常:

//前头自建的clone方法:

class Employee implements Cloneable
// 这里仅仅是将Object中的clone覆盖为public方法, 并没有添加其他的功能
public Employee clone() throws CloneNotSupportedException{
	return (Employee) super.clone();
}

如果一个方法可能抛出多个异常, 则用,逗号连接:

public Image loadlmage(String s) throws FileNotFoundException, EOFException{
    ...
}

方法能够抛出的异常类型:

方法只能也只应该抛出可能的受查异常
对于非受查异常, 要么无法控制(Error), 要么需要避免发生(RuntimeException)

void drawlmage(inti) throws ArraylndexOutOfBoundsException // bad style

异常与继承:

如果在子类中覆盖了超类的一个方法, 子类方法中声明的受查异常不能比超类方法中声明的异常更通用
也就是说, 子类方法中可以抛出更特定的异常, 或者根本不抛出任何异常

特别需要说明的是, 如果超类方法没有抛出任何受查异常, 子类也不能抛出任何受查异常

关于Java & C++中throw的区别:

在C++中, 异常的throw是在程序运行中执行的
即在try语句块中, 程序判定需要抛出异常时就会调用throw

而在Java中, 异常时在编译时执行并抛出的

异常的抛出:

对于Java内置的异常, 其抛出非常简单:

  • 找到一个合适的异常类
  • 创建这个类的一个对象
  • 使用throw将对象抛出
throw new EOFException; //此处将抛出一个EOFException异常

对于自定义异常, 抛出方式与Java内置异常相同, 主要在其定义:

  • 选择合适的异常超类

  • 定义两个构造器: 默认的无参构造器, 带有详细描述信息的构造器

    其中后者的详细信息将会被基类Throwable的toString打印, 非常方便

7.2 异常捕获:

与C++相同, Java也有try & catch语句块, 用来处理异常, 且语法与C++相同, 这里就不做阐述了

异常处理与异常抛出的关系:

如果一个方法在内部可能出现异常, 但是在内部自行消化解决了, 就不需要在方法头用throw声明可能抛出的异常, 但是如果没有处理, 或者希望方法的调用者来处理, 则需要使用throw说明可能抛出的异常, 而这样, 调用者就需要自己编写try & catch语句块

捕获多个异常:

语法仍然与C++相同

但是Java SE 7 中支持一个新操作, 对相似的异常进行合并, 用一个catch进行处理:

try{
	code that might throw exceptions
}
catch (FileNotFoundException | UnknownHostException e){		//两个异常合并处理
	emergency action for missing files and unknown hosts
}
catch (IOException e){
emergency action for all other I/O problems
}

当多个异常的处理方式相同, 且捕获的异常类型之间不存在子类关系时, 推荐这么整

异常的在抛出:

就是在catch中再抛出一个异常, 主要是为了改变异常的类型

一种比较高级的应用是将接受到的异常进行一定程度的包装, 而后返回一个更上层的异常:

    try{
        access the database;
    }
    /**
     * 创建一个新的上层异常, 而后将当前接受到的异常设置为上层异常的原因
     * 当外部捕获到异常时, 可以通过getCause()函数获得原始异常
     */
    catch(
    SQLException e){
        Throwable se = new ServletException("database error");
        se.initCause(e);
        throw se;
    }
Throwable e = se.getCause();	//获得异常产生原因, 即方法内部的原始异常

finally字句:

无论是否发生异常,finally 代码块中的代码总会被执行, 所以通常在finally代码块中设置清理类型等收尾善后性质的语句

比如打开的文件需要关闭, 等等…
finally代码块解决了这些善后问题, 否则想要处理, 就需要在正常代码与catch的异常处理代码中同时设置善后代码

finally 代码块出现在 catch 代码块最后,语法如下:

try{
  // 程序代码
}catch(异常类型1 异常的变量名1){
  // 程序代码
}catch(异常类型2 异常的变量名2){
  // 程序代码
}finally{
  // 程序代码
}

代码优化: 将try/catch & try/finally 解耦合:

其实就是将try/catch & try/finally分开处理, 使得代码更加清晰

其中外层的try与catch匹配, 内层的try与finally匹配, 各自负责单一的职能

InputStrean in = . . .;
try{
	try{
		//code that might throw exceptions
	}
	finally{
		in.close();
	}
}
catch (IOException e){
	//show error message
}

带资源的try语句:

这里主要针对上头可能发生的问题:

当try抛出异常后, 执行finally语句块时, 又发生了异常, 咋整?

如果在finally中不对finally中出现的异常做处理, 这个异常就会将上一个异常抑制, 使得上一个异常无法发出…

所以这里引入带资源的try语句, 语法如下:

就是在try语句后头加上一个使用的资源 (此处的资源是指那些必须在程序结束时显式关闭的资源,比如数据库连接,网络连接等) 这里的资源必须实现了Closeable或AutoCloseable接口
使用的资源会在try退出后自动被关闭

public static String processFile() throws IOException {
try (
	BufferedReader br = new BufferedReader(new FileReader("D:data.txt"))
    ) 
    { 
	return br.readLine();
    }
}

如果需要使用多个资源, 需使用分号分隔 (注意不是逗号):

try (//创建对象输出流
         ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("b.bin"));
         //创建对象输入流
         ObjectInputStream ois = new ObjectInputStream(new FileInputStream("b.bin"));
    )
    {
        //序列化java对象
        oos.writeObject(s);
        oos.flush();
        //反序列化java对象
        s2 = (Student) ois.readObject();
    }

分析堆栈轨迹元素:

这部分先放着

7.3 使用异常机制的技巧:

这里介绍了程序设计中处理异常的几个设计技巧, 还是蛮有用的

  1. 异常处理相当耗时, 所以能用简单的测试完成的任务不要使用异常处理

    执行上百万次下头的操作:

    //方法1
    if (!s.empty()) s.pop();
    //方法2
    try
    {
    	s.pop();
    }
    catch (EmptyStackException e)
    {
        //void
    }
    

    方法1耗时: 646 ms
    方法2耗时: 21739 ms

  2. 使用更为合适的异常

    如果Java内置异常类中没有合适的, 应当创建自己的异常, 而不是执着与RuntimeException

  3. 捕获更为精确的异常:

    InputStream is = null;
    try {
        is = new FileInputStream("file.txt");
    } catch (Exception e) {
        e.printStackTrace();
    }
    

    实际应该捕获 FileNotFoundException,却捕获了泛化的 Exception, 使得异常发生的具体原因未知

  4. 优先关闭没啥用的异常, 而不是压制(放着不管)

    当一个异常基本不会发生, 或是没啥影响时, 总是想把它直接忽略, 但是这样无法通过编译

    可以直接在try&catch中接收, 然后啥也不干:

    public Image loadImage(String s)
    {
    	try
    	{
    	// code that threatens to throw checked exceptions
    	}
    	catch (Exception e)
    	{} // 就是啥也不干
    }
    
  5. 对于一套处理流程, 将其包装在一个try&catch中, 比每个语句单独一个try&catch要有效的多

    比如:

    PrintStream out;
    Stack s;
    for (i = 0;i < 100; i++)
    {
    	try
    	{
    		n = s.popO;
    	}
    	catch (EmptyStackException e)
    	{
    		//stack was empty
    	}
    	try
    	{
    		out.writelnt(n);
    	}
    	catch (IOException e)
    	{
    		// problem writing to file
    	}
    }
    

    这样不但使得代码量迅速膨胀, 且还达不到良好的处理效果: 实际上这是一套处理流程, 一旦中间某处出现问题, 整个流程都应当取消

    优化后如下:

    try
        {
            for (i = 0; i < 100; i++) {
                n = s.popO;
                out.writelnt(n);
            }
        }
    catch(
        IOException e)
        {
    // problem writing to file
        }
    catch(
        EmptyStackException e)
    
        {
    // stack was empty
        }
    

7.4 使用断言:

与C++的断言机制相同, Java断言同样是用在开发期的DEBUG, 而release版本中可以移除所有的断言代码, 提升程序的运行速度

断言库语法:

assert 布尔表达式;
assert 布尔表达式:消息;

这两种形式都会对条件进行检测, 如果结果为 false, 则抛出一个 AssertionError 异常
在第二种形式中,表达式将被传人 AssertionError 的构造器, 并转换成一个消息字符串, 而后可以通过try&catch捕捉异常, 从中导出异常信息

	public static void main(String[] args) {
        testClass outObj = new testClass();
        outObj.anonymousClassTest();
        int x=1;
        try {
            assert x<0 : "This is an assert";
        }
        catch (AssertionError assR){
            System.out.println(assR.getMessage());
        }
    }

程序输出:

This is an assert

IDEA开启断言の方法

断言の合适使用:

断言的合适使用时机:

  • 断言失败是致命的, 不可恢复的错误

    所以如果是可恢复的错误, 应该使用异常来处理

  • 断言检查只用于开发和测阶段

    所以不应该把断言的内容暴露给用户

断言只应该用于在测试阶段确定程序内部的错误位置

7.5 记录日志:

日志API的优点:

  • 可以很容易地取消全部日志记录,或者仅仅取消某个级别的日志,而且打开和关闭这个操作也很容易。
  • 可以很简单地禁止日志记录的输出, 因此,将这些日志代码留在程序中的开销很小。
  • 日志记录可以被定向到不同的处理器, 用于在控制台中显示, 用于存储在文件中等。
  • 日志记录器和处理器都可以对记录进行过滤。过滤器可以根据过滤实现器制定的标准丢弃那些无用的记录项。
  • 日志记录可以采用不同的方式格式化,例如,纯文本或 XML。
  • 应用程序可以使用多个日志记录器, 它们使用类似包名的这种具有层次结构的名字,例如, com.mycompany.myapp
  • 在默认情况下,日志系统的配置由配置文件控制。 如果需要的话, 应用程序可以替换这个配置。

由于这里采用的是java自带的日志系统, 而广泛使用的是log4j, 更为NB的是slf4j, 所以, 这里的笔记采用混合学习的方式

Log4j & Slf4J 的安装:

  1. 首先需要使用Maven来管理项目

    不知道为啥, 但是Baidu大部分教程都是用这玩意的

    创建Maven具体教程看这里

  2. 在pom.xml中添加相应的依赖,然后点击右下角的import changes 即可自动导入相应的包

    pom.xml添加的内容如下:
    注意是添加在最后的</project>上头

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.2</version>
        </dependency>
     
        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.2</version>
        </dependency>
    </dependencies>
    

    添加后大概是这个样子:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>org.example</groupId>
        <artifactId>MarvenTestProject</artifactId>
        <version>1.0-SNAPSHOT</version>
                 
    //新添加的内容:
        <dependencies>
            <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>1.7.2</version>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <version>1.7.2</version>
            </dependency>
        </dependencies>
    
    </project>
    
  3. 在src>main>resources下添加 log4j.properties文件,内容如下:

    其中部分路径可修改

    ### 设置###
    log4j.rootLogger = debug,stdout,D,E,I
    
    ### 输出信息到控制抬 ###
    log4j.appender.stdout = org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.Target = System.out
    log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
    
    ### 输出DEBUG 级别以上的日志到=E://logs/log.log ###
    log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
    log4j.appender.D.File = E://logs/log.log
    log4j.appender.D.Append = true
    log4j.appender.D.Threshold = DEBUG
    log4j.appender.D.layout = org.apache.log4j.PatternLayout
    log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
    
    ### 输出ERROR 级别以上的日志到=E://logs/error.log ###
    log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
    log4j.appender.E.File =E://logs/error.log
    log4j.appender.E.Append = true
    log4j.appender.E.Threshold = ERROR
    log4j.appender.E.layout = org.apache.log4j.PatternLayout
    log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
    
    ### 输出INFO 级别以上的日志到=E://logs/info.log ###
    log4j.appender.I = org.apache.log4j.DailyRollingFileAppender
    log4j.appender.I.File =E://logs/info.log
    log4j.appender.I.Append = true
    log4j.appender.I.Threshold = INFO
    log4j.appender.I.layout = org.apache.log4j.PatternLayout
    log4j.appender.I.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
    
  4. 添加用于测试的class文件testDemo, 并添加如下代码

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class testDemo {
     
        private static Logger logger = LoggerFactory.getLogger(testDemo.class);
     
        public static void main(String[] args) {
            System.out.println("This is println message. yeah");
     
            // 记录debug级别的信息
            logger.debug("This is debug message.");
            // 记录info级别的信息
            logger.info("This is info message.");
            // 记录error级别的信息
            logger.error("This is error message.");
     
        }
    }
    
  5. 构建文件

    期间会下载所需要的包, 可能会比较耗时一点
    但Maven就这个功能最强大, 可以自动下载项目依赖库, 免去了配置的麻烦问题

    最后的输出结果:

    This is println message. yeah
    [DEBUG] 2020-01-22 10:14:02,955 method:HelloMaven.main(HelloMaven.java:15)

This is debug message.
[INFO ] 2020-01-22 10:14:02,958 method:HelloMaven.main(HelloMaven.java:17)
This is info message.
[ERROR] 2020-01-22 10:14:02,958 method:HelloMaven.main(HelloMaven.java:19)
This is error message.

如果在编译时出现这个:

Warning:java: 源值1.5已过时, 将在未来所有发行版中删除

这玩意是Maven的锅, 可以参考这个教程解决

总之到这里总是可以跑起来了

后来发现也不用去下SLF4j的jar包啥的, IDEA好像会自动完成

还是啥也不懂啊…

Log4j 使用教程:

上头的程序跑起来之后, 多少是有点B数了…现在可以开始正式的学习

定义配置文件:

当然, 完全可以在代码中配置Log4j环境, 但是使用配置文件可将Log4j 的环境与代码分离, 使得Log4j使用起来比较方便灵活

Log4j支持两种配置文件的方法:

  • XML格式的文件

    类似于Maven的pom.xml, 这个先放着

  • Java专用配置文件.properties

    上头使用的就是这个, 下头学的也是这玩意

配置rootLogger

其语法为:

log4j.rootLogger = [ level ] , appenderName, appenderName, …

其中:

  • level 是日志记录的优先级

    通过这里定义级别, 可以控制程序中相应级别的日志信息的输出

    总共有7个等级, 从上到下范围一次增大
    即范围大的包含范围小的, 当INFO打开时, DEBUG信息不输出

    1. ALL level:打开所有日志记录开关;是最低等级的,用于打开所有日志记录
    2. DEBUG:输出调试信息;指出细粒度信息事件对调试应用程序是非常有帮助的
    3. INFO: 输出提示信息;消息在粗粒度级别上突出强调应用程序的运行过程
    4. WARN: 输出警告信息;表明会出现潜在错误的情形
    5. ERROR:输出错误信息;指出虽然发生错误事件,但仍然不影响系统的继续运行
    6. FATAL: 输出致命错误;指出每个严重的错误事件将会导致应用程序的退出
    7. OFF level:关闭所有日志记录开关;是最高等级的,用于关闭所有日志记录

    通常只使用ERROR、WARN、INFO、DEBUG这四个等级的Log

  • appenderName 指定日志输出位置的别名

    别名需要在下头自定义
    可以同时指定多个位置, 并用逗号,分割

如之前的配置信息:

log4j.rootLogger = debug,stdout,D,E

表明日志等级设置为debug, 且将Log输出到stdout, D, E中, 后头的这仨是log4j.appender.stdoutlog4j.appender.Dlog4j.appender.E

配置日志信息输出目的地log4j.Appender

主要语法:

log4j.appender.appenderName = fully.qualified.name.of.appender.class 
log4j.appender.appenderName.option1 = value1 
… 
log4j.appender.appenderName.option = valueN
  • 第一句是创建一个别名, 并将其指向log4j中提供的几个appender之一

    其中,Log4j提供的appender有以下几种:

    • org.apache.log4j.ConsoleAppender

      输出到控制台

    • org.apache.log4j.FileAppender

      输出到特定文件

    • org.apache.log4j.DailyRollingFileAppender

      每天创建一个文件并将日志输出至其中

    • org.apache.log4j.RollingFileAppender

      文件大小到达指定尺寸的时候产生一个新的文件

    • org.apache.log4j.WriterAppender

      将日志信息以流格式发送到任意指定的地方

  • 而后是设置输出的各个属性:

    比较常用的是这几个:

    #定义日志文件的存储路径
    log4j.appender.myFile.File=src/log/logProperties/log4j.log
    
    #定义日志文件的大小
    log4j.appender.myFile.MaxFileSize=1024kb
    
    #定义日志文件最多生成几个(从0开始算1个,即此处最多3个文件)
    #超过该大小则会覆盖前面生成的文件
    log4j.appender.myFile.MaxBackupIndex=2
    
    #针对可能出现的中文乱码问题, 将编码设置为UTF-8:
    log4j.appender.myFile.encoding=UTF-8;
    #或者可以使用GBK:
    log4j.appender.myFile.encoding=gbk;
    
配置日志信息的格式:
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class 
log4j.appender.appenderName.layout.option1 = value1 
… 
log4j.appender.appenderName.layout.option = valueN

其中log4j提供有几种预定义好的格式:

  • org.apache.log4j.HTMLLayout
    以HTML表格形式布局
  • org.apache.log4j.PatternLayout
    可以灵活地指定布局模式
  • org.apache.log4j.SimpleLayout
    包含日志信息的级别和信息字符串
  • org.apache.log4j.TTCCLayout
    包含日志产生的时间、线程、类别等等信息

其中org.apache.log4j.PatternLayout支持使用类似C语言的printf格式进行自由设置:

%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL

%r 输出自应用启动到输出该log信息耗费的毫秒数

%c 输出所属的类目,通常就是所在类的全名

%t 输出产生该日志事件的线程名

%m 输出日志所在方法的名字

%n 输出一个回车换行符,Windows平台为“rn”,Unix平台为“n”

%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格
比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921

%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。

其他的辅助语法类似于printf

如上头的例子中:

log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]

输出效果:

2020-01-22 10:11:43 [ main:0 ] - [ DEBUG ] This is debug message.

在代码中使用log4j:

在配置完成后, 就可以在代码中愉快的使用log4j 了

首先是创建日志获取器:

private static final Logger logger = LoggerFactory.getLogger(HelloMaven.class);

由于未被任何变量引用的日志记录器可能被当做垃圾直接回收了, 所以最好加上static 与 final

而后是使用配置信息文件:

这一步如果是用Maven的话, 只要在resource中添加了相应的配置文件log4j.properties就可以自动完成

否则需要手动添加:

PropertyConfigurator.configure ( String configFilename)

其中文件名中的所有反斜杠\都要变成俩除号//, Java中的惯例

之后就可以在需要的位置插入Log信息:

通常只使用这4个等级:

Logger.debug ( Object message ) ; 
Logger.info ( Object message ) ; 
Logger.warn ( Object message ) ; 
Logger.error ( Object message ) ;

Log的输出会调用类的toString() 函数输出信息, 所以之前的toString是相当有用的

Slf4j 使用教程:

实际上变更的不多, 大部分操作和log4j还是相同的

主要的差别有以下几个:

  1. 创建日志记录器

    //注意此时import的是这俩
    //由于IDEA的自动import功能, 如果原先有import log4j的包, 需要先删掉
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    //创建记录器:
    //这里的记录器都是Logger类
    //但是使用的是记录器的工厂方法LoggerFactory, 后头的都相同
    private static final Logger logger= LoggerFactory.getLogger(HelloMaven.class);
    
  2. Log信息的输出:

    就是这四个不同等级的输出会有一定的差别, 看API了解一下就OK

    Logger.debug ( Object message ) ; 
    Logger.info ( Object message ) ; 
    Logger.warn ( Object message ) ; 
    Logger.error ( Object message ) ;
    

    这里强调一下Slf4j中比较重要的特性:

    其在字符串编排上引入了类似C语言printf的占位符功能, 免去了大量的字符串拼接, 使得日志的性能有几乎两倍的提升

    //slf4j
    log.debug("Found {} records matching filter: '{}'", records, filter);
     
    //log4j
    log.debug("Found " + records + " records matching filter: '" + filter + "'");
    

    其中的占位符就是{}, 而后将按顺序将后头的String对象填入其中 (注意后头的还是String类)

本部分还需要补充实战期间的技巧整合…

<未完待续>

7.6 调试技巧:

开发过程中常用的DEBUG技巧 (这里不包括IDEA的调试技巧)

  1. 在需要的位置打印变量的值:

    直接输出到控制台, 或是输出到日志

  2. 在每个类中单独放置一个main方法用于单元测试

    IDEA可以非常方便的运行单独的main, 所以可以将必要的测试代码丢到里头

    或者是使用单元测试框架, 一步到位才是真谛

  3. 利用日志代理:

    实际上就是创建一个假的由于拦截调用的和输出日志信息的子类

    可以在这个子类中输出调用信息和堆栈轨迹

  4. 通过printStackTrace的方法

    捕获异常之后, 可以仅仅输出其堆栈轨迹, 而后重新将其抛出, 交个上层处理等

  5. 利用Java虚拟机的功能, 可以查看更多程序运行的信息

IDEA debugger 使用:

总的教程可以看这里, 写的还是蛮好的:

IDEA调试教程

既然IDEA如此强大, 我还要啥自行车

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值