Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Thursday, 16 August 2012

Example for usage of Big Integer: Use of Big Integer to do factorial.


import java.math.BigInteger;
import java.util.ArrayList;


public class TextFactorial {

public static synchronized BigInteger bigNumber(int num){

ArrayList list = new ArrayList();
list.add(BigInteger.valueOf(1));

for (int i=list.size();i<(num+1);i++){
BigInteger lastfact = (BigInteger)list.get(i-1);
BigInteger nextfact = lastfact.multiply(BigInteger.valueOf(i));
list.add(nextfact);
}
return (BigInteger)list.get(num);

}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub

int num = 23;
System.out.println(TextFactorial.bigNumber(num));
}

}


Big Integer: 任意无限大。 As long as computer support, it could support. 

Java by Example Study Note - Chapter 2


Java by Example 

Study Note - Chapter 2

1. A method with same but different parameter - polymorphism(多态): overloaded (重载)

Example 2.1: 

Class Sub{
}

public class TextConstant {
Int number =0; 
public void run(Object obj){
System.out.println("is Object class: "+ obj);
}
public void run(Sub sub){
System.out.println("is sub class: "+ sub);
}
public static void main(String [] args){
TextConstant constant = new TextConstant();
Constant.run(null);
}
}

Output: Is sub class: null
Reason: When invoke run() method, it will first consider a parameter with class object. 
If the parameter is not class object, only it will consider object class. 
* null will not use for primitive variable like int. 

2. Primitive variable :  int, short, long, byte, float (4byte), double (8 byte), char (2 byte), boolean

Type conversion among primitive variable: can be auto change or force change. 

Rule for auto change: two type must be compatible, and target type must have range bigger than the initial type. 

Example 2.2: 
float f = 1.7f;
double dou = f;
system.out.println("dou: "+dou);

Output:  dou=1.7000000476837158

Rule for force change: put a cast in front with the new type. Will lost accuracy.  

Example 2.3: 
double d = 123.456d; 
byte b = (byte)d;
system.out.println("d: "+d+" b: "+b);
double d = 567.89d; 
byte b = (byte)d;
system.out.println("d: "+d+" b: "+b);

Output: 
d: 123.456 b=123
d: 567.89 b=55
Reason: 
The first d is within one byte (less than 256 (0x100)). So it take all the value in front the decimal. 
The second d is more than one byte (more than 256 (0x100)). So it will first convert 567.89 to 567. Then, 567 is mod with 256 and get the value and assign it to b. 

3. Operate on a variety of operators: 

Calculate operate: involve the problem on lost of accuracy when involve operation with different type of data. To ensure more accurate result, system will auto convert all the data to the type have more accuracy level. 

Example 2.4: 
int a=6, b=5; 
int divideA = a/b; 
float divideB=a/b;
float divideC=a/(b*2f);
System.out.println(divideA +","+divideB+","+divideC); 

Output: 1,1.0,0.6
Reason: 
For divideB: a = int type, b = int type. Largest accuracy : int. All the data will maintain at int type. And final result will convert from int to float. (6/5=1 in int. 1 int to float = 1.0)
For divideC: a=int, b = int, 2F=float. Largest accuracy: float. All the data will upgrade to float type. And final result will maintain in float type. (5*2f=10.0 in float, 6/10.0=0.6 in float too)

Note: 
~ : NOT 
^: XOR 
A+=1; : A=A+1; 
&& (Logical AND): When both side are true, then only true is return. Else, return false. 
If left side=false, right side is not executed. Return false directly. 
|| (Logical OR): When left side = true, return true, right side is not executed. 
If left side = false, return result as result of right side. 
& (Bitwise AND): When left side = false, right side is executed. 
| (Bitwise OR): When leftside = true, right side is executed but the result not concern, return true. 
Logical AND, OR: return type can be boolean type 
Bitwise AND, OR: return type can be boolean type or int type
For bit operator: <<(move left, LSB=0), >>(signed move right, if MSB=1 then MSB=1, if MSB=0, MSB=0), >>> (unsigned move right. MSB=0) 
Bit operator only suitable for the following data type: byte, char, short, int, long. 
<< : left move, multiple by 2. 
>>: right move, divide by 2. 
For int type right move:  a >> b,  if b > 31 . B will be mod with 324*8
a>>32 = a>>0
a>>33 = a>>1
For long type right move: a>>b, if b> 63. B will be mod with 648*8
a>>1 : a value will not change since it not assigned to a. 
a=a>>1: a value will change. 

