JAVA面试题解惑系列

转:http://zangweiren.iteye.com/blog/209895

以下是偶看的一些笔记摘录:

String str=new String("abc");

紧接着这段代码之后的往往是这个问题,那就是这行代码究竟创建了几个String对象呢?相信大家对这道题并不
陌生,答案也是众所周知的,2个。接下来我们就从这道题展开,一起回顾一下与创建String对象相关的一些
JAVA知识。

我们可以把上面这行代码分成String str、=、"abc"和new String()四部分来看待。String str只是定义了一个名
为str的String类型的变量,因此它并没有创建对象;=是对变量str进行初始化,将某个对象的引用(或者叫句
柄)赋值给它,显然也没有创建对象;现在只剩下new String("abc")了。那么,new String("abc")为什么又能
被看成"abc"和new String()呢?我们来看一下被我们调用了的String的构造器:

public String(String original) {
//other code ...
}

大家都知道,我们常用的创建一个类的实例(对象)的方法有以下两种:
1. 使用new创建对象。
2. 调用Class类的newInstance方法,利用反射机制创建对象。

我们正是使用new调用了String类的上面那个构造器方法创建了一个对象,并将它的引用赋值给了str变量。同
时我们注意到,被调用的构造器方法接受的参数也是一个String对象,这个对象正是"abc"。由此我们又要引入
另外一种创建String对象的方式的讨论——引号内包含文本。

这种方式是String特有的,并且它与new的方式存在很大区别。

String str="abc";毫无疑问,这行代码创建了一个String对象。

String a="abc";String b="abc";那这里呢?答案还是一个。

String a="ab"+"cd";再看看这里呢?答案仍是一个。有点奇怪吗?说到这里,我们就需要引入对字符串池相关知识的回顾了。

在JAVA虚拟机(JVM)中存在着一个字符串池,其中保存着很多String对象,并且可以被共享使用,因此它提
高了效率。由于String类是final的,它的值一经创建就不可改变,因此我们不用担心String对象共享而带来程序
的混乱。字符串池由String类维护,我们可以调用intern()方法来访问字符串池。

我们再回头看看String a="abc";,这行代码被执行的时候,JAVA虚拟机首先在字符串池中查找是否已经存在了
值为"abc"的这么一个对象,它的判断依据是String类equals(Object obj)方法的返回值。如果有,则不再创建
新的对象,直接返回已存在对象的引用;如果没有,则先创建这个对象,然后把它加入到字符串池中,再将它
的引用返回。因此,我们不难理解前面三个例子中头两个例子为什么是这个答案了。

String a="ab"+"cd";

由于常量的值在编译的时候就被确定了。在这里,"ab"和"cd"都是常量,因此变量a的值在编译时就可以确定。
这行代码编译后的效果等同于:String a="abcd";

因此这里只创建了一个对象"abcd",并且它被保存在字符串池里了。


现在问题又来了,是不是所有经过“+”连接后得到的字符串都会被添加到字符串池中呢?

只有使用引号包含文本的方式(String d = "ab" + "cd";)创建的String对象之间使用“+”连接产生的新对
象才会被加入字符串池中。对于所有包含new方式(String e = a + "cd";)新建对象(包括null)的“+”连接表达式,它所产生的新对
象都不会被加入字符串池中

但是有一种情况需要引起我们的注意,

public static final String A = "ab";

public static final String B = "cd";

String s = A + B;
String t = "abcd";

结果:s == t

s等于t,它们是同一个对象

这又是为什么呢?原因是这样的,对于常量来讲,它的值是固定的,因此在编译期就能被确定了,而变量的值
只有到运行时才能被确定,因为这个变量可以被不同的方法调用,从而可能引起值的改变。在上面的例子中,A
和B都是常量,值是固定的,因此s的值也是固定的,它在类被编译时就已经确定了


被final修饰的变量必须被初始化。初始化的方式有以下几种:
1. 在定义的时候初始化。
2. final变量可以在初始化块中初始化,不可以在静态初始化块中初始化。
3. 静态final变量可以在静态初始化块中初始化,不可以在初始化块中初始
4. final变量还可以在类的构造器中初始化,但是静态final变量不可以。


