`
fastbo
  • 浏览: 27251 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java容易搞错的知识点-觉得基础扎实的来看

阅读更多
以下几个知识点是非常容易搞混的Java知识点。大家不准开编译器,并且先不看答案,要是能全部答对,那Java基础是挺牢固的。如果答对了,还能分析出具体原因,那算你NB。近段时间有参加一些公司的面试,做了一些基础题,发现总有掌握得不好的地方。今天一并总结了这些问题,希望对大家有所帮助。如果大家认为还有其它易混淆的地方,也可以跟贴发出来,大家一起讨论。从大家的回贴中,发现了不少自已不知道的东西,想看的就看吧。
一、关于Switch
代码:
public class TestSwitch {
	public static void main(String[] args) {
		int i = 2;
		switch (i) {
		case 1:
			System.out.println(1);
		case 2:
			System.out.println(2);
		case 3:
			System.out.println(3);
		default:
			System.out.println(4);
		}
	}
}




结果:
2
3
4

分析:
少了break;所以2以下的case和default都执行了一遍。

二、Equals和==运算符
代码:
	public static void test() {
		String x = "hello";
		String y = "world";
		String z = new String("helloworld");
		String a = "helloworld";
		System.out.println("x+y equals z:" + (x + y).equals(z));
		System.out.println("a == z:" + (a == z));
		System.out.println("x == hello:" + (x == "hello"));
		System.out.println("a == helloworld:" + (a == "hello" + "world"));
		System.out.println("a == x+y:" + (a == (x + y)));
	}




结果:
x+y equals z:true
a == z:false
x == hello:true
a == helloworld:true
a == x+y:false

分析:
1.String.equals()方法比较的是字符串的内容,所以(x + y).equals(z)为true.
2.“==”比较的是 String 实例的引用,很明显 a 和z 并不是同一个 String 实例,所以(a == z)为false.
3.根据常量池的知识,容易得知(x == "hello")和(a == "hello" + "world")都为true.
(常量池指的是在编译期被确定并被保存在已编译的.class 文件中的一些数据。它包含了
关于方法、类、接口等,当然还有字符串常量的信息。也就是所谓的持久代。)
4.那么(a == (x + y))为什么是false呢?这点暂点有点不大清楚。初步认为是x+y是引用相加,不能放入常量池。

三、Override覆盖
代码:

public class Parent {

	public static String say() {
		return "parent static say";
	}

	public String say2() {
		return "parent say";
	}
}

public class Child extends Parent {
	public static String say() {
		return "child static say";
	}

	public String say2() {
		return "child say";
	}
}

/**
 * @author 子弹哥
 * 
 */
public class OverrideTest {

	public static void main(String[] args) {
		Parent p = new Child();
		System.out.println(p.say());
		System.out.println(p.say2());

	}

}





结果:
parent static say
child say
分析:
1.我们创建了一个Parent类的实例。变量 p 的数据类型为 Parent 类 但是它仍旧是 Child 类的一个实例。因为Child类覆盖了Parent类的方法say2(),所以p.say2()调用为子类的方法。
2.为什么p.say()却是调用父类Parent的方法呢?因为Java中规定“实例方法被覆盖,静态方法被隐藏”.
关于Override的一些规则:
用子类的静态方法隐藏父类中同样标识的实例方法是不合法的,编译器将会报错;
用子类的实例方法覆盖父类中同样标识的静态方法也是不合法的,编译器同样会报错;
带关键字 final的方法(静态和实例方法)都不能被覆盖;
实例方法能够被覆盖;
抽象方法必须在具体类中被覆盖。

四、Java强类型
代码:
/**
 * @author 子弹哥
 * 
 */
public class Type {

	public static void main(String[] args) {
		double i = 5.0;
		double j = 1 / 4 + 3 / 4 + i + 12 / 6.0 + 3 / 4 + 1 / 4;
		System.out.println(j);
	}

}







结果:
7.0

分析:
Java 是强类型的 strongly type,它支持8 种基本数据类型。通过对这些基本数据类型用法的严格检查 Java 编译器能够及时地在开发过程中捕捉到许多简单细微的错误。基本数据类型的转换可以隐性地发生,所以转换时会有精度损失。由于1/4和3/4发生隐性类型转换,精度损失,不会生成0.25和0.75,所以有分号的数都为0。

五、假构造函数
代码:
/**
 * @author 子弹哥
 * 
 */
public class Constructor {

	private int a, b, c;

	public void Constructor() {
		a = 3;
		b = 5;
		c = a + b;
	}

	public void test() {
		System.out.println("The value of c :" + c);
	}

	public static void main(String[] args) {
		Constructor c = new Constructor();
		c.test();
	}
}







结果:
The value of c :0

分析:
public void Constructor()并不是一个真正的构造函数,而是一个方法。所以c的值为默认值0.


六、提前引用
代码:
/**
 * @author 子弹哥
 * 
 */
public class ForwardReference {

	static int first = test();
	static int second = 2;

	static int test() {
		return second;
	}

	public static void main(String[] args) {
		System.out.println("first = " + first);
	}

}




结果:
first = 0

分析:
由于在初始化second之前test方法就访问了它,那么方法得到的是second的默认值,即 0。 因此输出结果first= 0,而不是2。假如你使用方法调用来初始化静态变量,那么你必须保证 这些方法并不依赖于在它们之后声明的其它静态变量。静态变量以及静态初始化块是在类被加载进 JVM 时执行初始化操作的。Java 语言规范8.5节指出“静态初始化块和静态变量是按照其在代码中出现的顺序依次执行初始化操作的,而不能在类变量声明出现之前就引用它”。


七、对象引用
代码:
/**
 * @author 子弹哥
 * 
 */
public class TestRef {

	public static void main(String[] args) {
		StringBuffer a = new StringBuffer("a");
		StringBuffer b = new StringBuffer("b");
		append(a, b);
		System.out.println(a.toString() + "," + b.toString());
		b = a;
		System.out.println(a.toString() + "," + b.toString());
	}

	public static void append(StringBuffer a, StringBuffer b) {
		a.append(b);
		b = a;
	}
}






结果:
ab,b
ab,ab


分析:
大家来分析一下这题,我还没有完全理解。
我的分析,可能是错的,哈哈,算是抛砖引玉。

1.a.append(b);-->ab 。因为a是引用,所以调用a的方法,相当于直接调用jvm中的a,所做的append也相当于直接在对象上操作,生效。
2.append方法中第一次b=a,-->b。因为a,b都为main方法内局部变量,跨append方法作用域b对a的引用不生效。
3.main方法中第二次b=a,-->ab。因为在同一作用域方法中,b对a的引用生效,。

分享到:
评论
106 楼 greatrobert 2011-04-12  
akunamotata 写道
关于第二题,我画了个图,有错请务必指出,免得误人子弟。


显然错了,x、y也在常量池里面
105 楼 biqiang86 2011-04-10  
sdtm1016 写道
SCJP 不少这样的题 ,如果不是靠背过关,而是扎实分析,还是挺有用的

对对,这些东西建议去看看scjp的考试资料,都是这些问题...
104 楼 zhangwe415 2011-03-29  
看的晕晕的,终于搞懂了
103 楼 nightbin0420 2011-03-29  
学习了,还是有些没明白..看来得加强基础了!
102 楼 wdz567 2011-03-19  
最后一条应该问题应该是在a.append(b) 上这个地方被append搞了个值传递
而b没有。如果b使用b.append(a) 我想也应该可以修改
101 楼 Jathon_hs 2011-03-18  
还行,除了a==(x+y)没答对
100 楼 h521999 2011-03-18  
曾经记得藏圩人整理过类似的一系列题目。
99 楼 pjcai 2011-03-16  
x跟y是final的下面
System.out.println("a == x+y:" + (a == (x + y))); 
就是true了,因为这时候是常量,编译期间就能确定的值,就直接饮用字符串池里的。

98 楼 sunlzx 2011-03-16  
这帖子挺给力的
97 楼 hommy8 2011-03-09  
fellatioyzx 写道
我都答对了

关于最后一题的话,由于java方法传值是值传递,所以方法里面的ab虽然和方法外的ab指向的对象一样,但是这两个引用在栈中的位置不一样。

过程大概是首先在栈中复制引用a为a',b为b',并令a'指向a所指的在堆中的StringBuffer对象,b'指向b所指的在堆中的StringBuffer对象。
因此方法中的b=a只是起到了b'=a'的作用,并没有影响方法外b的指向。


此言与我心中所想完全一致
96 楼 kingwolf543 2011-03-09  
java解惑上说的很明白啊
95 楼 thihy 2011-03-09  
那么(a == (x + y))为什么是false呢?这点暂点有点不大清楚。初步认为是x+y是引用相加,不能放入常量池。

因为你的x、y不是final的,无法在编译期间加入常量池。
94 楼 ljinkai 2011-03-09  
这里有最后一题的详细说明:http://anna-zr.iteye.com/blog/382459
93 楼 99381837 2011-03-09  
fastbo 写道
以下几个知识点是非常容易搞混的Java知识点。大家不准开编译器,并且先不看答案,要是能全部答对,那Java基础是挺牢固的。如果答对了,还能分析出具体原因,那算你NB。近段时间有参加一些公司的面试,做了一些基础题,发现总有掌握得不好的地方。今天一并总结了这些问题,希望对大家有所帮助。如果大家认为还有其它易混淆的地方,也可以跟贴发出来,大家一起讨论。从大家的回贴中,发现了不少自已不知道的东西,想看的就看吧。
一、关于Switch
代码:
public class TestSwitch {
	public static void main(String[] args) {
		int i = 2;
		switch (i) {
		case 1:
			System.out.println(1);
		case 2:
			System.out.println(2);
		case 3:
			System.out.println(3);
		default:
			System.out.println(4);
		}
	}
}




结果:
2
3
4

分析:
少了break;所以2以下的case和default都执行了一遍。

二、Equals和==运算符
代码:
	public static void test() {
		String x = "hello";
		String y = "world";
		String z = new String("helloworld");
		String a = "helloworld";
		System.out.println("x+y equals z:" + (x + y).equals(z));
		System.out.println("a == z:" + (a == z));
		System.out.println("x == hello:" + (x == "hello"));
		System.out.println("a == helloworld:" + (a == "hello" + "world"));
		System.out.println("a == x+y:" + (a == (x + y)));
	}




结果:
x+y equals z:true
a == z:false
x == hello:true
a == helloworld:true
a == x+y:false

分析:
1.String.equals()方法比较的是字符串的内容,所以(x + y).equals(z)为true.
2.“==”比较的是 String 实例的引用,很明显 a 和z 并不是同一个 String 实例,所以(a == z)为false.
3.根据常量池的知识,容易得知(x == "hello")和(a == "hello" + "world")都为true.
(常量池指的是在编译期被确定并被保存在已编译的.class 文件中的一些数据。它包含了
关于方法、类、接口等,当然还有字符串常量的信息。也就是所谓的持久代。)
4.那么(a == (x + y))为什么是false呢?这点暂点有点不大清楚。初步认为是x+y是引用相加,不能放入常量池。

三、Override覆盖
代码:

public class Parent {

	public static String say() {
		return "parent static say";
	}

	public String say2() {
		return "parent say";
	}
}

public class Child extends Parent {
	public static String say() {
		return "child static say";
	}

	public String say2() {
		return "child say";
	}
}

/**
 * @author 子弹哥
 * 
 */
public class OverrideTest {

	public static void main(String[] args) {
		Parent p = new Child();
		System.out.println(p.say());
		System.out.println(p.say2());

	}

}





结果:
parent static say
child say
分析:
1.我们创建了一个Parent类的实例。变量 p 的数据类型为 Parent 类 但是它仍旧是 Child 类的一个实例。因为Child类覆盖了Parent类的方法say2(),所以p.say2()调用为子类的方法。
2.为什么p.say()却是调用父类Parent的方法呢?因为Java中规定“实例方法被覆盖,静态方法被隐藏”.
关于Override的一些规则:
用子类的静态方法隐藏父类中同样标识的实例方法是不合法的,编译器将会报错;
用子类的实例方法覆盖父类中同样标识的静态方法也是不合法的,编译器同样会报错;
带关键字 final的方法(静态和实例方法)都不能被覆盖;
实例方法能够被覆盖;
抽象方法必须在具体类中被覆盖。

四、Java强类型
代码:
/**
 * @author 子弹哥
 * 
 */
public class Type {

	public static void main(String[] args) {
		double i = 5.0;
		double j = 1 / 4 + 3 / 4 + i + 12 / 6.0 + 3 / 4 + 1 / 4;
		System.out.println(j);
	}

}







结果:
7.0

分析:
Java 是强类型的 strongly type,它支持8 种基本数据类型。通过对这些基本数据类型用法的严格检查 Java 编译器能够及时地在开发过程中捕捉到许多简单细微的错误。基本数据类型的转换可以隐性地发生,所以转换时会有精度损失。由于1/4和3/4发生隐性类型转换,精度损失,不会生成0.25和0.75,所以有分号的数都为0。

五、假构造函数
代码:
/**
 * @author 子弹哥
 * 
 */
public class Constructor {

	private int a, b, c;

	public void Constructor() {
		a = 3;
		b = 5;
		c = a + b;
	}

	public void test() {
		System.out.println("The value of c :" + c);
	}

	public static void main(String[] args) {
		Constructor c = new Constructor();
		c.test();
	}
}







结果:
The value of c :0

分析:
public void Constructor()并不是一个真正的构造函数,而是一个方法。所以c的值为默认值0.


六、提前引用
代码:
/**
 * @author 子弹哥
 * 
 */
public class ForwardReference {

	static int first = test();
	static int second = 2;

	static int test() {
		return second;
	}

	public static void main(String[] args) {
		System.out.println("first = " + first);
	}

}




结果:
first = 0

分析:
由于在初始化second之前test方法就访问了它,那么方法得到的是second的默认值,即 0。 因此输出结果first= 0,而不是2。假如你使用方法调用来初始化静态变量,那么你必须保证 这些方法并不依赖于在它们之后声明的其它静态变量。静态变量以及静态初始化块是在类被加载进 JVM 时执行初始化操作的。Java 语言规范8.5节指出“静态初始化块和静态变量是按照其在代码中出现的顺序依次执行初始化操作的,而不能在类变量声明出现之前就引用它”。


七、对象引用
代码:
/**
 * @author 子弹哥
 * 
 */
public class TestRef {

	public static void main(String[] args) {
		StringBuffer a = new StringBuffer("a");
		StringBuffer b = new StringBuffer("b");
		append(a, b);
		System.out.println(a.toString() + "," + b.toString());
		b = a;
		System.out.println(a.toString() + "," + b.toString());
	}

	public static void append(StringBuffer a, StringBuffer b) {
		a.append(b);
		b = a;
	}
}






结果:
ab,b
ab,ab


分析:
大家来分析一下这题,我还没有完全理解。
我的分析,可能是错的,哈哈,算是抛砖引玉。

1.a.append(b);-->ab 。因为a是引用,所以调用a的方法,相当于直接调用jvm中的a,所做的append也相当于直接在对象上操作,生效。
2.append方法中第一次b=a,-->b。因为a,b都为main方法内局部变量,跨append方法作用域b对a的引用不生效。
3.main方法中第二次b=a,-->ab。因为在同一作用域方法中,b对a的引用生效,。


92 楼 oryjk 2011-03-08  
String x = "hello"; 
String y = "world";
String a = "helloworld";
System.out.println("a == x+y:" + (a == (x + y)));
因为x,y都是引用数据类型,都可以看成是一个对象,这样两个应用数据类型相加,会在堆中产生一个新的对象,所以就不相等了
91 楼 beneo 2011-03-08  
第七题弄明白的条件是,你么知道,传递的只是引用的拷贝
90 楼 云和山的彼端 2011-03-08  
这更应该叫面试知识点,或者说三年以下程序员面试知识点。
89 楼 ayumi11111 2011-03-08  
fastbo 写道
以下几个知识点是非常容易搞混的Java知识点。大家不准开编译器,并且先不看答案,要是能全部答对,那Java基础是挺牢固的。如果答对了,还能分析出具体原因,那算你NB。近段时间有参加一些公司的面试,做了一些基础题,发现总有掌握得不好的地方。今天一并总结了这些问题,希望对大家有所帮助。如果大家认为还有其它易混淆的地方,也可以跟贴发出来,大家一起讨论。从大家的回贴中,发现了不少自已不知道的东西,想看的就看吧。
一、关于Switch
代码:
public class TestSwitch {
	public static void main(String[] args) {
		int i = 2;
		switch (i) {
		case 1:
			System.out.println(1);
		case 2:
			System.out.println(2);
		case 3:
			System.out.println(3);
		default:
			System.out.println(4);
		}
	}
}




结果:
2
3
4

分析:
少了break;所以2以下的case和default都执行了一遍。

二、Equals和==运算符
代码:
	public static void test() {
		String x = "hello";
		String y = "world";
		String z = new String("helloworld");
		String a = "helloworld";
		System.out.println("x+y equals z:" + (x + y).equals(z));
		System.out.println("a == z:" + (a == z));
		System.out.println("x == hello:" + (x == "hello"));
		System.out.println("a == helloworld:" + (a == "hello" + "world"));
		System.out.println("a == x+y:" + (a == (x + y)));
	}




结果:
x+y equals z:true
a == z:false
x == hello:true
a == helloworld:true
a == x+y:false

分析:
1.String.equals()方法比较的是字符串的内容,所以(x + y).equals(z)为true.
2.“==”比较的是 String 实例的引用,很明显 a 和z 并不是同一个 String 实例,所以(a == z)为false.
3.根据常量池的知识,容易得知(x == "hello")和(a == "hello" + "world")都为true.
(常量池指的是在编译期被确定并被保存在已编译的.class 文件中的一些数据。它包含了
关于方法、类、接口等,当然还有字符串常量的信息。也就是所谓的持久代。)
4.那么(a == (x + y))为什么是false呢?这点暂点有点不大清楚。初步认为是x+y是引用相加,不能放入常量池。

三、Override覆盖
代码:

public class Parent {

	public static String say() {
		return "parent static say";
	}

	public String say2() {
		return "parent say";
	}
}

public class Child extends Parent {
	public static String say() {
		return "child static say";
	}

	public String say2() {
		return "child say";
	}
}

/**
 * @author 子弹哥
 * 
 */
public class OverrideTest {

	public static void main(String[] args) {
		Parent p = new Child();
		System.out.println(p.say());
		System.out.println(p.say2());

	}

}





结果:
parent static say
child say
分析:
1.我们创建了一个Parent类的实例。变量 p 的数据类型为 Parent 类 但是它仍旧是 Child 类的一个实例。因为Child类覆盖了Parent类的方法say2(),所以p.say2()调用为子类的方法。
2.为什么p.say()却是调用父类Parent的方法呢?因为Java中规定“实例方法被覆盖,静态方法被隐藏”.
关于Override的一些规则:
用子类的静态方法隐藏父类中同样标识的实例方法是不合法的,编译器将会报错;
用子类的实例方法覆盖父类中同样标识的静态方法也是不合法的,编译器同样会报错;
带关键字 final的方法(静态和实例方法)都不能被覆盖;
实例方法能够被覆盖;
抽象方法必须在具体类中被覆盖。

四、Java强类型
代码:
/**
 * @author 子弹哥
 * 
 */
public class Type {

	public static void main(String[] args) {
		double i = 5.0;
		double j = 1 / 4 + 3 / 4 + i + 12 / 6.0 + 3 / 4 + 1 / 4;
		System.out.println(j);
	}

}







结果:
7.0

分析:
Java 是强类型的 strongly type,它支持8 种基本数据类型。通过对这些基本数据类型用法的严格检查 Java 编译器能够及时地在开发过程中捕捉到许多简单细微的错误。基本数据类型的转换可以隐性地发生,所以转换时会有精度损失。由于1/4和3/4发生隐性类型转换,精度损失,不会生成0.25和0.75,所以有分号的数都为0。

五、假构造函数
代码:
/**
 * @author 子弹哥
 * 
 */
public class Constructor {

	private int a, b, c;

	public void Constructor() {
		a = 3;
		b = 5;
		c = a + b;
	}

	public void test() {
		System.out.println("The value of c :" + c);
	}

	public static void main(String[] args) {
		Constructor c = new Constructor();
		c.test();
	}
}







结果:
The value of c :0

分析:
public void Constructor()并不是一个真正的构造函数,而是一个方法。所以c的值为默认值0.


六、提前引用
代码:
/**
 * @author 子弹哥
 * 
 */
public class ForwardReference {

	static int first = test();
	static int second = 2;

	static int test() {
		return second;
	}

	public static void main(String[] args) {
		System.out.println("first = " + first);
	}

}




结果:
first = 0

分析:
由于在初始化second之前test方法就访问了它,那么方法得到的是second的默认值,即 0。 因此输出结果first= 0,而不是2。假如你使用方法调用来初始化静态变量,那么你必须保证 这些方法并不依赖于在它们之后声明的其它静态变量。静态变量以及静态初始化块是在类被加载进 JVM 时执行初始化操作的。Java 语言规范8.5节指出“静态初始化块和静态变量是按照其在代码中出现的顺序依次执行初始化操作的,而不能在类变量声明出现之前就引用它”。


七、对象引用
代码:
/**
 * @author 子弹哥
 * 
 */
public class TestRef {

	public static void main(String[] args) {
		StringBuffer a = new StringBuffer("a");
		StringBuffer b = new StringBuffer("b");
		append(a, b);
		System.out.println(a.toString() + "," + b.toString());
		b = a;
		System.out.println(a.toString() + "," + b.toString());
	}

	public static void append(StringBuffer a, StringBuffer b) {
		a.append(b);
		b = a;
	}
}






结果:
ab,b
ab,ab


分析:
大家来分析一下这题,我还没有完全理解。
我的分析,可能是错的,哈哈,算是抛砖引玉。

1.a.append(b);-->ab 。因为a是引用,所以调用a的方法,相当于直接调用jvm中的a,所做的append也相当于直接在对象上操作,生效。
2.append方法中第一次b=a,-->b。因为a,b都为main方法内局部变量,跨append方法作用域b对a的引用不生效。
3.main方法中第二次b=a,-->ab。因为在同一作用域方法中,b对a的引用生效,。


88 楼 broguy 2011-03-08  
给力撒,拿来看看
87 楼 miaofm 2011-03-08  
forsecond 写道

我奇怪的就是在
public static void append(Aa a1, Aa a2) {
a1.setA(a2.getA());
a2 = a1;
}
这当中a1 a2跟第七里面的StringBuffer难道不是一样的吗》?传递过来的都是对象的引用地址啊。既然StringBuffer在方法里面只是把局部变量b的指向修改了,而main没变,那我改的这个方法里面,有什么不同吗?



人家有两个字符串合并,你这就直接设置,我差点也给你蒙了,再仔细看一下代码

相关推荐

Global site tag (gtag.js) - Google Analytics