Friday, 29 June 2012

JAVA string library knowledge


一、创建并初始化字符串:
  1、使用字符串常量直接初始化 String s="hello!";
  2、使用构造方法创建并初始化 String();//初始化一个对象,表示空字符序列
  String(value);//利用已存在的字符串常量创建一个新的对象
  String (char[] value);//利用一个字符数组创建一个字符串
  String(char[] value,int offset,int count);//截取字符数组offsetcount的字符创建一个非空串
  String(StringBuffer buffer);//利用StringBuffer对象初始化String对象

二、String类主要方法的使用:
  1、获取长度 *.length();//这与数组中的获取长度不同,*.length;
  2、比较字符串(1) equals() //判断内容是否相同
  (2)compareTo() //判断字符串的大小关系
  (3)compareToIgnoreCase(String int) //在比较时忽略字母大小写
  (4)== //判断内容与地址是否相同
  (5)equalsIgnoreCase() //忽略大小写的情况下判断内容是否相同
  如果想对字符串中的部分内容是否相同进行比较,可以用
  (6)reagionMatches() //有两种 public boolean regionMatches(int toffset, String other,int ooffset,int len);表示如果String对象的一个子字符串与参数other的一个子字符串是相同的字符序列,则为true.要比较的String 对象的字符串从索引toffset开始,other的字符串从索引ooffset开始,长度为len
  public boolean reagionMatches(boolean ignoreCase,int toffset,String other,int ooffset,int len);//用布尔类型的参数指明两个字符串的比较是否对大小写敏感。
 
三、查找字符串中某个位置的字符
  public char charAt(int index);//返回指定索引index位置上的字符,索引范围从0开始

四、查找指定字符串在字符串中第一次或最后一词出现的位置
  在String类中提供了两种查找指定位置的字符串第一次出现的位置的方法
  (1)public int indexOf(String str);//从字符串开始检索str,并返回第一次出现的位置,未出现返回-1
  (2)public int indexOf(String str,int fromIndex);//从字符串的第fromIndex个字符开始检索str
  查找最后一次出现的位置有两种方法
  (1)public int lastIndexOf(String str);
  (2)public int lastIndexOf(String str,int fromIndex);
  如果不关心字符串的确切位置则可使用public boolean contains(CharSequence s);

五、检查字符串的起始字符和结束字符
  开始的字符串两种方法
  (1)public boolean starWith(String prefix,int toffset);//如果参数prefix表示的字符串序列是该对象从索引toffset处开始的子字符串,则返回true
  (2)public boolean starWith(String prefix);
  结束的字符串方法
  public boolean endsWith(String suffix);

六、截取子串
  (1)public String subString(int beginIndex);
  (2)public String subString(int beginIndex,int endIndex);//返回的字符串是从beginIndex开始到endIndex-1的串
  要返回后4位可以这样写Syetem.out.println(*.subString()(*.length()-4));

七、字符串的替换
  两种方法
  (1)public String replace(char oldChar,char newChar);
  (2)public String replace(CharSequence target,CharSequence replacement);//把原来的etarget子序列替换为replacement序列,返回新串
  (3)public String replaceAll(String regex,String replacement);//用正则表达式实现对字符串的匹配

八、字符串的大小写替转换
  (1)public String toLowerCase(Locale locale);
  (2)public String toLowerCase();
  (3)public String toupperCase(Locale locale);
  (4)public String toUpperCase();

九、去除字符串首尾空格
  *.trim();

