Java异常处理

很多刚接触Java的人不太理解Java的异常处理的用法,搞不清楚为啥有这个东西。

其实简单点说,异常就是控制流程的转意。没有特别的地方,可以在方法的任何地方抛出异常,也可以处理异常。Java里面的异常一般用throw,try…catch关键词。

下面是一个简单的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class T{
public void m2(){
}
public void he(Exception ex){
}
    public void m1(){
        try{
            m2();
        }catch(RuntimeException ex){
            he(ex);
        }
        catch(Exception ex){
            he(ex);
        }
    }
}

再看看这段代码的Class指令:

Compiled from "T.java"
public class T extends java.lang.Object{
public T();
  Code:
   0:	aload_0
   1:	invokespecial	#1; //Method java/lang/Object."":()V
   4:	return
 
public void m2();
  Code:
   0:	return
 
public void he(java.lang.Exception);
  Code:
   0:	return
 
public void m1();
  Code:
   0:	aload_0          //load this object....
   1:	invokevirtual	#2; //Method m2:()V  执行m2()方法
   4:	goto	22       //如果有异常就跳转。。
   7:	astore_1        //保存RuntimeException类型临时变量,异常表中会用到
   8:	aload_0         //load this object........
   9:	aload_1        //load #7的临时变量
   10:	invokevirtual	#4; //Method he:(Ljava/lang/Exception;)V
   13:	goto	22    
   16:	astore_1
   17:	aload_0
   18:	aload_1
   19:	invokevirtual	#4; //Method he:(Ljava/lang/Exception;)V
   22:	return
  Exception table:       // 异常表。
   from   to  target type
     0     4     7   Class java/lang/RuntimeException   
     0     4    16   Class java/lang/Exception
}

Java里面的异常就是控制流程的转移,没有别的东西。没有那么豪华。。。

程序中如果增加了关键词finally,指令集变得『复杂』起来。finally关键词在语言层面表示无论怎样都要被执行的意思。

还以上面的java代码为例:

public class T{
public void m2(){
}
public void he(Exception ex){
}
    public void m1(){      
        try{
            m2();
        }catch(RuntimeException ex){
            he(ex);
        }
        catch(Exception ex){
            he(ex);
        }finally{
            System.out.println("Hello");  //无论如何都打印这个字符串。
        }
    }
}

Class指令如下:

Compiled from "T.java"
public class T extends java.lang.Object{
public T();
  Code:
   0:	aload_0
   1:	invokespecial	#1; //Method java/lang/Object."":()V
   4:	return
 
public void m2();
  Code:
   0:	return
 
public void he(java.lang.Exception);
  Code:
   0:	return
 
public void m1();
  Code:
   0:	aload_0
   1:	invokevirtual	#2; //Method m2:()V
   4:	getstatic	#3; //Field java/lang/System.out:Ljava/io/PrintStream;
   7:	ldc	#4; //String Hello
   9:	invokevirtual	#5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   12:	goto	60
   15:	astore_1
   16:	aload_0
   17:	aload_1
   18:	invokevirtual	#7; //Method he:(Ljava/lang/Exception;)V
   21:	getstatic	#3; //Field java/lang/System.out:Ljava/io/PrintStream;
   24:	ldc	#4; //String Hello
   26:	invokevirtual	#5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   29:	goto	60
   32:	astore_1
   33:	aload_0
   34:	aload_1
   35:	invokevirtual	#7; //Method he:(Ljava/lang/Exception;)V
   38:	getstatic	#3; //Field java/lang/System.out:Ljava/io/PrintStream;
   41:	ldc	#4; //String Hello
   43:	invokevirtual	#5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   46:	goto	60
   49:	astore_2
   50:	getstatic	#3; //Field java/lang/System.out:Ljava/io/PrintStream;
   53:	ldc	#4; //String Hello
   55:	invokevirtual	#5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   58:	aload_2
   59:	athrow
   60:	return
  Exception table:
   from   to  target type
     0     4    15   Class java/lang/RuntimeException
 
