文章列表
 
您正在查看 "Java" 分类下的文章

2009-05-25 21:11

该博客以及搬迁至http://www.daniel-journey.com/ 该文的新地址为http://www.daniel-journey.com/archives/72

BeanUtils的copyProperties方法用来orig中的成员变量的值复制给dest,即将已经存在的dest变为orig的副本
    public void copyProperties(Object dest, Object orig) {
   .......
   }

      BeanUtils的populate方法用来将Map<Key,value>中的以值(String或String[])转换到目标bean对应的属性中,Map中的Key是目标bean的属性名。
   public static void populate(Object bean, Map properties){
        ......
    }

copyProperties同样支持了populate中的功能(apache的javadoc中,明确指明这个方法是为解析http请求参数特别定义和使用的,在正常的使用中不推荐使用.他们推荐使用BeanUtils.copyProperties方法)。

BeanUtils.copyProperties和populate的实现方法是将源bean(也可以是Map)中的每个element在转换器(Converter)的帮助下,将转换的结果设置到目标bean对应的属性中。例如在HTTP应用中需要从http request中抽取数据,http request传递过来的都是String 或是String数组类型的变量而目标类型可能是各种各样的,例如http request会有一个name=visitDate,value='2009-05-13'的参数,而目标bean 的visitDate属性的类型是java.util.Date。
       BeanUtils的copyProperties和populate需要在转换器(converter)的配合下实现源和目标对象之间的数据类型的转换。在BeanUtils.copyProperties的javadoc中说明的(Copy property values from the origin bean to the destination bean for all cases where the property names are the same—— 只要属性名相同就可以从源bean中拷贝值到目标bean中)这句话提供到功能就是要通过转换器才能实现的。在BeanUtils的copyProperties和populate的使用过程中Converter是一个非常重要的概念,它提供了强大的扩展能力。  
    /****************************************************************/
        public interface Converter {
           public Object convert(Class type, Object value);
        }
     /****************************************************************/
    
    convert方法的参数type是目标转换的类型,参数value是被转换的值,返回值就是转换以后的结果。当有需要自定义或扩展的Converter 的时候可以通过注册自定义的转换器来实现,例如Beanutil自带的DateConverter不支持String到java.util.Date的转换,通过扩展DateConverter就可以实现支持。需要特别注意的是Converter 是注册在classloader一级的,也就是说在一个class loader中同一时间只能有一个转换器起作用(BeanUtils的copyProperties和populate会依据目标bean属性的类型来决定启用那个转换器),我之前的项目中就有因为其他模块中在特定的时候会重新注册了某个类型的转换器,而新注册的转换器又没有支持我所需要的转换,从而导致在我的模块中出现NPE。 所以在使用BeanUtils.populate和copyProperties的时候要注意以下几点:

1. 只在系统初始化的时候注册一个转换器,而不要在某个功能的执行过程中注册转换器。
       2. 转换器要能够支持项目各个模块的使用需求
       3. 谨慎使用或者思考一下
BeanUtils.populate和copyProperties是你想要的方法吗?

在我使用BeanUtils.populate的模块中本意是实现bean和Map的属性拷贝并不需要类型和值的转换。至于说为什么会使用BeanUtils.populate完全是被它支持Map所误导,其实PropertyUtilsBean.copyProperties和 BeanUtils.copyProperties同样支持Map。就我想要实现的功能而言通过PropertyUtilsBean.copyProperties方法能更好地满足。PropertyUtilsBean.copyProperties方法不会有类型转换的逻辑,所以需要程序员自己保证目标和源Bean属性间的兼容性,也正因为如此PropertyUtilsBean.copyProperties的执行效率更高。

参考资料

BeanUtils.copyProperties与PropertyUtils.copyProperties用法及区别l

http://commons.apache.org/beanutils/v1.8.0/apidocs/org/apache/commons/beanutils/PropertyUtilsBean.html

http://commons.apache.org/beanutils/v1.8.0/apidocs/org/apache/commons/beanutils/BeanUtils.html

初用apache.commons.beanutils.BeanUtils

 
2009-05-01 18:26
我们的代码中像下面这样用法并不少见
/****************************************************************************/
private List cheesesInStock = ...;
/** * @return an array containing all of the cheeses in the shop,
    * or null if no cheeses are available for purchase.
*/
public Cheese[] getCheeses() {
     if (cheesesInStock.size() == 0)
      return null;
      ...
}
/***************************************************************************/

