java holder详解,Java基础系列18:Holder技术的实现原理分析

一 简介

(1)Java中的Holder是什么?

我这里说的Holder即这个类:javax.xml.ws.Holder

这个类属于JAX-WS 2.0规范中的一个类。它的作用是为不可变的对象引用提供一个可变的包装,这样就可以使Java能够与支持INOUT、OUT参数的编程语言编写的web service进行通信。示例代码如下:

Java

/**

* 分页查询航线

*

* @param page

*  分页属性(如:总数、每页数目、当前页、排序等)

* @return 航线集合

*/

@WebMethod

public RPCResponse selectAirlineByPage(@WebParam(name = "pageInfoHolder", mode = WebParam.Mode.INOUT) Holder pageInfoHolder);

1

2

3

4

5

6

7

8

9/**

* 分页查询航线

*

* @param page

*             分页属性(如:总数、每页数目、当前页、排序等)

* @return 航线集合

*/

@WebMethod

publicRPCResponseselectAirlineByPage(@WebParam(name="pageInfoHolder",mode=WebParam.Mode.INOUT)HolderpageInfoHolder);

了解web service的同学应该可以看出,这是一个简单的分页查询的web service接口,其中页面属性“pageInfoHolder”就使用了Holder来包装。同时,这里还标注了其类型是INOUT,这就表示可以在方法中将 pageInfoHolder 修改之后,不需要显式使用return返回,web service就可以自动将这个pageInfoHolder参数在方法结束时返回

上面这个接口的实现代码如下:

@Override

public RPCResponse selectAirlineByPage(

Holder pageInfoHolder) {

RPCResponse response = new RPCResponse();

if (pageInfoHolder.value != null) {

long count = airlineManager.selectCountAirline();

PageInfo pageInfo = new PageInfo(count,

pageInfoHolder.value.getPerSize(),

pageInfoHolder.value.getCurrentPage(),

pageInfoHolder.value.getSortName(),

pageInfoHolder.value.getSortOrder());

response.setResponseHolder(airlineManager

.selectAirlineByPage(pageInfo));

pageInfoHolder.value = pageInfo;

}

return response;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20@Override

publicRPCResponseselectAirlineByPage(

HolderpageInfoHolder){

RPCResponseresponse=newRPCResponse();

if(pageInfoHolder.value!=null){

longcount=airlineManager.selectCountAirline();

PageInfopageInfo=newPageInfo(count,

pageInfoHolder.value.getPerSize(),

pageInfoHolder.value.getCurrentPage(),

pageInfoHolder.value.getSortName(),

pageInfoHolder.value.getSortOrder());

response.setResponseHolder(airlineManager

.selectAirlineByPage(pageInfo));

pageInfoHolder.value=pageInfo;

}

returnresponse;

}

从上面的代码可以看出,pageInfoHolder并没有使用return返回,但是我们调用这个web service接口之后的pageInfoHolder实例却可以对应发生改变(PS:即上面代码中新增的PageInfo属性——count)

注:如果希望开发的接口能够同时兼容RESTFUL风格和SOAP风格,那么最好不要使用上面这种INOUT类型的传参方式,而是将所有希望返回的内容再次使用对象封装之后再return返回

(2)一个简化实例

如果说上面的测试代码涉及到了web service,因此不熟悉相关内容的话可能不太好理解。那么不改变原理,针对上面的例子进行简化,大致相当于下面这个例子:

i)javax.xml.ws.Holder类的源码:

Java

/*

* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.

* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.

*/

package javax.xml.ws;

import java.io.Serializable;

/**

* Holds a value of type T.

*

* @since JAX-WS 2.0

*/

