JAVA杂项

1.  变量是否会自动初始化?

    在类定义的field,String的值被赋予NULL,int被赋予0。而在方法内的String,int没有手动赋值会报编译错误The local variable s2 may not have been initialized。

 

class Test{
       int a; //a==0,不论是否为static
       String s;//s == null,不论是否为static;
       void func(){
             String s2;
             s2.length();//报错,提示未初始化
             s2 == null;//报错,提示未初始化
       }
}

 

 2. return 语句不要放到finally

 

因为如果抛出一个runtimeException,而catch没有捕获的话,那么finally中 return值会导致runtimeException被忽略!

 

 

static int func(){
		int a =1;
		try{
			if(1 ==a ){
				throw new RuntimeException();
			}
			throw new IOException();
		}catch(IOException e){
			
		}finally{
			return 1;
		}
}

public static void main(String[] args){
              System.out.println(func());//print 1; no exception throws out
}

 3. 一个类不依赖容器是很重要的。

 

曾经遇到过一个类,不能直接new ,需要在启动了Tomcat之后的环境里面才能new,导致非常难测试。到后面,还导致无法编写提速开发的工具类(自动生成配置和文件等)!因为需要使用反射执行某些方法获取结果,但是那个方法不是静态方法,需要实例化对象!所以以后我会要求一些类做到与servletContext,httpServlet无关,可以在脱离中间件情况直接newInstance!这里我进一步理解了为什么struts2为什么要与中间件API解耦合了

 

 4. 正则表达式的贪婪匹配,勉强匹配,全占匹配。

     例如有字符串 "{name} lives in {village} , which is far from the company."

     正则表达式 "\\{.*\\}"     会匹配到 "{name} lives in {village } ",这是贪婪匹配. 

     正则表达式 "\\{.*?\\}"   会匹配到 "{name}"和"{ village }",这是勉强匹配.

     正则表达式 "\\{.*+\\}"   没有匹配项,这是possessive匹配(不知道怎么翻译了,理解成”全占匹配“也可以吧)。

 



 
      算法
      3种匹配的匹配算法不一样。

      约定概念:

目标字符串a:被匹配的字符串,如上面所说的"{name} lives in {village} , which is far from the company."

        读入字符串r:算法中需要用到的临时字符串变量

      a. 贪婪匹配

          1) 将整个目标字符串r读入,使读入字符串r=a,如果能够匹配,则返回SUCCESS,退出,否则转入2)。

          2) 如果r的长度>=1:

去掉r末尾的一个字符,如果剩余的字符串能够匹配,则返回SUCESS,退出,否则转入2)。

              如果此时r的长度为0:

如果""能匹配正则表达式,则返回SUCCESS,退出,否则返回FAIL,退出。

          3) 如果1),2)之后返回SUCCESS,那么去掉a中的r部分,使 a = a - r, r = "",重新进入1)

       b. 勉强匹配

  与贪婪相反

  这里为了表达清晰,在引入一个临时变量b

          1)将读入字符串r置为"",使b=a, 如果空字符串能够匹配,则返回SUCESS,退出,否则转入2)

          2)如果r的长度<a的长度

b移出开始的字符,放入r的末尾,如果r能够匹配,返回SUCESS,退出,否则转入2)。

     如果r的长度==a的长度

如果r能够匹配,返回SUCESS,退出,否则返回FAILURE,退出

          3)如果1),2)之后返回SUCCESS,那么去掉a中的r部分,使 a = a - r, r = "",b="",重新进入1)

       c.全占匹配

  这个很简单,相当于贪婪的第一步

  1)将整个目标字符串r读入,使读入字符串r=a,如果能够匹配,则返回SUCCESS,退出,否则返回FAILURE。

  对于全占匹配,它只匹配一次——整个匹配,就像名字一样。个人觉得它没有什么存在的意义,贪婪正则表达式使用^...$不就可以取代全站匹配了吗?

 

Differences Among Greedy, Reluctant, and Possessive Quantifiers

