Java 如何调用操作系统命令

在日常开发中,我们会遇到这样的问题,我想通过Java程序调用一下oracle数据库的导入或者导出命令,或者调用linux的某个shell执行某个针对操作系统的操作,Java提供了一个方式让我们和操作系统交互:Runtime.getRuntime().exec(cmdmml);这里的cmdmml便是操作系统的命令,需要注意的是不同操作系统的规则,比如windows,执行操作系统的命令时,需要在命令前加”cmd /c “来告诉java执行的是一个dos命令。而linux下则不需要。

通过exec方法执行完命令后,大部分情况下,我们需要获取命令的执行结果,比如导出的话,我们需要知道现在正在导出哪个表?哪些表已经导出了。

Runtime.getRuntime().exec(cmdmml)方法会返回一个Process。

process.getInputStream()就是正常的返回结果,而process.getErrorStream()则是异常的返回结果。我们可以通过BufferedReader包装输出流读取里面的内容。需要注意的是,针对InputStream和ErrorStream的处理需要在新线程中处理。不要阻塞主线程。

Java常见基本常识

Java常见小常识有哪些呢?笔者整理了一下,具体如下。

1)  一个.java文件中必须有一个class,类名和文件名相同。而且只有这个class是Public,其他类不能是Public.

2)  数组不能强制转换,如果强制转换的话,需要每个数组内对象一个一个转换。

3)  如果父类有构造函数,子类是必须要显式调用到父类的构造函数的,如果父类只有默认的构造函数,则子类不用显式调用父类的构造函数。

4)  新线程运行要使用Thread的Start方法,而不是run方法。如果调用run方法则不会起新线程了。

5)  用多个字符串拼接一个字符串,不建议用”abc” + “udm”这样的格式,也就是用”+”号关联,建议使用StringBuffer的append方法。效率更高。

具体原因有如:

String是不可变对象,也就是final对象,一旦创建,就不能改变它的值,而StringBuffer是可变对象。可以动态改变它的值,这样每次拼接String都需要重新分配内存空间。

6)  各种集合中,HashTable是线程安全的,而HashMap不是,Vector是,而ArrayList不是。

7)  各种集合中,Set不允许有重复对象,而List允许。HashMap可以有key是null。

8)  Java的IO类中,有两大类,一大类用于处理字节流,根接口是InputStream,OutputStream,有一类是处理字符流,根接口是Reader,Writer

9)  Java常用的框架称为SSH,即Structs,Spring,Hibernate,分别对应展示层,业务框架和持久框架。

10)              Java中保留的基本类型byte,short,int,long,double,float,char,boolean

Eclipse使用技巧集锦

Eclipse使用技巧集锦

Eclipse是应用非常广泛的Java开发工具,几乎垄断了Java开发工具市场。它如此广泛,我们都应用它,但是却可能没有注意到它的一些功能,一些非常棒的设计,所以笔者认为有必要介绍下Eclipse比较特色的功能,总结出一个比较全的使用技巧。如安装插件,代码热替换,任务标记等。

技巧一,添加插件包,Eclipse是个框架,它有很强的扩展性,可以扩展我们Java开发中的常见工具,比如单元测试工具JUnit,版本管理工具SubVersion,以及代码统计,代码质量检查等等工具。那么这些附加工具如何集成到Eclipse里面呢?

安装插件有以下几种方法:

一,在线更新

help - install software,选择后界面如下,网上有很多在线更新的URL,直接拷贝到Work with输入框,然后点击”Add”按钮,这时候会弹出最上面的对话框,随便取个名字,然后点击OK即可,回到上一个界面,点击Finish即完成自动安装。
 onlineupdate

二,直接拷贝插件到Eclipse安装目录下

插件会有两个文件夹,分别为features,plugins,把下载的插件拷贝到eclipse相应的plugins,features下面。重启eclipse就行。

三,通过link文件链接插件到Eclipse.

直接解压后文件包放在电脑的任意位置,在%eclipse_home/links目录下里写一个XXX.link文件,这个link文件指向插件包,link文件的名字可以用插件名字定义,如svn插件叫svn.link,文件里面内容需要安装这样的格式:path=E:\\Program Files\\Genuitec\\Common\\svn1.0.0(这个就是对应的插件包名)

技巧二,远程调试和代码热替换,有个问题很困扰Java的开发人员,我们集成测试的时候,发现一个Bug,修改完这个Bug,要想让修改完的代码生效,需要先打包,然后拷到版本环境中,重启服务器。这个过程很耗时,一些大一点的系统,重启一个服务器往往就需要几分钟的时间,而我们修改一个Bug,往往就是几行代码,甚至一两个变量,但是为了验证这一个小小的改动,却要花费大量的时间来做“体力活”,有什么版本让代码修改完即生效呢?问题的解决版本是远程调试和代码热替换。首先,我们要保证系统中运行的Jar包是我们从Eclipse导出的,如下图:

exportjar

 

只有这样才能保证两边的代码是一致的,可以热替换,有人会说,我用maven编译的难道不一样吗?不一样的,两者的编译参数可能不同,稍微有点不同,Eclipse就认为是不同步,没法热替换。

第二步,通过远程调试链接远程服务器,路径如下图:

remotedebugmenu

而实际的调试界面如下图:

remotedebug

 