十、字符串转换
  1、将字符串转换成字符数组
  public char[] toCharArray();
  2、将字符串转换成字符串数组
  public String[] split(String regex);//regex 是给定的匹配
  3、将其它数据类型转化为字符串
  (1)public static String valueOf(boolean b);
  (2)public static String valueOf(char c);
  (3)public static String valueOf(int i);
  (4)public static String valueOf(long i);
  (5)public static String valueOf(float f);
  (6)public static String valueOf(double d);
  (7)public static String valueOf(char[] data);
  (8)public static String valueOf(Object obj);
  可变字符串的创建和初始化
  两种方法:
  public StringBuffer();
  public StringBuffer(int caoacity);
  StringBuffer类主要方法的使用:
  一、获取可变字符串长度
  (1)public int length();
  (2)public int capacity();
  (3)public void setLength(int newLength);
  二、比较可变字符串
  用String 类的equals()方法比较,但是不同。
  类Object中的equals()方法比较的是两个对象的地址是否相等,而不仅仅是比较内容,但是String类在继承Object类的时候重写了equals()方法,只是比较两个对象的内容是否相等
  而在StringBuffer类中没有重写Object类的equals()方法,所以比较的是地址和内容。
  三、追加和插入字符串
  (1)追加 public StringBuffer append(type t);
  (2)插入 public StringBuffer insert(int offset,type t);//offset处加入类型为type的字符串
  四、反转和删除字符串
  (1)反转 public StringBuffer reverse();
  (2)删除 public StringBuffer delete(int start,int end);
  五、减少用于可变字符序列的存储空间
  public void trimToSize();
  六、StringBuffer类转换成String
  public String toString();

Monday, 7 May 2012

java垃圾收集算法与内存泄露

from:  http://blog.sina.com.cn/s/blog_531424210100yz4b.html

1.垃圾收集算法的核心思想


 
  Java语言建立了垃圾收集机制,用以跟踪正在使用的对象和发现并回收不再使用(引用)的对象。该机制可以有效防范动态内存分配中可能发生的两个危险:因内存垃圾过多而引发的内存耗尽,以及不恰当的内存释放所造成的内存非法引用。
  垃圾收集算法的核心思想是:对虚拟机可用内存空间,即堆空间中的对象进行识别,如果对象正在被引用,那么称其为存活对象,反之,如果对象不再被引用, 则为垃圾对象,可以回收其占据的空间,用于再分配。垃圾收集算法的选择和垃圾收集系统参数的合理调节直接影响着系统性能,因此需要开发人员做比较深入的了 解。

2.触发主GC(Garbage Collector)的条件

JVM进行次GC的频率很高,但因为这种GC占用时间极短,所以对系统产生的影响不大。更值得关注的是主GC的触发条件,因为它对系统影响很明显。总的来说,有两个条件会触发主GC:
  ①当应用程序空闲时,即没有应用线程在运行时,GC会被调用。因为GC在优先级最低的线程中进行,所以当应用忙时,GC线程就不会被调用,但以下条件除外。
  ②Java堆内存不足时,GC会被调用。当应用线程在运行,并在运行过程中创建新对象,若这时内存空间不足,JVM就会强制地调用GC线程,以便回收 内存用于新的分配。若GC一次之后仍不能满足内存分配的要求,JVM会再进行两次GC作进一步的尝试,若仍无法满足要求,则 JVM将报“out of memory”的错误,Java应用将停止。
  由于是否进行主GC由JVM根据系统环境决定,而系统环境在不断的变化当中,所以主GC的运行具有不确定性,无法预计它何时必然出现,但可以确定的是对一个长期运行的应用来说,其主GC是反复进行的。

3.减少GC开销的措施

根据上述GC的机制,程序的运行会直接影响系统环境的变化,从而影响GC的触发。若不针对GC的特点进行设计和编码,就会出现内存驻留等一系列负面影响。为了避免这些影响,基本的原则就是尽可能地减少垃圾和减少GC过程中的开销。具体措施包括以下几个方面:
  (1)不要显式调用System.gc()
  此函数建议JVM进行主GC,虽然只是建议而非一定,但很多情况下它会触发主GC,从而增加主GC的频率,也即增加了间歇性停顿的次数。
  (2)尽量减少临时对象的使用
  临时对象在跳出函数调用后,会成为垃圾,少用临时变量就相当于减少了垃圾的产生,从而延长了出现上述第二个触发条件出现的时间,减少了主GC的机会。
  (3)对象不用时最好显式置为Null
  一般而言,为Null的对象都会被作为垃圾处理,所以将不用的对象显式地设为Null,有利于GC收集器判定垃圾,从而提高了GC的效率。
  (4)尽量使用StringBuffer,而不用String来累加字符串(详见blog另一篇文章JAVA中String与StringBuffer)
  由于String是固定长的字符串对象,累加String对象时,并非在一个String对象中扩增,而是重新创建新的String对象,如 Str5=Str1+Str2+Str3+Str4,这条语句执行过程中会产生多个垃圾对象,因为对次作“+”操作时都必须创建新的String对象,但 这些过渡对象对系统来说是没有实际意义的,只会增加更多的垃圾。避免这种情况可以改用StringBuffer来累加字符串,因StringBuffer 是可变长的,它在原有基础上进行扩增,不会产生中间对象。
  (5)能用基本类型如Int,Long,就不用Integer,Long对象
  基本类型变量占用的内存资源比相应对象占用的少得多,如果没有必要,最好使用基本变量。
  (6)尽量少用静态对象变量
  静态变量属于全局变量,不会被GC回收,它们会一直占用内存。
  (7)分散对象创建或删除的时间
  集中在短时间内大量创建新对象,特别是大对象,会导致突然需要大量内存,JVM在面临这种情况时,只能进行主GC,以回收内存或整合内存碎片,从而增 加主GC的频率。集中删除对象,道理也是一样的。它使得突然出现了大量的垃圾对象,空闲空间必然减少,从而大大增加了下一次创建新对象时强制主GC的机 会。

