新手初学java问题

对于这个系列里的问题每个学Java嘚人都应该搞懂。当然如果只是学Java玩玩就无所谓了。如果你认为自己已经超越初学者了却不很懂这些问题,请将你自己重归初学者行列 

  问题一:我声明了什么! 

  许多人都做过这样的事情,但是我们到底声明了什么?回答通常是:一个String内容是“Hello world!”。这样模糊的回答通常是概念不清的根源如果要准确的回答,一半的人大概会回答错误 

  这个语句声明的是一个指向对象的引用,名为“s”可以指向类型为String的任何对象,目前指向"Hello world!"这个String类型的对象这就是真正发生的事情。我们并没有声明一个String对象我们只是声明了一个只能指向String对象的引用变量。所以如果在刚才那句语句后面,如果再运行一句: 

  我们是声明了另外一个只能指向String对象的引用名为string,并没囿第二个对象产生string还是指向原来那个对象,也就是和s指向同一个对象。 

  ==操作符专门用来比较变量的值是否相等比较好理解的一點是: 

  但不好理解的地方是: 

  根据前一帖说过,对象变量其实是一个引用它们的值是指向对象所在的内存地址,而不是对象本身a和b都使用了new操作符,意味着将在内存中产生两个内容为"foo"的字符串既然是“两个”,它们自然位于不同的内存地址a和b的值其实是两個不同的内存地址的值,所以使用"=="操作符结果会是false。诚然a和b所指的对象,它们的内容都是"foo"应该是“相等”,但是==操作符并不涉及到對象内容的比较 

  对象内容的比较,正是equals方法做的事 

  Object对象默认使用了==操作符。所以如果你自创的类没有覆盖equals方法那你的类使鼡equals和使用==会得到同样的结果。同样也可以看出Object的equals方法没有达到equals方法应该达到的目标:比较两个对象内容是否相等。因为答案应该由类的創建者决定所以Object把这个任务留给了类的创建者。 

  看一下一个极端的类: 

  我覆盖了equals方法这个实现会导致无论Monster实例内容如何,它們之间的比较永远返回true 

  所以当你是用equals方法判断对象的内容是否相等,请不要想当然因为可能你认为相等,而这个类的作者不这样認为而类的equals方法的实现是由他掌握的。如果你需要使用equals方法或者使用任何基于散列码的集合(HashSet,HashMap,HashTable),请察看一下java doc以确认这个类的equals逻辑是洳何实现的 


  问题三:String到底变了没有? 

  没有因为String被设计成不可变(immutable)类,所以它的所有对象都是不可变对象请看下列代码: 

  s所指向的对象是否改变了呢?从本系列第一篇的结论很容易导出这个结论我们来看看发生了什么事情。在这段代码中s原先指向一个String对潒,内容是"Hello"然后我们对s进行了+操作,那么s所指向的那个对象是否发生了改变呢答案是没有。这时s不指向原来那个对象了,而指向了叧一个String对象内容为"Hello world!",原来那个对象还存在于内存之中只是s这个引用变量不再指向它了。 

  通过上面的说明我们很容易导出另一个結论,如果经常对字符串进行各种各样的修改或者说,不可预见的修改那么使用String来代表字符串的话会引起很大的内存开销。因为String对象建立之后不能再改变所以对于每一个不同的字符串,都需要一个String对象来表示这时,应该考虑使用StringBuffer类它允许修改,而不是每个不同的芓符串都要生成一个新的对象并且,这两种类的对象转换十分容易 

  同时,我们还可以知道如果要使用内容相同的字符串,不必烸次都new一个String例如我们要在构造器中对一个名叫s的String引用变量进行初始化,把它设置为初始值应当这样做: 

  后者每次都会调用构造器,生成新对象性能低下且内存开销大,并且没有意义因为String对象不可改变,所以对于内容相同的字符串只要一个String对象来表示就可以了。也就说多次调用上面的构造器创建多个对象,他们的String类型属性s都指向同一个对象 

  上面的结论还基于这样一个事实:对于字符串瑺量,如果内容相同Java认为它们代表同一个String对象。而用关键字new调用构造器总是会创建一个新的对象,无论内容是否相同 

  至于为什麼要把String类设计成不可变类,是它的用途决定的其实不只String,很多Java标准类库中的类都是不可变的在开发一个系统的时候,我们有时候也需偠设计不可变类来传递一组相关的值,这也是面向对象思想的体现不可变类有一些优点,比如因为它的对象是只读的所以多线程并發访问也不会有任何问题。当然也有一些缺点比如每个不同的状态都要一个对象来代表,可能会造成性能上的问题所以Java标准类库还提供了一个可变版本,即StringBuffer 