[QUOTE from http://docs.oracle.com/javase/tutorial/essential/regex/quant.html ]

There are subtle differences among greedy, reluctant, and possessive quantifiers.

Greedy quantifiers are considered "greedy" because they force the matcher to read in, or eat, the entire input string prior to attempting the first match. If the first match attempt (the entire input string) fails, the matcher backs off the input string by one character and tries again, repeating the process until a match is found or there are no more characters left to back off from. Depending on the quantifier used in the expression, the last thing it will try matching against is 1 or 0 characters.

The reluctant quantifiers, however, take the opposite approach: They start at the beginning of the input string, then reluctantly eat one character at a time looking for a match. The last thing they try is the entire input string.

Finally, the possessive quantifiers always eat the entire input string, trying once (and only once) for a match. Unlike the greedy quantifiers, possessive quantifiers never back off, even if doing so would allow the overall match to succeed.

  按照官方文档的说法,贪婪是从右往左吃,勉强是从左往右吃,全占是一次吃完!

 5. Class.getResource和ClassLoader.getResource的相对路径的基础路径不一样

 

前者是class本身所在的路径,而后者是classpath。前者有绝对路径概念,后者只有相对路径概念。

 

 


 

package lgc.tools.struts2;
public class ConventionUtil {
        public static void p(Object o){
		System.out.println(o);
	}
        public static void main(String[] args) throws Exception{
		
		//获取类的所在的路径
		p(ConventionUtil.class.getResource(""));//file:/E:/workspace/m4/web/WEB-INF/classes/lgc/tools/struts2/
		//获取类的所在的根路径,即排除package的路径,或者说是classpath
		p(ConventionUtil.class.getResource("/"));//file:/E:/workspace/m4/web/WEB-INF/classes/
		//以类的所在的路径为基础路径,查找相对于基础路径的jsp_template.jsp文件
		p(ConventionUtil.class.getResource("jsp_template.jsp"));//file:/E:/workspace/m4/web/WEB-INF/classes/lgc/tools/struts2/jsp_template.jsp
		//通过绝对路径查找jsp_template.jsp文件
		p(ConventionUtil.class.getResource("/lgc/tools/struts2/jsp_template.jsp"));//file:/E:/workspace/m4/web/WEB-INF/classes/lgc/tools/struts2/jsp_template.jsp
		
		//classpath路径
		p(ConventionUtil.class.getClassLoader().getResource(""));//file:/E:/workspace/m4/web/WEB-INF/classes/
		//没有绝对路径概念
		p(ConventionUtil.class.getClassLoader().getResource("/"));//null
		//相对于classpath路径查找jsp_template.jsp文件
		p(ConventionUtil.class.getClassLoader().getResource("lgc/tools/struts2/jsp_template.jsp"));//file:/E:/workspace/m4/web/WEB-INF/classes/lgc/tools/struts2/jsp_template.jsp
		

	}
	
}

 

6. String replace与replaceAll, 不能见名知意

1) replace(CharSequence a, CharSequence b)

是直接全文搜索,直接将所有与第一个参数相等的字符串替换为第二个参数

 

public String replace(CharSequence target, CharSequence replacement) {
        return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
            this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
    }

 

 

2) replaceAll(regex,replacement)的第一个参数是正则表达式,所有符合正则表达式的子字符串都被替换成replacement,replacement也不是普通字符串,它可以以$0~$8方式引用被匹配字符串的分组,如果你期望replacement是个普通字符串,必须对replacement中的$和\进行转义,以免被JAVA误认为引用分组,转义方法如下

replacement = replacement.replaceAll("\\\\","\\\\\\\\").replaceAll("\\$", "\\\\\\$");

 

 public String replaceAll(String regex, String replacement) {
	return Pattern.compile(regex).matcher(this).replaceAll(replacement);
    }

 

 

7 encoding

 

字符集(Charset):是一个系统支持的所有抽象字符的集合。字符是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。
字符编码(Character Encoding):是一套法则,使用该法则能够对自然语言的字符的一个集合,与数字进行配对。

