分类 Java 下的文章

由一个漏洞引发的思考

最近项目比较紧,没时间来写博客,意味着思考少了。两件事:
1、struts2最近爆出了一个漏洞,线上版本必须修补。但是线上的版本源代码已经不可恢复了,新版本代码又没开发、测试完成,代码版本管理没有做好,即使是自己一个人做项目也应该管理好代码。版本控制非常重要,虽说上一个版本不一定好!
2、新版程序发现一个问题,这个问题是在开发过程中很容易忽略掉的,而且必须放在并发访问中才能出现的问题。另外,由于日志输出没做好,导致调试很费劲。简单描述下该问题:
三句话:
1)从连接池获取一个连接
2)将字符型数字转换为整型数字
3)获取连接,查询数据,释放连接
问题出现在第二句话上,当第二句转换失败抛异常时,并未被处理,而是直接抛回给上层调用方法,所以,连接更本就不会被释放掉,就出现了资源枯竭,死锁的状态。
总结下:
真应该好好学习java啊  多线程  异常处理和异常处理对于程序的影响  程序执行流程  动态代理  线程池死锁 还有什么链接生成、关闭  如何查看resin堆栈信息 
先把异常处理 和 适当输出日志 弄好,这个是靠经验的!
 

400 Bad Request-Url特殊字符编码

今天碰到一个诡异的问题,线上系统没有问题可以访问,但在本机测试却不行。现象是在火狐访问空白页面,当时就蒙了啊,后来换ie则提示:400 Bad Request。
原来url中不允许出现特殊字符,比如:%
修改前url:http://index.youku.com/vr_keyword/id_6ZOB6KGA5L2%2f5ZG9&type=alldata
修改后url:http://index.youku.com/vr_keyword/id_6ZOB6KGA5L2%2f5ZG9&type=alldata
修改前代码:

var reg = new RegExp("\+", "g"); // 创建正则RegExp对象
var reg2 = new RegExp("\=", "g");
var reg3 = new RegExp("\/", "g");
basewords = basewords.replace(reg, "%2b");
basewords = basewords.replace(reg2, "%3d");
basewords = basewords.replace(reg3, "%2f");

修改后的代码:

var reg = new RegExp("\+", "g"); // 创建正则RegExp对象
var reg2 = new RegExp("\=", "g");
var reg3 = new RegExp("\/", "g");
basewords = basewords.replace(reg, "%252b");
basewords = basewords.replace(reg2, "%253d");
basewords = basewords.replace(reg3, "%252f");

PS:修改前url在线上是没有问题的,使用的resin服务器,但本机开发的tomcat却无法通过!估计处理机制有区别!
有些符号在URL中是不能直接传递的,如果要在URL中传递这些特殊符号。 编码的格式为:%加对应字符的ASCII(16进制)码值。例如:空格的编码值是”%20″ 。
一些URL特殊符号及编码(十六进制值)
1. + URL中+号表示空格 %2B
2. 空格 URL中的空格可以用+号或者编码 %20
3. / 分隔目录和子目录 %2F
4. ? 分隔实际的 URL 和参数 %3F
5. % 指定特殊字符 %25
6. # 表示书签 %23
7. & URL中指定的参数间的分隔符 %26
8. = URL中指定参数的值 %3D
java中URL的编码和解码函数: java.net.URLEncoder.encode(String s) java.net.URLDecoder.decode(String s);
javascript中URL的编码和解码函数: escape(String s) unescape(String s) ;
更详细资料:
http://www.cnblogs.com/leaven/archive/2012/07/12/2588746.html

检测java方法执行时间

在方法开始处加上第一句
long a = System.currentTimeMillis();
在要监测的点加上下面这句:
System.out.println(“监测的时间:”+(System.currentTimeMillis()-a)/1000f+” 秒 “);
就OK了!

Base64编码