用final修饰的变量(常量)比非final的变量(普通变量)拥有更高的效率,因此我们在实际编程中应该尽可能
多的用常量来代替普通变量,这也是一个很好的编程习惯。


调用System.gc()等同于调用下面这行代码:Runtime.getRuntime().gc();

调用它们的作用只是建议垃圾收集器(GC)启动,清理无用的对象释放内存空间,但是GC的启动并不是一定
的,这由JAVA虚拟机来决定。


那么当引用型变量被当作参数传递给方法时JAVA虚拟机又是怎样处理的呢?同样,它会拷贝一份这个变量所持
有的引用,然后把它传递给JAVA虚拟机为方法创建的局部变量,从而这两个变量指向了同一个对象。

对于引用类型,在方法体内对方法参数进行重新赋予引用,并不会改变原有变量所持有的引用。

方法体内对参数所指向对象的属性进行运算,将改变原有变量所指向对象的属性值。

最后我们得出如下的结论:
1. 基本类型和基本类型变量被当作参数传递给方法时,是值传递。在方法实体中,无法给原变量重新赋
值,也无法改变它的值。
2. 对象和引用型变量被当作参数传递给方法时,在方法实体中,无法给原变量重新赋值,但是可以改变它
所指向对象的属性。至于到底它是值传递还是引用传递,这并不重要,重要的是我们要清楚当一个引用
被作为参数传递给一个方法时,在这个方法体内会发生什么。


数组有一个属性length,这也是它唯一的属性

一个中文汉字能保存在一个char类型里吗?

因为在JAVA中,一个char是2个字节(byte),而一个中文汉字是一个字符,也是2个字节。而英文字母都是一个字节的,

因此它也能保存到一个byte里,一个中文汉字却不能。


// 将一个中文汉字赋值给一个char变量
char a = '中';
char b = '文';
char c = '测';
char d = '试';
char e = '成';
char f = '功';
System.out.print(a + b + c + d + e + f);

运行结果:156035

这显然不是我们想要的结果。只所以会这样是因为我们误用了“+”运算符,当它被用于字符串和字符串之
或者字符串和其他类型变量之间时,它产生的效果是字符串的拼接;但当它被用于字符和字符之间时,效果
同于用于数字和数字之间,是一种算术运算。因此我们得到的“156035”是'中'、'文'、'测'、'试'、'成'、'功
六个汉字分别对应的数值算术相加后的结果。


字符串的反转输出

最常用的方式就是反向取出每个位置的字符,然后依次将它们输出到控制台

但却不是最简单的方式,更简单的是使用现有的方法:

String s = "A quick brown fox jumps over the lazy dog.";

StringBuffer buff = new StringBuffer(s);

// java.lang.StringBuffer类的reverse()方法可以将字符串反转
System.out.println(buff.reverse().toString());


1. 英文字母:A
2. 字节数:1;编码:GB2312

3. 字节数:1;编码:GBK
4. 字节数:1;编码:GB18030
5. 字节数:1;编码:ISO-8859-1
6. 字节数:1;编码:UTF-8
7. 字节数:4;编码:UTF-16
8. 字节数:2;编码:UTF-16BE
9. 字节数:2;编码:UTF-16LE

10. 中文汉字:人

11. 字节数:2;编码:GB2312
12. 字节数:2;编码:GBK
13. 字节数:2;编码:GB18030
14. 字节数:1;编码:ISO-8859-1
15. 字节数:3;编码:UTF-8
16. 字节数:4;编码:UTF-16
17. 字节数:2;编码:UTF-16BE
18. 字节数:2;编码:UTF-16LE


Date类可以精确到毫秒数,这个毫秒数是相对
于格林威治标准时间“1970-01-01 00:00:00.000 GMT”的差值。

Date类有很多个构造器方法,大部分已经不被赞成使用了(Deprecated),不过还剩下两个可以使用的:

public Date() {
this(System.currentTimeMillis());
}
public Date(long date) {
//other code
}