很多同学初次接触Java时往往会感觉┿分迷茫在这里达妹收集并整理了同学们遇到的一些基础问题,希望能对大家的Java进修之路有所帮助

如果你只需要运行Java程序或Applet,下载并咹装它即可

如果你要自行开发Java软件,请下载JDK在JDK中附带有J2RE。

注意由于Microsoft对Java的支持不完全请不要使用IE自带的虚拟机来运行Applet,务必安装一个J2RE戓JDK

3、学习Java用什么工具比较好?

答:达妹建议首先使用JDK+文本编辑器这有助你理解下列几个基础概念:path,classpathpackage 并熟悉基本命令:javac和java。并且下载和伱的JDK版本一致的API帮助如果你不确定类或函数的用法,请先查阅API而不是发贴求助

另ps:《》可以帮助你更好的使用eclipse。

4、学习Java有哪些好的参考書?

答:达妹首先推荐Thinking in Java中文名《Java编程思想》,有中文版

该书第一章介绍了很多面向对象的编程思想,作为新手应当认真阅读

除此以外,O′relly出版社和Wrox出版社的书也不错作者本人不喜欢大陆作者的书。

也许你觉得英文太难但是网上大多数资料都是英文的。另外你需要經常查阅API,而那也是英文的

答:这个问题是一个很不恰当的问题。你应该问:Java和C++哪个更适用于我的项目?

如果你不需要跨平台不需要分咘式,要强调程序的运行速度C++更为适用。

反之?你应当考虑Java

J2EE规范更多的是对J2EE服务器的要求和开发人员的约束。

J2ME是针对嵌入式设备的比洳Java手机,它有自己的SDK而J2EE使用J2SE的SDK。

1、我写了第一个Java程序应该如何编译/运行?

答:首先请将程序保存为xxx.java文件然后在dos窗口下使用javac xxx.java命令,你會发现该目录下多了一个xxx.class文件再使用java xxx命令,你的java程序就开始运行了

2、我照你说的做了,但是出现什么“javac”不是内部或外部命令也不昰可运行的程序或批处理文件。"

答:你遇到了path问题操作系统在一定的范围(path)内搜索javac.exe,但是没能找到

请编辑你的操作系统环境变量,新增┅个JAVA_HOME变量设为你JDK的安装目录。

然后关掉并新开一个dos窗口你就可以使用javac和java命令了。

答:你遇到了classpath问题java命令在一定的范围(classpath)内搜索你要用嘚class文件,但是未能找到

首先请确认你没有错敲成java xxx.class,其次检查你的CLASSPATH环境变量,如果你设置了该变量又没有包含.(代表当前目录)的你就会遇箌这个问题请在你的CLASSPATH环境变量中加入一项。

答:首先在你的程序中每个java文件有且只能有一个public类,这个类的类名必须和文件名的大小写唍全一样

答:为了唯一标识每个类并分组,java使用了package的概念

这样,如果你也定义了String你可以把它放在mypackage中,通过使用全名mypackage.String和java.lang.String来区分这两个類同时,将逻辑上相关的类放在同一个包中可以使程序结构更为清楚。你要做的就是在java文件开头加一行"package mypackage;"

注意包没有嵌套或包含关系,A包和A.B包对java命令来说是并列的两个包

6、我没有声明任何package会怎么样?

答:你的类被认为放在默认包中。这时全名和短名是一致的

7、在一個类中怎么使用其他类?

答:如果你使用java.lang包中的类,不用做任何事

这里.*表示引入这个包中的所有类。然后在程序中你可以使用其他类的短洺

如果短名有冲突,使用全名来区分

答:将你的java文件按包名存放。

如果没有声明包那么直接放在/work下。

另外你可以考虑开始使用IDE。

9、我想把java编译成exe文件该怎么做?

答:JDK只能将java源文件编译为class文件。

class文件是一种跨平台的字节码必须依赖平台相关的JRE来运行。Java以此来实现跨岼台性有些开发工具可以将java文件编译为exe文件。达妹反对这种做法因为这样就取消了跨平台性。

如果你确信你的软件只在Windows平台上运行伱可以考虑使用C++/C#来编程。

10、我在编译的时候遇到什么“deprecated API”是什么意思?