在上面的代码中返回的数组长度为0的时候就返回null。这势必要求调用方的代码中有额外的代码来处理null返回值。
/****************************************************************************/
Cheese[] cheeses = shop.getCheeses();
if (cheeses != null &&
   Arrays.asList(shop.getCheeses()).contains(Cheese.STILTON)) {
   System.out.println("Jolly good, just the thing.");
}

/****************************************************************************/
而实际经验告诉我们开发人员往往会遗漏这种
cheeses != null 的条件判断,所以才有了Effective Java第二版——第43条中的规则(在Effective Java第一版中是第27条规则)——返回零长度的数组或集合,而不是null。
   有些同学可能觉得对返回一个特定类型(Cheese,Foo等等)的数组或者集合会很麻烦,其实一点都不麻烦。
如果是返回值是数组,正确的做法是
/****************************************************************************/
private List<Cheese> cheesesInStock = new ArrayList<Cheese>();
    private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];

    /**
    * @return an array containing all of the cheeses in the shop, or null if no
    *         cheeses are available for purchase.
    */
    public Cheese[] getCheeses() {
        return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY);
    }
/****************************************************************************/

如果结果是集合的话,就更简单了
/****************************************************************************/
public List<Cheese> getCheeseList() {
        if (cheesesInStock == null || cheesesInStock.size() == 0) {
            return Collections.emptyList();
        } else {
            return new ArrayList<Cheese>(cheesesInStock);
        }
    }

/****************************************************************************/
Collections中提供返回多种集合类型的方法(emptyList,emptyMap....),有兴趣的同学可以参考我的另一篇文章java.util.Collections使用说明
总结
Effective Java第二版——第43条:返回零长度的数组或集合,而不是null的价值就在于返回零长度的数组或集合可以避免由于调用方遗漏了对返回结果的null检查而导致NullPointException。

参考资料
java.util.Collections使用说明
Effective Java第二版
 
2009-04-25 17:26

为什么要使用Variable Arguments(Varargs)可变长方法参数.

   可变参数能够让一个方法能够使用同一类型的多个参数而不用在编译阶段就确定参数的数目。实际的效果可以通过比较一下下面的代码。为了支持方法参数在运行时刻不确定的情况,对于不支持Varargs的方法,调用方往往需要通过构造一个Collection或者Array,并将可变的参数都加入到Collection或者Array然后再传递给方法。而对于支持Varargs的方法调用者就少了这样的麻烦。这两段完成的完成的功能和最后结果可能并没有多少区别,但从代码的可读性、可维护性方面来比较,支持Varargs的方法明显少了那些拖沓和冗余。另外,如果concatMapStringWithoutVarargs是一个被使用几率很高的方法,支持Varargs的方法在易用性而带来的价值体现就就会更加明显。

/*******************不使用Varargs***********************************/
   List<String> contentList=new ArrayList<String>();
   contentList.add("value1");
   contentList.add("value2");
   concatMapStringWithoutVarargs(prefix,suffix,contentList);

   /*******************************************************/
  
  
   /*******************使用Varargs***********************************/
   concatMapStringWithVarargs(prefix,suffix,"value1","valu2");
   /*******************************************************/


使用方法
变量类型... 变量名
Varargs参数的定义很简单,以下就是一个使用了可变参数的构造函数
public Guitar(String builder, String model, String... features);
String... features 标志着这个构造函数可以接受可变数目的features变量,下面的两个构造函数使用都是OK的。
Guitar guitar = new Guitar("Martin",
                                          "HD-28V",
                                          "Hot-rodded by Dan Lashbrook",
                                           "Fossil Ivory Nut",
                                           "Fossil Ivory Saddle",
                                            "Low-profile bridge pins");
Guitar guitar = new Guitar("Bourgeois",
                                          "OMC",
                                          "Incredible flamed maple bindings on this one.");

使用者甚至可以不向features传递任何值
Guitar guitar = new Guitar("Bourgeois",
                                          "OMC");
对于Varargs参数的使用如果了解了它的本质,大家对它的使用就会感觉很轻松。事实上编译器会把Varargs参数转换成一个相应类型的数组。例如
编译器会将public Guitar(String builder, String model, String... features); 解释成
public Guitar(String builder, String model, String[] features)
无论是对features进行for循环或是foreach循环都是OK的
for(int i=0;i<features.length;i++){
    String feature=features[i];
    ......
}