第一个是无参构造器,使用系统当前时间的毫秒数来创建Date对象,它调用了java.lang.System类的
currentTimeMillis()来取得系统的当前时间的毫秒值

在创建完Date对象之后,我们可以通过调用getTime()方法来获得该对象的毫秒数值,调用setTime(long time)
方法来设置它的毫秒数值


对于基本类型void以及它的包装类java.lang.Void,我们都无法直接进行操作。

基本类型存储在栈中,因此它们的存取速度要快于存储在堆中的对应包装类的实例对象。

所有基本类型(包括void)的包装类都使用了final修饰,因此我
们无法继承它们扩展新的类,也无法重写它们的任何方法。

如果给long型变量赋予的值超出了int型值的范围,数字后必须加L(不区分大小写)标识

long long_c = 9300000000000000000L;


1. 未带有字符后缀标识的整数默认为int类型;未带有字符后缀标识的浮点数默认为double类型。
2. 如果一个整数的值超出了int类型能够表示的范围,则必须增加后缀“L”(不区分大小写,建议用大
写,因为小写的L与阿拉伯数字1很容易混淆),表示为long型。
3. 带有“F”(不区分大小写)后缀的整数和浮点数都是float类型的;带有“D”(不区分大小写)后缀
的整数和浮点数都是double类型的。
4. 编译器会在编译期对byte、short、int、long、float、double、char型变量的值进行检查,如果超出
了它们的取值范围就会报错。
5. int型值可以赋给所有数值类型的变量;long型值可以赋给long、float、double类型的变量;float型值
可以赋给float、double类型的变量;double型值只能赋给double类型变量。


在s1=s1+1;中,s1+1运算的结果是int型,把它赋值给一个short型变量s1,所以会报错;而在s1+=1;中,由于是s1是short类型的,所以1首先被强制转换为short型,然后再参与运算,并且结果也是short类型的,因此不会报错。那么,s1=1+1;为什么不报错呢?这是因为1+1是个编译时可以确定的常量,“+”运算在编译时就被执行了,而不是在程序执行的时候,这个语句的效果等同于s1=2,所以不会报错。


Math.round()方法

public static int round(float a) {
return (int)floor(a + 0.5f);
}


1. byte、char、short、int四种基本类型以及它们的包装类(需要Java5.0/1.5以上版本支持)都可以用于switch语句。
2. long、float、double、boolean四种基本类型以及它们的包装类(在Java所有版本中)都不能用于switch语句。
3. enum类型,即枚举类型可以用于switch语句,但是要在Java5.0(1.5)版本以上才支持。
4. 所有类型的对象(包括String类,但在Java5.0/1.5以上版本中,该项要排除byte、char、short、int四种基本类型对应的包装类)都不能用于switch语句。


下面的程序代码输出的结果是多少?

public class  smallT
{
	public static void  main(String args[])
	{
		smallT t  = new  smallT();
		int  b  =  t.get();
		System.out.println(b);
	}
	
	public int  get()
	{
		try
		{
			return 1 ;
		}
		finally
		{
			return 2 ;
		}
	}
}

返回的结果是2。

我可以通过下面一个例子程序来帮助我解释这个答案,从下面例子的运行结果中可以发现, try 中的 return 语句调用的函数先于 finally 中调用的函数执行,也就是

return语句先执行,finally语句后执行,所以,返回的结果是2Return并不是让函数马上返回,而是return语句执行后,将把返回结果放置进函数栈中,此时函数并不是马上返回,它要执行finally语句后才真正开始返回。

在讲解答案时可以用下面的程序来帮助分析:

public  class Test {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println(new Test().test());;
	}

	int test()
	{
		try
		{
			return func1();
		}
		finally
		{
			return func2();
		}
	}
	
	int func1()
	{
		System.out.println("func1");
		return 1;
	}
	int func2()
	{
		System.out.println("func2");
		return 2;
	}	
}

-----------执行结果-----------------

func1

func2

2

结论:finally中的代码比return 和break语句后执行



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值