     0     4    32   Class java/lang/Exception
 
     0     4    49   any
    15    21    49   any
    32    38    49   any
    49    50    49   any
 
}

方法m2()的字节码中0~9,15~26,32~43,49~59 分别表示了程序执行的4种可能结果。字节码变得复杂庞大。。。

GoLang学习(四)

现如今做个产品避免不了要用到数据库,GoLang做为新语言当然也提供了相关的函数库。运行环境中提供了database/sql模块,这个里面相当于Java的JDBC做了做了一些规范,但不同的是它默认有了一些实现。
GoLang对数据库的操作用起来很简单,不用写太多的代码,相对来说功能也比较少,日常用到的功能差不多都有了,没有找到批量提交功能。

以MySQL数据库举例,这也是最常用的数据库了,首先去官网下载驱动程序。

//要导入相关模块
import "database/sql"
import _ "github.com/go-sql-driver/mysql"
import "fmt"
 
func test1(){
//这个URL地址看起来很奇怪
//完成的格式:[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...&paramN=valueN]
db, e := sql.Open("mysql", "user:[email protected](1.1.1.1:3306)/databasename?charset=utf8")
defer db.Close()
if e!=nil{
    fmt.Printf("open Db error : %s\n",e.Error())
}
//接受一个interface{}数组做为参数,interface还不支持自定义结构。
rows,e := db.Query("select a,b,c from  xxxxx where x=? limit 10",10)
//打印出结果集中全部的列。
if columns ,err := rows.Columns();err==nil{
    fmt.Printf("columns : %#v\n",columns)
}
if e==nil{
   for ;rows.Next();{
        var a int
        var b int
        var c int
        //看到Scan函数别高兴,它同样不支持自定义的结构。
        e:=rows.Scan(&a,&b,&c)     
        if e==nil{
            fmt.Printf("result : %#v\n",obj)
        }else{
            fmt.Printf("scan error : %s\n",e.Error())
        }
   }
   rows.Close()
}
}
func main(){
test1()
}

DB结构里面还有预处理方法、事务方法、DB本身就是一个连接池。用起来很方便,使用很少的代码完成工作。。。。
入门扫盲结束,下一次介绍如何实现类似iBatis这样的轻量级框架。。。

GoLang学习(三)

本章节主要是Json编码/解码,golang里面的json编码/解码比python还简单,运行环境内置了json函数库,不需要单独安装模块;另外,可以支持自定义struct编解码和Java中的类似,可以把json转换成有类似属性的某个类。有一点需要注意,结构体中如果包含结构体,最好别用指针,否则用起来比较麻烦。

1、非指针方式,以下代码运行和预期一样

//导入模块
import(
        "fmt"
        "encoding/json"
      )
//定义两个数据结构
type Item struct{
    SName string
    IValue int
}
type JObject struct{
    IValue int
    SValue string
    SArray []string
    IArray[] int
    SIMap map[string]int
    OArray []Item
    SOMap map[string]Item
}
//编解码函数
func testEncoding(){
    var obj JObject
        obj.IValue=1
        obj.SValue="this is string"
        obj.IArray=[]int{1,2,3,4,5,5}
        obj.SArray=make([]string,10)
        obj.SIMap=make(map[string]int)
        obj.SIMap["akey"]=1
        obj.SIMap["bkey"]=2
        var item1 Item
            item1.SName="item name"
            item1.IValue=1
        obj.OArray=make([]Item,10)
        obj.OArray[0]=item1
        obj.SOMap=make(map[string]Item)
        obj.SOMap["abc"]=item1
    buf,err :=json.Marshal(&obj)
    if err==nil{
        fmt.Printf("encoding : %s\n",string(buf))
    }else{
        fmt.Printf("encoding error : %s\n",err.Error())
    }
}
func testDecoding(){
    var jsonstring=`{"IValue":1,"SValue":"this is string","SArray":["","","","","","","","","",""],"IArray":[1,2,3,4,5,5],"SIMap":{"akey":1,"bkey":2},"OArray":[{"SName":"item name","IValue":1},null,null,null,null,null,null,null,null,null],"SOMap":{"abc":{"SName":"item name","IValue":1}}}`
    var obj JObject
    err :=json.Unmarshal([]byte(jsonstring),&obj)
    if err==nil{
        fmt.Printf("json decode result : %#v\n",obj)
    }else{
        fmt.Printf("encoding error : %s\n",err.Error())
    }
}
//main函数
func main(){
    testEncoding()
    testDecoding()
}