for (String feature : features) {
    ......
}

另外也可以对Varargs参数进行相应的转换
private List features;
this.features = java.util.Arrays.asList(features);
由于可变长的参数支持0到N的参数,也就是说方法支持传入参数数目为0的情况,所以开发人员需要考虑到参数个数为0的边际条件。
public static int max(int... values) {
     if (values.length == 0) {
        throw new IllegalArgumentException("No values supplied.");
      }
      int max = Integer.MIN_VALUE;
      for (int i : values) {
        if (i > max)
          max = i;
      }
      return max;
}

使用限制
在使用Varargs参数的过程中有两点要特别注意的
1.一个方法只能定义一个Varargs参数,以下的定义方法在编译的时候就通不过。
public Guitar(String builder, String model,
    String... features, float... stringHeights)
2. 将可变长度参数只能出现在方法定义的最后面。下面的这个方法定义编译器会提示"The variable argument type String of the method concatMapStringWithVarargs must be the last parameter
public static void concatMapStringWithVarargs(String prefix, String ...features,String suffix)

总结
   其实可变参数不是一个新鲜的事物,在C语言中的printf函数就已经有这样很好的实现了,java在1.5的版本中才提供了类似的功能不能不说已经有点晚,不过迟来总比不来的好。

参考资料
Java 1.5 Tiger: A Developer's Notebook 第五章
2008.7.5 java的可变参数列表

 
2009-03-23 20:57
java的java.util.Collections类是一个非常有用的工具类。

空集合
Collections.EMPTY_LIST,Collections.emptyList()——返回只读的空LIST 集合
Collections.EMPTY_MAP,Collections.emptyMap()——返回只读的空MAP集合
Collections.EMPTY_SET,Collections.emptySet()返回只读的空SET集合
所谓的空集合指的是没有元素在这些集合中,特别需要主要的是返回的集合都是只读的。以下代码会抛出UnsupportedOperationException
异常。
-------------------------------------------------------
public static void main(String[] args) ...{
         List children
=Collections.EMPTY_LIST;
         children.add(
new HashMap());
}


-------------------------------------------------------
Collections.emptyList(),Collections.emptySet(),Collections.emptyMap()由于支持泛型使用起来会更方便,例如
--------------------------------------------------------------
List<String> s = Collections.emptyList();
---------------------------------------------------------------

单元素集合
Collections中的单元素集合指的是集合只有一个元素而且集合只读。
Collections.singletonList——用来生成只读的单一元素的List
Collections.singletonMap——用来生成只读的单Key和Value组成的Map
Collections.singleton——用来生成只读的单一元素的Set


只读集合
Collections提供了生成几种生成只读集合的方法unmodifiableCollection,unmodifiableList,unmodifiableMap,unmodifiableSet,
unmodifiableSortedMap,unmodifiableSortedSet。这些集合一旦初始化以后就不能修改,任何修改这些集合的方法都会抛出
UnsupportedOperationException异常。


Checked集合(Checked Collections)
Checked集合具有检查插入集合元素类型的特性,例如当我们设定checkedList中元素的类型是String的时候,如果插入起来类型的元素就会抛出
ClassCastExceptions异常,Java5中提供泛型的功能,泛型功能能够在代码编译阶段就约束集合中元素的类型,但有些时候声明的集合可能是raw集合,
编译阶段的类型约束就不起作用了,这个时候Checked集合就能起到约束集合中元素类型的作用。
Collections中提供了以下生成Checked集合的方法
checkedCollection,checkedList,checkedMap,checkedSet,checkedSortedMap,checkedSortedSet


同步集合(Synchronized Collections)
Collections的synchronizedXxxxx系列方法顾名思义会返回同步化集合类(SynchronizedMap,
SynchronizedList等等)。这些集合类内部实现都是通过一个mutex(互斥体)来实现对这些集合操作的同步化。



Enumeration接口
从JDK1.0开始Java就提供了Enumeration接口。Collections中list和enumeration和Enumeration接口相关。
list(Enumeration<T> e) 方法用于有Enumeration接口中产生一个List
———————————————————————————————————————
public void demonstrateEnumerationToList()
{
log("===== Demonstrate Collections.list(Enumeration) =====", System.out);
final Enumeration properties = System.getProperties().propertyNames();
final List propertiesList = Collections.list(properties);
log(propertiesList.toString(), System.out);
}
————————————————————————————————————————
enumeration(Collection<T> c) 方法用于基于Collection返回Enumeration。
——————————————————————————————————————
public void demonstrateCollectionToEnumeration()
{
log("===== Demonstrate Collections.enumeration(Collection) =====", System.out);
final Enumeration books = Collections.enumeration(this.favoriteBooks);
while (books.hasMoreElements())
{
log(books.nextElement().toString(), System.out);
}
————————————————————————————————————————————
}



