最近发现大家很多问题都涉及到了混淆器的概念,特发此贴,希望在大家的学习过程中有所借鉴。
最近试用了几个Java混淆器(Java Obfuscator),感觉没有一个完全另人满意的,
于是想干脆自己写一个得了。翻了几页Java虚拟机规范之后突发奇想,别的混淆器
都是在编译好的byte code上做文章,能不能从源码直接编译成经过混淆的class文
件呢?就这样花了一个多星期的时间写了一个Java混淆编译器(Java Obfuscator
Compiler)。
Q: 什么是混淆器?
A: 由于Java程序运行时是动态连接的,因此编译成的目标文件中包含有符号表,
使得Java程序很容易被反编译,混淆器可以打乱class文件中的符号信息,使反向
工程变得非常困难。
Q: 现有的混淆器有什么问题?
A: 现有的混淆器都是对编译好的class文件进行混淆,这样就需要编译和混淆两个
步骤。并不是所有的符号都需要混淆,如果你开发的是一个类库,或者某些类需要
动态装载,那些公共API就必须保留符号不变,这样别人才能使用你的类库。现有
的混淆器提供了GUI或脚本的方式来对那些需要保留的符号名称进行配置,如果程
序较大时配置工作变得很复杂,而程序一旦修改配置工作又要重新进行。某些混淆
器能够调整字节码的顺序,使反编译更加困难,但我经历过混淆之后的程序运行出
错的情况。
Q: Java混淆编译器是如何工作的?
A: Java混淆编译器是在Sun JDK中提供的Java编译器(javac)的基础上完成的,修
改了代码生成过程,对编译器生成的中间代码进行混淆,最后再生成class文件,
这样编译和混淆只需要一个步骤就可以完成。另外可以在源程序中插入符号保留指
令来控制哪些符号需要保留,不需要单独的配置。
Q: 如何安装和运行JOC?
A: 下载joc.jar (http://www.apusic.com/product/cpsy.htm),运行java -jar
joc.jar就可以启动Java混淆编译器,joc的命令行参数和javac完全相同,但增加
了一个新的参数-Xobfuscate,它的用法如下:
-Xobfuscate:;
其中;指定混淆级别,可以是以下几种级别:
-Xobfuscate:none 不进行混淆
-Xobfuscate:private 对所有private访问级别的元素进行混淆
-Xobfuscate:package 对所有private或package private元素进行混
淆
-Xobfuscate:protected 对所有private, package private,
protected元素进行混淆
-Xobfuscate:public 对所有的元素都进行混淆
-Xobfuscate:all 相当于-Xobfuscate:public
如果使用-Xobfuscate不带级别参数,则相当于-Xobfuscate:package
Q: 如何使用符号保留指令?
A: 除了在命令行用-Xobfuscate参数控制符号混淆级别外,还可以在源代码中使用
符号保留指令来控制那些符号需要保留,符号保留指令是一个Java文档注释指令,
可以插入在类和类成员的文档注释中,例如:
/**
* This class should preserve.
* @preserve
*/
public class Foo {
/**
* You can specify which field should be preserved.
* @preserve
*/
private int x;
/**
* This field is not preserved.
*/
private int y;
/**
* You can also preserve methods.
* @preserve
*/
public void hello() {}
/**
* This method is not preserved.
*/
private void collect() {}
}
如果没有@preserve指令,则根据混淆级别及成员的访问级别来确定符号是否保留
。
对于类的符号保留指令可以附带一个保留级别参数,来控制类成员的符号保留,包
括:
@preserve 仅对类名进行保留,类成员的保留根据
-Xobfuscate命令行参数决定
@preserve public 保留所有public成员
@preserve protected 保留所有public和protected成员
@preserve package 保留所有public, protected, package private成
员
@preserve private 保留所有成员
@preserve all 相当于@preserve private
Q: JOC有哪些限制?
A: 不支持分别编译,必须对所有的源文件进行混淆编译。
最后给出一个JOC混淆的效果:
源文件:
import java.awt.event.*;
import javax.swing.*;
public class AboutBox extends JDialog
{
public AboutBox()
{
initForm();
}
JPanel panel1 = new JPanel();
JButton button1 = new JButton();
JLabel jLabel2 = new JLabel();
JTextArea jTextArea1 = new JTextArea();
/**
* NOTE: The following code is required by the form designer.
* It can be modified using the form editor. Do not
* modify it using the code editor.
*/
private void initForm()
{
this.setDefaultCloseOperation( WindowConstants.DISPOSE_ON_CLOSE
);
this.getContentPane().setLayout( new java.awt.CardLayout());
this.setModal( true );
this.setResizable( false );
this.setTitle( "About..." );
panel1.setLayout( null );
button1.setText( "OK" );
button1.setBounds( 272, 168, 88, 24 );
panel1.add( button1 );
jLabel2.setText( "File System Viewer for Swing 1.1.1" );
jLabel2.setVerticalAlignment( SwingConstants.TOP );
jLabel2.setBounds( 64, 32, 240, 56 );
panel1.add( jLabel2 );
jTextArea1.setFont( new java.awt.Font( "Dialog", 0, 10 ));
jTextArea1.setLineWrap( true );
jTextArea1.setOpaque( false );
jTextArea1.setText( "This computer program is protected by
copyright law." );
jTextArea1.setWrapStyleWord( true );
jTextArea1.setBounds( 8, 112, 256, 80 );
panel1.add( jTextArea1 );
this.getContentPane().add( panel1, "Card1" );
this.setSize( 376, 228 );
button1.addActionListener( new java.awt.event.ActionListener(){
public void actionPerformed( java.awt.event.ActionEvent
ev ){
button1_actionPerformed( ev );
}});
}
private void button1_actionPerformed(ActionEvent ev)
{
this.dispose();
}
}
经Javac编译后用JAD反编译的结果:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.text.JTextComponent;
public class AboutBox extends JDialog
{
JPanel panel1;
JButton button1;
JLabel jLabel2;
JTextArea jTextArea1;
public AboutBox()
{
panel1 = new JPanel();
button1 = new JButton();
jLabel2 = new JLabel();
jTextArea1 = new JTextArea();
initForm();
}
private void initForm()
{
setDefaultCloseOperation(2);
getContentPane().setLayout(new CardLayout());
setModal(true);
setResizable(false);
setTitle("About...");
panel1.setLayout(null);
button1.setText("OK");
button1.setBounds(272, 168, 88, 24);
panel1.add(button1);
jLabel2.setText("File System Viewer for Swing 1.1.1");
jLabel2.setVerticalAlignment(1);
jLabel2.setBounds(64, 32, 240, 56);
panel1.add(jLabel2);
jTextArea1.setFont(new Font("Dialog", 0, 10));
jTextArea1.setLineWrap(true);
jTextArea1.setOpaque(false);
jTextArea1.setText("This computer program is protected by
copyright law.");
jTextArea1.setWrapStyleWord(true);
jTextArea1.setBounds(8, 112, 256, 80);
panel1.add(jTextArea1);
getContentPane().add(panel1, "Card1");
setSize(376, 228);
button1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent actionevent)
{
button1_actionPerformed(actionevent);
}
});
}
private void button1_actionPerformed(ActionEvent actionevent)
{
dispose();
}
}
经JOC混淆编译后用JAD反编译的结果:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.text.JTextComponent;
public class AboutBox extends JDialog
{
JPanel _$1;
JButton _$2;
JLabel _$3;
JTextArea _$4;
public AboutBox()
{
_$1 = new JPanel();
_$2 = new JButton();
_$3 = new JLabel();
_$4 = new JTextArea();
_$1();
}
private void _$1()
{
2;
this;
JVM INSTR swap
setDefaultCloseOperation();
getContentPane().setLayout(new CardLayout());
true;
this;
JVM INSTR swap
setModal();
false;
this;
JVM INSTR swap
setResizable();
"About...";
this;
JVM INSTR swap
setTitle();
_$1.setLayout(null);
_$2.setText("OK");
_$2;
168;
272;
JVM INSTR swap
24;
88;
JVM INSTR swap
setBounds();
_$1.add(_$2);
_$3.setText("File System Viewer for Swing 1.1.1");
_$3.setVerticalAlignment(1);
_$3;
32;
64;
JVM INSTR swap
56;
240;
JVM INSTR swap
setBounds();
_$1.add(_$3);
_$4;
JVM INSTR new #13 ;;
JVM INSTR dup
0;
"Dialog";
JVM INSTR swap
10;
Font();
setFont();
_$4.setLineWrap(true);
_$4.setOpaque(false);
_$4.setText("This computer program is protected by copyright
law.");
_$4.setWrapStyleWord(true);
_$4;
112;
8;
JVM INSTR swap
80;
256;
JVM INSTR swap
setBounds();
_$1.add(_$4);
getContentPane().add(_$1, "Card1");
376;
this;
JVM INSTR swap
228;
setSize();
_$2.addActionListener(new IIlIlIIIIlllIIII(this));
return;
}
private void _$1(ActionEvent actionevent)
{
dispose();
}
/*
static void access$0(AboutBox aboutbox, ActionEvent actionevent)
{
actionevent;
aboutbox;
JVM INSTR swap
_$1();
return;
}
*/
// Unreferenced inner classes:
/* anonymous class */
final class IIlIlIIIIlllIIII
implements ActionListener
{
public void actionPerformed(ActionEvent actionevent)
{
AboutBox.access$0(AboutBox.this, actionevent);
}
{
AboutBox.this;
this;
JVM INSTR swap
this$0;
}
}
}
elgs 回复于:2003-12-26 13:03:43
唉, 为什么要人为制造障碍。
南非蜘蛛 回复于:2003-12-26 13:11:41
不错,顶
猫小 回复于:2003-12-26 13:29:34
这就是Java软件面临的挑战
目前,由于黑客的频繁活动,使得Java类文件面临着反编译的挑战。有一些工具能够对Java源代码进行反工程,其结果甚至以比普通Java文件更可读的方式, 尽管普通的Java文件(由于代码风格不同)有注释。许可证和软件过期对于用户们来说将变得无用 。因此,防止软件被反编译或使得反编译的结果变得无意义对于Java来说非常重要。
一个Java类文件不一定非要存储在一个真正的文件里;它可以存在存贮器缓冲区,或从一个网络流获得。尽管防火墙和网络协议如TCP/IP有安全策略,黑客仍能打破访问限制获取一些类。尽管这些类能被混淆,他们(黑客)能够一步一步地分析和猜出每个指令的目的。如果这些代码是关键技术部分,例如是大产品的许可证或时间期满部分,反编译和分析指令的努力似乎很值得。如果这些关键类被隐藏或被一个关键字加密,黑客的非法入侵就很困难了。而且,未认证的软件复制对智能产权是普遍的攻击。还没有一个较好的通用方案来解决这类问题。
混淆器能够保护软件使之不被反编译。它通过混淆类文件使得反编译无效,并把敏感的名字指代变成另一个名字空间,这样可以使反编译的结果毫无疑义。混淆器有“完全”和“快速”两种选项,能处理任何Java产品包括API,应用程序和小程序。它遵从Java虚机规范。
Sun 中国技术开发中心开发的JADE就是为解决上述问题而设计的。
可以去官方网站看看:)
流浪的狗 回复于:2003-12-26 13:33:02
经过混淆后的java应用程序对java虚拟机有要求了,这样就违背了一次编写,到处运行的初衷了!
猫小 回复于:2003-12-26 13:45:53
Class文件的结构是这样:它包含一个常数池(constant pool)这个里面就存放了变量和方法的名称等一些和Class相关的东西,我们通常所说的混淆就是把这个常数池里面的东东弄乱,这样做只是为了骗过反编译器和你,呵呵:)这样反编译出的东西一般是编译不过去的。
也就是说混淆器是针对反编译器的,不针对编译器 :)
流浪的狗 回复于:2003-12-26 13:57:20
咿呀!混淆后的class文件拿到别的运行时系统下,java虚拟机绝对不认识。
cctech 回复于:2003-12-26 15:41:30
猫小大侠:
我就是上一次发帖子请教混淆器的问题的,这对我很重要,因为我辛苦编写的代码,编译后反编译一下,几乎是全部正确的。所以我想到要混淆,你上面转发的文章我也看过,我没有试成,根据网上的资料,我先后用了retroguard,jode,jodo等,都没有试成功,我所以还得请教:
1、是否能直接混淆*.java文件
2、我是在编辑器中编写的,是否不能被识别(因为格式没有可视化工具那样规整?)
3、我用javac -jar dest.jar source*.java编译的jar文件也没有混淆成功。
4、我在JB9里的混淆器中也没有试成功,所以我知道是我方法有问题。
以上问题,望能赐教,谢谢!cctech@vip.sina.com
netkiller 回复于:2003-12-26 20:56:22
越搞越慢...
不支持..对于高手来说..这不算什么...
如果反编译..我要的不是源程序.而是文件中的有用的东东..
如:数据库连接密码(有很多用户喜欢放在class)
建议使用程序签名技术..非对称加密程序..除非你有证书.不然...
elgs 回复于:2003-12-27 09:34:12
如果有人处心积虑要反编译你的源代码, 你应该很高兴才对! 这证明了你的辛苦没有白费啊! 我们受微软的不良影响太深了, 微软一个封闭的体系, 死亡是迟早的。
netkiller 回复于:2003-12-27 12:07:58
为什么出现 编译器?????
原始是机器码。
之后是汇遍
它们最终产生的代码都是机器码。
这也是为什么C/C++为什么火的原因。编译出来的东东又小,又快(注:小,快,不是因为他是C。而是他的编译器一流。当时palcas编译器的速度也非常快。注:JVM编译无论如果优化也不可能超越C。)
因为当时的机器很慢。。内存小。所以要编译。这样就不用一个解释器(他可站内存,和CPU,如JVM)
随着技术发展。。
CPU越来越快。内存也越大。。
而编译器技术又太复杂,能写编译器的人屈指可数。
这时出现解释器。(上学时都应该做过语法分析器吧,实现一下shell)
如win basic .... ,unix perl,awk,sed,tcl......
解释器发展一段时间后。。发现不理想。反回到编译器。(因为PC相的速度还是慢)
但解释器在UNIX平台上。发展越火。。UNIX多运行 mainframe
gonme xwindow整个都是用解释程序实现。(他比KDE慢。因为KDE是C++写的,哈哈)
猫小 回复于:2003-12-27 17:45:59
引用:原帖由 "cctech" 发表:
猫小大侠:
我就是上一次发帖子请教混淆器的问题的,这对我很重要,因为我辛苦编写的代码,编译后反编译一下,几乎是全部正确的。所以我想到要混淆,你上面转发的文章我也看过,我没有试成,根据网上的资料,我..........
之所以发此贴是因为最近感觉到有些朋友在问相关的问题
关于混淆器我没有做过实验,你所说的问题有待探讨和研究
如果上面所转文章有错误,请你指正一下帮助后来阅读的朋友,谢谢
chujian 回复于:2003-12-29 10:37:40
很复杂的东西,就算反编译过来,看也看的够呛,能连冠着看的懂的,我相信他也是会写这样的程序的,所以混淆编译意义也不大。
netkiller 回复于:2003-12-29 13:03:12
其实。从另一个角度看。。我也支持混淆器。
因为国内的公司。。真不要脸。。
给别人的东西改改。就说是自己研发的。
中国在这方面还没有相关法。。。
chujian 回复于:2003-12-30 00:25:05
是中国人不要脸
netkiller 回复于:2003-12-30 09:04:31
我听国外的朋友说。。外国人歧视中国人..
办签证时.其它国家的人.几分种.
到中国人时.就开始百般刁难。。。
(不过听收只要给送他20美金就OK了:))
sylpjx 回复于:2003-12-30 09:27:59
chujian你是哪儿人,如果你也是中国人的话,那你也就不要脸了,如果你不是中国人,请你出来说明到底是什么原因说中国人不要脸和要脸的标准是什么?
garydo 回复于:2003-12-30 09:39:04
下载joc.jar (http://www.apusic.com/product/cpsy.htm)
这个东东根本打不开!
chujian 回复于:2003-12-31 11:31:24
因为我是中国人,所以要被别人骂不要脸。
就是因为中国人不要脸,所以我被骂了也无奈。
wuxingwww 回复于:2004-03-13 17:29:50
因为混淆的只是私有变量,把本来见名知义的变量换成了一堆数字或是其它无意义的变量,包括私有函数都被混淆编译。而公有接口并不影响。
sakulagi 回复于:2004-03-13 21:48:55
引用:原帖由 "chujian"]是中国人不要脸
发表:
嘿嘿嘿,说什么哪?
sakulagi 回复于:2004-03-13 21:53:28
引用:原帖由 "netkiller" 发表:
为什么出现 编译器?????
原始是机器码。
之后是汇遍
它们最终产生的代码都是机器码。
这也是为什么C/C++为什么火的原因。编译出来的东东又小,又快(注:小,快,不是因为他是C。而是他的编译器一流。当?.........
我怎么觉得好像GNome比KDE快?另外,能不能介绍一下gnome是用什么语言写的?
还有,其实非对称加密和数字签名需要很多资源,总不能卖给最终用户的java产品都是加密过的,一边解密一便执行吧?所以个人觉得混淆器还是有市场的。
说得不对的话,请多指正。:)
原文链接:http://bbs.chinaunix.net/viewthread.php?tid=231125
转载请注明作者名及原文出处
2013年2月20日 星期三
Java混淆编译器(转)
Labels:
Java
在文字的世界裡尋找心靈的共鳴,遠山藍以溫柔的筆觸分享書籍的力量與生活的智慧。無論是細膩的書評、深刻的人生感悟,還是技術與創新的新奇發現,每篇文章都是一次內心的療癒旅程。希望透過閱讀,帶領讀者在忙碌的生活中找到一片寧靜與啟發。讓我們一起,在書香中遇見更好的自己!
JAVA初学教程进阶
本贴参考资料如下:1>;Thinking in Java(3rd)
2>;JAVA大全(电子工业出版社)
第一次:Java的语言基础
Java语言是目前最受欢迎的程序语言之一,与传统的程序语言相比(比如C、C++),Java语言简单易学,使用它编程时间短、功能强,人们接受起来也更快、更简便。Java主要具有以下三个特点:
第一, 跨平台,不管Windows还是Unix或者其他平台,它都可以使用;
第二, 面向对象;
第三, 动态域的控制。
1.1 初识Java语言
像其它编程语言一样,Java编程语言也被用来创建应用程序。那,首先我们还是从一个最普遍的小应用程序范例来切入——在屏幕上显示字串“Hello World!”。下列代码给出了这个Java应用程序。
1.1.1 HelloWorldApp
CODE:
[Copy to clipboard]
1.//
2.// Sample HelloWorld application
3.//
4.public class HelloWorldApp{
5.public static void main (String args[]) {
6.System.out.println ("Hello World!");
7.}
8.}
以上程序行是在你的屏幕上打印“Hello World!”所需的最少组件。接下来,我们就针对上面给出的这段程序来简单解释一下,并且来实际编译、运行它。
1.1.2 描述HelloWorldApp
第1-3行
程序中的1-3行是注释行
1 //
2 // Sample HelloWorld application
3 //
第4行
第4行声明类名为HelloWorldApp。类名(Classname)是在源文件中指明的,它可在与源代码相同的目录上创建一个classname.class文件。在本例题中,编译器创建了一个称为HelloWorldApp.class的文件, 它包含了公共类HelloWorldApp的编译代码。
4 public class HelloWorldApp{
第5行
第5行是程序执行的起始点。Java技术解释器必须发现这一严格定义的点,否则将拒绝运行程序。
其它程序语言(特别是C和C++)也采用main ()声明作为程序执行的起始点。此声明的不同部分将在本课程的后几部分介绍。
如果在程序的命令行中给出了任何自变量,它们将被传递给main()方法中被称作args的String数组。在本例题中,未使用自变量。
5 public static void main (String args[]) {
- public-方法main()可被任何程序访问,包括Java技术解释器。
- static- 是一个告知编译器main()是用于类HelloWorldApp中的函数的关键字。为使main()在程序做其它事之前就开始运行,这一关键字是必要的。
- void- 表明main()不返回任何信息。这一点是重要的,因为Java编程语言要进行谨慎的类型检查,包括检查调用的方法确实返回了这些方法所声明的类型。
- String args [] - 是一个String数组的声明,它将包含位于类名之后的命令行中的自变量。
java HelloWorldApp args[0]args[1]····
第6行
第6行声明如何使用类名、对象名和方法调用。它使用由System类的out成员引用的PrintStreamout对象的println()方法,将字串“Hello World!”打印到标准输出上。
6 System.out.println (“Hello World!”);
在这个例子中,println()方法被输入了一个字串自变量并将其写在了标准输出流上。
第7-8行
本程序的7-8行分别是方法main()和类HelloWorldApp的下括号。
7 }
8 }
1.1.3 编译并运行HelloWorldApp
编译
当你创建了HelloWorldApp.java源文件后,用下列程序行进行编译:
c:\student\javac HelloWorldApp.java
如果编译器未返回任何提示信息,新文件HelloWorldApp.class则被存储在与源文件相同的目录中,除非另有指定。
运行
为运行你的HelloWorldApp应用程序,需使用Java解释器和位于bin目录下的java:
c:\student\ java HelloWorldApp
Hello World!
1.1.4 严格的约定
通过上面实际编写、编译和运行这个简单的例子,我们会发现Java是一种限制很严格的语言。我们在编写Java程序的时候,一定要严格遵从它的约定,以后你会发现这些约定对维护和组织程序,使你养成一种优良的编程风格都很有帮助。
->; 源文件的类型
在Java中,一个源程序文件被称为一个编译单元。它是一个包含一个或多个类定义的文本文件。Java编译器要求源程序文件必须使用.java文件扩展名。这里可以注意到这个文件的扩展名是4个字符,所以,你的操作系统必须要有支持长文件名的能力。
->; 源文件的命名
如果.java文件包括一个公共类,那么它必须使用与那个公共类相同的文件名。例如在前例中的类的定义是
public class HelloWorldapp
源文件名则必须是HelloWorldapp.java
->; 类的数量
在源文件中,可以定义多个类,但每次只能定义一个公共类。
->; 标识符
Java中使用的标识符可以是大写和小写字母、数字、下划线(_)、美元符号($)的任意组合。以下是一些有效的标识符:
Year99 count this_is_ok $some
以下是一些无效的标识符:
99year hello# not/ok
这里需要提醒大家注意的是,Java是一种大小写敏感的语言,所以大家在书写代码的时候一定要注意大小写的运用。
1.2 结构与构件
现在,我们从一个典型的Java源文件来了解一下Java程序涉及到的一些重要的构件,以及这些构件的结构。
CODE:
[Copy to clipboard]
1 package trades;
2 import java.util.Vector;
3 public class Quote extends Thread implements Tradable {
4 // Code goes in here
5 }
那我们从上面的例子中可以发现源文件的结构主要包含三个要素:
->; 一个包声明(可选)
程序的第1行就是声明了一个包(package),把该程序放在这个包中间。
->; 任意数量的包引入语句(可选)
程序的第2行是要求该程序引入系统提供的一个包java.util.Vector。
->; 任意数量的类的声明
程序的3-5行则是声明了一个public的类Quote,它是从它的超类Thread扩展得到,并且实现Tradable的接口。
该三要素必须以上述顺序出现。即,任何包引入语句出现在所有类定义之前;如果使用包声明,则包声明必须出现在类和引入语句之前。
结合上面的例子,我们接下来简单的讨论一下涉及到的这些构件,包package、包引入import、类class、方法method和变量variable。
1.2.1 包package
包实际上就是一个组织类class和接口interface的集合。通过包的这种机制,来把类名空间划分成更多易于管理的块。这样做其主要目的就是可以避免类名的冲突,在不同的包里的类,其名字相同也不会发生冲突。包的主要功能如下:
->; 依照功能来组织和划分类。例如:java.awt包含了构成抽象窗口工具包(AWT)的类,这个包被用来构建和管理应用程序的图形用户界面。
->; 有助于定义变量和方法的访问控制。
->; 通过这种包和包引入的机制,也可以保持程序的相对独立性,有利于模块化程序设计的实现。
声明包的定义语句必须放在源文件的顶端。如果你没有显式的声明一个包,那么Java会把你的类放入缺省包(default package)里面,这个包没有名字。
Java使用文件系统目录来存储包,目录名必须和包严格的匹配。另外,这里还要简单谈论一下类路径(CLASSPATH)环境变量的问题。Java编译器使用参考的包层次的根是由类路径决定的。例如,你可以在DOS系统环境下使用如下的命令来指定类路径:
set CLASSPATH=C:\Mysource;C:\Mypackages
如果我们在源文件类的声明前有以下语句:
Package trades.quotes
那么,这个源文件就放在下面这个目录里面:
c:\Mypackages\trades\quotes
最后,需要提醒的是,如果你定义的类已经指定到了某个包中,那么在运行的时候你也需要指定相应的包名和类名一起使用。
同学们可以结合实验二来加深对包概念的理解。
1.2.2 类class
类是面向程序设计中最小的抽象单元,它就像是一个数据和代码的容器。它也是Java里面最重要的一个要件。它通过实例化转变为对象,在实例化的时候自动调用构造函数来初始化对象。
对于类的声明比较复杂,它可以使用很多的修饰符,我们以下面的例子来简单谈论一下这些修饰符。
public abstract class Myclass extends Parent implements A,B {}
首先我们看到的public修饰符,是定义的类的访问控制属性,这个我们在下一章会有详尽的介绍。
然后我们看到的abstract修饰符是表示该类是个抽象类,需要其他类来扩展。这里再介绍一个final修饰符,它与abstract修饰符的意义正好相反,它表示该类不能扩展。所以abstract和final不能在声明类的时候同时使用。
在Java中所有的类都扩展一个超类,如果我们没有指定一个超类,那么系统会把Object类作为它要扩展的超类,Object类就是所有类的超类。这里需要注意的是,在C++中我们使用基类(base class)和继承类(derived class)的称谓,而在Java中我们一般称为超类(super class)和子类(subclass)。
另外,需要提一下的是一个特殊的关键字this,它用来引用正在被调用方法的所属当前对象。
1.2.3 接口interface
接口是一个非功能性、抽象的类,它包含常量和没有功能性的方法声明。也就是说,用interface,你可以指定一个类必须做什么,而不是规定它如何去做。接口在语句的构成上与类十分相似,但是它们缺少实例变量,而且它们定义的方法是不含有方法体的。
接口中声明的变量,一般是final和static型的,意思是它们的值不能通过实现类而改变,也就是说它们被看作是常量,而且它们还必须以常量值初始化。
对于接口来说,它们定义以后都是需要类来实现implement的。也就是说,使用具体的类来实现上面的“如何做”。一个类可以实现多个接口,一个接口也可以被多个类来实现。
另外,接口也可以扩展其他接口来实现继承。如:
interface MyInterface extends YourInterface, Runnable {}
1.2.4 方法method
方法声明的是被调用执行的代码,它可以使用传递的参数并且返回一定类型的返回值。对于方法的修饰符也比较复杂和繁多。我们仍然以一个例子来简单谈论以下其中的一些修饰符。
public static final synchronized double getPrice ( final int itemCode, Color itemColor ) throw Exception { /* code here */}
首先,Public是定义的方法的访问控制属性,这个我们在下一章会有详细介绍。
然后,static、final和synchronized是特殊的修饰符。同样需要注意的是其中final和abstract修饰符不能同时使用的。
最后,我们可以看到该方法getPrice返回一个double数据类型的返回值,而且里面定义了两个参数(itemCode和itemColor)。
另外,我们还注意它通过throw修饰符,表示它会抛出一个Exception的异常,关于异常我们在后面的章节会介绍到。
接下来我们会介绍两种比较特殊的方法。
->; Main方法
Main()方法是Java程序的入口起点,它和其他方法的定义类似,只是它包含一个字符串数组用来传递命令行方式执行程序时所跟的参数。如下的例子它把命令行方式所跟的参数全部显示出来:
CODE:
[Copy to clipboard]
public class MainTest {
public static void main ( String [] args ) {
for ( int i = 0 ; i < args.lenth; i++ ) {
System.out.println(“Argument ” + i + “: ” + args[i] );
}
}
}
执行如下:
c:\java project\Main>; java MainTest Philip K Dick
Argumet 0: Philip
Argumet 1: K
Argumet 2: Dick
这里需要注意的是,如果Main()方法没有使用static修饰符,那么编译不会出错,但是如果你试图执行该程序将会报错,提示main()方法不存在。这是因为你如果这样使用命令行的形式直接执行该程序,MainTest类并没有实例化,所以其main()方法也不会存在,而使用static修饰符则表示该方法是静态的,不需要实例化即可使用。
->; 构造函数Constructor Method
当类被实例化的时候,第一个被调用的方法就是构造函数。构造函数的主要作用就是初始化变量。如果没有定义构造函数,那么Java会使用其超类的默认构造函数。
构造函数与其他方法相比,主要具有以下的特点:
->; 构造函数的名字和其类名相同。
->; 没有返回值。
->; 构造函数不能像其他超类的方法那样被继承。
->; 不能使用final、abstract、synchronized、native或者static修饰符。
有时候,我们在编写构造函数的时候,可能需要首先调用其超类的构造函数,这里我们使用super的关键字,实际上它同this关键字的作用类似,只是它指的是其超类。如下的例子:
CODE:
[Copy to clipboard]
class DataServer extends Server {
public String serverName;
public DataServer ( int startCode ) {
super ( startCode );
serverName = “Customer Service”;
}
}
需要注意的是,调用超类构造函数super()的语句必须放在其构造函数定义的前面,否则编译器会报错。
1.2.5 变量variable
变量是Java程序的一个基本存储单元。变量由一个标识符、类型及一个可选初始值的组合定义。此外,如同定义方法一样,它也有各种的修饰符。比如访问控制属性的修饰符public、private、protected和final、static等。这里需要注意的是,变量不能使用synchronized、abstract和native修饰符。
在定义变量的时候需要指明其类型,除了常用的基本类型(比如int型、Boolean型等)以外,也可以使用对象类型。
1.2.6 引入语句import
使用import引入语句是允许你选择某个包中的某个类或者所有类,使之能在你当前的代码中能方便的使用。例如:
import java.util.Dictionary; //引入java.util包中的Dictionary类
import javax.swing.*; //引入javax.swing包中的所有的类
当然如果你不使用引入语句也可以使用其他包中的其他的类,只要你指定引用对象的全名。比如:
java.util.Date now = new java.util.Date();
另外,java.lang包是会被自动引入到源程序中的。(待续)
__________________________________
~M.Meng's Cabin
Before posting did you try:
[ J2SE Javadocs | J2EE Javadocs | Google ]
返回页首 1楼
AIX性能调优最佳实践及工具下载 | P4 3.0杭州电信月租850,双线1250,托管3900 | HP ALPHA 有奖调查 | ALPHA 专题
猫小
版主-大天使
会员UID:38083
注册时间:2003-1-6 15:57
最后登录:2006-11-8 14:21
帖子总数: 2250
精华帖子: 20
积分:343
来自:火星人在地球
在线状态:...离线...
[会员资料] [站内短信] [Blog]
发表于: 2004-5-19 12:13 [投诉]
1.3 关键字和标识符
Java语言一共使用了48个保留关键字,他们主要可以分为如下几类。
->; 访问控制
private , protected , public
->; 类、方法和变量修饰符
abstract , class , extends , final , implements , interface , native , new , static , strictfp , synchronized , transient , volatile
->; 程序控制语句
break , case , continue , default , do , else , for , if , instanceof , return , switch , while
->; 错误处理
catch , finally , throw , throws , try
->; 包相关
import , package
->; 基本类型
boolean , byte , char , double , float , int , long , short
->; 变量引用
super , this , void
->; 未使用的关键字
const , goto
这些关键字的具体意义可以参考语法书,这里就不再详细阐述了。
另外,除了这48个关键字以外,还有3个语法保留字,即null、true和false。
1.4 变量的初始化
变量从声明的位置来看可以分为两种类型,一是实例变量instance variable,即声明在类一级;另一种是局部变量local variable,它声明在方法一级。这两类变量除了声明的位置不同,它们最主要的区别在于是否需要初始化的问题。下面我们具体来讨论一下这个问题。
->; 实例变量
类的成员就是定义在类一级的变量。它们实际上是可以不需要初始化的,系统一般会自动给它们赋一个默认值。
->; 基本类型
对于8种基本类型来说,Boolean型默认赋值false,char型会默认为Unicode字符集的/u0000,而其余几种类型都默认为0。
->; 对象类型
对于对象类型,系统不会赋予任何默认值,但会表示为null。
->; 数组类型
数组类型于对象类型类似,当你不初始化它的时候,它也是等于null。但如果你初始化它,则有些不同,它与基本类型相似会默认为0。
->; 局部变量
对于局部变量,一般来说它必须初始化。因为无论是基本类型还是对象类型,系统都不会自动赋于任何默认值,所以你必须指定一定的值。当然,如果你定义了局部变量而不去使用它,编译也是可以通过的。
__________________________________
~M.Meng's Cabin
Before posting did you try:
[ J2SE Javadocs | J2EE Javadocs | Google ]
返回页首 2楼
AIX性能调优最佳实践及工具下载 | P4 3.0杭州电信月租850,双线1250,托管3900 | HP ALPHA 有奖调查 | ALPHA 专题
猫小
版主-大天使
会员UID:38083
注册时间:2003-1-6 15:57
最后登录:2006-11-8 14:21
帖子总数: 2250
精华帖子: 20
积分:343
来自:火星人在地球
在线状态:...离线...
[会员资料] [站内短信] [Blog]
发表于: 2004-5-25 08:53 [投诉]
第二次:修饰符
2.1 访问控制
封装将数据和处理数据的代码连接起来。同时,封装也提供了另外一个重要属性:访问控制。通过封装你可以控制程序的某个部分可以访问类的成员,防止对象的滥用,从而保护对象中数据的完整性。对于所有的面向对象的语言,比如C++,访问控制都是一个很重要的方面。由于Java语言使用了包的概念,使它的访问控制相对来说更复杂一些。我们把控制访问控制权限的修饰符主要分为两类,类和它的方法及变量,下面我们分别简单介绍。
-类的访问控制
->; Default:当类不使用任何访问控制修饰符时,即采用的默认的访问控制权限。它允许同一个包内的类访问,而对于它所在包以外的类则不能访问。
->; Public:允许任何包中的任何类访问,对Java里面的所有类开放。
-方法和变量的访问控制
->; Public:所有类均可以访问。
->; Private:只能被它所在的类中的成员访问,使该定义的成员对外在的类不可见。
->; Protected:可以被同一个包的类访问,另外其所有子类也可以访问。
->; Default:当成员不使用任何访问控制修饰符时,即采用默认的访问控制权限。它和Protected类似,唯一的区别在于子类访问权限,它仅允许同一个包的子类访问,而其他包中的子类则不可以访问。
2.2 其他修饰符
除了访问控制修饰符,Java还有其他繁多的修饰符来声明类、方法和变量,下面分别针对所修饰的对象来简单介绍一下主要的修饰符。
-类修饰符
->; final:用来指定该类不能被其他类扩展,从而阻止继承。
->; abstract:表示该类是不允许被实例化的类,也就是说该类需要被扩展继承。被这样声明的类也称为抽象类。
显而易见,final和abstract不能同时使用。
-方法修饰符
->; abstract:被声明的方法称为抽象方法,不含任何代码,需要其继承的子类的相应方法覆盖重载。这里需要注意的是被声明有abstract方法的类必须被声明为abstract。
->; final:声明的方法不允许被覆盖重载。
->; static:声明的方法被成为类方法,不依赖于任何的对象,不需要实例化对象即可直接使用类名来调用该方法。注意的是在该方法体内不可访问实例变量。
->; 变量修饰符
->; static:被声明为static的变量实际可以看作就是全局变量,同样不需要实例化对象即可直接使用类名来引用之。
->; final:被声明的变量的内容不可以被修改,实际可以被看作是一个常量,类似于C或者C++中的const。
2.3 缺省构造函数
我们都知道当对象被实例化的时候,构造函数总是被调用。如果我们在定义类的时候不指定一个构造函数,Java会自行创建一个不带参数的缺省构造函数。而如果我们定义有了一个构造函数,则Java不会再创建缺省构造函数。
更值得注意的是,如果子类的超类不含有不带参数的构造函数,那么子类在使用缺省构造函数就会出错,Java不会为子类创建不带参数的缺省构造函数。因此,我们在使用缺省构造函数的时候要比较小心。我们可以看如下的例子:
CODE:
[Copy to clipboard]
class Fruit {
public Fruit ( String color ) {
System.out.print ( “color = ” + color ) ;
}
}
class Apple extends Fruit {
public static void main ( String [ ] args ) {
Apple m = new Apple () ;
}
}
运行结果出错:
Fruit.java:6: No constructor matching Fruit ( ) found in class Fruit .
Class Apple extends Fruit {
1 error
2.4 合法的返回类型
由于在方法调用的时候,方法返回的类型有可能与实际声明的类型不同,因此我们需要关心什么样的返回类型才是合法的。实际上,系统采用了隐式的类型转换来处理类型的返回。以下几种情况的是合法的:
->; 如果声明的是浮点类型,那么可返回整型类型。
->; 如果声明的是整型类型,那么只要返回的整型类型范围小于或等于声明的类型,返回合法。
->; 如果声明的是对象类型,那么只要返回的是该对象类型,或者是其子类的对象类型,合法。
__________________________________
~M.Meng's Cabin
Before posting did you try:
[ J2SE Javadocs | J2EE Javadocs | Google ]
返回页首 3楼
AIX性能调优最佳实践及工具下载 | P4 3.0杭州电信月租850,双线1250,托管3900 | HP ALPHA 有奖调查 | ALPHA 专题
猫小
版主-大天使
会员UID:38083
注册时间:2003-1-6 15:57
最后登录:2006-11-8 14:21
帖子总数: 2250
精华帖子: 20
积分:343
来自:火星人在地球
在线状态:...离线...
[会员资料] [站内短信] [Blog]
发表于: 2004-7-20 16:58 [投诉]
第三次:JAVA运算符
3 运算符
同大多数的编程语言一样,Java语言也包含了许多的运算符。如果大家学习过C或者C++,会发现下面介绍的各种Java的运算符都与之类似。
3.1.1 赋值运算符 =
这是任何编程语言的最基本的运算符,它用来给变量指定一个值。对于基本类型来说,赋值都便于理解,将新的值赋给变量并保存在变量中供使用。但对于对象类型来说,这里就有一点区别,特别需要提醒大家注意。
对象类型并不是把实际的值(这里是实例)赋给了对象类型的变量,而是赋给的一个参考指针。这样,源对象类型的变量和新的这个变量实际上是指向的同一个实例,如果使用其中一个让实例改变,那么相应的另一个所指向的实例也会改变。这里我们可以借用C里面的指针的概念来方便理解,但实际上Java是不具有指针的概念和定义的。
我们通过下面的例子可以进一步来理解这个概念。
CODE:
[Copy to clipboard]
import java.awt.Dimension;
class ReferenceTest {
Dimension a = new Dimension ( 5,10 );
System.out.println (“a.height = ” + a.height ) ;
Dimension b = a ;
b.height = 30 ;
System.out,println (“a.height = ” + a.height + “after change to b ”);
}
}
运行结果:
c:\java Project\Reference>;java ReferenceTest
a.height = 10
a. height = 30 afer change to b
另外,赋值运算符还可以和其他的运算符,联合组成新的赋值符。如*=、/=、+=、-=等等,这于C或者C++类似。
3.1.2 比较运算符
比较运算符是用来对相同数据类型的变量进行大小或者是否相等、相同的比较,返回的是Boolean类型的值。因此也就大概分为两类。
n >;、>;=、<、<=
这是比较变量的大小关系,与我们学过的任何编程语言相同,就不再介绍了。
n = = 、! =
这是比较变量是否相等或相同。这对于平常的比较基本类型的变量容易理解,只是我们要强调一下对对象类型的比较。与我们前面介绍的赋值运算符类似的是,它也是进行的对其参考指针的比较,而并不是比较两个内容上的差别。我们可以借助下面的例子来理解。
CODE:
[Copy to clipboard]
import java.awt.Button
class CompareRefernce {
public static void main ( String [ ] args ) {
Button a = new Button ( “Exit”);
Button b = new Button ( “Exit”);
Button c = a;
System.out.println ( “Is refernce a = = b ? ” + ( a = = b) ) ;
System.out.println ( “Is refernce a = = c ? ” + ( a = = c) ) ;
}
}
运行结果:
Is refernce a = = b ? false
Is refernce a = = c ? true
3.1.3 instanceof运算符
这个是Java语言特殊的一个运算符,它是用来测试其对象是否属于某类或其超类。但是这里需要提醒大家的是,如果你使用instanceof来比较不是一个继承关系树上的类,Java能够编译通过,但运行的时候会报错。另外,你可以对null对象使用这个运算符,只是无论对于什么类测试的结果都是false。
3.1.4 算术运算符
加+、减-、乘*、除/和取模%运算,这与其他的编程语言类似,不再详述。
3.1.5 自增++、自减--运算符
Java的自增和自减运算符,与C语言类似,同样需要注意的是其放置的位置不同,可能的结果也不同。如果放置在变量的前面,表示先自增(减)再参与下步运算,而如果放置在后面则表示先参与运算再自增(减)。如下的例子说明了自增运算符的使用:
CODE:
[Copy to clipboard]
class IncDec{
public static void main ( String [ ] args ) {
int a = 1;
int b = 1;
int c;
int d;
c = ++b;
d = a++;
c++;
System.out.println ( “a = ” + a );
System.out.println ( “b = ” + b );
System.out.println ( “c = ” + c );
System.out.println ( “d = ” + d );
}
}
运行结果:
a = 2
b = 2
c = 3
d = 1
3.1.6 字符串连接运算符 +
Java语言与C语言类似,也使用+作为连接字符串的运算符,这实际是对String类重载了+运算符。
3.1.7 位运算符
包括位移运算和位逻辑运算,这也与C语言相似。
->; 位移运算
>;>;右移、<<左移、>;>;>;无符号右移。
->; 位逻辑运算
&与、|或、^异或、~非运算,这于其他的编程语言类似,不再详述。
3.1.8 逻辑运算符
与&&、或||,这于其他的编程语言类似,不再详述。只是需要提醒大家不要把它们和位逻辑运算符混淆,这也是初学者最容易犯的错误。
3.1.9 条件运算符 ?:
这与C语言完全相同,具体不再解释。
3.1.10 类型转换
我们在编写程序的时候经常需要对变量的类型进行转换,Java语言与其他的编程语言类似,也提供两种类型转换方式,即显式转换和隐式转换。转换的对象可分为两类,一是基本类型,二是对象类型。
这里需要掌握这样一个要点。对于基本类型来说,凡是大转小(以类型的宽度考虑)需要使用显式转换,也就是需要在变量前面强制给出需要转换成的类型。而对小转大来说,系统会自行进行隐式转换。
对于对象类型来说,也与之类似。凡是超类转子类则需要使用显式强制转换,而子类转超类系统可自行进行隐式转换。另外还需要注意的一点是,对于不在一个继承关系树上的类要进行强制转换,Java编译可通过,但实际运行会出错。
3.2 equals()方法
equals()方法实际与= =运算符作用相同,也是用来比较相同类型的两个变量是否相同或相等。只是有点区别的是,对于String类来说,它重载equals()方法,使用它不是比较两个参考指针的区别,而是实际对所指向的具体内容进行比较,这也满足了平时我们对比较字符串的实际需求。当然,对其他类来说,你也可以重载equals()方法,使其满足你的实际需要,来比较两个对象类型的变量。
3.3 优先级
与其他编程语言类似的,Java语言的运算符同样涉及到优先级别的问题,书上130页从高到低给出了所有运算符的优先级。建议大家,如果对某些运算符之间的优先级不是很清楚的时候,可以使用()来改变它们的优先级关系。
3.4 方法的参数传递
最后,简单讨论一下方法的传递的问题。Java语言的参数传递类型主要可以分为两种,值传递和引用传递。借助C语言的概念,我们知道,第一种就是把参数值直接复制成方法体的参数,这样对方法体中的参数的改变不会影响到调用它的参数。而对于第二种,参数的引用(或者说是个指针)被传递给了方法体的参数,该引用用来访问调用中指定的实际参数。这样,对方法体参数的改变将会影响到调用方法体的参数。
由于没有指针的概念,Java的参数传递相对比较简单。对于一般的基本类型来说,都采用的是值传递;而对于对象类型则是使用的引用传递。
__________________________________
~M.Meng's Cabin
Before posting did you try:
[ J2SE Javadocs | J2EE Javadocs | Google ]
返回页首 4楼
AIX性能调优最佳实践及工具下载 | P4 3.0杭州电信月租850,双线1250,托管3900 | HP ALPHA 有奖调查 | ALPHA 专题
猫小
版主-大天使
会员UID:38083
注册时间:2003-1-6 15:57
最后登录:2006-11-8 14:21
帖子总数: 2250
精华帖子: 20
积分:343
来自:火星人在地球
在线状态:...离线...
[会员资料] [站内短信] [Blog]
发表于: 2006-4-20 10:20 主题: [投诉]
第四次:JAVA程序控制语句
4.程序控制语句
我们在使用任何编程语言都使用过程序控制语句,Java语言的程序控制语句基本与C语言完全相同,它主要分为以下几种:选择、循环和跳转。
4.1 选择语句
4.1.1 if语句
if语句是最基本的条件判断语句,用于根据条件来控制程序的执行路径。其完整格式如下:
if (condition) statement1;
else statement2;
if和else的对象statement可以是单个语句,也可以是个程序块。else子句是可选的。
另外if-else可以嵌套。所以这里需要提醒大家注意的是,如果你没有使用{}来保证每个程序块是具体所属那个if或者else子句,那么一定要注意if和else的匹配关系。一个else子句总是对应着它的同一个块中的最近的if语句,而且该语句没有于其他的else语句相关联。
4.1.2 switch语句
switch语句是Java的多路分支语句。它提供了一种基于一个表达式的值来使程序执行不同部分的简单方法。它比使用一系列if-else-if语句效率显得更高,也更方便。其通用格式如下:
switch ( expression ) {
case value1:
// statement sequence
break;
case value2:
// statement sequence
break;
.
.
.
case valueN:
// statement sequence
break;
default:
// default statement sequence
}
表达式expression必须为byte、short、int或char类型。每个case子句后的值必须是于表达式类型兼容的特定的一个常量(注意必须是常量,而不是变量)。Default子句是可选的。重复的default子句或者重复的case值是不允许的。
4.2 循环语句
4.2.1 while循环
while语句是Java最基本的循环语句。当它的控制表达式为真的时候,while语句重复执行单个语句或语句块。通用格式如下:
while ( condition ) {
// body of loop
}
注意的是,如果循环体只有单个语句,可以不使用{}。后面的do-while、for也类似。
同学们可以结合实验一来加深对while循环的理解。
4.2.2 do-while循环
与while循环类似,只是循环控制表达式是在循环体的尾部进行测试。这意味着即使表达式为假,循环体也至少要被执行一次。通用格式如下:
do {
// body of loop
} while ( condition ) ;
4.2.3 for循环
for循环是一个功能强大而且形式灵活的结构。通用格式如下:
for ( initialization ; condition ; iteration ) {
// body
}
它与C语言完全相同,按它分为的三段,for循环执行的过程也可以看作是三步。首先循环启动时先执行初始化initialization部分,通常这里是设置循环控制变量值的一个表达式,作为控制循环的计数器。然后,计算条件condition的值,来判断是否满足该表达式,执行循环体。最后执行循环体的反复部分iteration,一般来说这里通常是增加或者减少循环控制变量的一个表达式。
需要注意的是,对于initialization和iteration两个部分而言,可以使用多个变量表达式,中间用逗号隔开。另外,for循环也可以不含任何的部分,这样做就是一个死循环。总之,对于for循环语句的使用相当的灵活,大家可以在具体的应用中感受到。
4.3 跳转语句
4.3.1 break语句
break语句有三种作用。首先,可以在switch语句中使用,用来终止一个子句序列,跳出switch语句。另外,它能用来退出一个循环,这种使用与C语言也完全一致。第三种,它能作为一种“先进”的goto语句来使用。这是Java的一个特点,我们特别讨论一下。
我们知道,对于一个比较优秀的语言来讲,都限定使用goto语句。Java语言虽然把goto作为了保留关键字,但并不使用这个语句。它提供了break语句来扩展的实现它的一些功能。通过使用带有标签的break语句,来指定执行从何处重新开始。标签break语句的通用格式如下:
break label ;
这里,标签label是标识代码块的标签。当这种形式的break执行时,控制被传递处指定的代码块。但需要注意的是,如果一个标签不在包围break的块中定义,你就不能break到该标签。一般来说,在实际应用中,带标签的break语句可以用来跳转到多重循环体之外。
4.3.2 continue语句
用法与C语言类似,用来强迫一个循环提早反复。也就是说,你可以使用continue来继续运行循环,但忽略这次重复剩余的循环体语句。
__________________________________
~M.Meng's Cabin
Before posting did you try:
[ J2SE Javadocs | J2EE Javadocs | Google ]
返回页首 5楼
AIX性能调优最佳实践及工具下载 | P4 3.0杭州电信月租850,双线1250,托管3900 | HP ALPHA 有奖调查 | ALPHA 专题
猫小
版主-大天使
会员UID:38083
注册时间:2003-1-6 15:57
最后登录:2006-11-8 14:21
帖子总数: 2250
精华帖子: 20
积分:343
来自:火星人在地球
在线状态:...离线...
[会员资料] [站内短信] [Blog]
发表于: 2006-4-20 10:32 主题: [投诉]
第五次:异常处理
同大多数的编程语言一样,Java语言也包含了许多的运算符。如果大家学习过C或者C++,会发现下面介绍的各种Java的运算符都与之类似。
5.1 try-catch-finally
首先我们介绍一下异常的一些基本概念。Java异常是一个描述在代码段中发生的异常(也就是出错)情况的对象。当异常情况发生,一个代表该异常的对象被创建并在导致该错误的方法中被引发throw。另外,你需要定义一段代码来处理这一异常,首先它需要捕获catch被引发的异常。
如果我们不提供任何异常处理程序,那么异常会被Java运行时系统的默认处理程序捕获。任何不是被你程序捕获的异常最终都会被该默认处理程序处理。默认处理程序会显示一个描述异常的字符串,打印异常发生处的堆栈轨迹并终止程序。
堆栈轨迹实际显示了导致错误产生的方法调用序列。Java在运行的时候会维护一个堆栈来保存程序运行调用的方法序列。实际上,异常发生以后就是按照这个堆栈的方法序列依次呈递给这些方法,如果这些方法里面有异常处理程序就可以捕获它加以处理,否则会一直呈递到系统默认的处理程序。
我们通过下面的例子能更容易理解这个调用方法堆栈和异常的呈递机制。
CODE:
[Copy to clipboard]
Class Exc {
static void subroutine () {
int d = 0 ;
int a = 10 / d ;
}
public static void main ( String [ ] args ) {
Exc.subroutine ();
}
}
运行结果:
java.lang.ArithmeticException: / by zero
at Exc.subroutine(Exc.java:4)
at Exc.main(Exc.java:7)
尽管由Java运行时系统提供的默认异常处理程序对程序的调试很有用,但通常还是需要自己来处理异常。因为,这样做一可以修正错误,二来可以防止程序的自动终止。
接下来,我们来看一下异常处理块的通用格式:
try {
// block of code to monitor for errors
}
catch ( ExceptionType1 exOb ) {
// exception handler for ExceptionType1
}
catch ( ExceptionType2 exOb ) {
// exception handler for ExceptionType2
}
// …
finally {
// block of code to be executed before try block ends
}
☆ try子句
用来监控可能产生错误的代码
☆ catch子句
用来捕获你指定的异常类型,并进行错误处理。它必须紧跟try块,并且可以定义多个catch块。
☆ finally子句
用来处理善后清理的工作,因为无论异常是否引发,它里面的代码都会执行,甚至你在try块中定义了return子句,它也一样会被执行。这是可选的。
5.2 Exception类
前面我们讨论的所有异常都来自于Exception类。
在Java编程语言中,异常类可以大致分为三种。Java.lang.Throwable类充当所有对象的父类。它有Error和Exception两个基本子类,如下图所示:
Throwable类不能使用,而使用子类异常中的一个来描述任何特殊异常。
☆ Error表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。
☆ RuntimeException表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。比如,如果数组索引扩展不超出数组界限,那么,ArrayIndexOutOfBoundsException异常从不会抛出。比如,这也适用于取消引用一个空值对象变量。因为一个正确设计和实现的程序从不出现这种异常,通常对它不做处理。这会导致一个运行时信息,应确保能采取措施更正问题,而不是将它藏到谁也不注意的地方。
☆ 其它异常表示一种运行时的困难,它通常由环境效果引起,可以进行处理。例子包括文件未找到或无效URL异常(用户打了一个错误的URL),如果用户误打了什么东西,两者都容易出现。这两者都可能因为用户错误而出现,这就鼓励程序员去处理它们。
在捕获异常的时候,一定要注意异常类型的匹配。只有catch块中定义的捕获异常的类型匹配引发的异常的类型或者其超类,才捕获并处理。所以,一定要注意你处理的异常类型的继承关系。另外,对于多个catch块来说,如果你定义的带捕获处理的异常有子类和相应的其超类,一定要注意放置的顺序,捕获超类的catch块一定要放置捕获其子类的catch块后面。
5.3 throws子句
如果一个方法可以导致一个异常但不处理它,它必须指定这种行为以使方法的调用者可以保护它们自己而不发生异常。你可以在方法声明中使用throws子句来实现这一目的。一个throws子句列举了一个方法可能引发的所有异常类型。这对于除了Error或者RuntimeException及它们的子类以外类型的所有异常是必要的。一个方法可以引发的所有其他类型的异常必须在throws子句中声明。
针对上面的情况,这里简单介绍一下异常的检查类型的概念。RuntimeException异常类及其子类就是非检查类型,Error类虽然不属于Exception类也是非检查类型的。对于非检查类型的异常,即使你在方法声明中没有指定它为可能引发的异常类型,它也会自动正确的被引发,系统保证它们的运行而不会出错。而除此以外的其他异常类型都是检查的,也就是如果你要引发它就必须在方法声明中指定,而且还必须使用catch块来捕获处理。
另外,对于throws子句还需要提一下的是其方法的重载问题。对于子类重载超类方法的这种覆盖重载override来说,要注意子类方法所引发的异常不能比超类的多,你引发的异常类型可以是超类引发异常类型的子类或者根本不引发任何异常,反正不可以超出其超类的范围。
5.4 引发和创建自定义异常
5.4.1 throw子句
前面我们都是获取的Java运行时系统引发的异常,然而,我们也可以在程序中用throw语句来引发明确的异常。Throw子句通用格式如下:
throw ThrowableInstance ;
ThrowableInstance一定是Throwable类类型或者其子类的一个对象,所以这里又涉及到了一个实例化异常类的问题。可以使用new来实例化异常类,需要注意的是,异常的有两个构造函数:一个没有参数,一个带有一个字符串参数。使用后者时,参数指定的是一个描述异常的字符串。如下:
throw new NullpointerException (“demo”) ;
5.4.2 创建自定义异常类
尽管Java内置异常可以处理大多数常见错误,你还是可以建立你自己的异常类型来满足你的特殊要求。建立的方法很简单,只要定义一个Exception 的一个子类就可以了。
[ 本帖最后由 猫小 于 2006-4-20 10:35 编辑 ]
__________________________________
~M.Meng's Cabin
Before posting did you try:
[ J2SE Javadocs | J2EE Javadocs | Google ]
返回页首 6楼
AIX性能调优最佳实践及工具下载 | P4 3.0杭州电信月租850,双线1250,托管3900 | HP ALPHA 有奖调查 | ALPHA 专题
猫小
版主-大天使
会员UID:38083
注册时间:2003-1-6 15:57
最后登录:2006-11-8 14:21
帖子总数: 2250
精华帖子: 20
积分:343
来自:火星人在地球
在线状态:...离线...
[会员资料] [站内短信] [Blog]
发表于: 2006-4-20 10:40 主题: [投诉]
第六次 垃圾回收机制
这里讨论Java的垃圾回收机制,也就是Java管理使用内存的机制。当对象在内存中创建以后,如果你的程序使用完毕了该对象,通常都应该回收这个对象所占用的内存空间。在C 和C++语言中,一般都靠程序员在代码中手工编写代码来处理这个过程。而Java语言则通过它的JVM(Java虚拟机)使用一种称作mark-sweep的垃圾回收机制来自动处理这一过程。
6.1 垃圾回收机制
内存空间是计算机最基本、最重要的资源。对内存空间的耗费最主要的就是创建对象。当我们不再需要使用创建的对象时,就需要释放分配给对象的内存,回收资源。Java使用一种称为“堆heap”的结构来管理内存,它使用垃圾回收机制来保证始终有足够的内存分配给创建的对象。这一内存管理机制通常是系统自动进行,不受程序员干预的。这也就减轻了程序员的工作负担。
在回收内存空间中,系统主要使用一种称作标记-清理(mark-sweep)的机制。首先,系统遍历所有对象的引用指针,如果发现有对象没有被引用,那么就把它标记为未使用。然后,系统把所有的凡是标记了未使用状态的对象进行清理回收。即使是对于那种两个未使用的对象互相引用的情况,系统通过的遍历算法也可以发现,所以这一机制是十分有效的。
另外需要注意的是,回收处理是周期性的运行,它由系统自行控制。它可能延迟任意长的时间来运行回收器,也可能在任意时候运行。所以这就可能在运行的时候出现一些问题,比如程序会暂停或者运行速度减慢。
针对这样的回收机制,我们在程序中可以显式的使用一些方法来告诉系统回收我们不需要的对象,提高回收执行的效率。比如,把不再需要使用的对象置为null,或者赋给其他对象类型变量,使其不引用指向原对象。
6.2 手工干预回收
对于回收机制,都是由系统自行完成,通常我们并不需要关心。但有时候也可以使用一些手段来手工干预内存空间的回收。
Java提供一个java.lang包中的Runtime类来做这样的工作。我们使用Runtime类的gc()方法来运行垃圾回收器,强制内存空间回收。需要注意的是使用这个方法只是给系统发出一个要求,而不是命令,所以并不能保证一定完成你指定的工作,达到你满意的效果。 另外还需要注意的是Runtime类没有构造函数,你需要使用其getRuntime()来实例化它。
有时候当撤消一个对象的时候,需要完成一些操作,来回收一些非Java的资源。对处理这样的情况,Java提供了一种被称为收尾(finalization)的机制,你可以采用与C语言类似的方式来手工回收一些资源。
要实现这一机制,只需要在你需要回收的类中定义finalize()方法即可。Java回收该类的一个对象时就会调用这个方法。在finalize()方法中你需要指定一个对象被撤消前必须执行的操作。
finalize()方法的通用格式如下:
protected void finalize ()
{
// finalization code here
}
__________________________________
~M.Meng's Cabin
Before posting did you try:
[ J2SE Javadocs | J2EE Javadocs | Google ]
返回页首 7楼
AIX性能调优最佳实践及工具下载 | P4 3.0杭州电信月租850,双线1250,托管3900 | HP ALPHA 有奖调查 | ALPHA 专题
猫小
版主-大天使
会员UID:38083
注册时间:2003-1-6 15:57
最后登录:2006-11-8 14:21
帖子总数: 2250
精华帖子: 20
积分:343
来自:火星人在地球
在线状态:...离线...
[会员资料] [站内短信] [Blog]
发表于: 2006-4-20 10:48 主题: [投诉]
第七次 重载
重载对于面向对象编程来说是一个难点,相对C++来说,Java这个方面较为简单。这里我们主要讨论方法的两种形式的重载和构造函数的重载。通过本次学习,大家会对Java面向对象编程的思想有更一步的理解。
7.1 方法的重载
通过方法重载可以实现多态,多态性是面向对象编程的一个重要的特点。Java语言中对方法的重载分为两类,我们在下面依次具体介绍。
7.1.1 Overload名称重载
指同一个类中的多个方法可以享用相同的名字,只要它们声明的参数不同。这种不同体现在或者参数的个数不同,或者其参数类型不同。编译器根据参数的个数和类型来决定当前所使用的方法。需要注意的是多个方法必须返回相同的数据类型,它们的不同只在于参数。
我们可以通过如下的例子来了解名称重载。
CODE:
[Copy to clipboard]
public class Mytest {
public void printNumbers ( int a, int b ) {
System.out.println(a);
System.out.println(b);
}
public void printNumbers ( int a, int b, int c ) {
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
7.1.2 Override覆盖重载
在类层次结构中,如果子类中的一个方法与它超类中的方法用相同的方法名、返回类型和参数表,就称子类中的方法覆盖重载超类中的方法。从子类中调用重载方法时,它总是引用子类定义的方法,而超类中定义的方法将被隐藏。也就是说,通过覆盖重载,子类修改了从超类继承下来的行为。
需要注意的是方法覆盖仅载两个方法的名称和类型都相同时才发生。如果仅有名称相同,那两个方法只是覆盖。
另外使用方法覆盖还需要注意要保证以下几点:
☆ 覆盖方法的返回类型必须与它所覆盖的方法相同。
☆ 覆盖方法不能比它所覆盖的方法访问控制范围差。
☆ 覆盖方法不能比它所覆盖的方法抛出更多的异常。
☆ synchronized修饰符对超类和子类的作用是独立的。
我们可以通过如下的例子来了解覆盖重载。
Myparent.java
CODE:
[Copy to clipboard]
public class Myparent {
public void printClassName () {
System.out.println ( “Myparent” );
}
}
Mychild.java
CODE:
[Copy to clipboard]
public class Mychild {
public void printClassName () {
System.out.println ( “Mychild” );
}
}
7.2 构造函数的重载
构造函数和其他的方法一样也可以被重载,由于它的特殊性,所以它的重载也与其他的方法重载有些不同。主要包括以下几个方面。
☆ 使用this调用自身的其余构造函数实现构造函数的名称覆盖。如下的示例:
CODE:
[Copy to clipboard]
public class Myclass {
String title ;
//Calling constructor
public Myclass () {
this ( “Default Title ” );
}
//Called constructor
public Myclass ( String t ) {
title = t ;
}
}
☆ 使用super调用超类的构造函数。需要注意的是,如果不显式的使用super调用超类的构造函数,系统会调用超类的不带参数的缺省构造函数。另外要使用this()或者super()则必须把它放在其构造函数的首行。如下的示例:
CODE:
[Copy to clipboard]
public class MySuper {
//first constructor
public MySuper () {
System.out.println (“Super1”) ;
}
//second constructor
public MySuper ( String title ) {
System.out.println (“Super2”) ;
}
}
public class MySub extends MySuper {
//first constructor
public MySub () {
super(“the title”)
System.out.println (“Sub1”) ;
}
//second constructor
public MySub ( String title ) {
System.out.println (“Sub2”) ;
}
}
7.3 所属Is和拥有Has关系
这里主要是提醒大家明白了一个我们在平时程序设计中经常混淆的概念,即所属Is和拥有Has关系的概念和区别。我们需要注意的是只有对于所属的这种关系,我们才可以使用子类和超类的程序设计思想。
__________________________________
~M.Meng's Cabin
Before posting did you try:
[ J2SE Javadocs | J2EE Javadocs | Google ]
返回页首 8楼
AIX性能调优最佳实践及工具下载 | P4 3.0杭州电信月租850,双线1250,托管3900 | HP ALPHA 有奖调查 | ALPHA 专题
猫小
版主-大天使
会员UID:38083
注册时间:2003-1-6 15:57
最后登录:2006-11-8 14:21
帖子总数: 2250
精华帖子: 20
积分:343
来自:火星人在地球
在线状态:...离线...
[会员资料] [站内短信] [Blog]
发表于: 2006-4-20 10:51 主题: [投诉]
第八次 内部类
内部类就是定义在其他类、方法、甚至表达式中的类,它的特点与一般的类并没有什么大的不同。Java语言有四种类型的内部类,下面分别介绍。
8.1 静态Static内部类
静态内部类是定义在类内部的类,所以也称为嵌套类,并且使用static修饰符来声明。需要注意的是凡是内部类,其名字都不能和封装它的类名字相同。对于静态内部类来说,它只能访问其封装类中的静态成员(包括方法和变量)。
静态内部类示例如下:
CODE:
[Copy to clipboard]
package mypackage;
public class EnclosiongClass {
private static int staticvar = 0;
public int instancevar = 0 ;
public static class StaticInnerClass {
}
}
8.2 成员Member内部类
成员内部类与静态内部类相似,也是定义在类内部这一级,只是它不使用static修饰符来声明。所以它和类中的其他实例变量一样,都可以看作是其封装类中的成员。它可以访问其封装类中的任何成员。需要注意的是,在成员内部类中不能定义有static变量,但是可以使用局部变量。
由于成员内部类是非static的,所以在创建内部类的实例时,必须有一个其封装类的实例引用。可见下面的例子:
CODE:
[Copy to clipboard]
public class EnclosingClass {
private int instVar = 1;
public class MemberClass {
public void innerMethod {
// it is okay to do something with instVar here
}
}
public MemberClass createMember () {
return this.new MemberClass ();
}
}
这里是在封装类内部创建内部类的实例,实际这个this可以不显式使用,系统会自行隐式使用来引用封装类的实例。如果对于在封装类外部来创建内部类的实例,依然必须要有一个封装类的实例引用。其语法有两种,可分别见下面的两个示例:
EnclosingClass ec = new EnclosingClass ();
EnclosingClass.MemberClass mc = ec.new MemberClass();
或者
EnclosingClass.MemberClass mc = new EnclosingClass ().new MemberClass ();
另外还需要提一下的是this和super在内部类中使用所代表的意义,this是指的该内部类的实例引用,而super是指的其封装类的实例引用。所以大家在使用这两个关键字的时候一定要注意它所处的位置。
8.3 局部Local内部类
局部内部类是定义在方法体或更小的语句块中的类。它的使用如同方法体中的局部变量,所以你不可以为它声明访问控制修饰符。同成员内部类相同,也不可以含有static成员。这里需要注意的是,在局部内部类和后面将介绍的匿名内部类中可以引用方法中声明的变量,但这些变量必须是final的。
8.4 匿名Anonymous内部类
在某些时候,你可能需要定义一个类,但在程序中却从来不会定义它的对象,只是用来把这个类的对作为自变量直接传递给方法,这时就使用匿名类。如下的例子:
pickButton.AddActionListener ( new ActionListener ( )
{
//Code to define the class that implements
//the ActionListener interface
}
)
匿名类总是用来扩展一个现有的类,或者实现一个接口。它没有名字,所以也没有构造函数,并且也没有任何修饰符来声明它。
一般来说,匿名类经常用于AWT和Swing中的事件处理。
__________________________________
~M.Meng's Cabin
Before posting did you try:
[ J2SE Javadocs | J2EE Javadocs | Google ]
返回页首 9楼
AIX性能调优最佳实践及工具下载 | P4 3.0杭州电信月租850,双线1250,托管3900 | HP ALPHA 有奖调查 | ALPHA 专题
猫小
版主-大天使
会员UID:38083
注册时间:2003-1-6 15:57
最后登录:2006-11-8 14:21
帖子总数: 2250
精华帖子: 20
积分:343
来自:火星人在地球
在线状态:...离线...
[会员资料] [站内短信] [Blog]
发表于: 2006-4-20 10:56 主题: [投诉]
第九次 线程
Java语言中一个十分重要的特点就是支持多线程编程。多线程程序包含两条或两条以上并发运行的部分。程序中每个这样的部分都叫做一个线程thread,每个线程都有独立的执行路径。因此,多线程是多任务处理的一个特殊形式。
9.1 创建进程
通常,通过实例化一个Thread对象来创建一个线程,Java对此定义了两种方式。下面我们分别介绍一下。
☆ 实现Runnable接口
创建线程的最简单的方法是创建一个实现Runnable接口的类。创建过程大致分以下的几步。
首先,你通过实现Runnable接口的run()方法来创建每一个对象的线程。run()方法能够像主线程那样调用其他方法,引用其他类,声明变量。唯一不同的是,run()在程序中确定另一个并发的线程执行入口。当run()方法返回时,该线程即结束。
其次,你要在你创建的实现Runnable接口的类中,实例化一个Thread类的对象。
最后,实例化了Thread类创建了一个线程后,线程并没有运行。你要运行你创建的这个线程还要调用它的start()方法。实际上,start()执行的就是一个对run()的调用。
☆ 扩展Thread类
创建线程的另一个方法是创建一个新类来扩展继承Thread类,然后再创建这个类的实例。当这个子类继承Thread类时,它必须重载run()方法,这个run()方法就是新线程的入口。另外它同样也必须调用start()方法去启动新线程的执行。
9.2 线程的同步
当两个或两个以上的线程需要共享资源,它们需要某种方法来确定资源在某一时刻仅被一个线程占用。这个过程就成为同步(synchronization)。
与其他语言不同的是,Java提供了一种语言上对同步的支持,极大的简化了管理线程同步的复杂性。有两种方法来同步化代码,两者都包括了synchronized关键字的运用,下面分别说明。
☆ 使用同步方法
在多线程的情况下,你如果有一个或多个方法操纵对象的内部状态,都必须使用synchronized关键字来声明方法为同步方法,防止状态出现竞争。一旦线程进入实例的同步方法,就没有其他线程可以进入相同实例的同步方法。
☆ 使用同步语句
当我们在类的定义中没有用到同步方法的时候,你可能也需要对一些操纵对象内部状态的代码同步化。这时你可以使用synchronized来声明一个同步语句块。
下面给出了synchronized语句的普通格式:
synchronized(object) {
// statement to be synchronized
}
9.3 线程间通信
对于多线程的管理,一般采用一种轮询的方式。轮询通常由重复监测条件的循环实现。一旦条件成立,就采用适当的动作。这种方式相对简单,但却很大程度上浪费了CPU的时间。Java语言则采用了另外一种机制,避免了轮询。它通过wait()、notify()和notifyAll()方法实现一个进程间通信机制。这些方法是在Object类中用final声明定义了的,所以所有的类都包含它们。它们的意义分别如下:
☆ wait():通知被调用的线程放弃管程进入睡眠直到其他线程进入相同管程并调用notify()把它唤醒。
☆ notify():恢复相同对象中第一个调用wait()的线程。
☆ notifyAll():恢复相同对象中所有调用wait()的线程。
这里需要提醒的是,这三个方法仅在synchronized方法中才能被调用。
9.4 死锁
这里,我们介绍一个死锁的概念。这是我们需要避免在多任务处理中出现的情况。
死锁发生在当两个线程对一对有依赖循环时。例如,假设一个线程进入了对象A的管程而另一个线程则进入了对象B的管程。如果A的线程试图调用Y的同步方法,它将被锁定。这时,如果B的线程希望调用A的一些同步方法,线程就会永远等待。系统陷入死锁的情况。因为为到达A,B必须释放自己的锁定以使第一个线程可以完成。
9.5 优先级问题
线程优先级被线程调度用来判断何时每个线程允许运行。理论上,优先级高的线程比优先级低的线程获得更多的CPU时间。但实际上,线程获得CPU时间通常由包括优先级在内的多个因素决定的。
我们可以使用setPriority()方法来设置线程的优先级,该方法是Thread类的成员。它的通常格式如下:
final void setPriority(int level)
level指定了对调用线程的新的优先级的设置。Level的值必须在MIN_PRIORITY 到MAX_PRIORITY之间。通常它们的值是1和10。如果要指定默认的优先级使用NORM_PRIORITY,通常值为5。
另外你还可以使用getPriority()方法来获得当前的优先级设置。
__________________________________
~M.Meng's Cabin
Before posting did you try:
[ J2SE Javadocs | J2EE Javadocs | Google ]
Labels:
Java
在文字的世界裡尋找心靈的共鳴,遠山藍以溫柔的筆觸分享書籍的力量與生活的智慧。無論是細膩的書評、深刻的人生感悟,還是技術與創新的新奇發現,每篇文章都是一次內心的療癒旅程。希望透過閱讀,帶領讀者在忙碌的生活中找到一片寧靜與啟發。讓我們一起,在書香中遇見更好的自己!
DOM文件作業和XML文件互相轉換的java實作
級別: 入門
郭洪鋒
2001 年 8 月 01 日
簡介︰該文簡要描述了 DOM 的概念和內部邏輯結構,提供了 DOM 文件作業和 XML 文件互相轉換的 java 實作過程。
1. DOM簡介
目前,W3C 已於2000年11月13日推出了規範 DOM level 2。文件物件模型(DOM)是 HTML 和 XML 文件的程式化介面規範,它與平台和語言是無關的,因而可以用各種語言在各種平臺上實作。該模型定義了 THML 和 XML 文件在記憶體中的邏輯結構(即為文件),提供了存取、存取 THML 和 XML 文件的方法。利用 DOM 規範,可以實作 DOM 文件和 XML 之間的相互轉換,巡訪、作業相應 DOM 文件的內容。可以說,要自由的操縱 XML 文件,就要用到 DOM 規範。
回到頂端
2. DOM 內部邏輯結構
DOM 文件中的邏輯結構可以用節點樹的形式進行描述。透過對 XML 文件的解析處理,XML 文件中的元素便轉換為 DOM 文件中的節點物件。DOM 的文件節點有 Document、Element、Comment、Type 等等節點類型,其中每一個 DOM 文件必須有一個 Document 節點,並且為節點樹的根節點。它可以有子節點,或者葉子節點如 Text 節點、Comment 節點等。任何的格式良好的 XML 文件中的每一個元素均有 DOM 文件中的一個節點類型與之對應。利用 DOM 介面將 XML 文件轉換成 DOM 文件後,我們就可以自由的處理 XML 文件了。
回到頂端
3. java 中的 DOM 介面
DOM 規範提供的 API 的規範,目前 Sun 公司推出的 jdk1.4 測試版中的 java API 遵循了 DOM level 2 Core 推薦介面的語義說明,提供了相應的 java 語言的實作。
在 org.xml.dom 中,jkd1.4 提供了 Document、DocumentType、Node、NodeList、Element、Text 等介面,這些介面均是存取 DOM 文件所必須的。我們可以利用這些介面建立、巡訪、修改 DOM 文件。
在 javax.xml.parsers 中,jkd1.4 提供的 DoumentBuilder和DocumentBuilderFactory 組合可以對 XML 文件進行解析,轉換成 DOM 文件。
在 javax.xml.transform.dom 和 javax.xml.transform.stream 中,jdk1.4 提供了 DOMSource 類別和 StreamSource 類別,可以用來將更新後的 DOM 文件寫入產生的 XML 文件中。
回到頂端
4. 範例程式
4.1 將 XML 文件轉換成 DOM 文件
這個過程是獲得一個 XML 文件解析器,解析 XML 文件轉換成 DOM 文件的過程。
Jdk1.4中,Document介面描述了對應於整個XML文件的文件樹,提供了對文件資料的存取,是該步驟的目標。Document 介面可以從類別 DocumentBuilder 中取得,該類別含有了從 XML 文件獲得 DOM 文件實體的 API。XML 的解析器可以從類別 DocumentBuilderFactory 中取得。在 jdk1.4 中,XML 文件轉換成 DOM 文件可以有如下程式碼實作︰
//獲得一個 XML 文件的解析器
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//解析 XML 文件產生 DOM 文件的介面類別,以便存取 DOM。
DocumentBuilder builder = factory.newDocumentBuilder();
document = builder.parse( new File(FileName) );
4.2 巡訪 DOM 文件
獲得介面類別 document 實體後,可以對 DOM 的文件樹進行存取。要巡訪 DOM 文件,首先要獲得 Root 元素。然後獲得 Root 元素的子節點清單。這裡透過遞迴的方法實作巡訪的目的。
//獲得 Root 元素
Element element = document.getDocumentElement();
//獲得 Root 元素的子節點清單
nodelist = element.getChildNodes();
//用遞迴方法實作 DOM 文件的巡訪
GetElement(nodelist);
其中 GetElement 方法實作如下︰
public void GetElement(NodeList nodelist){
Node cnode;
int i,len;
String str;
if(nodelist.getLength() == 0){
// 該節點沒有子節點
return;
}
for(i=0;i 1)
System.out.println(" "+str+" "+len);
}
}
}
4.3 修改DOM文件
修改 DOM 文件的 API 在 DOM level 2 Core規範中做了說明,jkd1.4 中的 org.xml.dom 中實作了這些 API。修改 DOM 文件作業主要集中在 Document、Element、Node、Text 等類別中,這裡給出的例子中是在解析出的 DOM 文件中增加一系列物件,對應與在 XML 文件中增加一條記錄。
// 獲得 Root 物件
Element root = document.getDocumentElement();
// 在 DOM 文件中增加一個 Element 節點
Element booktype = document.createElement("COMPUTES");
//將該節點轉換成 root 物件的子節點
root.appendChild(cdrom);
//在 DOM 文件中增加一個 Element 節點
Element booktitle = document.createElement("Title");
//將該節點轉換成 booktype 物件的子節點
booktype.appendChild(booktitle);
//在 DOM 文件中增加一個 Text 節點
Text bookname = document.createTextNode("understand Corba");
//將該節點轉換成 bookname 物件的子節點
booktitle.appendChild(bookname);
4.4 將 DOM 文件轉換成 XML 文件
// 獲得將 DOM 文件轉換為 XML 文件的轉換器,在 jdk1.4 中,有類別 TransformerFactory
// 來實作,類別 Transformer 實作轉換 vPI。
TransformerFactory tfactory = TransformerFactory.newInstance();
Transformer transformer = tfactory.newTransformer();
// 將 DOM 物件轉換為 DOMSource 類別物件,該物件表現為轉換成別的表達形式的訊息容器。
DOMSource source = new DOMSource(document);
// 獲得一個 StreamResult 類別物件,該物件是 DOM 文件轉換成的其它形式的文件的容器,可以是 XML 文件,純文字文件,HTML 文件。這裡為一個 XML 文件。
StreamResult result = new StreamResult(new File(“text.xml”));
// 呼叫 API,將 DOM 文件轉換成 XML 文件。
transformer.transform(source,result);
這裡提供了該範例程式的 完整程式 ,該範例程式已在 windows 2000中jdk1.4環境中測試過。
以上提供了一個例子,讀者可以從中瞭解到對 DOM 作業的思路。因為對 DOM 的作業均遵循了 DOM 規範,所以也適用於其它語言對 DOM 的處理。
回到頂端
參考資料
Java 2 Platform, Standard Edition, V1.4.0 API Specificaion
回到頂端
關於作者
郭洪鋒︰主要從事 UNIX 系統下分散式應用的開發和研究。您可以透過電子郵件 ghf_email@sohu.com 和他連絡。
Labels:
Java
在文字的世界裡尋找心靈的共鳴,遠山藍以溫柔的筆觸分享書籍的力量與生活的智慧。無論是細膩的書評、深刻的人生感悟,還是技術與創新的新奇發現,每篇文章都是一次內心的療癒旅程。希望透過閱讀,帶領讀者在忙碌的生活中找到一片寧靜與啟發。讓我們一起,在書香中遇見更好的自己!
Java字串的比較 [精
很多老問題一直被新手提出來問
我把我之前在Java週報上發表過舊的文章貼上來
希望對初學者觀念的釐清有點幫助
眨在這,下次再問類似的問題
browser就別客氣,殺無赦吧~~~~
-------------------------------
原文刊登在 2002/09/12 Java 週報上
初學Java的人﹐在學習的過程中遇到的第一個不能理解的問題﹐通常就是
字串的比較問題﹐因為他們實在不能理解明明兩個"一樣"的字串﹐為什麼
有時在做相等的比較運算時會得到ture的結果﹐可是有時得到的結果確是
false?這個問題在網路上常常都被提出來討論﹐已經算是一個FAQ到不能
FAQ的問題﹐也許是那些初學者們都沒有買到一本好的教科書﹐書中並沒有
教導他們正確的認識字串﹐要是有認真地讀完筆者寫的書﹐應該就不會被
這個問題所困擾了。好吧!大家都是窮學生買不起五百塊以上的書﹐那筆
者就藉著Java週報的一角﹐詳細地說明字串的比較運算。
嚴格說起來﹐字串在Java中也是一個完整的物件﹐兩個物件要做比較時﹐
首先你得先想清楚你想要比較的東西是什麼。舉個例子來說吧﹐日常生活
中婆婆媽媽們聚在一起總是喜歡比較來比較去的﹐有時後比的是誰家的孩
子功課比較好啦﹐誰的老公賺的錢比較多啦等…。人也是物件的一種﹐當
你把兩個人做比較時﹐你一定是把這兩個人身上的某一個屬性拿出來比較
﹐比如說身高、體重、成績等等﹐所以你要說這兩個人完全相同時﹐一定
是你所拿出來比較的每個屬性的內容都相同。另外一種觀點的比較方式是
﹐一個人有時會有好幾個不同的綽號﹐別的人要經由比較確認之後﹐才知
道原來這幾個綽號指的都是你這個人。
話說回來﹐在Java程式中﹐你要比較兩個物件時﹐首先你要確認要比較的
是兩件物件的內容?還是比較兩個物件變數是否指向同一個物件?如果你
是要比較兩個物件的內容時﹐請使用該物件所提供的"equals"方法。例如:
String str1 = new String("hello");
String str2 = new String("hello");
boolean b1 = str1.equals(str2);
booealn b2 = str2.equals(str1);
上例中用誰的equals方法來使用﹐結果都是一樣的(都是true)﹐不過如果
str1和str2是兩個不同的類別物件﹐意義可就不太一樣了﹐這個問題有空
再說。如果你比較個物件變數指的是不是同一個物件﹐那你就得使用比較
算的"=="運算子。例如:
String str3 = new String("hello");
String str4 = new String("hello");
String str5 = str3;
boolean b3 = str3 == str4;
boolean b4 = str3 == str5;
b3的結果是false﹐b4的結果是true。不知道筆者這樣的解說﹐大家應該
可以清楚的知道什麼時候要使用equals方法﹐又什麼時後要使用"=="運算子。
話再說回來﹐會令初學者感到困惑的是類似下面的例子:
String str6 = "hello";
String str7 = "hello";
boolean b5 = str6.equals(str7);
boolean b6 = str6 == str7;
b5的結果是true﹐b6的結果也是true。為什麼b3的結果會是false﹐而b6
的結果卻是true呢?上面幾個例子中的字串物件﹐不都是"hello"這個字
串嗎?你只說對了一半﹐上面例子中str1到str7所指向的字串物件﹐它
們的"內容"都是"hello"這幾個字母沒錯﹐可是它們指向的可不都是同一
個字串物件!str1到str4這四個物件變數指向的字串物件﹐是經由標準
的物件產生方式(new)所產生出來的﹐就算內容一樣﹐可是還是不同的物
件。就像兩個雙胞胎來說﹐甚至兩個用複製出來的人﹐就算所有的屬性
都一樣﹐兩個人還是不同的個體﹐在地球上佔用不同的空間。==運算子
既然比較的是兩個物件變數是否指向的是同一個物件﹐所以b3的結果會
是false就是這個原因囉。
那b6的結果為什麼會是true呢?這是Java為了效能的考量﹐而使用的一
種技巧。因為字串對於一個應用程式來說﹐使用上是非常的頻繁的﹐如
果每個相同內容的字串物件﹐都佔用不同的記憶空間不是很浪費嗎?而
且JVM也要去處理這些可能只用過一次就不再使用的字串。因此﹐當你用
雙引號來產生一個字串物件時﹐JVM會先到記憶體中一個名叫String Pool
的地方去查詢一下﹐是否裡面已經有這個字串了﹐如果有則直接拿出來
使用;如果沒有就產生一個新的放到裡面去。所以str6和str7指向的字
串物件﹐都是在Stirng Pool中的hello字串物件﹐既然兩個物件變數指
向的是同一個物件﹐那麼用==運算子比較的結果當然就是true了。
被搞的昏頭轉向了嗎?其實就記住筆者最前面所說的﹐當你要比較的是
兩個物件的內容相不相等時﹐請用equals方法﹐如果要比較的是兩個物
件變數指向的是不是同一個物件時、請用==運算子。這樣一來﹐不管是
一般的物件還是字串物件在比較時﹐你就不會感到困惑了。
piggy edited on 2003-09-21 00:17
An Apple a day, keeps M$ away
作者 Re:[新手必看] Java字串的比較 [Re:piggy]
getch
發文: 15
積分: 0
於 2004-05-04 17:06 收藏文章? |
但這種情形
String str1=new String("hello");
String str2="hello";
boolean arg=(str1==str2);
又會得到false
是因為new所產生的物件不是在String pool的關係,所以str2在String pool找不到相同的字串,因此才創造一個新的物件嗎?
作者 Re:[新手必看] Java字串的比較 [Re:piggy]
hellfire
大爆炸!!??
發文: 24
積分: 0
於 2004-05-04 21:17 收藏文章? |
str1做了兩件事
創造"hello"字串放到pool中
再根據它的內容做出一個新的字串
str2則是從pool中取得"hello"
所以才會得到false
作者 Re:[新手必看] Java字串的比較 [Re:hellfire]
hkdennis2k
發文: 1069
積分: 4
於 2004-11-08 12:33 收藏文章? |
hellfire wrote:
str1做了兩件事
創造"hello"字串放到pool中
再根據它的內容做出一個新的字串
str2則是從pool中取得"hello"
不,
在compile 時, java 檔內的所有 hardcode 了的 string
會被找出來, 作為 const, 共用一個 char[]
比較像這個 1
2
3
4 static final String _STRING1=new String("hello".toCharArray());
.......
String str1=new String(_STRING1);
String str2=_STRING1;
作者 Re:[新手必看] Java字串的比較 [Re:hkdennis2k]
Duncan
等待是一生最初的蒼老
版主
發文: 4482
積分: 38
於 2004-11-12 03:05 收藏文章? |
hkdennis2k wrote:
不,
在compile 時, java 檔內的所有 hardcode 了的 string
會被找出來, 作為 const, 共用一個 char[]
比較像這個 1
2
3
4 static final String _STRING1=new String("hello".toCharArray());
.......
String str1=new String(_STRING1);
String str2=_STRING1;
現在是在討論兩個 statement runtime 的行為,你講的是編譯期的事。
主要的差別在於使用 new operator 建構 String object 即使其內容在 VM runtime maintain 的 string pool 裡有相同的,依然會建構新的 String object,直接用 string literal 的寫法則是 VM 到 class const pool 去找對應的字串,如果 string pool 已經有相同內容的就使用 string pool 裡的,否則就依照 const pool 裡 string 內容建構 String object,並放到 string pool 裡。
Labels:
Java
在文字的世界裡尋找心靈的共鳴,遠山藍以溫柔的筆觸分享書籍的力量與生活的智慧。無論是細膩的書評、深刻的人生感悟,還是技術與創新的新奇發現,每篇文章都是一次內心的療癒旅程。希望透過閱讀,帶領讀者在忙碌的生活中找到一片寧靜與啟發。讓我們一起,在書香中遇見更好的自己!
2011年7月1日 星期五
用方法名稱呼叫方法
import java.lang.reflect.*;
public class method2 {
public int add(int a, int b) {
return a + b;
}
public static void main(String args[]) {
try {
Class cls = Class.forName("method2");
Class partypes[] = new Class[2];
partypes[0] = Integer.TYPE;
partypes[1] = Integer.TYPE;
Method meth = cls.getMethod(
"add", partypes);
method2 methobj = new method2();
Object arglist[] = new Object[2];
arglist[0] = new Integer(37);
arglist[1] = new Integer(47);
Object retobj = meth.invoke(methobj, arglist);
Integer retval = (Integer)retobj;
System.out.println(retval.intValue());
}
catch (Throwable e) {
System.err.println(e);
}
} }
Labels:
Java
在文字的世界裡尋找心靈的共鳴,遠山藍以溫柔的筆觸分享書籍的力量與生活的智慧。無論是細膩的書評、深刻的人生感悟,還是技術與創新的新奇發現,每篇文章都是一次內心的療癒旅程。希望透過閱讀,帶領讀者在忙碌的生活中找到一片寧靜與啟發。讓我們一起,在書香中遇見更好的自己!
2011年6月30日 星期四
取得檔名不要延伸檔名
import java.io.File;
public class GetFilenameWithoutExtension{
public static String getFileNameWithoutExtension() {
String fileName="C:\\anu\\My\\Hello.txt";
File file = new File(fileName);
int index = file.getName().lastIndexOf('.');
if (index>0&& index <= file.getName().length() - 2 ) {
System.out.println("Filename without Extension: "+file.getName().substring(0, index));
}
return "";
}
public static void main(String []args){
GetFilenameWithoutExtension example=new GetFilenameWithoutExtension();
example.getFileNameWithoutExtension();
}
}
Labels:
Java
在文字的世界裡尋找心靈的共鳴,遠山藍以溫柔的筆觸分享書籍的力量與生活的智慧。無論是細膩的書評、深刻的人生感悟,還是技術與創新的新奇發現,每篇文章都是一次內心的療癒旅程。希望透過閱讀,帶領讀者在忙碌的生活中找到一片寧靜與啟發。讓我們一起,在書香中遇見更好的自己!
string 轉 enum
Yes, Blah.valueOf("A") will give you Blah.A.
Labels:
Java
在文字的世界裡尋找心靈的共鳴,遠山藍以溫柔的筆觸分享書籍的力量與生活的智慧。無論是細膩的書評、深刻的人生感悟,還是技術與創新的新奇發現,每篇文章都是一次內心的療癒旅程。希望透過閱讀,帶領讀者在忙碌的生活中找到一片寧靜與啟發。讓我們一起,在書香中遇見更好的自己!
取得目前時間
import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
private String getDateTime() {
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Date date = new Date();
return dateFormat.format(date);
}
Labels:
Java
在文字的世界裡尋找心靈的共鳴,遠山藍以溫柔的筆觸分享書籍的力量與生活的智慧。無論是細膩的書評、深刻的人生感悟,還是技術與創新的新奇發現,每篇文章都是一次內心的療癒旅程。希望透過閱讀,帶領讀者在忙碌的生活中找到一片寧靜與啟發。讓我們一起,在書香中遇見更好的自己!
2011年3月29日 星期二
Unable to execute dex: Java heap space
最近要用到第三方的庫android-sdk-windows\platforms\android-8\data\layoutlib.jar,但出現Conversion to Dalvik format failed: Unable to execute dex: null這個問題。且出現對話方塊
下面是解法:
(註: 紅色標記表示有試過但不行,綠色標記表示有試過可行。)
As Android developer, I have met a strange problem when I want use a third party jar, it remained me that:
Dex Loader] Unable to execute dex: null
Conversion to Dalvik format failed: Unable to execute dex: null
out of memory error…An internal error occurred during: “Build Project”.
Java heap space
This is the DEX Error,I find the solutions by Google, one way is to modify the eclipse.ini, you can increase the memory allocated in eclipse.ini to this: -Xms128m -Xmx512m or something higher, but this didn’t work for me. After modify the eclipse.ini, I cannot start the eclipse. By StackOverflow, I find a solution:
eclipse.ini file must have -vm on first line and path on second line. don’t try to put everything into one line!
-vm
C:\Program Files\Java\jdk1.6.0_07\bin\javaw.exe
After add the -vm and the path in my eclipse.ini, I can start my Eclipse and the problem has been resolved.
Another way to solve the ” Conversion to Dalvik format failed: Unable to execute dex: null” problem is using the user library, the concrete steps are as follows:
1. Right-click the project in Eclipse and select “Build Path -> Add Libraries…”.
2. Select User Library from the list and click Next.
3. Click the “User Libraries…” button.
4. Click “New…” in the User Libraries dialog.
5. Give the user library a name and select the System library checkbox and click OK.
6. Highlight the newly added user library in the list and click the “Add JARs…” button and add the desired jar files.
7. Click OK on the User Libraries dialog.
8. Make sure the new user library is checked in the Add Library dialog and …
Posted by CuteAndroid
下面是解法:
(註: 紅色標記表示有試過但不行,綠色標記表示有試過可行。)
As Android developer, I have met a strange problem when I want use a third party jar, it remained me that:
Dex Loader] Unable to execute dex: null
Conversion to Dalvik format failed: Unable to execute dex: null
out of memory error…An internal error occurred during: “Build Project”.
Java heap space
This is the DEX Error,I find the solutions by Google, one way is to modify the eclipse.ini, you can increase the memory allocated in eclipse.ini to this: -Xms128m -Xmx512m or something higher, but this didn’t work for me. After modify the eclipse.ini, I cannot start the eclipse. By StackOverflow, I find a solution:
eclipse.ini file must have -vm on first line and path on second line. don’t try to put everything into one line!
-vm
C:\Program Files\Java\jdk1.6.0_07\bin\javaw.exe
After add the -vm and the path in my eclipse.ini, I can start my Eclipse and the problem has been resolved.
Another way to solve the ” Conversion to Dalvik format failed: Unable to execute dex: null” problem is using the user library, the concrete steps are as follows:
1. Right-click the project in Eclipse and select “Build Path -> Add Libraries…”.
2. Select User Library from the list and click Next.
3. Click the “User Libraries…” button.
4. Click “New…” in the User Libraries dialog.
5. Give the user library a name and select the System library checkbox and click OK.
6. Highlight the newly added user library in the list and click the “Add JARs…” button and add the desired jar files.
7. Click OK on the User Libraries dialog.
8. Make sure the new user library is checked in the Add Library dialog and …
Posted by CuteAndroid
在文字的世界裡尋找心靈的共鳴,遠山藍以溫柔的筆觸分享書籍的力量與生活的智慧。無論是細膩的書評、深刻的人生感悟,還是技術與創新的新奇發現,每篇文章都是一次內心的療癒旅程。希望透過閱讀,帶領讀者在忙碌的生活中找到一片寧靜與啟發。讓我們一起,在書香中遇見更好的自己!
2011年2月15日 星期二
Error - java.lang.VerifyError
是說當“校驗器”檢測到一個類文件雖然格式正確,但包含著一些內部不一致性或安全性問題時,拋出該錯誤。從你列出的信息這能看出這些信息具體的你依據提示信息檢查一下你的程序
補充:很可能從1,4 版本以後jdk加強了安全機製或者增進了改善這是jdk的版本問題 沒有什麼好鬱悶的
肯定是jdk版本不一致比如你用1.5的編譯出來,然後去1.4運行就可能出現這樣的問題。1.4編譯出來的,到1.5上運行就不會有這樣的問題
補充:很可能從1,4 版本以後jdk加強了安全機製或者增進了改善這是jdk的版本問題 沒有什麼好鬱悶的
肯定是jdk版本不一致比如你用1.5的編譯出來,然後去1.4運行就可能出現這樣的問題。1.4編譯出來的,到1.5上運行就不會有這樣的問題
在文字的世界裡尋找心靈的共鳴,遠山藍以溫柔的筆觸分享書籍的力量與生活的智慧。無論是細膩的書評、深刻的人生感悟,還是技術與創新的新奇發現,每篇文章都是一次內心的療癒旅程。希望透過閱讀,帶領讀者在忙碌的生活中找到一片寧靜與啟發。讓我們一起,在書香中遇見更好的自己!
2010年12月30日 星期四
使用LinkedList
程式碼
// Create the list
List list = new LinkedList(); // Doubly-linked list
list = new ArrayList(); // List implemented as growable array
// Append an element to the list
list.add("a");
// Insert an element at the head of the list
list.add(0, "b");
// Get the number of elements in the list
int size = list.size(); // 2
// Retrieving the element at the end of the list
Object element = list.get(list.size()-1); // a
// Retrieving the element at the head of the list
element = list.get(0); // b
// Remove the first occurrence of an element
boolean b = list.remove("b"); // true
b = list.remove("b"); // false
// Remove the element at a particular index
element = list.remove(0); // a
Labels:
Java
在文字的世界裡尋找心靈的共鳴,遠山藍以溫柔的筆觸分享書籍的力量與生活的智慧。無論是細膩的書評、深刻的人生感悟,還是技術與創新的新奇發現,每篇文章都是一次內心的療癒旅程。希望透過閱讀,帶領讀者在忙碌的生活中找到一片寧靜與啟發。讓我們一起,在書香中遇見更好的自己!
訂閱:
文章 (Atom)