SCJP考试笔记

这是09年写的,难免有很多错误,望各位海涵并指正。

声明
变量
  声明变量要以 $、_、英文字母开头,后面可以加入数字。
  int $o_fd65465efewk = 0;//√
  int _o_$654_k = 0;//√
  int s2321_$_3f = 32;//√
  int 232erwe = 1;//x
  int 3dwq = 2;//x
  Long l = 232L;//√
  Long l = 232l;//√
  long l = 5;//√
  transient 标记为transient的变量,在对象持久化存储时,这些变量状态不会被持久化。
  volatile修饰变量。在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。也就是保持多个线程访问的同一对象的同步,低效,和synchronized 差不多的意思,但是synchronized不可以修饰变量。多线程并发环境下,一旦某个线程更改其值,任何线程访问该值应该立即看到修改后的值,但一旦某线程将其值代入计算时,只能使用代入代入前那一刻的值,代入并将其值推送到CPU计算之后,该值可能会被其他线程更改且不会通知到CPU运算单元,所以并不合适用来作为计数器。

  类里的变量,如果你没有初始化,会自动帮你初始化,对象类型为null,基础类型为最小值.方法里的变量,虽然你可以定义但不初始化,需要你初始化后才能使用。


数组
  数组的定义,必须带上维度(容量),或者直接赋值(可以为空),不能即定义维度又赋值,也不能既给维度也不赋值,数组的索引是从0开始算的。数组的长度是    intarr.length,不是length().
  int i[] = new int[];//这是错误的
  Integer i[] = new Integer[]{1,2};
  Object [] o = new Object[32];
  int ii[] = new int[]{};
  int iii = {1,2,3,};//编译通过
  int iiii = {,1,2,3};//编译错误
  int l = 2;
  int iii[] = new int[l];//指定数组长度的数值类型只能是byte,short或int,而不能是long,double之类.也就是数组最大容量不得超过Integer.MAX_VALUE.
  int[] iiii[] = new int[2][];//这样定义是可以的
  java声明的数组是容量不可变的,只可以通过Arrays类提供的方法来操纵数组,或者使用数组索引引用元素,不可以i[] = 2;这样来增加元素,除基础类型的元素,数组中均存放元素的引用。

  多维数组,就是数组里放数组。
  int arr[][] = new int[][]({1,2,3},{1,2,4,5,6,7}};
  其中
  arr[0] = {1,2,3};
  arr[1] = {1,2,3,4,5,6,7};
  []中的数字是用来定义数组的元素个数的,不超过int最大值即可。
  int ii[] = new int[5];

  数组是对象,也可以转型。数组的元素类型可以是类,抽象类,接口,基础类型。
  Object intarr = new int[]{1,2,3,4};
  int ii[] = (int[])intarr;
  Integer[] iii = new int[2];//这样是错误的,因为数组对象之间不存在隐式转换。
  Integer i1 = new Integer("213");
  Integer i1[] = (Integer[])i1;//这是错误的,Integer和Integer数组是不同的两个对象类型。

  实例类型可与声明类型不同,实例类型要是声明的引用的继承子类型,允许放入不同类型的,操作(排序、取出等)的时候要注意放入的不同的类型。而且要用对应的类型取.对象类型的数组取出时需要转型,取出数组中的元素时并调用方法时,会按照第一个对象的类型转型之后的类型,所以没有继承关系的会抛出ClassCastException;
  Object[] obj = new Object[]{1,"ok",'c'};
  int i = (Integer)obj[0];
  String s = (String)obj[1];
  char c = (Character)obj[2];

  顺便提一下Arrays.binarySearch();方法,该方法在使用前需要你执行一下sort,否则结果将不确定。
  这里的对象数组,在声明之后,不能放入比声明的实例更小型别的对象,只能是同样类型,或者继承自该类型的对象。


方法

  public void func(String args){}

  同名方法之间可以以参数的类型和顺序来区分,不可以以返回类型来区别,这成为重载(Overload)。

  如下定义,编译出错:
  Number ps(){/*codes*/}
  Integer ps(){/*codes*/}
  如下定义,编译出错:
  int ps(){/*codes*/}
  Integer ps(){/*codes*/}
  但如下定义没问题:
  假设:int k=2;ps(k);
  void ps(Number n){/*codes*/}//这一句不会被调用到
  void ps(int i){/*codes*/}//优先匹配
  void ps(Integer I){/*codes*/}//如果没有上面一句则调用这一句;如果修改k为Integer类型则调用这句,直接略过上一句;
  void ps(Long l){/*codes*/}//这一句永远不会调用
  void ps(Object o){/*codes*/}//如果只注释掉第2,3句则调用这一句;如果修改k为Integer类型,且只注释第3句,不会调用第二句的,会直接调用这一句;
  void ps(short s){/*codes*/}//这一句不会被调用,除非有匹配的short类型,java默认的数字类型是int.如果没有int/Integer参数的方法,ps(k)会调用  ps(Object)方法。
  上述代码如果在同一个类中,不能使用ps(null)方法调用,编译错误,因为ps(Integer i)和ps(Long l)的参数类型不是继承关系(或包装类),或者其关系无法比较出型别的大小,以便调用;但值为null的引用是可以的;或者注释掉第三或第四行可以编译,也可以通过ps(null)方法调用,此时调用的将是参数类型最大的方法(ps(Integer i)/ps(Long l))。

  编译之前会检查方法调用过程,优先调用匹配类型,如果没有,查找匹配的,自动向上转型参数匹配方法参数(可多次向上转型),如果没有,则查找Object参数方法,没有则编译出错。
  非静态方法可以直接访问实例和类中的任何可视方法和变量,静态方法不得直接访问类中的任何非静态方法和变量,可以通过实例的引用访问,这是因为非静态方法和变量随着运行时状态会有多种行为和值,而静态方法和变量在内存中始终只会有一个值和行为存在。
  多参数方法
  方法的可变参数,参数一定要放到最后,一个方法中只能有一个可变参数,其余的参数都要放在可变参数前面,类型不限。下面的定义,两个函数是一样的(pr和pr1是一样的,编译器将自动把你的多个参数合并到数组中,同时定义编译时会报错,可以看作写法上不一样):
  void pr(String[] args){/*代码*/}
  void pr1(String... args){/*代码*/}
  func(String...a)=func(String... a)
  func(new String[]{"A","B"})优先调用pr/pr1.
  func("A","B")优先调用void func(String a,String b){/*代码*/},如果没有,则调用void func(String...args){}

  func(String[]...a) 接受的是多个数组参数。func(new String[]{"A","B"})可以调用,或者多维数组,如果调用方法为空,如:func();也是可以调用这个方法的。
  func(Object o,int...i);调用方法是func("xx")可以,但func()就不行了。如果你定义func(String...a),使用func()调用也是可以的。这是多参数方法的特例。

  如下不能编译出错

  s({1,2});
  static void s(int... i){System.out.println("B");}
  static void s(Integer... i){System.out.println("A");}

  native用来修饰方法,是使用其他语言C/C++/汇编等语言实现的本地方法,不能有方法体,abstract方法和Interface中的方法不能用native来修饰,也就是该方法只可被声明在类中。
  strictfp,精确浮点,修饰类和方法,当一个class或interface用strictfp声明,内部所有的float和double表达式都会成为精确的。接口方法不能被声明为strictfp的,类中的方法可以,不能声明sbatract strictfp方法。

访问权限
  public 公开访问权限,任意
  protected 受限制访问,同一个类、包和子类
  friendly(默认) 同一个类、包内
  private 类内访问,这里,只要代码是在该类内执行的,能通过引用访问到,超出该类,则引用无效。
  static 静态,在内存中一直存在,只有一份。公开类不可以static,只有内部类才可以。static不能修饰构造函数.
  final 不能被指向别的对象或引用,类中的final必须由你给予初值,而方法中的final可以不给予初值,但必须确保实例化之前被赋值,一旦被赋值,将不可改指它处;

  static final 可以不赋予初始值,但必须在实例化之前赋值,否则必须给予初值;也就是final变量在能被引用到的地方,一定是要有值,一旦赋值则不能被改变。

  final不能和abstract搭配,因为abstract要求继承实现,而final则要求不被继承。final class,不能被继承,且该类中所有的方法为final;final方法可以覆盖非final方法,但不可以覆盖final方法,可以被继承。

  总结一下,这里的访问权限是在编译器编译的时候就处理的,权限限定的是编译器对对象和方法等的调用的可视和不可视状态。

所有关键字
  abstract continue for new switch
  assert default if package synchronized
  boolean do goto(保留,未使用) private this
  break double implements protected throw
  byte else import public throws
  case enum instanceof return transient
  catch extends int short try
  char final interface static void
  class finally long strictfp volatile
  const(保留,未使用) float native super while

  其他
  静态导入 是直接导入类的常量/静态方法,而不是包;
  import static java.lang.System.*;
  import static java.lang.System.out;
  import static java.lang.System.getProperties;
  out.print("XX");

控制
  条件/if/else/else if
  if()里面的表达式的计算结果值或返回值只能是true/false.
  boolean b = true;
  if (b=true){/*代码*/}可以编译执行
  int i=0;
  if (i=2){/*代码*/}编译错误
  注意:elseif 是错误的写法
  int i=1;
  if (i==1){//条件为真 则不再比较后面else if的条件。

  }else if (i==2){

  }

  循环/for/while/do while/break/continue
  break终止所在循环,continue 跳过后面的代码(step++会执行),回到开头继续执行,break/continue+lable 一般用于循环嵌套跳转。只能在方法内跳。
  break lable跳出标签所在的循环,和循环下的循环;不带标签则跳出所在的循环。
  continue lable不中断循环,直接回到所在循环的开头执行循环或标签所在的循环,要防止死循环。
  标签只能放到for while do-while 前和其他控制语句前面,不能放到变量定义前面,这主要用来做控制。
  lableA:
  for (int n=0;n<3;){
    int i=0;
    lableB:
    for (;i<10;i++){
      if (i==3)break lableB;
    }
    n++;
  }
  do{}while(); 先执行一次do后面的计算再while比较。
  for (String str:stringVars){/*代码*/} stringVars 是字符串数组,也可以是容器,但不可以是Iterator.str就是按顺序迭代选取的元素。
  异常try/catch/finally/throw/throws
  func(String args) throws Exception,IOException{}放在方法后面用来表示一旦产生这两个被声明的异常,就马上抛出,并不再执行出现异常的位置后面的代码;所以,你不得不在使用这类会抛出异常的方法的时候try/catch一下,注意:继承关系中,如果父类构造函数throws了异常,在继承的时候,必须要写一些构造方法处理这些异常,一般是把super放在try/catch里然后处理。

  finally 的执行是在 try/catch/finally 这个代码块区段内完成的.
  在离开这个区段前如果有return语句则先执行finally再返回,如果无返回则该区段执行完最后执行finally,这同样包括throw new Xxx..的代码,虽然执行了throw,但是finally是在throw 之后执行的;finally是为了配合抛出异常后继续执行善后操作而存在的。
  finally之后还有的代码,将在处理完异常,执行finally的代码后继续执行,所以finally的应用时机在这里可以是:处理因前面的异常导致的,后面的代码无法继续使用的变量,调整这些变量以便下面的代码能够继续执行或者关闭/回收资源。

  异常和错误抛出后,异常抛出的那一行至catch之间的代码将略过,交给匹配的异常处理段处理异常或错误,然后继续执行。
  使用了throw new XxxException();之后的代码应该是}catch(Exception e){/*处理异常代码*/},或者后面没有代码,因为throw...会面的代码除了catch,不可能被执行。
  if (true) throw new XxxException();/*这里不是catch区段*/
  上述可以编译通过,因为你的if判断导致后面的代码有执行的可能,哪怕是一个你确定的且事实上永远不可能执行的代码,因为编译器不会主动帮你做判断,他只能简单推测和断言。

  public String reader(){
    String line = "没有";
    BufferedReader in = null;
    try{
      in = new BufferedReader(new FileReader("abc.txt"));
      line = in.readLine();//如果这里抛出异常则下面的return line不执行,
      return line;
    }catch(IOException ie){//匹配异常
      System.out.println(ie.getMessage());
    }catch(Exception e){//匹配多个异常的时候,前面的异常不可以是后面的异常的子类异常,相反,前面的异常只可以是后面的子类异常。
      System.out.println(e.getMessage());
    }finally{
      if (in!=null)in.close();//用于保证关闭读入对象。
      if (line==null||line.equals("没有"))line="根本就没找到";//这里保证String对象不会是null,以便后面调用.
    }
    line = line.substring(1,2);//上面的finally会在这一行之前执行,因为已经处理过line对象,这里一般是不会执行错误的。
    return line;
   }

  switch/case/default
  switch/case只能接受基础类型(int,byte,char,short)或其包装类型和枚举类型.不包含boolean/long,case值只能为常量或final修饰的常量,或者枚举的内部变量表达式。如果case变量是常量值,切可以通过=赋值到switch的条件表达式结果的引用类型,可以编译执行。
  switch()中的条件判断可以是非逻辑判断表达式,如switch(i=2)/switch(i+10),switch(func())也是可以的,但是func()必须是返回的基础类型或其包装类型;case中的型别要与switch中条件表达式的类型一致。
  default语句并不是必须。
  如果执行中,找到匹配的case,其中没有break,代码将从该匹配case执行到最后一个case,遇到break,跳出.
  如果执行中,没有找到匹配的case,代码将从default一直执行到最后一个case,遇到break,跳出。
  如果匹配到了case,并且有break,则直接跳出。
  如果执行中,既没有匹配到,也无default,那么将不做任何执行退出.
  switch (Vars){
    case 'a':
    System.out.println("a");
    //break;
    default:
    System.out.println("default");
    //break;
    case 'b':
    System.out.println("b");
    //break;
    case 'c':
    System.out.println("c");
    //break;
    case 22:
    System.out.println("22");
    //break;
   }
  设char Vars = 'a'/'b'/'c'/'d';看看结果。


对象
  基础型别
  byte, from -128 to 127, inclusive(大小都包括)
  short, from -32768 to 32767, inclusive(大小都包括)
  int, from -2147483648 to 2147483647, inclusive(大小都包括)
  long, from -9223372036854775808 to 9223372036854775807, inclusive(大小都包括)
  char, from '\u0000' to '\uffff' inclusive(大小都包括), that is, from 0 to 65535
  float, 32 bit from 3.4028235E38 to 1.4E-45
  double, 64 bit from 1.7976931348623157E308 to 4.9E-324
  boolean, true/false

  byte 1字节,short 2字节(char 2字节),int 4字节,long 8字节.乘以二类推.要使用高精度,需要使用BigDecimal BigInteger;
  byte,short,char,int,long,float,double 都有对应的包装类.
  小数类型不指定后缀如:1.232321,默认是double型。后缀的L/l,F/f,D/d标识是一样的。
  char c = 's';//√
  char c = '\u3233';//√,unicode在JAVA中用使用了2个字节,且char是2个字节长度,可以直接给予一个unicode;注意 char类型只可以用''表示;
  char c1 = 'ss';//x
  float f = 232.321f;;//√
  float ff = 23432.32432d;//x
  double d = 100L;//√
  double dd = 3213.4323f;//√

  基础型别的默认值一般为0,0.0f,false 等具体的值(或者被强制要求初始化),包装类型的默认值为null(或者被强制要求初始化),计算的时候,将null的包装类型加入计算,会抛出NullPointerException异常。只要参与计算的包装类型里没有null,就不会抛异常,编译器会在计算的时候帮你转换到合适的类型,比如int转double,toString()等。

  基础型别在转型的时候,先在基础型别之间转,如short>int>long,再转Short>Integer/Long;

  几个涉及数学运算的基础型别的包装类(boolean/char除外)都继承了abstract class Number,所以包含有转为其他6个基础类型的非静态方法xxxValue();如int i = new Long(23L).intValue()/double d = new Integer("23").doubleValue()...注意,相互转换可能会损失精度,比如double转int是不会通过编译的,但是int转double是可以的,可以依据类型所占字节长度类判断是否可以转换并编译通过.
  包装类都有以自身或String做参数的构造函数,还有valueOf(String)/parseXXX(String)的转换的静态方法。parseXXX(String)是转型至基础类型的。如果String参数的原型不是数字,会抛出异常。
  上述类型,都包含static的常量:MAX_VALUE/MIN_VALUE/SIZE/TYPE;意思如字面意思。

  char类型不包含上面的规则,因为它不是继承自abstract class Number,但可以参加运算,因为他和short一样是2个字节的,可以等价转换。可以转到其他大于等于char类型的字节长度的基础类型上去。它的valueOf(char)静态方法,是直接返回一个Character对象。
  int i = 2;
  char c = 4;
  System.out.println(c+i);
  打印:6

  基础类型的包装类型已经是对象了,只可以在基础类型的运算环境里隐式转换,不可以在对象类型的环境里转换,如Long l = new Integer("2");这是错误的,但可以long l = new Integer("2");

  包装类型在与基础类型的转换进行之后,实例地址应该是改变了的,int i =1;Integer ii = new Integer("1");尽管ii==i;ii.equals(i);均为true,但实际的实例地址却是不一样的,即编译器在运算式中总是先隐式的转换包装类型到基础类型再计算,这样忽略了基础类型和包装类的引用类型计算,也模糊了两者引用的差别。
  JavaBean写法
  public class User{
    private String userName;
    private Integer age;
    public void setUserName(String username){
      this.userName=username;
    }
    public String getUserName(){
      return this.userName;
    }
    public void setAge(Integer age){
      this.age=age;
    }
    public Integer getAge(){
      return this.age;
    }
  }
  addActionListener/removeActionListener/final HTTP_STATUS_CODE.
  isLisenter/getLisenter/removeItemLisenter/getItemSelectable 合法


构造对象
  如果你自己实现了构造函数,编译器就不会帮你实现默认构造函数。
  public class B{
    void B(){};
    B(int i){}
  }
  则不能B b = new B();注意void B(){}不是构造函数

  代码段{}可以随意放置,随所属方法或类执行,不可以用static修饰,类中的非静态代码段是在对象实例化的时候的,但先于构造函数执行。
  class A{
    {}//执行代码块.
  }
  静态代码段。不能放在方法区段内,只能放在类里面。初始化的时候按顺序执行,所以只会执行一次.
  class A{
    static{}//静态执行代码块.
  }

  可以用this()调用其他的构造函数,this引用到当前语境所在的类的实例,所以不可以在static方法或代码块里使用,但可以用this来代表当前实例调用static方法。
  synchronized不能用来修饰类,只能用来修饰方法和代码段.


继承/extends
  继承中,父类的private方法/变量对子类不可见,或者对于其他权限限制造成的不可视,子类同样的方法和变量不视为继承方法,不构成覆写,所以调用的时候,也不能按覆写的思维认为调用到了父类方法,这就跟static的方法复写,是一个道理了,如果是通过父类的引用调用同名方法,其实是调用的父类的方法,但是因为其是private或不可视,只能内部调用或相应的实例才能调用,所以无法根据引用调用这个方法,最多只能根据子类的引用调用到子类同名方法。无法覆写为更低的访问权限,只能使用高级别权限覆写低级别权限的父类方法,public覆写>protected覆写>friendly覆写,并注意继承接口与实现接口的对象关系。private在继承关系中基本上可以理解为,不会参与到任何继承.

  继承关系里,父类和子类的是分开的两个实例存在于内存中,子类通过super持有父类引用。对外调用的时候方法被复写掉,变量还是在各自的实例里有效存在(static/private)。继承和实现中,子类中的方法,如果与父类中的某方法同名、同参数,但返回型别是该父类方法返回型别的同型别或继承型别或接口实现,视为重载。

  继承的时候,只是覆写方法,并不复写成员变量和静态方法,所以可以根据引用类型,访问其成员变量和静态方法.在调用方法的时候,调用的是实例方法,而不是引用方法;引用类型决定在编译时使用哪个方法进行编译。

  继承而来的构造函数中,this可以调用类中的方法或参数,super调用父类方法或参数,super/this方法必须是除开try之外的,构造函数下的第一行代码,在子类中只可以通过super调用父类方法和参数。如果父类另行实现带参数构造函数,需要在子类的构造方法里调用super(),而且子类的的每一个构造方法都需要调用super()方法,或者每一个构造方法都需要使用this()调用执行了super()的构造方法.

  super/this均不可以用在static方法和区段内调用,因为static的对象或方法,是一个类对象的,而super/this可能有多个实例对象。

  子类与父类的同名同参数的static方法可以同时存在,但不视为覆写;不可以用static方法覆写一个非static的同名同参数的非private方法,也不能用一个非static方法覆盖一个static方法,但同为static方法时可独立存在于各自的类;父类和子类同名同参数,而返回类型一个是基础型别,一个是包装类型,编译错误。final可以覆盖非final,但final方法不能被覆盖,除非private.

  转型需要继承关系对应,接口也可以转型。注意向下转型和向上转型的差别:向下转型安全,但运行期转型成功依赖于该对象是否是由子类型对象实例化而来.;向上转型不安全,损失可调用的方法/常量,但带来继承多态等好处;这里所说的安全,只是单纯的指数据安全,即不损失数据,不是说的转型过程安全,也就是:实例并不一定可以转型成功,这依赖于实例对象的具体型别和目标型别的关系.

  向上转型后调用实例的方法和常量时,如果调用的是没有覆写的方法和常量,实际则根据引用类型,调用到对应实例的方法和常量,如static,private也是没有被复写到的,private可能完全不能被调用,但也符合这个概念,因为其仅仅是不可视。
  null的引用可以转型到任意型别,A a= null;B b = (B)a;这里A和B没有任何关系。
  注意,在没有任何继承实现的关系下,类(含抽象类)可以转型至任意接口( Runnable r = (Runnable)(new Object());),但是接口没有实例,且其实现类(含抽象类)是无法转型到其他类的。
  转型的时候,=赋值表达式会确认你的类型是否符合继承关系(只要是继承与被继承关系都通过),如果符合关系,会让你编译通过,因为编译时仅仅检查你的继承关系,而运行时会检查实例的具体类型,所以运行时可能抛出转型异常。

  总结:继承中,子类对于父类方法和变量的状态(static/final),访问权限不可以从大到小的限制,只能从小到大,比如父类public,子类不能是protected.不能用非static覆写static方法.

  接口可以一次继承多个接口,但类只能继承自一个类,抽象类继承抽象类,不能实现父类中的抽象方法,抽象方法应由非抽象类实现。

  接口/抽象类/interface/implements/abstract
  抽象类和接口同属于一样的东西,接口更具抽象化.接口只能继承接口,但抽象类可以继承非抽象类,任何接口和抽象类实现的时候都会默认继承Object,尽管接口无法继承类,但其实现类默认就会继承Object.类和抽象类都只能实现接口,不能继承接口。

  接口interface默认public,不能abstract,方法默认public abstract,不能static修饰,接口常量 public static final,方法覆写的时候注意权限。

  抽象类实现接口,可以不实现或者实现接口的方法,或者继续声明为abstract,由子类实现。抽象方法只能在抽象类里,抽象类不能实例化,但可以有构造方法,虽然不能被直接实例化,但构造方法在子类实例化时和非抽象化类一样执行,抽象类可不包含任何抽象函数。抽象方法的访问权限限制,和普通类方法一样,继承的时候也需要遵守访问权限规则等。

  抽象类中可以有非抽象方法,但是抽象方法一定要放到抽象类中,抽象方法是不带{}的:abstract void visul();也就是没有具体实现,不同于空方法:void visul(){};抽象类是可以继承非抽象类的;比如public abstract Number extends Object

内部类
  JAVA本身不支持的多重继承,内部类可实现,并且可以扩展为私有的内部类.非公用类都可以做成内部类以便自己扩展调用. 方法内的内部类不能是public/private/protected,方法内的内部类只能看到该方法内的final变量,作用该方法范围,外部类不能是private。代码段(if/else/while/try...)内的内部类只能在这段代码内有效。

  内部类和外部类不得重名,
  abstract private 仅仅可以定义内部类一起使用,且只能在outer class的内部使用,不能在方法里使用。private修饰的内部类只能在内部被使用,无法通过传递引用等方法给外部使用。
  static内部类只能访问对应外部类的static方法和常量。
  静态成员类只能定义于外部类的顶层代码或外部类其它静态成员类的顶层代码中(嵌套定义);不能定义于外部类的某个函数(静态/非静态)中。

  非静态内部类内部不能使用static修饰符,但可以使用static final修饰变量,final变量可以不指定值。非static方法能访问class内任何常量和方法,不带this也可,new OuterClass().this;

  public class Wrapper {
    public class InnerClass{}
  }

  非静态内部类中,隐式的传递了一个外部类的引用给内部类,所以你必须先实例化外部类才能访问到内部类。静态内部类因为始终存在,所以不需要非得实例化外部类就可以访问到。
  Wrapper wrapper = new Wrapper();
  Wrapper.InnerClass ic = new Wrapper().new InnerClass();//实例化外部类
  public class Wrapper1{static class InnerClass{}}
  Wrapper1.InnerClass ic = new Wrapper1.InnerClass();//这里,静态内部类的实例化,引用的时候和非静态内部类的不一样,不能通过外部类的引用来引用静态内部类。

  在另一个类里继承一个内部类,需要先调用该内部类所在外部类的构造方法,再做其他操作,JAVA隐式的传递了一个外部类的引用给内部类,你必须先实例化外部类才能通过该引用访问到内部类,这里的super是调用的父类的内部类的构造方法,和非内部类的继承的构造方法相比,这里,无论是默认构造方法和非默认构造方法,都需要你主动的调用一下构造方法,
  格式是:OuterClassInstanceReference.super(),一般的类(内部类除开)的继承可能会自动的调用默认构造方法,而这里不论构造函数为何都要你手动调用,有点匪夷所思。
  class SuperWrapper extends Wrapper.InnerClass{
    public SuperWrapper(Wrapper w){w.super();}
  }
  在从其他的类使用这个内部类时,需要给它提供一个引用。
  SuperWrapper superwrapper = new SuperWrapper(new Wrapper());

  InterfaceI i = new InterfaceI(){/*some codes*/};//直接实现并实例化接口,这是个定义语句,所以要带;结尾.
  class AutoImplementsInterface implements BasicInterface {/*some codes*/}仅仅是定义实现了接口的内部类,需要实例化才能使用.
  class AutoExtendsBasic extends BasicClass{/*some codes*/};//继承
  func(){class AutoImplementsInterface implements BasicInterface {/*some codes*/}}//方法内实现接口

  匿名内部类没有名字,没有构造函数,编译器会实现默认构造函数,可以定义为private.
  func(new BasicInterface(){/*implements codes*/}){};
  func(new BasicClass(){/*implements codes*/}){};
  如果匿名内部类继承的是使用了非默认构造方法的类,必须使用super调用构造函数,这和继承的构造方法调用是一样的。
  func(new BasicClass(variables){super(vars);/*some codes*/}){};

枚举/enum
  枚举可以定义在class外,但必须在package/import/下面。如果没有package或import,可以在第一行定义。其结构可以近似的看作是一个类。
  ClassXXX.TestEnum te = ClassXXX.TestEnum.sString;//每一个枚举内部常量,都隐式的继承了自身所属的枚举类,且是static final,所以枚举值只能直接引用。
  内部常量都是public static final,枚举是static类型,直接引用,不能实例化,构造函数默认且只能private,不能abstract/final,但可以声明abstract方法,可switch/case。可以按自己的需要添加带参数的构造方法和toString()方法。
  枚举值为函数的枚举,可以自定义一个构造函数,默认private,参数与枚举的实际参数类型同。
  枚举中的常量或方法常量继承了所属的枚举类(隐式继承),所以可以直接使用枚举类定义的方法,常量下的同名同参数方法可以看作是复写了枚举已经定义的方法,可以将枚举类的引用指向到枚举类下属的枚举个体。
  枚举都默认继承了java.lang.Enum.因为单根继承的关系,不能再继承,但可以实现接口。
Enum默认实现了java.lang.Comparable接口。Enum覆载了toString方法,valueOf方法返回值对应的枚举键,values遍历枚举,oridinal按顺序的offset.

  for (TestEnum te:TestEnum.values()){/*some codes*/}
    EnumMap EnumSet

    public enum Color {
      Red,
      Green,
      Blue;
    }

    Color c = Color.Red;
    switch(c){//这里的switch,如果给出了枚举类型,那么下面的case可以自动编译到枚举值类型。
      case Red://不可以用Color.Red.编译错误,下同
      System.out.println(Color.Red);
      case Green:
      System.out.println(Color.Green);
      case Blue:
      System.out.println(Color.Blue);
      default : System.out.println("none");
    }

    enum TestEnum{
      sssString("ok"),
      sString("s"),
      SString("kk");
      TestEnum(String k){this.value=k;}
      private final String value;
      public String getValue(){return value;}
    }

    enum TestEnum{
      RED{public String toString(){return "RED";}},
      GREEN{public String toString(){return "GREEN";}},
      BLUE{public String toString(){return "BLUE";}};
      private String value;
      TestEnum(String k){
      this.value=k;
    }
    public String getValue(){return this.value;}
  }


泛型

  可以声明多个泛型参数类型,比如<T,P,Q...>,同时还可以嵌套泛型,例如:<List<String>>.
  在范围限定的时候使用&连接,<T extends K & Comparable & Serializable>

  <参数类型 extends 基础类型/接口>
  ?和super/extends之间不带空格也能编译的。><和其他关键词之间的空格也可以忽略。
  泛型不能使用基础类型来表示,只能使用对象类型(接口与抽象类也属于对象).
  super T表示是泛型的型别是T或T的父类,extends表示是泛型的型别是继承自B或B的子类.?通配符表示的是,未知的泛型型别,表示其类型并不一定是继承自Object,也可能是你后面定义的类型.而:你没有定义泛型类型的任意位置,都表示的其泛型类型一定(默认)是继承自Object.
  implements不是泛型语法。还要注意final的class(如:String)不能extends,因为final不能被继承,但可以编译,执行的时候会抛出异常。

  类泛型定义不得使用通配符,通配符导致类无法传导泛型类型.不可以使用super定义,类的定义不可以定义成一个区间,必须定义为向下或者接收所有类型。
引用的泛型定义,只能用?/super/extends定义,T之类的符号是不可以的。使用通配符的都可以编译通过,<? super T>可以在任何执行泛型检查操作的时候编译通过,但是<?>/<? extends T>编译不通过。前一种定义,编译器可以通过既有代码找到所有的泛型类型,所以在实例化和涉及类型操作的时候有一个明确的类型列表可以参照;后两种定义导致编译器无法准确预测你的泛型到底是哪些类型,因为这种定义实际上是无向下界限的定义,此时编译器在遇到你执行一些涉及到类型操作的代码时,会给你一个异常,因为你的定义(?/? extends)让他无法找全所需要的类型,而你现在你却要操作它,操作并不安全;而这里为什么要说找全所需要的类型,


  这里的引用的定义和实例化类的泛型定义需要符合目标类的泛型定义的范围,因为编译器会对泛型的使用进行检查,而且引用泛型的定义应该是定义为能够明确知道的类型,再匹配可能未知的类泛型的区间范围。所以,编译器在引用和实例的泛型定义里无法找到对应类型(原生类型Object除外),但又可以通过原生方法执行没有错误的,会给出Unchecked Warning警告,而无法找到引用或类泛型描述的所有泛型类,不能编译。
  Gentype<?> g = new GenType<Number>();//可以定义编译,但在执行涉及泛型类型操作的时候会编译异常.
  Gentype<? extends Number> g = new GenType<Number>();//表示右边的实例化的类的泛型必须定义为继承自Number的所有类,这里,涉及类型操作同样会出现异常,所以可以插入null,但插入任意非null值就会异常。
  Gentype<? super Number> g = new GenType<Number>();//表示右边的实例化的类的泛型必须定义为Number所继承过的对象或实现过的接口,如:Object/Serializable;这里所有可以匹配的泛型类型都是可以找到的,是可以经过检查并通过的,所以只要符合泛型定义,其编译执行没有问题。
  Gentype<? super Integer> g = new GenType<Integer>();
  Gentype<Integer,String> g = new GenType<Integer,String>();
  List<Integer> lis = new ArrayList();
  List lis = new ArrayList();//上面一行和本行中,如果在后面的代码执行add操作,会给出Unchecked Warning;因为类型并不确定,但可以执行add(Object)方法。

  <Object>表示明确了泛型类型是Object.<?>,等价于<? extends Object>;此时并未明确泛型类型,需要你来给予明确的泛型型别,但一定是Object继承过的类型,且一旦给定,将不可变更.

  class GenType<K extends Object,V>{
    private V obj;
    public void setObj(V obj){this.obj=obj;}
    public V getObj(){return obj;}
    private HashMap<K,V> hm;
    public void setHm(Map<K,V> m){this.hm=m;}
    public HashMap getHm(){return this.hm}
  }

  泛型方法.
  public void setter(Gentype<?super T> g){}//这里的参数定义和引用定义规则是一样的,没有什么特殊之处。
  public <T extends K> Gentype<T> func(T first){return first;}//这里的K是针对方法返回型别的约束,参数也可以引用K的约束规则。但是返回类型的泛型不可以使用通配符和super,这里的定义和类的泛型定义规则是差不多的,可以参照类为什么不可以用通配符和super。
  class C<T extends Comparable<? super T> & Serializable>
  貌似很迷惑人的语法,这里的Comparable必须是实现自Comparable的类,并且Comparable的实现类应该是带T类型泛型型别的,类型T必须是实现了Serialable的类,然后后面可以跟多个参数,使用&隔开,这里的多个参数必须是接口,原因,见下面的分析.
  因为JAVA的单根继承和可同时实现多个接口,所以泛型的范围限制中,只可以是一个类后面跟多个接口,extends后面的第一个参数,只可以是类(当然也可以是接口),第二个直至最后一个参数,只能是接口了。作为JAVA来说,语法是严谨的,和多参数方法一样,把不同的类型放前面了。
  public class B<T extends D & I & java.io.Serializable> extends A{
    private T s;
    public void setS(T i){this.s=i;}
    public T getS(){return this.s;}
    public T getSS(T s){return s;}
  }

  泛型依赖于运行时的具体状态,不可以对静态方法,静态变量,静态内部类使用泛型作为类型参数。泛型约束的是运行时对象,不是编译时对象。
  方法返回值的泛型和类的泛型约束是一样的级别,所以可以在静态方法的返回类型上使用泛型约束。如
  List<Integer> l = Arrays.asList({1,2,3});
  public static <T> List<T> asList(T... a) {return new ArrayList<T>(a);}
  l的引用类型的泛型告诉了asList方法,返回的List的泛型必须是Integer.Arrays.asList的调用参数是一个int型数组对象,其泛型型别自动为Integer,调用的时候,会自动传递给asList方法,所以可以通过T来引用到泛型限制。

  总结:泛型,就是定义可以使用参数的类型的范围区间,比如System.out.println();我们可以把is-a Object 的类型的对象放进去,差不多就是所有对象了,但是我们希望这些对象有所限制,至少有个区间范围,也就是对象继承的层级,那么泛型可以解决这个问题。
我们要明确的是,泛型其实就是编译器自动帮你转型到你在泛型型别里定义的型别,多数时候用来代替你在代码里的转型。
  整个定义过程是:类定义最基本的根泛型型别,限定方法体参数的根泛型型别,再具体到返回或参数的根型别至最下层型别,这样构成了一个区间范围;初始化时需要通过在引用和实例处,给予泛型类和方法一个明确的型别,因为泛型被设计用来容纳一个区间内的型别,当你不明确时,编译器目标不明确,无从帮你自动转型到明确的型别。

  所以在这个过程中,需要注意,型别是随着类>返回值>方法>参数一步步的明确具体的型别的,返回的时候是用extends限定方法返回型别的范围,意思差不多就是,放入一类数据,抽出一类数据,所以通配符和super的应用时机必须在你给予一定的型别信息才能够使用的,否则型别信息不明确。


GC
  GC回收的时候要注意多个引用的互相指向(网状结构),如果某个对象以及对象内部的引用不能通过这个对象的引用之外的引用访问,造成引用和对象实例脱钩,可能会被GC回收,可画一幅对象树图,从最顶层引用找,不可到达的实例对象即可被回收,要注意的是,引用和对象实例,实例会被回收,引用只是实例的一个访问句柄。
  GC的执行永远不确定。finalize()/System.gc()/Runtime.getRuntime().gc() 也不能让GC马上执行。


异常/错误
  class Exception/Error 都是继承自Throwable.class,Error extends Throwable,可以直接throw new XXXXException();
  方法体后面抛出异常的格式是throws new XXXException,..Exception...;
  Exception/Error,一个是异常,一个是错误。基本可以理解为,异常是程序执行过程中你应该知道或者捕获而且能够处理的问题,成为Checked Exception;
  错误是可能未知的或者你无法在程序中正常处理的问题,或者需要外界协助处理,成为运行时异常RuntimeException。throw出来的异常,是需要你try/catch的,当然你也可以继续向外throws...;
  类继承中,覆写的函数默认继承抛出父函数同样的异常,不必写到方法体的后面,如果另行指定,则要求异常是父类函数异常的子类,父类函数若不抛出任何异常,则子类函数也不得抛出异常,因为父类没有任何定义异常抛出,子类不得抛出任何异常。thorws关键词不受异常类型约束,可以随意抛出,当然你使用它也可以代替try/catch,但是多数情况下是,你最好处理一下异常,并处理一下因异常带来的或未处理的问题。

  RuntimeException以及其子异常都不需要在代码里try/catch,这个代表着运行期异常,是由JVM来负责抛出的,当然你自己处理也没关系,这称为Uncheck Exception;Checked Exception一般是继承自Exception,编译器能够检测得到,简单说就是你使用的某个方法或类throws/throw了异常,而编译器通过检查,需要你处理的这些异常,可以看作是可控的程序错误。不可以在不会引发并抛出某异常的区段,特意catch某个异常。这其中包括你自己实现的继承自Exception的异常类。

  StackOverflowError 一般是函数嵌套层数太多导致,比如调用自己或互相调用导致死循环。OutOfMemory一般是内存耗尽,应该检查产生的对象的数目,检查内存泄漏或加大JVM内存。

  继承关系中,如果父类的构造函数抛出了异常,哪怕是无参数的构造函数,是需要在子类的构造函数里处理一下的异常的。要么try/catch要么throws,和其他普通方法一样,不过是需要你引起注意并处理一下,通常来说,构造函数里的异常是需要额外注意的。
  所以比较重要的一点是:你不得在不可能产生某个异常,或者你使用的方法中没有抛出(throw new XxxException/throws XxxException)任何异常的地方try/catch Checked Exception.

运算
  计算规则
  运算表达式中的包装类型会自动转换到基础类型,参与计算后的包装类型,可能会改变实例地址。比如自增包装类型的值后,原引用将不再指向原来的实例地址。或者可以看作,在传递包装类型的实例时,传递的本来就是基础类型,所以进行运算后,该值转换回包装类型,因其是复制品,所以是保存在另一个内存地址。
+ 运算符,如果整个表达式中,有String类型的字符串参与计算,则将所有的变量都隐式执行toString()(遵守运算符优先序并从左至右依序执行,基础类型一并隐式转型至包装类型),然后相加为字符串。
  在String字符串拼接或任何隐式执行toString()的地方,任何类型的指向null的引用虽然都不能执行toString()方法,但会被转换为字符串null.
  八进制:0-7,如012;十六进制 0-F,如0x12 .
  byte b1 = 10;
  byte b2 = 20;
  byte b3 = b1+b2;//错误,运算之后超过类型最大大小的,会自动晋升为int.
  int i = b1+b2;//正确.

  9:00000000000000000000000000001001
  8:00000000000000000000000000001000
  7:00000000000000000000000000000111
  6:00000000000000000000000000000110
  5:00000000000000000000000000000101
  4:00000000000000000000000000000100
  3:00000000000000000000000000000011
  2:00000000000000000000000000000010
  1:00000000000000000000000000000001
  0:00000000000000000000000000000000
  -1:11111111111111111111111111111111
  -2:11111111111111111111111111111110
  -3:11111111111111111111111111111101
  -4:11111111111111111111111111111100
  -5:11111111111111111111111111111011
  -6:11111111111111111111111111111010
  -7:11111111111111111111111111111001
  -8:11111111111111111111111111111000
  -9:11111111111111111111111111110111
  趋势是递减的,也就是,从-1,全部位为1,开始数字递减。
  正数的机器数是原码,负数的机器数是补码.就是0和1。

  逻辑运算符中的 && 和 & 的区别在于,&&在左值为false时,将不再计算右值;而&不论左值为何,总是会计算右值。
  位移 就是向左向右推动,舍弃被推掉的值,填入被推的位置合适的值。

  & 同一进位 都为1则为1,否则为0.;可以理解为上面的逻辑运算符中,左右两边的数值用二进制摊开,同时为1才为true,填1,否则填0.
  示例代码:
  int a = 6;
  int b = 10;
  int c = a & b;
  计算过程:
  6 的 二进制:0000 0000 0000 0000 0000 0000 0000 0110
  10的二进制:0000 0000 0000 0000 0000 0000 0000 1010
  计算 结果为:0000 0000 0000 0000 0000 0000 0000 0010
  这个数字转换为十进制就是数字2

  | 同一进位 有一位为1 则为1 否则为0;可以理解为逻辑运算符中的||,有一个条件为真则同进位填1,否则填0。
  示例代码:
  int a = 4;
  int b = -10;
  int c = a | b;
  计算过程:
  4 的二进制:0000 0000 0000 0000 0000 0000 0000 0100
  -10的二进制1111 1111 1111 1111 1111 1111 1111 0110
  计算结果为:1111 1111 1111 1111 1111 1111 1111 0110
  这个二进制数转换为十进制就是数字-10。

  ^ 同一进位 相同为0 不同为1;可以理解为逻辑运算符中的==,相等则填1,否则填0.
  示例代码:
  int a = 6;
  int b = 10;
  int c = a ^ b;
  计算过程:
  4 的二进制:0000 0000 0000 0000 0000 0000 0000 0110
  10的二进制:0000 0000 0000 0000 0000 0000 0000 1010
  计算结果为:0000 0000 0000 0000 0000 0000 0000 1100
  这个数字转换为十进制就是数字12

  ~ 只操作一位数 1变0,0变1;和上面的理解则不一样了,他是直接把1和0交换。
  示例代码:
  int a = 4;
  int c = ~a;
  计算过程:
  4 的二进制:0000 0000 0000 0000 0000 0000 0000 0100
  计算结果为:1111 1111 1111 1111 1111 1111 1111 1011
  这个数字转换为十进制就是数字-5。

  >>> 无符号 右位移,向右边移动,舍弃值,左边补0;
  >> 有符号 右移,正数补0,负数补1;
  << 左移 空位补0
  这里没有无符号 左移位,因为向左移动,位数补齐就会在右边,但是不可以在右边补位,所以无此运算符。
  左右位移和无符号右移运算符只能用于整形及其兼容类型(byte,int,short,long),在移位运算时,byte、short和char类型在移位前先转为int型,移位后的结果自然也是int类型,int类型进行移位时,规定实际移动的次数是移动次数和32的余数,也就是移位33次就是移位1次,移位33次和移位1次得到的结果相同。移动long型的数值时,规定实际移动的次数是移动次数和64的余数,也就是移位66次就是移位2次,移动66次和移动2次得到的结果相同。
  例如11 >> 2,则是将数字11右移2位
  计算过程:
  11的二进制形式为:0000 0000 0000 0000 0000 0000 0000 1011,把低位的最后两个数字移出,该数是正数,高位补零。
  得到的最终结果是:0000 0000 0000 0000 0000 0000 0000 0010。转换为十进制是2。

  java.lang.Math 是提供数学运算方法的类。java.Math 是一个高级数学计算的包,包含浮点数BigDecimal,高精度整数BigInteger;
  这里所需要使用的参数的类型基本都是double/float/int/long或其包装类,不可以有boolean,可以有char/byte/short,不过都会被自动转为int或适合的类型作为参数。 abs返回绝对值;max/min(first,second) 2个参数,顺序可变,返回较大/小者;random产生 0-1.0之间的double;round将小数点后一位 四舍五入,返回long/int;
  floor:取小于且最接近参数的整数的double值,Math.floor(32432.432432),返回32432.0;ceil:取大于且最接近于参数的整数的double值,Math.floor(32432.432432),返回32433.0;

  ++i 从左到右原则是先自增再做别的操作,而i++是在做别的操作之前自增。
  一般来说boolean值只可以进行逻辑运算,可操作的运算符只有= == && ||,另外移位运算符中的^ & |的运算方式可能也是来自于逻辑运算符,因为他们也可以用在boolean上进行计算,用于boolean:&&和&一样,|和||一样,^和!=一样。
  优先序
  优先级  运算符         结合性
  1     () [] .         从左到右
  2     ! +(正) -(负) ~ ++ -- 从右向左
  3     * / %         从左向右
  4     +(加) -(减)       从左向右
  5     << >> >>>      从左向右
  6     < <= > >= instanceof 从左向右
  7     == !=         从左向右
  8     &(按位与)       从左向右
  9     ^           从左向右
  10 | 从左向右
  11 && 从左向右
  12 || 从左向右
  13 ?: 从右向左
  14 = += -= *= /= %= &= |= ^= ~= <<= >>= >>>= 从右向左
  该表中优先级按照从高到低的顺序书写,也就是优先级为1的优先级最高,优先级14的优先级最低。
  结合性是指运算符结合的顺序,通常都是从左到右。从右向左的运算符最典型的就是负号,例如3+-4,则意义为3加-4,符号首先和运算符右侧的内容结合。

对象比较
  对象实现Comparable接口或继承Comparator类,那么该实例就是可以排序的,就可以调用Collections的sort方法对集合中的元素排序.JAVA中很多基础类都实现了这个接口。一般情况下,你必须自己实现属于你的类的hashcode/equals方法,所以实现Comparator是最好的选择(会强制要求你实现方法boolean equals(Object)和compara(T1,T2)),而JAVA中的对象一般都有相应的hashcode/equals实现,所以只实现了Comparable,且只实现了方法comparaTo(T)方法。

  指定一个Comparator,也就是实现了Comparator的类的一个实例. 但是Java本身只提供了一个Comparator的实现,就是Collections.reverseOrder(). 该方法返回的是一个已经实现了Comparable接口的反序.

  SortMap/SortTree以及List等可被排序的容器里的对象都要实现比较器接口Comparable或Comparator,实现comparaTo(T)/compara(T)方法,如果不实现,将抛出ClassCastException异常,因为容器在sort的时候,自动调用comparaTo(T)方法,T要么为泛型类型,要么为自己的实现类类型,该方法返回正数,0,负数,该方法的排序结果与equals可能不同,这是根据你自己的具体实现来决定的。所有执行比较的 Java 核心类都具有 equals 一致的自然排序。

  hashCode()/equals(),JAVA容器中,HashMap、HashSet、Hashtable等都是通过hashcode值来映射对象的位置,从而实现对象容纳,两个使用同样参数初始化的POJO,因为其所有常量的值都是相等的,我们应该将此两者视为相等,但,两者的hashcode,可能不相等,因为默认的实现是继承自Object,所以,这里我们改写了equals方法,比对各个条目的结果相等;但是放到前述容器中却不是这么回事,因为前述容器是根据的hashcode值来比较两个对象是否相等的。所以,你必须另外实现hashcode()方法,以保证在equals()方法返回true时,确保hashcode一致;复写boolean equals(Object),判断类型(instanceof)(判断null(NullPointerException)),类型不同则ClassCastException,再对比对象中各条目的值而不是hashCode.hashcode相同的equals一定为真,一般来说,hashCode是JAVA内约定的寻址方式,所以hashCode等,equals等。

  public int hashCode(){return Attribute1.hashCode() + Attribute1.hashCode()... +super.hashCode();}
  一般情况下可以将属性值计算在里面,缓存起来,待状态改变更新此值,这里的加入计算的属性越多,性能就越差,但是加入计算的属性越少,性能虽提高,但是hashcode的值域会变小,可能导致很多对象的hashcode相同。

  instanceof 可以判断是否是某接口或类的继承子类或实例。遵从继承/实现关系。可以多层判断。instanceof表达式的左边只可以是引用(不可以是基础类型,引用类型是final class时,只能和右边的类名称是同一class),右边只可以是类名称(不可以是引用).

String/序列化/IO
  String/Scanner/Pattern
  final class String是不可变对象,你只能通过改变引用改变。String abc = "abc";"abc"先放在JVM的对象池里,然后将abc引用指向过去。String的排序,大写字母的值小于小写。String只可以用""表示

  final class Scanner implements Iterator<String>; Java1.5开始提供,是一个文本扫描器,如该接口描述一般,hasNext() 测试是否有下一个迭代元素,next() 返回下一个迭代元素,hasNextBoolean/Float/Integer... nextBoolean/Float/Integer 基本同意思。不过就是返回型别与方法意思一样,要注意的是在使用nextXxx方法是,不会自动挑选下一个Xxx类型返回给你,而是按照扫描规则返回按规则迭代找到的Xxx类型的对象,如果这个返回的对象不是Xxx类型,则抛出异常.没有find()方法(这个方法是Pattern才有的),只有findInLine(Pattern).

  abstract class Calendar,日历类,提供add/set方法操作日历的详细内容,年份,日期...void setTime(Date d)/Date getTime()设置/获取日历时间,getInstance()/getInstance(Locale)/getInstance(TimeZone)/getInstance(TimeZone,Locale)取得日历实例;

  String.split(Pattern);正则 \d数字\D非数字 \s空白(空格缩进换行...)\S \w(单词字符[a-zA-z0-9_])\W非单词字符。^行头 $行尾 要注意正则的格式,错误则会抛出异常。
  Pattern p = Pattern.compile("a*b");
  Matcher m = p.matcher("aaaaab");
  boolean b = m.matches();
  boolean b = Pattern.matches("a*b", "aaaaab"); group()在匹配失败后使用,会抛出异常。

  StringBuilder sb = new StringBuilder();
  Formatter formatter = new Formatter(sb, Locale.US);//将格式化结果填充到StringBuilder/StringBuffer里去。看API.
  //java.util.Formatter f = new Formatter();
  //String s = f.toString();//返回格式化结果。
  formatter.format("%4$s %3$s %2$s %1$s %4$s %3$s %2$s %1$s","a", "b", "c", "d");// -> "d c b a d c b a"
  formatter.format("%s %s %s %s", "a", "b", "c", "d");// -> "a b c d"
  formatter.format("%s %<s %2s %s %s", "a", "b", "c", "d");// -> a a b c d
  formatter.format("%s %<s %2s %s %s", "a", "b", "c", "d");// -> a a b c d
  formatter.format("%s %<s %2s %s %s", "a", "b", "c", "d");// -> a a b c d
  formatter.format("%s %<s %<s %s %s", "a", "b", "c", "d");// -> a a a b c

  格式:%占位,紧跟数字,取参数索引位的字符串,如果没有数字,就按参数顺序取数字(从1开始算索引),并递增参数索引,%后面的数字,1就是第一个参数,和数组容器的offset不一样。<取上一位字符串再输出一次,$是紧跟数字只有的字符。
  %[argument_index$][flags][width][.precision]conversion
  基础abstarct class java.text.Format下的派生类abstarct class DateFormat/NumberFormat...不可以new ,只能getInstance().final class Locale只能getDefault/setDefault或者new Local("国家代码").不能setLocale();Format也不能setLocale();


  String StringBuffer 中的startoffset endoffset 里,只包含startoffset,不包含endoffset.如果endoffset大于length/size,则直接到最尾端。一般来说这两个类里的方法涉及到截取,删除,插入等offset操作的,都不会涉及到length。StringBuffer/StringBuilder继承equals方法,有substring

  性能 StringBuilder>StringBuffer>String ,StringBuilder用于多线程环境里,非线程安全,即,不同线程在访问该对象的append,insert 方法时会不按执行先后顺序执行,造成被插入的内容的错乱,而StringBuffer线程安全,API基本一致。Java1.5版开始提供StringBuilder.

序列化
  对象序列化主要是为了支持两种主要的特性,一是Java远程方法调用(RMI),另外一个是序列化Java Beans。序列化就是把对象的常量和对象类型写入到文件,但不写入方法和静态常量/方法.
  一般是按变量的写入先后顺序读入,先写则先读,文件的写入是向后的,但读取是从开头读的。序列化的目标对象中,包含了不可被序序列化的对象(未实现Serializable接口),则抛出异常。
  略过该对象的序列化操作,使用transient修饰,略过该对象的序列化操作,实例化的时会有一个你自己赋的值,但在反序列化的时候,只具有初始值,如int型为0。
  JAVA集合都实现了Serializable接口.
  Externalizable接口继承了Serializable接口,同时添加了writeExternal()和readExternal()
  private void writeObject(ObjectOutputStream stream) throws IOException
  private void readObject(ObjectInputStream stream)throws IOException, ClassNotFoundException

  FileOutputStream fos = new FileOutputStream(filename);
  ObjectOutputStream oos = new ObjectOutputStream(fos);
  oos.writeObject(obj);
  oos.close();
  fos.close();


IO
  Console console = System.Console;
  final java.io.Console;
  readLine,readPassword,Reader reader(),PrintWriter writer();
  readLine(String fmt, Object... args) ,格式化读取输入,会显示一个command命令提示,readLine全部读入,printf的格式是输出的格式.readPassword() 返回char[];这是JDK1.6才添加的类。

  java io 包 是用的包装器模式设计的,使用的时候组合使用;
  缓冲读取:
  BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filename));

  最基础的包为stream(abstratc InputStream/OutpuStream)/Reader/Writer.三者的实现类的方法基本类似,读取/标记/关闭/跳过/写入/刷出,要注意这些方法并不是直接从返回值得到读取的结果的。
  stream是按字节读取,Reader/Writer是按字符读取,专门用来读取UNICODE的。
  int available() 返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数。
  void close() 关闭此输入流并释放与该流关联的所有系统资源。
  void mark(int readlimit) 在此输入流中标记当前的位置。
  boolean markSupported() 测试此输入流是否支持 mark 和 reset 方法。
  abstract int read() 从输入流中读取数据的下一个字节。
  int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
  int read(byte[] b, int off, int len) 将输入流中最多 len 个数据字节读入 byte 数组。
  void reset() 将此流重新定位到最后一次对此输入流调用 mark 方法时的位置。
  long skip(long n) 跳过和丢弃此输入流中数据的 n 个字节。

  void close() 关闭此输出流并释放与此流有关的所有系统资源。
  void flush() 刷新此输出流并强制写出所有缓冲的输出字节。
  void write(byte[] b) 将 b.length 个字节从指定的 byte 数组写入此输出流。
  void write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
  abstract void write(int b) 将指定的字节写入此输出流。

  java.io.Console (implements java.io.Flushable)
  java.io.File (implements java.lang.Comparable, java.io.Serializable)

  java.io.InputStream (implements java.io.Closeable)
    java.io.ByteArrayInputStream
    java.io.FileInputStream
    java.io.FilterInputStream
    java.io.BufferedInputStream
    java.io.DataInputStream (implements java.io.DataInput)
    java.io.LineNumberInputStream
    java.io.PushbackInputStream
    java.io.ObjectInputStream (implements java.io.ObjectInput, java.io.ObjectStreamConstants)
    java.io.PipedInputStream
    java.io.SequenceInputStream
    java.io.StringBufferInputStream

  java.io.OutputStream (implements java.io.Closeable, java.io.Flushable)
    java.io.ByteArrayOutputStream
    java.io.FileOutputStream
    java.io.FilterOutputStream
    java.io.BufferedOutputStream
    java.io.DataOutputStream (implements java.io.DataOutput)
    java.io.PrintStream (implements java.lang.Appendable, java.io.Closeable)
    java.io.ObjectOutputStream (implements java.io.ObjectOutput, java.io.ObjectStreamConstants)
    java.io.PipedOutputStream

  java.io.Reader (implements java.io.Closeable, java.lang.Readable)
    java.io.BufferedReader
    java.io.LineNumberReader
    java.io.CharArrayReader
    java.io.FilterReader
    java.io.PushbackReader
    java.io.InputStreamReader
    java.io.FileReader
    java.io.PipedReader
    java.io.StringReader

  java.io.Writer (implements java.lang.Appendable, java.io.Closeable, java.io.Flushable)
    java.io.BufferedWriter
    java.io.CharArrayWriter
    java.io.FilterWriter
    java.io.OutputStreamWriter
    java.io.FileWriter
    java.io.PipedWriter
    java.io.PrintWriter
    java.io.StringWriter


Collection接口/Collections
  放入集合的对象都是放引用进去,基础类型的会放包装类型进去,放进去/拿出来的时候会隐式转换类型。从一个集合把元素放到另一个集合内,并不一定保证元素的顺序不变。
  Collections.asList(List l)/.sort(List l)/.reverse(List l)/.swap(List l,int fromoffset,int tooffset);中list内的所有元素都必须实现Comparable/Comparator接口.

  集合可以排序/插入/交换,丰富多样的操作方法,不支持原生类型(int,double...,可以用包装类),速度慢点,数组支持原生类型,但没有丰富的操作方法;Collection 是容器的公用接口(Map除外) Collections 是一个操作实现Collection接口(Set/List/Queue..)的类的公用静态方法的集合。非常有用,提供了操作实现了Collection接口的容器公用方法。

  大多数的集合都实现了Collection接口,它基本方法是boolean add(T)(没有get()方法,但如ArryList等可能会实现),还有一些常用的方法如iterator(),size(),toArray()(注:toArray()是返回一对象----object数组,而Arrays----也是java.util下的一个类,还有一个toArray(T[] a)方法,返回T类型的数组,有一个asList方法它们通常认为是各集合之间转换的桥梁).

  Arrays类与Collections类提供了一些相似的方法来操纵数组,但远未及后者提供的方法丰富多样,只有比较(equals)/hash(hashCode)/排序(order)/复制(copyOf/copyOfRange)/搜索(binarySearch)/填充(fill)等几个简单方法.


  Map(映射)
  Map接口跟Collection接口实际上没有半点关系,也就是Map并不是继承自Collection.集合中的每一个元素都包含一对键对对象和值对象,集合中没有重复的键对象,值对象可以重复。它的有些实现类能对集合中的键对象进行排序。与Collection截然不同的是,它其中所存取的是一些值与名相对应的数据。也就是一个Key对应一个Value的方式来存储。所以它就有与之对应的一些方法如:put (K key, V value)
  HashMap,主要特点是存放的一个键值对,一些有用的方法是可返回视图(我觉得可以把它理解为一个集合)如:keyset(),values(),entyset()等。
  HashMap是由键值对组成的,关于HashMap有二点要注意:1. 它的键只能是一个Object对象。 2. 当二个HashMap用equals方法比较时,实际的比较是它的Key,而与Value无关。
  TreeMap,它与HashMap差不多,不过是增加了对元素的排序功能,所以运行速度也就当然没有hashmap来得快了。
  Properties,继承于hashtable。

  List(列表)
  集合中的对象按索引位置排序,可以有重复对象,允许按照对象在集合中的索引位置检索对象.
  add remove clear 元素可重复,但是按顺序放入的.可重复的Set;
  Arrylist ,它是List接口的实现类,而List则是继承于Collection.
  LinkedList,它也是间接对Colections的实现。用linkedlist的一些方法如addfirst(),removefirst(),addlast()等等可以用来实现如C中的堆栈,链表。对于频繁使用插入与删除操作使用linkedlist是个不错的选择,对于经常进行索引操作则arrylist较好。

  Set(集)
  集合中的对象中按特定的方式排序,并且没有重复对象。它的有些实现类能对集合中的对象按特定的方式排序.

  add 方法调用了 equals 方法,保证元素不重复,remove也调用了equals,先equals,然后根据具体的值是否相等决定是否放入删除,不是单纯的操作hashCode;

  HashSet(散列集合),它实现了Set接口,也就意味着它的元素不能有重复值出现,且不保证元素的顺序,即使是按顺序添加的。并且在HashSet中没有get()方法,但可以通过iterator()来实现。要注意的是假如要在HasSet中存放一些对象,那么你得重定义hashCode()与equals()二个方法来保不可以存放相同的内容的元素。对于hashcode()所返回的值,hashset用它来计算(通过特定的函数)该对象在内存中的存放位置;后者主要用来判断二个对象的内容是否相等而返回对应的boolen型。LinkedHashSet比HashSet快,因为HashSet维护一个双重链表,负责支持数据的插入顺序。
  TreeSet,主要用来对元素进行排序操作,假如要往其中添加对象,则对象得实现Comparable接口。假如不要对元素排序,则一般可选用HashSet,TreeSet调用的是元素实例的int compareTo()(返回负数0整数)来比较大小并进行排序。

  NavigableSet extends SortedSet;
  class TreeSet extends AbstractSet implements NavigableSet subset(from,true,to,true);
  TreeSet subset返回的Set和原Set是联动的,在子Set中的操作,会反映到原Set中,意思就是,此Set仅仅是一个丈量元素范围的标尺,任何操作都要遵守两个Set的设置,显然,原Set的元素大小范围等也将限制这个Set的行为,否则会抛异常,使用subset的时候要注意,如果Set中你的fromElement排在后面,而toElement排在最前,且你给了一个反转顺序的构造参数来构造这个Set,那么将会抛出异常,因为他按照你给的反方向取不到元素。
  迭代器:Iterator
  它是一个接口,只有三个方法hasnext(),next(),remove();remove()是可选(在实现的时候)。其可选性也意味着它的实现类中,remove方法是可有可无的。比如List,只实现了List.remove(offset)/remove(Objuect);
  其他
ArrayList/HashSet/HashMap/LinkList/TreeSet/TreeMap/LinkedHashSet非线程安全.
Hashtable/Vector 线程安全,要产生线程安全的容器,应该借由Collections的方法产生。
Queue 序列 实现Collection接口,在1.5版是用LinkList实现的。PriorityQueue 是一个自排序的队列。如果设定size,add会跳unchecked warning,poll找到头一个队列元素并移除,不移除则用peek,无则null

  多线程
  多线程extends Thread或implements Runnable。
  synchronized 给对象或方法、代码段加锁;继承自Thread需要把具体的执行内容写到start里,也可以写到void run里,都会执行的.
  实现Runnable需要在run()里写具体的执行内容;接口Runnable只有一个声明的public void run函数;suspend()/resume()/stop()过时,sleep(),参数是毫秒,需要捕捉异常.
  static void yield() 建议当前正在执行的线程让出CPU,让等待的高优先级线程执行。如果没有合适的线程执行则这个恢复执行.
  static void wait()/notify()/notifyAll() 只能在synchronized同步化的代码里引用,否则会抛出非法监视器异常。线程不保证马上执行,所以结果顺序不确定。状态改变函数冲突会带来异常,如start(),已经调用则不得再调用;static void sleep/wait,是让线程休眠,但是接受了休眠的线程的,被中断(static void interrupted())要抛异常InterrruptException的。sleep/wait的执行都会导致线程暂停执行,sleep则只是让当前正在执行的线程暂停执行,而wait会在notify执行之后才继续执行,这里的暂停执行,均为sleep/wait行暂停执行之后的代码,在收到立即执行的信号,后面的代码才会继续执行。
  要注意,已启动的线程未必就是当前执行线程。
  Thread是Runnable的实现类,用Runnable实现类作为Thread的构造函数,是在Thread执行start的时候代为调用Runnable的run(),如果是继承Thread则会自动调用Thread.run()。
  synchronized只针对同一个实例有效,不同的实例的同样的synchronized方法是可以同时执行的。同样的,多线程只是在同一实例内有效。

  注意用词,class safe(类安全)/thread safe(线程安全)/block safe(块安全)/execute safe(执行安全).

  JDK1.5添加了并发包,java.util.concurrent ,其中比较重要的是Lock/Executor/ExecutorService/Queue