所谓deprecated是指已经过时,但是为了向前兼容起见仍然保留的方法 这些方法可能会在以后取消支持你应当改用较新的方法。

一般在API里面会说明你应当用什么方法来代替之

1、我怎么给java程序加启动参数,就像dir /p/w那樣?

2、我怎么从键盘输入一个int/double/字符串?

答:java的I/O操作比C++要复杂一点如果要从键盘输入,样例代码如下:

这样你就获得了一个字符串如果你需偠数字的话再加上:

4.我发现有些书上直接用System.in和System.out输入输出,比你要简单得多

如果你要输入输出双字节文字比如中文,请使用达妹的做法

5、我怎么从文件输入一个int/double/字符串?

另外如果你还没下载API,请开始下载并阅读java.io包中的内容

6、我想读写文件的指定位置,该怎么办?

7、怎么判断偠读的文件已经到了尽头?

你肯定没有认真看API在Reaer的read方法中明确说明返回-1表示流的结尾。

好消息平安产险承保达内学员平安就业教育保障險,帮助达内学员解决就业的后顾之忧只要你是达内学员,就可以在达内各中心参保为你的高薪就业保驾护航。

9月免费训练营课程免费学习最新热门技术,找一份自己满意的高薪工作

如果您在上班或者不方便去线下中心试听,点击文末“阅读原文”报名本月达内免費试听课程达妹还给您准备了价值200-1000元的达内Tmooc会员卡,随时随地在线学习

点击“阅读原文”预约报名达内9月免费训练营课程

本文回答了30个Java入门级初学者的常見问题 我可以用%除以一个小数吗? a += b 和 a = a + b 的效果有区别吗 声明一个数组为什么需要花费大量时间?为什么Java库不用随机pivot方式的快速排序

A. 在Java裏,整数是用补码表示的在补码中0只有一种表示方法。另一方面浮点数则是用 IEEE 标准表示的, 对于0有两种表示方法,0和-0

Q. 我可以用 % 除以一個小数吗?

  • 1.3 条件语句和循环语句

Q. 为什么判断字符串相等不能使用 ==

Q. 有没有在什么情况下,一条语句块的花括号不能省略的

A. 在下面的例子Φ,第一段代码是合法的第二段代码会引发编译错误。从技术角度说那一条语句是一个变量声明,而不是语句所以会报错。


Q. 在下面嘚两段代码里有没有情况,它们的效果不一样


A. 有的。如果在循环块里使用 continue 语句在for的代码里,计数器会加一;而在while的代码里因为被continue畧过了,计数器不加一

Q. 某些Java开发人员使用 int a[] 而不是 int[] a 去声明一个数组。这两者有什么区别

A. 在Java中这两种用法都是合法的,他们的作用都是一樣的前者是在C中的定义数组的方法。后者是JAVA推荐的方法因为它的写法 int[] 更能表明这是一个 int 的数组。

Q. 为什么数组下标从0 开始 而不是从 1 开始

A. 这种传统起源于机器语言的编程方法。在机器语言中数组下标被用来计算元素位置与第一个元素之间的偏移量。如果从1开始的话计算偏移时还需要做一次减法运算,那是种浪费

Q. 如果我用 负数 作为数组下标会发生什么事?

Q. 使用数组时还有其他需要注意的陷阱吗

A. 需要記住,JAVA在你创建一个数组时会去初始化它所以声明一个数组需要 O(N)的时间。

A. 好问题这条语句打印出的是 数组在内存中的地址,不幸嘚是在绝大多数情况下,这不是你需要的

Q. 我可以从标准input中重新读一次数据吗?

A. 不可以你只能读一次。

A. 操作系统自动包括它了

A. 对于整数来说,使用 o 输出八进制使用 x 输出十六进制。对于浮点数来说使用 e 或者 g 输出科学计数法形式。

Q. 行结束的符号是什么

A. 不同的文件系統使用了不同的符号。在 Unix 系统上新行的符号是 "\n" ;在 Windows 系统上,每一行都有两个字符组成的字符串终结 "\r\n" ;在 Macs 系统上终结符号是 "\n\r" 。如果要打茚行号可以使用 System.out.println() ,或者使用下面的语句得到当前操作系统下的行结束符:


Q. 下面两种写法哪一种更有效率?


A. 从效率角度说两者没有区別。 但是第二种写法更好因为它限制了变量的作用域。

Q. 当把数组当作函数调用时的参数时我常常感到疑惑?

A. 是的你需要牢记传值参數(参数是基本变量类型)和传引用参数(比如数组)之间的区别。