4.gc与finalize方法

⑴gc方法请求垃圾回收
  使用System.gc()可以不管JVM使用的是哪一种垃圾回收的算法,都可以请求Java的垃圾回收。需要注意的是,调用System.gc() 也仅仅是一个请求。JVM接受这个消息后,并不是立即做垃圾回收,而只是对几个垃圾回收算法做了加权,使垃圾回收操作容易发生,或提早发生,或回收较多而 已。
  ⑵finalize方法透视垃圾收集器的运行
  在JVM垃圾收集器收集一个对象之前 ,一般要求程序调用适当的方法释放资源,但在没有明确释放资源的情况下,Java提供了缺省机制来终止化该对象释放资源,这个方法就是finalize()。它的原型为:
  protected void finalize() throws Throwable
  在finalize()方法返回之后,对象消失,垃圾收集开始执行。原型中的throws Throwable表示它可以抛出任何类型的异常。
  因此,当对象即将被销毁时,有时需要做一些善后工作。可以把这些操作写在finalize()方法里。
  java 代码
  protected void finalize()
  {
  // finalization code here
  }
  ⑶代码示例
  java 代码
  class Garbage{
  int index;
  static int count;
  Garbage() {
  count++;
  System.out.println("object "+count+" construct");
  setID(count);
  }
  void setID(int id) {
  index=id;
  }
  protected void finalize() //重写finalize方法
  {
  System.out.println("object "+index+" is reclaimed");
  }
  public static void main(String[] args)
  {
  new Garbage();
  new Garbage();
  new Garbage();
  new Garbage();
  System.gc(); //请求运行垃圾收集器
  }
  }

5.Java 内存泄漏

由于采用了垃圾回收机制,任何不可达对象(对象不再被引用)都可以由垃圾收集线程回收。因此通常说的Java 内存泄漏其实是指无意识的、非故意的对象引用,或者无意识的对象保持。无意识的对象引用是指代码的开发人员本来已经对对象使用完毕,却因为编码的错误而意 外地保存了对该对象的引用(这个引用的存在并不是编码人员的主观意愿),从而使得该对象一直无法被垃圾回收器回收掉,这种本来以为可以释放掉的却最终未能 被释放的空间可以认为是被“泄漏了”。
  考虑下面的程序,在ObjStack类中,使用push和pop方法来管理堆栈中的对象。两个方法中的索引(index)用于指示堆栈中下一个可用位 置。push方法存储对新对象的引用并增加索引值,而pop方法减小索引值并返回堆栈最上面的元素。在main方法中,创建了容量为64的栈,并64次调 用push方法向它添加对象,此时index的值为64,随后又32次调用pop方法,则index的值变为32,出栈意味着在堆栈中的空间应该被收集。 但事实上,pop方法只是减小了索引值,堆栈仍然保持着对那些对象的引用。故32个无用对象不会被GC回收,造成了内存渗漏。
  java 代码
  public class ObjStack {
  private Object[] stack;
  private int index;
  ObjStack(int indexcount) {
  stack = new Object[indexcount];
  index = 0;
  }
  public void push(Object obj) {
  stack[index] = obj;
  index++;
  }
  public Object pop() {
  index--;
  return stack[index];
  }
  }
  public class Pushpop {
  public static void main(String[] args) {
  int i = 0;
  Object tempobj;
  //new一个ObjStack对象,并调用有参构造函数。分配stack Obj数组的空间大小为64,可以存64个对象,从0开始存储
  ObjStack stack1 = new ObjStack(64);
  while (i < 64)
  {
  tempobj = new Object();//循环new Obj对象,把每次循环的对象一一存放在stack Obj数组中。
  stack1.push(tempobj);
  i++;
  System.out.println("第" + i + "次进栈" + "\t");
  }
  while (i > 32)
  {
  tempobj = stack1.pop();//这里造成了空间的浪费。
  //正确的pop方法可改成如下所指示,当引用被返回后,堆栈删除对他们的引用,因此垃圾收集器在以后可以回收他们。
  
  i--;
  System.out.println("第" + (64 - i) + "次出栈" + "\t");
  }
  }
  }

