目录
应用场景
重要提示
定义
快捷键
运行图
案例1
案例2
案例3
案例4
练习题
第一题
题目
代码
toString方法
第二题
题目
应用场景
查找错误时,用断点调试一步一步的看源码执行的过程,从而发现错误所在
重要提示
在断点调试(Debug)过程中,是运行状态,是以对象的运行类型来执行的
断点调式(Debug)过程中,是运行状态,是以对象的运行类型来执行的
eg :A extends B;B b = new A();b.xx();在调试时看运行类型A
定义
断点调试是指在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后程序员可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下,进行分析从而找到这个Bug,又叫Debug。
可以查看到java底层源代码的执行过程,提高程序员的Java水平
快捷键
F7(跳入)跳入方法体内执行 F8(跳过)逐行执行代码. | shift+F8(跳出) :跳出方法 F9(resume,执行到下一个断点) |
运行图
和老师的IDEA 版本不同,所以有些细节不太一样
案例1
package com.hspedu.poly_.e1210;
public class Debug01 {
public static void main(String[] args) {
//演示逐行执行代码
int sum = 0;
for (int i = 0; i < 5; i++) {
sum += i;
System.out.println("i=" + i);
System.out.println("sum=" + i);
}
System.out.println("退出 for....");
}
}
总结:当高光蓝条跳到下一行的时候才会显示上一行的结果
当蓝条跳到
for (int i = 0; i < 5; i++) {
这一行时,才会显示出变量 sum = 0
执行完毕后的显示
案例2
数组越界
package com.hspedu.poly_.e1210;
public class Debug02 {
public static void main(String[] args) {
int[] arr = {1, 10, -1};
//数组越界异常
for (int i = 0; i <= args.length; i++) {
System.out.println(arr[i]);
}
System.out.println("退出for");
}
}
在上述代码中故意将arr.length写成args.length,看一下Debug的流程
提示一个是args,一个是数组arr
输出控制台直接退出程序了
修改之后再次Debug
Debug控制台显示
输出控制台显示
提成程序员思考为什么i可以等于3
案例3
如何追踪源代码
package com.hspedu.poly_.e1210;
import java.util.Arrays;
public class Debug03 {
public static void main(String[] args) {
int[] arr = {1, -1, 10, -20, 100};
//直接使用IDEA提供的方法进行排序
//需求:想看一下Arrays.sort的底层实现
Arrays.sort(arr);
for (int i = 0; i < arr.length; i++) {
//在Debug之前是按照排序正常输出
System.out.print(arr[i] + "\t");
}
}
}
设置完毕后可按F7(Step Into)进入Arrays.sort方法的底层源码
再次 Step Into
可以一直Step Into,直到找到最底层的源代码
返回(跳出的时候就是Shift + F8,即Step Out)
案例4
演示Resume功能,快捷键F9
package com.hspedu.poly_.e1210;
import java.util.Arrays;
//演示执行到下一个断点,同时支持动态的下断点
public class Debug04 {
public static void main(String[] args) {
int[] arr = {1, -1, 10, -20 , 100};
//直接使用IDEA提供的方法进行排序
//需求:想看一下Arrays.sort的底层实现
Arrays.sort(arr);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + "\t");
}
System.out.println("hello100");
System.out.println("hello200");
System.out.println("hello300");
System.out.println("hello400");
System.out.println("hello500");
System.out.println("hello600");
System.out.println("hello700");
}
}
在第10行和第17行分别下一个断点,在运行Debug之后,按Reseum键,
直接跳到了第二个断点,中间代码的结果也全都输出了
此时Debug还在运行中,但是还可以下断点并运行
练习题
第一题
题目
代码
创建一个Person,有两个private属性,显示定义构造器,重写toString方法,可以输出对象的属性
package com.hspedu.debug_;
public class DebugExercise01 {
public static void main(String[] args) {
//创建对象的流程
//(1)加载Person类信息
//(2)初始化 2.1默认初始化,2.2显式初始化 2.3构造器初始化
//返回对象地址(知识点:7.9 对象创建的流程分析)
Person person = new Person("jack", 23);
System.out.println(person);
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
在Person person = new Person("jack", 23);一行下断点
Force Step Into进入ClassLoader.java(我的没有成功,以下是课程截图),
即第一步:加载Person类信息
第二步:默认初始化(name 为null; age 为0);无法查看
第三步:显式初始化,这里没有给属性赋值
第四步:构造器初始化
属性name从null------->jack;age从0------>23
toString方法
Debug输出对象语句,查看是如何调用toString方法
Force Step Into
再次Force Step Into
即(Object obj) 传入一个Person类对象person,此时已经向上转型了
三元运算符号,判断传入的对象是否为null,如果≠null,则调用toString方法,运行类型是Person,运行Person类重写的Object类的toString方法(动态绑定机制)
再次Force Step Into,Person类重写的Object类的toString方法
连续Step Over
输出
第二题
题目
使用断点调试,查看动态绑定机制的工作原理
动态绑定机制的代码
package com.hspedu.debug_;
public class DeBugExercise02 {
public static void main(String[] args) {
//向上转型,编译类型是A,运行类型是B
A01 a = new B01();
//从子类-B类中开始查找
System.out.println(a.sum());//20+20//30
System.out.println(a.sum1());//20+10//20
}
}
class A01 {//父类
public int i = 10;
public int sum() {
return getI() + 10;
}
public int sum1() {
return i + 10;
}
public int getI() {
return i;
}
}
class B01 extends A01 {//父类
public int i = 20;
// public int sum() {
// return i + 20;
// }
public int getI() {
return i;
}
// public int sum1() {
// return i + 10;
// }
}
Force Step Into
根据动态绑定机制,调用方法时看运行类型,对象a的的运行类型是B01类,所以先从子类开始查找,由于B01类没有这个方法,就向上父类-A01类中查找
再次Force Step Into
属性是哪里声明就调用哪里的,所以是调用B01的属性20