初学Java的几个tips

最近为公司开发了一个 Oracle 卸载数据程序(将Oracle的数据unload成定长文本格式, 然后通过FTP传给Teradata, Teradata etl automation将定长文本加载到数据仓库中). 部署的平台是RHEL 4.5 64bit, 本打算使用Python开发, 但考虑到系统的可维护性, Python直接被否了, 看来像Python这样的语言, 在非IT公司还是没什么市场的. 最后, 选用Java作为开发语言.

Java原先不熟, 幸好有google, 碰到的问题都解决了. 下面记载一些开发过程中记录下来的tip.



/******
Java和C#的语法比较
***/
参考 http://www.javacamp.org/javavscsharp/
比较下来, 还是C#的语法更简洁. 我们使用的是JDK 1.6, 不爽的地方有:
1. 没有Linq

2. java没有delegate. 如果要将某个函数helloWorld()传给consumer, 有3种做法:
  a)使用反射, 我一般不用这种做法
  b)将要传递的方法封装在一个专门的接口或类中, 然后再将这个接口或类的传给consumer.
  c)将要传递的方方法封装在一个匿名类中. 我比较喜欢使用这个做法.
  针对(b)和(c),  如果我们的专门包装的类实现了Java已有的interface的话, 在consumer端就清楚了应该怎样调用我们的方法, 因为方法名已经确定了
  比如下面是一个匿名类的例子, 可以看到这个匿名类实现了IListener.
    this.addListener(new IListener(){
        public void listen() {...}
    });
3. java类没有property概念, 类的成员变量往往需要向外部暴露一对public getter/setter函数. C#/Delphi因为有property概念, 仅仅需要向外部暴露一个public的 property就行了.
4. Java没有多行字符串, C#字符串如以@开头, 就是多行字符串, Java多行字符串, 只能使用StringBuilder.append().append()..., 或者String的连续加来实现. 通常我们的SQL语句都很复杂, 有很多行, 如果按照这样的写法, 代码就变得很难看了.
5. 我最不能忍受的是, 多个Java的public类不能放在同一个文件中, 如果你将package分的很细的话, 就不得不多一点定义public类, 项目如果再稍微复杂点, 就会导致java文件超级多.
6. Java 不支持操作符重载, 只有String的+号做了特别重载的处理.
    若在两个reference type对象之间使用==和!=来比较, Java总是判断它们是否是同一个reference;
    对于两个boxing类型做>=,>,<=,<等比较操作, Java其实会自动unbox, 然后比较对应的primitive value.  详见 http://stackoverflow.com/questions/1514910/when-comparing-two-integers-in-java-does-auto-unboxing-occur

/******
java Coding Standard
***/
http://gee.cs.oswego.edu/dl/html/javaCodingStd.html#secDoc

/******
Java的简单教程
***/
http://www.javacamp.org/javavscsharp/

http://www.javapractices.com/
http://www.rgagnon.com/howto.html
http://www3.ntu.edu.sg/home/ehchua/programming/index.html#Java
http://mindprod.com/jgloss/jgloss.html
 

/******
线程安全
***/
http://www.iteye.com/topic/806990 中文
http://www.iteye.com/topic/808550 中文
http://www.artima.com/designtechniques/threadsafety.html


All threads inside a Java virtual machine (JVM) share the same heap and method area

Given the architecture of the JVM, you need only be concerned with instance and class variables when you worry about thread safety. Because all threads share the same heap, and the heap is where all instance variables are stored, multiple threads can attempt to use the same object's instance variables concurrently. Likewise, because all threads share the same method area, and the method area is where all class variables are stored, multiple threads can attempt to use the same class variables concurrently. When you do choose to make a class thread-safe, your goal is to guarantee the integrity -- in a multithreaded environment -- of instance and class variables declared in that class.
 


 /******
如何让Java命令行程序返回exit code
***/

Java的main函数返回类型是void, 不像C#, 返回类型可以是void或int型. 不过也可以给命令行程序带上一个exit code, 方法是使用System.exit(0);或者是System.exit(1);



 
/******
Java的开放数据参数
***/
Java的写法: public Book retrieve(String... entityPk)
C# 的写法: public Book retrieve(params string[] entityPk)



/******
分隔符
***/
public static final String FILE_SEPERATOR = System.getProperties().getProperty("file.separator");

public static final String LINE_SEPERATOR = System.getProperties().getProperty("line.separator");