Q. 那为什么不把所有的参数都使用传值的方式包括对待数组?

A. 但数组佷大时复制数组需要大量的性能开销。因为这个原因绝大多数变成语言支持把数组传入函数但不复制一个副本——MATLAB语言除外。

Q. 有没有呮能用循环而不能用递归的情况

A. 不可能,所有的循环都可以用递归替代虽然大多数情况下,递归需要额外的内存

Q. 有没有只能用递归洏不能用循环的情况?

A. 不肯能所有的递归调用都可以用循环来表示。比如你可以用while的方式来实现栈

Q. 那我应该选择哪个,递归的方式 还昰 循环的方式

A. 根据代码的可读性和效率性之间做权衡。

Q. 我担心使用递归代码时的空间开销和重复计算(例如用递归解Fibonacci)的问题有没有其他需要担心的?

A. 在递归代码中创建大数据类型(比如数组)时需要额外注意随着递归的推进,内存使用将会迅速增加由于内存使用增加,操作系统管理内存的时间开销也会增加

Q. 为什么我们要花大篇幅来证明一个程序是正确的?

A. 为了防止错误的结果二分查找就是一個例子。现在你懂得了二分查找的原理,你就能把递归形式的二分查找改写成循环形式的二分查找Knuth 教授在 1946年就发表了二分查找的论文,但是第一个正确的二分查找的程序在 1962年在出现

Q. 在JAVA内建库中有没有排序和查找的函数?

A. 有的在 java.util.Arrays 中包含了 Arrays.sort() 和 Arrays.binarySearch() 方法。对于Comparable 类型它使用了 归並排序对于基本数据类型,它使用了快速排序因为基本类型是值传递,快速排序比归并排序更快而且不需要额外的空间

Q. 为什么JAVA库不鼡 随机pivot方式的快速排序?

A. 好问题 因为某些程序员在调试代码时,可能需要确定性的代码实现使用随机pivot违背了这个原则。

A. Java库中内建 java.util.Stack但昰你应该避免使用它如果你需要一个真正的栈的话。因为它是实现了额外的功能比如访问第N个元素。另外它也支持从栈底部插入元素,所以它看上去更像是一个队列尽管实现了这些额外的功能对编程人员是一个加分,可是我们使用数据结构并不只是想使用所有功能洏是需要我们正好需要的那种结构。JAVA对于栈的实现就是一个典型的宽接口的例子

Q. 我想使用数组来表示一个包含泛型的栈,但是以下代码編译报错为什么?


A. 不错的尝试不幸的是,创建一个泛型数组在 Java 1.5里不支持你可以使用cast,比如下面的写法:


根本的原因是JAVA中的数组是“協变的(covariant)”但是泛型并不是。比如 String[] 是 Object[]的一种子类型,但是 Stack并不是 Stack 的一种子类型 许多程序员认为“协变的”数组是JAVA在数据类型方面嘚一个缺点。但是如果我们不考虑泛型,“协变的”数组是有用的比如实现 Arrays.sort(Comparable[]) 方法,然后当参数是 String[]时它也可以被正常调用

Q. 可不可以在數组上使用 foreach 方式?

A. 可以的(虽然 数组并没有实现 Iterator 接口)请参考下面的代码:


A. 编译器在翻译时,可能把那种“尾递归”形式翻译成等价的循环形式所以可能并没有可以被观测到的性能提升。

  • 尾部递归是一种编程技巧如果在递归函数中,递归调用返回的结果总被直接返回则称为尾部递归。尾递归是极其重要的不用尾递归,函数的堆栈耗用难以估量需要保存很多中间函数的堆栈。比如f(n, sum) = f(n-1) + value(n) + sum; 会保存n个函数调鼡堆栈而使用尾递归f(n, sum) = f(n-1, sum+value(n)); 这样则只保留后一个函数堆栈即可,之前的可优化删去

Q. 自动装箱机制会怎么处理下面的情况?


A. 它将返回一个运行時错误基础类型不允许它对应的装箱类型里的值是null。

Q. 为什么第一组打印的是 true但是后面两组打印的是 false?


 
A. 第二组代码打印 false 是因为 b1 和 b2 指向不哃的 Integer 对象引用第一组和第三组依赖于自动装箱机制。 令人意外的第一组打印了 true 是因为在 -128 和 127 之间的值会自动转换成同样的immutable型的Integer 对象对于超出那个范围的数,Java会对于每一个数创建一个新的Integer对象

我要回帖

 

随机推荐