查找替换
fill——使用指定元素替换指定列表中的所有元素。
frequency——返回指定 collection 中等于指定对象的元素数。
indexOfSubList—— 返回指定源列表中第一次出现指定目标列表的起始位置,如果没有出现这样的列表,则返回 -1。
lastIndexOfSubList——返回指定源列表中最后一次出现指定目标列表的起始位置,如果没有出现这样的列表,则返回-1。
max—— 根据元素的自然顺序,返回给定 collection 的最大元素。
min——根据元素的自然顺序 返回给定 collection 的最小元素。
replaceAll——使用另一个值替换列表中出现的所有某一指定值。

集合排序
Collections还提供了集中对集合进行排序的方法。
reverse——对List中的元素倒序排列
shuffle——对List中的元素随即排列,这个方法让我想到了Apple的iPod Shuffle
sort——对List中的元素排序
swap——交换List中某两个指定下标位元素在集合中的位置。
rotate——循环移动。循环移动这个方法让人比较难以理解,下面的例子就会让你一下子就理解这个方法的含义。
——————————————————————————————————————————————————————————————————————
假设 list 包含 [t, a, n, k, s]。在调用 Collections.rotate(list, 1)(或 Collections.rotate(list, -4))之后,list 将包含 [s, t, a, n, k]
——————————————————————————————————————————————————————————————————————

其他方法
binarySearch——使用二进制搜索算法来搜索指定列表,以获得指定对象。
addAll——将所有指定元素添加到指定 collection 中。
copy——将所有元素从一个列表复制到另一个列表。
disjoint——如果两个指定 collection 中没有相同的元素,则返回 true。
nCopies——返回由指定对象的 n 个副本组成的不可变列表。

参考资料
http://java.sun.com/javase/6/docs/api/java/util/Collections.html
http://gceclub.sun.com.cn/Java_Docs/html/zh_CN/api/java/util/Collections.html
java.util.Collections类包的学习
The Java Collections Class
 
2009-03-22 15:23
   很多公司都有自己的代码规范,不过这些规范多数都只能停留在文字上,很难真正在实际开发的时候加以贯彻和实施,往往只有在code review才能发现之类的问题。不过我相信代码规范之类的问题最好能够在编码阶段就发现和纠正,而在code review真正要review是是业务逻辑的实现和设计。 可目前一方面目前的Java IDE只提供了有限的代码规范的检验功能, 每个公司有自己不同的规范和实际情况需要灵活的加以定义。 CheckStyle可以用来检查java程序源代码编码风格的是否符合公司的要求,另外Checkstyle可以跟Eclipse整合起来使用,方便程序员检查自己的代码风格。以下是在Eclipse中安装、使用Checkstyle的几个主要步骤。

1.       Eclipse安装CheckStyle插件

   CheckStyle Eclipse 插件的安装download url http://eclipse-cs.sourceforge.net/update

   

CheckStyle安装完毕以后,重启Eclipse,在Eclipsewindows->Preferences窗口会出现CheckStyle的定义就表示CheckStyle安装成功!

   

2.       启用CheckStyle

选中你想要启用CheckStyleJava project,点击右键。


   选中CheckStyle,启用“Checkstyle active for this project”,确定以后。CheckStyle会对该项目的代码风格进行检查了。

  


黄色的区域就是checkstyle检查出有问题的地方(红色表示错误,黄色表示警告)。

3.       启用特定的规范

checkstyl默认使用的是sun建议的代码规范。这种规范有以下几点不太适合马上运用到我们的工作中(虽然是很好的规范):

代码格式要过很高,有没有空格,注释有没有句号都会被要求,导致warning过多。

默认都是warning没有error级别的验证,提示效果不好。我们是希望多某些特别严重的错误,例如javadoc的缺失视同java code的错误一样对待——即eclipse project compile error

借助Checkstyle提供了自定义格式的功能,只需要提供专门制作适合自己的checkstyle配置文件就OK了!