2、把结构体JObject里面的某个属性修改成指针类型;

type JObject struct{
    IValue int
    SValue string
    SArray []string
    IArray[] int
    SIMap map[string]int
    OArray []*Item   //此处修改成指针数组
    SOMap map[string]Item
}
//编码解码函数需要做相应的调整
func testEncoding(){
    var obj JObject
        obj.IValue=1
        obj.SValue="this is string"
        obj.IArray=[]int{1,2,3,4,5,5}
        obj.SArray=make([]string,10)
        obj.SIMap=make(map[string]int)
        obj.SIMap["akey"]=1
        obj.SIMap["bkey"]=2
        var item1 Item
            item1.SName="item name"
            item1.IValue=1
        obj.OArray=make([]*Item,10)  //此处修改为指针类型
        obj.OArray[0]=&item1  //此处
        obj.SOMap=make(map[string]Item)
        obj.SOMap["abc"]=item1
    buf,err :=json.Marshal(&obj)
    if err==nil{
        fmt.Printf("encoding : %s\n",string(buf))
    }else{
        fmt.Printf("encoding error : %s\n",err.Error())
    }
}
 
//解码函数
func testDecoding(){
    var jsonstring=`{"IValue":1,"SValue":"this is string","SArray":["","","","","","","","","",""],"IArray":[1,2,3,4,5,5],"SIMap":{"akey":1,"bkey":2},"OArray":[{"SName":"item name","IValue":1},null,null,null,null,null,null,null,null,null],"SOMap":{"abc":{"SName":"item name","IValue":1}}}`
    var obj JObject
        obj.OArray=make([]*Item,10) //A:此处初始化一个数组
        var item1 Item
        obj.OArray[0]=&item1 //B:此处引用
    err :=json.Unmarshal([]byte(jsonstring),&obj)
    if err==nil{
        fmt.Printf("item1 : %#v\n",item1)
        fmt.Printf("json decode result : %#v\n",obj)
    }else{
        fmt.Printf("encoding error : %s\n",err.Error())
    }
}

编码运行结果:通第一个例子
解码运行结果:

item1 : main.Item{SName:"item name", IValue:1}
json decode result : main.JObject{IValue:1, SValue:"this is string", SArray:[]string{"", "", "", "", "", "", "", "", "", ""}, IArray:[]int{1, 2, 3, 4, 5, 5}, SIMap:map[string]int{"akey":1, "bkey":2}, OArray:[]*main.Item{(*main.Item)(0x21021d200), (*main.Item)(nil), (*main.Item)(nil), (*main.Item)(nil), (*main.Item)(nil), (*main.Item)(nil), (*main.Item)(nil), (*main.Item)(nil), (*main.Item)(nil), (*main.Item)(nil)}, SOMap:map[string]main.Item{"abc":main.Item{SName:"item name", IValue:1}}}

数组第一个元素是非空的,因为预先给这个指针引用了一个数据,如果在解码函数中去掉A到B三行,解码出来的数组都会是(nil)。
这就引发一个问题,一般我们不知道数组的长度是多少,也就是A处无法写长度,那么就无法正确解码出来了,得到的都是(nil)。
看decode.go代码的实现,没有对指针的处理,也就是遇到指针的情况没有处理。仔细想是对的,解码函数内部不应该[分配内存]返回一个指针,这个恰恰是调用者应该做的。

所以,如果不用指针用起来会更方便。

————–EOF————–