按照RFC2045的定义,Base64被定义为:Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式。(The Base64 Content-Transfer-Encoding is designed to represent arbitrary sequences of octets in a form that need not be humanly readable.)
Base64是一种基于64个可打印字符来表示二进制数据的表示方法。由于2的6次方等于64,所以每6个位为一个单元,对应某个可打印字符。三个字节有24个位,对应于4个Base64单元,即3个字节需要用4个可打印字符来表示。它可用来作为电子邮件的传输编码。在Base64中的可打印字符包括字母A-Z、a-z、数字0-9 ,这样共有62个字符,此外两个可打印符号在不同的系统中而不同。一些如uuencode的其他编码方法,和之后binhex的版本使用不同的64字符集来代表6个二进制数字,但是它们不叫Base64。
Base64常用于在通常处理文本数据的场合,表示、传输、存储一些二进制数据。包括MIME的email,email via MIME, 在XML中存储复杂数据.
在MIME格式的电子邮件中,base64可以用来将binary的字节序列数据编码成ASCII字符序列构成的文本。使用时,在传输编码方式中指定base64。使用的字符包括大小写字母各26个,加上10个数字,和加号「+」,斜杠「/」,一共64个字符,等号「=」用来作为后缀用途。
完整的base64定义可见 RFC 1421和 RFC 2045。编码后的数据比原始数据略长,为原来的。在电子邮件中,根据RFC 822规定,每76个字符,还需要加上一个回车换行。可以估算编码后数据长度大约为原长的135.1%。
转换的时候,将三个byte的数据,先后放入一个24bit的缓冲区中,先来的byte占高位。数据不足3byte的话,于缓冲区中剩下的bit用0补足。然后,每次取出6(因为)个bit,按照其值选择ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/中的字符作为编码后的输出。不断进行,直到全部输入数据转换完成。
如果最后剩下两个输入数据,在编码结果后加1个「=」;如果最后剩下一个输入数据,编码结果后加2个「=」;如果没有剩下任何数据,就什么都不要加,这样才可以保证数据还原的正确性。
如果要编码的字节数不能被3整除,最后会多出1个或2个字节,那么可以使用下面的方法进行处理:先使用0字节值在末尾补足,使其能够被3整除,然后再进行base64的编码。在编码后的base64文本后加上一个或两个’=’号,代表补足的字节数。也就是说,当最后剩余一个八位字节(一个byte)时,最后一个6位的base64字节块有四位是0值,最后附加上两个等号;如果最后剩余两个八位字节(2个byte)时,最后一个6位的base字节块有两位是0值,最后附加一个等号。
在URL中的应用:
Base64编码可用于在HTTP环境下传递较长的标识信息。例如,在Java持久化系统Hibernate中,就采用了Base64来将一个较长的唯一标识符(一般为128-bit的UUID)编码为一个字符串,用作HTTP表单和HTTP GET URL中的参数。在其他应用程序中,也常常需要把二进制数据编码为适合放在URL(包括隐藏表单域)中的形式。此时,采用Base64编码不仅比较简短,同时也具有不可读性,即所编码的数据不会被人用肉眼所直接看到。
然而,标准的Base64并不适合直接放在URL里传输,因为URL编码器会把标准Base64中的「/」和「+」字符变为形如「%XX」的形式,而这些「%」号在存入数据库时还需要再进行转换,因为ANSI SQL中已将「%」号用作通配符。
为解决此问题,可采用一种用于URL的改进Base64编码,它不在末尾填充’=’号,并将标准Base64中的「+」和「/」分别改成了「-」和「_」,这样就免去了在URL编解码和数据库存储时所要作的转换,避免了编码信息长度在此过程中的增加,并统一了数据库、表单等处对象标识符的格式。
另有一种用于正则表达式的改进Base64变种,它将「+」和「/」改成了「!」和「-」,因为「+」,「*」以及前面在IRCu中用到的「[」和「]」在正则表达式中都可能具有特殊含义。
此外还有一些变种,它们将「+/」改为「_-」或「._」(用作编程语言中的标识符名称)或「.-」(用于XML中的Nmtoken)甚至「_:」(用于XML中的Name)。
其他应用:
Mozilla Thunderbird和Evolution用Base64来保密电子邮件密码
Base64 也会经常用作一个简单的“加密”来保护某些数据,而真正的加密通常都比较繁琐。
垃圾讯息传播者用Base64来避过反垃圾邮件工具,因为那些工具通常都不会翻译Base64的讯息。
在LDIF档案,Base64用作编码字符串。
在设计BASE64编码的时候,我想设计人员最主要考虑了3个问题:
1.是否加密?
2.加密算法复杂程度和效率
3.如何处理传输?
加密是肯定的,但是加密的目的不是让用户发送非常安全的Email。这种加密方式主要就是“防君子不防小人”。即达到一眼望去完全看不出内容即可。
基于这个目的加密算法的复杂程度和效率也就不能太大和太低。和上一个理由类似,MIME协议等用于发送Email的协议解决的是如何收发Email,而并不是如何安全的收发Email。因此算法的复杂程度要小,效率要高,否则因为发送Email而大量占用资源,路就有点走歪了。
但是,如果是基于以上两点,那么我们使用最简单的恺撒法即可,为什么Base64看起来要比恺撒法复杂呢?这是因为在Email的传送过程中,由于历史原因,Email只被允许传送ASCII字符,即一个8位字节的低7位。因此,如果您发送了一封带有非ASCII字符(即字节的最高位是1)的Email通过有“历史问题”的网关时就可能会出现问题。网关可能会把最高位置为0!很明显,问题就这样产生了!因此,为了能够正常的传送Email,这个问题就必须考虑!所以,单单靠改变字母的位置的恺撒之类的方案也就不行了。关于这一点可以参考RFC2046。
基于以上的一些主要原因产生了Base64编码。