/*
file.separator      文件分隔符(Unix   下为'/')     
line.separator      换行符(Unix   下为'\n')
还有更多的系统属性, 可以访问 http://www.sadtojoy.com/article/2011/2/16/3863.htm  
*/



/******
Java的properties文件, C#的config文件
***/
使用Vistual Studio设置setting时, 将自动生成一个config文件(XML), 来保存设置值, 并自动生成一个Settings类来mapping属性和属性值. 用起来很方便的.

Java程序常使用Properties文件来保存设定值, (类似于.ini的文件), 保存和获取property都需要自己写代码. Apache Commons项目有一个子项目Configuration, 支持XML/Ini/Properties等很多格式, 推荐使用.

关于properties文件的使用, 可以学习下面的博客
主题:DAO设计模式(希望大家指正)
http://www.iteye.com/topic/427289

 
/*******
Java List 排序,
*/
可以使用Collections.sort() 方法, 对List对象进行排序, 我们可以采用默认的comparator, 也可以自定义一个comparator传给sort()函数.
http://www.ehow.com/how_2094980_sort-linked-list-java.html
http://www.blogjava.net/zygcs/archive/2008/01/17/176032.html

/*******
Key-Value集合
*/
1. Map是Java的一个Key-Value集合接口, HashMap<K,V>是Map的一个具体实现类
2. 如何循环Map这样的Key-Value集合
for(Map.Entry<String, Double> nameValuePair : nameValueMap.entrySet())
{
    //nameValuePair.getValue()
}

/******
对象的XML序列化/反序列化
***/
使用XStream,  http://xstream.codehaus.org



/******
Java字符串替换
***/
两种实现方法:
1.使用Java的String.replaceAll(str1,str2), 要求str1为正则表达式

2. 使用org.apache.commons.lang3的StringUtils.replace(String text, String searchString, String replacement), 这里的searchString为普通字符串, (即非正则字符串)
 


/******
对于Map和HashMap在做foreach循环时, 容器应该是myMap.entrySet(), 而不是myMap本身.
***/
for(Map.Entry<String, Double> myPair : myMap.entrySet())
{
        //...
 }
 
   
/******
enum和String之间转换比较容易
enum==>String, //直接使用ToString()
String==>enum, //可以使用Enum类, Enum.valueOf(StepResult.class, "Successful")

enum和int的转换比较麻烦.
enum==>Int, 注意这里不是指求ordinal值, 而是求enum对应的数值, 使用StepResult.getIntValue()
int=>enum, 这个最麻烦了, 需要在enum代码中额外写一个static工具方法, 来完成这个转换.
***/
   
public enum StepResult {
    Successful(10), Failed(20);

    private int value;

    StepResult(int arg1) {
        value = arg1;
    }

    public int getIntValue() {
        return value;
    }
   
    //这个static函数, 专门负责将 int 值转成 enum
    public static StepResult fromIntValue(int code)
    {
        StepResult result;
        switch (code)
        {
        case 10:
            result=Successful;
            break;
        case 20:
            result=Failed;
            break;
        default:
            result=Successful;
            break;
        }
        return result;
    }
}   
   
   
   
/******
使用oracle tns方式来创建Jdbc Connection, 我google了很久, 包括sun/oracle官网, 都没有解决, 直到看了http://www.rgagnon.com/javadetails/java-0112.html, 才最终得以解决.
***/

    public static Connection getDbConnection(String tnsName, String user, String pwd, String tnsnames_ora_Folder) throws Exception
    {
        String connString = "jdbc:oracle:thin:@"+tnsName;
        OracleDataSource ods = new OracleDataSource();
        //ods.setNetworkProtocol("tcp");
        ods.setURL(connString);
        ods.setUser(user);
        ods.setPassword(pwd);
       
        //it is required set oracle.net.tns_admin property with the folder of nsnames.ora file, otherwise, it will throw one SqlException to indicate "Unknown host specified "
        //tnsnames_ora_Folder="C:\\oracle\\ora92\\network\\ADMIN", this is must, or one exception will be raised
        System.setProperty("oracle.net.tns_admin", tnsnames_ora_Folder);
        Connection conn = ods.getConnection();

        return conn;
    }
   
   
    //use jdbc:oracle:thin to get oracle jdbc connection
    public static Connection getDbConnection(String oracleHost,String oraclePort String serviceName, String user, String pwd) throws Exception
    {
        Connection conn = null;
       
        String dBDriver = "oracle.jdbc.driver.OracleDriver";
        //"jdbc:oracle:thin:@10.224.141.35:1521:ORCA";
        //private static final String DEDICATED_CONNECTION_STR = "jdbc:oracle:thin:@(description=(address=(host=#IP)(protocol=tcp)(port=#PORT))(connect_data=(service_name=#SID)(SERVER = DEDICATED)))";
        //private static final String SHARED_CONNECTION_STR = "jdbc:oracle:thin:@#IP:#PORT:#SID";
        String connStrFmt = "jdbc:oracle:thin:@%s:%s:%s";
        String ConnStr=String.Format(connStrFmt,oracleHost,oraclePort,serviceName);

        Class.forName(dBDriver);
        conn = DriverManager.getConnection(sConnStr,user,pwd);
       
        return conn;
    }

   
   