点击Eclipse->windows->preferences->CheckStyle->New,打开如下的Check Configuration Properties对话框类型选择External Configuration File,并导入自定义的CheckStyle格式文件,确定以后配置文件安装成功。

3.还是回到需要启动的项目的Propeties中,为项目启用刚才我们importcheckStyle,确定,CheckStyle就开始作用了

默认情况下CheckStyle会马上起作用,改成有写Warning的地方就显示红色(表示有error)。


同样这些checkstyle发现的问题也会被eclipse当成Problem来处理。

还需要进一步做的事。
代码规范是需要在整个开发团队中得到认可和实施的就需要大家要共同制定出一份被大家认可的编码规范,并根据这个规范来生成与之相匹配的checkstyle文件。Checkstyle除了跟eclipse集成起来用用之外,还可以独立的运行——例如作为Ant的一个task,或是Maven的Plugin这样project leam就可以很方便的检查team member的代码是否符合项目所要求的代码规范了。



相关链接
为什么我们需要统一的编码风格

 
2009-03-18 17:14
   文中的内容参考至IBATIS in action 的第九章Improving performance with caching。
入门样例
   首先是一个非常简单的Caching 实例
<cacheModel id="categoryCache" type="MEMORY">
   <flushOnExecute statement="insert"/>
   <flushOnExecute statement="update"/>
   <flushOnExecute statement="delete"/>
   <property name="reference-type" value="WEAK"/>
</cacheModel>
<select
   id="getCategory" parameterClass="Category"
   resultClass="Category" cacheModel="categoryCache">
   SELECT *
   FROM Category
   WHERE categoryId=#categoryId#
</select>
   这个例子中有两个主要部件:Cache Model 和 映射语句(select),Cache Model定义了如何保存数据到Cache以及清除过时数据的清理策略。而映射语句(select或Procedure)想要使用Cache的时候只要对定义好的CacheModel加以引用。在这个例子中Cache Model的类型是MEMORY,这是Ibatis内建的cache方案。当flushOnExecute 元素中定义的方法被执行的时候,cache就会被flush掉。如果多个select使用了同一个Cache Model,这样几个select的cache结果都会被一起清除掉。cacheModel 的property元素用来为各个不同类型的缓存定义其特定的属性及其属性的value。
Ibatis的缓存方案跟传统O/R Mapping的缓存方案还是有所区别的。传统的OR Mapping会根据Ojbect的唯一标识符来缓存,这样当多次不同的结果返回相同结果的时候,对象只会被cache一次,而ibatis是直接对完全的结果进行缓存,而不管是否已经有相同的对象在cache中存在了。

CacheModel元素说明
  
cacheModel元素包括了以下属性和子元素
Id(必选) 唯一的ID,让需要cache功能的select语句能够引用
type(必选) cache的类型,合法的值包括MEMORY,LRU,FIFO,OSCACHE。如果需要自定义cache的类型就需要实现CacheController接口,type值的value就是实现类的类名
readOnly(可选) 如果是true,表示这个cache是只读cache,从cache中获取对象的属性都不应该被修改。但设置成true并不能阻止对象属性被改变,如果对返回的结果做了改变,cache中的内容也会跟着改变。换句话说,readonly=true意味着应用程序要保证不会对cache的内容做任何改变。相反,readonly=false意味着应用程序会对返回结果加以修改,Ibais返回的缓存结果和ocache中的内容就不会使用同一个应用实例。该选项可选,缺省值是true
serialize(可选) 这个属性意味着是否要在获取对象之前做“深度拷贝”。serialize=true意味着向缓存请求得到的内容都是cache对象深度拷贝的结果,即有相同的值但不是相同的实例。该选项可选,缺省值是false。

readonly和serialize的组合会影响到应用程序的性能,
readonly serialize 结果
true false
false true
false false 警告
true true

缓存类型

   MEMORY类型的cache只是简单的将cache的数据放入到内存中,只到GC将其释放。Memory类型的cache只有一个属性reference-type,其值可以是WEAK、SOFTStrong三种类型的引用。使用WEAK、SOFT这两种引用,GC会根据内存的使用情况和缓存内容的访问状况来决定缓存内容的清除还是保留。而使用Strong引用缓存起来的内容会一直保留下来只到被Flush掉。下面是该类型缓存使用样例。