6.如何消除内存泄漏

虽然Java虚拟机(JVM)及其垃圾收集器(garbage collector,GC)负责管理大多数的内存任务,Java软件程序中还是有可能出现内存泄漏。实际上,这在大型项目中是一个常见的问题。避免内存泄 漏的第一步是要弄清楚它是如何发生的。本文介绍了编写Java代码的一些常见的内存泄漏陷阱,以及编写不泄漏代码的一些最佳实践。一旦发生了内存泄漏,要 指出造成泄漏的代码是非常困难的。因此本文还介绍了一种新工具,用来诊断泄漏并指出根本原因。该工具的开销非常小,因此可以使用它来寻找处于生产中的系统 的内存泄漏。
  垃圾收集器的作用
  虽然垃圾收集器处理了大多数内存管理问题,从而使编程人员的生活变得更轻松了,但是编程人员还是可能犯错而导致出现内存问题。简单地说,GC循环地跟 踪所有来自“根”对象(堆栈对象、静态对象、JNI句柄指向的对象,诸如此类)的引用,并将所有它所能到达的对象标记为活动的。程序只可以操纵这些对象; 其他的对象都被删除了。因为GC使程序不可能到达已被删除的对象,这么做就是安全的。
  虽然内存管理可以说是自动化的,但是这并不能使编程人员免受思考内存管理问题之苦。例如,分配(以及释放)内存总会有开销,虽然这种开销对编程人员来 说是不可见的。创建了太多对象的程序将会比完成同样的功能而创建的对象却比较少的程序更慢一些(在其他条件相同的情况下)。
  而且,与本文更为密切相关的是,如果忘记“释放”先前分配的内存,就可能造成内存泄漏。如果程序保留对永远不再使用的对象的引用,这些对象将会占用并 耗尽内存,这是因为自动化的垃圾收集器无法证明这些对象将不再使用。正如我们先前所说的,如果存在一个对对象的引用,对象就被定义为活动的,因此不能删 除。为了确保能回收对象占用的内存,编程人员必须确保该对象不能到达。这通常是通过将对象字段设置为null或者从集合(collection)中移除对 象而完成的。但是,注意,当局部变量不再使用时,没有必要将其显式地设置为null。对这些变量的引用将随着方法的退出而自动清除。
  概括地说,这就是内存托管语言中的内存泄漏产生的主要原因:保留下来却永远不再使用的对象引用。
  典型泄漏
  既然我们知道了在Java中确实有可能发生内存泄漏,就让我们来看一些典型的内存泄漏及其原因。
  全局集合
  在大的应用程序中有某种全局的数据储存库是很常见的,例如一个JNDI树或一个会话表。在这些情况下,必须注意管理储存库的大小。必须有某种机制从储存库中移除不再需要的数据。
  这可能有多种方法,但是最常见的一种是周期性运行的某种清除任务。该任务将验证储存库中的数据,并移除任何不再需要的数据。
  另一种管理储存库的方法是使用反向链接(referrer)计数。然后集合负责统计集合中每个入口的反向链接的数目。这要求反向链接告诉集合何时会退出入口。当反向链接数目为零时,该元素就可以从集合中移除了。
  缓存
  缓存是一种数据结构,用于快速查找已经执行的操作的结果。因此,如果一个操作执行起来很慢,对于常用的输入数据,就可以将操作的结果缓存,并在下次调用该操作时使用缓存的数据。
  缓存通常都是以动态方式实现的,其中新的结果是在执行时添加到缓存中的。典型的算法是:
  检查结果是否在缓存中,如果在,就返回结果。
  如果结果不在缓存中,就进行计算。
  将计算出来的结果添加到缓存中,以便以后对该操作的调用可以使用。
  该算法的问题(或者说是潜在的内存泄漏)出在最后一步。如果调用该操作时有相当多的不同输入,就将有相当多的结果存储在缓存中。很明显这不是正确的方法。
  为了预防这种具有潜在破坏性的设计,程序必须确保对于缓存所使用的内存容量有一个上限。因此,更好的算法是:
  检查结果是否在缓存中,如果在,就返回结果。
  如果结果不在缓存中,就进行计算。
  如果缓存所占的空间过大,就移除缓存最久的结果。
  将计算出来的结果添加到缓存中,以便以后对该操作的调用可以使用。
  通过始终移除缓存最久的结果,我们实际上进行了这样的假设:在将来,比起缓存最久的数据,最近输入的数据更有可能用到。这通常是一个不错的假设。
  新算法将确保缓存的容量处于预定义的内存范围之内。确切的范围可能很难计算,因为缓存中的对象在不断变化,而且它们的引用包罗万象。为缓存设置正确的大小是一项非常复杂的任务,需要将所使用的内存容量与检索数据的速度加以平衡。
  解决这个问题的另一种方法是使用java.lang.ref.SoftReference类跟踪缓存中的对象。这种方法保证这些引用能够被移除,如果虚拟机的内存用尽而需要更多堆的话。
  ClassLoader
  Java ClassLoader结构的使用为内存泄漏提供了许多可乘之机。正是该结构本身的复杂性使ClassLoader在内存泄漏方面存在如此多的问题。 ClassLoader的特别之处在于它不仅涉及“常规”的对象引用,还涉及元对象引用,比如:字段、方法和类。这意味着只要有对字段、方法、类或 ClassLoader的对象的引用,ClassLoader就会驻留在JVM中。因为ClassLoader本身可以关联许多类及其静态字段,所以就有 许多内存被泄漏了。
  确定泄漏的位置
  通常发生内存泄漏的第一个迹象是:在应用程序中出现了OutOfMemoryError。这通常发生在您最不愿意它发生的生产环境中,此时几乎不能进 行调试。有可能是因为测试环境运行应用程序的方式与生产系统不完全相同,因而导致泄漏只出现在生产中。在这种情况下,需要使用一些开销较低的工具来监控和 查找内存泄漏。还需要能够无需重启系统或修改代码就可以将这些工具连接到正在运行的系统上。可能最重要的是,当进行分析时,需要能够断开工具而保持系统不 受干扰。
  虽然OutOfMemoryError通常都是内存泄漏的信号,但是也有可能应用程序确实正在使用这么多的内存;对于后者,或者必须增加JVM可用的 堆的数量,或者对应用程序进行某种更改,使它使用较少的内存。但是,在许多情况下,OutOfMemoryError都是内存泄漏的信号。一种查明方法是 不间断地监控GC的活动,确定内存使用量是否随着时间增加。如果确实如此,就可能发生了内存泄漏。 (本信息来源于 国信拉点 http://www.fsailing.com)

Friday, 4 May 2012

Program to find Mother's day date



Question: Use java language to write a program that could generate Mother’s Day date of each year. (Mother’s day located on 2nd week of May each year .)


Solution & problem faced: 

 
It’s seems perfect huh?
So let’s run it and see whether it could work correctly.
 
 
Oh my god. It’s failed. My code throw an exception named NumberFormatException. This exception is throw when execute the  line below:
Int year = Integer.parseInt(new String(b).trim());

At first, I thought it was because of I handle the data type incorrectly. I had a long try and research about it, but still the problem exist. Why why why? Long time struggle on it and finally I found out this is due I got the wrong format for the text file encoding.

 Path: Right click your .java file in eclipse>Properties>Resources>Text file encoding 





Lesson 1: Do pay attention to Text file encoding properties.  Make sure you know what encoding you would like to use.


Learning Source:



Now I change it back to US-ASC-II .  Run my code again and see whether it could work correctly. 
 


It seems correct right? I got the output. But when you see into details, you will find out that the date show is incorrect. Mother’s Day located on April? How come? It should located on May. Here’s come the second problem.


I got no idea how the problem could exist. But I guess it is because of my default setting for time zone and locale. By try an error, I add the code below to my program:
calendar.setFirstDayOfWeek (Calendar.MONDAY);       



Haha…It’s work finally. This is the output I get:
 



Final Code: