王方第九周Java实验总结

项目

内容

这个作业属于哪个课程

https://www.cnblogs.com/nwnu-daizh/

这个作业的要求在哪里

https://www.cnblogs.com/nwnu-daizh/p/11703678.html

作业学习目标

1.掌握java异常处理技术;

2.了解断言的用法;

3.了解日志的用途;

4.掌握程序基础调试技巧。

第一部分 基础知识

异常  日志  断言和调试

异常概念

——异常、异常类型、声明抛出异常、异常抽出、异常捕获

异常:程序执行过程中所发生的异常事件,它中断指令的正常执行。

Java的异常处理机制可以控制程序从错误产生的位置转移到能够进行错误处理的位置。

程序中常出现的错误和问题有:

—用户输入错误

—设备错误

— 物理限制

—代码错误

异常分类:Java把程序运行时的错误分为两类

(1)非致命异常:通过某种修订后程序还能够继续运行(异常),如:文件不存在、无效的数组下标、空引用、网络断开、打印机脱机磁盘满等。Java提供了一种独特的异常处理机制处理这类错误。

(2)致命异常:程序遇到了非常严重的不正常 状态,不能简单恢复执行,是致命性错误。如:内存耗尽、系统内部错误等。这种错误程序本身无法解决。

Java的异常类直接或间接的继承于Throwable类。出内置异常类外,程序员可自定义异常类。Java的异常类可分为两大类:

——Error

Error类层次结构描述了Java运行时系统的内部错误和资源耗尽错误。应运程序不应该捕获这类异常,也不会捕获这种异常。

——Exception

Exception类是需重点掌握的异常类。Exception层次结构又分解为两个分支:一个分支派生与RuntimeException;另一个分支包含其他异常。

RuntimeException为运行时异常类,一般是程序错误产生。

派生于RuntimeException的异常类包含下面几种情况:

——错误的类型转换

——数组访问越界

——访问空指针

........

java将派生与Error类或RuntimeException类的所有异常称为未检查异常,编译器不允许对它们异常处理。

注意:如果出现RuntimeException异常,就一定是程序员的问题!

非运行时异常中程序本身没有问题,但由于某种情况的变化,程序不能正常运行,导致异常出现。

除运行时异常外,其他继承自Exception类的异常类包括:

——试图在文件后面读取数据

——试图打开一个错误格式的URL

.......

编译器要求程序必须对这类异常进行处理,称为已检查异常。

IOException:输入输出异常类

——IOException:申请I/O操作没有正常完成。

——EOFException:在输入操作正常结束前遇见了文件结束符。

——FileNotFoundException:在文件系统中,没有找到由文件名字符串指定的文件。

Exception的其它子类

声明抛出(已检查)异常

声明抛出异常 :如果一个方法可能会生成 一些异常方法,但是该方法并不确切的知道如何对这些异常事件进行处理,此时,这个方法就需声明抛出这些异常。

一个方法不仅需要告诉编译器将要返回什么值,还要告诉编译器会发生什么异常。

声明抛出异常在方法声明中用throws子句中来指明。例如:-public FileInputStream(String name)throws

FileNotFoundException

throws子句可以同时指明多个异常,说明该方法将不对这些异常进行处理,而是声明抛出它们。

——Public  static void main (String arg[])throws IOExeption,IndexOutOfBoundsException

以下4种情况需要方法用Throws子句声明抛出异常。

——方法调用抛出已检查异常的方法。

——程序运行过程中可能发生异常,并且利用throw语句抛出一个已检查异常对象。

——程序出现错误。例如a[-1]=0;

——java虚拟机和运行时库出现的内部异常。

一个方法必须声明该方法所有可能抛出的已检查异常,二未检查异常要么不可控(Error),要么应该避免发生(RuntimeException)。如果方法没有声明所有可能已发生的已检查异常,编译器会给出一个错误消息。

抛出异常:

当Java营运程序出现错误时,会根据错误类型产生一个异常对象,这个对象包含了异常的类型和错误出现时程序所处的状态信息。把异常对象递交给Java编译器的过程称为抛出。

抛出异常要生成异常对象,异常对象可由某些类的实例生成,也可由JVM生成。

抛出对象通过throw语句来实现。

如何抛出异常

首先决定抛出异常类型。

代码为:  throw new EOFException();

或者

EOFException e= new EOFException();

Throw e;

对于已存在的异常类,抛出该类的异常对象步骤为:

——找到一个合适的异常类

——创建这个类的一个对象

——将这个对象抛出。

一个方法抛出异常后,它就不能反悔调用者了。

创建异常类

自定义异常类:定义一个派生于Exception的直接或间接子类。如派生一个于Exception的类。

自定义异常类应该包括两个构造器:

——默认构造器:

——带有详细描述信息的构造器(超类Throwable的toString方法会打印出这些详细信息,有利于代码调试)

捕获异常:

程序运行期间,异常发生时,Java运行系统从异常生成的代码块开始,寻找相应的异常处理代码,并将异常交给该处理方法,这一过程叫做捕获。

某个异常发生时,若程序没有在任何地方进行该异常的捕获,则程序就会终止执行,并在控制台上输出异常信息。

若要捕获一个异常,需要在程序中设置一个try/catch/finally块 :

——Try语句括住可能抛出异常的代码段。

——catch语句指明要捕获的异常类及相应的处理代码。

——finally语句指明必须执行的程序块。

捕获异常代码为:

Try{

.......

}

Catch(Exception e){

......}

Finally{

......},

try子句
捕获异常的第一步是try{...}子句选定捕获异常的代码范围,由try所限定的代码块中的语句在执行过程中可能会自动生成异常对象并抛出。

对于异常处理,可以捕获,也可以只声明抛出不做任何处理。

catch子句

Catch块是对异常对象进行处理的代码:

每个try代码可以伴随一个或多个catch语句,用于处理try代码中所生成的各类异常事件:

catch语句只需要一个形式参数指明它所能捕获的异常类对象,这个异常类必须是Throwable的子类,运行时系统通过参数值把抛出的异常对象传递给catch块:

Catch块可通过异常对象调用类throwable

所提供的方法。

——getMessage()用来得到有关异常事件的信息:

——printStackTrance()用来跟踪异常事件发生时执行堆栈的内容。

可以在一个try块中捕获多个异常类型,每一异常类型需要单独的catch子句。如以下代码所示:

Try{抛出异常代码}

catch(ExceptionTypel e1){

抛出exceptionType1时要执行的代码}

catch(ExceptionType2 e2){

抛出exceptionType1时要执行的代码}

catch(ExceptionTypel e3){

抛出exceptionType3时要执行的代码}

catch语句的顺序:

——捕获异常的顺序和不同catch语句的顺序有关,当捕获到一个异常时,剩下的catch一句就不再进行匹配。

——因此,在安排catch语句的顺序时,首先应该捕获最特殊的异常,然后再准建一般化,也就是一般先安排子类,再安排父类。

再次抛出异常与异常链

——catch子句也可以抛出一个日常,这样做的目的时改变异常的类型。

Finally子句

捕获异常的最后一步是通过finally语句为异常处理提供一个统一的出口,时的控制流程在转到程序其它部分以前,能够对程序状态做同一的管理。

不论在try代码块中是否发生了异常事件,finally块中的语句都会被执行。

Java异常捕获处理代码的一般形式是:

try{//括号内为被监视程序块}

catch(异常类1 e1){//括号内为处理异常类1的程序块}

catch(异常类2 e2){//括号内为处理异常类2的程序块}

... ...

catch(异常类n en){//括号内为处理异常类n的程序块}

finally{//括号内必须要执行的程序块}

异常处理中分析堆栈跟踪元素

堆栈跟踪是程序执行中一个方法调用过程的列表,它包含了程序执行过程中方法调用的特定位置。

程序编码时异常处理的两种方式

(1) 积极处理方式

确切知道如何处理的异常应该捕获;

(2) 消极处理方式

不知道如何去处理的异常声明抛出。

异常处理的原则(*)

(1)异常处理不能代替简单的条件检测,旨在异常情况下使用异常机制。例如:重复对空栈进行退栈操作。

(2)程序代码不要很过分细化异常,尽量将有可能产生异常的语句放在同一个try语句块中。

(3)抛出的异常类型尽可能明确。

(4)不要压制异常,对于很少发生的异常,应该将其关闭。

(5)早抛出,晚捕获,尽可能让高层次的方法通告用户发生了错误。

断言

断言:是程序的开发和测试阶段用于插入一些代码检测一些错误语句的工具。

断言(assert)语法如下:

1、assert  条件或者2、assert  条件:表达式

编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设。断言表示为一些布尔表达式,程序员相信在程序中的某个特定点该表达式值为真,可以在任何时候启用和禁用断言验证,因此可以在测试时启用断言而在部署时禁用断言。同样,程序投入运行后,最终用户在遇到问题时可以重新启用断言。

使用断言可以创建更稳定、品质更好且 不易于出错的代码。当需要在一个值为FALSE时中断当前操作的话,可以使用断言。单元测试必须使用断言(Junit/JunitX)。

除了类型检查和单元测试外,断言还提供了一种确定各种特性是否在程序中得到维护的极好的方法。

断言的使用

1.可以在预计正常情况下程序不会到达的地方放置断言 :assert false

2.断言可以用于检查传递给私有方法的参数。(对于公有方法,因为是提供给外部的接口,所以必须在方法中有相应的参数检验才能保证代码的健壮性)

3.使用断言测试方法执行的前置条件和后置条件

4.使用断言检查类的不变状态,确保任何情况下,某个变量的状态必须满足。(如age属性应大于0小于某个合适值)

断言使用示例:

断言在默认情况下是关闭的,要在编译时启用断言,需要使用source1.4标记 既javac source1.4 Test.java ,在运行时启用断言需要使用 -ea参数 。要在系统类中启用和禁用断言可以使用 -esa 和 -dsa参数。

例如:

public class AssertExampleOne{

public AssertExampleOne(){}

public static void main(String args[]){

int x=10;

System.out.println("Testing Assertion that x==100");

assert x==100:"Out assertion failed!";

System.out.println("Test passed!");

}

}

如果编译时未加 -source1.4,则编译通不过

在执行时未加 -ea 时输出为

Testing Assertion that x==100

Test passed

jre忽略了断言的旧代码,而使用了该参数就会输出为

Testing Assertion that x==100

Exception in thread "main" java.lang.AssertionError: Out assertion failed!

at AssertExampleOne.main(AssertExampleOne.java:6)

JAVA程序中的调试技术

(1)应用局部Boolean变量

(2)应用全局Boolean变量

(3)使用断言

Eclipse下程序调试:可以交互式执行代码,检查变量和表达式的功能反应其强大性有效工具,同时 可检查和修复JAVA程序代码问题。

(1)条件断点

(2)变量断点

(3)方法断点

(4)异常断点

(5)重新调试

(6)单步执行程序

(7)检查变量

(8)改变变量值

第二部分  实验部分

1、实验目的与要求

(1) 掌握java异常处理技术;

(2) 了解断言的用法;

(3) 了解日志的用途;

(4) 掌握程序基础调试技巧;

2、实验内容和步骤

实验1:用命令行与IDE两种环境下编辑调试运行源程序ExceptionDemo1、ExceptionDemo2,结合程序运行结果理解程序,掌握未检查异常和已检查异常的区别。

//异常示例1
public class ExceptionDemo1 {
public static void main(String args[]) {
int a = ;
System.out.println( / a);
}
}
//异常示例2
import java.io.*; public class ExceptionDemo2 {
public static void main(String args[])
{
FileInputStream fis=new FileInputStream("text.txt");//JVM自动生成异常对象
int b;
while((b=fis.read())!=-)
{
System.out.print(b);
}
fis.close();
}
}

输出结果截图分别为;

命令行输出结果为:

通过异常检测后为:

public class ExceptionDemo1 {

    public static void main(String[] args) {
int a = ;
if(a==) {
System.out.println("除数为零");
}
else
{
System.out.println( / a);
} } }
import java.io.*;

public class ExceptionDemo2{
public static void main(String args[])
{
FileInputStream fis;
try {
fis = new FileInputStream("text.txt");
int b;
while((b=fis.read())!=-)
{
System.out.print(b);
}
fis.close();
}catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();//打印堆栈信息
// System.out.println("Hello.");
}//JVM自动生成异常对象
}
}

输出结果截图为:

实验2 导入以下示例程序,测试程序并进行代码注释。

测试程序1:

l 在elipse IDE中编辑、编译、调试运行教材281页7-1,结合程序运行结果理解程序;

l 在程序中相关代码处添加新知识的注释;

掌握Throwable类的堆栈跟踪方法;

程序7-1的源代码为:

package stackTrace;

import java.util.*;

/**
* A program that displays a trace feature of a recursive method call.
* //显示递归方法调用的跟踪特性的程序
* @version 1.10 2017-12-14
* @author Cay Horstmann
*/
public class StackTraceTest
{
/**
* Computes the factorial of a number
* //计算一个数的阶乘
* @param n a non-negative integer
* @return n! = 1 * 2 * . . . * n
*/
public static int factorial(int n)
{
System.out.println("factorial(" + n + "):");
var walker = StackWalker.getInstance();//创建一个新的StackWalker
walker.forEach(System.out::println);
int r;
if (n <= ) r = ;
else r = n * factorial(n - );
System.out.println("return " + r);
return r;
} public static void main(String[] args)
{
try (var in = new Scanner(System.in))
//新创建一个输入的Scanner 对象,然后赋值给in
{
System.out.print("Enter n: ");
int n = in.nextInt();
factorial(n);
}
}
}

程序运行结果截图为:

测试程序2:

l Java语言的异常处理有积极处理方法和消极处理两种方式;

l 下列两个简单程序范例给出了两种异常处理的代码格式。在elipse IDE中编辑、调试运行源程序ExceptionTest.java,将程序中的text文件更换为身份证号.txt,要求将文件内容读入内容,并在控制台显示;

l 掌握两种异常处理技术的特点。

//积极处理方式  
import java.io.*; class ExceptionTest {
public static void main (string args[])
{
try{
FileInputStream fis=new FileInputStream("text.txt");
}
catch(FileNotFoundExcption e)
{ …… }
……
}
}
//消极处理方式

import java.io.*;
class ExceptionTest {
public static void main (string args[]) throws FileNotFoundExcption
{
FileInputStream fis=new FileInputStream("text.txt");
}
}

积极处理程序源代码为:

//积极处理方式  
import java.io.*;
import java.io.BufferedReader;
import java.io.FileReader;
class ExceptionTest {
public static void main (String args[])
{ File fis=new File("身份证号.txt");
try {
FileReader fr = new FileReader(fis);
BufferedReader br = new BufferedReader(fr);
try {
String s, s2 = new String();
while ((s = br.readLine()) != null) {
s2 += s + "\n ";
}
br.close();
System.out.println(s2);
}catch (IOException e) {
e.printStackTrace();
}
}catch(FileNotFoundExcption e)
{
e.printStackTrace();
}
}
}

消极处理程序代码为:

//消极处理方式

import java.io.*;
class ExceptionTest1 {
public static void main (String args[]) throws IOException
{
File fis=new File("身份证号.txt");
FileReader fr = new FileReader(fis);
BufferedReader br = new BufferedReader(fr); String s, s2 = new String();
while ((s = br.readLine()) != null)
{
s2 += s + "\n ";
}
br.close();
System.out.println(s2);
}
}

输出结果截图为:

实验3: 编程练习

l 编写一个计算器类,可以完成加、减、乘、除的操作;

l 利用计算机类,设计一个小学生100以内数的四则运算练习程序,由计算机随机产生10道加减乘除练习题,学生输入答案,由程序检查答案是否正确,每道题正确计10分,错误不计分,10道题测试结束后给出测试总分;

l 将程序中测试练习题及学生答题结果输出到文件,文件名为test.txt;

在以上程序适当位置加入异常捕获代码。

程序源代码为:

package test;
import java.util.Random;
import java.util.Scanner;
public class test{
int sum;
public static void main(String[] args) {
test t=new test();
System.out.println("本次测试共十道题,每题十分,满分一百分");
t.sum=0;
Random r=new Random();
for(int i=0;i<10;i++) {
t.core();
}
System.out.println("考试结束");
System.out.println("你的总分为"+t.sum);
}
private void core() {
Random r=new Random();
int m,n;
m=r.nextInt(11);
n=m%4;
switch(n) {
case 0:
int a ,b,c;
a=r.nextInt(101);
b=r.nextInt(101);
System.out.println(a+"+"+"("+b+")"+"=");
Scanner x=new Scanner(System.in);
c=x.nextInt();
if(c!=a+b)
System.out.println("回答错误");
else {
System.out.println("回答正确");
sum=sum+10;
}
break;
case 1:
int h,g,f;
h=r.nextInt(101);
g=r.nextInt(101);
System.out.println(h+"-"+"("+g+")"+"= ");
Scanner u=new Scanner(System.in);
f=u.nextInt();
if(f!=h-g)
System.out.println("回答错误");
else {
System.out.println("回答正确");
sum=sum+10;
}
break;
case 2:
int q,w,e;
q=r.nextInt(101);
w=r.nextInt(101);
System.out.println(q+"*"+"("+w+")"+"= ");
  Scanner y=new Scanner(System.in);
e=y.nextInt();
if(e!=q*w)
System.out.println("回答错误");
else {
System.out.println("回答正确");
sum=sum+10;
}
break;
case 3:
double j,k,l;
j=r.nextInt(101);
k=r.nextInt(101);
if(k==0)
k++;
System.out.println(j+"/"+"("+k+")"+"= ");
Scanner z=new Scanner(System.in);
l=z.nextDouble();
if(l!=(j/k)/1.00)
System.out.println("回答错误");
else {
System.out.println("回答正常");
sum=sum+10;
}
break;
}
}

输出结果截图为:

实验4:断言、日志、程序调试技巧验证实验。

实验程序1:

//断言程序示例
public class AssertDemo {
public static void main(String[] args) {
test1(-);
test2(-);
} private static void test1(int a){
assert a > ;
System.out.println(a);
}
private static void test2(int a){
assert a > : "something goes wrong here, a cannot be less than 0";
System.out.println(a);
}
}

l 在elipse下调试程序AssertDemo,结合程序运行结果理解程序;

l 注释语句test1(-5);后重新运行程序,结合程序运行结果理解程序;

l 掌握断言的使用特点及用法。

输出结果截图为:

注释过(-5)后

实验程序2:

l 用JDK命令调试运行教材298页-300页程序7-2,结合程序运行结果理解程序;

并掌握Java日志系统的用途及用法。

程序代码为:

package logging;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.logging.*;
import javax.swing.*; /**
* A modification of the image viewer program that logs various events.
* @version 1.03 2015-08-20
* @author Cay Horstmann
*/
public class LoggingImageViewer
{
public static void main(String[] args)
{
if (System.getProperty("java.util.logging.config.class") == null
&& System.getProperty("java.util.logging.config.file") == null)
{
try
{
Logger.getLogger("com.horstmann.corejava").setLevel(Level.ALL);
final int LOG_ROTATION_COUNT = ;
Handler handler = new FileHandler("%h/LoggingImageViewer.log", , LOG_ROTATION_COUNT);
Logger.getLogger("com.horstmann.corejava").addHandler(handler);
}
catch (IOException e)
{
Logger.getLogger("com.horstmann.corejava").log(Level.SEVERE,
"Can't create log file handler", e);
}
} EventQueue.invokeLater(() ->
{
Handler windowHandler = new WindowHandler();
windowHandler.setLevel(Level.ALL);
Logger.getLogger("com.horstmann.corejava").addHandler(windowHandler); JFrame frame = new ImageViewerFrame();
frame.setTitle("LoggingImageViewer");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Logger.getLogger("com.horstmann.corejava").fine("Showing frame");
frame.setVisible(true);
});
}
} /**
* The frame that shows the image.
*/
class ImageViewerFrame extends JFrame
{
private static final int DEFAULT_WIDTH = ;
private static final int DEFAULT_HEIGHT = ; private JLabel label;
private static Logger logger = Logger.getLogger("com.horstmann.corejava"); public ImageViewerFrame()
{
logger.entering("ImageViewerFrame", "<init>");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); // set up menu bar
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar); JMenu menu = new JMenu("File");
menuBar.add(menu); JMenuItem openItem = new JMenuItem("Open");
menu.add(openItem);
openItem.addActionListener(new FileOpenListener()); JMenuItem exitItem = new JMenuItem("Exit");
menu.add(exitItem);
exitItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
logger.fine("Exiting.");
System.exit();
}
}); // use a label to display the images
label = new JLabel();
add(label);
logger.exiting("ImageViewerFrame", "<init>");
} private class FileOpenListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
logger.entering("ImageViewerFrame.FileOpenListener", "actionPerformed", event); // set up file chooser
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new File(".")); // accept all files ending with .gif
chooser.setFileFilter(new javax.swing.filechooser.FileFilter()
{
public boolean accept(File f)
{
return f.getName().toLowerCase().endsWith(".gif") || f.isDirectory();
} public String getDescription()
{
return "GIF Images";
}
}); // show file chooser dialog
int r = chooser.showOpenDialog(ImageViewerFrame.this); // if image file accepted, set it as icon of the label
if (r == JFileChooser.APPROVE_OPTION)
{
String name = chooser.getSelectedFile().getPath();
logger.log(Level.FINE, "Reading file {0}", name);
label.setIcon(new ImageIcon(name));
}
else logger.fine("File open dialog canceled.");
logger.exiting("ImageViewerFrame.FileOpenListener", "actionPerformed");
}
}
} /**
* A handler for displaying log records in a window.
*/
class WindowHandler extends StreamHandler
{
private JFrame frame; public WindowHandler()
{
frame = new JFrame();
final JTextArea output = new JTextArea();
output.setEditable(false);
frame.setSize(, );
frame.add(new JScrollPane(output));
frame.setFocusableWindowState(false);
frame.setVisible(true);
setOutputStream(new OutputStream()
{
public void write(int b)
{
} // not called public void write(byte[] b, int off, int len)
{
output.append(new String(b, off, len));
}
});
} public void publish(LogRecord record)
{
if (!frame.isVisible()) return;
super.publish(record);
flush();
}
}

