1、栈
1、什么是栈
- 栈的英文为(stack)
- 栈是一个先入后出(FILO-First In Last Out)的有序列表
- 栈(stack)是限制线性表中元素的插入和删除只能在线性表的同一段进行的一种特殊线性表,允许插入和删除的一端,为变化的一端,称为栈顶(top),另外一端为固定的一端,称为栈低(Bottom)。
- 根据栈的定义可知,最先放入栈中元素的栈低,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除
- 图解出栈(pop)入栈(push)
-
2、栈的实际应用
1、输入表达式,计算结果
2、引用场景
子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中。
- 处理递归调用:和子程序的调用类似,只是除了储存下一个指令的地址外,也将参数、区域变量等数据存入堆栈中。
- 表达式的转换[中缀表达式转后缀表达式]与求值(实际解决)。
- 二叉树的遍历。
-
3、快速入门
1、用数组实现栈
用数组模拟栈的使用,由于栈是一种有序列表,当然可以使用数组的结构来存储栈的数据内容
2、思路分析
实现栈的思路分析使用数组来模拟栈
- 定义一个top来表示栈顶,初始化为-1
- 入栈的操作,当有数据加入到栈时,top++;stack[top]=data;
- 出栈的操作,int value = stack[top];top—,return value;
3、代码实现
1、数组实现
```java package com.daijunyi.structure.stack;
class ArrayStackMain{
public static void main(String[] args) {
ArrayStack
/**
- @author djy
- @createTime 2021/12/25 上午7:47
@description 用数组实现栈 */ public class ArrayStack
{ private Object[] array;
private int maxSize;
private int top;
public ArrayStack(int maxSize){
array = new Object[maxSize];
this.maxSize = maxSize;
top = -1;
}
public Boolean isFull(){
return maxSize-1 == top;
}
public Boolean isEmpty(){
return top == -1;
}
public T push(T element){
if (isFull()){ System.out.println("栈满了没有数据了"); return null; } array[++top] = element; return element;
}
public T pop(){
if (isEmpty()){ System.out.println("栈中没有数据了"); return null; } Object o = array[top]; top--; return (T) o;
}
public T peek(){
if (isEmpty()){ System.out.println("栈中没有数据了"); return null; } return (T) array[top];
}
}
<a name="kbiaj"></a>
#### 2、单链表实现栈
<a name="HuGBm"></a>
##### 1、创建一个单链表
- LinkedList
```java
package com.daijunyi.structure.linked;
/**
* @author djy
* @createTime 2021/12/27 下午7:33
* @description
*/
public class LinkedList<T> {
private Node<T> head = new Node(null);
private int size = 0;
public boolean add(T element){
if (element == null){
return false;
}
Node tmp = head;
while (tmp.next != null){
tmp = tmp.next;
}
tmp.next = new Node<T>(element);
size++;
return true;
}
/**
* 添加到第一个
* @param element
* @return
*/
public boolean addFront(T element){
if (element == null){
return false;
}
Node<T> tNode = new Node<>(element);
tNode.next = head.next;
head.next = tNode;
return true;
}
/**
* 根据角标添加
* @param element
* @param index
* @return
*/
public boolean add(T element,int index){
if (element == null){
return false;
}
int currentIndex = 0;
Node tmp = head;
while (tmp.next != null){
if (currentIndex == index){
Node<T> tNode = new Node<>(element);
tNode.next = tmp.next;
tmp.next = tNode;
size++;
return true;
}
currentIndex++;
tmp = tmp.next;
}
throw new IndexOutOfBoundsException("角标越界");
}
public T first(){
return head.next.obj;
}
public boolean remove(T element){
if (element == null){
return false;
}
Node<T> tmp = head;
while (tmp.next != null){
if (tmp.next.obj == element){
tmp.next = tmp.next.next;
return true;
}
tmp = tmp.next;
}
return false;
}
private class Node<T>{
T obj;
Node<T> next;
public Node(T obj) {
this.obj = obj;
}
}
}
2、用单链表来实现
package com.daijunyi.structure.stack;
import com.daijunyi.structure.linked.LinkedList;
class SingleLinkedStackMain{
public static void main(String[] args) {
SingleLinkedStack<Integer> stack = new SingleLinkedStack<>(3);
System.out.printf("加入数据:%d",stack.push(1));
System.out.printf("加入数据:%d",stack.push(2));
System.out.printf("加入数据:%d",stack.push(3));
System.out.printf("取出数据:%d",stack.pop());
System.out.printf("取出数据:%d",stack.pop());
System.out.printf("取出数据:%d",stack.pop());
}
}
/**
* @author djy
* @createTime 2021/12/27 下午7:26
* @description 单链表栈
*/
public class SingleLinkedStack<T> {
private int maxSize;
private LinkedList<T> list = new LinkedList<>();
private int top;
public SingleLinkedStack(int maxSize){
this.maxSize = maxSize;
top = -1;
}
public boolean isFull(){
return top+1 == maxSize;
}
public boolean isEmpty(){
return top == -1;
}
public T push(T element){
if (isFull()){
System.out.println("已经满了没法再增加了");
return null;
}
list.addFront(element);
top++;
return element;
}
public T pop(){
if (isEmpty()){
System.out.println("已经没有数据可以退出了");
return null;
}
T first = list.first();
list.remove(first);
return first;
}
}
4、栈实现综合计算器(中缀表达式)
1、不考虑()不考虑小数的的计算
1、目标
输入一个表达式:比如,计算式722-5+1-5+3-3
- 先不考虑()
- 不考虑小数
-
2、思路分析
创建两个栈,一个保存数字,一个保存符号栈
- 通过一个index值,来遍历我们的表达式,扫描字符串
- 如果发现是一个数字,缓存追加当前数字
- 下一位是数字,就继续循环,并且缓存当前值
- 下一位不是数字,直接把缓存值入数字栈
- 如果扫描到一个符号,就分如下情况
- 当前符号栈为空,直接入栈
- 符号栈有操作符,进行比较,
- 当前的操作符优先级小于或者等于栈中操作符,就需要从数栈中pop出两个数,在从符号栈中pop出一个符号,进行运算,将得到的结果,入数栈,并重复刚刚的步骤,直到符号栈为空,或者当前符号优先级大于栈中符号,
- 如果当前的操作符优先级大于栈中的操作符,直接入符号栈
- 当前表达式扫描完毕,就顺序的从数栈和符号栈中pop出相应的数和符号,并运算,值再存入数字栈
- 最后在数栈中只有一个数字,就是表达式的结果
3、代码实现
package com.daijunyi.structure.calculator;
import com.daijunyi.structure.stack.ArrayStack;
class CalculatorInfixMain{
public static void main(String[] args) {
int compute = CalculatorInfix.compute("6-8*5+2");
System.out.println("结果"+compute);
int compute1 = CalculatorInfix.compute("10-1002-2+100*400+2");
System.out.println("结果"+compute1);
}
}
/**
* @author djy
* @createTime 2021/12/27 下午8:48
* @description 中缀计算器
*/
public class CalculatorInfix {
/**
*
* ● 创建两个栈,一个保存数字,一个保存符号栈
* ● 通过一个index值,来遍历我们的表达式,扫描字符串
* ● 如果发现是一个数字
* 如果下一位也是数字就继续循环,缓存当前的数值
* 如果下一位不是数字/空就直接把当前的值入栈
* ● 如果扫描到一个符号,就分如下情况
* ○ 当前符号栈为空,直接入栈
* ○ 符号栈有操作符,进行比较,
* ■ 当前的操作符优先级小于或者等于栈中操作符,就需要从数栈中pop出两个数,在从符号栈中pop出一个符号,进行运算,将得到的结果,入数栈,再次重复刚刚的比较。直到操作符栈为空,或者优先级大于操作符栈第一个
* ■ 如果当前的操作符优先级大于栈中的操作符,直接入符号栈
* ○ 当前表达式扫描完毕,就顺序的从数栈和符号栈中pop出相应的数和符号,并运算,值再存入数字栈
* ○ 最后在数栈中只有一个数字,就是表达式的结果
* 计算中缀表达式 不包含()整数,加减乘除
* @param expression
* @return
*/
public static int compute(String expression){
//数字栈
ArrayStack<Integer> numberStack = new ArrayStack<>(100);
//符号栈
ArrayStack<String> operatorStack = new ArrayStack<>(100);
//扫描中缀表达式进栈
scanExpression(expression, numberStack, operatorStack);
//进行计算
while (!operatorStack.isEmpty()){
Integer value2 = numberStack.pop();
Integer value1 = numberStack.pop();
numberStack.push(compute(value1,value2,operatorStack.pop()));
}
return numberStack.pop();
}
private static void scanExpression(String expression, ArrayStack<Integer> numberStack, ArrayStack<String> operatorStack) {
int index = 0;
String s;
String numberCache = "";
while (true){
if (index == expression.length()){
break;
}
s = expression.substring(index, index+1);
if (s.matches("^\\d+$")){
numberCache += s;
//数字
if (index+1 <expression.length()){
String nextV = expression.substring(index + 1, index+2);
if (nextV.matches("^\\d$")){
}else{
numberStack.push(Integer.valueOf(numberCache));
numberCache = "";
}
}else{
numberStack.push(Integer.valueOf(numberCache));
numberCache = "";
}
}else{
if (operatorStack.isEmpty()){
//符号栈为空直接入栈
operatorStack.push(s);
}else{
//符号
while (operatorStack.peek() != null && priority(s) <= priority(operatorStack.peek())){
//需要pop两个数进行计算
Integer value2 = numberStack.pop();
Integer value1 = numberStack.pop();
//计算并入栈
numberStack.push(compute(value1,value2,operatorStack.pop()));
}
operatorStack.push(s);
}
}
index++;
}
}
private static int compute(Integer front,Integer rear,String operator){
switch (operator){
case "*":
return front*rear;
case "/":
return front/rear;
case "-":
return front-rear;
case "+":
return front+rear;
default:
return 0;
}
}
private static int priority(String operator){
if ("*".equals(operator) || "/".equals(operator)) {
return 3;
}else if ("-".equals(operator)){
return 2;
}else if ("+".equals(operator)){
return 1;
}else {
return 0;
}
}
}
2、考虑()以及小数的计算
1、目标
输入一个表达式:比如,计算式7.222-(5+1)-5+3-3
- 考虑()
- 考虑小数
-
2、思路分析
创建两个栈,一个保存数字,一个保存符号栈
- 通过一个index值,来遍历我们的表达式,扫描字符串
- 如果发现是一个数字,缓存追加当前数字
- 下一位是数字,就继续循环,并且缓存当前值
- 下一位不是数字,直接把缓存值入数字栈
- 如果扫描到一个符号,就分如下情况
- 当前符号栈为空,直接入栈
- 符号栈有操作符,进行比较,
- 当前的操作符优先级小于或者等于栈中操作符,就需要从数栈中pop出两个数,在从符号栈中pop出一个符号,进行运算,将得到的结果,入数栈,并重复刚刚的步骤,直到符号栈为空,或者当前符号优先级大于栈中符号,
- 如果当前的操作符优先级大于栈中的操作符,直接入符号栈
- 当前表达式扫描完毕,就顺序的从数栈和符号栈中pop出相应的数和符号,并运算,值再存入数字栈
- 最后在数栈中只有一个数字,就是表达式的结果
- 如果扫描到(符号,是直接入栈
- 如果扫描到)是要进行计算直到扫描到符号栈中最上面一个是(
- 计算结果直接入数栈,并且舍弃)和pop出符号栈的(舍弃
3、代码实现
package com.daijunyi.structure.calculator;
import com.daijunyi.structure.stack.ArrayStack;
import java.math.BigDecimal;
class CalculatorInfixBracketAndDecimalsMain{
public static void main(String[] args) {
BigDecimal compute = CalculatorInfixBracketAndDecimals.compute("6.2-8*(5+2)/2");
System.out.println("结果"+compute.toString());
BigDecimal compute1 = CalculatorInfixBracketAndDecimals.compute("10.55-1002.3/2*(3-2)+100.2*400+2");
System.out.println("结果"+compute1.toString());
}
}
/**
* @author djy
* @createTime 2021/12/29 下午4:57
* @description 计算器中缀表达式 处理 括号,处理小数
*/
public class CalculatorInfixBracketAndDecimals {
/**
*
● 创建两个栈,一个保存数字,一个保存符号栈
● 通过一个index值,来遍历我们的表达式,扫描字符串
● 如果发现是一个数字,缓存追加当前数字
○ 下一位是数字,就继续循环,并且缓存当前值
○ 下一位不是数字,直接把缓存值入数字栈
● 如果扫描到一个符号,就分如下情况
○ 当前符号栈为空,直接入栈
○ 符号栈有操作符,进行比较,
■ 当前的操作符优先级小于或者等于栈中操作符,就需要从数栈中pop出两个数,在从符号栈中pop出一个符号,进行运算,将得到的结果,入数栈,并重复刚刚的步骤,直到符号栈为空,或者当前符号优先级大于栈中符号,
■ 如果当前的操作符优先级大于栈中的操作符,直接入符号栈
○ 当前表达式扫描完毕,就顺序的从数栈和符号栈中pop出相应的数和符号,并运算,值再存入数字栈
○ 最后在数栈中只有一个数字,就是表达式的结果
● 如果扫描到(符号,是直接入栈
● 如果扫描到)是要进行计算直到扫描到符号栈中最上面一个是(
○ 计算结果直接入数栈,并且舍弃)和pop出符号栈的(舍弃
* @param expression
* @return
*/
public static BigDecimal compute(String expression){
//数字栈
ArrayStack<BigDecimal> numberStack = new ArrayStack<>(100);
//符号栈
ArrayStack<String> operatorStack = new ArrayStack<>(100);
//扫描中缀表达式进栈
scanExpression(expression, numberStack, operatorStack);
//进行计算
while (!operatorStack.isEmpty()){
BigDecimal value2 = numberStack.pop();
BigDecimal value1 = numberStack.pop();
numberStack.push(compute(value1,value2,operatorStack.pop()));
}
return numberStack.pop();
}
private static void scanExpression(String expression, ArrayStack<BigDecimal> numberStack, ArrayStack<String> operatorStack) {
int index = 0;
String s;
String numberCache = "";
while (true){
if (index == expression.length()){
break;
}
s = expression.substring(index, index+1);
//匹配数字或者小数点.
if (checkIsNumber(s)){
numberCache += s;
//数字
if (index+1 <expression.length()){
String nextV = expression.substring(index + 1, index+2);
if (checkIsNumber(nextV)){
}else{
numberStack.push(new BigDecimal(numberCache));
numberCache = "";
}
}else{
numberStack.push(new BigDecimal(numberCache));
numberCache = "";
}
}else if(s.matches("^\\({1}$")) {
operatorStack.push(s);
}else if (s.matches("^\\){1}$")){
//判断是不是(的话就继续计算
while (!operatorStack.peek().matches("^\\({1}$")){
BigDecimal value2 = numberStack.pop();
BigDecimal value1 = numberStack.pop();
numberStack.push(compute(value1,value2,operatorStack.pop()));
}
//舍弃掉(
operatorStack.pop();
}else{
if (operatorStack.isEmpty()){
//符号栈为空直接入栈
operatorStack.push(s);
}else{
//符号
while (operatorStack.peek() != null && priority(s) <= priority(operatorStack.peek())){
//需要pop两个数进行计算
BigDecimal value2 = numberStack.pop();
BigDecimal value1 = numberStack.pop();
//计算并入栈
numberStack.push(compute(value1,value2,operatorStack.pop()));
}
operatorStack.push(s);
}
}
index++;
}
}
private static boolean checkIsNumber(String s) {
return s.matches("^\\d+$") || s.matches("^\\.{1}$");
}
private static BigDecimal compute(BigDecimal front, BigDecimal rear, String operator){
switch (operator){
case "*":
return front.multiply(rear);
case "/":
return front.divide(rear);
case "-":
return front.subtract(rear);
case "+":
return front.add(rear);
default:
return BigDecimal.valueOf(0);
}
}
private static int priority(String operator){
if ("*".equals(operator) || "/".equals(operator)) {
return 3;
}else if ("-".equals(operator)){
return 2;
}else if ("+".equals(operator)){
return 1;
}else {
return 0;
}
}
}
5、逆波兰计算器
1、目标完成一个逆波兰计算器
中缀表达式(3+4)*5-6对应的后缀表达式就是3 4 + 5 X 6 -,计算这个后缀表达式的值
2、分析
- 从左至右扫描,将3和4压入栈
- 遇到运算符,弹出数字3和4进行计算3+4=7 压入栈
- 继续扫描到5 压入栈
- 扫描到符号X 弹出 7和5 计算 7*5=35
- 将6压入栈
- 最后扫描到运算符 - 出栈 35-6=29,所以结果是29
3、代码实现
```java package com.daijunyi.structure.calculator;
import java.math.BigDecimal; import java.util.Stack;
class PolandNotationCalculatorMain{ public static void main(String[] args) { BigDecimal compute = PolandNotationCalculator.compute(“3 4 + 5 * 6 -“); System.out.println(“计算的值:”+compute.toString()); } }
/**
- @author djy
- @createTime 2021/12/30 下午2:05
@description 逆波兰计算器 */ public class PolandNotationCalculator {
public static BigDecimal compute(String expression){
if (expression == null || expression.length() == 0){ return BigDecimal.valueOf(0); } String[] s = expression.split(" "); Stack<BigDecimal> stack = new Stack<>(); for (String item : s){ if (item.matches("\\d")){ //数字 stack.push(new BigDecimal(item)); }else{ BigDecimal rear = stack.pop(); BigDecimal front = stack.pop(); stack.push(CalculatorInfixBracketAndDecimals.compute(front, rear, item)); } } return stack.pop();
}
}
<a name="Sfrbr"></a>
## 6、中缀表达式转换为后缀表达式
<a name="IlxMU"></a>
### 1、目标
支持小数,支持括号
<a name="Ehwwc"></a>
### 2、分析
1. 初始化两个栈,运算符栈S1和存储中间结果的栈S2;
1. 从左至右扫描中缀表达式
1. 遇到操作数时,将其压入S2
1. 遇到运算符时,比较其与S1栈顶运算符的优先级
1. 如果s1为空,或栈顶运算符为左括号(,则直接将此运算符入栈
1. 否则,若优先级比栈顶运算符的高,也将运算符压入s1
1. 否则,将s1栈顶的运算符弹出压入到s2中,再次重复新的S1栈顶优先级比较
5. 遇到括号时
1. 如果是左括号(直接压入S1
1. 如果是右括号,则依次弹出S1栈顶的运算符,并压入S2,直到遇到左括号为止,此时将这一对括号丢弃
6. 重复步骤2至5,直到表达式的最右边
6. 将S1中剩余的运算符依次弹出并压入S2
6. 依次弹出S2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
<a name="YxFZe"></a>
### 3、举例
将中缀表达式 1+((2+3)X4)-5转换成后缀表达式<br />结果:1 2 3 + 4 X + 5 -
| **扫描到的元素** | **S2结果(栈低->栈顶)** | **S1符栈(栈低->栈顶)** | **说明** |
| --- | --- | --- | --- |
| 1 | 1 | 空 | 数字,直接入结果栈 |
| + | 1 | + | S1为空,运算符入栈 |
| ( | 1 | +( | 左括号直接入栈 |
| ( | 1 | +(( | 左括号直接入栈 |
| 2 | 1 2 | +(( | 数字直接入栈 |
| + | 1 2 | +((+ | 直接入栈 |
| 3 | 1 2 3 | +((+ | 数值直接入栈 |
| ) | 1 2 3 + | +( | 遇到右括号运算符出栈,直到遇到左括号 |
| X | 1 2 3 + | +(X | 栈顶为(直接入符号栈 |
| 4 | 1 2 3 + 4 | +(X | 直接入数栈 |
| ) | 1 2 3 + 4 X | + | 遇到右括号运算符出栈,直到遇到左括号 |
| - | 1 2 3 + 4 X + | - | 优先级相同,弹出,压入结果栈 |
| 5 | 1 2 3 + 4 X + 5 | - | 数字直接入栈 |
| 到达最右端 | 1 2 3 + 4 X + 5 - | 空 | s1中剩余的运算符 |
<a name="Lv1o5"></a>
### 4、代码实现
```java
package com.daijunyi.structure.calculator;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Stack;
class PolandNotationCalculatorMain{
public static void main(String[] args) {
BigDecimal compute = PolandNotationCalculator.computePostfix("3 4 + 5 X 6 -");
System.out.println("计算的值:"+compute.toString());
String s = PolandNotationCalculator.infixToPostfixExpression("(3+4)*5-6");
System.out.println("后缀表达式:"+s);
BigDecimal bigDecimal = PolandNotationCalculator.computeInfix("(3+4)*5-6");
System.out.println("计算值:"+bigDecimal);
}
}
/**
* @author djy
* @createTime 2021/12/30 下午2:05
* @description 逆波兰计算器
* 中缀转后缀
* 逆波兰计算器
*/
public class PolandNotationCalculator {
/**
* 中缀转后缀表达式
* 1+((2+3)X4)-5
* 1 2 3 + 4 X + 5 -
* @param infixExpression
* @return
*/
public static String infixToPostfixExpression(String infixExpression){
//结果
ArrayList<String> resultArray = new ArrayList<>();
//符号
Stack<String> operatorStackStack = new Stack<>();
char[] chars = infixExpression.toCharArray();
int index = 0;
String numberCache = "";
for (char item : chars){
String s = String.valueOf(item);
if (checkIsNumber(s)){
numberCache += s;
//数字
//判断下一个是不是数字
if (index+1 != chars.length && checkIsNumber(String.valueOf(chars[index+1]))){
}else{
resultArray.add(numberCache);
numberCache="";
}
}else if (s.matches("^\\({1}$")){
operatorStackStack.push(s);
}else if(s.matches("^\\){1}$")){
//符号栈直到第一个是(
while (operatorStackStack.size() > 0 && !operatorStackStack.peek().matches("^\\({1}$")){
resultArray.add(operatorStackStack.pop());
}
if (operatorStackStack.size() > 0){
operatorStackStack.pop();
}
}else{
while (operatorStackStack.size() > 0
&& !operatorStackStack.peek().matches("^\\({1}$")
&& priority(s) <= priority(operatorStackStack.peek())){
resultArray.add(operatorStackStack.pop());
}
operatorStackStack.push(s);
}
index++;
}
//符号栈一次入结果栈
while (operatorStackStack.size() > 0){
resultArray.add(operatorStackStack.pop());
}
//拼接后缀表达式 空格隔开
String expression = "";
for (String item : resultArray){
if (expression.length() > 0){
expression += " ";
}
expression += item;
}
return expression;
}
/**
* 判断是不是数字
* @param s
* @return
*/
private static boolean checkIsNumber(String s) {
return s.matches("^\\d{1}$") || s.matches("^\\.{1}$");
}
/**
* 中缀计算结果
* @param expression
* @return
*/
public static BigDecimal computeInfix(String expression){
String s = infixToPostfixExpression(expression);
return computePostfix(s);
}
/**
* 后缀计算结果
* @param expression
* @return
*/
public static BigDecimal computePostfix(String expression){
if (expression == null || expression.length() == 0){
return BigDecimal.valueOf(0);
}
String[] s = expression.split(" ");
Stack<BigDecimal> stack = new Stack<>();
for (String item : s){
if (item.matches("\\d")){
//数字
stack.push(new BigDecimal(item));
}else{
BigDecimal rear = stack.pop();
BigDecimal front = stack.pop();
stack.push(CalculatorInfixBracketAndDecimals.compute(front, rear, item));
}
}
return stack.pop();
}
private static int priority(String operator){
if ("*".equals(operator) || "/".equals(operator)) {
return 3;
}else if ("-".equals(operator) || "+".equals(operator)){
return 2;
}else {
return 0;
}
}
}
2、递归(Recursion)
1、递归的概念
递归就是方法自己调用自己,每次调用时传入不同的变量,递归有助于编程者解决复杂的问题,同时可以让代码变的简洁
2、规则
递归需要遵守的重要规则
- 1) 执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
- 2) 方法的局部变量是独立的,不会相互影响, 比如n变量
- 3)如果方法中使用的是引用类型变量(比如数组),就会共享该引用类型的数据.
- 4) 递归必须向退出递归的条件逼近,否则就是无限递归,出现StackOverflowError,死龟了:)
- 5) 当一个方法执行完毕,或者遇到 return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