UTF-8UTF-16GKBISO88559-1都是字符编码,它们0-127配对的字符都是一致的,除此之外配对的规则都不一样,这是产生乱码的根本原因。

 

 

例如"中文"这两个字的UTF-8编码是E4 B8 AD E6 96 87,那么当使用UTF-8去解析数字E4 B8 AD E6 96 87的话,会得出“中文”两个字,但是当使用GBK去解析的话,就成了乱码,因为数字E4 B8 AD E6 96 87在GBK的编码规则中代表的字符和UTF-8是不一样的,通常会匹配到一些极少见的字符,这就造成了所谓的乱码现象。

 

java字符串是以UTF-16的编码方式保存在内存中。getBytes("")和new String(byte[],"")是最常用的转码API。

 

 

byte[] a = str.getBytes("charsetName")会将内存中的字符串由UTF-16编码转换为charsetName的编码格式,存放在数组a中。

 

String s = new String (byte[] ,"charsetName");是getBytes的逆过程,由charsetName编码转换为UTF-16,保存在内存中。

 

后面我打算写篇博文详细讲讲编码,这里就主要说个原理吧。避免字符串乱码在不同的机器节点传输一个极其有效的方法是将中文字符转换\uxxxx的形式,那么字符串完全变成了unicode的形式(只是unicode形式,unicode是字符集,不是编码!),只包含编码为0-127的字符,不可能出现乱码问题。这带来的代价是,

代价1 需要在发送方将在字符串变成unicode形式

例如要发送 "abc中文",首先转换成abc\u4e2d\u6587,再发送。调用toUnicodeString("abc中文")即可转换为abc\u4e2d\u6587

public static String toUnicodeString(String s){
    	StringBuilder strb = new StringBuilder();
    	char[] c = s.toCharArray();
    	for(int i=0,len=c.length;i<len;i++){
	    	if(c[i] < 128){
	    		strb.append(c[i]);
	    	}else{
	    		strb.append("\\u");
	    		String hex = Integer.toHexString(c[i]);
	    		
	    		if(hex.length()<4){
	    			int padLen = 4-hex.length();//需要左补齐的0
	    			for(int k=0;k<padLen;k++){
	    				hex = "0" + hex;
	    			}
	    		}
	    		strb.append(hex);
	    	}
    	}
    	return strb.toString();
    }

 

代价2 需要在接受方法将unicode形式的字符串转换成原来的字符串

         例如收到abc\u4e2d\u6587,就转换为"abc中文"。调用parseUnicodeString("abc\u4e2d\u6587")即可转换为"abc中文"。

 

 

public static String parseUnicodeString(String s){
    	StringBuilder rtn = new StringBuilder();
    	for(int i=0,len=s.length();i<len;i++){
    		char c = s.charAt(i);
    		if(c=='\\'){
    			i++;
    			char c1 = s.charAt(i);
    			if(c1 == 'u'){
    				try{
    					char c_chinese = (char) Integer.parseInt(s.substring(i+1,i+1+4),16);
    					rtn.append(c_chinese);
    					i += 4;
    				}catch(Exception e){
    					throw new RuntimeException("parseUnicodeString出错",e);
    				}
    			}else{
    				rtn.append(c).append(c1);
    			}
    		}else{
    			rtn.append(c);
    		}
    	}
    	return rtn.toString();
    }

   备注:以上两个方法已经经历实际环境磨练,暂时没有错误。理解上面的两个方法,首先要理解好,JAVA的一个字符的在内存中的保存,是使用两个BYTE存放一个字符UTF-16编码(如果不够则使用4个BYTE,大部分情况2个BYTE)。

 

 

8 禁止直接访问JSP小技巧

如果不想浏览器能直接访问jsp文件,可以考虑把jsp文件都放到WEB-INF下,然后在中间层某个类(servlet,action等)使用request,getRequestDispatcher转向访问。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值