技巧三,如何标记任务以便以后完成,Java开发时,往往遇到一个问题,暂时没有好的思路,想放一放,过两天再解决,而过两天后呢?想改这块代码,但是却找不到了,想改改不了了,怎么办呢?解决问题的思路是,使用标记语言标记,标记语言有FIXME,TODO,XXX,其中FIXME标记的含义是,这块代码处理的不完善,需要完善我,TODO的含义是这边有个功能没实现,需要实现;XXX的含义是功能实现了,但是方案值得商榷,可以再优化下。

这个只是初步整理下,后续还会逐步完善。

java字符集问题总结

从事Java开发近10年了,发现有个问题一直如影随形,那就是字符集问题。解析获取过来的中文数据乱码,解析xml文件中的中文乱码等等。真是烦不胜烦。

自己到网上也搜集了一些资料,解决过一些问题,今天将这些已经获取的知识整理一下。

一,   字符集有哪些呢?字符集有很多,分类的话可以从是否支持中文来分,gbk,gb2312,utf-8,utf-16,big5等是支持中文的,但是ascii,iso-8859-1等是不支持中文的。也就是用iso-8859-1编码格式来解析字符串,中文是肯定乱码的。而其他,只要编码和解析用的字符集相同,就没有问题。

二,   现有的系统中的字符集是什么样的?

相信很少有人关心,一个操作系统安装好以后,它支持哪些字符集。实际上中文的操作系统肯定安装了支持中文的字符集的,而纯英文的操作系统也支持中文字符集,因为英文的操作系统一般都支持unicode的编码格式,按照第一条我们知道,unicode字符集是可以用来存储中文的。当然,是要转成unicode编码

三,   Java提供了哪些方法来转换字符集?

Java提供方法包括:

1)      字符串 转换成 字节流,String 的getBytes()和getBytes(String encoding),前者按照操作系统的默认字符集转换,后者按照指定的字符集转换。

2)      字节流转成成字符串 String 的构造函数提供了方法,如new String(byte[] bytes)或者new String(byte[] bytes, String endcoding),前者按照操作系统默认的字符集转成String,后者按照指定的字符集。

3)      IO类中,分为两种,字节方式传输的InputStream,OutputStream和按照字符方式传输的Reader和Writer。这两类IO类,字节方式传输如果都不读取实际可以不用关心。Reader和Writer实际需要关心,否则读取或者写入的是乱码,就不对了。这里面涉及的方法包括:InputStreamReader(Reader reader, String encoding)等不一一列举。

四,   读取XML文件乱码是为什么?

程序中读取XML文件乱码,根本原因在于读取时设定的编码格式不支持中文,需要制定支持中文的编码格式。当然,如果输入流是InputSource,而流中的信息是字符形式的,需要保证传进来的字符流就是正确编码的,因为SAX解析器对字符流是不进行编码转换的。

java线程池技术的历史

总体而言,java线程池技术经历了两个阶段,第一阶段是我们需要自己通过自己写代码来实现线程池技术的,网上有不少现成的资料。总体来说,设计一个线程池包括以下几个类:

1)总体执行类

ThreadPool ,用来创建线程池,这个类还包括一些属性,如下:

a)任务列表

private static List<Task> taskQueue = Collections
.synchronizedList(new LinkedList<Task>());

b)线程池中的所有线程

public PoolWorker[] workers;

提供的方法中,包括构造函数,默认构造默认个数的worker并作为守护线程执行。

Execute方法,执行一个Task。

closePool方法,关闭pool

 

2)执行线程,即PoolWorker,每个PoolWorker都继承自Thread,即没新起一个worker就是一个新线程。Worker的run方法里面,主要工作就是从taskQueue中获取Task,并执行Task的run()方法。

3) 任务Task,一般都是一个实现Runnable接口的实现类。该类的run()方法就是具体要做的事情,你可以用来实现自己的业务逻辑。

以上的实现总体来说有一个缺陷,就是相对来说还是比较死的,线程数一般都是固定的,没有伸缩性。不能安装系统的吞吐量要求来增加或者减少线程。

因为线程池技术如此有用,而自身实现总有这样那样的功能缺陷,从5.0开始,jdk自身提供了线程池技术。比较完美的解决了以上问题,总体来说,主要包括以下几个类:

ExecutorService 线程池接口

ThreadPoolExecutor 线程池接口的默认实现类等,类图如下:

classdiagram

核心类ThreadPoolExecutor:常用构造方法为:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
corePoolSize: 线程池维护线程的最少数量
maximumPoolSize:线程池维护线程的最大数量
keepAliveTime: 线程池维护线程所允许的空闲时间
unit: 线程池维护线程所允许的空闲时间的单位
workQueue: 线程池所使用的缓冲队列
handler: 线程池对拒绝任务的处理策略

一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是 Runnable类型对象的run()方法。

当一个任务通过execute(Runnable)方法欲添加到线程池时:

如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。

也就是:处理任务的优先级为:
核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。

当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

unit可选的参数为java.util.concurrent.TimeUnit中的几个静态属性:
NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。

workQueue我常用的是:java.util.concurrent.ArrayBlockingQueue

handler有四个选择:
ThreadPoolExecutor.AbortPolicy()
抛出java.util.concurrent.RejectedExecutionException异常
ThreadPoolExecutor.CallerRunsPolicy()
重试添加当前的任务,他会自动重复调用execute()方法
ThreadPoolExecutor.DiscardOldestPolicy()
抛弃旧的任务
ThreadPoolExecutor.DiscardPolicy()
抛弃当前的任务

 

类Executors提供了一系列的工厂方法创建一些常用的线程池,

newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

newSingleThreadExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。