2012年1月2日
#============================================================Logger CommonLog
log4j.logger.CommonLog=DEBUG, Console, LogRollingFile
# Console output...
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
# RollingFileAppender output...
log4j.appender.LogRollingFile=org.apache.log4j.RollingFileAppender
log4j.appender.LogRollingFile.File=${user.dir}/yccb/log/yccb.log
log4j.appender.LogRollingFile.Append=true
log4j.appender.LogRollingFile.MaxFileSize=46MB
log4j.appender.LogRollingFile.MaxBackupIndex=50
log4j.appender.LogRollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.LogRollingFile.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH\:mm\:ss,SSS} method\:%l%n%m%n
#============================================================Logger SkmLog
log4j.logger.SkmLog=DEBUG, DailyRollingFile
# DailyRollingFile output...
log4j.appender.DailyRollingFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.DailyRollingFile.DatePattern=yyyy-MM-dd'.log'
log4j.appender.DailyRollingFile.File=${user.dir}/yccb/log/skm/skm.log
log4j.appender.DailyRollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.DailyRollingFile.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH\:mm\:ss,SSS} method\:%l%n%m%n
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setLenient(false);
boolean b = true;
try {
sdf.parse("2002-15-11");
} catch (ParseException e) {
e.printStackTrace();
b = false;
}
System.out.println(b);
http://blog.csdn.net/wuxianglong/article/details/6285978
1,公钥和私钥成对出现
2,公开的密钥叫公钥,只有自己知道的叫私钥
3,用公钥加密的数据只有对应的私钥可以 解密
4,用私钥加密的数据只有对应的公钥可以解密
5,如果可以用公钥解密,则必然是对应的私钥加的密
6,如果可以用私钥解密,则 必然是对应的公钥加的密
明白了?
假设一下,我找了两个数字,一个是1,一个是2。我喜欢2这个数字,就保留起来,不告诉你们,然 后我告诉大家,1是我的公钥。
我有一个文件,不能让别人看,我就用1加密了。别人找到了这个文件,但是他不知道2就是解密的私钥啊,所以 他解不开,只有我可以用数字2,就是我的私钥,来解密。这样我就可以保护数据了。
我的好朋友x用我的公钥1加密了字符a,加密后成了b, 放在网上。别人偷到了这个文件,但是别人解不开,因为别人不知道2就是我的私钥,只有我才能解密,解密后就得到a。这样,我们就可以传送加密的数据了。
现在我们知道用公钥加密,然后用私钥来解密,就可以解决安全传输的问题了。如果我用私钥加密一段数据(当然只有我可以用私钥加密,因为只有我知道 2是我的私钥),结果所有的人都看到我的内容了,因为他们都知道我的公钥是1,那么这种加密有什么用处呢?
但是我的好朋友x说有人冒充我 给他发信。怎么办呢?我把我要发的信,内容是c,用我的私钥2,加密,加密后的内容是d,发给x,再告诉他解密看是不是c。他用我的公钥1解密,发现果然 是c。这个时候,他会想到,能够用我的公钥解密的数据,必然是用我的私钥加的密。只有我知道我得私钥,因此他就可以确认确实是我发的东西。这样我们就能确 认发送方身份了。这个过程叫做数字签名。当然具体的过程要稍微复杂一些。用私钥来加密数据,用途就是数字签名。
好,我们复习一下:
1, 公钥私钥成对出现
2,私钥只有我知道
3,大家可以用我的公钥给我发加密的信了
4,大家用我的公钥解密信的内容,看看能不能解开, 能解开,说明是经过我的私钥加密了,就可以确认确实是我发的了。
总结一下结论:
1,用公钥加密数据,用私钥来解密数据
2, 用私钥加密数据(数字签名),用公钥来验证数字签名。
在实际的使用中,公钥不会单独出现,总是以数字证书的方式出现,这样是为了公钥的安 全性和有效性。
二,SSL
我和我得好朋友x,要进行安全的通信。这种通信可以是QQ聊天,很频繁的。用我的公钥加密数据就不行 了,因为:
1,我的好朋友x没有公私钥对,我怎么给他发加密的消息啊? (注:实际情况中,可以双方都有公私钥对)
2,用公私钥加密运算 很费时间,很慢,影响QQ效果。
好了,好朋友x,找了一个数字3,用我的公钥1,加密后发给我,说,我们以后就用这个数字来加密信息吧。 我解开后,得到了数字3。这样,只有我们两个人知道这个秘密的数字3,别的人都不知道,因为他们既不知x挑了一个什么数字,加密后的内容他们也无法解开, 我们把这个秘密的数字叫做会话密钥。
然后,我们选择一种对称密钥算法,比如DES,(对称算法是说,加密过程和解密过程是对称的,用一个 密钥加密,可以用同一个密钥解密。使用公私钥的算法是非对称加密算法),来加密我们之间的通信内容。别人因为不知道3是我们的会话密钥,因而无法解密。
好,复习一下:
1,SSL实现安全的通信
2,通信双方使用一方或者双方的公钥来传递和约定会话密钥 (这个过程叫做握手)
3, 双方使用会话密钥,来加密双方的通信内容
上面说的是原理。大家可能觉得比较复杂了,实际使用中,比这还要复杂。不过庆幸的是,好心的先行 者们在操作系统或者相关的软件中实现了这层(Layer),并且起了一个难听的名字叫做SSL,(Secure Socket Layer)。
超文本传输安全协议(缩写:HTTPS,英语:Hypertext Transfer Protocol Secure)是超文本传输协议和SSL/TLS的组合,用以提供加密通讯及对网络服务器身份的鉴定。HTTPS连接经常被用于万维网上的交易支付和企业信息系统中敏感信息的传输。HTTPS不应与在RFC 2660中定义的安全超文本传输协议(S-HTTP)相混。
主要思想
HTTPS的主要思想是在不安全的网络上创建一安全信道,并可在使用适当的加密包和服务器证书可被验证且可被信任时,对窃听和中间人攻击提供合理的保护。
HTTPS的信任继承基于预先安装在浏览器中的证书颁发机构(如VeriSign、Microsoft等)(意即“我信任证书颁发机构告诉我应该信任的”)。因此,一个到某网站的HTTPS连接可被信任,当且仅当:
- 用户相信他们的浏览器正确实现了HTTPS且安装了正确的证书颁发机构;
- 用户相信证书颁发机构仅信任合法的网站;
- 被访问的网站提供了一个有效的证书,意即,它是由一个被信任的证书颁发机构签发的(大部分浏览器会对无效的证书发出警告);
- 该证书正确地验证了被访问的网站(如,访问
https://example
时收到了给“Example Inc.”而不是其它组织的证书); - 或者互联网上相关的节点是值得信任的,或者用户相信本协议的加密层(TLS或SSL)不能被窃听者破坏。
技术细节
- pasting
与HTTP的差异[编辑]
与HTTP的URL由“http://
”起始且默认使用端口80不同,HTTPS的URL由“https://
”起始且默认使用端口443。
HTTP是不安全的,且攻击者通过监听和中间人攻击等手段,可以获取网站帐户和敏感信息等。HTTPS被设计为可防止前述攻击,并(在没有使用旧版本的SSL时)被认为是安全的。
网络层[编辑]
HTTP工作在应用层(OSI模型的最高层),但安全协议工作在一个较低的子层:在HTTP报文传输前对其加密,并在到达时对其解密。严格地讲,HTTPS并不是一个单独的协议,而是对工作在一加密连接(TLS或SSL)上的常规HTTP协议的称呼。
HTTPS报文中的任何东西都被加密,包括所有报头和荷载。除了可能的CCA(参见限制小节)之外,一个攻击者所能知道的只有在两者之间有一连接这一事实。
服务器设置[编辑]
要使一网络服务器准备好接受HTTPS连接,管理员必须创建一数字证书,并交由证书颁发机构签名以使浏览器接受。证书颁发机构会验证数字证书持有人和其声明的为同一人。浏览器通常都预装了证书颁发机构的证书,所以他们可以验证该签名。
获得证书[编辑]
由证书颁发机构签发的证书有免费的[3][4],也有每年收费13美元[5]到1500美元[6]不等的。
一个组织也可能有自己的证书颁发机构,尤其是当设置浏览器来访问他们自己的网站时(如,运行在公司或学校局域网内的网站)。他们可以容易地将自己的证书加入浏览器中。
此外,还存在一个人到人的证书颁发机构,CAcert。
作为访问控制[编辑]
HTTPS也可被用作客户端认证手段来将一些信息限制给合法的用户。要做到这样,管理员通常会给每个用户创建证书(通常包含了用户的名字和电子邮件地址)。这个证书会被放置在浏览器中,并在每次连接到服务器时由服务器检查。
当私钥失密时[编辑]
TLS有两种策略:简单策略和交互策略。交互策略更为安全,但需要用户在他们的浏览器中安装个人的证书来进行认证。
不管使用了哪种策略,协议所能提供的保护总强烈地依赖于浏览器的实现和服务器软件所支持的加密算法。
HTTPS并不能防止站点被网络蜘蛛抓取。在某些情形中,被加密资源的URL可仅通过截获请求和响应的大小推得,[11]这就可使攻击者同时知道明文(公开的静态内容)和密文(被加密过的明文),从而使选择密文攻击成为可能。
因为SSL在HTTP之下工作,对上层协议一无所知,所以SSL服务器只能为一个IP地址/端口组合提供一个证书。[12]这就意味着在大部分情况下,使用HTTPS的同时支持基于名字的虚拟主机是不很现实的。一种叫域名指示(SNI)的方案通过在加密连接创建前向服务器发送主机名解决了这一问题。Firefox 2、Opera8和运行在Windows Vista的Internet Explorer 7都加入了对SNI的支持。[13][14][15]
因为HTTPS连接所用的公钥以明文传输,因此中国大陆的防火长城可以对特定网站按照匹配的黑名单证书,通过伪装成对方向连接两端的计算机发送RST包干扰两台计算机间正常的TCP通讯,以打断与特定IP地址之间的443端口握手,或者直接使握手的数据包丢弃,导致握手失败,从而导致TLS连接失败。[16]这也是一种互联网信息审查和屏蔽的技术手段。
如果Mac OS X中的家长控制被启用,那么HTTPS站点必须显式地在“总是允许”列表中列出。[17]
值传递(pass by value):stack(栈,常量、基本数据类型(八种)、对象引用、指令(对象的方法)),简单类型。
引用传递(pss by reference):stack(栈)和heap(堆,对象实例(object instance))。
stream在引用传递的过程中,不要随意关闭,一关都关!
double:双精度浮点数,64位(bits)8字节,它可以表示十进制的15或16位有效数字。
float:单精度浮点数,32位(bits)4字节。
浮点:浮动小数点。
当部分网页上面的JAVA插件无法运行,系统提示是由于您的安全设置,所以该运行程序被阻止了,此问题是由于您的JAVA安全设置的级别引起的。
JAVA安全设置修改方式:windows控制面板 -> 程序 -> Java -> 安全。
通常做法是定义一个Servlet,并在web.xml中配置Servlet的启动顺序<load-on-startup>的值在DispatcherServlet之后。但这样做的缺点是在Servlet中无法使用Spring的依赖注入功能,只能使用WebApplicationContext的getBean()方法获取bean。
找到的解决办法如下:
1、自定义一个用于代理启动Servlet的类DelegatingServletProxy:
package com.test.common.util;
import java.io.IOException;
import javax.servlet.GenericServlet;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
public class DelegatingServletProxy extends GenericServlet {
private String targetBean;
private Servlet proxy;
@Override
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException {
proxy.service(arg0, arg1);
}
@Override
public void init() throws ServletException {
this.targetBean = getServletName();
getServletBean();
proxy.init(getServletConfig());
}
private void getServletBean() {
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
this.proxy = (Servlet)wac.getBean(targetBean);
}
}
2、编写启动Servlet:
package com.test.common.util;
import java.io.IOException;
import java.util.List;
import javax.annotation.Resource;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import cn.edu.swu.oa.agency.model.Department;
import cn.edu.swu.oa.agency.model.Group;
import cn.edu.swu.oa.agency.service.DepService;
import cn.edu.swu.oa.agency.service.GroService;
import cn.edu.swu.oa.common.model.SysCode;
import cn.edu.swu.oa.safe.model.User;
import cn.edu.swu.oa.safe.service.UserService;
/**
*
*
* 类型解释:Spring启动完成后执行初始化操作
* 类型表述:预读某些实体的Key-Value,放入map,方便以后使用
* @author
* @version
*
*/
@Component("initialServlet")
public class InitialServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Resource
private UserService userService;
@Resource
private DepService depService;
@Resource
private GroService groService;
/**
* @see HttpServlet#HttpServlet()
*/
public InitialServlet() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
}
@Override
public void init(ServletConfig config) throws ServletException {
//初始化eserMap
List<User> users = userService.getUsers();
for(int i = 0; i < users.size(); i++) {
User user = users.get(i);
Integer userId = user.getUserId();
String userName = user.getUserName();
SysCode.userMap.put(userId, userName);
}
//初始化depMap
List<Department> deps = depService.getAllDeps();
for(int i = 0; i < deps.size(); i++) {
Department dep = deps.get(i);
Integer depId = dep.getDepId();
String depName = dep.getDepName();
SysCode.depMap.put(depId, depName);
}
//初始化groMap
List<Group> gros = groService.getAllGroups();
for(int i = 0; i < gros.size(); i++) {
Group gro = gros.get(i);
Integer groId = gro.getGroId();
String groName = gro.getGroName();
SysCode.groMap.put(groId, groName);
}
}
}
3、在web.xml文件中配置InitialServlet :
<servlet>
<description></description>
<display-name>InitialServlet</display-name>
<servlet-name>initialServlet</servlet-name>
<servlet-class>
com.test.common.util.DelegatingServletProxy
</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>initialServlet</servlet-name>
<url-pattern>/InitialServlet</url-pattern>
</servlet-mapping>
完成这些操作后,就可以在Spring容器启动后执行自定义的Servlet,并且在自定义Servlet中可以使用Spring Annotation的自动注入功能。 <script></script>
package com.athrunwang.test;
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class TestTimer {
static int count = 0;
public static void showTimer() {
TimerTask task = new TimerTask() {
@Override
public void run() {
++count;
System.out.println("时间=" + new Date() + " 执行了" + count + "次"); // 1次
}
};
// 设置执行时间
Calendar calendar = Calendar.getInstance();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);// 每天
// 定制每天的21:09:00执行,
calendar.set(year, month, day, 9, 54, 00);
Date date = calendar.getTime();
Timer timer = new Timer();
System.out.println(date);
int period = 2 * 1000;
// 每天的date时刻执行task,每隔2秒重复执行
timer.schedule(task, date, period);
// 每天的date时刻执行task, 仅执行一次
//timer.schedule(task, date);
}
public static void main(String[] args) {
showTimer();
}
}
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class ByteToInputStream {
public static final InputStream byte2Input(byte[] buf) {
return new ByteArrayInputStream(buf);
}
public static final byte[] input2byte(InputStream inStream)
throws IOException {
ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
byte[] buff = new byte[100];
int rc = 0;
while ((rc = inStream.read(buff, 0, 100)) > 0) {
swapStream.write(buff, 0, rc);
}
byte[] in2b = swapStream.toByteArray();
return in2b;
}
}
package others.interesting;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import javazoom.jl.decoder.JavaLayerException;
import javazoom.jl.player.Player;
public class MP3Player {
private String fileName;
private Player player;
public MP3Player(String fileName) {
this.fileName = fileName;
}
public void play() {
try {
BufferedInputStream buffer = new BufferedInputStream(
new FileInputStream(fileName));
player = new Player(buffer);
player.play();
} catch (FileNotFoundException e) {
System.err.println("FileNotFoundException:");
e.printStackTrace();
} catch (JavaLayerException e) {
System.err.println("JavaLayerException:");
e.printStackTrace();
}
}
public static void main(String[] args) {
MP3Player mp3Player = new MP3Player(
"C:\\Users\\Athrunwang\\Desktop\\杀死那个石家庄人.mp3");
mp3Player.play();
}
}
package org.study.sort;
import java.util.Arrays;
/**
* 问题描述:
* 吸血鬼数字是指位数为偶数的数字,可以由一对数字相乘而得到,而这对数字各包含乘积的一半位数的数字,
* 其中从最初的数字中选取的数字可以任意排序。
* 例如:
* 1260 = 21 * 60 1827 = 21 * 87 2187 = 27 * 81
* 要求输出所有四位数的吸血鬼数字。
*
* @author heng.ai
*
* 注:参考了CSDN一朋友的写法
*/
public class VampireNumber {
public static void main(String[] args) {
for(int i = 1; i < 100; i++){
for(int j = i+1; j < 100; j++){
//只要求输出四位数
if(i * j >= 1000){
String a = i + "" + j;
String b = i * j + "";
if(equal(a, b)){
System.out.printf("%d * %d = %d", i, j, i*j);
System.out.println();
}
}
}
}
}
//判断两个字符串包含的数字是否一致
private static boolean equal(String a, String b) {
//先排序
char[] as = a.toCharArray();
char[] bs = b.toCharArray();
Arrays.sort(as); //排序
Arrays.sort(bs); //排序
if(Arrays.equals(as, bs)){
return true;
}
return false;
}
}
在看这篇文章前,我推荐你看一下Eclipse 快捷键手册,我的eclipse版本是4.2 Juno。
先提三点
- 不要使用System.out.println作为调试工具
- 启用所有组件的详细的日志记录级别
- 使用一个日志分析器来阅读日志
1、条件断点想象一下我们平时如何添加断点,通常的做法是双击行号的左边。在debug视图中,BreakPoint View将所有断点都列出来,但是我们可以添加一个boolean类型的条件来决定断点是否被跳过。如果条件为真,在断点处程序将停止,否则断点被跳过,程序继续执行。
2、异常断点
在断点view中有一个看起来像J!的按钮,我们可以使用它添加一个基于异常的断点,例如我们希望当NullPointerException抛出的时候程序暂停,我们可以这样:
3、观察点
这个特性我非常喜欢,他允许当一个选定的属性被访问或者被更改的时候程序执行暂停,并进行debug。最简单的办法是在类中声明成员变量的语句行号左边双击,就可以加入一个观察点。
4、查看变量
在选中的变量上使用Ctrl+Shift+d 或者 Ctrl+Shift+i可以查看变量值,另外我们还可以在Expressions View中添加监视。
5、改变变量值
我们可以在Debug的时候改变其中变量的值。在Variables View中可以按下图所示操作。
6、在Main方法中停止
在Run/Debug设置中,我们可以按如下图所示的启用这个特性。程序将会在main方法的第一行停住
7、环境变量
我们可以很方便的在Edit Conriguration对话框中添加环境变量
8、Drop to frame
这个功能非常酷,是我第二个非常喜欢的功能,Drop to frame就是说,可以重新跳到当前方法的开始处重新执行,并且所有上下文变量的值也回到那个时候。不一定是当前方法,可以点击当前调用栈中的任何一个frame跳到那里(除了最开始的那个frame)。主要用途是所有变量状态快速恢复到方法开始时候的样子重新执行一遍,即可以一遍又一遍地在那个你关注的上下文中进行多次调试(结合改变变量值等其它功能),而不用重来一遍调试到哪里了。当然,原来执行过程中产生的副作用是不可逆的(比如你往数据库中插入了一条记录)。
9、Step 过滤
当我们在调试的时候摁F5将进入方法的内部,但这有个缺点有的时候可能会进入到一些库的内部(例如JDK),可能并不是我们想要的,我们可以在Preferences中添加一个过滤器,排除指定的包。
10、进入、跳过、返回
其实这个技巧是debug最基本的知识。
- F5-Step Into:移动到下一步,如果当前的行是一个方法调用,将进入这个方法的第一行。(可以通过第九条来排除)
- F6-Step Over:移动到下一行。如果当前行有方法调用,这个方法将被执行完毕返回,然后到下一行。
- F7-Step Return:继续执行当前方法,当当前方法执行完毕的时候,控制将转到当前方法被调用的行。
- F8-移动到下一个断点处。
public class Main {
public static void main(String[] args) {
String pathB = "/P/y/z/a/b/c/d/34/c.php";
String pathA = "/P/y/z/a/b/a/g/e.php";
System.out.println(pathARelativePathB(pathA,pathB,0));
}
public static String pathARelativePathB(String pathA , String pathB, int i){
if(pathA.contains(pathB)){
StringBuilder replaceSb = new StringBuilder();
if(i==1){
replaceSb.append(".");
}else{
while(i>1){
replaceSb.append("../");
--i;
}
}
return pathA.replace(pathB,replaceSb.substring(0, replaceSb.lastIndexOf("/")));
}else{
return pathARelativePathB(pathA,pathB.substring(0,pathB.lastIndexOf("/")),++i);
}
}
}
if(window.external&&window.external.twGetRunPath&&window.external.twGetRunPath().toLowerCase().indexOf("360se")>-1){alert('本站不支持360浏览器访问,请更换其他浏览器!');}
摘要: 1、面向对象的特征有哪些方面 (1).抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细 节。抽象包括两个方面,一是过程抽象,二是数据抽象。 (2).继承: 继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个... 阅读全文
Beanutils用了魔术般的反射技术,实现了很多夸张有用的功能,都是C/C++时代不敢想的。无论谁的项目,始终一天都会用得上它。我算是后知后觉了,第一回看到它的时候居然错过。
1.属性的动态getter,setter
在这框架满天飞的年代,不能事事都保证执行getter,setter函数了,有时候属性是要需要根据名字动态取得的,就像这样:
BeanUtils.getProperty(myBean,"code");
而BeanUtils更强的功能是直接访问内嵌对象的属性,只要使用点号分隔。
BeanUtils.getProperty(orderBean, "address.city");
相比之下其他类库的BeanUtils通常都很简单,不能访问内嵌的对象,所以经常要用Commons BeanUtils替换它们。
BeanUtils还支持List和Map类型的属性。如下面的语法即可取得顾客列表中第一个顾客的名字
BeanUtils.getProperty(orderBean, "customers[1].name");
其中BeanUtils会使用ConvertUtils类把字符串转为Bean属性的真正类型,方便从HttpServletRequest等对象中提取bean,或者把bean输出到页面。
而PropertyUtils就会原色的保留Bean原来的类型。
2.beanCompartor 动态排序
还是通过反射,动态设定Bean按照哪个属性来排序,而不再需要在bean的Compare接口进行复杂的条件判断。
List peoples = ...; // Person对象的列表Collections.sort(peoples, new BeanComparator("age"));
如果要支持多个属性的复合排序,如"Order By lastName,firstName"
ArrayList sortFields = new ArrayList();sortFields.add(new BeanComparator("lastName"));
sortFields.add(new BeanComparator("firstName"));
ComparatorChain multiSort = new ComparatorChain(sortFields);
Collections.sort(rows,multiSort);
其中ComparatorChain属于jakata commons-collections包。
如果age属性不是普通类型,构造函数需要再传入一个comparator对象为age变量排序。
另外, BeanCompartor本身的ComparebleComparator, 遇到属性为null就会抛出异常, 也不能设定升序还是降序。
这个时候又要借助commons-collections包的ComparatorUtils.
Comparator mycmp = ComparableComparator.getInstance();
mycmp = ComparatorUtils.nullLowComparator(mycmp); //允许null
mycmp = ComparatorUtils.reversedComparator(mycmp); //逆序
Comparator cmp = new BeanComparator(sortColumn, mycmp);
3.Converter 把Request或ResultSet中的字符串绑定到对象的属性 经常要从request,resultSet等对象取出值来赋入bean中,下面的代码谁都写腻了,如果不用MVC框架的绑定功能的话。
String a = request.getParameter("a"); bean.setA(a); String b = ....
不妨写一个Binder:
MyBean bean = ...; HashMap map = new HashMap(); Enumeration names = request.getParameterNames(); while (names.hasMoreElements()) { String name = (String) names.nextElement(); map.put(name, request.getParameterValues(name)); } BeanUtils.populate(bean, map);
其中BeanUtils的populate方法或者getProperty,setProperty方法其实都会调用convert进行转换。
但Converter只支持一些基本的类型,甚至连java.util.Date类型也不支持。而且它比较笨的一个地方是当遇到不认识的类型时,居然会抛出异常来。
对于Date类型,我参考它的sqldate类型实现了一个Converter,而且添加了一个设置日期格式的函数。
要把这个Converter注册,需要如下语句:
ConvertUtilsBean convertUtils = new ConvertUtilsBean();
DateConverter dateConverter = new DateConverter();
convertUtils.register(dateConverter,Date.class);
//因为要注册converter,所以不能再使用BeanUtils的静态方法了,必须创建BeanUtilsBean实例
BeanUtilsBean beanUtils = new BeanUtilsBean(convertUtils,new PropertyUtilsBean());
beanUtils.setProperty(bean, name, value);
4 其他功能4.1 PropertyUtils,当属性为Collection,Map时的动态读取:
Collection: 提供index
BeanUtils.getIndexedProperty(orderBean,"items",1);
或者
BeanUtils.getIndexedProperty(orderBean,"items[1]");
Map: 提供Key Value
BeanUtils.getMappedProperty(orderBean, "items","111");//key-value goods_no=111
或者
BeanUtils.getMappedProperty(orderBean, "items(111)")
4.2 PropertyUtils,获取属性的Class类型
public static Class getPropertyType(Object bean, String name)
4.3 ConstructorUtils,动态创建对象
public static Object invokeConstructor(Class klass, Object arg)
4.4 MethodUtils,动态调用方法
MethodUtils.invokeMethod(bean, methodName, parameter);
4.5 动态Bean 见用DynaBean减除不必要的VO和FormBean
很久很久以前,有一群人,他们决定用8个可以开合的晶体管来组合成不同的状态,以表示世界上的万物。他们看到8个开关状态是好的,于是他们把这称为"字节"。
再后来,他们又做了一些可以处理这些字节的机器,机器开动了,可以用字节来组合出很多状态,状态开始变来变去。他们看到这样是好的,于是它们就这机器称为"计算机"。
开始计算机只在美国用。八位的字节一共可以组合出256(2的8次方)种不同的状态。
他们把其中的编号从0开始的32种状态分别规定了特殊的用途,一但终端、打印机遇上约定好的这些字节被传过来时,就要做一些约定的动作。遇上00x10, 终端就换行,遇上0x07, 终端就向人们嘟嘟叫,例好遇上0x1b, 打印机就打印反白的字,或者终端就用彩色显示字母。他们看到这样很好,于是就把这些0x20以下的字节状态称为"控制码"。
他们又把所有的空格、标点符号、数字、大小写字母分别用连续的字节状态表示,一直编到了第127号,这样计算机就可以用不同字节来存储英语的文字了。大家看到这样,都感觉很好,于是大家都把这个方案叫做 ANSI 的"Ascii"编码(American Standard Code for Information Interchange,美国信息互换标准代码)。当时世界上所有的计算机都用同样的ASCII方案来保存英文文字。
后来,就像建造巴比伦塔一样,世界各地的都开始使用计算机,但是很多国家用的不是英文,他们的字母里有许多是ASCII里没有的,为了可以在计算机保存他们的文字,他们决定采用127号之后的空位来表示这些新的字母、符号,还加入了很多画表格时需要用下到的横线、竖线、交叉等形状,一直把序号编到了最后一个状态255。从128到255这一页的字符集被称"扩展字符集"。从此之后,贪婪的人类再没有新的状态可以用了,美帝国主义可能没有想到还有第三世界国家的人们也希望可以用到计算机吧!
等中国人们得到计算机时,已经没有可以利用的字节状态来表示汉字,况且有6000多个常用汉字需要保存呢。但是这难不倒智慧的中国人民,我们不客气地把那些127号之后的奇异符号们直接取消掉, 规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就叫"半角"字符了。
中国人民看到这样很不错,于是就把这种汉字方案叫做 "GB2312"。GB2312 是对 ASCII 的中文扩展。
但是中国的汉字太多了,我们很快就就发现有许多人的人名没有办法在这里打出来,特别是某些很会麻烦别人的国家领导人。于是我们不得不继续把 GB2312 没有用到的码位找出来老实不客气地用上。
后来还是不够用,于是干脆不再要求低字节一定是127号之后的内码,只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容。结果扩展之后的编码方案被称为 GBK 标准,GBK 包括了 GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。
后来少数民族也要用电脑了,于是我们再扩展,又加了几千个新的少数民族的字,GBK 扩成了 GB18030。从此之后,中华民族的文化就可以在计算机时代中传承了。
中国的程序员们看到这一系列汉字编码的标准是好的,于是通称他们叫做 "DBCS"(Double Byte Charecter Set 双字节字符集)。在DBCS系列标准里,最大的特点是两字节长的汉字字符和一字节长的英文字符并存于同一套编码方案里,因此他们写的程序为了支持中文处理,必须要注意字串里的每一个字节的值,如果这个值是大于127的,那么就认为一个双字节字符集里的字符出现了。那时候凡是受过加持,会编程的计算机僧侣们都要每天念下面这个咒语数百遍:
"一个汉字算两个英文字符!一个汉字算两个英文字符......"
因为当时各个国家都像中国这样搞出一套自己的编码标准,结果互相之间谁也不懂谁的编码,谁也不支持别人的编码,连大陆和台湾这样只相隔了150海里,使用着同一种语言的兄弟地区,也分别采用了不同的 DBCS 编码方案。当时的中国人想让电脑显示汉字,就必须装上一个"汉字系统",专门用来处理汉字的显示、输入的问题,但是那个台湾的愚昧封建人士写的算命程序就必须加装另一套支持 BIG5 编码的什么"倚天汉字系统"才可以用,装错了字符系统,显示就会乱了套!这怎么办?而且世界民族之林中还有那些一时用不上电脑的穷苦人民,他们的文字又怎么办?
真是计算机的巴比伦塔命题啊!
正在这时,大天使加百列及时出现了:一个叫 ISO (国际标谁化组织)的国际组织决定着手解决这个问题。他们采用的方法很简单:废了所有的地区性编码方案,重新搞一个包括了地球上所有文化、所有字母和符号的编码!他们打算叫它"Universal Multiple-Octet Coded Character Set",简称 UCS, 俗称 "UNICODE"。
UNICODE 开始制订时,计算机的存储器容量极大地发展了,空间再也不成为问题了。于是 ISO 就直接规定必须用两个字节,也就是16位来统一表示所有的字符,对于ascii里的那些"半角"字符,UNICODE 包持其原编码不变,只是将其长度由原来的8位扩展为16位,而其他文化和语言的字符则全部重新统一编码。由于"半角"英文符号只需要用到低8位,所以其高8位永远是0,因此这种大气的方案在保存英文文本时会多浪费一倍的空间。
这时候,从旧社会里走过来的程序员开始发现一个奇怪的现象:他们的strlen函数靠不住了,一个汉字不再是相当于两个字符了,而是一个!是的,从 UNICODE 开始,无论是半角的英文字母,还是全角的汉字,它们都是统一的"一个字符"!同时,也都是统一的"两个字节",请注意"字符"和"字节"两个术语的不同,"字节"是一个8位的物理存贮单元,而"字符"则是一个文化相关的符号。在UNICODE 中,一个字符就是两个字节。一个汉字算两个英文字符的时代已经快过去了。
从前多种字符集存在时,那些做多语言软件的公司遇上过很大麻烦,他们为了在不同的国家销售同一套软件,就不得不在区域化软件时也加持那个双字节字符集咒语,不仅要处处小心不要搞错,还要把软件中的文字在不同的字符集中转来转去。UNICODE 对于他们来说是一个很好的一揽子解决方案,于是从 Windows NT 开始,MS 趁机把它们的操作系统改了一遍,把所有的核心代码都改成了用 UNICODE 方式工作的版本,从这时开始,WINDOWS 系统终于无需要加装各种本土语言系统,就可以显示全世界上所有文化的字符了。
但是,UNICODE 在制订时没有考虑与任何一种现有的编码方案保持兼容,这使得 GBK 与UNICODE 在汉字的内码编排上完全是不一样的,没有一种简单的算术方法可以把文本内容从UNICODE编码和另一种编码进行转换,这种转换必须通过查表来进行。
如前所述,UNICODE 是用两个字节来表示为一个字符,他总共可以组合出65535不同的字符,这大概已经可以覆盖世界上所有文化的符号。如果还不够也没有关系,ISO已经准备了UCS-4方案,说简单了就是四个字节来表示一个字符,这样我们就可以组合出21亿个不同的字符出来(最高位有其他用途),这大概可以用到银河联邦成立那一天吧!
UNICODE 来到时,一起到来的还有计算机网络的兴起,UNICODE 如何在网络上传输也是一个必须考虑的问题,于是面向传输的众多 UTF(UCS Transfer Format)标准出现了,顾名思义,UTF8就是每次8个位传输数据,而UTF16就是每次16个位,只不过为了传输时的可靠性,从UNICODE到UTF时并不是直接的对应,而是要过一些算法和规则来转换。
受到过网络编程加持的计算机僧侣们都知道,在网络里传递信息时有一个很重要的问题,就是对于数据高低位的解读方式,一些计算机是采用低位先发送的方法,例如我们PC机采用的 INTEL 架构,而另一些是采用高位先发送的方式,在网络中交换数据时,为了核对双方对于高低位的认识是否是一致的,采用了一种很简便的方法,就是在文本流的开始时向对方发送一个标志符。如果之后的文本是高位在位,那就发送"FEFF",反之,则发送"FFFE"。不信你可以用二进制方式打开一个UTF-X格式的文件,看看开头两个字节是不是这两个字节?
讲到这里,我们再顺便说说一个很著名的奇怪现象:当你在 windows 的记事本里新建一个文件,输入"联通"两个字之后,保存,关闭,然后再次打开,你会发现这两个字已经消失了,代之的是几个乱码!呵呵,有人说这就是联通之所以拼不过移动的原因。
其实这是因为GB2312编码与UTF8编码产生了编码冲撞的原因。
从网上引来一段从UNICODE到UTF8的转换规则:
Unicode
UTF-8
0000 - 007F
0xxxxxxx
0080 - 07FF
110xxxxx 10xxxxxx
0800 - FFFF
1110xxxx 10xxxxxx 10xxxxxx
例如"汉"字的Unicode编码是6C49。6C49在0800-FFFF之间,所以要用3字节模板:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 1100 0100 1001,将这个比特流按三字节模板的分段方法分为0110 110001 001001,依次代替模板中的x,得到:1110-0110 10-110001 10-001001,即E6 B1 89,这就是其UTF8的编码。
而当你新建一个文本文件时,记事本的编码默认是ANSI, 如果你在ANSI的编码输入汉字,那么他实际就是GB系列的编码方式,在这种编码下,"联通"的内码是:
c1 1100 0001
aa 1010 1010
cd 1100 1101
a8 1010 1000
注意到了吗?第一二个字节、第三四个字节的起始部分的都是"110"和"10",正好与UTF8规则里的两字节模板是一致的,于是再次打开记事本时,记事本就误认为这是一个UTF8编码的文件,让我们把第一个字节的110和第二个字节的10去掉,我们就得到了"00001 101010",再把各位对齐,补上前导的0,就得到了"0000 0000 0110 1010",不好意思,这是UNICODE的006A,也就是小写的字母"j",而之后的两字节用UTF8解码之后是0368,这个字符什么也不是。这就是只有"联通"两个字的文件没有办法在记事本里正常显示的原因。
而如果你在"联通"之后多输入几个字,其他的字的编码不见得又恰好是110和10开始的字节,这样再次打开时,记事本就不会坚持这是一个utf8编码的文件,而会用ANSI的方式解读之,这时乱码又不出现了。
/**
*
*/
package sortAlgorithm;
import java.io.File;
import java.io.IOException;
import java.sql.Time;
import java.util.Random;
/**
* @author sky
* 该类给出各种排序算法
*
*/
public class sort{
private static Integer[] elem(int n){
int N=n;
Random random=new Random();
Integer elem[]=new Integer[N];
for (int i=0;i<N;i++){
elem[i]=random.nextInt(1000);
}
return elem;
}
public static void main (String Args[]) throws InterruptedException{
int n=30000;
Integer elem[]=elem(n);
long start,end;
class sort0 extends Thread{
Integer elem[];
int n;
sort0(Integer elem[],int n){
this.elem=elem;
this.n=n;
}
public void run(){
System.out.println("线程启动");
straightInsertSort(elem,n);
}
}
elem=elem(n);
start=System.currentTimeMillis();
sort0 s1=new sort0(elem,n);
elem=elem(n);
sort0 s2=new sort0(elem,n);
elem=elem(n);
sort0 s3=new sort0(elem,n);
elem=elem(n);
sort0 s4=new sort0(elem,n);
elem=elem(n);
sort0 s5=new sort0(elem,n);
s1.start();
s2.start();
s3.start();
s4.start();
s5.start();
s2.join();
s1.join();
s3.join();
s4.join();
s5.join();
System.out.println("多线程简单插入排序:");
end=System.currentTimeMillis();
System.out.println(end-start);
elem=elem(n);
start=System.currentTimeMillis();
straightInsertSort(elem,n);
end=System.currentTimeMillis();
System.out.println("简单插入排序:");
System.out.println(end-start);
elem=elem(n);
start=System.currentTimeMillis();
shellSort(elem,n);
end=System.currentTimeMillis();
System.out.println("希尔排序:");
System.out.println(end-start);
elem=elem(n);
start=System.currentTimeMillis();
bubbleSort(elem,n);
end=System.currentTimeMillis();
System.out.println("冒泡排序:");
System.out.println(end-start);
/*
elem=elem(n);
start=System.currentTimeMillis();
quickSort(elem,n);
end=System.currentTimeMillis();
System.out.println("快速排序:");
System.out.println(end-start);*/
elem=elem(n);
start=System.currentTimeMillis();
simpleSelectionSort(elem,n);
end=System.currentTimeMillis();
System.out.println("简单选择排序:");
System.out.println(end-start);
elem=elem(n);
start=System.currentTimeMillis();
heapSort(elem,n);
end=System.currentTimeMillis();
System.out.println("堆排序:");
System.out.println(end-start);
elem=elem(n);
start=System.currentTimeMillis();
mergeSort(elem,n);
end=System.currentTimeMillis();
System.out.println("归并排序:");
System.out.println(end-start);
}
//显示排序结果
public static <T extends Comparable<? super T>> void show(T[] elem,int n){
for (int i=0;i<n;i++){
System.out.print(elem[i]);
System.out.print(' ');
}
System.out.println();
}
//交换元素
private static <T extends Comparable<? super T>> void swap(T[] elem,int i,int j){
T tmp=elem[i];
elem[i]=elem[j];
elem[j]=tmp;
}
//直接插入排序法,复杂度为O(n^2)
public static <T extends Comparable<? super T>> void straightInsertSort (T elem[],int n){
for (int i=1;i<n;i++){
T e=elem[i];
int j;
for (j=i-1;j>=0 && e.compareTo(elem[j])<0;j--){
elem[j+1]=elem[j];
}
elem[j+1]=e;
}
}
//shell插入排序算法,复杂度为O(n^1.5)
private static <T extends Comparable<? super T>> void shellInsertHelp(T elem[],int n,int incr){
for (int i=incr;i<n;i++){
T e=elem[i];
int j=i-incr;
for (;j>=0 && e.compareTo(elem[j])<0;j=j-incr){
elem[j+incr]=elem[j];
}
elem[j+incr]=e;
}
}
public static <T extends Comparable<? super T>> void shellSort(T elem[],int n ){
for (int incr=n/2;incr>0;incr=incr/2){
shellInsertHelp(elem,n,incr);
}
}
//冒泡排序算法,时间复杂度为O(n^2)
public static <T extends Comparable<? super T>> void bubbleSort(T elem[],int n){
for (int i=n-1;i>0;i--){
for (int j=0;j<i;j++){
if (elem[j].compareTo(elem[i])>0){
swap(elem,i,j);
}
}
}
}
//快速排序算法,时间复杂度为O(n*log(n))
private static <T extends Comparable<? super T>> int partition(T elem[],int low,int high){
while (low<high){
for (;elem[high].compareTo(elem[low])>=0 && low<high;high--);
swap(elem,high,low);
for (;elem[high].compareTo(elem[low])>=0 && low<high;low++);
swap(elem,high,low);
}
return low;
}
private static <T extends Comparable<? super T>> void quickSortHelp(T elem[],int low,int high){
if (low<high){
int pivot=partition(elem,low,high);
quickSortHelp(elem,low,pivot-1);
quickSortHelp(elem,pivot+1,high);
}
}
public static <T extends Comparable<? super T>> void quickSort(T elem[],int n){
quickSortHelp(elem,0,n-1);
}
//简单选择排序算法,时间复杂度为O(n^2)
public static <T extends Comparable<? super T>> void simpleSelectionSort(T elem[],int n){
for (int i=0;i<n-1;i++){
int lowIdx=i;
for (int j=i+1;j<n;j++){
if (elem[lowIdx].compareTo(elem[j])>0)
lowIdx=j;
}
swap(elem,lowIdx,i);
}
}
//堆排序,时间复杂度为O(n*log(n))
private static <T extends Comparable<? super T>> void heapAdjust(T elem[],int low,int high){
for (int i=low,lhs=2*i+1 ;lhs<=high;lhs=2*i+1){
if (lhs<high && elem[lhs].compareTo(elem[lhs+1])<0)lhs++;
if (elem[i].compareTo(elem[lhs])<0){
swap(elem,i,lhs);
i=lhs;
}else break;
}
}
public static <T extends Comparable<? super T>> void heapSort(T elem[],int n){
//初始化堆
for (int i=(n-2)/2;i>=0;i--){
heapAdjust(elem,i,n-1);
}
swap(elem,0,n-1);
//排序
for (int i=n-2;i>0;--i){
heapAdjust(elem,0,i);
swap(elem,0,i);
}
}
//归并排序算法,时间复杂度为O(n*log(n))
private static <T extends Comparable<? super T>> void simpleMerge(T elem[],T tmpElem[],int low ,int mid, int high){
int i=low,j=mid+1,k=low;
for (;i<=mid && j<=high;k++){
if (elem[i].compareTo(elem[j])<=0)
tmpElem[k]=elem[i++];
else
tmpElem[k]=elem[j++];
}
for (;i<=mid;i++){
tmpElem[k++]=elem[i];
}
for (;j<=high;j++){
tmpElem[k++]=elem[j];
}
for (;low<=high;low++){
elem[low]=tmpElem[low];
}
}
private static <T extends Comparable<? super T>> void mergeHelp(T elem[],T tmpElem[],int low ,int high){
if (low < high){
int mid=(low+high)/2;
mergeHelp(elem,tmpElem,low,mid);
mergeHelp(elem,tmpElem,mid+1,high);
simpleMerge(elem,tmpElem,low,mid,high);
}
}
public static <T extends Comparable<? super T>> void mergeSort(T elem[],int n){
T[] tmpElem=(T[])new Comparable[n];
mergeHelp(elem,tmpElem,0,n-1);
}
}
--创建用户
CREATE USER "APITEST" PROFILE "DEFAULT"
IDENTIFIED BY "apitest" DEFAULT TABLESPACE "LOUSHANG"
TEMPORARY TABLESPACE "TEMP"
ACCOUNT UNLOCK;
--为用户指定表空间
GRANT UNLIMITED TABLESPACE TO "APITEST";
--为用户授权
GRANT "CONNECT" TO "APITEST";
GRANT "DBA" TO "APITEST";
GRANT "RESOURCE" TO "APITEST";
--将锁定用户解锁
alter user <用户名> account unlock;
--修改用户密码
alter user <用户名> identified by <新密码>;
--删除用户
drop user apitest; ----仅仅是删除用户,
drop user apitest cascade ;----会删除此用户名下的所有表和视图。
---查看当前用户信息
select * from user_users;
---查询当前数据库实例中有哪些用户
select * from dba_users order by username;
---查看当前用户拥有的角色
select * from user_role_privs;
---查看当前用户所拥有的表
select * from user_tables;
---查看当前用户所拥有表的列
select * from USER_TAB_COLUMNS ;
---显示特权用户(一般包括sys、system)
select * from v$pwfile_users;
---查询当前用户所拥有的所有对象(表、视图、索引、存储函数和过程等)
select * from user_objects
----查看序列号
select * from user_sequences;
---查看当前用户所有的视图
select * from user_views;
--查看当前连接信息
select SID,SERIAL#,USERNAME,MACHINE,LOGON_TIME from v$session where username='APITEST';
--断开指定连接
alter system kill session '530,49177';
DAO层的代码分页代码:
public PageModel findByPageModel(String hql,PageModel pm) {
pm.setTotalCount(this.getHibernateTemplate().find(hql).size());
pm.setGoToHref(ServletActionContext.getRequest().getServletPath().replace("/",""));
int totalCount = pm.getTotalCount();
int pageSize = pm.getPageSize();
int totalPage = (totalCount+pageSize-1)/pageSize ;
int currentPage = pm.getCurrentPage() ;
pm.setTotalPage(totalPage);
int offset = (currentPage-1)*pageSize;
pm.setList(this.getSession().createQuery(hql).setFirstResult(offset).setMaxResults(pageSize).list());
return pm;
}
分页的JAVABEAN:
public class PageModel {
private int currentPage;
private int pageSize;
private int totalCount;
private int totalPage;
private List list ;
private String goToHref;
public int getCurrentPage() {
if(currentPage<=0) currentPage=1;
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public int getPageSize() {
if(pageSize<=0) pageSize=10;
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
public int getTotalPage() {
return totalPage;
}
public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}
public List getList() {
return list;
}
public void setList(List list) {
this.list = list;
}
public String getGoToHref() {
return goToHref;
}
public void setGoToHref(String goToHref) {
this.goToHref = goToHref;
}
}
JSP页面:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<link rel="stylesheet" type="text/css" href="<%=basePath %>findByHql/pagingBar/css/pagingBar.css">
<input type="button" class="firstPage commonPage" alt="首页" title="首页"/>
<input type="button" class="beforePage commonPage" alt="上一页" title="上一页"/>
<input type="button" class="nextPage commonPage" alt="下一页" title="下一页"/>
<input type="button" class="lastPage commonPage" alt="尾页" title="尾页" />
<input type="hidden" id="currentPage" value="${requestScope.pm.currentPage }" />
<input type="hidden" id="totalPage" value="${requestScope.pm.totalPage }" />
<input type="hidden" id="goToHref" value="${requestScope.pm.goToHref }" />
<span class="cp">当前第${requestScope.pm.currentPage }页</span>
<span class="tc"> 相关资讯:${requestScope.pm.totalCount }条</span>
<span class="ps">每页${requestScope.pm.pageSize }条 </span>
<span class="tp">共${requestScope.pm.totalPage}页</span>
<script type="text/javascript" src="<%=basePath%>js/jquery.js"></script>
<script type="text/javascript">
(function($) {
var currentPage = parseInt($('#currentPage').val());
var totalPage = parseInt($('#totalPage').val());
var toHref = $('#goToHref').val();
$('.firstPage').bind('click', function() {
goToHref(1);
});
$('.nextPage').bind('click', function() {
if (currentPage >= totalPage)
goToHref(totalPage);
else
goToHref(currentPage + 1);
});
$('.beforePage').bind('click', function() {
if (currentPage <= 1)
goToHref(1);
else
goToHref(currentPage - 1);
});
$('.lastPage').bind('click', function() {
goToHref(totalPage);
});
function goToHref(cp) {
document.location.href = toHref+"?currentPage=" + cp;
}
})(jQuery)
</script>
CSS:下面有几张图片需要自己找...
/*点击栏*/
.commonPage{
width: 16px;
height: 16px;
border: none;
cursor: pointer;
}
.firstPage{
background: url("../images/page-first.png") no-repeat;
}
.nextPage{
background: url("../images/page-next.png") no-repeat;
}
.beforePage{
background: url("../images/page-prev.png") no-repeat;
}
.lastPage{
background: url("../images/page-last.png") no-repeat;
}
/*显示栏*/
.cp,.tc,.ps,.tp{
font-size: 14px;
}
在action中调用DAO层的方法,给currentPage和pageSize设置初始值,然后就返回一个list到你分页的页面迭代,以后就直接嵌套在分页页面中就行
这也许是你一直期待的文章,在关注这部分技术问题的同时,请务必阅读有关面试中有关个人的问题和解答。这里的回答并不是十分全面,这些问题可以通过多个角度来进行解释,也许你不必在面试过程中给出完全详尽的答案,只需要通过你的解答使面试考官了解你对ORACLE概念的熟悉程度。
1.解释冷备份和热备份的不同点以及各自的优点
解答:热备份针对归档模式的数据库,在数据库仍旧处于工作状态时进行备份。而冷备份指在数据库关闭后,进行备份,适用于所有模式的数据库。热备份的优点在于当备份时,数据库仍旧可以被使用并且可以将数据库恢复到任意一个时间点。冷备份的优点在于它的备份和恢复操作相当简单,并且由于冷备份的数据库可以工作在非归档模式下,数据库性能会比归档模式稍好。(因为不必将archive log写入硬盘)
2.你必须利用备份恢复数据库,但是你没有控制文件,该如何解决问题呢?
解答:重建控制文件,用带backup control file 子句的recover 命令恢复数据库。
3.如何转换init.ora到spfile?
解答:使用create spfile from pfile 命令.
4.解释data block , extent 和 segment的区别(这里建议用英文术语)
解答:data block是数据库中最小的逻辑存储单元。当数据库的对象需要更多的物理存储空间时,连续的data block就组成了extent . 一个数据库对象拥有的所有extents被称为该对象的segment.
5.给出两个检查表结构的方法
解答:1.DESCRIBE命令
2.DBMS_METADATA.GET_DDL 包
6.怎样查看数据库引擎的报错
解答:alert log.
7.比较truncate和delete 命令
解答:两者都可以用来删除表中所有的记录。区别在于:truncate是DDL操作,它移动HWK,不需要rollback segment .而Delete是DML操作, 需要rollback segment 且花费较长时间.
8.使用索引的理由
解答:快速访问表中的data block
9.给出在STAR SCHEMA中的两种表及它们分别含有的数据
解答:Fact tables 和dimension tables. fact table包含大量的主要的信息而dimension tables 存放对fact table 某些属性描述的信息
10.FACT Table上需要建立何种索引?
解答:位图索引 (bitmap index)
11. 给出两种相关约束?
解答:主键和外键
12. 如何在不影响子表的前提下,重建一个母表
解答:子表的外键强制实效,重建母表,激活外键
13. 解释归档和非归档模式之间的不同和它们各自的优缺点
解答:归档模式是指你可以备份所有的数据库 transactions并恢复到任意一个时间点。非归档模式则相反,不能恢复到任意一个时间点。但是非归档模式可以带来数据库性能上的少许提高.
14. 如何建立一个备份控制文件?
解答:Alter database backup control file to trace.
15. 给出数据库正常启动所经历的几种状态 ?
解答:STARTUP NOMOUNT – 数据库实例启动
STARTUP MOUNT - 数据库装载
STARTUP OPEN – 数据库打开
16. 哪个column可以用来区别V$视图和GV$视图?
解答:INST_ID 指明集群环境中具体的 某个instance 。
17. 如何生成explain plan?
解答:运行utlxplan.sql. 建立plan 表
针对特定SQL语句,使用 explain plan set statement_id = 'tst1' into plan_table
运行utlxplp.sql 或 utlxpls.sql察看explain plan
18. 如何增加buffer cache的命中率?
解答:在数据库较繁忙时,适用buffer cache advisory 工具,查询v$db_cache_advice.如果有必要更改,可以使用 alter system set db_cache_size 命令
19. ORA-01555的应对方法?
解答:具体的出错信息是snapshot too old within rollback seg , 通常可以通过增大rollback seg来解决问题。当然也需要察看一下具体造成错误的SQL文本
20. 解释$ORACLE_HOME和$ORACLE_BASE的区别?
解答:ORACLE_BASE是oracle的根目录,ORACLE_HOME是oracle产品的目录。
技术债务, 是指匆忙的实现一个功能,却对现有的程序库造成了破坏(在实现的过程中污染了代码库的设计),这对于一些项目经理/客户来说就像是天书奇谈。也许他们是明 白的,只是不愿意承认罢了,我估计是这样的。不管怎样,我想起来一个小故事,当下次遇到这种情况,需要向他们解释增加某些新功能的代价时,也可用讲这个故 事给他们听。
一个农夫有3只母鸡。每只母鸡每天下一个蛋。农夫跟当地的一个食品店老板做生意。食品店老板每天从农夫那里买2给鸡蛋放在店里出售。一切都很好,直到有一天,食品店老板出现在农夫家里:
食品店老板: 哎呀,今天我需要一些鸡肉。
农夫: 鸡肉?你和我的生意里可不包括这些。
食品店老板: 我知道。但我真的需要一些鸡肉。我计划要做一个B2S(S是胃的缩写)模式的PaaS(P是肉禽的缩写)平台。
农夫: 什么?
食品店老板: 非常重要的东西。你可以提供我一些鸡肉吗?
农夫: 这样呀,事情不是那么容易办到 — 我要孵化鸡蛋,等小鸡长大了才能给你…少说也要一个月吧。
食品店老板: 一个月?太久了…我以为你现在就能给我呢。
农夫: 时间有自己的脚步,你必须耐心一点等。
食品店老板: 可是,为什么你不能在现有的母鸡中杀一个呢?这样一来,我有了鸡肉,你每天还能产两个蛋。这就够了,不是吗?
农夫: 可是,我不觉得这是一个好主意。这会把我推向一个没有回旋余地的境况,万一剩下的鸡中有一只突然出了什么意外怎么办。
食品店老板: 放心啦,不会发生那样的事的…我真的非常非常需要鸡肉!杀一只鸡吧!
农夫: 那好吧,我想我可以…
于是,农夫拿起一把刀,把他的一只母鸡送入了天堂。食品店老板得到了他的鸡肉,返回了食品店。
一周后,食品店老板又一次来到了农夫家里:
食品店老板: 你好,我来了!
农夫: 你好,有什么事?
食品店老板: 你听我说 — 你的鸡肉好极了。事实上,它是如此的鲜美,卖的如此的好,你必须要再给我一只鸡。最迟明天早上。
农夫: 这是不可能的事。如果我要再杀一只鸡给你,我就没法每天提供你两个鸡蛋了。
食品店老板: 哦,别那么紧张!客户需要鸡肉,我已经答应客户明天早上提供给他们了…
农夫: 不行,绝对不能这么干。如果我这么做,我就履行不了我和你的协议了,你知道吗?如果我这么做,我就没法提供你足够的鸡蛋了。
食品店老板: 可是我真的真的需要鸡肉!明天早上之前!否则客户会发飙的,地球将会塌陷,世界末日将会到来!给我一只鸡吧,现在!
农夫: 那好吧,如果你非要这么不顾后果的想要,那就拿去吧!但是,从现在开始,鸡蛋我是没法提供你了,明白?
食品店老板: 当然,当然。但我相信是个很聪明的人,我猜你能找到方法解决这个问题。再见!
食品店老板离开回到了店里。
第二天:
食品店老板: 嗨,鸡蛋呢?
农夫: 你什么意思?
食品店老板: 鸡蛋。你只给了我一个鸡蛋。发生了什么事?
农夫: 发生了什么事?我有3只鸡,你拿走了两只。现在就剩下一只。一只鸡,一个鸡蛋。我认为我解释的已经很清楚了。
食品店老板: 但是合同里并没有这些!合同里说的很清楚 — 你每天提供我2给鸡蛋!你现在让我向客户怎么交代?
农夫: 哦,情况我很明白。我无能为力。
食品店老板: 好吧,好吧,不谈这事了。咱们聊点其它事情…要是能再能点鸡肉就好了。你再给我一些吧?
所以,千万别学农夫 — 坚决拒绝为了当前利益而长久的破坏你的代码库的无理要求,如果你被强迫这样做,拒绝承担这样的任务 — 也不要做食品店老板 — 不要做提出这样不合理的要求,你要为自己的决定承担后果。
[代码] web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
<!--spring 的配置文件-->
classpath:/applicationContext-hibernate.xml
</param-value>
</context-param>
<!-- shiro -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Listeners -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
[代码] applicationContext-hibernate.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- SessionFactory, DataSource, etc. omitted -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan">
<list>
<value>org.projects.graduates.domain</value>
</list>
</property>
<property name="hibernateProperties">
<value>hibernate.dialect=${hibernate.dialect}</value>
</property>
</bean>
<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="find*" read-only="true" />
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="appOperation"
expression="execution(* org.projects.graduates.app.GradApplication.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="appOperation" />
</aop:config>
<!-- shiro -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login.action" />
<property name="successUrl" value="/main.action" />
<property name="unauthorizedUrl" value="/login.action" />
<property name="filterChainDefinitions">
<value>
/index.action = anon
/login.action = anon
/main.action = authc, roles[admin]
/course/** = authc, roles[admin]
</value>
</property>
</bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!--设置自定义realm-->
<property name="realm" ref="myRealm" />
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<!--myRealm 继承自AuthorizingRealm-->
<bean id="myRealm" class="org.projects.graduates.shiro.GradRealm" ></bean>
<bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod"
value="org.apache.shiro.SecurityUtils.setSecurityManager" />
<property name="arguments" ref="securityManager" />
</bean>
</beans>
[代码] org.projects.graduates.shiro.GradRealm
public class GradRealm extends AuthorizingRealm {
private SecurityApplication securityApplication = new SecurityApplicationImpl();
public GradRealm() {
super();
//设置认证token的实现类
setAuthenticationTokenClass(UsernamePasswordToken.class);
//设置加密算法
setCredentialsMatcher(new HashedCredentialsMatcher(Sha1Hash.ALGORITHM_NAME));
}
//授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String loginName = (String) principalCollection.fromRealm(getName()).iterator().next();
User user = securityApplication.findby(loginName);
if (null == user) {
return null;
} else {
SimpleAuthorizationInfo result = new SimpleAuthorizationInfo();
result.addRoles(UserRoles.findRoleNamesOf(user));
for (Role role : UserRoles.findRolesOf(user)) {
result.addStringPermissions(role.getPermissions());
}
return result;
}
}
//认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
User user = securityApplication.findby(upToken.getUsername());
if (user != null) {
return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
}
return null;
}
}
问题
某海量用户网站,用户拥有积分,积分可能会在使用过程中随时更新。现在要为该网站设计一种算法,在每次用户登录时显示其当前积分排名。用户最大规模为2亿;积分为非负整数,且小于100万。
PS: 据说这是迅雷的一道面试题,不过问题本身具有很强的真实性,所以本文打算按照真实场景来考虑,而不局限于面试题的理想环境。
存储结构
首先,我们用一张用户积分表user_score来保存用户的积分信息:
表结构:
示例数据:
下面的算法会基于这个基本的表结构来进行。
算法1:简单SQL查询
首先,我们很容易想到用一条简单的SQL语句查询出积分大于该用户积分的用户数量:
select 1 + count(t2.uid) as rank
from user_score t1, user_score t2
where t1.uid = @uid and t2.score > t1.score
对于4号用户我们可以得到下面的结果:
算法1总结
优点:简单,利用了SQL的功能,不需要复杂的查询逻辑,也不引入额外的存储结构,对小规模或性能要求不高的应用不失为一种良好的解决方案。
缺点:需要对user_score表进行全表扫描,还需要考虑到查询的同时若有积分更新会对表造成锁定,在海量数据规模和高并发的应用中,性能是无法接受的。
算法2:均匀分区设计
在许多应用中缓存是解决性能问题的重要途径,我们自然会想能不能把用户排名用Memcached缓存下来呢?不过再一想发现缓存似乎帮不上什么忙,因为用户排名是一个全局性的统计性指标,而并非用户的私有属性,其他用户的积分变化可能会马上影响到本用户的排名。然而,真实的应用中积分的变化其实也是有一定规律的,通常一个用户的积分不会突然暴增暴减,一般用户总是要在低分区混迹很长一段时间才会慢慢升入高分区,也就是说用户积分的分布总体说来是有区段的,我们进一步注意到高分区用户积分的细微变化其实对低分段用户的排名影响不大。于是,我们可以想到按积分区段进行统计的方法,引入一张分区积分表score_range:
表结构:
数据示例:
表示[from_score, to_score)区间有count个用户。若我们按每1000分划分一个区间则有[0, 1000), [1000, 2000), …, [999000, 1000000)这1000个区间,以后对用户积分的更新要相应地更新score_range表的区间值。在分区积分表的辅助下查询积分为s的用户的排名,可以首先确定其所属区间,把高于s的积分区间的count值累加,然后再查询出该用户在本区间内的排名,二者相加即可获得用户的排名。
乍一看,这个方法貌似通过区间聚合减少了查询计算量,实则不然。最大的问题在于如何查询用户在本区间内的排名呢?如果是在算法1中的SQL中加上积分条件:
select 1 + count(t2.uid) as rank
from user_score t1, user_score t2
where t1.uid = @uid and t2.score > t1.score and t2.score < @to_score
在理想情况下,由于把t2.score的范围限制在了1000以内,如果对score字段建立索引,我们期望本条SQL语句将通过索引大大减少扫描的user_score表的行数。不过真实情况并非如此,t2.score的范围在1000以内并不意味着该区间内的用户数也是1000,因为这里有积分相同的情况存在!二八定律告诉我们,前20%的低分区往往集中了80%的用户,这就是说对于大量低分区用户进行区间内排名查询的性能远不及对少数的高分区用户,所以在一般情况下这种分区方法不会带来实质性的性能提升。
算法2总结
优点:注意到了积分区间的存在,并通过预先聚合消除查询的全表扫描
缺点:积分非均匀分布的特点使得性能提升并不理想
算法3:树形分区设计
均匀分区查询算法的失败是由于积分分布的非均匀性,那么我们自然就会想,能不能按二八定律,把score_range表设计为非均匀区间呢?比如,把低分区划密集一点,10分一个区间,然后逐渐变成100分,1000分,10000分 … 当然,这不失为一种方法,不过这种分法有一定的随意性,不容易把握好,而且整个系统的积分分布会随着使用而逐渐发生变化,最初的较好的分区方法可能会变得不适应未来的情况了。我们希望找到一种分区方法,既可以适应积分非均匀性,又可以适应系统积分分布的变化,这就是树形分区。
我们可以把[0, 1,000,000)作为一级区间;再把一级区间分为两个2级区间[0, 500,000), [500,000, 1,000,000),然后把二级区间二分为4个3级区间[0, 250,000), [250,000, 500,000), [500,000, 750,000), [750,000, 1,000,000),依此类推,最终我们会得到1,000,000个21级区间[0,1), [1,2) … [999,999, 1,000,000)。这实际上是把区间组织成了一种平衡二叉树结构,根结点代表一级区间,每个非叶子结点有两个子结点,左子结点代表低分区间,右子结点代表高分区间。树形分区结构需要在更新时保持一种不变量(Invariant):非叶子结点的count值总是等于其左右子结点的count值之和。
以后,每次用户积分有变化所需要更新的区间数量和积分变化量有关系,积分变化越小更新的区间层次越低。总体上,每次所需要更新的区间数量是用户积分变量的log(n)级别的,也就是说如果用户积分一次变化在百万级,更新区间的数量在二十这个级别。在这种树形分区积分表的辅助下查询积分为s的用户排名,实际上是一个在区间树上由上至下、由粗到细一步步明确s所在位置的过程。比如,对于积分499,000,我们用一个初值为0的排名变量来做累加;首先,它属于1级区间的左子树[0, 500,000),那么该用户排名应该在右子树[500,000, 1,000,000)的用户数count之后,我们把该count值累加到该用户排名变量,进入下一级区间;其次,它属于3级区间的[250,000, 500,000),这是2级区间的右子树,所以不用累加count到排名变量,直接进入下一级区间;再次,它属于4级区间的…;直到最后我们把用户积分精确定位在21级区间[499,000, 499,001),整个累加过程完成,得出排名!
虽然,本算法的更新和查询都涉及到若干个操作,但如果我们为区间的from_score和to_score建立索引,这些操作都是基于键的查询和更新,不会产生表扫描,因此效率更高。另外,本算法并不依赖于关系数据模型和SQL运算,可以轻易地改造为NoSQL等其他存储方式,而基于键的操作也很容易引入缓存机制进一步优化性能。
算法3总结
优点:结构稳定,不受积分分布影响;每次查询或更新的复杂度为积分最大值的log(n)级别,且与用户规模无关,可以应对海量规模;不依赖于SQL,容易改造为NoSQL等其他存储方式
缺点:算法相对更复杂
总结
上面介绍了用户积分排名的3种算法,算法1简单易于理解和实现,适用于小规模和低并发应用;算法3引入了更复杂的树形分区结构,但是性能优越,可以应用于海量规模和高并发。本问题是一个开放性的问题,相信一定还有其他优秀的算法和解决方案,欢迎探讨!
public static void TestStr(){
//null 和 ""操作~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//判断是否Null 或者 ""
//System.out.println(StringUtils.isEmpty(null));
//System.out.println(StringUtils.isNotEmpty(null));
//判断是否null 或者 "" 去空格~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//System.out.println(StringUtils.isBlank(" "));
//System.out.println(StringUtils.isNotBlank(null));
//去空格.Null返回null~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//System.out.println(StringUtils.trim(null));
//去空格,将Null和"" 转换为Null
//System.out.println(StringUtils.trimToNull(""));
//去空格,将NULL 和 "" 转换为""
//System.out.println(StringUtils.trimToEmpty(null));
//可能是对特殊空格符号去除??
//System.out.println(StringUtils.strip("大家好 啊 \t"));
//同上,将""和null转换为Null
//System.out.println(StringUtils.stripToNull(" \t"));
//同上,将""和null转换为""
//System.out.println(StringUtils.stripToEmpty(null));
//将""或者Null 转换为 ""
//System.out.println(StringUtils.defaultString(null));
//仅当字符串为Null时 转换为指定的字符串(二参数)
//System.out.println(StringUtils.defaultString("", "df"));
//当字符串为null或者""时,转换为指定的字符串(二参数)
//System.out.println(StringUtils.defaultIfEmpty(null, "sos"));
//去空格.去字符~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//如果第二个参数为null去空格(否则去掉字符串2边一样的字符,到不一样为止)
//System.out.println(StringUtils.strip("fsfsdf", "f"));
//如果第二个参数为null只去前面空格(否则去掉字符串前面一样的字符,到不一样为止)
//System.out.println(StringUtils.stripStart("ddsuuu ", "d"));
//如果第二个参数为null只去后面空格,(否则去掉字符串后面一样的字符,到不一样为止)
//System.out.println(StringUtils.stripEnd("dabads", "das"));
//对数组没个字符串进行去空格。
//ArrayToList(StringUtils.stripAll(new String[]{" 中华 ", "民 国 ", "共和 "}));
//如果第二个参数为null.对数组每个字符串进行去空格。(否则去掉数组每个元素开始和结尾一样的字符)
//ArrayToList(StringUtils.stripAll(new String[]{" 中华 ", "民 国", "国共和国"}, "国"));
//查找,判断~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//判断2个字符串是否相等相等,Null也相等
//System.out.println(StringUtils.equals(null, null));
//不区分大小写比较
//System.out.println(StringUtils.equalsIgnoreCase("abc", "ABc"));
//查找,不知道怎么弄这么多查找,很多不知道区别在哪?费劲~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//普通查找字符,如果一参数为null或者""返回-1
//System.out.println(StringUtils.indexOf(null, "a"));
//从指定位置(三参数)开始查找,本例从第2个字符开始查找k字符
//System.out.println(StringUtils.indexOf("akfekcd中华", "k", 2));
//未发现不同之处
//System.out.println(StringUtils.ordinalIndexOf("akfekcd中华", "k", 2));
//查找,不区分大小写
//System.out.println(StringUtils.indexOfIgnoreCase("adfs", "D"));
//从指定位置(三参数)开始查找,不区分大小写
//System.out.println(StringUtils.indexOfIgnoreCase("adfs", "a", 3));
//从后往前查找
//System.out.println(StringUtils.lastIndexOf("adfas", "a"));
//未理解,此结果为2
//System.out.println(StringUtils.lastIndexOf("d饿abasdafs我", "a", 3));
//未解,此结果为-1
//System.out.println(StringUtils.lastOrdinalIndexOf("yksdfdht", "f", 2));
//从后往前查,不区分大小写
//System.out.println(StringUtils.lastIndexOfIgnoreCase("sdffet", "E"));
//未解,此结果为1
//System.out.println(StringUtils.lastIndexOfIgnoreCase("efefrfs看", "F" , 2));
//检查是否查到,返回boolean,null返回假
//System.out.println(StringUtils.contains("sdf", "dg"));
//检查是否查到,返回boolean,null返回假,不区分大小写
//System.out.println(StringUtils.containsIgnoreCase("sdf", "D"));
//检查是否有含有空格,返回boolean
//System.out.println(StringUtils.containsWhitespace(" d"));
//查询字符串跟数组任一元素相同的第一次相同的位置
//System.out.println(StringUtils.indexOfAny("absfekf", new String[]{"f", "b"}));
//查询字符串中指定字符串(参数二)出现的次数
//System.out.println(StringUtils.indexOfAny("afefes", "e"));
//查找字符串中是否有字符数组中相同的字符,返回boolean
//System.out.println(StringUtils.containsAny("asfsd", new char[]{'k', 'e', 's'}));
//未理解与lastIndexOf不同之处。是否查到,返回boolean
//System.out.println(StringUtils.containsAny("啡f咖啡", "咖"));
//未解
//System.out.println(StringUtils.indexOfAnyBut("seefaff", "af"));
//判断字符串中所有字符,都是出自参数二中。
//System.out.println(StringUtils.containsOnly("中华华", "华"));
//判断字符串中所有字符,都是出自参数二的数组中。
//System.out.println(StringUtils.containsOnly("中华中", new char[]{'中', '华'}));
//判断字符串中所有字符,都不在参数二中。
//System.out.println(StringUtils.containsNone("中华华", "国"));
//判断字符串中所有字符,都不在参数二的数组中。
//System.out.println(StringUtils.containsNone("中华中", new char[]{'中', '达人'}));
//从后往前查找字符串中与字符数组中相同的元素第一次出现的位置。本例为4
//System.out.println(StringUtils.lastIndexOfAny("中国人民共和国", new String[]{"国人", "共和"}));
//未发现与indexOfAny不同之处 查询字符串中指定字符串(参数二)出现的次数
//System.out.println(StringUtils.countMatches("中国人民共和中国", "中国"));
//检查是否CharSequence的只包含Unicode的字母。空将返回false。一个空的CharSequence(长()= 0)将返回true
//System.out.println(StringUtils.isAlpha("这是干什么的2"));
//检查是否只包含Unicode的CharSequence的字母和空格('')。空将返回一个空的CharSequence假(长()= 0)将返回true。
//System.out.println(StringUtils.isAlphaSpace("NBA直播 "));
//检查是否只包含Unicode的CharSequence的字母或数字。空将返回false。一个空的CharSequence(长()= 0)将返回true。
//System.out.println(StringUtils.isAlphanumeric("NBA直播"));
//如果检查的Unicode CharSequence的只包含字母,数字或空格('')。空将返回false。一个空的CharSequence(长()= 0)将返回true。
//System.out.println(StringUtils.isAlphanumericSpace("NBA直播"));
//检查是否只包含ASCII可CharSequence的字符。空将返回false。一个空的CharSequence(长()= 0)将返回true。
//System.out.println(StringUtils.isAsciiPrintable("NBA直播"));
//检查是否只包含数值。
//System.out.println(StringUtils.isNumeric("NBA直播"));
//检查是否只包含数值或者空格
//System.out.println(StringUtils.isNumericSpace("33 545"));
//检查是否只是空格或""。
//System.out.println(StringUtils.isWhitespace(" "));
//检查是否全是英文小写。
//System.out.println(StringUtils.isAllLowerCase("kjk33"));
//检查是否全是英文大写。
//System.out.println(StringUtils.isAllUpperCase("KJKJ"));
//交集操作~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//去掉参数2字符串中在参数一中开头部分共有的部分,结果为:人民共和加油
//System.out.println(StringUtils.difference("中国加油", "中国人民共和加油"));
//统计2个字符串开始部分共有的字符个数
//System.out.println(StringUtils.indexOfDifference("ww.taobao", "www.taobao.com"));
//统计数组中各个元素的字符串开始都一样的字符个数
//System.out.println(StringUtils.indexOfDifference(new String[] {"中国加油", "中国共和", "中国人民"}));
//取数组每个元素共同的部分字符串
//System.out.println(StringUtils.getCommonPrefix(new String[] {"中国加油", "中国共和", "中国人民"}));
//统计参数一中每个字符与参数二中每个字符不同部分的字符个数
//System.out.println(StringUtils.getLevenshteinDistance("中国共和发国人民", "共和国"));
//判断开始部分是否与二参数相同
//System.out.println(StringUtils.startsWith("中国共和国人民", "中国"));
//判断开始部分是否与二参数相同。不区分大小写
//System.out.println(StringUtils.startsWithIgnoreCase("中国共和国人民", "中国"));
//判断字符串开始部分是否与数组中的某一元素相同
//System.out.println(StringUtils.startsWithAny("abef", new String[]{"ge", "af", "ab"}));
//判断结尾是否相同
//System.out.println(StringUtils.endsWith("abcdef", "def"));
//判断结尾是否相同,不区分大小写
//System.out.println(StringUtils.endsWithIgnoreCase("abcdef", "Def"));
//字符串截取~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//截取指定位置的字符,null返回null.""返回""
//System.out.println(StringUtils.substring("国民党", 2));
//截取指定区间的字符
//System.out.println(StringUtils.substring("中国人民共和国", 2, 4));
//从左截取指定长度的字符串
//System.out.println(StringUtils.left("说点什么好呢", 3));
//从右截取指定长度的字符串
//System.out.println(StringUtils.right("说点什么好呢", 3));
//从第几个开始截取,三参数表示截取的长度
//System.out.println(StringUtils.mid("说点什么好呢", 3, 2));
//截取到等于第二个参数的字符串为止
//System.out.println(StringUtils.substringBefore("说点什么好呢", "好"));
//从左往右查到相等的字符开始,保留后边的,不包含等于的字符。本例:什么好呢
//System.out.println(StringUtils.substringAfter("说点什么好呢", "点"));
//这个也是截取到相等的字符,但是是从右往左.本例结果:说点什么好
//System.out.println(StringUtils.substringBeforeLast("说点什么好点呢", "点"));
//这个截取同上是从右往左。但是保留右边的字符
//System.out.println(StringUtils.substringAfterLast("说点什么好点呢?", "点"));
//截取查找到第一次的位置,和第二次的位置中间的字符。如果没找到第二个返回null。本例结果:2010世界杯在
//System.out.println(StringUtils.substringBetween("南非2010世界杯在南非,在南非", "南非"));
//返回参数二和参数三中间的字符串,返回数组形式
//ArrayToList(StringUtils.substringsBetween("[a][b][c]", "[", "]"));
//分割~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//用空格分割成数组,null为null
//ArrayToList(StringUtils.split("中华 人民 共和"));
//以指定字符分割成数组
//ArrayToList(StringUtils.split("中华 ,人民,共和", ","));
//以指定字符分割成数组,第三个参数表示分隔成数组的长度,如果为0全体分割
//ArrayToList(StringUtils.split("中华 :人民:共和", ":", 2));
//未发现不同的地方,指定字符分割成数组
//ArrayToList(StringUtils.splitByWholeSeparator("ab-!-cd-!-ef", "-!-"));
//未发现不同的地方,以指定字符分割成数组,第三个参数表示分隔成数组的长度
//ArrayToList(StringUtils.splitByWholeSeparator("ab-!-cd-!-ef", "-!-", 2));
//分割,但" "不会被忽略算一个元素,二参数为null默认为空格分隔
//ArrayToList(StringUtils.splitByWholeSeparatorPreserveAllTokens(" ab de fg ", null));
//同上,分割," "不会被忽略算一个元素。第三个参数代表分割的数组长度。
//ArrayToList(StringUtils.splitByWholeSeparatorPreserveAllTokens("ab de fg", null, 3));
//未发现不同地方,分割
//ArrayToList(StringUtils.splitPreserveAllTokens(" ab de fg "));
//未发现不同地方,指定字符分割成数组
//ArrayToList(StringUtils.splitPreserveAllTokens(" ab de fg ", null));
//未发现不同地方,以指定字符分割成数组,第三个参数表示分隔成数组的长度
//ArrayToList(StringUtils.splitPreserveAllTokens(" ab de fg ", null, 2));
//以不同类型进行分隔
//ArrayToList(StringUtils.splitByCharacterType("AEkjKr i39:。中文"));
//未解
//ArrayToList(StringUtils.splitByCharacterTypeCamelCase("ASFSRules234"));
//拼接~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//将数组转换为字符串形式
//System.out.println(StringUtils.concat(getArrayData()));
//拼接时用参数一得字符相连接.注意null也用连接符连接了
//System.out.println(StringUtils.concatWith(",", getArrayData()));
//也是拼接。未发现区别
//System.out.println(StringUtils.join(getArrayData()));
//用连接符拼接,为发现区别
//System.out.println(StringUtils.join(getArrayData(), ":"));
//拼接指定数组下标的开始(三参数)和结束(四参数,不包含)的中间这些元素,用连接符连接
//System.out.println(StringUtils.join(getArrayData(), ":", 1, 3));
//用于集合连接字符串.用于集合
//System.out.println(StringUtils.join(getListData(), ":"));
//移除,删除~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//删除所有空格符
//System.out.println(StringUtils.deleteWhitespace(" s 中 你 4j"));
//移除开始部分的相同的字符
//System.out.println(StringUtils.removeStart("www.baidu.com", "www."));
//移除开始部分的相同的字符,不区分大小写
//System.out.println(StringUtils.removeStartIgnoreCase("www.baidu.com", "WWW"));
//移除后面相同的部分
//System.out.println(StringUtils.removeEnd("www.baidu.com", ".com"));
//移除后面相同的部分,不区分大小写
//System.out.println(StringUtils.removeEndIgnoreCase("www.baidu.com", ".COM"));
//移除所有相同的部分
//System.out.println(StringUtils.remove("www.baidu.com/baidu", "bai"));
//移除结尾字符为"\n", "\r", 或者 "\r\n".
//System.out.println(StringUtils.chomp("abcrabc\r"));
//也是移除,未解。去结尾相同字符
//System.out.println(StringUtils.chomp("baidu.com", "com"));
//去掉末尾最后一个字符.如果是"\n", "\r", 或者 "\r\n"也去除
//System.out.println(StringUtils.chop("wwe.baidu"));
//替换~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//替换指定的字符,只替换第一次出现的
//System.out.println(StringUtils.replaceOnce("www.baidu.com/baidu", "baidu", "hao123"));
//替换所有出现过的字符
//System.out.println(StringUtils.replace("www.baidu.com/baidu", "baidu", "hao123"));
//也是替换,最后一个参数表示替换几个
//System.out.println(StringUtils.replace("www.baidu.com/baidu", "baidu", "hao123", 1));
//这个有意识,二三参数对应的数组,查找二参数数组一样的值,替换三参数对应数组的值。本例:baidu替换为taobao。com替换为net
//System.out.println(StringUtils.replaceEach("www.baidu.com/baidu", new String[]{"baidu", "com"}, new String[]{"taobao", "net"}));
//同上,未发现不同
//System.out.println(StringUtils.replaceEachRepeatedly("www.baidu.com/baidu", new String[]{"baidu", "com"}, new String[]{"taobao", "net"}));
//这个更好,不是数组对应,是字符串参数二和参数三对应替换.(二三参数不对应的话,自己看后果)
//System.out.println(StringUtils.replaceChars("www.baidu.com", "bdm", "qo"));
//替换指定开始(参数三)和结束(参数四)中间的所有字符
//System.out.println(StringUtils.overlay("www.baidu.com", "hao123", 4, 9));
//添加,增加~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//复制参数一的字符串,参数二为复制的次数
//System.out.println(StringUtils.repeat("ba", 3));
//复制参数一的字符串,参数三为复制的次数。参数二为复制字符串中间的连接字符串
//System.out.println(StringUtils.repeat("ab", "ou", 3));
//如何字符串长度小于参数二的值,末尾加空格补全。(小于字符串长度不处理返回)
//System.out.println(StringUtils.rightPad("海川", 4));
//字符串长度小于二参数,末尾用参数三补上,多于的截取(截取补上的字符串)
//System.out.println(StringUtils.rightPad("海川", 4, "河流啊"));
//同上在前面补全空格
//System.out.println(StringUtils.leftPad("海川", 4));
//字符串长度小于二参数,前面用参数三补上,多于的截取(截取补上的字符串)
//System.out.println(StringUtils.leftPad("海川", 4, "大家好"));
//字符串长度小于二参数。在两侧用空格平均补全(测试后面补空格优先)
//System.out.println(StringUtils.center("海川", 3));
//字符串长度小于二参数。在两侧用三参数的字符串平均补全(测试后面补空格优先)
//System.out.println(StringUtils.center("海川", 5, "流"));
//只显示指定数量(二参数)的字符,后面以三个点补充(参数一截取+三个点=二参数)
//System.out.println(StringUtils.abbreviate("中华人民共和国", 5));
//2头加点这个有点乱。本例结果: ...ijklmno
//System.out.println(StringUtils.abbreviate("abcdefghijklmno", 12, 10));
//保留指定长度,最后一个字符前加点.本例结果: ab.f
//System.out.println(StringUtils.abbreviateMiddle("abcdef", ".", 4));
//转换,刷选~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//转换第一个字符为大写.如何第一个字符是大写原始返回
//System.out.println(StringUtils.capitalize("Ddf"));
//转换第一个字符为大写.如何第一个字符是大写原始返回
//System.out.println(StringUtils.uncapitalize("DTf"));
//反向转换,大写变小写,小写变大写
//System.out.println(StringUtils.swapCase("I am Jiang, Hello"));
//将字符串倒序排列
//System.out.println(StringUtils.reverse("中国人民"));
//根据特定字符(二参数)分隔进行反转
//System.out.println(StringUtils.reverseDelimited("中:国:人民", ':'));
}
//将数组转换为List
private static void ArrayToList(String[] str){
System.out.println(Arrays.asList(str) + " 长度:" + str.length);
}
//获得集合数据
private static List getListData(){
List list = new ArrayList();
list.add("你好");
list.add(null);
list.add("他好");
list.add("大家好");
return list;
}
//获得数组数据
private static String[] getArrayData(){
return (String[]) getListData().toArray(new String[0]);
}
public static void main(String[] args) {
TestStr();
}
1 、关闭MyEclipse的自动validation windows > perferences > myeclipse > validation 将Build下全部勾取消 如果你需要验证某个文件的时候,我们可以单独去验证它。方法是: 在需要验证的文件上( 右键 -> MyEclipse -> Run Validation 。
2、 启动优化,关闭不需要使用的模块 所以可以将一些不使用的模块禁止 加载启动。 Window > Preferences > General > Startup andy Shutdown 在这里列出的是MyEclipse启动时加载的模块 我这里只让它加载tomcat6 勾选 MyEclipse EASIE Tomcat 7 。 WTP :一个跟myeclipse差不多的东西,主要差别是 WTP 是免费的,如果使用myeclipse,这个可以取消 Mylyn:组队任务管理工具,类似于 CVS ,以任务为单位管理项目进度,没用到的可以取消 Derby:一种保存成 jar 形式的数据库,我没用到,取消 一大排以 MyEclipse EASIE 打头的启动项:myeclipse 支持的服务器,只选自己用的,其他取消, 比如我只选了 tomcat 。 第一项: 启动功能介绍和样例(红色为需要保留的文件,此为本人样例,请按需选择)
- Automatic Updates Scheduler //自动升级调度
- MyEclipse QuickSetup //快速启动
- MyEclipse Derby //derby是一个开源数据库的名字
- MyEclipse EASIE Geronimo 1 //同色都是应用服务器的名字
- MyEclipse EASIE Geronimo 2
- MyEclipse EASIE JBOSS 2
- MyEclipse EASIE JBOSS 3
- MyEclipse EASIE JBOSS 4
- MyEclipse EASIE JBOSS 5
- MyEclipse EASIE JBOSS
- MyEclipse EASIE Jetty 4
- MyEclipse EASIE Jetty 5
- MyEclipse EASIE Jetty 6
- MyEclipse EASIE Jetty
- MyEclipse EASIE JOnAS 3
- MyEclipse EASIE JOnAS 4
- MyEclipse EASIE JOnAS
- MyEclipse EASIE JRun 4
- MyEclipse EASIE JRun
- MyEclipse EASIE Oracle 10 AS
- MyEclipse EASIE Oracle 9 AS
- MyEclipse EASIE Oracle AS
- MyEclipse EASIE Orion 1
- MyEclipse EASIE Orion 2
- MyEclipse EASIE Resin 2
- MyEclipse EASIE Resin 3
- MyEclipse EASIE Resin
- MyEclipse EASIE Sun 8 .x
- MyEclipse EASIE Sun 8
- MyEclipse EASIE Sun 9
- MyEclipse EASIE Glassfish 2
- MyEclipse EASIE Glassfish 1
- MyEclipse EASIE Sun One
- MyEclipse EASIE MyEclipse Tomcat 6 Server
- MyEclipse EASIE Tomcat 4
- MyEclipse EASIE Tomcat 5
- MyEclipse EASIE Tomcat 6
- MyEclipse EASIE Tomcat 7
- MyEclipse EASIE Tomcat
- MyEclipse EASIE WebLogic 10
- MyEclipse EASIE WebLogic 6
- MyEclipse EASIE WebLogic 7
- MyEclipse EASIE WebLogic 8
- MyEclipse EASIE WebLogic 9
- MyEclipse EASIE WebLogic
- MyEclipse EASIE WebSphere 5
- MyEclipse EASIE WebSphere 6.1
- MyEclipse EASIE WebSphere 6
- MyEclipse EASIE WebSphere 4
- MyEclipse Examples //样例
- MyEclipse Memory Monitor //内存监控
- MyEclipse Tapestry Integration //插件集成
- MyEclipse JSP Debug Tooling //jsp调试插件
- MyEclipse File Creation Wizards //文件创建程序
- ICEfaces Integration for MyEclipse //基于Ajax的JSF开发框架()
- MyEclipse Backward Compatibility //后台功能
- MyEclipse Perspective Plug-in //透视图插件
- Pluse Collaboration Control Center //Eclipse的网页管理中心
- eclipse-cs 4.x.x -> 5.0.0 Migration Plug-in //Eclipse插件兼容组件
- Mozilla Debug UI Plug-in(Incubation) //Mozilla调试插件(Mozilla是一款浏览器)
- Dynamic Languages ToolKit Core UI //对入PHP等动态语言支持的用户接口
- WTP Webservice UI Plug-in //Web 服务视图插件
- JavaServer Faces Tools - Core //jsf工具核心包
- Automatic Updates Scheduler //自动更新
- Service policy //Web提供的服务性能目标定义,自动管理
- Atfdebug Plug-in(Incubation) //动态语言的调试工具
- Auxiliary Web Module Support for MeEclipse// 辅助的Web模块支持.(可能是Struts等文件自动添加)
- JSF Editor Preview Support for MyEclipse //jsf编辑器
第二项: MyEclipse Validation
由于文件导入的时候,不能保证文件的正确性.所以在启动服务前需要做一下验证.包括语法等.
另外可以自己添加需要的验证模块.如checkStyle的验证.
3 、去掉MyEclipse的拼写检查(如果你觉的有用可以不去) 拼写检查会给我们带来不少的麻烦,我们的方法命名都会是单词的缩写,他也会提示有错, 所以最好去掉,没有多大的用处 Window > perferences > General > Editors > Text Editors > Spelling > 将Enable spell checking复选框的勾选去掉。 4 、修改MyEclipse编辑JSP页面时的编辑工具 Window > perferences > General > Editors > File Associations > 在File types 中选择 *.jsp > 在Associated editors 中将"MyEclipse JSP Editor"设置为默认。 还有XML文件的默认编辑器
5. 关闭自动更新 1).window->Perferences->General->Startup and Shutdown 勾掉 Automatic Updates Scheduler(自动更新调度程序) 2).window->Perferences->MyEclipse->Maven4MyEclipse 勾上 Enable Maven4MyEclipse featrures ;确定关闭窗口;该步骤是为了显示第3步中的Maven节点 3).window->Perferences->MyEclipse->Maven4MyEclipse 勾掉 Download repository index updates on startup
第六步: 更改内存使用文件
1、打开 eclipse.ini
-showsplash
com.genuitec.myeclipse.product
--launcher.XXMaxPermSize
256M
-vmargs
-Dosgi.requiredJavaVersion=1.5
-Xms256m
-Xmx1024m
-Dosgi.splashLocation=e:MyEclipse 6.0eclipseMyEclipseSplash.bmp
-Duser.language=en
-XX:PermSize=128M
-XX:MaxPermSize=256M
把下面的那个 -XX:MaxPermSize 调大,比如 -XX:MaxPermSize=512M,再把 -XX:PermSize 调成跟 -XX:MaxPermSize一样大
原因:大家一定对这个画面很熟悉吧:
几乎每次 eclipse 卡到当都是因为这个非堆内存不足造成的,把最大跟最小调成一样是因为不让 myeclipse 频繁的换内存区域大小
注意:XX:MaxPermSize 和 Xmx 的大小之和不能超过你的电脑内存大小
第七步: 修改Struts-config.xml文件打开错误
有时点击myeclipse里的struts的xml配置文件,会报错:
Error opening the editorUnable to open the editor ,unknow the editor id…..
把这个窗口关闭后才出正确的xml文件显示,这个我们这样改:
windows–>perferences–>general–>editors->file associations选择*.xml,选择myeclipse xml editor点default,ok
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
*
* @author Andy.Chen
* @mail Chenjunjun.ZJ@gmail.com
*
*/
public class InputStreamUtils {
final static int BUFFER_SIZE = 4096;
/**
* 将InputStream转换成String
* @param in InputStream
* @return String
* @throws Exception
*
*/
public static String InputStreamTOString(InputStream in) throws Exception{
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] data = new byte[BUFFER_SIZE];
int count = -1;
while((count = in.read(data,0,BUFFER_SIZE)) != -1)
outStream.write(data, 0, count);
data = null;
return new String(outStream.toByteArray(),"ISO-8859-1");
}
/**
* 将InputStream转换成某种字符编码的String
* @param in
* @param encoding
* @return
* @throws Exception
*/
public static String InputStreamTOString(InputStream in,String encoding) throws Exception{
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] data = new byte[BUFFER_SIZE];
int count = -1;
while((count = in.read(data,0,BUFFER_SIZE)) != -1)
outStream.write(data, 0, count);
data = null;
return new String(outStream.toByteArray(),"ISO-8859-1");
}
/**
* 将String转换成InputStream
* @param in
* @return
* @throws Exception
*/
public static InputStream StringTOInputStream(String in) throws Exception{
ByteArrayInputStream is = new ByteArrayInputStream(in.getBytes("ISO-8859-1"));
return is;
}
/**
* 将InputStream转换成byte数组
* @param in InputStream
* @return byte[]
* @throws IOException
*/
public static byte[] InputStreamTOByte(InputStream in) throws IOException{
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] data = new byte[BUFFER_SIZE];
int count = -1;
while((count = in.read(data,0,BUFFER_SIZE)) != -1)
outStream.write(data, 0, count);
data = null;
return outStream.toByteArray();
}
/**
* 将byte数组转换成InputStream
* @param in
* @return
* @throws Exception
*/
public static InputStream byteTOInputStream(byte[] in) throws Exception{
ByteArrayInputStream is = new ByteArrayInputStream(in);
return is;
}
/**
* 将byte数组转换成String
* @param in
* @return
* @throws Exception
*/
public static String byteTOString(byte[] in) throws Exception{
InputStream is = byteTOInputStream(in);
return InputStreamTOString(is);
}
}
写了一个用org.apache.tools.zip压缩/解压缩zip文件的例子,用来解决中文乱码问题。代码如下:
Java代码
- package org.coolyongzi;
-
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.InputStream;
- import org.apache.tools.zip.ZipEntry;
- import org.apache.tools.zip.ZipFile;
- import org.apache.tools.zip.ZipOutputStream;
- import java.io.IOException;
- import java.util.Enumeration;
- import org.apache.log4j.LogManager;
- import org.apache.log4j.Logger;
-
-
- public class ZipTools {
- public static final Logger logger = LogManager.getLogger(FileTools.class);
- public ZipTools()
- {
-
- }
- /*
- * @description:Compressed files or folders
- * @param compressedFilePath String,zipFileRootPath String,zipFileName String
- * @return boolean
- */
- public static boolean compressFloderChangeToZip(String compressedFilePath,String zipFileRootPath,String zipFileName)
- throws IOException
- {
- File compressedFile = new File(compressedFilePath);
- if("".equalsIgnoreCase(zipFileName))
- {
- zipFileName = StringTools.getShortFileNameFromFilePath(compressedFilePath);
- }
- if(!StringTools.conversionSpecialCharacters(zipFileRootPath).endsWith(File.separator))
- {
- zipFileRootPath = zipFileRootPath + File.separator;
- }
- ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(zipFileRootPath + zipFileName));
- String base ="";
- logger.debug("compress [" + compressedFilePath + "] start!");
- boolean result = ZipTools.compressFloderChangeToZip(compressedFile,zipOutputStream,base);
- logger.debug("compress [" + compressedFilePath + "] end!");
- zipOutputStream.close();
- return result;
-
- }
-
- private static boolean compressFloderChangeToZip(File compressedFile,ZipOutputStream zipOutputStream,String base)
- throws IOException
- {
- FileInputStream fileInputStream = null;
-
- try{
- if(compressedFile.isDirectory())
- {
- File[] childrenCompressedFileList = compressedFile.listFiles();
- base = base.length() == 0 ? "" : base + File.separator;
- for (int i = 0; i < childrenCompressedFileList.length; i++) {
- ZipTools.compressFloderChangeToZip(childrenCompressedFileList[i],
- zipOutputStream,base+childrenCompressedFileList[i].getName());
- }
- }
- else
- {
- if("".equalsIgnoreCase(base))
- {
- base = compressedFile.getName();
- }
- zipOutputStream.putNextEntry(new ZipEntry(base));
- fileInputStream = new FileInputStream(compressedFile);
- int b;
- while((b=fileInputStream.read())!=-1)
- {
- zipOutputStream.write(b);
- }
- fileInputStream.close();
- }
- return true;
- }catch(Exception e)
- {
- e.getStackTrace();
- logger.error(e.getMessage());
- return false;
- }
- }
- /*
- * @param:zipFilePath String,releasePath String
- * @return void
- * @description:Decompress A File
- */
- @SuppressWarnings("unchecked")
- public static void decompressFile(String zipFilePath,String releasePath) throws IOException
- {
- ZipFile zipFile = new ZipFile(zipFilePath);
- Enumeration<ZipEntry> enumeration = zipFile.getEntries();
- InputStream inputStream = null;
- FileOutputStream fileOutputStream = null;
- ZipEntry zipEntry = null;
- String zipEntryNameStr ="";
- String[] zipEntryNameArray = null;
- while (enumeration.hasMoreElements()) {
- zipEntry = enumeration.nextElement();
- zipEntryNameStr = zipEntry.getName();
- zipEntryNameArray = zipEntryNameStr.split("/");
- String path = releasePath;
- File root = new File(releasePath);
- if(!root.exists())
- {
- root.mkdir();
- }
- for (int i = 0; i < zipEntryNameArray.length; i++) {
- if(i<zipEntryNameArray.length-1)
- {
- path = path + File.separator+zipEntryNameArray[i];
- new File(StringTools.conversionSpecialCharacters(path)).mkdir();
- }
- else
- {
- if(StringTools.conversionSpecialCharacters(zipEntryNameStr).endsWith(File.separator))
- {
- new File(releasePath + zipEntryNameStr).mkdir();
- }
- else
- {
- inputStream = zipFile.getInputStream(zipEntry);
- fileOutputStream = new FileOutputStream(new File(
- StringTools.conversionSpecialCharacters(releasePath + zipEntryNameStr)));
- byte[] buf = new byte[1024];
- int len;
- while ((len = inputStream.read(buf)) > 0)
- {
- fileOutputStream.write(buf, 0, len);
- }
- inputStream.close();
- fileOutputStream.close();
- }
- }
- }
- }
- zipFile.close();
- }
- }
junit测试类
Java代码
- package org.coolyongzi.testcase;
-
- import java.io.IOException;
-
- import org.coolyongzi.ZipTools;
-
- import junit.framework.TestCase;
-
- public class ZipToolsTest extends TestCase {
-
- protected void setUp() throws Exception {
- super.setUp();
- }
-
- protected void tearDown() throws Exception {
- super.tearDown();
- }
-
- public void testCompressFloderChangeToZip(){
- try {
- ZipTools.compressFloderChangeToZip("f:/iDocumentBanner2.gif", "f:", "test.zip");
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- public void testDecompressFile(){
- try {
- ZipTools.decompressFile("f:/java对解压Zip格式的文件.zip","f:/test/");
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- System.out.println(e.getMessage());
- }
- }
- }
import java.io.*;
import org.apache.tools.zip.*;
import java.util.Enumeration;
/**
*功能:zip压缩、解压(支持中文文件名)
*说明:本程序通过使用Apache Ant里提供的zip工具org.apache.tools.zip实现了zip压缩和解压功能.
* 解决了由于java.util.zip包不支持汉字的问题。
* 使用java.util.zip包时,当zip文件中有名字为中文的文件时,
* 就会出现异常:"Exception in thread "main " java.lang.IllegalArgumentException
* at java.util.zip.ZipInputStream.getUTF8String(ZipInputStream.java:285)
*注意:
* 1、使用时把ant.jar放到classpath中,程序中使用import org.apache.tools.zip.*;
* 2、Apache Ant 下载地址:[url]http://ant.apache.org/[/url]
* 3、Ant ZIP API:[url]http://www.jajakarta.org/ant/ant-1.6.1/docs/mix/manual/api/org/apache/tools/zip/[/url]
* 4、本程序使用Ant 1.7.1 中的ant.jar
*
*仅供编程学习参考.
*
*@author Winty
*@date 2008-8-3
*@Usage:
* 压缩:java AntZip -zip "directoryName"
* 解压:java AntZip -unzip "fileName.zip"
*/
public class AntZip{
private ZipFile zipFile;
private ZipOutputStream zipOut; //压缩Zip
private ZipEntry zipEntry;
private static int bufSize; //size of bytes
private byte[] buf;
private int readedBytes;
public AntZip(){
this(512);
}
public AntZip(int bufSize){
this.bufSize = bufSize;
this.buf = new byte[this.bufSize];
}
//压缩文件夹内的文件
public void doZip(String zipDirectory){//zipDirectoryPath:需要压缩的文件夹名
File file;
File zipDir;
zipDir = new File(zipDirectory);
String zipFileName = zipDir.getName() + ".zip";//压缩后生成的zip文件名
try{
this.zipOut = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFileName)));
handleDir(zipDir , this.zipOut);
this.zipOut.close();
}catch(IOException ioe){
ioe.printStackTrace();
}
}
//由doZip调用,递归完成目录文件读取
private void handleDir(File dir , ZipOutputStream zipOut)throws IOException{
FileInputStream fileIn;
File[] files;
files = dir.listFiles();
if(files.length == 0){//如果目录为空,则单独创建之.
//ZipEntry的isDirectory()方法中,目录以"/"结尾.
this.zipOut.putNextEntry(new ZipEntry(dir.toString() + "/"));
this.zipOut.closeEntry();
}
else{//如果目录不为空,则分别处理目录和文件.
for(File fileName : files){
//System.out.println(fileName);
if(fileName.isDirectory()){
handleDir(fileName , this.zipOut);
}
else{
fileIn = new FileInputStream(fileName);
this.zipOut.putNextEntry(new ZipEntry(fileName.toString()));
while((this.readedBytes = fileIn.read(this.buf))>0){
this.zipOut.write(this.buf , 0 , this.readedBytes);
}
this.zipOut.closeEntry();
}
}
}
}
//解压指定zip文件
public void unZip(String unZipfileName){//unZipfileName需要解压的zip文件名
FileOutputStream fileOut;
File file;
InputStream inputStream;
try{
this.zipFile = new ZipFile(unZipfileName);
for(Enumeration entries = this.zipFile.getEntries(); entries.hasMoreElements();){
ZipEntry entry = (ZipEntry)entries.nextElement();
file = new File(entry.getName());
if(entry.isDirectory()){
file.mkdirs();
}
else{
//如果指定文件的目录不存在,则创建之.
File parent = file.getParentFile();
if(!parent.exists()){
parent.mkdirs();
}
inputStream = zipFile.getInputStream(entry);
fileOut = new FileOutputStream(file);
while(( this.readedBytes = inputStream.read(this.buf) ) > 0){
fileOut.write(this.buf , 0 , this.readedBytes );
}
fileOut.close();
inputStream.close();
}
}
this.zipFile.close();
}catch(IOException ioe){
ioe.printStackTrace();
}
}
//设置缓冲区大小
public void setBufSize(int bufSize){
this.bufSize = bufSize;
}
//测试AntZip类
public static void main(String[] args)throws Exception{
if(args.length==2){
String name = args[1];
AntZip zip = new AntZip();
if(args[0].equals("-zip"))
zip.doZip(name);
else if(args[0].equals("-unzip"))
zip.unZip(name);
}
else{
System.out.println("Usage:");
System.out.println("压缩:java AntZip -zip directoryName");
System.out.println("解压:java AntZip -unzip fileName.zip");
throw new Exception("Arguments error!");
}
}
}
jdk提供了Zip相关的类方便的实现压缩和解压缩。使用方法很简单。下边分别是压缩和解压缩的简单事例
1,压缩的
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class Zip {
static final int BUFFER = 2048;
public static void main(String argv[]) {
try {
BufferedInputStream origin = null;
FileOutputStream dest = new FileOutputStream("E:\\test\\myfiles.zip");
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(
dest));
byte data[] = new byte[BUFFER];
File f = new File("e:\\test\\a\\");
File files[] = f.listFiles();
for (int i = 0; i < files.length; i++) {
FileInputStream fi = new FileInputStream(files[i]);
origin = new BufferedInputStream(fi, BUFFER);
ZipEntry entry = new ZipEntry(files[i].getName());
out.putNextEntry(entry);
int count;
while ((count = origin.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
}
origin.close();
}
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
2,解压缩的。
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class UnZip {
static final int BUFFER = 2048;
public static void main(String argv[]) {
try {
String fileName = "E:\\test\\myfiles.zip";
String filePath = "E:\\test\\";
ZipFile zipFile = new ZipFile(fileName);
Enumeration emu = zipFile.entries();
int i=0;
while(emu.hasMoreElements()){
ZipEntry entry = (ZipEntry)emu.nextElement();
//会把目录作为一个file读出一次,所以只建立目录就可以,之下的文件还会被迭代到。
if (entry.isDirectory())
{
new File(filePath + entry.getName()).mkdirs();
continue;
}
BufferedInputStream bis = new BufferedInputStream(zipFile.getInputStream(entry));
File file = new File(filePath + entry.getName());
//加入这个的原因是zipfile读取文件是随机读取的,这就造成可能先读取一个文件
//而这个文件所在的目录还没有出现过,所以要建出目录来。
File parent = file.getParentFile();
if(parent != null && (!parent.exists())){
parent.mkdirs();
}
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos,BUFFER);
int count;
byte data[] = new byte[BUFFER];
while ((count = bis.read(data, 0, BUFFER)) != -1)
{
bos.write(data, 0, count);
}
bos.flush();
bos.close();
bis.close();
}
zipFile.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
[代码] [Java]代码,服务器端实现
File file = new File(location);
if (file.exists()) {
long p = 0;
long fileLength;
fileLength = file.length();
// get file content
InputStream ins = new FileInputStream(file);
bis = new BufferedInputStream(ins);
// tell the client to allow accept-ranges
response.reset();
response.setHeader("Accept-Ranges", "bytes");
// client requests a file block download start byte
if (request.getHeader("Range") != null) {
response.setStatus(javax.servlet.http.HttpServletResponse.SC_PARTIAL_CONTENT);
p = Long.parseLong(request.getHeader("Range")
.replaceAll("bytes=", "")
.replaceAll("-", "")
);
}
// support multi-threaded download
// respone format:
// Content-Length:[file size] - [client request start bytes from file block]
response.setHeader("Content-Length", new Long(fileLength - p).toString());
if (p != 0) {
// 断点开始
// 响应的格式是:
// Content-Range: bytes [文件块的开始字节]-[文件的总大小 - 1]/[文件的总大小]
String contentRange = new StringBuffer("bytes ")
.append(new Long(p).toString())
.append("-")
.append(new Long(fileLength - 1).toString())
.append("/")
.append(new Long(fileLength).toString())
.toString();
response.setHeader("Content-Range", contentRange);
// pointer move to seek
bis.skip(p);
}
String fileName = file.getName();
response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
while ((size = bis.read(buf)) != -1) {
response.getOutputStream().write(buf,0,size);
response.getOutputStream().flush();
}
bis.close();
[代码] 客户端下载测试
public class TestDownload {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
HttpURLConnection httpURLConnection = null;
URL url = null;
BufferedInputStream bis = null;
byte[] buf = new byte[10240];
int size = 0;
String fileName = "aaa.zip";
String filePath = "C:\\Users\\Desktop";
String remoteUrl = "http://127.0.0.1:8080/down.zip";
// 检查本地文件
RandomAccessFile rndFile = null;
File file = new File(filePath + "\\" + fileName);
long remoteFileSize = getRemoteFileSzie(remoteUrl);
long nPos = 0;
if (file.exists()) {
long localFileSzie = file.length();
if (localFileSzie < remoteFileSize) {
System.out.println("文件续传...");
nPos = localFileSzie;
} else {
System.out.println("文件存在,重新下载...");
file.delete();
try {
file.createNewFile();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
} else {
// 建立文件
try {
file.createNewFile();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
// 下载文件
try {
url = new URL(remoteUrl);
httpURLConnection = (HttpURLConnection)url.openConnection();
// 设置User-Agent
httpURLConnection.setRequestProperty("User-Agent", "Net");
// 设置续传开始
httpURLConnection.setRequestProperty("Range", "bytes=" + nPos + "-");
// 获取输入流
bis = new BufferedInputStream(httpURLConnection.getInputStream());
rndFile = new RandomAccessFile(filePath + "\\" + fileName, "rw");
rndFile.seek(nPos);
int i = 0;
while ((size = bis.read(buf)) != -1) {
//if (i > 500) break;
rndFile.write(buf, 0, size);
i++;
}
System.out.println("i=" + i);
httpURLConnection.disconnect();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
public static long getRemoteFileSzie(String url) {
long size = 0;
try {
HttpURLConnection httpUrl = (HttpURLConnection)(new URL(url)).openConnection();
size = httpUrl.getContentLength();
httpUrl.disconnect();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return size;
}
}
1. 基本 概念
IO 是主存和外部设备 ( 硬盘、终端和网络等 ) 拷贝数据的过程。 IO 是操作系统的底层功能实现,底层通过 I/O 指令进行完成。
所有语言运行时系统提供执行 I/O 较高级别的工具。 (c 的 printf scanf,java 的面向对象封装 )
2. Java 标准 io 回顾
Java 标准 IO 类库是 io 面向对象的一种抽象。基于本地方法的底层实现,我们无须关注底层实现。 InputStream\OutputStream( 字节流 ) :一次传送一个字节。 Reader\Writer( 字符流 ) :一次一个字符。
3. nio 简介
nio 是 java New IO 的简称,在 jdk1.4 里提供的新 api 。 Sun 官方标榜的特性如下:
– 为所有的原始类型提供 (Buffer) 缓存支持。
– 字符集编码解码解决方案。
– Channel :一个新的原始 I/O 抽象。
– 支持锁和内存映射文件的文件访问接口。
– 提供多路 (non-bloking) 非阻塞式的高伸缩性网络 I/O 。
本文将围绕这几个特性进行学习和介绍。
4. Buffer&Chanel
Channel 和 buffer 是 NIO 是两个最基本的数据类型抽象。
Buffer:
– 是一块连续的内存块。
– 是 NIO 数据读或写的中转地。
Channel:
– 数据的源头或者数据的目的地
– 用于向 buffer 提供数据或者读取 buffer 数据 ,buffer 对象的唯一接口。
– 异步 I/O 支持
图1:channel和buffer关系
例子 1:CopyFile.java:
Java代码
- package sample;
-
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.nio.ByteBuffer;
- import java.nio.channels.FileChannel;
-
- public class CopyFile {
- public static void main(String[] args) throws Exception {
- String infile = "C:\\copy.sql";
- String outfile = "C:\\copy.txt";
- // 获取源文件和目标文件的输入输出流
- FileInputStream fin = new FileInputStream(infile);
- FileOutputStream fout = new FileOutputStream(outfile);
- // 获取输入输出通道
- FileChannel fcin = fin.getChannel();
- FileChannel fcout = fout.getChannel();
- // 创建缓冲区
- ByteBuffer buffer = ByteBuffer.allocate(1024);
- while (true) {
- // clear方法重设缓冲区,使它可以接受读入的数据
- buffer.clear();
- // 从输入通道中将数据读到缓冲区
- int r = fcin.read(buffer);
- // read方法返回读取的字节数,可能为零,如果该通道已到达流的末尾,则返回-1
- if (r == -1) {
- break;
- }
- // flip方法让缓冲区可以将新读入的数据写入另一个通道
- buffer.flip();
- // 从输出通道中将数据写入缓冲区
- fcout.write(buffer);
- }
- }
- }
其中 buffer 内部结构如下 ( 下图拷贝自资料 ):
图2:buffer内部结构
一个 buffer 主要由 position,limit,capacity 三个变量来控制读写的过程。此三个变量的含义见如下表格:
参数 | 写模式 | 读模式 |
position | 当前写入的单位数据数量。 | 当前读取的单位数据位置。 |
limit | 代表最多能写多少单位数据和容量是一样的。 | 代表最多能读多少单位数据,和之前写入的单位数据量一致。 |
capacity | buffer 容量 | buffer 容量 |
Buffer 常见方法:
flip(): 写模式转换成读模式
rewind() :将 position 重置为 0 ,一般用于重复读。
clear() :清空 buffer ,准备再次被写入 (position 变成 0 , limit 变成 capacity) 。
compact(): 将未读取的数据拷贝到 buffer 的头部位。
mark() 、 reset():mark 可以标记一个位置, reset 可以重置到该位置。
Buffer 常见类型: ByteBuffer 、 MappedByteBuffer 、 CharBuffer 、 DoubleBuffer 、 FloatBuffer 、 IntBuffer 、 LongBuffer 、 ShortBuffer 。
channel 常见类型 :FileChannel 、 DatagramChannel(UDP) 、 SocketChannel(TCP) 、 ServerSocketChannel(TCP)
在本机上面做了个简单的性能测试。我的笔记本性能一般。 ( 具体代码可以见附件。见 nio.sample.filecopy 包下面的例子 ) 以下是参考数据:
– 场景 1 : Copy 一个 370M 的文件
– 场景 2: 三个线程同时拷贝,每个线程拷贝一个 370M 文件
场景 | FileInputStream+ FileOutputStream | FileInputStream+ BufferedInputStream+ FileOutputStream | ByteBuffer+ FileChannel | MappedByteBuffer +FileChannel |
场景一时间 ( 毫秒 ) | 25155 | 17500 | 19000 | 16500 |
场景二时间 ( 毫秒 ) | 69000 | 67031 | 74031 | 71016 |
5. nio.charset
字符编码解码 : 字节码本身只是一些数字,放到正确的上下文中被正确被解析。向 ByteBuffer 中存放数据时需要考虑字符集的编码方式,读取展示 ByteBuffer 数据时涉及对字符集解码。
Java.nio.charset 提供了编码解码一套解决方案。
以我们最常见的 http 请求为例,在请求的时候必须对请求进行正确的编码。在得到响应时必须对响应进行正确的解码。
以下代码向 baidu 发一次请求,并获取结果进行显示。例子演示到了 charset 的使用。
例子 2BaiduReader.java
Java代码
- package nio.readpage;
-
- import java.nio.ByteBuffer;
- import java.nio.channels.SocketChannel;
- import java.nio.charset.Charset;
- import java.net.InetSocketAddress;
- import java.io.IOException;
- public class BaiduReader {
- private Charset charset = Charset.forName("GBK");// 创建GBK字符集
- private SocketChannel channel;
- public void readHTMLContent() {
- try {
- InetSocketAddress socketAddress = new InetSocketAddress(
- "www.baidu.com", 80);
- //step1:打开连接
- channel = SocketChannel.open(socketAddress);
- //step2:发送请求,使用GBK编码
- channel.write(charset.encode("GET " + "/ HTTP/1.1" + "\r\n\r\n"));
- //step3:读取数据
- ByteBuffer buffer = ByteBuffer.allocate(1024);// 创建1024字节的缓冲
- while (channel.read(buffer) != -1) {
- buffer.flip();// flip方法在读缓冲区字节操作之前调用。
- System.out.println(charset.decode(buffer));
- // 使用Charset.decode方法将字节转换为字符串
- buffer.clear();// 清空缓冲
- }
- } catch (IOException e) {
- System.err.println(e.toString());
- } finally {
- if (channel != null) {
- try {
- channel.close();
- } catch (IOException e) {
- }
- }
- }
- }
- public static void main(String[] args) {
- new BaiduReader().readHTMLContent();
- }
- }
6. 非阻塞 IO
关于非阻塞 IO 将从何为阻塞、何为非阻塞、非阻塞原理和异步核心 API 几个方面来理解。
何为阻塞?
一个常见的网络 IO 通讯流程如下 :
图3:网络通讯基本过程
从该网络通讯过程来理解一下何为阻塞 :
在以上过程中若连接还没到来,那么 accept 会阻塞 , 程序运行到这里不得不挂起, CPU 转而执行其他线程。
在以上过程中若数据还没准备好, read 会一样也会阻塞。
阻塞式网络 IO 的特点:多线程处理多个连接。每个线程拥有自己的栈空间并且占用一些 CPU 时间。每个线程遇到外部为准备好的时候,都会阻塞掉。阻塞的结果就是会带来大量的进程上下文切换。且大部分进程上下文切换可能是无意义的。比如假设一个线程监听一个端口,一天只会有几次请求进来,但是该 cpu 不得不为该线程不断做上下文切换尝试,大部分的切换以阻塞告终。
何为非阻塞?
下面有个隐喻:
一辆从 A 开往 B 的公共汽车上,路上有很多点可能会有人下车。司机不知道哪些点会有哪些人会下车,对于需要下车的人,如何处理更好?
1. 司机过程中定时询问每个乘客是否到达目的地,若有人说到了,那么司机停车,乘客下车。 ( 类似阻塞式 )
2. 每个人告诉售票员自己的目的地,然后睡觉,司机只和售票员交互,到了某个点由售票员通知乘客下车。 ( 类似非阻塞 )
很显然,每个人要到达某个目的地可以认为是一个线程,司机可以认为是 CPU 。在阻塞式里面,每个线程需要不断的轮询,上下文切换,以达到找到目的地的结果。而在非阻塞方式里,每个乘客 ( 线程 ) 都在睡觉 ( 休眠 ) ,只在真正外部环境准备好了才唤醒,这样的唤醒肯定不会阻塞。
非阻塞的原理
把整个过程切换成小的任务,通过任务间协作完成。
由一个专门的线程来处理所有的 IO 事件,并负责分发。
事件驱动机制:事件到的时候触发,而不是同步的去监视事件。
线程通讯:线程之间通过 wait,notify 等方式通讯。保证每次上下文切换都是有意义的。减少无谓的进程切换。
以下是异步 IO 的结构:
图4:非阻塞基本原理
Reactor 就是上面隐喻的售票员角色。每个线程的处理流程大概都是读取数据、解码、计算处理、编码、发送响应。
异步 IO 核心 API
Selector
异步 IO 的核心类,它能检测一个或多个通道 (channel) 上的事件,并将事件分发出去。
使用一个 select 线程就能监听多个通道上的事件,并基于事件驱动触发相应的响应。而不需要为每个 channel 去分配一个线程。
SelectionKey
包含了事件的状态信息和时间对应的通道的绑定。
例子 1 单线程实现监听两个端口。 ( 见 nio.asyn 包下面的例子。 )
例子 2 NIO 线程协作实现资源合理利用。 (wait,notify) 。 ( 见 nio.asyn.multithread 下的例子 )