开发

  断言
  断言是1.4版添加的。断言的比较条件为false 则 扔出断言提示异常。断言也是一种异常信息,他不会被catch,但一般在try/catch/finally代码段的最后才抛出。
  class AssertionError 可以直接实例化抛出.
  throw new AssertionError();
  class AssertionError extends Error;
  class Error extends Throwable;
  javac -source 1.5 H.java;
  java -enableassertions H;
  -enableassertions=-ea;
  -da=-disableassertions;
  系统启用 -esa/-dsa;

  注意javac 编译只能指定版本,然后会根据版本将断言代码编译进去,运行时的java命令启用或禁用断言,所以,编译一定可以通过.
  编译/执行
  java -Dpropertys.property Loader System.getProperty("propertys.property");
  java -Dperp="perporty1" Sys
  System.getPropertys.getProperty("propertys.property");

  Java Javac 命令行使用方法
  -cp=-classpath;如果引用了其他的class,编译时是需要加入classpath才能编译的
  cd 工作目录(以后整个工程文件都在该目录下,引用的时候前面不得加斜杠/反斜杠);javac只根据package构建目录结构,不根据文件路径构建目录结构。
  java -classpath c:\classes;c:\jar\abc.jar;c:\zip\abc.zip;c:\src abc
  javac -d classes -classpath lib\*.jar src\com\xx\Main.java -target 1.5;
  javac -sourcepath src src\*.java -d classes -classpath classes:lib\mylib.jar;lib\mylib1.jar;lib\mylib2.jar
  classpath引用的jar文件的文件名必须以.jar为后缀(扩展名)。


  程序的执行流程
  先将class读入Class内存区,static/final按定义顺序初始化,包括静态代码段,这样,就构成了一个实例的基础对象原型
  JVM代为调用static void main(String args[])函数执行。然后就开始了程序的执行,再通过调用构造函数,根据继承等上下文结构构建对象,分配新的内存区,完成对象实例化构建。
  出错则抛出ExceptionInInitializerError错误;
  方法传值/传引用
  java方法中基础类型传值时是传递的值的复制,即复制引用指向的值的那一块内存传递给方法,对象类型传引用传递的是引用的复制,传引用是复制一个引用传递给方法,即调用方法时,作为参数的引用和方法参数的引用是同时指向一个实例的,具体测试不见内存增长。
  容器中,Map List Set 都是存的引用进去的。所以要注意存进去对象后再修改对象,下次取出对象时,其实是取引用然后找到这个对象,然而,该对象已被修改过。


打包
  jar {ctxu}[vfm0M] [jar-文件] [manifest-文件] [-C 目录] 文件名 ...

  其中 {ctxu} 是 jar 命令的子命令,每次 jar 命令只能包含 ctxu 中的一个,它们分别表示:

  -c 创建新的 JAR 文件包
  -t 列出 JAR 文件包的内容列表
  -x 展开 JAR 文件包的指定文件或者所有文件
  -u 更新已存在的 JAR 文件包 (添加文件到 JAR 文件包中)

  [vfm0M] 中的选项可以任选,也可以不选,它们是 jar 命令的选项参数

  -v 生成详细报告并打印到标准输出
  -f 指定 JAR 文件名,通常这个参数是必须的
  -m 指定需要包含的 MANIFEST 清单文件
  -0 只存储,不压缩,这样产生的 JAR 文件包会比不用该参数产生的体积大,但速度更快
  -M 不产生所有项的清单(MANIFEST〕文件,此参数会忽略 -m 参数

  [jar-文件] 即需要生成、查看、更新或者解开的 JAR 文件包,它是 -f 参数的附属参数 ,文件名必须打包为.jar文件。
  [-C 目录] 表示转到指定目录下去执行这个 jar 命令的操作。它相当于先使用 cd 命令转该目录下再执行不带 -C 参数的 jar 命令,它只能在创建和更新 JAR 文件包的时候可用。
  jar cvfm test.jar manifest.mf test
  java -classpath myapplication.jar com.example.myapp.MyAppMain
  java -jar myapplication.jar
  java -classpath mail.jar:activation.jar -jar myapplication.jar

  [manifest-文件] 即 MANIFEST 清单文件,它是 -m 参数的附属参数
  Manifest for myapplicationlib.jar:
  Manifest-Version: 1.0
  Created-By: JDJ example
  Class-Path: mail.jar activation.jar

  Manifest for myappconsole.jar:
  Manifest-Version: 1.0
  Created-By: JDJ example
  Class-Path: myapplicationlib.jar
  Main-Class: com.example.myapp.MyAppMain

  Manifest for myappadmin.jar:
  Manifest-Version: 1.0
  Created-By: JDJ example
  Class-Path: myapplicationlib.jar
  Main-Class: com.example.myapp.MyAdminTool

  在manifest文件中,可以为每个package定义描述和实现版本,声明名字,并加入描述属性和实现属性,这些属性是
  Specification-Title
  Specification-Version
  Specification-Vendor
  Implementation-Title
  Implementation-Version
  Implementation-Vendor

  当要提供一个类库或编程接口时,描述信息显得是很重要,见以下范例:
  Manifest-Version: 1.0
  Created-By: JDJ example
  Class-Path: mail.jar activation.jar
  Name: com/example/myapp/
  Specification-Title: MyApp
  Specification-Version: 2.4
  Specification-Vendor: example.com
  Implementation-Title: com.example.myapp
  Implementation-Version: 2002-03-05-A
  Implementation-Vendor: example.com
  注释/Javadoc
  /** */为javadoc能够提取的内容。/* */是不能被提取的。提取出来的内容是HTML格式的,所以可以在这个内容中写入HTML代码。
  @author 对类的说明 标明开发该类模块的作者
  @version 对类的说明 标明该类模块的版本
  @see 对类、属性、方法的说明 参考转向,也就是相关主题
  @param 对方法的说明 对方法中某参数的说明
  @return 对方法的说明 对方法返回值的说明
  @exception 对方法的说明 对方法可能抛出的异常进行说明
  public class TestJavaDoc {
    /**
     * @param n a switch
     * @param b excrescent parameter
     * @return true or false

     * @return excrescent return

     * @exception java.lang.Exception throw when switch is 1
     * @exception NullPointerException throw when parameter n is null
    */
    public boolean fun(Integer n) throws Exception {
      switch (n.intValue()) {
        case 0:
        break;
        case 1:
        throw new Exception("Test Only");
        default:
        return false;
      }
      return true;
    }
  }


  上面是说的JAVADOC中的注释,而下面说的是JDK的注释语法。
  JAVA已经提供的注释类型:
  public @interface Target//应用到程序元素的种类,比如函数,参数等,参见ElementType
  public @interface Retention//指示注释类型的注释要保留多久。参见RetentionPolicy

  public @interface Inherited//指示注释类型被自动继承。
  java.lang.annotation.ElementType描述了注释的类型
  public enum ElementType extends Enum{
    ANNOTATION_TYPE 注释类型声明
    CONSTRUCTOR 构造方法声明
    FIELD 字段声明(包括枚举常量)
    LOCAL_VARIABLE 局部变量声明
    METHOD 方法声明
    PACKAGE 包声明
    PARAMETER 参数声明
    TYPE 类、接口(包括注释类型)或枚举声明
  }

  java.lang.annotation.RetentionPolicy描述了注释使用的位置
  public enum RetentionPolicy{
    CLASS 编译器将把注释记录在类文件中,但在运行时 VM 不需要保留注释。
    RUNTIME 编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取。
    SOURCE 编译器要丢弃的注释。
  }

  上述是JAVA自身定义的注解类型,自己也可以定义注解类型。

  注解类型的定义跟接口定义相似,只是需要在inteface关键字前面加一个at符(@),声明一个方法即为注解类型定义一个元素。方法声明时不允许有参数或throw语句,返回值类型被限定为原始数据类型、字符串String、Class、枚举enums、注解类型,或前面这些的数组,方法可以有默认值,基础类型的包装类不可以。
  public @interface RequestForEnhancement {
    int id();
    String synopsis();
    String engineer() default "[unassigned]";//可以给定默认值
    String date() default "[unimplemented]";
  }
  调用(调用的时候,如果包含多个元素,应该指出多个元素)
  @RequestForEnhancement( id = 2868724, synopsis = "Enable time-travel", engineer = "Mr. Peabody", date = "4/1/3007" )
  public static void travelThroughTime(Date destination) { ... }

  如果注解只包含一个元素,那么只能是value(),如果有多个,可以包含value()
  public @interface Annot{
    String value();
  }
  使用:
  public class Exm{
    @Annot(value="这是一个注解")
    //@Annot("这是一个注解") //注意,当且仅注解类中只包含一个String value()的注解条目是,才可以简写成这样.
    public void main(){/*some codes*/}
  }
  定义了注解的类,可以用反射得到注解的详细内容。一般是先得到方法,变量在分析其注解。

posted on 2012-02-23 23:24  過眼云煙  阅读(362)  评论(0)    收藏  举报

导航