[原创]Java基础知识记录(1)

前言:本文『很傻很天真』,任何稍有Java编程经验的都不用看了。

 

(1)Java文件编译得到class文件,jar包是若干class文件(以及其他一些文件)的集合,如果要在命令行运行jar包中的一个class文件(相当于一个Java程序),可以这样做:

java -cp /root/software/tmp/XXX.jar:/root/software/tmp/lib/* com.abc.calculate.MyClass --date 20101101

其中,-cp 表示指定classpath,后面需要以分号(:)分隔的方式将所有需要用到的依赖jar包添加进来,此处添加了 /root/software/tmp/XXX.jar 包以及 /root/software/tmp/lib/ 目录下的所有jar包。

com.abc.calculate.MyClass 是要运行的jar包 XXX.jar 中的一个class文件,这么长的名字是它在jar包中的路径全名。

--date是传给 MyClass 程序命令行参数。还可以带更多的参数,要看你在Java程序中是如何写的代码。

(2)import static的含义
类似于

import static com.abc.calculate.MyClass.*;

这样的语句,意思是导入类中的静态方法和静态变量,从而可以在导入的文件中,可以直接使用静态方法或静态变量,不需要再以 类名.静态方法名 类名.静态变量名 的方式来调用了,从而使得代码很简洁。

(3)java.lang.ClassNotFoundException: com.mysql.jdbc.Driver 错误

这是由于找不到连接MySQL数据库的JDBC驱动。这个驱动是一个jar包,例如 mysql-connector-java-5.1.17-bin.jar 。你需要在classpath中添加这个文件,或者在运行你的Java程序时,用java -cp 临时指定这个jar文件的路径:

java -cp /home/mydir/mysql-connector-java-5.1.17-bin.jar your_program_name

(4)IntelliJ IDEA中用快捷鍵生成main函数

IntelliJ IDEA这个Java开发的IDE中,要输出一个标准形式的main函数简直方便到不行,完全不用你输入任何内容:在要插入main函数的地方按Ctrl+J快捷键,然后按p键,就会看到一个包含“psvm”等选项的下拉菜单,菜单右边有每一项的解释,其中“psvm”对应的解释是“main() method declaration”,因此,我们直接回车,即选择了“psvm” 这一项,然后main函数就直接生成了:

public static void main(String[] args) {

}

便捷得让人心欢喜。

文章来源:http://www.codelast.com/

(5)由 javac XXX.java 命令编译得到 XXX.class 文件,但是在执行此Java程序的时候,是执行 java XXX 而不是 java XXX.class !注意不要傻傻地带上“.class”,否则一定会得到一大堆错误,例如“Exception in thread "main" java.lang.NoClassDefFoundError”之类。

(6)一个类实现了接口 public interface Iterable<T> 之后,该类的对象就可以成为 foreach 语句的目标。

(7)如果你执行程序时得到类似于以下错误:

Exception in thread "main" java.lang.NoClassDefFoundError: 

Caused by: java.lang.ClassNotFoundException: 

        at java.net.URLClassLoader$1.run(URLClassLoader.java:202)

        at java.security.AccessController.doPrivileged(Native Method)

        at java.net.URLClassLoader.findClass(URLClassLoader.java:190)

        at java.lang.ClassLoader.loadClass(ClassLoader.java:307)

        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)

        at java.lang.ClassLoader.loadClass(ClassLoader.java:248)

Could not find the main class: .  Program will exit.

那么,这些可能是根本就无关你程序问题的错误。你可以试着执行一下命令:

java ""

会发现也会输出以上错误(双引号中间什么也没有,即一个空字符串)。

这说明是你要执行的程序找不到这些包造成的。请检查你的classpath,确认其正确;另外还要检查你执行命令的当前用户,是不是正确的用户,因为不同的用户会配置不同的环境变量,有可能导致找不到需要的jar包,或者根本没有权限去读取某些jar包!

(8)IntelliJ中自动生成JUnit单元测试代码框架的插件:JunitGenerator 的使用方法

①假设你是在Windows下(Linux也类似),需要在环境变量中的Path里添加上junit.jar文件的路径,例如d:\ProgramFiles\IntelliJ\lib\junit.jar

由于IntelliJ已经自带了一个junit.jar,所以你直接设置成它带的那个文件的路径就可以了。

②下载IntelliJ的JunitGenerator插件,在这里下载。需要下载的是那个zip包:JUnitGeneratorV2.0.zip(如果版本升级了,也下载类似名字的压缩包即可)。

③将zip包里的junitgen.jar文件解压到 IntelliJ安装目录\lib\ 下,将zip包里的resources文件夹解压到C:\Documents and Settings\XXX\.IntelliJIdea10\config\plugins 目录下,其中,XXX是你的登录用户名。至于为什么要这样,你可以先试着不这样做,然后打开IntelliJ,等着它报找不到一堆文件的错误吧。从错误信息就知道怎么解决了。

④打开IntelliJ,在File→Settings→Plugins选项中,可以看到“Installed”列表,即已经安装的插件的列表,确保JunitGeneratorV2.0已经选中,然后重启IntelliJ即可。

⑤在需要生成单元测试代码的类中,按快捷键Alt+Insert,在弹出的菜单中选择“JUnit Test”,即可生成单元测试代码框架,搞定!

文章来源:http://www.codelast.com/

(9)Java中对简单数据类型(例如int,float),在作为函数参数使用时,是传值的,无法作为引用传递,所以如果你的一个函数原型是 public void func(int a, float b),则你传入的a、b值即使在函数func中被更改了,也无法反映到函数外部。如果你习惯了C++的传递引用的方式,那么你在Java中找不到一样的方式,这时,你可以把简单数据数据当作数组来传递,或者把它们封装成一个类来传递。

当作数组传递的例子:

public void func(int[] a, float[] b) {
    //......
}

这时,你可以在在func中更改a[0],a[1],a[2]……的值,在外部取出来的值就跟着变了。

(10)如何对一个map中的各元素,按照 value 的值从大到小排序?

假设你有一个TreeMap<Integer, Double>对象,里面有5个元素:

(3, 1.3),(4, 2.7),(16, 6.5),(1, 0.5),(9, 0.8)

我们知道,把一对(key, value)放进TreeMap中时,TreeMap会把它们按key自动排序的,例如在这个例子中,你把上面的5个(key, value)对放进了TreeMap之后,你再把它们用遍历entrySet的方式输出,你会发现key的输出顺序为1,3,4,9,16

现在我想把它们排序成:

(16, 6.5),(4, 2.7),(3, 1.3),(9, 0.8),(1, 0.5)

即:按value值从大到小排序,怎么做呢?

有人会说,把(key, value)对倒过来,按(value, key)的形式保存到另一个TreeMap中,然后再把得到的TreeMap逆序输出,就得到了正确的结果了。在本例中,这样做是可行的,但是,由于TreeMap的key是不能重复的,因此,如果我们在原始的TreeMap中有两个相同的value,则这样做就不行了,因为把(key, value)倒过来之后会出现两个相同的key',保存到新的TreeMap中之后就会丢失一组数据。

那么,还可以怎么办呢?可以借助ArrayList,用Collections.sort()方法来对ArrayList排序,从而实现最终的效果。

假设我们需要排序的TreeMap<Integer, Double>对象名为 tm,其中已经有上面所说的5组(key, value)对了,然后:

// 将TreeMap中的元素放到ArrayList中
ArrayList<Map.Entry<Integer, Double>> tmList = new ArrayList<Map.Entry<Integer, Double>>(tm.entrySet());

// 利用Collections.sort方法排序
Collections.sort(tmList, new Comparator<Map.Entry<Integer, Double>>() {

  @Override
  public int compare(Map.Entry<Integer, Double> o1, Map.Entry<Integer, Double> o2) {
    return o2.getValue().compareTo(o1.getValue());
  }
});

// 输出排序后的数据
for (Map.Entry<Integer, Double> entry : tmList) {
  System.out.println(String.format("Key : %d, Value : %f", entry.getKey(), entry.getValue()));
}

可见,在上面的代码中,你需要创建一个Comparator对象,并把它作为参数传递到Collections.sort()函数中,在这个Comparator对象中,你需要重载对两个对象进行大小比较的compare()方法。
如果你使用的是Java8或以上版本,那么还有更简洁的做法:

Map<Integer, Double> sortedMapReverseOrder = tm.entrySet().
    stream().
    sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).
    collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));

然后遍历 sortedMapReverseOrder 就可以输出按原map对象的value降序排列的值。但要注意,sortedMapReverseOrder 不再是一个TreeMap。
文章来源:http://www.codelast.com/
(12)批量向MySQL数据库INSERT数据时,可以用JDBC的batch insert来插入,这样效率上会提高一些。如果你要将所有待插入的数据用 VALUES (a, b, c), (d, e, f), (g, h, i) 这样的方式拼装成1条SQL语句,再插入数据库的话,不要忘了,SQL语句长度是有限制的,你无法拼接一个极长的语句并执行,而batch insert甚至可以将20万条记录一起批量插入(网上的某些文章说的),所以,在一次插入很多数据时,你也不得不用batch insert。
文章来源:http://www.codelast.com/
(13)如果要开发Android应用程序,就必须安装Android SDK,在Windows下安装Android SDK的时候可能会有一直怪异的现象:明明已经安装过了JDK的,但是Android SDK的安装程序装报告说找不到JDK,这个时候,你只要在安装向导中点一下后退按钮返回上一界面,再点击前进按钮回来,就发现不再提示找不到JDK了,雷人啊。另外,安装好之后启动SDK Manager时也有可能会提示“Java not found in your path”,真让人不解。要解决这个问题,你可以试试把JDK安装路径下的java.exe的路径添加到你Windows的环境变量中的“Path”中,例如你的java.exe是在 D:\Java\jdk1.6.0_26\bin 目录下,那么你就把这个路径添加到Path中就可以了。

(14)IntelliJ如果提示“@Override”语法错误(波浪线),你可以在IntelliJ的“FileProject Structure”中设置:在“Project Language Level”中选择“6.0 - @Override in interfaces”,并重新加载工程即可。

(15)Ant中的 ** 匹配任意数量的目录,例如,模式 **/*.java 将匹配当前目录结构下的所有 Java 文件。

(16)在Linux下装ant
这里去下载ant的tar.gz版本,解压出来得到 apache-ant-1.8.2 目录,放置到 /usr/local/ 目录下,然后做一个软链接:

ln -s /usr/local/apache-ant-1.8.2 /usr/local/ant

再将此链接添加到 /etc/profile 中:

ANT_HOME=/usr/local/ant
export PATH=$JAVA_HOME/bin:$ANT_HOME/bin:$PATH

如果不想重启当前session就使此设置生效的话,需要手工加载一下 /etc/profile :

source /etc/profile

现在,你应该可以在命令行运行ant命令了。安装成功。 文章来源:http://www.codelast.com/
(17)Java设计模式之Factory
请看这个链接

(18)抽象类(abstract class)和接口(interface)的区别
请看这个链接。引用其中的几段话:

abstract class可以有自己的数据成员,也可以有非abstract的成员方法,而interface只能够有静态的不能被修改的数据成员(也就是必须是static final 的,不过在interface中一般不定义数据成员),所有的成员方法都是abstract的。从某种意义上说,interface是一种特殊形式的abstract class。

abstract class 在 Java 语言中表示的是一种继承关系,一个类只能使用一次继承关系(因为Java不支持多继承 -- 转注)。但是,一个类却可以实现多个interface。也许,这是Java语言的设计者在考虑Java对于多重继承的支持方面的一种折中考虑吧。
  其次,在abstract class的定义中,我们可以赋予方法的默认行为。但是在interface的定义中,方法却不能拥有默认行为,为了绕过这个限制,必须使用委托,但是这会增加一些复杂性,有时会造成很大的麻烦。

abstract class在Java语言中体现了一种继承关系,要想使得 继承关系合理,父类和派生类之间必须存在"is-a"关系,即父类和派生类在概念本质上应该是相同的。对于interface来说则不然,并不要求interface的实现者和interface定义在概念本质上是一致的, 仅仅是实现了interface定义的契约而已。

1.abstract class 在 Java 语言中表示的是一种继承关系,一个类只能使用一次继承关系。但是,一个类却可以实现多个interface。
 
2.在abstract class 中可以有自己的数据成员,也可以有非abstarct的成员方法,而在interface中,只能够有静态的不能被修改的数据成员(也就是必须是static final的,不过在 interface中一般不定义数据成员),所有的成员方法都是abstract的。
 
3.abstract class和interface所反映出的设计理念不同。其实abstract class表示的是"is-a"关系,interface表示的是"like-a"关系。 
 
4.实现抽象类和接口的类必须实现其中的所有方法。抽象类中可以有非抽象方法。接口中则不能有实现方法。
 
5.接口中定义的变量默认是public static final 型,且必须给其初值,所以实现类中不能重新定义,也不能改变其值。
 
6.抽象类中的变量默认是 friendly 型,其值可以在子类中重新定义,也可以重新赋值。 
 
7.接口中的方法默认都是 public,abstract 类型的。

(19)在Java中执行Linux shell命令
类似于C中的system()函数:

NAME
       system - execute a shell command
SYNOPSIS
       #include <stdlib.h>
       int system(const char *command);
Java中的做法是:

Runtime runTime = Runtime.getRuntime();
String cmd = String.format("hadoop fs -put %s %s", fileLocal, fileOnHDFS);
runTime.exec(cmd);

这样就可以执行字符串 cmd 中的shell命令。

(20)时间库Joda Time,一个用来替换JDK的时间库的Java库
其主页:http://joda-time.sourceforge.net/
Joda Time之所以被创造出来,是为了从根本上改变Java中的日期、时间的处理方式。JDK中的Date和Calendar类被设计得非常糟糕,并且有很多bug,因此,人们才创造了Joda Time这个Java库,来彻底替代它们。

(21)如何得到一个时间戳对应的时间中的hour(小时)
假设有时间戳(毫秒数)1212680215000,它对应的时间是“2008-06-05 23:36:55”,如果我们要取出其中的小时数,即 23,该怎么做?
对,你可以用Date类,向它的构造函数传入这个时间戳,就可以得到一个对应这个时间的对象,然后可以用Date类的getHours()方法来获取小时。但是你会发现,JDK文档表示,该方法早已过时了,那么,如果我要用一个“不过时”的方法来实现,应该怎么写?
方法如下:

Calendar calendar = GregorianCalendar.getInstance();
calendar.setTimeInMillis(timestamp);
int hour = calendar.get(Calendar.HOUR_OF_DAY);

这种方法非常麻烦。你需要先构造一个Calendar类的对象,然后再设置其时间(这里的timestamp即为上面所说的时间戳),然后再用get方法获取小时。这里的Calendar.HOUR)OF_DAY是指定了获取24小时制的小时。
文章来源:http://www.codelast.com/
对刚接触Java的人来说,用Date类的getHours方法来获取小时是让人感觉非常直观的做法,但是它居然“过时”了,并且,如你所见,用上面的Calendar类来达到同样的效果,是多么地绕弯子啊,并且给人感觉非常脑残,所以,这就是Joda Time对JDK的日期、时间库批评有加的原因了。对Joda Time的详细介绍,请看其主页。在这里,也只能对你说,能用Joda Time的话,就尽量抛弃JDK的日期、时间库吧。

(22)JAVA中的静态代码块(转载)

一个类可以使用不包含在任何方法体中的静态代码块,当类被载入时,静态代码块被执行,且之被执行一次,静态块常用来执行类属性的初始化。例如:

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
	……
	static {  
		try {  
			java.sql.DriverManager.registerDriver(new Driver());  
		} catch (SQLException E) {  
			throw new RuntimeException("Can't register driver!");  
		}  
	}
	……
}

初始化类中属性是静态代码块的常用用途,但只能使用一次。

(23)org.apache.commons.io.FileUtilsdeleteQuietly()方法与JDKdelete()方法的区别
同样是删除 文件或目录 的方法,前者与后者的区别是:前者不要求被删除目录为空(里面可以有东西),删不掉时也不会抛出异常

(24)类(class)的访问权限(转载)

public:可以供所有的类访问。
默认(没有提供修饰符时,就是默认):默认可以称为friendly,但是,Java语言中是没有friendly这个修饰符的,这样称呼应该是来源于c++。默认的访问权限是包级访问权限。即,如果写了一个没有访问权限修饰符的类,那么就是默认的访问权限,同一个包下的类都可以访问到。
每个编译单元(类文件)都仅能有一个public class。
private和protected也可以作为class的修饰符,但只是当此class是一个内部类的时候可以,对最外层的class来说,它只能有public修饰符或没有修饰符。

(25)方法抛出NullPointerException异常不需要声明
NullPointerException是不需要在方法声明中写的。
文章来源:http://www.codelast.com/
(26)重写父类方法的@Override
一个类方法的上方有一行 @Override 标记的话,表明此方法是重写了父类的方法,如果没有,这个方法会被认为是本类自己的方法(而不是重写父类的),因此,如果你的方法名写错了(例如少写了一个字母),并且你是想重写父类的方法的话,加上@Override可以让编译器在编译的时候就检查出错误,例如:父类中的方法名为“setTitle”,本来你想写的方法名也是“setTitle”,结果你却误写成了“setTitla”,那么编译的时候就报错了,因为该方法上方有一个@Override,表明它是重写父类方法的。

(27)运行一个使用log4j的Java程序时,如何在命令行指定使用特定的log4j配置文件
如果你的Java程序使用了log4j来记录日志,那么,将log4j的配置文件打进jar包中会导致无法方便地修改log4j配置(要重新打包比较麻烦),所以,你可以在调用此Java程序的脚本中使用如下方法来使用指定的log4j配置文件:

DIR=/example/code
java -Dlog4j.configuration=file://$DIR/config/my_log4j.properties \
	com.codelast.LogTest

上面的代码使得:运行 com.codelast.LogTest 程序时,使用 /example/code/config/my_log4j.properties 作为log4j的配置文件。

(28)打印出java.util.Date对象的日期
假设你有一个Date对象,它是由一个字符串“20120228”解析来的,现在要以“2012-02-28”的形式打印出其日期,则:

String str = "20120228";
date = DateUtils.parseDate(str, new String[]{"yyyyMMdd"});
String res = new SimpleDateFormat("yyyy-MM-dd").format(date);

就可以做到。
再举个例子,打印出昨天的日期:

String yesterday = new SimpleDateFormat("yyyy-MM-dd").format(DateUtils.addDays(new Date(), -1));

(29)Spring中的若干标签的含义
constructor-arg:构造函数的参数。
例如:

<bean id="abc" class="com.codelast.MyClass" >
  <constructor-arg value="33" index="0" />
  <constructor-arg value="google" index="1" />
</bean>

这段XML说明:类MyClass的构造函数应该包含两个参数,在生成MyClass对象的时候,第1个参数(index="0")的值被赋为33,第2个参数(index="1")的值被赋为“google”。
其构造函数类似于:

public MyClass(int userId, String companyName) {
    //TODO:
}

文章来源:http://www.codelast.com/
property:属性,对应到类中的setter方法。
例如:

<bean id="abc" class="com.codelast.MyClass" >
  <property name="companyName" value="google" />
</bean>

这段XML说明:类MyClass应该有一个叫做 setCompanyName 的方法(对应于“companyName”这个属性),类似于:

public void setCompanyName(String companyName) {
  //TODO:
}

构造MyClass对象的时候,就会自动调用 setCompanyName 方法,把“google”这个值传进去。

构造函数,set方法,init-method的执行顺序
按照这篇文章的说法,以上三者的执行顺序为:构造函数→set方法→init-method。因此,在init-method中是可以取到配置文件通过set方法设置的参数值的。

(30)不可修改的Collection
假设你有一个Collection:

Collection<String> strCol = new ArrayList<String>;

如果要在一个方法中,返回这个Collection,但是又不想返回值被外部修改,那么就要返回一个不可修改的Collection:

Collections.unmodifiableCollection(strCol);

注意是“Collections”,别看错了!

(31)IntelliJ缓存文件的清理
IntelliJ这个强大的JAVA IDE非常好用,如果你的系统盘空间紧张,会发现它的缓存文件越来越大的确是个严重的问题,所以我们需要手动清理。如果你是在Windows下使用IntelliJ,那么找到系统盘的这个目录:
C:\Users\登录用户名\.IntelliJIdea10\system
不同的IntelliJ版本对应的目录名是不同的,例如,IntelliJ 11.1对应的目录不是“...\.IntelliJIdea10\...”,而是“...\.IdeaIC11\...”(同理,IntelliJ 12对应的是.IdeaIC12)。你可以把这个system目录删掉(其占用空间可能非常大),再次打开IntelliJ时它会重建。

(32)多线程实现之Runnable接口和Thread类
实现多线程可以 extends Thread类,也可以implements Runnable接口。由于JAVA的单继承特性,第一种方法使得你的类不能再继承第二个类,这在某些情况下是不太好的(可能你一定需要继承自某个类,但是为了实现多线程,就必须要放弃继承它);第二种方法使得你不必影响原有的类层次关系,就可以为你的类添加上多线程功能,并且接口是可以实现多个的,在某些情况下扩展性更佳。

(33)周期性执行某一job的一种方法
如果要周期性执行某一个任务,那么我们首先想到的方法就是:创建一个线程,在里面用while循环配合sleep方法来实现周期性执行。且不论这种方法的好坏,我们有另一种更简单的方法:使用 ScheduledExecutorService 接口。

private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);

文章来源:http://www.codelast.com/
设置周期性执行的任务:

executorService.scheduleAtFixedRate(myRunnableClassObject, 0, 2, TimeUnit.MINUTES);

其中,第一个参数是一个实现了Runnable接口的类的对象(此类必须实现run方法),第二、三个参数分别表示任务开始的时间和周期,第四个参数为时间单位。
这样,你就不用自己在一个线程中用while循环来控制周期性任务了,非常方便。

(34)如何生成XML字符串/Java生成XML
如果要把你指定的内容生成一段XML字符串,可以用JDOM这个类库,你需要在你的项目中添加 jdom-2.0.2.jar 这个jar包(名字可能不同,请去官方网站下载最新的jar包)。
示例代码如下:

String content = "";
Element element = new Element("ele");
Document doc = new Document(element);

element.addContent(new Element("date").setText("2012-07-11"));
element.addContent(new Element("id").setText("78jskfjk4"));

ByteArrayOutputStream byteRep = new ByteArrayOutputStream();

Format format = Format.getPrettyFormat();
format.setEncoding("UTF-8");
format.setIndent("     ");
XMLOutputter xmlOutputter = new XMLOutputter(format);
try {
  xmlOutputter.output(doc, byteRep);
} catch (IOException e) {
}
content += byteRep.toString();

System.out.printf(content);

另外,上面的代码需要import的JDOM相关类如下:

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;

最后输出的XML字符串为:

<?xml version="1.0" encoding="UTF-8"?>
<ele>
     <date>2012-07-11</date>
     <id>78jskfjk4</id>
</ele>

(35)@Override is not allowed when implementing interface method”错误的解决办法
如果你使用的Java IDE是IntelliJ,并且遇到了这个错误提示,那么可能是项目属性设置不对导致的,试试:
在菜单“File”→“Project Structure”→“Project Language Level”里选择“6.0 - @Override in interfaces”,然后重新打开工程。

(36)用String类的split()方法来分割字符串时,如果分隔符是“|”,应该怎样写
假设有一个字符串s的内容为“a|b|c”(不含引号),那么如果你使用 s.split("|") 来分割字符串,你会发现结果是错误的,那是因为,按split()的定义:

public String[] split(String regex)
    根据给定正则表达式的匹配拆分此字符串。 
而在正则表达式中,竖线("|")的作用是:
左右两边表达式之间 "或" 关系,匹配左边或者右边。
因此,为了使用竖线作为分隔符,需要使用"\"将其转义,而在Java字符串的双引号中,要两个"\\"才能表示一个"\",因此,最后我们正确的用法应该是:

s.split("\\|");

这样就能得到正确的字符串分割结果了。

(37)如何获取当前时间戳(毫秒数)

long timestamp = System.currentTimeMillis();

用currentTimeMillis()这个静态方法来取当前时间戳很方便。

(38)客户端向Thrift service发送数据时,如何指定超时时间
超时时间是在 TSocket 对象的构造函数中设置的:

int timeout = 2;
socket = new TSocket(hostAddress, thriftServicePort, timeout);
TProtocol protocol = new TCompactProtocol(socket);

而Thrift client对象又需要通过 protocol 来构造,所以,当你调用了client对象的方法来向Thrift service发送数据时,就会以这个超时时间来执行了。

(39)HashMap按key来排序
直接看示例代码:

Map<String, String> m = new HashMap<String, String>();
m.put("c", "12");
m.put("0", "34");
m.put("a", "09");
m.put("e", "10");
m.put("b", "11");
Object[] keys = m.keySet().toArray();
Arrays.sort(keys);
for (Object key : keys) {
  System.out.println(String.format("Key: [%s], value: [%s]", key, m.get(key.toString())));
}

sort()方法的说明如下:

public static void sort(byte[] a)
对指定的 byte 型数组按数字升序进行排序。该排序算法是一个经过调优的快速排序法,改编自 Jon L. Bentley 和 M. Douglas McIlroy 合著的 Engineering a Sort Function", Software-Practice and Experience Vol. 23(11) P. 1249-1265 (November 1993)。此算法在许多数据集上提供 n*log(n) 性能,这导致其他快速排序会降低二次型性能。 
 
参数:
a - 要排序的数组

以上代码的输出结果为:

Key: [0], value: [34]
Key: [a], value: [09]
Key: [b], value: [11]
Key: [c], value: [12]
Key: [e], value: [10]

可见确实按key的升序排序了。

(40)Java读写文本文件时,中文乱码的问题
向文本文件写入中文时乱码,可以这样解决:

File outputFile = new File("/home/abc/out.txt");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(outputFile), "UTF-8");
BufferedWriter bw = new BufferedWriter(osw);
bw.write("中文" + "\n");
bw.close();

即显式地指定charset为UTF-8。

读取文本文件时,如果文件编码是UTF-8:

File inputFile = new File("data.txt");
InputStreamReader reader;
BufferedReader br = null;
try {
  reader = new InputStreamReader(new FileInputStream(inputFile), "UTF-8");
  br = new BufferedReader(reader);
  String line;
  while ((line = br.readLine()) != null) {
	//TODO: deal with the string 
  }
} catch (Exception e) {
  //TODO:
} finally {
  if (br != null) {
	try {
	  br.close();
	} catch (IOException e) {
	  //TODO:
	}
  }
}

同样要指定编码为UTF-8。

(41)解析XML的的两种方式:DOM & SAX
DOM解析慢,SAX解析快。Apache Digester是一个实现了SAX解析的Java库。

(42)创建List的更方便的途径:Google Guava
有了Google Guava,可以很方便进对集合类对象进行操作,例如创建一个List:

List<String> myList = Lists.newArrayList();

而我们平常会这样做:

List<String> myList = new ArrayList<String>();

再举一例:

Map<String, String> myMap = Maps.newHashMap();

可见用Google Guava会简单得多。这只是用Google Guava简化工作的一个非常简单的例子,还有更多很实用的功能,请参考其官网。
文章来源:http://www.codelast.com/
(43)Spring配置文件中的 & 字符转义
如果你用Spring来解析XML配置文件,并且在配置文件里的双引号中用了 & 字符:

<bean id="urlProcessor" class="com.codelast.URLProcessor">
 <property name="url" value="http://www.codelast.com/search.php?time=2012-08-02&id=36" />
</bean>

那么,你就不能像上面那样直接写 & 字符,否则Spring解析的时候会报错:

Exception in thread "main" org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 71 in XML document from class path resource [abc.xml] is invalid; nested exception is org.xml.sax.SAXParseException: The reference to entity "XXX" must end with the ';' delimiter.

你应该把配置文件中的 & 字符换成:
&amp;
这样就解决了。

(44)Spring JDBC配置编码,使得中文可以正确写入MySQL数据库
如下:

my_db_name.datasource.driver:com.mysql.jdbc.Driver
my_db_name.datasource.url:jdbc:mysql://192.168.1.100:3306/my_db_name?characterEncoding=UTF-8
my_db_name.datasource.username:root
my_db_name.datasource.password:XXXXXX

如上,你应该用“?characterEncoding=UTF-8”来指定编码为UTF-8,这样写入的中文才能正确显示,否则写入的中文可能会乱码或者显示为一堆问号。

(45)一些时间常量
利用 org.apache.commons.lang.time 包里的 DateUtils 类,我们可以轻易获取很多有用的时间常量:
DateUtils.MILLIS_PER_DAY:一天的毫秒数
DateUtils.MILLIS_PER_HOUR:一小时的毫秒数
DateUtils.MILLIS_PER_MINUTE:一分钟的毫秒数
DateUtils.MILLIS_PER_SECOND:一秒的毫秒数
还有更多,请看源码或文档。

(46)HashMap是不保证顺序的
按一定顺序添加到HashMap中的元素,遍历读出的顺序不一定是添加进去的顺序,如果要保持这种顺序不变,可以用LinkedHashMap.

(47)将一个Iterable<Element>集合中的所有元素添加到一个ArrayList中
利用Google Guava库,可以轻松做到这一点:

其中,values是一个 Iterable<String> 对象。

(48)如何配置log4j,使得可以将 INFO、ERROR 的日志输出到一个文件,其中 ERROR 的日志还同时会输出到另一个文件
要实现这种效果,你需要的是两个appender,其中一个appender是输出 INFO、ERROR 等日志的appender,另一个appender是只输出 ERROR(和以上级别)的appender:

log4j.rootLogger=INFO, A1, A2

log4j.logger.com.codelast=INFO

log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender
log4j.appender.A1.File=/example/code/all.log
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p %t (%F:%L) %c - %m%n

log4j.appender.A2=org.apache.log4j.DailyRollingFileAppender
log4j.appender.A2.Threshold=ERROR
log4j.appender.A2.File=/example/code/err.log
log4j.appender.A2.layout=org.apache.log4j.PatternLayout
log4j.appender.A2.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p %t (%F:%L) %c - %m%n

文章来源:http://www.codelast.com/
其中,第二句代码指定了所有 com.codelast 域下的 INFO 级别以上的日志都会被记录下来,appender A1没有特别地指定 Threshold,所以它就使用了这个Threshold定义;而appender A2指定了 log4j.appender.A2.Threshold=ERROR,因此,它只会记录ERROR以上级别的日志。
这样,all.log文件中就会记录所有INFO、ERROR、FATAL的日志,而err.log只会记录ERROR、FATAL的日志。

(49)Thrift service中的“oneway”定义
如果Thrift service的一个方法被定义成“oneway”的,那么表示:客户端只会向server提交一个resquest,而不会监听server的任何response。oneway的方法必须是void的方法。

(50)IntelliJ部分快捷键
首先说一下修改IntelliJ快捷键的方法:在菜单 File→Settings→Keymap 中可以修改(至少对12.0.2版是这样的),在右上方的搜索框里输入快捷键的名称(快捷键是干什么用的)就可以定位到该快捷键的设定处。然后右键点击它,选择“Remove XXX”,先把原来的快捷键删除,然后还是在右键菜单中用“Add Keyboard Shortcut”来添加一个新的快捷键,就相当于修改了快捷键的设置了。

Alt+F1:假设你正在编辑一个代码文件,想要快速跳转到Project视图中,该代码文件所在的树形结点上,用这个快捷键就可以做到。该快捷键会弹出一个菜单,选择“Project View”即可跳转过去。
注意:Ubuntu中Alt+F1被系统占用了,你可以按照上面的方法来修改成Alt+F5(当然也可以修改成其他的,要找一个还没被占用的)。
Ctrl+Alt+O:优化import的package。例如,在Java文件中你定义了:import org.apache.log4j.Logger; 而你实际上并没有使用到log4j,那么这就是一个多余的import项,可以把它去掉。可以用这个快捷键让IntelliJ帮你完成优化工作。

(51)运行Java项目中的test时提示一大堆定义找不到的问题
如果你在IntelliJ中运行Java项目里的test,提示一大堆定义找不到,而实际上这些文件都是存在的,那么有可能通过重新编译整个项目来解决:
点击菜单的“Build”→“Rebuild Project”即可。

(52)生成随机数
可以使用Apache的 org.apache.commons.lang.RandomStringUtils 类来生成随机数,里面有N个方法,可以生成各种各样的随机数,基本上能满足各种要求了,详细请看这里
举个例子,要生成5位数字的随机数,这样就可以:

RandomStringUtils.randomNumeric(5);

方便吧?

(53)Java读文件、写文件的最简单的方式
使用Apache的org.apache.commons.io.FileUtils的readFileToString()和writeStringToFile()方法,可以用一句代码将String写入文件,或者将文件中的内容读入String,详情请看这里
文章来源:http://www.codelast.com/
(54)BufferedWriter写文件时,write方法不会立即写入
调用
BufferedWriter的write方法写文件时,它不会立即写入,而是写到了缓冲区里,除非你调用flush方法刷新缓冲区。或者在写结束的时候调用close方法也可以达到同样的效果。

(55)Zookeeper自带的客户端zkCli.sh创建Zookeeper节点的方法
首先执行该脚本,进入交互模式:

./zkCli.sh

如果你要连接另一台服务器的Zookeeper,使用下面的命令即可:

./zkCli.sh -server host:port

其中,host是你要连接的Zookeeper服务器的地址,port是Zookeeper的端口。

“help”命令可以查看帮助:

[zk: localhost:2181(CONNECTED) 0] help
ZooKeeper -server host:port cmd args
	connect host:port
	get path [watch]
	ls path [watch]
	set path data [version]
	delquota [-n|-b] path
	quit 
	printwatches on|off
	create [-s] [-e] path data acl
	stat path [watch]
	close 
	ls2 path [watch]
	history 
	listquota path
	setAcl path acl
	getAcl path
	sync path
	redo cmdno
	addauth scheme auth
	delete path [version]
	setquota -n|-b val path

可猜出“create”命令可用于创建节点。该命令可带 -e 或 -s 参数执行,分别表示创建Ephemeral Node(临时的)Sequence Node(顺序的且永久的),这两个参数可以同时使用。
如果不加参数,那么就表示创建一个永久节点,例如:

[zk: localhost:2181(CONNECTED) 2] create /codelast "node_info"
Created /codelast

其中,第二个参数填的是节点的内容(注意:在这里,引号也会被当作是你填写的内容),必须要填写,不提供该参数则会创建失败。
如果我们创建一个
Ephemeral Node

[zk: localhost:2181(CONNECTED) 8] create -e /codelast "node_info"
Created /codelast

那么退出该session(在此处即退出zkCli.sh工具)后,创建的临时节点就会消失。
如果创建一个
Sequence Node,就会发现Zookeeper在你指定的节点名后添加了一串数字:

[zk: localhost:2181(CONNECTED) 2] create -s /codelast "node_info"
Created /codelast0000000011

再执行一遍同样的命令,会发现那串数字在递增:

[zk: localhost:2181(CONNECTED) 3] create -s /codelast "node_info"
Created /codelast0000000012

这说明,我们可以用一模一样的命令创建N个不同的结点,在某些情况下这是很有用的。
在创建了
Sequence Node之后,退出zkCli.sh,再重新连接,会发现前面创建的Sequence Node还存在,这说明Sequence Node也是永久的。
文章来源:http://www.codelast.com/
我们也可以同时使用 -e -s 参数来创建一个临时的顺序节点:

[zk: localhost:2181(CONNECTED) 0] create -e -s /codelast "node_info"
Created /codelast0000000015

在session断开之后,我们会发现该节点已不复存在。

(56)Java方法中的参数类型后跟三个点的作用
例如:

public void test(String... a) {
  for (String s : a) {
    System.out.println(s);
  }
}

如上所示,当参数类型后面跟三个点的时候,a就被当作一个数组来用了。

(57)如何添加一个文件/目录到IntelliJ项目的classpath中
请看这里

(58)thrift --gen java:private-members的作用
当你使用如下方法来生成一个thrift文件对应的源代码文件时:

thrift --gen java:private-members xxx.thrift

那么,生成的源码中,类的成员变量都是private的,而不是public的(public的是很不好的设计,但由于历史遗留原因,它们还是被保留了)。

(59)很简单的以追加内容的方式写文件的代码

FileWriter writer = new FileWriter("output.txt", true);
writer.write(message.message);
writer.close();

其中,FileWriter的构造函数的第二个参数为true决定了是以追加的方式写文件。

(60)Thrift在Java中为什么没有BufferedTransport
请看这里

文章来源:https://www.codelast.com/
➤➤ 版权声明 ➤➤ 
转载需注明出处:codelast.com 
感谢关注我的微信公众号(微信扫一扫):

wechat qrcode of codelast

发表评论