程序运行结果为:

实验程序3:

l 用JDK命令调试运行教材298页-300页程序7-2,结合程序运行结果理解程序;

l 按课件66-77内容练习并掌握Elipse的常用调试技术。

l 画ULML图(思维导图)四则混合运算(运算出错)小程序软件

第三部分  实验总结

(1)在上周的学习过程中我们主要学习了异常、日制、断言和调试的相关概念及知识。通过学习异常我们更加深入的理解了在平时的学习过程中出现的错误,比如文件的输入等。同时我也意识到有些错误是我们可以通过调试,修改代码等来改变的。而有些错误是我们不能人为控制的,比如电脑内存不够等问题。
捕获异常也是我们应当尤为重视的知识,想要改正错误,我们首先得找到错误来源,然后进行处理。
(2)通过上次的考试,我意识到自己还有很多不足,编写程序是我的短板到同时理论知识我也没有足够的掌握,导致成绩很不理想,希望以后可以继续努力。

最新文章

  1. 来吧,HTML5之基础标签(上)
  2. JVM监控工具介绍
  3. CentOS安装Erlang
  4. HTML语义化之常见模块
  5. jquery中如何退出each循环
  6. VMware系统运维(十三)部署虚拟化桌面 Horizon View Agent 5.2安装
  7. pygame系列_游戏中的事件
  8. 《ECMAScript标准入门》第二版读书笔记
  9. 在Linux环境下实现一个非常好的bash脚本框架
  10. 数据库分库分表(sharding)系列
  11. rem 是如何实现自适应布局的
  12. idea远程调试tomcat
  13. [设计模式] javascript 之 命令模式
  14. 用mobiscroll.js的treelist实现弹出下拉效果
  15. mysql5.7.20更改root密码
  16. CAS与LDAP集成
  17. 键盘输入,输出int数组的函数
  18. SDN2017 第一次作业
  19. CSU - 2055 Wells‘s Lottery
  20. 搭建Git工作环境

热门文章

  1. python3.5.3rc1学习三:文件操作
  2. python time和datetime常用写法格式
  3. [C1] Andrew Ng - AI For Everyone
  4. rabbit 发送者设置
  5. D3D9.0管线图
  6. (十八)golang--defer关键字
  7. 微信小程序跳转web-vie时提示appId无法读取:Cannot read property &#39;appId&#39; of undefined
  8. MyBatis 构造动态 SQL 语句
  9. 安装docker后修改docker文件目录
  10. IO多路复用(转)