Q1.2什么是JRE/J2RE?
答:J2RE是Java2RuntimeEnvironment,即Java运行环境,有时简称JRE。
如果你只需要运行Java程序或Applet,下载并安装它即可。
如果你要自行开发Java软件,请下载JDK。在JDK中附带有JRE。
注意由于Microsoft对Java的支持不完全,请不要使用IE自带的虚拟机来运行Applet,务
必
安装一个JRE或JDK。
Q1.3学习Java用什么工具比较好?
答:作者建议首先使用JDK+文本编辑器,这有助你理解下列几个基础概念:path,
classp
ath,package
并熟悉基本命令:javac和java。并且下载和你的JDK版本一致的API帮助。
如果你不确定类或函数的用法,请先查阅API而不是发贴求助。
当你熟悉Java之后,你可以考虑开始使用一个IDE。
作者推荐eclipse,下载网址http://www.eclipse.org。因为eclispe是免费的,插件化
的
。
eclispe的主要缺点是缺乏一个可视化的桌面程序开发工具,
幸运的是IBM在2003年11月已经将部分代码捐给eclipse组织,可以预计这个缺点很快就
会
得到弥补。
无论如何,请不要使用Microsoft的VJ++!众所周知Microsoft从来就没有认真支持过
Java
。
最后但并非最不重要,要有一本好的参考书,并且英文要过关。
Q1.4学习Java有哪些好的参考书?
答:作者首先推荐ThinkinginJava,中文名《Java编程思想》,有中文版。
目前的最新版本是第三版。
在http://64.78.49.204可以免费下载英文版。
该书第一章介绍了很多面向对象的编程思想,作为新手应当认真阅读。
除此以外,O'relly出版社和Wrox出版社的书也不错。作者本人不喜欢大陆作者的书。
也许你觉得英文太难,但是网上大多数资料都是英文的。另外,你需要经常查阅API,而
那
也是英文的。
Q1.5Java和C++哪个更好?
答:这个问题是一个很不恰当的问题。你应该问:Java和C++哪个更适用于我的项目?
Java的优点和缺点一样明显。
跨平台是Java的主要优点,但代价是运行速度的下降。
VC和Windows平台有良好的集成和足够快的速度,但是也只能局限在Windows平台上。
和C++相比,Java学起来更快,开发人员不会碰到很多容易出错的特性。
但是VB程序员甚至只需要拼装模块就可以了。
Q1.6什么是J2SE/J2EE/J2ME?
答:J2SE就是一般的Java。
J2ME是针对嵌入式设备的,比如支持Java的手机,它有自己的JRE和SDK。
J2EE是一组用于企业级程序开发的规范和类库,它使用J2SE的JRE。
二、命令篇
Q2.1我写了第一个Java程序,应该如何编译/运行?
答:首先请将程序保存为xxx.java文件,注意你可能需要修改文件后缀名。
然后在dos窗口下使用javacxxx.java命令,你会发现该目录下多了一个xxx.class文
件,
再使用javaxxx命令,你的java程序就开始运行了。
Q2.2我照你说的做了,但是出现什么“'javac'不是内部或外部命令,也不是可运行
的
程序或批处理文件。”。
答:你遇到了path问题。操作系统在一定的范围(path)内搜索javac.exe,但是没能找
到。
请编辑你的操作系统环境变量,新增一个JAVA_HOME变量,设为你JDK的安装目录,
再编辑Path变量,加上一项%JAVA_HOME%/bin。
然后保存并新开一个dos窗口,你就可以使用javac和java命令了。
Q2.3环境变量怎么设置?
答:请向身边会设的人咨询。
Q2.4我在javacxxx.java的时候显示什么“unreportedexception
java.io.IOExcepti
on;”。
答:参见Q4.8以了解java中的异常机制。
Q2.5javacxxx.java顺利通过了,但是javaxxx的时候显示什么
“NoClassDefFoundErr
or”。
答:1.你遇到了classpath问题。java命令在一定的范围(classpath)内搜索你直接或间
接
使用的class文件,但是未能找到。
首先请确认你没有错敲成javaxxx.class,
其次,检查你的CLASSPATH环境变量,其实你并不需要设置该变量,
但如果你设置了该变量又没有包含.(代表当前目录)的项,
你就会遇到这个问题。请在你的CLASSPATH环境变量中加入一项.或干脆删掉这个变量。
2.如果你使用了并非JDK自带的标准包,比如javax.servlet.*包,也会遇到这个问题,
请
将相应的jar文件加入classpath。
3.如果你在java源文件中定义了package,请参见Q2.11。
Q2.6我在javaxxx的时候显示“Exceptioninthread"main"
java.lang.NoSuchMetho
dError:main”。
答:首先,在你的程序中每个java文件有且只能有一个public类,
这个类的类名必须和文件名的大小写完全一样。
其次,在你要运行的类中有且只能有一个publicstaticvoidmain(String[]args)方
法
,
这个方法就是你的主程序。
Q2.7在javaxxx的时候显示“Exceptioninthread"main"
java.lang.NullPointerEx
ception”。
答:在程序中你试图在值为null的对象变量上调用方法,请检查你的程序确保你的对象
被恰当的初始化。
参见Q4.8以了解java中的异常机制。
Q2.8package是什么意思?怎么用?
答:为了唯一标识每个类并分组,java使用了package的概念。
每个类都有一个全名,例如String的全名是java.lang.String,其中java.lang是包名,
S
tring是短名。按照java命名惯例,包名是全部小写的,而类名的第一个字母是大写的。
这样,如果你自行定义了同样名字的类String,你可以把它放在mypackage中,
通过使用全名mypackage.String和java.lang.String来区分这两个类。
同时,将逻辑上相关的类放在同一个包中,可以使程序结构更为清楚。
为了定义包,你要做的就是在java文件开头加一行“packagemypackage;”。
注意包没有嵌套或包含关系,mypackage包和mypackage.mysubpackage包对JRE来说是并
列的两个包(虽然开发者可
能暗示包含关系)。
Q2.9我没有声明任何package会怎么样?
答:你的类被认为放在默认包中。这时全名和短名是一致的。
Q2.10在一个类中怎么使用其他类?
答:如果你使用java.lang包或者默认包中的类,不用做任何事。
如果你的类位于mypackage包中,并且要调用同一包中的其他类,也不用做任何事。
如果你使用其他包中的类,在package声明之后,类声明之前使用import
otherpackage1.Class
1;或importotherpackage2.*;
这里.*表示引入这个包中的所有类。然后在程序中你可以使用其他类的短名。
如果短名间有重名冲突,必须使用全名来区分。
注意在使用其他包中的类时,你只能使用public的类和接口,参见Q5.4。
Q2.11我用了package的时候显示"NoClassDefFoundError",但是我把所有package去掉
的
时候能正常运行。
答:将你的java文件按包名组织存放。
比如你的工作目录是/work,你的类是package1.Class1,那么将它存放
为/work/package1
/Class1.java。
如果没有声明包,那么直接放在/work下。
在/work下执行javacpackage1/class1.java,再执行javapackage1.class1,你会发现
一
切正常。
另外,如果你的类的个数已经多到了你需要使用包来组织的话,你可以考虑开始使用
IDE。
Q2.12我想把java编译成exe文件,该怎么做?
答:JDK只能将java源文件编译为class文件。
class文件是一种跨平台的字节码,必须依赖平台相关的JRE来运行。Java以此来实现跨
平
台性。
有些开发工具可以将java文件编译为exe文件。作者反对这种做法,因为这样就取消了跨
平
台性。
如果你确信你的软件只在Windows平台上运行,你可以考虑使用C++/C#来编程。
Q2.13我在编译的时候遇到什么"deprecatedAPI",是什么意思?
答:所谓deprecated是指已经过时,但是为了向前兼容起见仍然保留的方法。
这些方法可能会在以后取消支持。你应当改用较新的方法。
在API里面会说明你应当用什么方法来代替之。
三、I/O篇
Q3.1我怎么给java程序加启动参数,就像dir/p/w那样?
答:还记得publicstaticvoidmain(String[]args)吗?这里的args就是你的启动参
数
。
在运行时你输入javapackage1.class1arg1arg2,args中就会有两个String,第一个
是
arg1,第二个是arg2。
Q3.2我怎么从键盘输入一个int/double/字符串?
答:java的I/O操作比C++要复杂一点。如果要从键盘输入,样例代码如下:
BufferedReadercin=newBufferedReader(newInputStreamReader(
System.in));
Strings=cin.readLine();
这样你就获得了一个字符串,如果你需要数字的话再使用:
intn=Integer.parseInt(s);或者doubled=Double.parseDouble(s);
来将字符串"534"转换成int或double。
Q3.3我怎么输出一个int/double/字符串?
答:使用System.out.println(n)或者System.out.println("Hello")等等。
Q3.4我发现有些书上直接用System.in输入,比你要简单得多。
答:java使用unicode,是双字节。而System.in是单字节的stream。
如果你要输入双字节文字比如中文,请使用作者的做法。
Q3.5我怎么从文件输入/输出一个int/double/字符串?
答:类似于从键盘输入,只不过换成
BufferedReaderfin=newBufferedReader(newFileReader("myFileName"));
PrintWriterfout=newPrintWriter(newFileWriter("myFileName"));
另外如果你还没下载API,请开始下载并阅读java.io包中的内容。
Q3.6我想读写文件的指定位置,该怎么办?
答:java.io.RandomAccessFile可以满足你的需要。
Q3.7怎么判断要读的文件已经到了尽头?
答:在Reader的read方法中明确说明返回-1表示流的结尾。
四、关键字篇
Q4.1java里面怎么定义宏?
答:java不支持宏,因为宏代换不能保证类型安全。
如果你需要定义常量,可以将它定义为某个类的staticfinal成员。参见Q4.2和Q4.6。
Q4.2java里面没法用const。
答:你可以用final关键字。例如finalintm=9。被声明为final的变量不能被再次
赋
值。唯一的例外是所谓blankfinal,如下例所示:
publicclassMyClass1{
privatefinalinta=3;
privatefinalintb;//blankfinal
publicMyClass1(){
a=5;//不合法,final变量不能被再次赋值。
b=4;//合法,这是b第一次被赋值。
b=6;//不合法,b不能被再次赋值。
}
}
final也可以用于声明方法或类,被声明为final的方法或类不能被继承。
注意const是java的保留字以备扩充。
Q4.3java里面也不能用goto。
答:甚至在面向过程的语言中你也可以完全不用goto。请检查你的程序流程是否合理。
如果你需要从多层循环中迅速跳出,java增强了(和C++相比)break和continue的功
能,
支持label。
例如:
outer:
while(...)
{
inner:
for(...)
{
...breakinner;...
...continueouter;...
}
}
和const一样,goto也是java的保留字以备扩充。
Q4.4java里面能不能重载操作符?
答:不能。String的+号是唯一一个内置的重载操作符。你可以通过定义接口和方法来实
现
类似功能。
Q4.5我new了一个对象,但是没法delete掉它。
答:java有自动内存回收机制,即所谓GarbargeCollection。你不需要删除对象。你再
也
不用担心指针错误,内存溢出了。
Q4.6我想知道为什么main方法必须被声明为publicstatic?为什么在main方法中不能
调
用非static成员?
答:声明为public是为了这个方法可以被外部调用,详情见Q5.4。
static是为了将某个成员变量/方法关联到类(class)而非实例(instance)。
你不需要创建一个对象就可以直接使用这个类的static成员,因而在static成员中不能
调
用非static成员,因为后者是关联到对象实例(instance)的。
在A类中调用B类的static成员可以使用B.staticMember的写法。
注意一个类的static成员变量是唯一的,被所有该类对象所共享的,在多线程程序设计
中尤其要谨慎小心。
类的static成员是在类第一次被JRE装载的时候初始化的。
你可以使用如下方法来使用非static成员:
publicclassA
{
privatevoidsomeMethod()//非static成员
{}
publicstaticvoidmain(Stringargs)
{
Aa=newA();//创建一个对象实例
a.someMethod();//现在你可以使用非static方法了
}
}
Q4.7throw和throws有什么不同?
答:throws用于方法声明中,声明一个方法会抛出哪些异常。而throw是在方法体中实际
执行抛出异常的
动作。
如果你在方法中throw一个异常,却没有在方法声明中声明之,编译器会报错。
注意Error和RuntimeException的子类是例外,无需特别声明。
Q4.8什么是异常?
答:异常最早在Ada语言中引入,用于在程序中动态处理错误并恢复。
你可以在方法中拦截底层异常并处理之,也可以抛给更高层的模块去处理。
你也可以抛出自己的异常指示发生了某些不正常情况。常见的拦截处理代码如下:
try
{
......//以下是可能发生异常的代码
......//异常被你或低层API抛出,执行流程中断并转向拦截代码。
......
}
catch(Exception1e)//如果Exception1是Exception2的子类并要做特别处理,应排在
前
面
{
//发生Exception1时被该段拦截
}
catch(Exception2e)
{
//发生Exception2时被该段拦截
}
finally//这是可选的
{
//无论异常是否发生,均执行此段代码
//即使在catch段中又向外抛出了新的exception,finally段也会得到执行。
}
Q4.9final和finally有什么不同?
答:final请见Q4.2。finally用于异常机制,参见Q4.8。
五、面向对象篇
Q5.1extends和implements有什么不同?
答:对于class而言,extends用于(单)继承一个类(class),而implements用于实现
一个接口(interf
ace)。
interface的引入是为了部分地提供多继承的功能。
在interface中只需声明方法头,而将方法体留给实现的class来做。
这些实现的class的实例完全可以当作interface的实例来对待。
在interface之间也可以声明为extends(多继承)的关系。
注意一个interface可以extends多个其他interface。
Q5.2java怎么实现多继承?
答:java不支持显式的多继承。
因为在显式多继承的语言例如c++中,会出现子类被迫声明祖先虚基类构造函数的问题,
而这是违反面向对象的封装性原则的。
java提供了interface和implements关键字来部分地实现多继承。参见Q5.1。
Q5.3abstract是什么?
答:被声明为abstract的方法无需给出方法体,留给子类来实现。
而如果一个类中有abstract方法,那么这个类也必须声明为abstract。
被声明为abstract的类无法实例化,尽管它可以定义构造方法供子类使用。
Q5.4public,protected,private有什么不同?
答:这些关键字用于声明类和成员的可见性。
public成员可以被任何类访问,
protected成员限于自己和子类访问,
private成员限于自己访问。
Java还提供了第四种的默认可见性,一般称为packageprivate,当没有任何
public,protected,private修饰符时,成员
是同一包内可见。
类可以用public或默认来修饰。
Q5.5Override和Overload有什么不同?
答:Override是指父类和子类之间方法的继承关系,这些方法有着相同的名称和参数类
型
。
Overload是指同一个类中不同方法(可以在子类也可以在父类中定义)间的关系,
这些方法有着相同的名称和不同的参数类型。
Q5.6我继承了一个方法,但现在我想调用在父类中定义的方法。
答:用super.xxx()可以在子类中调用父类方法。
Q5.7我想在子类的构造方法中调用父类的构造方法,该怎么办?
答:在子类构造方法的第一行调用super(...)即可。
Q5.8我在同一个类中定义了好几个构造方法并且想在一个构造方法中调用另一个。
答:在构造方法第一行调用this(...)。
Q5.9我没有定义构造方法会怎么样?
答:自动获得一个无参数的构造方法。
Q5.10我调用无参数的构造方法失败了。
答:如果你至少定义了一个构造方法,就不再有自动提供的无参数的构造方法了。
你需要另外显式定义一个无参数的构造方法。
另外一种可能是你的构造方法或者类不是public的,参见Q5.4了解java中的可见性。
Q5.11我该怎么定义类似于C++中的析构方法(destructor)?
答:提供一个voidfinalize()方法。在GarbargeCollector回收该对象时会调用该方
法。
注意实际上你很难判断一个对象会在什么时候被回收。作者从未感到需要用到该方法。
Q5.12我想将一个父类对象转换成一个子类对象该怎么做?
答:强制类型转换。如
publicvoidmeth(Aa)
{
Bb=(B)a;
}
如果a实际上并不是B的实例,会抛出ClassCastException。所以请确保a确实是B的实
例。
Q5.13其实我不确定a是不是B的实例,能不能分情况处理?
答:可以使用instanceof操作符。例如
if(ainstanceofB)
{
Bb=(B)a;
}
else
{
...
}
Q5.14我在方法里修改了一个对象的值,但是退出方法后我发现这个对象的值没变!
答:很可能你把传入参数重赋了一个新对象,例如下列代码就会造成这种错误:
publicvoidfun1(Aa)//a是局部参数,指向了一个外在对象。
{
a=newA();//a指向了一个新对象,和外在对象脱钩了。如果你要让a作为
传出变量,
不要写这一句。
a.setAttr(attr);//修改了新对象的值,外在对象没有被修改。
}
基本类型也会出现这种情况。例如:
publicvoidfun2(inta)
{
a=10;//只作用于本方法,外面的变量不会变化。
}
六、java.util篇
Q6.1java能动态分配数组吗?
答:可以。例如intn=3;Language[]myLanguages=newLanguage[n];
Q6.2我怎么知道数组的长度?
答:用length属性。如上例中的myLanguages.length就为3。
Q6.3我还想让数组的长度能自动改变,能够增加/删除元素。
答:用顺序表--java.util.List接口。
你可以选择用ArrayList或是LinkedList,前者是数组实现,后者是链表实现。
例如:Listlist=newArrayList();或是Listlist=newLinkedList();。
Q6.4什么是链表?为什么要有ArrayList和LinkedList两种List?
答:请补习数据结构。
Q6.5我想用队列/栈。
答:用java.util.LinkedList。
Q6.6我希望不要有重复的元素。
答:用集合--java.util.Set接口。例如:Setset=newHashSet()。
Q6.7我想遍历集合/Map。
答:用java.util.Iterator。参见API。
Q6.8我还要能够排序。
答:用java.util.TreeSet。例如:Setset=newTreeSet()。放进去的元素会自动排
序
。
你需要为元素实现Comparable接口,还可能需要提供equals()方法,compareTo()方法,
h
ashCode()方法。
Q6.9但是我想给数组排序。
答:java.util.Arrays类包含了sort等实用方法。
Q6.10我想按不同方式排序。
答:为每种方式定义一个实现了接口Comparator的排序类并和Arrays或TreeSet综合运
用。
Q6.11Map有什么用?
答:存储key-value的关键字-值对,你可以通过关键字来快速存取相应的值。
Q6.12set方法没问题,但是get方法返回的是Object。
答:强制类型转换成你需要的类型。参见Q5.12。
Q6.13ArrayList和Vector有什么不同?HashMap和Hashtable有什么不同?
答:ArrayList和HashMap是多线程不安全的,在多个线程中访问同一个ArrayList对象可
能
会引起冲突并导致错误。
而Vector和Hashtable是多线程安全的,即使在多个线程中同时访问同一个Vector对象也
不
会引起差错。
看起来我们更应该使用Vector和Hashtable,但是实际上Vector和Hashtable的性能太
差,
所以如果你不在多线程中使用的话,还是应该用ArrayList和HashMap。
Q6.14我要获得一个随机数。
答:使用java.util.Random类。
Q6.15我比较两个String总是false,但是它们明明都是"abc"!
答:比较String一定要使用equals或equalsIgnoreCase方法,不要使用==!
==比较的是两个引用(变量)是否指向了同一个对象,而不是比较其内容。
Q6.16我想修改一个String但是在String类中没找到编辑方法。
答:使用StringBuffer类。
Stringstr=".......";//待处理的字符串
StringBufferbuffer=newStringBuffer(str);//使用该字符串初始化一个
StringBuf
fer
buffer.append("...");//调用StringBuffer的相关API来编辑字符串
Stringstr2=buffer.toString();//获得编辑后的字符串。
另外,如果你需要将多个字符串连接起来,请尽量避免使用+号直接连接,而是使用
Strin
gBuffer.append()方法。
Q6.17我想处理日期/时间。
答:使用java.util.Date类。你可以使用java.text.SimpleDateFormat类来在String和
Da
te间互相转换。
SimpleDateFormatformatter=newSimpleDateFormat("yyyy-MM-ddHH:mm:ss");//
规
定日期格式
Datedate=formatter.parse("2003-07-2618:30:35");//将符合格式的String转换
为
Date
Strings=formatter.format(date);//将Date转换为符合格式的String
关于定义日期格式的详细信息请参见API。