<cacheModel id="categoryCache" type="MEMORY">
   <flushInterval hours="24"/>
   <flushOnExecute statement="insert"/>
   <flushOnExecute statement="update"/>
   <flushOnExecute statement="delete"/>
   <property name="reference-type" value="WEAK"/>
</cacheModel>


   LRU类型的cache有一个最大缓存数,当保存的数量超过这个值的时候,Ibatis会依据“
least recently used”的算法将数据从缓存中清除。LRU类型的缓存惟一可以设置的属性就是这个最大缓存数。下面是该类型缓存使用样例。
<cacheModel id="categoryCache" type="LRU">
   <flushInterval hours="24"/>
   <flushOnExecute statement="insert"/>
   <flushOnExecute statement="update"/>
   <flushOnExecute statement="delete"/>
   <property name="size" value="200"/>
</cacheModel>

   FIFO类型cache跟LRU类型的缓存类似也有一个固定的SIZE,不同的是其使用使用的是“先进先出”的策略。该类型的缓存惟一可以设置的属性就是这个最大缓存数。下面是该类型缓存使用样例。
<cacheModel id="categoryCache" type="FIFO">
   <flushInterval hours="24"/>
   <flushOnExecute statement="insert"/>
   <flushOnExecute statement="update"/>
   <flushOnExecute statement="delete"/>
   <property name="size" value="1000"/>
</cacheModel>

   OSCACHE类型的cache值得就是使用OpenSymphony的oscache作为cache的实现方案。
   当然我们还可以通过实现CacheController接口来使用我们自己的缓存方案。
Cache的清除
Ibatis定义了两中tag——flushOnExecute和flushInterval来处理cache内容的清除。flushOnExecute的statement属性用来引用那些会引起缓存清除的语句。下面是一个flushOnExecute的使用案例
<sqlMap namespace="Category">

<cacheModel id="categoryCache" type="MEMORY">

<flushOnExecute statement="Category.insert"/>

</cacheModel>

<select
id="getCategory" parameterClass="Category"
resultClass="Category" cacheModel="categoryCache">
SELECT *
FROM Category
WHERE parentCategoryId=#categoryId#
</select>

<insert id="insert" parameterClass="Category" >
INSERT INTO Category
(title,description,sequence)
VALUES
(#title#,#description#,#sequence#)
</insert>

</sqlMap>

   需要注意的是如果statement对应的sqlmap使用了namespace,statement就要填写完整的语句名称,即便statement引 用的映射语句就在同个namespace中,如果statement引用的映射语句在其他的sqlmap配置文件中,那就要保证引用的语句
flushInterval则是用来定义连续的、定时的、重复的缓存清除。flushInterval有四个属性hours、minutes、second、millisecondss,例如以下这个例子
<cacheModel id="categoryCache" type="MEMORY">

<flushInterval hours= "12" />

</cacheModel>
总结
   Ibatis内建了对Cache的支持,目的就是通过cache的运用来提升性能和Scalability等等。但跟App自己使用Cache一样,Ibatis对cache的使用同样要考虑集群、其他应用直接修改数据库等问题。
 
2009-03-14 19:55
    很多Java WEB Applicaiton的开发都离不开Web Container。可接下来的一个问题就是各种Java IDE未必能够对各种版本的Web Container提供很好的调试支持。在这种情况下,Java的远程应用程序调试功能倒是能够解决一下我们的燃眉之急
    远程调试是通过JVM的JPDA规范来实现的。JPDA是JVM规范的,一下子要理解可还真不容易。文章后面有相关的链接。使用起来倒是挺简单的在启动Java application的命令中加上以下参数
-Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n。
以Eclipse为例,新建一个Remote Java Application, 输入远程应用运行的IP地址和端口,启动Debug就可以了。




参考资料
Java Platform Debugger Architecture

Java实用技巧:用JPDA轻松调试Java代码

深入 Java 调试体系: 第 1 部分,JPDA 体系概览

 
 
   
 
 
文章分类
 
   
 
文章存档
 
     
 
最新文章评论
  

如果我想学习了解单元测试的话,我想知道我学到什么水平、或者说了哪些内容后才可以
 

按照这种操作,创建分支,点击ok后,提示access to 'http://xxxx/svn' forbidden,这
 

今天刚了解了这个设计原则,摊开来讲的话,博大精深
 

能详细阐述一下就好了
 

thx
   
帮助中心 | 空间客服 | 投诉中心 | 空间协议
©2012 Baidu