在工作中,会遇到要在CMD中执行命令和命令行的情况。比如要调出Windows的计算器……(确实有这样的需求)。

那么怎样调用才能易操作,易扩展并没有副作用呢?
推荐使用ProcessBuilder。示例代码如下:

try {
    ProcessBuilder processBuilder = 
        new ProcessBuilder("calc.exe");            
    processBuilder.start();
} catch (IOException e) {
    // TODO: handle exception
    e.printStackTrace();
}

首先实例化一个ProcessBuilder,参数就是要执行的命令,然后调用ProcessBuilder.start()方法即可。

注意,这里要执行的命令是要加入到系统Path下的,否则无法调用。
如果你的命令没有加入到系统Path下,那么可以给ProcessBuilder指定工作目录,将其目录指定为包含你要执行命令的目录即可:

processBuilder.directory(new File("E://mydir/"));

如果你要执行的命令只有通过系统CMD才能执行,比如echo,那么怎么办呢?

ProcessBuilder processBuilder = new ProcessBuilder
    ("cmd.exe","/c","echo","Hello, world!");

第一个参数是要执行的命令,后面的参数都是该命令的参数。通过在命令行执行命令即cmd.exe /c即可。但是需要注意的事,第一个参数必须为执行的命令,后面每个参数都是该命令的参数。

比如

ProcessBuilder processBuilder = new ProcessBuilder
    ("cmd.exe","/c","ping www.chengjf.com");

或者

ProcessBuilder processBuilder = new ProcessBuilder
    ("ping", "www.chengjf.com");

都是正确的。但是如果

ProcessBuilder processBuilder = new ProcessBuilder
("ping www.chengjf.com");

那么就是错误的,会抛出IOException。

如果你要获得执行的结果,咋整?比如,你执行了一个ping命令,想拿到执行结果。

try {
    ProcessBuilder processBuilder = 
        new ProcessBuilder("ping","www.chengjf.com");            
    Process process = processBuilder.start();
    InputStream inputstream = process.getInputStream();
    // Windows中文操作系统此处要使用gbk转码,否则输出会乱码
    BufferedReader bufferedReader = 
        new BufferedReader(new InputStreamReader(inputstream,"gbk"));

    String rs = null;
    while((rs = bufferedReader.readLine()) != null){
        System.out.println(rs);
    }
} catch (IOException e) {
    // TODO: handle exception
    e.printStackTrace();
}

通过ProcessBuilder.start()会生成一个Process,通过调用Process.getInputStream()方法可以获得结果的输入流。这里推荐使用BufferedReader,可以实时获取到命令行的输出,而不是等到命令执行完毕再一起输出。

Java中Map这种数据结构非常常用,也非常实用。最常用的方法就是下面几个:

V get(Object key);
V put(K key, V value);
V remove(Object key);
boolean containsKey(Object key);

不过,对于Map的遍历操作还是很常用的。下面就介绍几种Map的遍历操作方法。

通过key的遍历

首先拿到Map的key的集合Set,然后使用Set的迭代器Iterator,遍历每个key,通过Map的get()方法获取其值。

Set<Object> set = map.keySet();
Iterator<Object> iterator = set.iterator();
while (iterator.hasNext()) {
    Object key = iterator.next();
    System.out.println(key + " " + map.get(key));
}

上述代码的前两行可以合成一行:

Iterator<String> iterator = map.keySet().iterator();
while (iterator.hasNext()) {
    Object key = iterator.next();
    System.out.println(key + " " + map.get(key));
}

上面这个版本就是我原来最常用的遍历方式。这个版本的进化版本就是使用foreach:

for (Object key : map.keySet()) {
     System.err.println(key + " " + map.get(key));
}
这类方法的主要思想就是拿到key,然后使用get()方法拿到值。

通过Entry

Entry是Map的内部接口:

interface Entry<K,V>{
    K getKey();
    V getValue();
    V setValue(V value);
    boolean equals(Object o);
    int hashCode();
}

可用通过遍历Map的Entry,调用Entry的getKey()方法和getValue()方法拿到键和值。

Iterator iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
    Map.Entry entry = (Map.Entry) iterator.next();
    System.err.println(entry.getKey() + " " + entry.getValue());
}

更加简单的foreach版本如下:

for (Map.Entry<Object, Object> entry : map.entrySet()) {
    System.err.println(entry.getKey() + " " + entry.getValue());
}
这类方法的主要思想就是拿到Map的Entry,Entry就是键值对的组合。从效率上来说,通过Entry效率要稍高。http://stackoverflow.com/questions/3870064/performance-considerations-for-keyset-and-entryset-of-map

参考JavaCodeGeeks上个这篇文章 http://www.javacodegeeks.com/2011/12/cloning-of-serializable-and-non.html 作者Craig Flichel

通常开发者都会依赖于第三方的类库来进行开发,这样避免重复制造轮子。特别是在Java的世界中,有着像Apache和Spring这样流行的开源项目。但在实际使用过程,我们总是无法控制这些类的行为,或者是只能控制一点点,叫人不爽。

这有时会带来问题。举个例子,比如说你要深度克隆一个没有提供合适克隆方法的对象,你准备咋整?

通过序列化来克隆

最简单的方法,如果对象实现了Serializable接口,那么直接利用这个就好。Apache Commons库中提供了一个方法去做这个,但为了完整,自己实现的话,代码可能就像下面这样:

public static Serializable cloneThroughSerialize(Serializable ser) throws Exception {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    serializeToOutputStream(ser, bos);
    byte[] bytes = bos.toByteArray();
    ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(
            bytes));
    return (Serializable) ois.readObject();
}

