注意,此文档有更新的PDF版本:http://wendang.baidu.com/view/b0d3631aff00bed5b9f31d0d.html
目 录
0. 引言
1.Java特性
1.1 语法与词汇
1.2 类型、值、变量、表达式
1.3 Java类与对象
1.4 继承与多态
1.5 命名控制
1.6 抽象类、接口和内部类
1.7 异常处理
1.8 包容器类
1.9 Java I/O
1.10 多线程编程
1.11 Java GUI编程
2.Java与C++的一般性比较
3.对参考文献的评价
4.参考文献
0.引言
我自己写的Java学习笔记,本来是为自己复习用的,后来觉得对他人或许有所用处,所以发表出来。本文适合哪些掌握了C/C++又想掌握Java以及想了解C++与Java的区别的人阅读。有人曾说,若精通C++,学会Java只要几周的时间,但是照我的感受:Java与C++虽然在语法上虽然有很多类似之处,却是形似而神不似的两种不同的语言,在使用时一定要注意两者的区别,否则可能造成互相干扰。本文先从介绍Java的各特性,着重于它们与C++类似特性的区别,然后对Java与C++进行一般性的比较,最后是对参考文献的评价。
1.Java特性
1.1语法和词汇
Java的语言是在C++基础上发展起来的一种面向对象的高级语言,它的语法基本是继承了C++的语言,但有以下几点值得注意:
- C语言是函数的集合。C++兼容C, 既有函数,也有类。C++可以实现面向对象编程,但是仍然离不开一个非对象的main主函数。这也是初学C++者常常会感到费解之处。他们虽然理解了C++的语法,但是不知道怎样实现面向对象编程。事实上,如果将C++当成更好的C使用,其实本来就是面向过程的语言。只有利用面向对象的思想,使用面向对象的特性进行编程时,它才是一种面向对象的语言。Java是比C++更纯的面向对象的语言[1]。Java程序是类的集合,程序的执行进入点是某一个类的public void static main()方法。正因为Java程序是类的集合,Java类定义后并不需要“;”(C++中类后面忘记加“;”有时是个会害死人的Bug!)。
- Java没有goto语句,虽然Java可以用带标号的break, continue语句实现跳转,但是跳转的范围是有限制,它不像C/C++那样可以随意跳转。
- Java语言中boolean变量是一种单独的特殊的变量,它只有两个值:true, false。并且boolean变量不能与整型变量相互转换。逻辑操作符AND(&&), OR(||)和NOT(!)在Java中只能作用在boolean变量上。C的布尔变量是用整型代替的,ANSI C++虽然发展出bool变量,但是可以用整型代替,也可以与整型相互转换。因次if(a==b)错写成if(a=b)的错误将被编译器发觉。
- Java中相对C++增加了无符号右移运算符(>>>, >>>=)。
- Java语言使用的字符集是Unicode字符集,Java用/uxxxx来标识一个Unicode。标识符名没有长度限制,而且可以使用绝大部分Unicode字符。标识符第一个字符是Character.isJavaIdentifierStart方法对之返回true的字符。Java语言提供了一种用Unicode编写的程序向ASCII转换的方式,以便能为基于ASCII的工具处理(/uxxxx变成/uuxxxx,而非ASCII字符转换为/uxxxx)。C++使用ASCII字符集,第一字母必须是字母或下划线,其余字母还可以是数字。 特别的字符包括/b(BS),/t(HT), /n(LF), /f(FF), /r(CR) /”(双引号),/’(单引号),//(反斜杠)。
- String类型是Java语言默认包含包java.lang中定义的类型,可以直接使用。Literal strings何时指向同一对象值得注意:
(1)literal strings在同一包里,无论属于同一类,还是不同类,都指向同一String对象;
(2)在不同包不同类的literal strings也指向同一对象;
(3)在编译期间通过常量计算表达式出的String仿佛是literal string(“hel”+”lo”==”hello”)
(4) 运行时计算出的strings是新建的,所以是不同的(String lo=”lo”, “hel”+lo!=”hello”)。
(5)显式用intern()方法变换的String与预先定义的相同。
(6)使用new String()方法定义的对象与literal string不同(String a=”abcd”, String b=new String(“abcd”), a与b是不同的)。
- Java的String是java.lang中定义的,可以说是内置的,而C++的string是STL中引入的,注意C++是用string而不是String。Java的String不可改变,要改变必须使用StringBuffer。C++中string相当于Java的StringBuffer。
- 位操作运算符(bitwise AND(&), bitwise OR(|), bitwise XOR(^))都可以作用在boolean量上,但是位操作NOT运算符(bitwise NOT(~))不能作用在boolean量。对于boolean量,位操作运算符的产生的结果是与逻辑运算符一样的,但是它们不short circuit表达式(逻辑运算符只要得到结果就不往后计算)。
表1.1 Java与C++的比较1 (语法和词汇)
项 目 | Java | C++ |
字符集 | Unicode | ASCII,其它语言支持是附加的。 |
标识符 | 不定长。第一个字符可使用任何Unicode字符 | 规定字符(第一个字符必须是字母或下划线,变量名中只能使用字母、数字和下划线),C++标准未规定标识符可识别长度(可识别长度随系统而定,但一般不要超过32个字符[2])。 |
关键字 | 特殊:boolean, abstract, extends, final, finally, implements, import, Interface, package, native, strictfp, synchronized, throws, transient | extern, register, sizeof, struct, union, bool, delete, friend, inline, virtual, operator, template, typedef |
操作符 | 同C++,另加>>>, >>>= | =, >, < , !, ~, ?, : , ==, <=, >= !=, &&, ||, ++, --, +, -, * ,/, &, |, ^, %, <<, >>, +=, -=, *=, /=, &=, |=, ^= , %=, <<=, >>= |
逗号表达式 | 只能在for循环中使用 | 任意地方使用 |
布尔变量 | 关键字boolean, 与整型没有任何联系 | 开始是用整型代替的,0为false,非0即为true |
字符串变量 | String (不可变) | string (可变) |
1.2类型、值、变量、表达式
- Java语言是一种强类型的语言,这意味着所有的变量和表达式在编译期间都有一个类型。
- Java的类型有两种:基本类型(primitive types)和引用类型(reference types)。基本类型的存在说明Java语言不是一种纯粹的面向对象的语言(纯粹面向对象的语言一切都是对象,而基本类型不是对象)。以下是Java类型的分类图:
基本类型 <布尔类型(boolean>
<void>
<数值类型(numeric)> <整型(integral)> <byte>
<short>
<int>
<long>
<char>
引用类型 <类(class)>
<接口(interface)>
<数组 (array)>
<空类型(null)>
- 当任何类型在类变量定义时,如果没有初始化,都会赋予默认值,数值类型默认值为0,布尔类型默认值为false. 引用类型默认值为null。Java中所有数值类型的长度在任何机器、任何平台上都是固定的,因此也没有C/C++的那种sizeof运算符。本条也是Java语言只所以具有平台无关性的重要原因。另有一点非常值得注意的是:对于局域基本类型,Java不允许其不做初始化就使用。而且不允许重定义一局域变量,以下代码在Java中是错的:
{
int x=12;
{
int q=96;
int x=10;
}
}
- 在Java中,boolean类型除了可以转换为String(true, false)外,不允许转换为整型或浮点类型。
- 数值类型的运算不会产生上溢或下溢异常,这一点值得注意。double d=100./0.;d将为Infinity。Java中没有C++那样的signed和unsigned整型。
- Java中浮点型向整型转换的过程中默认采用的是去尾法。
- 注意Java语言中的final量与C++中的const量是有区别的:一个final变量是只能赋值一次的量,这个final量是可以通过类中的方法来改变的,但是C/C++的const量却不能再改变。类申明中加final保证此类不能再被继承。Java中void f( final int i)类似于C++中void f(const int i), 而void g(final Bob b)却完全不同于C++中的void g(const Bob& b)。
- 在Java中方法也可以被定义为final的,final方法不能在继承时重定义,可能获得更好效率(编译器可以将它变成内联函数)。
- 在Java中所有的引用类型都在堆中创建,不像C++中有在heap和stack中创建两种选择, 在Java中new关键子的使用率远比在C++中使用高。在C++中,RealPoint rp; 已经建立对象rp并进行了初始化(假定RealPoint有默认构造函数)。而在Java中, RealPoint rp;只是申明了一个RealPoint的引用,未进行任何初始化,它的值为null。Java中也不允许用Integer ia(3);方式来定义一个Integer,而只能通过Integer ia=new Integer(3)的方式。
- Java语言中,数组类型是一种普通类型。数组的长度通过访问其公有的length变量得到。注意,语言Point[] ia=new Point[5];只是定义了长度为5的Point数组引用,任何一个都未进行初始化。
- 所有的Java基本类型(primitive types)都有其对应的包装类(Wrapper Classes)。包装类的作用一是为某一类型相关的方法和变量提供一个家,二是将原始类型包装成类以便为某些只能处理对象引用的类使用,比如HashMap等Collection对象。
- 包装类包括Boolean, Character, Void, Number(Byte,Short, Integer, Long, Float, Double)。
- 应注意Float和Double对象的比较与float和double值的比较是不同的。每一包装类定义了一个顺序:-0小于+0,NaN大于所有值(包括+∞),所有的NaN相等。
表1.2 Java与C++的比较2
项 目 | Java | C++ |
类型 | 长度固定,无signed, unsigned整型,无sizeof运算符。整型默认都是signed的。 | 长度随系统、机器不同,有signed,unsigned整型,有sizeof运算符 |
类型初始化 | 类定义中进行默认初始化 | 类中用构造函数初始化 |
对象创建方式 | 堆中创建 | 堆中或栈中创建 |
常量表示 | final,但是与C++之常量有很大区别。 | const |
局域变量重定义 | 不允许 | 允许 |
局域变量未初始化使用 | 不允许 | 允许 |
1.3 Java类与对象
- Java中的变量名与方法名可以重名(但这通常不是好的做法),C++中不能。
- Java是单继承的语言,所有的类都是从Object类继承的,所以可以使用Object类中定义的toString(),clone(), hashCode(), equals()等方法。
- Java中非static变量可以在定义时进行初始化(这代替了C++中初始化表达式的功能),而C++中只能通过初始化表达式或在构造函数中初始化,C++中非static变量初始化会产生编译错误。
- Java类中默认的访问(即既不加private, public或protected关键字)控制是包访问,它与private, public, protected都不同。C++中类的默认访问控制是private。另外Java中的访问控制是分开定义的(即每个字段或方法分开定义),而C++中是集中定义的(即通过public: private:的方式)。
- Java的函数申明中不能使用默认参数。这似乎会造成在有多个构造函数时需要做更多的工作,但是Java中允许在一个构造函数中调用另一个构造函数,实际上也很方便。但是应注意,如果调用其它构造函数,必须在构造函数体的第一个语句使用。
- Java中this代表对象本身,所以用this.x, this.y方式可以访问对象本身的x,y域。C++中this是一个指向自身的指针,必须用this->x, this->y方式访问x,y域。C++用this.x,this.y会产生编译错误。
- Java语言中没有运算符重载的功能。唯一一个重载的运算符是String中定义的”+”运算符。利用这一个运算符,以及Object的toString()方法,可以实现类似C++中重载<<运算符的功能。
- Java中无const支持(final不能实现类似C++中const的功能),所以要防止改变对象,只有靠用户自己控制(可以使用clone()方法对对象进行拷贝,然后使用)。
- Java方法内的参数传递方式,依照文献【3】的说法,“Java语言不是按引用传递对象,而是按值传递对象引用。”很多文献会说是“Java方法内引用类型按引用传递,基本类型按值传递”。尽管一般也可以这样说,但严格地说,这种说法是有问题的!按引用传递意思是,当一个参数传递给一个函数,函数得到一个原始对象的引用,而不是一个值的拷贝。引用是只能一次赋值的,赋值后不能再改变,而Java中并不是如此。比如:
class Body {
public long idNum;
public String name="<unnamed>";
public Body orbits=null;
private static long nextID=0;
{
idNum=nextID++;
}
public Body(String bodyName,Body orbitsAround){
name=bodyName;
orbits=orbitsAround;
}
public String toString(){
String desc=idNum+" ("+name+")";
if(orbits!=null)
desc+=" orbits "+orbits.toString();
return desc;
}
}
class BodyRef {
public static void main(String[] args){
Body sirius=new Body("Sirus",null);
System.out.println("before "+sirius);
commonName(sirius);
System.out.println("after "+sirius);
}
public static void commonName(Body bodyRef) {
bodyRef.name="Dog Star";
bodyRef=new Body("Other",null);
}
}
输出结果为:
before 0 (Sirus)
after 0 (Dog Star)
尽管bodyRef已经改为”Other”,但是这并没有改变sirius对象。
如果在C++中使用以下代码:
class Body {
long nextID;
public:
long idNum;
string name;
Body(string bodyName){
name=bodyName;
}
string toString(){
string desc=" ("+name+")";
return desc;
}
};
void commonName(Body& bodyRef) {
bodyRef.name="Dog Star";
Body newBody("Other");
bodyRef=newBody;
}
int main(void) {
Body sirius("Sirius");
cout<<"before "<<sirius.toString()<<endl;
commonName(sirius);
cout<<"after "<<sirius.toString()<<endl;
}
则运行结果为:
before (Sirius)
after (Other)
而C++中才是真正的按引用传递。
- Java中没有拷贝构造函数,按C++的方式定义拷贝构造函数会产生问题。
- Native方法是用平台相关的代码实现的方法。
- 在Java中,若基类中有两个public同名方法,派生类overide了其中一个,派生类将继续继承另外一个;这与C++中一个同名方法将overide所有基类方法的方式是不同的。
- 应能分别Java中的shadowing(阴影化)、obscuring(屏蔽)和hiding(隐藏)和overriding(:
(1) Shadowing指一些声明将被另一同名的声明屏蔽掉,以致用单独一个名字难以访问,比如构造函数中A(int i){this.i=i;}中类定义中的i就被局域的i 屏蔽(shadow)掉了。本文件中定义的类也可能屏蔽掉import进的类。
(2) Obscuring是指一个名字可以被解释为变量名、类型名或者包名。在这种情况下,编译器将优先将其解释为变量名,然后是类型名,最后才是包名。因此,有可能无法访问一个可见的类型名或包名,这时称声明被obscured. 遵守Java的命名规则[5]可以防止obscuring。
(3) Hiding.如果一个类定义一个static方法,这个定义将hide基类或基接口的所有同名声明。派生类中定义的变量将hide基类中的同名变量。
(4) Overriding指派生类对基类的方法进行重定义,使派生类拥有新的表现。
1.3 Java与C++的比较3
项 目 | Java | C++ |
继承方式 | 单重继承 | 多重继承 |
变量名与方法名是否可重名 | 可以 | 不能 |
缺省参数 | 不能使用 | 可以使用 |
this | 对象本身引用 | 本对象指针 |
运算符重载 | 不可 | 可 |
拷贝构造函数 | 不能用 | 经常使用 |
参数传递方式 | 按值传递(但是引用类型传递的是对象引用的拷贝,所以非常类似传递了一个引用,而基本类型则与C++类似) | 默认按值传递 |
默认访问权限 | 包访问权限 | private |
访问权限定义方式 | 单独定义 | 集中定义 |
1.4 继承和多态
- 对象、代码重用的重要机制是继承。Java的继承方式是单重继承,这与C++的多重继承方式有着根本区别。也正是由于这一区别,使得Java在使用时与C++有着很大不同。
- 所有的Java对象都从Object基类继承的。C++没有这种类似的基类,所以它的多重继承性也是逻辑上必然的。
- Java有Interface对象,它类似一种纯虚类,通过Interface可以实现多重继承。C++没有Interface对象。
- 派生类的构造函数可以调用基类的构造函数,如果不是调用默认构造函数,需要显式调用,并且必须是在第一句调用。
- Java没有析构函数,其资源的回收通过垃圾收集器来实现;C++通过析构函数机制来安全消除对象。
- 在Java中,如果基类中一方法名被重载许多次,在派生类中重定义这一方法名不会隐藏任一基类中的同名方法。这与C++有着不同。
- Java中类继承用extends关键字,而C++中用“ : ”来实现。
- Java中继承与组合的选择原则与C++一样,is-a关系时用继承,has-a关系时用组合。
- Java中类可以被定义为final的,final类不能被继承。
- Alan Kay关于纯粹面向对象语言的5条规则:
(1) Everything is an object.(任何东西都是对象);
(2) A program is a branch of objects telling each other what to do by sending messages.(一个程序是一组相互传递信息的对象的集合);
(3) Each Object has its own memory made up of other objects. (任一对象有它自己的由其它对象组成的内存)。
(4) Every object has a type.(任一对象都有类型)。
(5) All objects of a particular type can receive the same messages.(所有同一类型的对象能接收相同信息)。
- 面向对象语言的三个基本特征是:数据抽象、继承和多态。
- Java中所有方法默认都是使用晚绑定的,除非方法是static或者final或者private(private方法默认是final的)。而C++中函数有晚绑定与早绑定的区别。晚绑定通过虚函数来实现。
- Java中定义有抽象(abstract)方法的类是一个抽象类,这与C++中至少有一个纯虚函数的类是抽象类类似。但是在Java中抽象类必须用abstract关键字显式说明,而C++中并没有抽象类关键字。即C++中抽象类是由是否定义纯虚函数决定的,而Java中是由abstract关键字决定的,Java中可以定义一个类为abstract而不定义任何abstract方法。
- Java中建构对象的顺序如下:
(1) 调用基类的构造函数;
(2) 成员按定义顺序初始化;
(3) 派生类的构造函数被调用。
- Java构造函数中不要轻易调用方法,因为方法在Java中默认是晚绑定的。这可能会造成基类构造函数调用派生类的方法。使用构造函数的原则是在保证对象进入正确状态的前提下做尽量少的事,不要调用任何方法。
表1.4 Java与C++的比较4 (继承)
项 目 | Java | C++ |
继承方式 | 单重继承 | 多重继承 |
Interface类 | 有 | 无 |
析构函数 | 无 | 有 |
方法默认绑定方式 | 晚绑定 | 早绑定 |
重定义方法 | 不隐藏基类同名方法(重载) | 隐藏基类所有同名方法 |
继承关键字 | extends(类), implements(接口) | 无,用“:”实现 |
抽象类 | 用abstract关键字实现 | 通过定义纯虚函数实现 |
1.5 命名控制
- C++的命名控制主要通过引进命名空间(namespace)来实现的,而Java的命名控制则通过包(package)定义。
- Java中field和method和成员类(或接口)可以重名,编译器对两者的区分是通过不同的使用环境。
- Java的一般命名规则:
(1) 类名:大写开头,接连词首字母大写;类名应该是描述性名词或名词短语,不要太长。
(2) 字段名:一般小写开头,级联词首字母大写;final static字段一般全部大写,各单词间用下横杠隔开。
(3) 方法名:动词或动词短语,首字母小写,接连词首字母大写;getV和setV是获取和设置变量V的值;获取某量长度的方法名应被命名为length,象String方法中一样[6];boolean字段V的值通过isV方法来获取;将对象转化为某一特定格式F时应用toF方法来命名。
(4) 临时变量的命名规则:b(byte),c(char),d(double),e(Exception),f(float),i,j,k(integer),l(long),o(Object),s(String),v(某类型的随机值).。
- java.lang包是每一编译单元自动import的,所以不用重新引入。
- 最高层declaration不能有protected,private或static修饰语,而只能无修饰词,或者有public修饰词。
- 包的命名按照逆国际域名的规则来实现。另外,如果域名包含hyphen或者在标识符中不允许的其它字符,将它转化为下横杠,如果是关键字,加underscore, 如果以数字开头,前面加underscore。
表1.5 Java与C++的比较5 (命名控制)
项 目 | Java | C++ |
命名控制主要机制 | 包控制 | 命名空间 |
字段名和变量名重名 | 可 | 不可 |
1.6 抽象类、接口和内部类
- 接口(interface)是抽象类概念的进一步深化。接口可以看成是一个完全抽象的类,是一个没有任何实现的类,是抽象方法的集合。
- 接口除了可以包含方法外,也可以包含field,接口内的field默认是public、final和static的。
- 接口中的方法默认是public的。
- 接口的作用不仅仅是一个抽象类更纯的抽象类。因为接口没有任何实现,所以一个类可以implments不止一个接口,这提供了一种实现多重继承的方式。所以接口存在的原因除了与抽象类存在的原因一致外,还可以帮助将一个对象映射到多个基类。
- 如何选择接口和抽象类:接口即提供抽象类的优点也能提供接口本身独特的优点,所以应该优先选择接口类,只有你想拥有方法定义和成员变量时才应该考虑将其转化为抽象类。
- 因为接口内的变量默认是static和final的,可以用接口定义常数集合。
- 内部类是Java又一个解决多重继承问题的方案。通过内部类可以继承任意多的类,而所有的内部类都可以方便地使用外部类的变量和方法。
- 一个外部类可以拥有多个内部类,每一个内部类可以以不同的方式implements同一接口,继承相同类。内部类可以拥有不同实例,每一个可以拥有自己不同的状态。
- 内部类自动可引用外部类的所有元素(包括其私有元素),这也是内部类的一大优势。
- 内部类可以是无名的,无名类常在定义listener类时使用。
- Java中内部类的特点和优点使得Java中内部类的使用远比C++要频繁得多。
- 内部类如果定义为static,通常称为嵌套类,嵌套类与C++中的嵌套类类似(但Java嵌套类可以访问private元素)。嵌套类可以拥有static数据,而普通内部类不能拥有static数据。
- 局部内部类(Local Inner Classes)是定义在方法、构造函数或者初始化块中的类。局部内部类不是类的成员。
- 接口内也可以定义内部类,这是很好的实现共享的可更改的资源的方式,因为接口中的内是public和static的。比如:
interface SharedData{
class Data{
private int x=0;
public int getX(){return x;}
public void setX(int newX){x=newX; }
}
Data data=new Data();
}
表1.6 Java与C++的比较6 (接口和内部类)
项 目 | Java | C++ |
接口 | 有且很重要 | 没有 |
内部类是否与外部类有联系 | 有 | 不大 |
1.7 异常处理
- Java与C++在异常处理方面的区别是:Java的异常处理是与语言本身紧密结合在一起的,如同Garbage collector一样,你被迫使用异常。而C++的异常的异常处理是后来新加的一个特性。
- Java的异常机制也与其同步模型结合在一起,所以当synchronized语句和synchronized方法突然中止时锁将被释放。
- Java异常的层次结构如下:
Object->Throwable->Exception->Error
->Error
- 异常产生的原因可能有如下三种:
(1) Java虚拟机检测到的同步异常:
(1.1) 表达式运算违反通常语言语义
(1.2) 装载和连接部分程序时出错;
(1.3) 资源超限
(2) throw语句被执行;
(3) 异步异常:
(3.1)Thread类的stop方法被调用
(3.2)虚拟机发生内部错误。
- 异常可以分为同步异常(synchronous exception)和异步异常(Asynchronous exception),同步异常是指throw语句产生的异常。异步异常可能由两种原因产生:1)JVM内部错误; 2)使用Thread.stop(deprecated)方法或Java Virtual Machine Debug Interface (JVMDI)的stopThread方法。
- 同步异常可以分为checked exceptions和unchecked exceptions。unchecked exceptions是指RuntimeException和它的子类,Error以及它的子类。所有其余的异常都是checked的异常。Checked exceptions必须在程序中捕获处理。
- RuntimeException和Error可以被继承,但是一般不要这样做,因为这样做会造成混乱。程序员应该使用Checked Exceptions.
- Java异常是precise的,这是说Java异常产生后,处理完毕不会自动在异常产生语句之后继续运行。
- Java异常产生和处理的方法与C++都有着不同:Java产生时在方法中用throws说明,在内部用throw语句抛出,C++在任意地方抛出;Java的构造函数中也可以抛出异常。
Java的处理方式如下:
try{
}catch(Exception1 e){
}catch(Exception2 e){
}finally{
}
C++中的处理方式为
try{
}catch(Exception1 ){
}catch(Exception2){
}catch(…){
}
C++中无finally语句,但可以用…表示所有异常。
表1.7 Java与C++的比较7(异常处理)
项 目 | Java | C++ |
异常与语言结合程度 | 紧密,离开异常机制,没有Java | 附加属性 |
异常产生方式 | throws, throw | throw |
异常处理方式 | 见上文字说明 | 见上文字说明 |
1.8 包容器类
- Java中对象的集合表示包括使用数组(Array)和集合类(Container class)
- Java中的数组是一种类型,应注意如果只是定义A[] a; a只是一个null对象,没有被赋予任何值。
- Java中数组只能由int(int short byte char)值指标来获取,用long变量来获取数组值将产生编译错误。
- Java中一个字符数组与字符串(String)是不同的。String对象是不可变的(它的内容不能改变),而字符数组可以逐个改变其元素。String对象中的toCharArray()方法可以将一个String转化为一个字符数组。
- Java的包容器类包括Collection类和Map类,Collection类又包括List类和Set类。Map类是键-值对的集合。
- 应注意Java包容器类和数组元素个数获取方式的不同:包容器类通过其size()方法获取,而数组通过其公有length字段获取(String通过其length()方法获取)。
- 有了以上包容器类,一般没有必要使用Java 1.0/1.1的包容器类(Vector,Enumeration, Hashtable,Stack)。Vector可以用List代替,Enumeration用Iterator代替,Hashtable用HashMap代替,Stack用LinkedList代替。
- C++中的包容器类在<set><vector><list><queue><stack><valarray>中定义,它们都是模板类。C++中vector<T>很常见,而Java中一般用List. 他们都通过size()方法来获取集合元素的个数。
- Java中Collection类的使用方法如下:
Collection coll;
Iterator it=coll.iterator();
while(it.hasNext()){
String str=(String)it.next();
…
}
表1.8 Java与C++的比较8(包容器类)
项 目 | Java | C++ |
包容器类的实现 | Collection, Map | <set><vector><list><queue><stack> <valarray>它们都是模板函数 |
最常用的集合类 | List(ArrayList, LinkedList) | vector<T> |
,
1.9 Java I/O
- java.io.File是一个可以用来进行目录操作的类,File既可能是一个文件,也可能是一个目录。
- Java I/O的使用远比C++要复杂得多,因为即使简单的键盘输入操作也不是轻易能实现的(C++的>>操作符可以轻易实现输入操作)。下面列出常见I/O操作的实现方式:
(1) 从标准输入读:
BufferedReader stdin=new BufferedReader(new InputStreamReader(System.in);
Stdlin.readLine();
(2) 文件输出:
BufferedReader in4=new BufferedReader(new StringReader(s2));
//BufferedReader in4=new BufferedReader(new FileReader(filename));
PrintWriter out1=new PrintWriter(new BufferedWriter(new FileWriter(“IODemo.out”)));
(3) 存储和恢复数据:
DataOutputStream out2=new DataOutputStream(new BufferedOutputStream(new FileOutputStream(“Data.txt”)));
out2.writeDouble(3.14159);
out2.wrtieUTF(“That is pi”);
out2.close();
DataInputStream in5=new DataInputStream(new BufferedInputStream(new FileInputStream(“Data.txt”)));
In5.readDouble();
In5.readUTF();
- Readers和Writers类(Java1.1)与InputStream和OutputStream类(Java1.0)的区别是:
(1) Reader和Writer类是基于Unicode和character的I/O,而InputStream和OutputStream是面向byte的I/O。
(2) Reader类的派生类必须实现Reader类的抽象close()方法,而InputStream只需继承空实现。
(3) Reader是用ready()方法来表示是否有数据,InputStream用available()方法告诉你有多少可读的数据。
两者的转换方式是:InputStreamReader可以将InputStream转换为Reader,OutputStreamWriter可以将OutputStream转化为Writer。
- Java 1.0在Java 1.1中没有改变和对应的类包括DataOutputStream,File,RandomAccessFile和SequenceInputStream。
- JDK 1.4在java.nio包和java.nio.channels包中定义了新的I/O库,以增进速度:ByteBuffer,Buffer,FileChannel
- System.currentTimeMillis()方法可以获取系统时间。
- Java中包含以下压缩类,可以实现压缩功能:CheckedInputStream,CheckedOutputStream,DeflaterOutputStream,ZipOutputStream,GZIPOutputStream,InflaterInputStream,ZipInputStream,GZIPInputStream。
- 对象的串行化的方法如下:
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream(“worm.out”));
out.writeObject(“Worm storage/n”);
out.writeObject(w);
out.close();
ObjectInputStream in new ObjectInputStream(new FileInputStream(“worm.out”);
String s=(String)in.readObject();
Worm w2=(Worm)in.readObject();
- JDK 1.4介绍了Preferences API(java.util.prefs.*),可以方便地实现程序环境变量的自动存储(不必在退出时将环境变量存到另外的文件中),很有用。
表1.9 Java与C++的比较8(I/O)
项 目 | Java | C++ |
标准I/O的实现方式 | System.out, System.in 通过重载Object类中的toString()方法实现输出。 | <iostream> istream,ostream 一般通过重载<<, >>实现自定义输入输出 |
1.10 多线程编程
- Java是一种多线程的语言,实现多线程的机制已经内置于语言之中。Java基类Object之wait(),notify(),notifyAll()方法可以实现多线程之间的控制传递。
- 应理解死锁发生的四个条件(同时满足):
(1) 互斥。至少有一个资源被多个线程共享;
(2) 至少有一个进程必须拥有一种资源,并等待另一进程占有的另一资源;
(3) 某一资源不能从一进程中被强夺;
(4) 环形等待发生,一个进程等待另一进程拥有的资源。
- 应注意Java语言本身不能防止或检测死锁。程序员应采用通常防止死锁的机制来防止和控制死锁。
- 一个线程可能处于以下四种状态之一:New,Runnable,Dead,Blocked。
- 实现多线程的方法有二:一是继承Thread类,用start()启动run(),在run()方法定义线程的行为;二是implements Runnable接口,这种方法适用于类已经继承了其它类的场合。
- 守护进程(daemon thread)是指一个在后台提供服务的进程。当所有的非守护进程结束后,整个程序将中止:setDeamon(true)可以用来设置进程为守护进程。
- 应掌握Thread中sleep()和wait()方法的区别:sleep()不释放锁,wait()释放锁;用法:Object.wait(),Thread.sleep()。
- 掌握Thread.jon()的作用:等待一个线程完成。
1.11 Java GUI编程
- 应明白什么是AWT,什么是Swing。AWT是Java1.0的产品(Abstract Window Toolkit)(java.awt.*),Swing是Java 2的Java Foundation Classes(JFC)(javax.swing.*)
- 分别JApplet, JFrame和JApplet。
- Java GUI编程很容易,用很少的代码就可以实现GUI,而且这些代码是可控制的。Java GUI编程不会象某些GUI builder那样产生不可读代码。
- 应掌握Java GUI的layout:BorderLayout,FlowLayout,GridLayout,GridBagLayout,BoxLayout。
- 掌握Swing的Event模型以及Listener的使用,这时候内部类经常是很有用的。
- 应明白什么是Javabeans:Javabean是遵守一定命名规则的特殊的Java类。
2.对Java与C++的一般性比较
Java是一种通用的、基于class的、支持并发处理的、强类型的、相对高级的面向对象的语言。Java是一种从头开始设计的编程语言,它没有C++那样的向前兼容的问题。Java是比C++更纯粹的面向对象的语言,采用C++的语法,Smalltalk的风格。它针对C++的不足对C++有一定改进,几乎可以保证不能运行坏的程序。Java也是一种网络编程语言,几乎不会产生病毒,易于进行浏览器端,服务器端编程。Java可以让更少的人使用更少的时间编写更大、更复杂的程序。但是同时Java也一直存在着速度问题。Java不是一个操作系统,Java也并不比C++简单(Larry O’brien曾说Saying Java is simplier than C++ is like saying KZ is shorter than Everest),也并不完美。应该说Java是比C++更理想主义的一种语言,Java更能满足理想主义者的口味。与Java相关技术也远远比C++要多得多(J2SE,J2ME,J2EE,JSP, Servlet,Javabeans,JMS,Jini…),精通Java编程与精通C++编程其实同样难。
3.对参考文献的评价
文献【1】是Java2规范,涉及了Java语言最基本的方面,值得一读。但它对于Java Windows编程,I/O编程都无甚涉及。文献【2】很实用,涉及了Java编程的多个方面,很适合于有一定Java编程经验,想将自己对Java的理解提高到一个新的层次的读者阅读。文献【2】也可以作为Java编程的很好的教科书。其不足之处是对某些方面的探讨显得不够深入。文献【3】是Java的经典书籍,正如《C++ Programming Language》之于C++的经典。它对Java语言的许多地方有较为深入的讨论或简明的概括(比如Java中参数传递方式的说明)。文献【4】是C++标准,遇到有争议的问题可通过查阅这一文档解决。
4 参考文献
【1】James Gosling, Bill Joy, Guy Steele and Gilad Bracha, The Java Language Specification Second Edition, ISBN 0-201-31008-2
【2】Bruce Eckel, Thinking in Java(Third Edition), 英文影印版.机械工业出版社.2004
【3】Ken Arnold, James Gosling, David Holmes. The Java Programming Language. Third Edition.(影印版). 北京:中国电力出版社. 2003
【4】International Standard: Programming Languages—C++ (ISO/IEC 14882). Second edition. 2003-10-15
[1] 但是Java也不是纯粹的面向对象的语言,后面会对此做出解释。
[2] 有些书上会说“只有前32位是有效的”,这是不对的,C++标准并未对可识别长度做出规定,这一点在C++标准(ISO/IEC14882)中有特别的说明:An identifier is an arbitrarily long sequence of letters and digits. Each universal-character-name in an identifier shall designate a character whose encoding in ISO 10646 falls into one of the ranges specified in Annex E. Upper- and lower-case letters are different. All characters are significant. (注: On systems in which linkers cannot accept extended characters, an encoding of the universal-character-name may be used in forming valid external identifiers. For example, some otherwise unused character or sequence of characters may be used to encode the /u in a universal-character-name. Extended characters may produce a long external identifier, but C + + does not place a translation limit on significant characters for external identifiers. In C + +, upper- and lower-case letters are considered different for all identifiers, including external identifiers. )
[3] true, false, null与关键字很类似,因为它们不能作为标识符,但是它们实际是常数。
[4] const, goto虽然作为Java语言的保留关键字,但是没有使用。它们作为关键字可以有助于编译器更好地报错。
[5] 临时变量:b(byte),c(char), d(double), e(Exception), f(float), i,j,k(Integer), l(long),o(Object),s(String), v(某一类型的任意值)。类名大写开头,方法名小写开头,常量(大写,下横杠分隔)。
[6] 应区别String变量与数组获取长度的不同方式,String通过length()方法获取长度,而数组通过length字段(public)来获取长度。