public final class Holder implements Serializable {

private static final long serialVersionUID = 2623699057546497185L;

/**

* The value contained in the holder.

*/

public T value;

/**

* Creates a new holder with a null value.

*/

public Holder() {

}

/**

* Create a new holder with the specified value.

*

* @param value The value to be stored in the holder.

*/

public Holder(T value) {

this.value = value;

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39/*

* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.

* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.

*/

packagejavax.xml.ws;

importjava.io.Serializable;

/**

* Holds a value of type T.

*

* @since JAX-WS 2.0

*/

publicfinalclassHolderimplementsSerializable{

privatestaticfinallongserialVersionUID=2623699057546497185L;

/**

* The value contained in the holder.

*/

publicTvalue;

/**

* Creates a new holder with a null value.

*/

publicHolder(){

}

/**

* Create a new holder with the specified value.

*

* @param value The value to be stored in the holder.

*/

publicHolder(Tvalue){

this.value=value;

}

}

这里首先给出Holder类的源代码。可以发现代码很简单,仅仅只是一个对泛型的value对象的包装类

ii)测试实例:

Java

package cn.zifangsky.holder;

import javax.xml.ws.Holder;

public class Demo3 {

public static void testHolder(Holder uHolder){

User user = new User();

user.setId(uHolder.value.getId());

user.setName(uHolder.value.getName());

user.setPassword(uHolder.value.getPassword());

user.setHomepage("https://www.zifangsky.cn"); //新增

uHolder.value = user;

}

public static void main(String[] args) {

User user = new User();

user.setId(1);

user.setName("zifangsky");

user.setPassword("123456");

//Holder holder = new Holder();

//holder.value = user;

Holder holder = new Holder(user); //使用Holder对User进行包装

Demo3.testHolder(holder);

System.out.println("新增的主页是: " + holder.value.getHomepage());

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32packagecn.zifangsky.holder;

importjavax.xml.ws.Holder;

publicclassDemo3{

publicstaticvoidtestHolder(HolderuHolder){

Useruser=newUser();

user.setId(uHolder.value.getId());

user.setName(uHolder.value.getName());

user.setPassword(uHolder.value.getPassword());

user.setHomepage("https://www.zifangsky.cn");//新增

uHolder.value=user;

}

publicstaticvoidmain(String[]args){

Useruser=newUser();

user.setId(1);

user.setName("zifangsky");

user.setPassword("123456");

//        Holder holder = new Holder();

//        holder.value = user;

Holderholder=newHolder(user);//使用Holder对User进行包装

Demo3.testHolder(holder);

System.out.println("新增的主页是: "+holder.value.getHomepage());

}

}

最后的输出如下:

新增的主页是: https://www.zifangsky.cn

1新增的主页是:https://www.zifangsky.cn

可以发现,在testHolder方法中是没有返回值的,但是我们在testHolder方法中对uHolder的value属性——User对象改变之后,这种改变反应到了方法调用之前的holder.value

这就是Holder这个类的使用地方,即:不通过返回值在一个方法中改变一个对象

二 Holder技术的实现原理分析

(1)Java中的参数传递:

在正式介绍Holder技术的实现原理之前,容我简单介绍下Java中的参数传递

在Java中,虽然我们通常会说值传递和引用传递。但是,在底层工作原理上是不存在传引用的概念的,Java的参数传递只有传值

Java中的基本类型(PS:int、long、boolean等)存放在栈内存中,Java对象引用(PS:Integer、Long、Boolean等其他Java对象)参数也存放在栈内存中,但是引用指向的具体的值却存放在堆内存中。下面先看一个简单的例子:

Java

package cn.zifangsky.holder;

public class Demo {

public static void main(String[] args) {

int a = 1;

change(a);

System.out.println("a之后的值: " + a);

System.out.println("-----------------我是分割线--------------------");

Integer b = 1;

change2(b);

System.out.println("b之后的值: " + b);

}

public static void change(int aa){

System.out.println("aa初始值: " + aa);

aa = 10;

System.out.println("aa之后的值: " + aa);

}

public static void change2(Integer bb){

System.out.println("bb初始值: " + bb);

bb = 10;

System.out.println("bb之后的值: " + bb);

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29packagecn.zifangsky.holder;

publicclassDemo{

publicstaticvoidmain(String[]args){

inta=1;

change(a);

System.out.println("a之后的值: "+a);

System.out.println("-----------------我是分割线--------------------");

Integerb=1;

change2(b);

System.out.println("b之后的值: "+b);

}

publicstaticvoidchange(intaa){

System.out.println("aa初始值: "+aa);

aa=10;

System.out.println("aa之后的值: "+aa);

}

publicstaticvoidchange2(Integerbb){

System.out.println("bb初始值: "+bb);

bb=10;

System.out.println("bb之后的值: "+bb);

}

}

输出如下:

aa初始值: 1

aa之后的值: 10

a之后的值: 1

-----------------我是分割线--------------------

bb初始值: 1

bb之后的值: 10

b之后的值: 1

1

2

3

4

5

6

7aa初始值:1

aa之后的值:10

a之后的值:1

-----------------我是分割线--------------------

bb初始值:1

bb之后的值:10

b之后的值:1

很明显,这两个方法都没能改变原始的a、b变量。上面这段代码执行时的内存单元变化大致是这样的:

9a4b19b2bbfa1e14b9f2135af79269b5.gif

从这个图可以看出,执行change方法时,aa变量先是在另一个内存单元中复制了一份a变量的东西,接着在方法中一直改变的都是aa变量,因此很明显a变量从始至终都没有发生改变;执行change2方法时,同样bb变量也是在另一个内存单元中复制了一份b变量的东西,接着方法中改变了bb变量具体指向的值(即:堆内存中的 1 改变成了 10),很明显这个过程跟b变量也没有关系,因此在change2方法执行完毕之后b变量也没有发生改变

注:我画图时为了简便并没有严格区分栈内存和堆内存,大家明白这个意思就行

上图中的内存地址是我随便虚构的,一段代码执行时的实际内存地址并不一定是这样

那么,我在最上面提到的Holder技术是怎么实现在一个方法中改变对象的值的呢?

(2)Holder技术的实现分析:

上面我已经说过了,javax.xml.ws.Holder这个类实际上就是一个简单的泛型——对一个Java类的简单封装。那么,如果我们手动对Integer类进行封装,其具体的代码基本上就是这样的:

Java

package cn.zifangsky.holder;

public class IntegerHolder {

public Integer value;

public IntegerHolder() {

}

public IntegerHolder(Integer value) {

this.value = value;

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13packagecn.zifangsky.holder;

publicclassIntegerHolder{

publicIntegervalue;

publicIntegerHolder(){

}

publicIntegerHolder(Integervalue){

this.value=value;

}

}

然后,我们再写一个简单的测试实例:

Java

package cn.zifangsky.holder;

public class Demo2 {

public static void changeInteger(IntegerHolder ii){

ii.value = new Integer(10);

}

public static void main(String[] args) {

IntegerHolder i = new IntegerHolder(1);

System.out.println("初始值: " + i.value);

changeInteger(i);

System.out.println("改变之后的值: " + i.value);

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18packagecn.zifangsky.holder;

publicclassDemo2{

publicstaticvoidchangeInteger(IntegerHolderii){

ii.value=newInteger(10);

}

publicstaticvoidmain(String[]args){

IntegerHolderi=newIntegerHolder(1);

System.out.println("初始值: "+i.value);

changeInteger(i);

System.out.println("改变之后的值: "+i.value);

}

}

注:如果上面的两段代码使用javax.xml.ws.Holder这个类的话,可以等效于下面这段代码:

Java

package cn.zifangsky.holder;

import javax.xml.ws.Holder;

public class Demo2 {

public static void changeInteger(Holder ii) {

ii.value = new Integer(10);

}

public static void main(String[] args) {

Holder i = new Holder<>(1);

System.out.println("初始值: " + i.value);

changeInteger(i);

System.out.println("改变之后的值: " + i.value);

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21packagecn.zifangsky.holder;

importjavax.xml.ws.Holder;

publicclassDemo2{

publicstaticvoidchangeInteger(Holderii){

ii.value=newInteger(10);

}

publicstaticvoidmain(String[]args){

Holderi=newHolder<>(1);

System.out.println("初始值: "+i.value);

changeInteger(i);

System.out.println("改变之后的值: "+i.value);

}

}

最后输出如下:

初始值: 1

改变之后的值: 10

1

2初始值:1

改变之后的值:10

发现没有,没有使用return返回值但是却让值发生了改变。如果还是用内存单元图来表示的话,那么大致上是这样的:

9a4b19b2bbfa1e14b9f2135af79269b5.gif

从上面的图可以看出,虽然在changeInteger方法中,变量 ii 的地址跟变量 i 的地址不一样,但是它们存储的内容(PS:ii.value这个Integer对象的地址)却从始至终都没有发生改变。相反,在changeInteger方法中改变的是ii.value这个Integer对象真正的值,ii.value的内存地址并没有发生改变,仍然和 i.value的内存地址一样。因此在这个方法结束之后 i.value对应的值也相应发生了改变。这就是Holder技术的实现原理

最后,如果弄明白了上面测试代码的原理的话,可以思考下下面这段代码最后的输出是什么?

Java

package cn.zifangsky.holder;

import javax.xml.ws.Holder;

public class Demo1 {

public static void main(String[] args) {

int a = 1;

Integer b = new Integer(11);

System.out.println("初始状态--> " + "a: " + a + " b: " + b);

add(a, b);

System.out.println("方法一 --> " + "a: " + a + " b: " + b);

add2(a, b);

System.out.println("方法二 --> " + "a: " + a + " b: " + b);

Holder holder = new Holder(b);

add3(a, holder);

System.out.println("方法三 --> " + "a: " + a + " b: " + holder.value);

}

public static void add(int aa, Integer bb) {

aa = 2;

bb = 22;

}

public static void add2(int aa, Integer bb) {

aa = new Integer(2);

bb = new Integer(22);

}

public static void add3(int aa, Holder bb) {

aa = 2;

bb.value = new Integer(22);

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39packagecn.zifangsky.holder;

importjavax.xml.ws.Holder;

publicclassDemo1{

publicstaticvoidmain(String[]args){

inta=1;

Integerb=newInteger(11);

System.out.println("初始状态--> "+"a: "+a+"    b: "+b);

add(a,b);

System.out.println("方法一    --> "+"a: "+a+"    b: "+b);

add2(a,b);

System.out.println("方法二    --> "+"a: "+a+"    b: "+b);

Holderholder=newHolder(b);

add3(a,holder);

System.out.println("方法三    --> "+"a: "+a+"    b: "+holder.value);

}

publicstaticvoidadd(intaa,Integerbb){

aa=2;

bb=22;

}

publicstaticvoidadd2(intaa,Integerbb){

aa=newInteger(2);

bb=newInteger(22);

}

publicstaticvoidadd3(intaa,Holderbb){

aa=2;

bb.value=newInteger(22);

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值