在基于 Java 语言的编程中,我们经常碰到汉字的处理及显示的问题。一大堆看不懂的乱码肯定不是我们愿意看到的显示效果,怎样才能够让那些汉字正确显示呢?Java语言默认的编码方式是UNICODE,而我们中国人通常使用的文件和数据库都是基于GB2312或者BIG5等方式编码的,怎样才能够恰当地选择汉字编码方式并正确地处理汉字的编码呢?本文将从汉字编码的常识入手,结合Java编程实例,分析以上两个问题并提出解决它们的方案。
现在 Java 编程语言已经广泛应用于互联网世界,早在 Sun 公司开发 Java 语言的时候,就已经考虑到对非英文字符的支持了。Sun 公司公布的 Java 运行环境(JRE)本身就分英文版和国际版,但只有国际版才支持非英文字符。不过在 Java 编程语言的应用中,对中文字符的支持并非如同 Java Soft 的标准规范中所宣称的那样完美,因为中文字符集不只一个,而且不同的操作系统对中文字符的支持也不尽相同,所以会有许多和汉字编码处理有关的问题在我们进行应用开发中困扰着我们。有很多关于这些问题的解答,但都比较琐碎,并不能够满足大家迫切解决问题的愿望,关于 Java 中文问题的系统研究并不多,本文从汉字编码常识出发,分析 Java 中文问题,希望对大家解决这个问题有所帮助。
汉字编码的常识
我们知道,英文字符一般是以一个字节来表示的,最常用的编码方法是 ASCII 。但一个字节最多只能区分256个字符,而汉字成千上万,所以现在都以双字节来表示汉字,为了能够与英文字符分开,每个字节的最高位一定为1,这样双字节最多可以表示64K格字符。我们经常碰到的编码方式有 GB2312、BIG5、UNICODE 等。关于具体编码方式的详细资料,有兴趣的读者可以查阅相关资料。我肤浅谈一下和我们关系密切的 GB2312 和 UNICODE。GB2312 码,中华人民共和国国家标准汉字信息交换用编码,是一个由中华人民共和国国家标准总局发布的关于简化汉字的编码,通行于中国大陆地区及新加坡,简称国标码。两个字节中,第一个字节(高字节)的值为区号值加32(20H),第二个字节(低字节)的值为位号值加32(20H),用这两个值来表示一个汉字的编码。UNICODE 码是微软提出的解决多国字符问题的多字节等长编码,它对英文字符采取前面加“0”字节的策略实现等长兼容。如 “A” 的 ASCII 码为0x41,UNICODE 就为0x00,0x41。利用特殊的工具各种编码之间可以互相转换。
Java 中文问题的初步认识
我们基于 Java 编程语言进行应用开发时,不可避免地要处理中文。Java 编程语言默认的编码方式是 UNICODE,而我们通常使用的数据库及文件都是基于 GB2312 编码的,我们经常碰到这样的情况:浏览基于 JSP 技术的网站看到的是乱码,文件打开后看到的也是乱码,被 Java 修改过的数据库的内容在别的场合应用时无法继续正确地提供信息。
| String sEnglish = “apple”; String sChinese = “苹果”; String s = “苹果 apple ”; |
sEnglish 的长度是5,sChinese的长度是4,而 s 默认的长度是14。对于 sEnglish来说, Java 中的各个类都支持得非常好,肯定能够正确显示。但对于 sChinese 和 s 来说,虽然 Java Soft 声明 Java 的基本类已经考虑到对多国字符的支持(默认 UNICODE 编码),但是如果操作系统的默认编码不是 UNICODE ,而是国标码等。从 Java 源代码到得到正确的结果,要经过 “Java 源代码-> Java 字节码-> ;虚拟机->操作系统->显示设备”的过程。在上述过程中的每一步骤,我们都必须正确地处理汉字的编码,才能够使最终的显示结果正确。
“ Java 源代码-> Java 字节码”,标准的 Java 编译器 javac 使用的字符集是系统默认的字符集,比如在中文 Windows 操作系统上就是 GBK ,而在 Linux 操作系统上就是ISO-8859-1,所以大家会发现在 Linux 操作系统上编译的类中源文件中的中文字符都出了问题,解决的办法就是在编译的时候添加 encoding 参数,这样才能够与平台无关。用法是
javac ?Cencoding GBK。
“ Java 字节码->虚拟机->操作系统”, Java 运行环境 (JRE) 分英文版和国际版,但只有国际版才支持非英文字符。 Java 开发工具包 (JDK) 肯定支持多国字符,但并非所有的计算机用户都安装了 JDK 。很多操作系统及应用软件为了能够更好的支持 Java ,都内嵌了 JRE 的国际版本,为自己支持多国字符提供了方便。
“操作系统->显示设备”,对于汉字来说,操作系统必须支持并能够显示它。英文操作系统如果不搭配特殊的应用软件的话,是肯定不能够显示中文的。
还有一个问题,就是在 Java 编程过程中,对中文字符进行正确的编码转换。例如,向网页输出中文字符串的时候,不论你是用
| out.println(string); |
还是用<%=string%>,都必须作 UNICODE 到 GBK 的转换,或者手动,或者自动。在 JSP 1.0中,可以定义输出字符集,从而实现内码的自动转换。用法是
| <%@page contentType=”text/html;charset=gb2312” %> |
但是在一些 JSP 版本中并没有提供对输出字符集的支持,(例如 JSP 0.92),这就需要手动编码输出了,方法非常多。最常用的方法是
| String s1 = request.getParameter(“keyword”); String s2 = new String(s1.getBytes(“ISO-8859-1”),”GBK”); |
getBytes 方法用于将中文字符以“ISO-8859-1”编码方式转化成字节数组,而“GBK” 是目标编码方式。我们从以ISO-8859-1方式编码的数据库中读出中文字符串 s1 ,经过上述转换过程,在支持 GBK 字符集的操作系统和应用软件中就能够正确显示中文字符串 s2 。
Java 中文问题的表层分析及处理
|
背景
|
|||
| 开发环境 | JDK1.15 | Vcafe2.0 | JPadPro |
| 服务器端 | NT IIS | Sybase System | Jconnect(JDBC) |
| 客户端 | IE5.0 | Pwin98 | |
.CLASS 文件存放在服务器端,由客户端的浏览器运行 APPLET , APPLET 只起调入 FRAME 类等主程序的作用。界面包括 Textfield ,TextArea,List,Choice 等。
I.用 JDBC 执行 SELECT 语句从服务器端读取数据(中文)后,将数据用 APPEND 方法加到 TextArea(TA) ,不能正确显示。但加到 List 中时,大部分汉字却可正确显示。
将数据按“ISO-8859-1” 编码方式转化为字节数组,再按系统缺省编码方式 (Default Character Encoding) 转化为 STRING ,即可在 TA 和 List 中正确显示。
程序段如下:
| dbstr2 = results.getString(1); //After reading the result from DB server,converting it to string. dbbyte1 = dbstr2.getBytes(“iso-8859-1”); dbstr1 = new String(dbbyte1); |
在转换字符串时不采用系统默认编码方式,而直接采用“ GBK” 或者 “GB2312” ,在 A 和 B 两种情况下,从数据库取数据都没有问题。
II.处理方式与“取中文”相逆,先将 SQL 语句按系统缺省编码方式转化为字节数组,再按“ISO-8859-1”编码方式转化为 STRING ,最后送去执行,则中文信息可正确写入数据库。
程序段如下:
| sqlstmt = tf_input.getText(); //Before sending statement to DB server,converting it to sql statement. dbbyte1 = sqlstmt.getBytes(); sqlstmt = newString(dbbyte1,”iso-8859-1”); _stmt = _con.createStatement(); _stmt.executeUpdate(sqlstmt); …… |
问题:如果客户机上存在 CLASSPATH 指向 JDK 的 CLASSES.ZIP 时(称为 A 情况),上述程序代码可正确执行。但是如果客户机只有浏览器,而没有 JDK 和 CLASSPATH 时(称为 B 情况),则汉字无法正确转换。
我们的分析:
1.经过测试,在 A 情况下,程序运行时系统的缺省编码方式为 GBK 或者 GB2312 。在 B 情况下,程序启动时浏览器的 JAVA 控制台中出现如下错误信息:
Can't find resource for sun.awt.windows.awtLocalization_zh_CN
然后系统的缺省编码方式为“8859-1”。
2.如果在转换字符串时不采用系统缺省编码方式,而是直接采用 “GBK” 或“GB2312”,则在 A 情况下程序仍然可正常运行,在 B 情况下,系统出现错误:
UnsupportedEncodingException。
3.在客户机上,把 JDK 的 CLASSES.ZIP 解压后,放在另一个目录中, CLASSPATH 只包含该目录。然后一边逐步删除该目录中的 .CLASS 文件,另一边运行测试程序,最后发现在一千多个 CLASS 文件中,只有一个是必不可少的,该文件是:
sun.io.CharToByteDoubleByte.class。
将该文件拷到服务器端和其它的类放在一起,并在程序的开头 IMPORT 它,在 B 情况下程序仍然无法正常运行。
4.在 A 情况下,如果在 CLASSPTH 中去掉 sun.io.CharToByteDoubleByte.class ,则程序运行时测得默认编码方式为“8859-1”,否则为 “GBK” 或 “GB2312” 。
如果 JDK 的版本为1.2以上的话,在 B 情况下遇到的问题得到了很好的解决,测试的步骤同上,有兴趣的读者可以尝试一下。
Java 中文问题的根源分析及解决
在简体中文 MS Windows 98 + JDK 1.3 下,可以用 System.getProperties() 得到 Java 运行环境的一些基本属性,类 PoorChinese 可以帮助我们得到这些属性。
类 PoorChinese 的源代码:
public class PoorChinese {}
执行 java PoorChinese 后,我们会得到:
系统变量 file.encoding 的值为 GBK ,user.language 的值为 zh , user.region 的值为 CN ,这些系统变量的值决定了系统默认的编码方式是 GBK 。
在上述系统中,下面的代码将 GB2312 文件转换成 Big5 文件,它们能够帮助我们理解 Java 中汉字编码的转化:
| import java.io.*; import java.util.*; public class gb2big5 { static int iCharNum=0; public static void main(String[] args) { System.out.println("Input GB2312 file, output Big5 file."); if (args.length!=2) { System.err.println("Usage: jview gb2big5 gbfile big5file"); System.exit(1); String inputString = readInput(args[0]); writeOutput(inputString,args[1]); System.out.println("Number of Characters in file: "+iCharNum+"."); } static void writeOutput(String str, String strOutFile) { try { FileOutputStream fos = new FileOutputStream(strOutFile); Writer out = new OutputStreamWriter(fos, "Big5"); out.write(str); out.close(); } catch (IOException e) { e.printStackTrace(); e.printStackTrace(); } } static String readInput(String strInFile) { StringBuffer buffer = new StringBuffer(); try { FileInputStream fis = new FileInputStream(strInFile); InputStreamReader isr = new InputStreamReader(fis, "GB2312"); Reader in = new BufferedReader(isr); int ch; while ((ch = in.read()) > -1) { iCharNum += 1;buffer.append((char)ch); } in.close(); return buffer.toString(); } catch (IOException e) { e.printStackTrace(); return null; } } } |
编码转化的过程如下:
GB2312------------------>Unicode------------->Big5
执行 java gb2big5 gb.txt big5.txt ,如果 gb.txt 的内容是“今天星期三”,则得到的文件 big5.txt 中的字符能够正确显示;而如果 gb.txt 的内容是“情人节快乐”,则得到的文件 big5.txt 中对应于“节”和“乐”的字符都是符号“?”(0x3F),可见 sun.io.ByteToCharGB2312 和 sun.io.CharToByteBig5 这两个基本类并没有编好。
正如上例一样, Java 的基本类也可能存在问题。由于国际化的工作并不是在国内完成的,所以在这些基本类发布之前,没有经过严格的测试,所以对中文字符的支持并不像 Java Soft 所声称的那样完美。前不久,我的一位技术上的朋友发信给我说,他终于找到了 Java Servlet 中文问题的根源。两周以来,他一直为 Java Servlet 的中文问题所困扰,因为每面对一个含有中文字符的字符串都必须进行强制转换才能够得到正确的结果(这好象是大家公认的唯一的解决办法)。
后来,他确实不想如此继续安分下去了,因为这样的事情确实不应该是高级程序员所要做的工作,他就找出 Servlet 解码的源代码进行分析,因为他怀疑问题就出在解码这部分。经过四个小时的奋斗,他终于找到了问题的根源所在。原来他的怀疑是正确的, Servlet 的解码部分完全没有考虑双字节,直接把 %XX 当作一个字符。(原来 Java Soft 也会犯这幺低级的错误!)
如果你对这个问题有兴趣或者遇到了同样的烦恼的话,你可以按照他的步骤 对Servlet.jar 进行修改:
找到源代码 HttpUtils 中的 static private String parseName ,在返回前将 sb(StringBuffer) 复制成 byte bs[] ,然后 return new String(bs,”GB2312”)。作上述修改后就需要自己解码了:
| HashTable form=HttpUtils .parseQueryString(request.getQueryString())或者 form=HttpUtils.parsePostData(……) |
千万别忘了编译后放到 Servlet.jar 里面。
关于 Java 中文问题的总结
Java 编程语言成长于网络世界,这就要求 Java 对多国字符有很好的支持。 Java 编程语言适应了计算的网络化的需求,为它能够在网络世界迅速成长奠定了坚实的基础。 Java 的缔造者 (Java Soft) 已经考虑到 Java 编程语言对多国字符的支持,只是现在的解决方案有很多缺陷在里面,需要我们付诸一些补偿性的措施。而世界标准化组织也在努力把人类所有的文字统一在一种编码之中,其中一种方案是 ISO10646 ,它用四个字节来表示一个字符。当然,在这种方案未被采用之前,还是希望 Java Soft 能够严格地测试它的产品,为用户带来更多的方便。
附一个用于从数据库和网络中取出 中文乱码的处理函数,入参是有问题的字符串,出参是问题已经解决了的字符串。
泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
Java语言引入泛型的好处是安全简单。
在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。
泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。
泛型在使用中还有一些规则和限制:
1、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。
2、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
3、泛型的类型参数可以有多个。
4、泛型的参数类型可以使用extends语句,例如<T extends superclass>。习惯上成为“有界类型”。
5、泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName(java.lang.String);
泛型还有接口、方法等等,内容很多,需要花费一番功夫才能理解掌握并熟练应用。在此给出我曾经了解泛型时候写出的两个例子(根据看的印象写的),实现同样的功能,一个使用了泛型,一个没有使用,通过对比,可以很快学会泛型的应用,学会这个基本上学会了泛型70%的内容。
例子一:使用了泛型
| public class Gen<T> { private T ob; //定义泛型成员变量 public Gen(T ob) { this.ob = ob; } public T getOb() { return ob; } public void setOb(T ob) { this.ob = ob; } public void showTyep() { System.out.println("T的实际类型是: " + ob.getClass().getName()); } } public class GenDemo { public static void main(String[] args){ //定义泛型类Gen的一个Integer版本 Gen<Integer> intOb=new Gen<Integer>(88); intOb.showTyep(); int i= intOb.getOb(); System.out.println("value= " + i); System.out.println("----------------------------------"); //定义泛型类Gen的一个String版本 Gen<String> strOb=new Gen<String>("Hello Gen!"); strOb.showTyep(); String s=strOb.getOb(); System.out.println("value= " + s); } } |
例子二:没有使用泛型
| public class Gen2 { private Object ob; //定义一个通用类型成员 public Gen2(Object ob) { this.ob = ob; } public Object getOb() { return ob; } public void setOb(Object ob) { this.ob = ob; } public void showTyep() { System.out.println("T的实际类型是: " + ob.getClass().getName()); } } public class GenDemo2 { public static void main(String[] args) { //定义类Gen2的一个Integer版本 Gen2 intOb = new Gen2(new Integer(88)); intOb.showTyep(); int i = (Integer) intOb.getOb(); System.out.println("value= " + i); System.out.println("----------------------------------"); //定义类Gen2的一个String版本 Gen2 strOb = new Gen2("Hello Gen!"); strOb.showTyep(); String s = (String) strOb.getOb(); System.out.println("value= " + s); } } |
运行结果:
两个例子运行Demo结果是相同的,控制台输出结果如下:
T的实际类型是:
| java.lang.Integer value= 88 ---------------------------------- T的实际类型是: java.lang.String value= Hello Gen! Process finished with exit code 0 |
看明白这个,以后基本的泛型应用和代码阅读就不成问题了。
当现在不适合这个操作,或是没有必要进行这个操作时就直接放弃这个操作而回去。这个就是Balking模式例如王某在餐厅吃饭,当王某需要点餐时喊服务员需要点餐。当服务员A和B都注意到了王某点餐的示意,这时服务员B看到服务员A已经去响应了王某的点餐请求,所以服务员B就不会再过去响应王某的点餐请求。
程序示例:程序的需求是模拟一个自动保存的功能。自动保存是为了预防计算机忽然断电或则软件突然出错的危险,定期将数据保存在文件里的功能。
1、Data类:表示是否修改,及修改函数的类2、SaveThread类:定期保存3、ChangeThread:修改数据,保存文件4、Main:模拟数据
view plainprint?
package org.zhy.demo04;
import java.io.FileWriter;import java.io.IOException;import java.io.Writer;/** * 表示是否修改,及修改函数的类* @author zhengyi * */ public class Data {
private final String fileName; //文件名称private String content; //修改内容private boolean changed; //是否可以修改
//构造函数public Data(String fileName,String content){ this.fileName=fileName;this.content=content;}
/** * 新的修改内容 如果有新的需要保存的数据,changed的属性修改为true * 注:这个方法是用synchronized修饰的* 为了保护content与changed属性用* synchronized修饰后就只能同时有* 1个线程访问也就防止了多线程造成的脏数据* @param newContent */ public synchronized void change(String newContent){ content=newContent;changed=true;}
/** * 保存*/ public synchronized void save(){ if(!changed){ //当changed值为false时直接return出去return ;}else{ doSave();//调用保存方法changed=false; //保存后changed的值修改为false } /** * 具体的修改方法,模拟写到文本中。
*/ private void doSave(){ System.out.println("线程名称:"+Thread.currentThread()。getName()+",最新需要保存的值="+content);try { Writer writer=new FileWriter(fileName);writer.write(content);writer.close();} catch (IOException e) { e.printStackTrace();}
}
}
view plainprint?
package org.zhy.demo04;
/** * 定期保存的类* @author zhengyi * */ public class SaveThread extends Thread{
private Data data;public SaveThread(String name,Data data){ super(name);//线程名称this.data=data;}
public void run(){ try { while(true){ data.save(); //模拟保存方法Thread.sleep(1000); //休息1秒} } catch (Exception e) { e.printStackTrace();}
view plainprint?
package org.zhy.demo04;
import java.util.Random;/** * 修改数据,保存文件的类* @author zhengyi * */ public class ChangeThread extends Thread{
private Data data;private Random random=new Random();public ChangeThread(String name,Data data){ super(name);//线程名称this.data=data;} public void run(){ try { for(int i=0;true;i++){ data.change("No:"+i); //模拟的数据Thread.sleep(random.nextInt(1000)); //模拟去做别的事情data.save(); //明确的要求要保存} } catch (Exception e) { e.printStackTrace();}
view plainprint?
package org.zhy.demo04;
public class Main {
public static void main(String[] args) { Data data=new Data("data.txt", "我是默认内容"); //文件的名称及文件的默认内容new ChangeThread("ChangeThread", data)。start(); //手动保存new SaveThread("SaveThread", data)。start(); //自动保存}
运行结果:
报表输出是Java应用开发中经常涉及的内容,而一般的报表往往缺乏通用性,不方便用户进行个性化编辑。Java程序由于其跨平台特性,不能直接操纵Excel。因此,本文探讨一下POI视线Java程序进行Excel的读取和导入。
项目结构:

用到的Excel文件:

XlsMain .java 类
//该类有main方法,主要负责运行程序,同时该类中也包含了用poi读取Excel(2003版)
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.util.ArrayList;
- import java.util.List;
- import org.apache.poi.hssf.usermodel.HSSFCell;
- import org.apache.poi.hssf.usermodel.HSSFRow;
- import org.apache.poi.hssf.usermodel.HSSFSheet;
- import org.apache.poi.hssf.usermodel.HSSFWorkbook;
- /**
- *
- * @author Hongten</br>
- *
- * 参考地址:http://hao0610.iteye.com/blog/1160678
- *
- */
- public class XlsMain {
- public static void main(String[] args) throws IOException {
- XlsMain xlsMain = new XlsMain();
- XlsDto xls = null;
- List<XlsDto> list = xlsMain.readXls();
- try {
- XlsDto2Excel.xlsDto2Excel(list);
- } catch (Exception e) {
- e.printStackTrace();
- }
- for (int i = 0; i < list.size(); i++) {
- xls = (XlsDto) list.get(i);
- System.out.println(xls.getXh() + " " + xls.getXm() + " "
- + xls.getYxsmc() + " " + xls.getKcm() + " "
- + xls.getCj());
- }
- }
- /**
- * 读取xls文件内容
- *
- * @return List<XlsDto>对象
- * @throws IOException
- * 输入/输出(i/o)异常
- */
- private List<XlsDto> readXls() throws IOException {
- InputStream is = new FileInputStream("pldrxkxxmb.xls");
- HSSFWorkbook hssfWorkbook = new HSSFWorkbook(is);
- XlsDto xlsDto = null;
- List<XlsDto> list = new ArrayList<XlsDto>();
- // 循环工作表Sheet
- for (int numSheet = 0; numSheet < hssfWorkbook.getNumberOfSheets(); numSheet++) {
- HSSFSheet hssfSheet = hssfWorkbook.getSheetAt(numSheet);
- if (hssfSheet == null) {
- continue;
- }
- // 循环行Row
- for (int rowNum = 1; rowNum <= hssfSheet.getLastRowNum(); rowNum++) {
- HSSFRow hssfRow = hssfSheet.getRow(rowNum);
- if (hssfRow == null) {
- continue;
- }
- xlsDto = new XlsDto();
- // 循环列Cell
- // 0学号 1姓名 2学院 3课程名 4 成绩
- // for (int cellNum = 0; cellNum <=4; cellNum++) {
- HSSFCell xh = hssfRow.getCell(0);
- if (xh == null) {
- continue;
- }
- xlsDto.setXh(getValue(xh));
- HSSFCell xm = hssfRow.getCell(1);
- if (xm == null) {
- continue;
- }
- xlsDto.setXm(getValue(xm));
- HSSFCell yxsmc = hssfRow.getCell(2);
- if (yxsmc == null) {
- continue;
- }
- xlsDto.setYxsmc(getValue(yxsmc));
- HSSFCell kcm = hssfRow.getCell(3);
- if (kcm == null) {
- continue;
- }
- xlsDto.setKcm(getValue(kcm));
- HSSFCell cj = hssfRow.getCell(4);
- if (cj == null) {
- continue;
- }
- xlsDto.setCj(Float.parseFloat(getValue(cj)));
- list.add(xlsDto);
- }
- }
- return list;
- }
- /**
- * 得到Excel表中的值
- *
- * @param hssfCell
- * Excel中的每一个格子
- * @return Excel中每一个格子中的值
- */
- @SuppressWarnings("static-access")
- private String getValue(HSSFCell hssfCell) {
- if (hssfCell.getCellType() == hssfCell.CELL_TYPE_BOOLEAN) {
- // 返回布尔类型的值
- return String.valueOf(hssfCell.getBooleanCellValue());
- } else if (hssfCell.getCellType() == hssfCell.CELL_TYPE_NUMERIC) {
- // 返回数值类型的值
- return String.valueOf(hssfCell.getNumericCellValue());
- } else {
- // 返回字符串类型的值
- return String.valueOf(hssfCell.getStringCellValue());
- }
- }
- }
Alt+Ctrl+F 代码格式化
Alt+Ctrl+O 导入包
Ctrl+/ 注释当前行
Ctrl+1 快速修复(最经典的快捷键,就不用多说了)
Ctrl+D: 删除当前行
Ctrl+Alt+↓ 复制当前行到下一行(复制增加)
Ctrl+Alt+↑ 复制当前行到上一行(复制增加)
Alt+↓ 当前行和下面一行交互位置(特别实用,可以省去先剪切,再粘贴了)
Alt+↑ 当前行和上面一行交互位置(同上)
Alt+← 前一个编辑的页面
Alt+→ 下一个编辑的页面(当然是针对上面那条来说了)
Alt+Enter 显示当前选择资源(工程,or 文件 or文件)的属性
Shift+Enter 在当前行的下一行插入空行(这时鼠标可以在当前行的任一位置,不一定是最后)
Shift+Ctrl+Enter 在当前行插入空行(原理同上条)
Ctrl+Q 定位到最后编辑的地方
Ctrl+L 定位在某行 (对于程序超过100的人就有福音了)
Ctrl+M 最大化当前的Edit或View (再按则反之)
Ctrl+/ 注释当前行,再按则取消注释
Ctrl+O 快速显示 OutLine
Ctrl+T 快速显示当前类的继承结构
Ctrl+W 关闭当前Editer
Ctrl+K 参照选中的Word快速定位到下一个
1,集合框架概述集合框架是Java中非常重要的一种数据结构,它是用来存储对象的一个容器。在容器中可以存放多个多类型的元素。集合的概念进一步提高了java的封装思想。数据的杂乱多现象用类来封装,把各个需要的数据封装到一个具体类中,一类的形式体现对象的特性。集合是用来存储类多的现象,一层层的往上封装,体现给用户只是一个简单的接口,这在程序的开发和阅读性减轻的代码的复杂度。java的扩平台易扩展特性,充分体现出这一点。这是我个人的理解。集合中存放的元素不同,根据这些元素做了相应的处理和存储,根据不同的存储结构,集合分为了List集合和Set集合两大分支。List集合的特点就是元素具备角标,对List集合中元素的访问也多了一种方式,就是按照角标操作。List集合中的元素可以重复。Set集合的特点是元素不具备角标,元素不能重复。对元素的访问使用Iterator迭代器。
2,集合的分类及特点Collection:|——list:元素是有序的,元素可以重复,因为该集合体系有索引。取元素方式有两种:一种是for循环,一种是迭代器。
ArrayList:底层数组结构,特点:增加删除比较慢,查询速度快,线程不同步。
linkedList:底层是链表结构,特点:增加删除比较快,查询速度慢。
特有方法:addFirst();addLast();getFirst();getLast();获取元素,但不删除元素。如果没有元素会出现NoSuchElementException removeFirst();removeLast();获取元素,但是元素被删除。如果没有元素会出现NoSuchElementException在JDK1.6出现了替代方法。
OfferFirst();OfferLast();peekFirst();peekFirst();获取元素,但不删除元素。如果没有元素不会出现NoSuchElementException pollFirst();pollLast();获取元素,但是元素被删除。如果没有元素不会出现NoSuchElementException vector:底层是数组结构。线程同步。增删改查速度都慢。被ArrayList代替。
枚举是vector特有的取出方式,vector有三种取出方式。枚举和迭代器很像。
其实枚举和迭代是一样的,因为枚举的名称以及方法的名称比较长,被迭代器取代了。
List集合判断元素是否相等使用是自身的equals方法。默认的equals方式,比较的是地址值。当我们比较元素是否相等时,如果判断的条件不一样时,需要复写equals方法。
|——set:元素是无序,元素不重复。Set集合的方法和Collection是一致的。
|——HashSet:底层结构式哈希表。
在对HashSet集合存储元素的时候,首先调用hashCode()方法获得给对象的哈希值,当以后加入元素时,也是首先获得该元素的哈希值,然后跟集合中已有元素的h哈希值比较,如果哈希值不等,不会调用equals()方法,元素存储成功。如果新加元素和已有某个元素的哈希值相同,在调用equals()判断是否为同一元素,做进一步的判断,这个可以根据自己的条件复写hashCode()和equals()。HashSet集合对元素不会进行默认排序。
注意:对于判断元素是否存在,以及删除等操作,依赖的方法是元素hashCode和equals |——TreeSet:可以对set集合中的元素进行排序。TreeSet集合中的元素要具备比较性,存储多个不具备比较性的元素,编译时会报错。是元素具备比较性的方式有两种:实现comparable接口和在创建集合时传递比较器comparator.
Java实现通用组合算法,存在一个类似{31311133,33113330}这样的集合,经过8取5组合,其他位置用非字母数字字符替代,比如使用*号,得到类似{3***1133,***13330,... ...}这样的集合;
现在有这样的需求:
存在一个类似{31311133,33113330}这样的集合,经过8取5组合,其他位置用非字母数字字符替代,比如使用*号,得到类似{3***1133,***13330,... ...}这样的集合;
还要求对于{3***1133,***13330}这样的集合,再次经过5取3组合,其他位置用非字母数字字符替代,比如使用*号,得到类似{*****133,*****330,3***1*3*,... ...}这样的集合。
对于这样的要求,实现的思路如下:
首先,主要思想是基于信息编码原理,通过扫描字符串,将10组合变为01组合。
其次,对于每个数字字符串,设置一个单线程,在单线程类中设置一个List用来存放待处理数字字符串(可能含有*号,或者不含有)中每个数字的(而非*号)索引位置值;
再次,设置BitSet来标志每个位置是否被*号替换得到新的组合字符串。
最后,在扫描原始待处理数字字符串的过程中,根据设置的字符列表List中索引,来操作BitSet,对于每一个BitSet得到一个新的组合。
使用Java语言实现如下:
package org.shirdrn;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
/**
* 通用组合拆分类(基于单线程)
* 可以完成两种功能:
* 第一,可以将完全数字串拆分成为含有*号的字符串。
* 例如:输入集合{31311133,33113330},Splitter类会遍历该集合,对每个字符串,创建一个SplitterThread
* 线程来处理,如果是2取1组合,即starCount=8-2=6,经过线程处理得到类似******33,*****1*3等结果
* 第二,根据从带有*号的字符串经过拆分过滤后得到的字符串集合,对其中每一个字符串进行组合
* 例如:输入集合5取1组合字符串集合{3***1133,***113330}
* CommonSplitter类会遍历该集合,对每个带有*号的字符串,创建一个SplitterThread
* 线程来处理,如果是2串1组合,即starCount=8-3-2=3,经过线程处理得到类似******33,*****1*3等结果
* @author 时延军
*/
public class CommonSplitter {
private int starCount;
private boolean duplicate;
private Collection filteredContainer;
public Collection getFilteredContainer() {
return filteredContainer;
}
/**
* 构造一个Spilitter实例
* @param container 输入的待处理字符串集合
* @param starCount 如果对于长度为N的数字字符串,进行M组合(即N取M),则starCount=N-M
* @param duplicate 是否去重
*/
public CommonSplitter(Collection container, int starCount, boolean duplicate) {
this.duplicate = duplicate;
this.starCount = starCount;
if(this.duplicate) { // 根据指定是否去重的选择,选择创建容器
filteredContainer = Collections.synchronizedSet(new HashSet());
}
else {
filteredContainer = Collections.synchronizedList(new ArrayList());
}
Iterator it = container.iterator();
while(it.hasNext()) {
new Thread(new SplitterThread(it.next().trim())).start();
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 对一个指定的N场比赛的长度为N的单式投注字符串进行组合
* 输入单式投注注字符串string,例如31311133,组合得到类似******33,*****1*3,... ...结果的集合
*
* @author 时延军
*/
class SplitterThread implements Runnable {
private char[] charArray;
private int len; // 数字字符的个数
List occupyIndexList = new ArrayList(); // 统计字符串中没有带*的位置的索引
private List container = new ArrayList();
private BitSet startBitSet; // 比特集合起始状态
private BitSet endBitSet; // 比特集合终止状态,用来控制循环
public SplitterThread(String string) {
this.charArray = string.toCharArray();
this.len = string.replace("*", "").length();
this.startBitSet = new BitSet(len);
this.endBitSet = new BitSet(len);
// 初始化startBitSet,左侧占满*符号
int count = 0; //
for (int i=0; i
if(charArray[i] != '*') {
if(count < starCount) {
this.startBitSet.set(i, true);
count++;
}
occupyIndexList.add(i);
}
}
// 初始化endBit,右侧占满*符号
count =0;
for (int i = string.length()-1; i > 0; i--) {
if(charArray[i] != '*') {
if(count < starCount) {
this.endBitSet.set(i, true);
count++;
}
ccupyIndexList.add(i);
}
}
// 根据起始startBitSet,构造带*的组合字符串并加入容器
char[] charArrayClone = this.charArray.clone();
for (int i=0; i
if (this.startBitSet.get(i)) {
charArrayClone[i] = '*';
}
}
this.container.add(new String(charArrayClone));
}
public void run() {
this.split();
synchronized(filteredContainer) {
filteredContainer.addAll(this.container);
}}
public void split() {
while(!this.startBitSet.equals(this.endBitSet)) {
int zeroCount = 0; // 统计遇到10后,左边0的个数
int oneCount = 0; // 统计遇到10后,左边1的个数
int pos = 0; // 记录当前遇到10的索引位置
char[] charArrayClone = this.charArray.clone();
// 遍历startBitSet来确定10出现的位置
for (int i=0; i
if (!this.startBitSet.get(this.occupyIndexList.get(i))) {
zeroCount++;
}
if (this.startBitSet.get(this.occupyIndexList.get(i))
&& !this.startBitSet.get(this.occupyIndexList.get(i+1))) {
pos = i;
oneCount = i - zeroCount;
// 将10变为01
this.startBitSet.set(this.occupyIndexList.get(i), false);
this.startBitSet.set(this.occupyIndexList.get(i+1), true);
break;
}
}
// 将遇到10后,左侧的1全部移动到最左侧
int count = Math.min(zeroCount, oneCount);
int startIndex = this.occupyIndexList.get(0);
int endIndex = 0;
if(pos>1 && count>0) {
pos--;
endIndex = this.occupyIndexList.get(pos);
for (int i=0; i
this.startBitSet.set(startIndex, true);
this.startBitSet.set(endIndex, false);
startIndex = this.occupyIndexList.get(i+1);
pos--;
if(pos>0) {
endIndex = this.occupyIndexList.get(pos);
}
}}
// 将遇到1的位置用*替换
for (int i=0; i
if (this.startBitSet.get(this.occupyIndexList.get(i))) {
charArrayClone[this.occupyIndexList.get(i)] = '*';
}
}
this.container.add(new String(charArrayClone));
}
}}}
测试用例如下所示:
package org.shirdrn;
import java.util.ArrayList;
import java.util.Collection;
import junit.framework.TestCase;
import org.shirdrn.util.GoodTools;
public class TestCommonSplitter extends TestCase {
private CommonSplitter splitter;
public void setSplitter(Collection container, int starCount, boolean duplicate) {
this.splitter = new CommonSplitter(container, starCount, duplicate);
}
public void testSplliter() {
Collection container = new ArrayList();
container.add("1*10**");
int starCount = 2;
boolean duplicate = true;
this.setSplitter(container, starCount, duplicate);
System.out.println(this.splitter.getFilteredContainer());
}
public void testSplliter3() {
Collection container = new ArrayList();
container.add("1*10*1300*");
int starCount = 3;
boolean duplicate = true;
this.setSplitter(container, starCount, duplicate);
System.out.println(this.splitter.getFilteredContainer());
assertEquals(35, this.splitter.getFilteredContainer().size());
}
public void testNoStar() {
Collection container = new ArrayList();
container.add("3110330");
int starCount = 3;
boolean duplicate = true;
this.setSplitter(container, starCount, duplicate);
System.out.println(this.splitter.getFilteredContainer());
assertEquals(35, this.splitter.getFilteredContainer().size());
}
public void testSplitter_8_310() {
// 8 场:310
String multiSeq = "310,310,310,310,310,310,310,310";
Collection container = GoodTools.getNSingleList(multiSeq);
assertEquals(6561, container.size());
int starCount = 4;
boolean duplicate = false;
this.setSplitter(container, starCount, duplicate);
assertEquals(459270, this.splitter.getFilteredContainer().size());
}
}
上述测试耗时大约2s左右。
上述算法实现主要是针对两种条件进行实现的,即:
第一个是完全数字字符串 ——> 带有*号的组合数字字符串;
第二个带有*号的组合数字字符串 ——> 在该基础上继续组合得到带有*号的组合数字字符串。
如果使用上述算法实现处理第一个条件,由于使用了列表List来记录索引,使执行速度略微低一点,比之于前面实现的不使用List列表来处理。
垂直定距对齐选择对象的中心 【Shift】+【A】
垂直分散对齐选择对象的中心 【Shift】+【C】
垂直对齐选择对象的中心 【C】
将文本更改为垂直排布(切换式) 【Ctrl】+【.】
打开一个已有绘图文档 【Ctrl】+【O】
打印当前的图形 【Ctrl】+【P】
打开“大小工具卷帘” 【Alt】+【F10】
运行缩放动作然后返回前一个工具 【F2】
运行缩放动作然后返回前一个工具 【Z】
导出文本或对象到另一种格式 【Ctrl】+【E】
导入文本或对象 【Ctrl】+【I】
发送选择的对象到后面 【Shift】+【B】
将选择的对象放置到后面 【Shift】+【PageDown】
发送选择的对象到前面 【Shift】+【T】
将选择的对象放置到前面 【Shift】+【PageUp】
发送选择的对象到右面 【Shift】+【R】
发送选择的对象到左面 【Shift】+【L】
将文本对齐基线 【Alt】+【F12】
将对象与网格对齐 (切换) 【Ctrl】+【Y】
对齐选择对象的中心到页中心 【P】
绘制对称多边形 【Y】
拆分选择的对象 【Ctrl】+【K】
将选择对象的分散对齐舞台水平中心 【Shift】+【P】
将选择对象的分散对齐页面水平中心 【Shift】+【E】
打开“封套工具卷帘” 【Ctrl】+【F7】
打开“符号和特殊字符工具卷帘” 【Ctrl】+【F11】
复制选定的项目到剪贴板 【Ctrl】+【C】
复制选定的项目到剪贴板 【Ctrl】+【Ins】
设置文本属性的格式 【Ctrl】+【T】
恢复上一次的“撤消”操作 【Ctrl】+【Shift】+【Z】
剪切选定对象并将它放置在“剪贴板”中 【Ctrl】+【X】
剪切选定对象并将它放置在“剪贴板”中 【Shift】+【Del】
将字体大小减小为上一个字体大小设置。 【Ctrl】+小键盘【2】
将渐变填充应用到对象 【F11】
结合选择的对象 【Ctrl】+【L】
绘制矩形;双击该工具便可创建页框 【F6】
打开“轮廓笔”对话框 【F12】
打开“轮廓图工具卷帘” 【Ctrl】+【F9】
绘制螺旋形;双击该工具打开“选项”对话框的“工具框”标签 【A】
启动“拼写检查器”;检查选定文本的拼写 【Ctrl】+【F12】
在当前工具和挑选工具之间切换 【Ctrl】+【Space】
取消选择对象或对象群组所组成的群组 【Ctrl】+【U】
显示绘图的全屏预览 【F9】
将选择的对象组成群组 【Ctrl】+【G】
删除选定的对象 【Del】
将选择对象上对齐 【T】
将字体大小减小为字体大小列表中上一个可用设置 【Ctrl】+小键盘【4】
转到上一页 【PageUp】
将镜头相对于绘画上移 【Alt】+【↑】
生成“属性栏”并对准可被标记的第一个可视项 【Ctrl】+【Backspase】
打开“视图管理器工具卷帘” 【Ctrl】+【F2】
在最近使用的两种视图质量间进行切换 【Shift】+【F9】
用“手绘”模式绘制线条和曲线 【F5】
使用该工具通过单击及拖动来平移绘图 【H】
按当前选项或工具显示对象或工具的属性 【Alt】+【Backspase】
刷新当前的绘图窗口 【Ctrl】+【W】
水平对齐选择对象的中心 【E】
将文本排列改为水平方向 【Ctrl】+【,】
打开“缩放工具卷帘” 【Alt】+【F9】
缩放全部的对象到最大 【F4】
缩放选定的对象到最大 【Shift】+【F2】
缩小绘图中的图形 【F3】
将填充添加到对象;单击并拖动对象实现喷泉式填充 【G】
打开“透镜工具卷帘” 【Alt】+【F3】
打开“图形和文本样式工具卷帘” 【Ctrl】+【F5】
退出 CorelDRAW 并提示保存活动绘图 【Alt】+【F4】
绘制椭圆形和圆形 【F7】
绘制矩形组 【D】
将对象转换成网状填充对象 【M】
打开“位置工具卷帘” 【Alt】+【F7】
添加文本(单击添加“美术字”;拖动添加“段落文本”) 【F8】
将选择对象下对齐 【B】
将字体大小增加为字体大小列表中的下一个设置 【Ctrl】+小键盘6
转到下一页 【PageDown】
将镜头相对于绘画下移 【Alt】+【↓】
包含指定线性标注线属性的功能 【Alt】+【F2】
添加/移除文本对象的项目符号 (切换) 【Ctrl】+M
将选定对象按照对象的堆栈顺序放置到向后一个位置 【Ctrl】+【PageDown】
将选定对象按照对象的堆栈顺序放置到向前一个位置 【Ctrl】+【PageUp】
使用“超微调”因子向上微调对象 【Shift】+【↑】
向上微调对象 【↑】
使用“细微调”因子向上微调对象 【Ctrl】+【↑】
使用“超微调”因子向下微调对象 【Shift】+【↓】
向下微调对象 【↓】
使用“细微调”因子向下微调对象 【Ctrl】+【↓】
使用“超微调”因子向右微调对象 【Shift】+【←】
向右微调对象 【←】
使用“细微调”因子向右微调对象 【Ctrl】+【←】
使用“超微调”因子向左微调对象 【Shift】+【→】
向左微调对象 【→】
使用“细微调”因子向左微调对象 【Ctrl】+【→】
创建新绘图文档 【Ctrl】+【N】
编辑对象的节点;双击该工具打开“节点编辑卷帘窗” 【F10】
打开“旋转工具卷帘” 【Alt】+【F8】
打开设置 CorelDRAW 选项的对话框 【Ctrl】+【J】
【Ctrl】+【A】
打开“轮廓颜色”对话框 【Shift】+【F12】
给对象应用均匀填充 【Shift】+【F11】
显示整个可打印页面 【Shift】+【F4】
将选择对象右对齐 【R】
将镜头相对于绘画右移 【Alt】+【←】
再制选定对象并以指定的距离偏移 【Ctrl】+【D】
将字体大小增加为下一个字体大小设置。 【Ctrl】+小键盘【8】
将“剪贴板”的内容粘贴到绘图中 【Ctrl】+【V】
将“剪贴板”的内容粘贴到绘图中 【Shift】+【Ins】
启动“这是什么?”帮助 【Shift】+【F1】
重复上一次操作 【Ctrl】+【R】
转换美术字为段落文本或反过来转换 【Ctrl】+【F8】
将选择的对象转换成曲线 【Ctrl】+【Q】
将轮廓转换成对象 【Ctrl】+【Shift】+【Q】
使用固定宽度、压力感应、书法式或预置的“自然笔”样式来绘制曲线 【I】
左对齐选定的对象 【L】
将镜头相对于绘画左移 【Alt】+【→】
文本编辑
显示所有可用/活动的 HTML 字体大小的列表 【Ctrl】+【Shift】+【H】
将文本对齐方式更改为不对齐 【Ctrl】+【N】
在绘画中查找指定的文本 【Alt】+【F3】
更改文本样式为粗体 【Ctrl】+【B】
将文本对齐方式更改为行宽的范围内分散文字 【Ctrl】+【H】
更改选择文本的大小写 【Shift】+【F3】
将字体大小减小为上一个字体大小设置。 【Ctrl】+小键盘【2】
将文本对齐方式更改为居中对齐 【Ctrl】+【E】
将文本对齐方式更改为两端对齐 【Ctrl】+【J】
将所有文本字符更改为小型大写字符 【Ctrl】+【Shift】+【K】
删除文本插入记号右边的字 【Ctrl】+【Del】
删除文本插入记号右边的字符 【Del】
将字体大小减小为字体大小列表中上一个可用设置 【Ctrl】+小键盘【4】
将文本插入记号向上移动一个段落 【Ctrl】+【↑】
将文本插入记号向上移动一个文本框 【PageUp】
将文本插入记号向上移动一行 【↑】
添加/移除文本对象的首字下沉格式 (切换) 【Ctrl】+【Shift】+【D】
选定“文本”标签,打开“选项”对话框 【Ctrl】+【F10】
更改文本样式为带下划线样式 【Ctrl】+【U】
将字体大小增加为字体大小列表中的下一个设置 【Ctrl】+小键盘【6】
将文本插入记号向下移动一个段落 【Ctrl】+【↓】
将文本插入记号向下移动一个文本框 【PageDown】
将文本插入记号向下移动一行 【↓】
显示非打印字符 【Ctrl】+【Shift】+【C】
向上选择一段文本 【Ctrl】+【Shift】+【↑】
向上选择一个文本框 【Shift】+【PageUp】
向上选择一行文本 【Shift】+【↑】
向下选择一段文本 【Ctrl】+【Shift】+【↓】
向下选择一个文本框 【Shift】+【PageDown】
向下选择一行文本 【Shift】+【↓】
更改文本样式为斜体 【Ctrl】+【I】
选择文本结尾的文本 【Ctrl】+【Shift】+【PageDown】
选择文本开始的文本 【Ctrl】+【Shift】+【PageUp】
选择文本框开始的文本 【Ctrl】+【Shift】+【Home】
选择文本框结尾的文本 【Ctrl】+【Shift】+【End】
选择行首的文本 【Shift】+【Home】
选择行尾的文本 【Shift】+【End】
选择文本插入记号右边的字 【Ctrl】+【Shift】+【←】
选择文本插入记号右边的字符 【Shift】+【←】
选择文本插入记号左边的字 【Ctrl】+【Shift】+【→】
选择文本插入记号左边的字符 【Shift】+【→】
显示所有绘画样式的列表 【Ctrl】+【Shift】+【S】
将文本插入记号移动到文本开头 【Ctrl】+【PageUp】
将文本插入记号移动到文本框结尾 【Ctrl】+End
将文本插入记号移动到文本框开头 【Ctrl】+【Home】
将文本插入记号移动到行首 【Home】
将文本插入记号移动到行尾 【End】
移动文本插入记号到文本结尾 【Ctrl】+【PageDown】
框架是网页中经常使用的页面设计方式,框架的作用就是把网页在一个浏览器窗口下分割成几个不同的区域,实现在一个浏览器窗口中显示多个HTML页面。使用框架可以非常方便的完成导航工作,让网站的结构更加清晰,而且各个框架之间决不存在干扰问题。利用框架最大的特点就是使网站的风格一致。通常把一个网站中页面相同的部分单独制作成一个页面,作为框架结构的一个子框架的内容给整个网站公用。
一个框架结构有两部分网页文件构成:
框架(Frame):框架是浏览器窗口中的一个区域,它可以显示与浏览器窗口的其余部分中所显示内容无关的网页文件。
框架集(Frameset):框架集也是一个网页文件,它将一个窗口通过行和列的方式分割成多个框架,框架的多少根据具体有多少网页来决定,每个框架中要显示的就是不同的网页文件。
一、创建框架
在创建框架集或使用框架前,通过选择“查看/可视化助理/框架边框”命令,使框架边框在文档窗口的设计视图中可见。
1、使用预制框架集
(1)、新建一个HTML文件,在快捷工具栏选择“布局”,单击 “框架”按钮,在弹出的下拉菜单中选择“顶部和嵌套的左侧框架”。
(2)、使用鼠标直接从框架的左侧边缘河上边缘向中间拖动,直至合适的位置,这样顶部和嵌套的左侧框架就完成了。
2、鼠标拖动创建框架
(1)、新建普通网页,命名后将其打开。
(2)、把鼠标放到框架边框上,出现双箭头光标时拖拽框架边框,可以垂直或水平分割网页。
二、、保存框架
每一个框架都有一个框架名称,可以用默认的框架名称,也可以在属性面板修改名称,我们采用系统默认的框架名称topFrame(上方)、leftFrame(左侧)、mainFrame(右侧)。
选择菜单栏>文件>保存全部,将框架集保存为index.html,上方框架保存为07.html,左侧框架保存为08.html,右侧框架保存为09.html。
这个步骤虽然简单,但是很关键,只有将总框架集和各个框架保存在本地站点根目录下,才能保证浏览页面时显示正常。
三、 编辑框架式网页
虽然框架式网页把屏幕分割成几个窗口,每个框架(窗口)中放置一个普通的网页,但是编辑框架式网页时,要把整个编辑窗口当作一个网页来编辑,插入的网页元素位于哪个框架,就保存在哪个框架的网页中。框架的大小可以随意修改。
1、 改变框架大小
用鼠标拖拽框架边框可随意改变框架大小。
2、 删除框架
用鼠标把框架边框拖拽到父框架的边框上,可删除框架。
3、设置框架属性
设置框架属性时,必须先选中框架。选择框架方法如下:
选择菜单栏>窗口>框架,打开框架面板,单击某个框架,即可选中该框架。
在编辑窗口某个框架内按住Alt键并单击鼠标,即可选择该框架。当一个框架被选择时,它的边框带有点线轮廓
2. 设置框架属性
选中框架,在属性面板上可以设置框架属性:框架名称、源文件、空白边距、滚动条、重置大小和边框属性等。
需要注意的是:1、框架是不可以合并的。2、在创建链接时要用到框架名称,所以我们要很清楚的知道每个框架对应的框架名。
四、在框架中使用超级链接
在框架式网页中制作超级链接时,一定要设置链接的目标属性,为链接的目标文档指定显示窗口。链接目标较远(其他网站)时,一般放在新窗口,在导航条上创建链接时,一般将目标文档放在另一个框架中显示(当页面较小时)或全屏幕显示(当页面较大时)。
“目标”下拉菜单中的选项:
* _blank 放在新窗口中。
* _parent 放到父框架集或包含该链接的框架窗口中。
* _self 放在相同窗口中(默认窗口无须指定)。
* _top 放到整个浏览器窗口并删除所有框架。
在我们保存有框架名为mainFrame、leftFrame、topFrame的框架后,在目标下拉菜单中,还会出现mainFrame、leftFrame、topFrame选项:
* mainFrame 放到名为mainFrame的框架中。
* leftFrame 放到名为leftFrame的框架中。
* topFrame放到名为topFrame的框架中。
五、制作框架页面
1、选择菜单栏>窗口>框架,打开框架面板,选中整个框架集,如下图所示:
在属性面板中,将行的值设置为100,单位为像素,如下图所示:
2、选择菜单栏>窗口>框架,打开框架面板,选中子框架集,如下图所示:
在属性面板中,将列的值设置为200,单位为像素,如下图所示:
这样,我们就完成了对整个框架的布局。下面我们来布局各个框架页面。
3、鼠标在topFrame框架中的空白处点击一下,我们会看见文档窗口上方的文件名变为了07.html。在页面属性中将上、下、左。右边距全部设为0。
插入一个1行2列的表格,宽度为100%,高度为100px,左单元格宽度为382px并插入背景图片img/103.jpg,设置表格的背景颜色为103.jpg图片右边缘的绿色(用吸管吸取)。
4、鼠标在leftFrame框架中的空白处点击一下,我们会看见文档窗口上方的文件名变为了08.html,在页面属性中将上、下、左。右边距全部设为0。
插入一个6行1列 的表格,表格宽度为95%,居中对齐。将第一个单元格的高度设为20px,选中其余单元格将高度设置为50px。分别输入文字设置导航栏目。
分别对各个导航栏目建立链接关系,链接路径指向要链接到的网页,目标选择mainFrame框架。
5、鼠标在mainFrame框架中的空白处点击一下,我们会看见文档窗口上方的文件名变为了09.html,在页面属性中将上、下、左。右边距全部设为0。
自己设置一个欢迎页面。
至此,我们完成了一个框架网站的制作。
本文介绍如下几个方面的内容:
1.如何创建数组
2.如何对数组进行操作(添加,删除,读取)
3.数组常见方法和属性
如何创建一个数组,一般地根据初始化设定简单分为3种:
1.单纯创建数组:
var arr=new Array();
要点:用new关键字创建数组对象Array(),Array()对象是一个本地类,可以用new创建一个对象后使用
2.创建数组的同时规定数组大小:
var arr=new Array(10);//这里就创建了一个初始化大小为10的数组
注意:当使用数组大小操作初始化大小时,数组会自动被撑大,不会像C语言那样发生错误.动态增长是js数组的一个性质.另外,js中支持最大数组长度为4294967295
3.直接初始化:
var arr=new Array("草履虫","爱","毛毛");//这里就直接初始化了数组
或var arr=["草履虫","爱","毛毛"];//括号也可以声明一个数组对象
当然,类似C语言,你也可以定义2维3维和多维的数组,这里不讨论.
数组的属性:length
arr.length返回数组arr的长度,常见于循环中对数组的遍历,比如:
for(var i=0;i<arr.length;i++){
执行部分
}
数组元素的访问: arr[index],其中index表示索引即数组基数,从0开始,共有arr.length个元素.比如: arr[0]访问第一个数组元素,arr[1]访问第二个数组元素....依次类推 数组的操作方法:先概览下下面的这些操作数组常用的方法(13个)
toString(),valueOf(),toLocalString(),join(),split(),slice(),concat(),
pop(),push(),shift(),unshift(),sort(),splice()
下面逐一分析这些方法的功能和用法.
toString(),valueOf(),toLocalString():
功能:返回数组的全部元素
注:数组名也可以返回整个数组
代码:
var m=["am","bm","cm"];//用括号声明一个数组对象
alert(m.toString());//toString()返回数组对象的所有内容,用逗号分割,即am,bm,cm
alert(m.valueOf());//valueOf()也返回数组对象的所有内容
alert(m.toLocaleString());//toLocaleString()也返回数组对象的所有内容,但有地区语言区别,暂不研究
alert(m);//数组名也返回数组对象的所有内容
var m=["am","bm","cm"];//用括号声明一个数组对象 alert(m.toString());//toString()返回数组对象的所有内容,用逗号分割,即am,bm,cm alert(m.valueOf());//valueOf()也返回数组对象的所有内容 alert(m.toLocaleString());//toLocaleString()也返回数组对象的所有内容,但有地区语言区别,暂不研究 alert(m);//数组名也返回数组对象的所有内容
运行: <script>var m=["am","bm","cm"];alert(m.toString());alert(m.valueOf());alert(m.toLocaleString());</script>
join():
功能:把数组各个项用某个字符(串)连接起来,但并不修改原来的数组
代码:
var m=["am","bm","cm"];//用括号声明一个数组对象
var n=m.join("---");//用---连接am,bm,cm.
alert(m.toString());//m并没有被修改,返回am,bm,cm
alert(n);//n是一个字符串,为am---bm---cm
var m=["am","bm","cm"];//用括号声明一个数组对象 var n=m.join("---");//用---连接am,bm,cm. alert(m.toString());//m并没有被修改,返回am,bm,cm alert(n);//n是一个字符串,为am---bm---cm
运行: <script>var m=["am","bm","cm"];var n=m.join("---");alert(m.toString());alert(n);</script>
split():
功能:把一个字符串按某个字符(串)分割成一个数组,但不修改原字符串
代码:
var str="I love maomao,I am caolvchong";
var arr=str.split("o");//按字符o把str字符串分割成一个数组
alert(arr);//输出整个数组
var str="I love maomao,I am caolvchong"; var arr=str.split("o");//按字符o把str字符串分割成一个数组 alert(arr);//输出整个数组
运行: <script>var str="I love maomao,I am caolvchong";var arr=str.split("o");alert(arr);</script>
slice():返回某个位置开始(到某个位置结束)的数组部分,不修改原数组
代码:
var m=["am","bm","cm","dm","em","fm"];
var n=m.slice(2);//返回第二个元素bm后面的元素,即cm,dm,em,fm
var q=m.slice(2,5);//返回第二个元素后到第五个元素,即cm,dm,em
alert(n);
alert(q);
var m=["am","bm","cm","dm","em","fm"]; var n=m.slice(2);//返回第二个元素bm后面的元素,即cm,dm,em,fm var q=m.slice(2,5);//返回第二个元素后到第五个元素,即cm,dm,em alert(n); alert(q);
运行: <script>var m=["am","bm","cm","dm","em","fm"];var n=m.slice(2);var q=m.slice(2,5);alert(n);alert(q);</script>
数组对象的栈操作:
push():数组末尾添加一个项
pop():删除数组最后一个项
代码:
var m=["am","bm","cm","dm","em","fm"];
m.push("gm");//在数组末尾添加元素gm
alert(m);
m.pop();//删除数组最后一个元素gm
alert(m);
var m=["am","bm","cm","dm","em","fm"]; m.push("gm");//在数组末尾添加元素gm alert(m); m.pop();//删除数组最后一个元素gm alert(m);
运行: <script>var m=["am","bm","cm","dm","em","fm"];m.push("gm");alert(m);m.pop();alert(m);</script>
数组对象的队列操作:
unshift():数组头添加一个项
shift():删除数组第一个项
代码:
var m=["am","bm","cm","dm","em","fm"];
m.unshift("gm");//在数组第一个元素位置添加元素gm
alert(m);
m.shift();//删除数组第一个元素gm
alert(m);
var m=["am","bm","cm","dm","em","fm"]; m.unshift("gm");//在数组第一个元素位置添加元素gm alert(m); m.shift();//删除数组第一个元素gm alert(m);
运行: <script>var m=["am","bm","cm","dm","em","fm"];m.unshift("gm");alert(m);m.shift();alert(m);</script>
sort():数组按字符的ASCII码进行排序,修改数组对象
注:即便是数字数组,也将转化为字符串来进行比较排序
代码:
var m=["am","fm","gm","bm","em","dm"];
m.sort();//按字母序排序
alert(m);
var m=["am","fm","gm","bm","em","dm"]; m.sort();//按字母序排序 alert(m);
运行: <script>var m=["am","fm","gm","bm","em","dm"];m.sort();alert(m);</script>
concat():在数组尾添加元素,但不修改数组对象
代码:
var m=["am","bm"]
var n=m.concat("cm");//添加一项cm,并且赋予新数组对象
alert(m);//原数组没有被修改
alert(n);//输出新数组对象
var m=["am","bm"] var n=m.concat("cm");//添加一项cm,并且赋予新数组对象 alert(m);//原数组没有被修改 alert(n);//输出新数组对象
运行: <script>var m=["am","bm"];var n=m.concat("cm");alert(m);alert(n);</script>
splice():在数组的任意位置进行添加,删除或者替换元素,直接修改数组对象
细节:
splice()有三个参数或三个以上参数,前两个是必须的,后面参数是可选的
进行添加:splice(起始项,0,添加项)
进行删除:splice(起始项,要删除的项个数)
进行替换:splice(起始项,替换个数,替换项) 这个其实是添加删除的共同结果
代码:
var m=["am","bm"]
m.splice(1,0,"fm","sm");//在第一项后面添加fm和sm,返回am,fm,sm,bm
alert(m);
m.splice(2,1);//删除第二项后面一项(即第三项sm,返回am,fm,bm)
alert(m);
m.splice(2,1,"mm");//替换第二项后面一项(即第三项,返回am,fm,mm)
alert(m);
var m=["am","bm"] m.splice(1,0,"fm","sm");//在第一项后面添加fm和sm,返回am,fm,sm,bm alert(m); m.splice(2,1);//删除第二项后面一项(即第三项sm,返回am,fm,bm) alert(m); m.splice(2,1,"mm");//替换第二项后面一项(即第三项,返回am,fm,mm) alert(m);
运行: <script>var m=["am","bm"];m.splice(1,0,"fm","sm");alert(m);m.splice(2,1);alert(m);m.splice(2,0,"mm");alert(m);</script>