/******
使用JDBC执行DML的一个示例,DML执行成功, 返回true, 否则返回false.
注意外层为try--catch, 内层为try--catch--finally, 因为java的checked exception特性, 这应该是最优美的写法了.
***/
private static boolean ExecuteSql(String sql)
    {
        boolean result=true;
        try
        {
            Connection conn = null;
            try
            {
                //to get Oracle connection
                conn = DalHelper.getDbConnection();
                SqlHelper.execute(conn, sql); //to run DML
                conn.commit();
            }
            catch (Exception e)
            {
                result = false;
                conn.rollback();
                log.error("fatal error when query:" + sql, e);
            }
            finally
            {
                if (conn != null) conn.close();
            }
        }
        catch (Exception e)
        {
            log.error("fatal error " , e);
        }
        return result;
    }   

   
   
   
/******
log4j的使用, 功能够强大
***/
log4j确实功能强大, 不仅可以设置不同的log级别(debug/info/warning), 而且可以输出到多个appender, 甚至可以为不同的class设置不同的appender

以下为log4j的级别, 级别从小到大排列:
    TRACE,
    DEBUG,
    INFO,
    WARN,
    ERROR,
    FATAL
   
log4j输出多个自定义日志文件
http://blog.csdn.net/janestone/article/details/1862678

下面的文章有能详细的使用说明.
Short introduction to log4j
http://logging.apache.org/log4j/1.2/manual.html



   
/******
Apache-DBUtils的使用
***/
原本打算借此机会学习一下mybatis ORM框架, 最后还是放弃了, 因为发现Apache DBUtils框架更容易上手. 也对, 干嘛非要使用ORM框架呢?  Apache DBUtils 算是JDBC的一个封装, 封装的恰到好处.
  Apache-DBUtils
http://blog.sina.com.cn/s/blog_3eb047df0100ov2u.html
http://blog.sina.com.cn/s/blog_3eb047df0100ou9z.html
  Apache DBUtils使用总结
http://lavasoft.blog.51cto.com/62575/270661
  log-cd的apache commons DbUtils博文
http://log-cd.iteye.com/blog/431087
  Apache DBUtils 入门
http://hi.baidu.com/augustus_blog/blog/item/bf13f47893589ee02f73b36c.html
  dbutils开源项目用法
http://xwangly.iteye.com/blog/930350

我喜欢用BeanListHandler将Select的结果映射成一个POJO的List中. 要求POJO 的属性名和字段输出名必须一致, 大小写可以不同, 除非我们dbutils缺省的BasicRowProcessor

下面文章中, 提及commons dbutils有如下问题:
    http://www.blogjava.net/nighty/archive/2011/02/12/344155.html
    http://www.blogjava.net/nighty/archive/2011/04/26/349050.html
问题1: commons dbutils的 BeanProcessor是不支持关联查询的,只能局限于单表的转换.
  经我测试, 一切正常. 可能是该博客作者使用的jdbc driver有问题吧.
问题2: 如果我们的sql是, select user_type, count(*) as count from users group by user_type, 然后用dbutils将结果自动转换Map及MapList时,
将不能识别能识别聚合函数的列名.
  经我测试, 确实存在这个问题, 可以参见博客作者的解决方案

/*******
guava 类库 (原名为google collections)
*/
C#对于常用的集合类, 增加了不少extension method, 来扩展sort/find, Java本身没有这些东西, 但guava实现了很多类似的东西
http://code.google.com/p/guava-libraries/
http://www.tfnico.com/presentations/google-guava#TOC-Resources


  

posted @ 2011-08-19 16:01  harrychinese  阅读(522)  评论(1编辑  收藏  举报