private static void serializeToOutputStream(Serializable ser,
        OutputStream os) throws IOException {
    ObjectOutputStream oos = null;
    try {
        oos = new ObjectOutputStream(os);
        oos.writeObject(ser);
        oos.flush();
    } finally {
        oos.close();
    }
}

// using our custom method
Serializable cloned = cloneThroughSerialize(someObject);

// or with Apache Commons
Object cloned = org.apache.commons.lang.SerializationUtils.clone(someObject);

- 阅读剩余部分 -

参考JavaCodeGeeks上个这篇文章 http://www.javacodegeeks.com/2014/02/serializing-java-objects-with-non-serializable-attributes.html 作者Dustin Marx

有很多原因,使得我们想使用自定义的序列化方法代替Java默认的序列化方法。最普遍的一个原因就是为了提升性能。其他的原因大都是默认的序列化机制不支持。其中一个非常普遍的场景就是,要序列化一个拥有非序列化属性的对象。

下面的代码展示了一个小例子:

SerializationDemonstrator.java
package com.chengjf;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SerializationDemonstrator {
    public static <T> void serialize(final T objectToSerialize,
            final String fileName) {
        if (fileName == null) {
            throw new IllegalArgumentException(
                    "Name of file to which to serialize object to cannot be null.");
        }
        if (objectToSerialize == null) {
            throw new IllegalArgumentException(
                    "Object to be serialized cannot be null.");
        }
        FileOutputStream fos = null;
        ObjectOutputStream oos = null;
        try {
            fos = new FileOutputStream(fileName);
            oos = new ObjectOutputStream(fos);
            oos.writeObject(objectToSerialize);
            System.out.println("Serialization of object " + objectToSerialize
                    + " completed!");
            oos.close();
            fos.close();
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
    }

    public static <T> T deserialize(final String fileToSerialize,
            final Class<T> classBeingDeserialized) {
        if (fileToSerialize == null) {
            throw new IllegalArgumentException(
                    "Cannot deserialize from a null filename.");
        }
        if (classBeingDeserialized == null) {
            throw new IllegalArgumentException(
                    "Type of class to be deserialized cannot be null.");
        }
        T objectOut = null;
        FileInputStream fis = null;
        ObjectInputStream ois = null;
        try {
            fis = new FileInputStream(fileToSerialize);
            ois = new ObjectInputStream(fis);
            objectOut = (T) ois.readObject();
            System.out.println("Deserialization of object " + objectOut
                    + " is completed!");
            ois.close();
            fis.close();
        } catch (IOException ioException) {
            ioException.printStackTrace();
        } catch (ClassNotFoundException classNotFoundException) {
            classNotFoundException.printStackTrace();
        }
        return objectOut;
    }
}

- 阅读剩余部分 -

Preferences API与对象序列化相比,前者与对象持久性更密切,因为它可以自动存储和读取信息。不过,它只能用于小的、受限的数据集合——我们只能存储基本类型和字符串,并且每个字符串的存储长度不能超过8K(不是很小,但我们也并不想用它来创建任何重要的东西)。顾名思义,Preferences API用于存储和读取用户的偏好(preferences)以及程序配置项的设置。

Preferences是一个键-值集合(类似映射),存储在一个节点层次结构中。尽管节点层次结构可用来创建更为复杂的结构,但通常是创建以你的类名命名的单一节点,然后将信息存储于其中。

    // : io/PreferencesDemo.java
    import java.util.prefs.*;

    public class PreferencesDemo {
    public static void main(String[] args) throws Exception {
        Preferences prefs = Preferences.userNodeForPackage(PreferencesDemo.class);
        prefs.put("Location", "Xi'an");
        prefs.put("Name", "chengjf");
        prefs.putInt("Age", 24);
        prefs.putBoolean("Is he a coder?");
        int usageCount = prefs.getInt("UsageCount", 0);
        usageCoun++;
        prefs.putInt("UsageCount", usageCount);
        for(String key : prefs.getKeys()){
            System.out.println(key + " : " + prefs.get(key, null));
        }
        // You must always provide a default value:
        System.out.println("How old is he? " + prefs.getInt("Age", 0);
    }
    }

    // Output: (Sample)
    ......
    //
最好将“user”用于个别用户的偏好,将“system”用于通用的安装配置。
尽管我们不一定非要把当前的类作为节点标示符,但这仍不失为一种很有用的方法。

一旦我们创建了节点,就可以用它来加载或者读取数据了。在这个例子中,向节点载入了各种不同类型的数据项,然后获取其keys()。它们是以String[]的形式返回的,如果你习惯于keys()属于集合类库,那么这个返回结果可能并不是你所期望的。注意get()的第二个参数,如果某个关键字下没有任何条目,那么这个参数就是所产生的默认值。当在一个关键字集合内迭代时,我们总要确信条目是存在的,因此用null作为默认值是安全的,但是通常我们会获得一个具名的关键字,就像下面这条语句:

    prefs.getInt("Companions", 0);

在通常情况下,我们希望提供一个合理的默认值。实际上,典型的习惯用法可见于下面几行:

    int usageCount = prefs.getInt("UsageCount", 0);
    usageCount++;
    prefs.putInt("UsageCount", usageCount);

这样,在我们第一次运行程序时,UsageCount的值是0,但在随后引用中,它将会是非零值。

在我们运行PreferencesDemo.java时,会发现每次运行程序时,UsageCount的值都会增加1,但是数据存储到哪里了呢?在程序第一次运行之后,并没有出现任何本地文件。Preferences API利用合适的系统资源完成了这个任务,并且这些资源会随着操作系统不同而不同。例如在Windows里,就是用注册表(因为它已经有“键值对”这样的节点对层次结构了)。但是最重要的一点是,它已经神奇般地为我们存储了信息,所以我们不必担心不同的操作系统是怎么运作的。