Java BASE64加密解密

Java BASE64加密解密 sun.misc包是Sun公司提供给内部使用的专用API,不建议使用。另外apache已经实现了一套:
参考org.apache.commons.codec.binary.Base64
下载地址:http://commons.apache.org/codec/download_codec.cgi

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
/**
 * BASE64加密解密
 */
public class BASE64
{
    /**
     * BASE64解密
   * @param key
     * @return
     * @throws Exception
     */
    public static byte[] decryptBASE64(String key) throws Exception {
        return (new BASE64Decoder()).decodeBuffer(key);
    }
    /**
     * BASE64加密
   * @param key
     * @return
     * @throws Exception
     */
    public static String encryptBASE64(byte[] key) throws Exception {
        return (new BASE64Encoder()).encodeBuffer(key);
    }
    public static void main(String[] args) throws Exception
    {
        String data = BASE64.encryptBASE64("法证先锋 第二部".getBytes());
        System.out.println("加密前:"+data);
        //5rOV6K+B5YWI6ZSLIOesrOS6jOmDqA==
        byte[] byteArray = BASE64.decryptBASE64(data);
        System.out.println("解密后:"+new String(byteArray));
    }
}

利用HDFS java API增删改查操作

利用HDFS java API增删改查操作
在做这个实验的时候需要特别注意下面三个问题:
1、hdfs安全模式需要关闭 命令:./hadoop dfsadmin -safemode leave
2、工程中依赖的版本必须和集群的一致,否则也会报 version不一致错误
3、hadoop集群用户权限的问题,以及各个目录的作用
目前为什么会有这三个问题的原因待查!!!
未验证目前使用hadoop的版本(release-0.20.0)是否支持webhdfs,反正我是怎么都连接不上啊!!!
从这上面看,0.20.0 可能是不支持的
https://jira.springsource.org/browse/IMPALA-15?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
Serengeti Distro:
Apache Hadoop:1.0.1
GreenPlum HD:1.1(Apache Hadoop 1.0.0)
CloudEra: CDH3(Apache Hadoop 0.20.2, WebHDFS is not supported in this version)
Hortonworks: 1.0.7 (Apache Hadoop 1.0.2)
步骤如下:
工程结构,如图:

工程结构

工程结构


上代码 O(∩_∩)O哈哈~
pom.xml配置如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.yun.hdfs</groupId>
	<artifactId>hdfs</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-assembly-plugin</artifactId>
				<configuration>
					<appendAssemblyId>false</appendAssemblyId>
					<descriptorRefs>
						<descriptorRef>jar-with-dependencies</descriptorRef>
					</descriptorRefs>
					<archive>
						<manifest>
							<mainClass>com.yun.hdfs.WangPan</mainClass>
						</manifest>
					</archive>
				</configuration>
				<executions>
					<execution>
						<id>make-assembly</id>
						<phase>package</phase>
						<goals>
							<goal>assembly</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>
	<dependencies>
		<dependency>
			<groupId>org.apache.hadoop</groupId>
			<artifactId>hadoop-core</artifactId>
			<version>0.20.2</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
	</dependencies>
</project>

WangPan.java 主方法用于调用:

package com.yun.hdfs;
import java.io.IOException;
public class WangPan {
	/*** 运行结果描述. */
	private static String result = "";
	public static void main(String[] args) {
		try {
			// 判断命令输入是否正确
			if (args[0] != null && !"".equals(args[0]) && args.length > 0) {
				if ("upload".equals(args[0])) {
					result = "upload:" + WangPanUtils.uploadFile(args);
				} else if ("delete".equals(args[0])) {
					result = "delete:" + WangPanUtils.deleteFile(args);
				} else if ("query".equals(args[0])) {
					if (WangPanUtils.listFile(args) == null) {
						result = "query:fail!";
					} else {
						result = "query:success";
					}
				} else if ("read".equals(args[0])) {
					result = "read:" + WangPanUtils.readFile(args);
				} else {
					System.out.println("sorry,wo have no this service!");
				}
				System.out.println(result);
			} else {
				System.out.println("fail!");
				System.exit(1);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

WangPanUtils.java增删改查:

package com.yun.hdfs;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.FileUtil;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
public class WangPanUtils {
	/**
	 * 上传文件. java -jar /root/hdfs-0.0.1-SNAPSHOT.jar upload /root/test-hdfs.txt
	 * hdfs://hadoopm:9000/user/root/upload/12390po.txt
	 *
	 * @param args
	 * @return
	 * @throws IOException
	 */
	public static String uploadFile(String[] args) throws IOException {
		String loaclSrc = args[1];
		String dst = args[2];
		if (args.length < 3) {
			return "fail";
		}
		InputStream in = new BufferedInputStream(new FileInputStream(loaclSrc));
		Configuration conf = new Configuration();
		FileSystem fs = FileSystem.get(URI.create(dst), conf);
		OutputStream out = fs.create(new Path(dst));
		IOUtils.copyBytes(in, out, 4096, true);
		return "success";
	}
	/**
	 * 查询文件列表. java -jar /root/hdfs-0.0.1-SNAPSHOT.jar query
	 * hdfs://hadoopm:9000/user/root/
	 *
	 * @param args
	 * @return
	 * @throws IOException
	 */
	public static Path[] listFile(String[] args) throws IOException {
		if (args.length < 2) {
			return null;
		}
		String dst = args[1];
		Configuration conf = new Configuration();
		FileSystem fs = FileSystem.get(URI.create(dst), conf);
		FileStatus[] statu = fs.listStatus(new Path(dst));
		Path[] listPaths = FileUtil.stat2Paths(statu);
		return listPaths;
	}
	/**
	 * 删除文件.
	 * java -jar /root/hdfs-0.0.1-SNAPSHOT.jar delete hdfs://hadoopm:9000/user/root/upload/12390po.txt
	 *
	 * @param args
	 * @return
	 * @throws IOException
	 */
	public static String deleteFile(String[] args) throws IOException {
		if (args.length < 2) {
			return "fail";
		}
		String fileName = args[1];
		Configuration config = new Configuration();
		FileSystem hdfs = FileSystem.get(URI.create(fileName), config);
		Path path = new Path(fileName);
		if (!hdfs.exists(path)) {
			return "fail";
		}
		boolean isDeleted = hdfs.delete(path, false);
		if (isDeleted) {
			return "success";
		} else {
			return "fail";
		}
	}
	/**
	 * 读取文件.
	 * java -jar /root/hdfs-0.0.1-SNAPSHOT.jar read hdfs://hadoopm:9000/user/root/upload/123.txt /root/test-readfile898.txt
	 *
	 * @param args
	 * @return
	 * @throws IOException
	 */
	public static String readFile(String[] args) throws IOException {
		if(args.length < 3){
			return "fail";
		}
		String dst = args[1];
		String newPath = args[2];
		Configuration conf = new Configuration();
		FileSystem fs = FileSystem.get(URI.create(dst), conf);
		FSDataInputStream hdfsInStream = fs.open(new Path(dst));
		OutputStream out = new FileOutputStream(newPath);
		byte[] ioBuffer = new byte[1024];
		int readLen = hdfsInStream.read(ioBuffer);
		while (-1 != readLen) {
			out.write(ioBuffer, 0, readLen);
			readLen = hdfsInStream.read(ioBuffer);
		}
		out.close();
		hdfsInStream.close();
		fs.close();
		return "success";
	}
}
/**
	 * 创建文件夹.
	 * java -jar /root/hdfs-0.0.1-SNAPSHOT.jar mkdir hdfs://hadoopm:9000/user/root/upload/test909
	 *
	 * @return
	 * @throws IOException
	 */
	public static String mkdir(String[] args) throws IOException{
		if(args.length < 2){
			return "fali";
		}
		String dst = args[1];
		Configuration conf = new Configuration();
		FileSystem fs = FileSystem.get(URI.create(dst), conf);
		Path path = new Path(dst);
		if (fs.exists(path)) {
			return "fail";
		}
		fs.mkdirs(path);
		return "success";
	}

PS:需要注意的是,我们需要把这个工程利用maven打包成一个可运行的jar包,使用如下命令:

打包命令

打包命令


执行命令在每个方法注释上写明了,执行效果如下:
增删改查效果

增删改查效果


还需要访问 http://hadoopm:50070/ -> Browse the filesystem 查看hdfs文件操作是否真的成功
web hdfs

web hdfs

js的Prototype属性解释及常用方法

函数:原型
每一个构造函数都有一个属性叫做原型(prototype,下面都不再翻译,使用其原文)。这个属性非常有用:为一个特定类声明通用的变量或者函数。
prototype的定义
你不需要显式地声明一个prototype属性,因为在每一个构造函数中都有它的存在。例子:
Example PT1
CODE:

function Test()
{
}
alert(Test.prototype); // 输出 "Object"

给prototype添加属性
就如你在上面所看到的,prototype是一个对象,因此,你能够给它添加属性。你添加给prototype的属性将会成为使用这个构造函数创建的对象的通用属性。
例如,我下面有一个数据类型Fish,我想让所有的鱼都有这些属性:livesIn=”water”和price=20;为了实现这个,我可以给构造函数Fish的prototype添加那些属性。
Example PT2
CODE:

function Fish(name, color)
{
this.name=name;
this.color=color;
}
Fish.prototype.livesIn="water";
Fish.prototype.price=20;

接下来让我们作几条鱼:
CODE:
var fish1=new Fish(“mackarel”, “gray”);
var fish2=new Fish(“goldfish”, “orange”);
var fish3=new Fish(“salmon”, “white”);
再来看看鱼都有哪些属性:
CODE:

for (int i=1; i<=3; i++)
{
var fish=eval_r("fish"+i);   // 我只是取得指向这条鱼的指针
alert(fish.name+","+fish.color+","+fish.livesIn+","+fish.price);
}

输出应该是:
CODE:

"mackarel, gray, water, 20"
"goldfish, orange, water, 20"
"salmon, white water, 20"

你看到所有的鱼都有属性livesIn和price,我们甚至都没有为每一条不同的鱼特别声明这些属性。这时因为当一个对象被创建时,这个构造函数将会把它的属性prototype赋给新对象的内部属性__proto__。这个__proto__被这个对象用来查找它的属性。
你也可以通过prototype来给所有对象添加共用的函数。这有一个好处:你不需要每次在构造一个对象的时候创建并初始化这个函数。为了解释这一点,让我们重新来看Example DT9并使用prototype来重写它:
用prototype给对象添加函数
Example PT3
CODE:

function Employee(name, salary)
{
this.name=name;
this.salary=salary;
}
Employee.prototype.getSalary=function getSalaryFunction()
{
return this.salary;
}
Employee.prototype.addSalary=function addSalaryFunction(addition)
{
this.salary=this.salary+addition;
}

我们可以象通常那样创建对象:
CODE:

var boss1=new Employee("Joan", 200000);
var boss2=new Employee("Kim", 100000);
var boss3=new Employee("Sam", 150000);

并验证它:
CODE:

alert(boss1.getSalary());   // 输出 200000
alert(boss2.getSalary());   // 输出 100000
alert(boss3.getSalary());   // 输出 150000

这里有一个图示来说明prototype是如何工作的。这个对象的每一个实例(boss1, boss2, boss3)都有一个内部属性叫做__proto__,这个属性指向了它的构造器(Employee)的属性prototype。当你执行 getSalary或者addSalary的时候,这个对象会在它的__proto__找到并执行这个代码。注意这点:这里并没有代码的复制(和 Example DT8的图表作一下对比)。

js实现hashmap

js实现hashmap ,项目中遇到了要在js中使用java hashmap的功能,找到一个不错的JS实现,
感谢lujc:

/***
 * js 实现hashmap  功能  lujc
 * 常用功能实现
 * @return
 */
function HashMap(){
	this.length = 0;
	this.maxLength = Number.MAX_VALUE;
	this.container = {};
}
HashMap.prototype.put = function(objName,objValue){
	try{
		if(this.length >= this.maxLength)
			throw new Error("[Error HashMap] : Map Datas' count overflow !");
		if(objName != ""){
			for(var p in this.container){
				if(p == objName){
					this.container[objName] = objValue;
					return ;
				}
			}
			this.container[objName] = objValue;
			this.length ++ ;
		}
	}catch(e){
		return e;
	}
};
HashMap.prototype.get = function(objName){
	try{
		if(this.container[objName])
			return this.container[objName];
	}catch(e){
		return e;
	}
};
HashMap.prototype.contains = function(objValue){
	try{
		for(var p in this.container){
			if(this.container[p] === objValue)
				return true;
		}
		return false;
	}catch(e){
		return e;
	}
};
HashMap.prototype.remove = function(objName){
	try{
		if(this.container[objName]){
			delete this.container[objName];
			this.length -- ;
		}
	}catch(e){
		return e;
	}
};
HashMap.prototype.pop = function(objName){
	try{
		var ov = this.container[objName];
		if(ov){
			delete this.container[objName];
			this.length -- ;
			return ov;
		}
		return null;
	}catch(e){
		return e;
	}
};
HashMap.prototype.removeAll = function(){
	try{
		this.clear();
	}catch(e){
		return e;
	}
};
HashMap.prototype.clear = function(){
	try{
		delete this.container;
		this.container = {};
		this.length = 0;
	}catch(e){
		return e;
	}
};
HashMap.prototype.isEmpty = function(){
	if(this.length === 0)
		return true;
	else
		return false;
};
HashMap.prototype.runIn = function(fun){
	try{
		if(!fun)
			throw new Error("[Error HashMap] : The paramer is null !");
		for(var p in this.container){
			var ov = this.container[p];
			fun(ov);
		}
	}catch(e){
		return e;
	}
};

锟斤拷

今天又长知识了 O(∩_∩)O哈哈~
“xefxbfxbdxefxbfxbdxefxbfxbd”-锟斤拷
百度百科上面说:
锟斤拷,经常在搜索引擎和网站上看到的字符。是一种因微软漏洞造成的乱码。
这些字句是毫无实际意义的,但它们却形象地表达出了一些莫名其妙的问题。相当经典!
我们上网的时候不用去关心网站采用了什么编码格式,但是页面中不时出现的乱码还是会让我们头疼。在这点上,Firefox的用户更是深有体会,用Firefox浏览网页看到乱码的机会要比IE多得多。 乱码主要与字符编码系统有关。例如一个网页中常出现的乱码“锟斤拷”(百度,Google),它就是新老编码系统转换中出现的。
网友est专门写了一篇文章来考证问题来源:
Unicode和老编码体系的转化过程中,肯定有一些字,用Unicode是没法表示的,Unicode官方用了一个占位符来表示这些文字,这就是:U+FFFD REPLACEMENT CHARACTER。那么U+FFFD的UTF-8编码出来,恰好是 ‘xefxbfxbd’。如果这个’xefxbfxbd’,重复多次,例如 ‘xefxbfxbdxefxbfxbd’,然后放到GBK/CP936/GB2312/GB18030的环境中显示的话就是“锟斤拷——锟(0xEFBF),斤(0xBDEF),拷(0xBFBD)”。

Struts2报到异常:The content of element type "package" must match

Struts2工程启动提示错误:
The content of element type “package” must match
The content of element type “package” must match “(result-types?,interceptors?,default-interceptor-ref?,default-action-ref?,default-class-ref?,global-results?,global-exception-mappings?,action*)”. struts.xml /webstruts/src line 12 XML Problem
这个错误的意思是,package里元素必须按照其默认的顺序排列。这个顺序
是:
result-types
interceptors
default-interceptor-ref
default-action-ref
default-class-ref
global-results
global-exception-mappings
action*(把所有的action放到最后)