Java容易搞错的知识点

一、关于Switch

代码:

Java代码

1         public class TestSwitch {  

2             public static void main(String[] args) {  

3                 int i = 2;  

4                 switch (i) {  

5                 case 1:  

6                     System.out.println(1);  

7                 case 2:  

8                     System.out.println(2);  

9                 case 3:  

10                 System.out.println(3);  

11             default:  

12                 System.out.println(4);  

13             }  

14         }  

15     } 

 

 

结果:

2

3

4

 

分析:

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

 

二、Equals和==运算符

代码:

Java代码

1         public static void test() {  

2             String x = "hello";  

3             String y = "world";  

4             String z = new String("helloworld");  

5             String a = "helloworld";  

6             System.out.println("x+y equals z:" + (x + y).equals(z));  

7             System.out.println("a == z:" + (a == z));  

8             System.out.println("x == hello:" + (x == "hello"));  

9             System.out.println("a == helloworld:" + (a == "hello" + "world"));  

10         System.out.println("a == x+y:" + (a == (x + y)));  

11     } 

 

 

结果:

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覆盖

代码:

 

Java代码

1         public class Parent {  

2           

3             public static String say() {  

4                 return "parent static say";  

5             }  

6           

7             public String say2() {  

8                 return "parent say";  

9             }  

10     }  

11       

12     public class Child extends Parent {  

13         public static String say() {  

14             return "child static say";  

15         }  

16       

17         public String say2() {  

18             return "child say";  

19         }  

20     }  

21       

22     public class OverrideTest {  

23       

24         public static void main(String[] args) {  

25             Parent p = new Child();  

26             System.out.println(p.say());  

27             System.out.println(p.say2());  

28       

29         }  

30       

31     } 

 

 

结果:

parent static say

child say

 

分析:

1.我们创建了一个Parent类的实例。变量 p 的数据类型为 Parent 类 但是它仍旧是 Child 类的一个实例。因为Child类覆盖了Parent类的方法say2(),所以p.say2()调用为子类的方法。

2.为什么p.say()却是调用父类Parent的方法呢?因为Java中规定“实例方法被覆盖,静态方法被隐藏”.

关于Override的一些规则:

用子类的静态方法隐藏父类中同样标识的实例方法是不合法的,编译器将会报错;

用子类的实例方法覆盖父类中同样标识的静态方法也是不合法的,编译器同样会报错;

带关键字 final的方法(静态和实例方法)都不能被覆盖;

实例方法能够被覆盖;

抽象方法必须在具体类中被覆盖。

 

我们知道,在JAVA中,子类可以继承父类,如果子类声明的方法与父类有重名的情况怎么办,大伙儿都知道要是重写,但是实际上这又分为两种情况,就是方法和变量在继承时的覆盖和隐藏问题,这些概念性的东西看似无聊,但是在面试或者是SCJP认证题中围绕这些是会经常碰到的,所以这里来讨论下

 

首先我们来看几个概念

       隐藏 :child隐藏了parent的变量和方法,那么,child不能访问parent被隐藏的变量或者方法,但是,讲B转换成A中,可以访问A被隐藏的变量或者方法

        覆盖 :child覆盖了parent的变量或者方法,那么,child不能访问parent被覆盖的变量或者方法,将child转换成parent后同样不能访问parent被覆盖的变量或者方法

 

首先看一下JAVA中方法和变量在继承时的覆盖和隐藏规则

       1.父类的实例变量和静态变量能被子类的同名变量隐藏

       2.父类的静态方法被子类的同名静态方法隐藏

       3.父类的实例方法被子类的同名实例变量覆盖

 

还有几点需要注意的是

       1.不能用子类的静态方法隐藏 父类中同样标示(也就是返回值 名字 参数都一样)的实例方法

       2.不能用子类的实例方法覆盖 父类中同样标示的静态方法

       3.这点儿请注意,就是变量只会被隐藏 不会被覆盖 ,无论他是实例变量还是静态变量,而且,子类的静态变量可以隐藏 父类的实例变量,子类的实例变量可以隐藏 父类的静态变量

 

Java代码  

1         //父类   

2         class Parent   

3         {   

4             public static String kind="javastudy.extendsstudy.parent";   

5             public static int age=50;   

6             public String name="Parent";   

7           

8             //静态方法,返回包名   

9             public static String getKind()   

10         {   

11             System.out.println("parent的getKind()方法被调用了");   

12             return kind;   

13         }   

14         //静态方法,返回年龄   

15         public static int getAge()   

16         {   

17             System.out.println("Parent的getAge()方法被调用了");   

18             return age;   

19         }   

20       

21         //实例方法,返回姓名   

22         public String getName()   

23         {   

24             System.out.println("Parent的getName()方法被调用了");   

25             return this.name;   

26         }   

27     }   

28     //子类   

29     class Child extends Parent   

30     {   

31         public static String kind="javastudy.extendsstudy.child";   

32         public int age=25;   

33         public String name="child";   

34       

35         //隐藏父类静态方法   

36         public static String getKind()   

37         {   

38             System.out.println("child的getkind()方法被调用了");   

39             return kind;   

40         }   

41            

42         //获取父类包名   

43         public static String getParentKind()   

44         {   

45             return Parent.kind;   

46         }   

47            

48         //覆盖父类实例方法   

49         public String getName()   

50         {   

51             System.out.println("child的getName()被调用了");   

52             return this.name;   

53         }   

54            

55         //获取父类名称   

56         public String getParentName()   

57         {   

58             return super.name;   

59         }   

60         /*  

61          *错误,实例方法不能覆盖父类的静态方法  

62         public int getAge()  

63         {  

64             return this.age;  

65         }  

66         */  

67     }   

68       

69       

70     class Test    

71     {   

72         public static void main(String[] args)    

73         {   

74             Child child=new Child();   

75             System.out.printf("子类名称:%s,年龄:%d,包  名:%s%n",child.name,child.age,child.kind);   

76             //输出:子类名称:child,年龄:25,包:javastudy.extendsstudy.child   

77       

78             //把child转换成parent对象   

79             Parent parent=child;   

80       

81             System.out.printf("转换后的名称:%s,年龄:%d,包名:%s%n",parent.name,parent.age,parent.kind);   

82             //输出:转换后的名称:Parent,年龄:50,包:javastudy.extendsstudy.parent   

83       

84             System.out.printf("子类访问父类被隐藏的实例变量name:%s%n",child.getParentName());   

85             //输出:子类访问父类被隐藏的实例变量name:Parent   

86                

87             System.out.printf("子类访问父类被隐藏的静态变量kind:%s",child.getParentKind());   

88             //输出:子类访问父类被隐藏的静态变量kind:javastudy.extendsstudy.parent   

89       

90             child.getName();   

91             //输出:child的getName()被调用了   

92       

93             //**************注意看这个方法,返回的还是子类的getName   

94             parent.getName();   

95             //输出:child的getName()被调用了   

96       

97             child.getKind();   

98             //输出:child的getkind()方法被调用了   

99       

100          parent.getKind();   

101          //输出:parent的getKind()方法被调用了   

102      }   

103  }  

//父类

class Parent

{

public static String kind="javastudy.extendsstudy.parent";

public static int age=50;

public String name="Parent";

 

//静态方法,返回包名

public static String getKind()

{

  System.out.println("parent的getKind()方法被调用了");

  return kind;

}

//静态方法,返回年龄

public static int getAge()

{

  System.out.println("Parent的getAge()方法被调用了");

  return age;

}

 

//实例方法,返回姓名

public String getName()

{

  System.out.println("Parent的getName()方法被调用了");

  return this.name;

}

}

//子类

class Child extends Parent

{

public static String kind="javastudy.extendsstudy.child";

public int age=25;

public String name="child";

 

//隐藏父类静态方法

public static String getKind()

{

  System.out.println("child的getkind()方法被调用了");

  return kind;

}

//获取父类包名

public static String getParentKind()

{

  return Parent.kind;

}

//覆盖父类实例方法

public String getName()

{

  System.out.println("child的getName()被调用了");

  return this.name;

}

//获取父类名称

public String getParentName()

{

  return super.name;

}

/*

 *错误,实例方法不能覆盖父类的静态方法

public int getAge()

{

  return this.age;

}

*/

}

 

 

class Test

{

public static void main(String[] args)

{

  Child child=new Child();

  System.out.printf("子类名称:%s,年龄:%d,包名:%s%n",child.name,child.age,child.kind);

  //输出:子类名称:child,年龄:25,包:javastudy.extendsstudy.child

 

  //把child转换成parent对象

  Parent parent=child;

 

  System.out.printf("转换后的名称:%s,年龄:%d,包名:%s%n",parent.name,parent.age,parent.kind);

  //输出:转换后的名称:Parent,年龄:50,包:javastudy.extendsstudy.parent

 

  System.out.printf("子类访问父类被隐藏的实例变量name:%s%n",child.getParentName());

  //输出:子类访问父类被隐藏的实例变量name:Parent

 

  System.out.printf("子类访问父类被隐藏的静态变量kind:%s",child.getParentKind());

  //输出:子类访问父类被隐藏的静态变量kind:javastudy.extendsstudy.parent

 

  child.getName();

  //输出:child的getName()被调用了

 

  //**************注意看这个方法,返回的还是子类的getName

  parent.getName();

  //输出:child的getName()被调用了

 

  child.getKind();

  //输出:child的getkind()方法被调用了

 

  parent.getKind();

  //输出:parent的getKind()方法被调用了

}

}

  1.同名的实例方法被覆盖 ,同名的静态方法被隐藏 ,child类的getName实例方法覆盖 了parent的getName实例方法,chind的getKind方法隐藏 了parent类的getKind方法

  2.隐藏 和覆盖 的区别在于,子类对象转换成父类对象后,能够访问父类被隐藏 的变量和方法,而不能访问父类被覆盖 的方法

  3.如果需要访问父类被隐藏 的实例变量,加上super就好了,比如访问父类的name,写上super.name就好了

 

 

四、Java强类型

代码:

Java代码

1         public class Type {  

2           

3             public static void main(String[] args) {  

4                 double i = 5.0;  

5                 double j = 1 / 4 + 3 / 4 + i + 12 / 6.0 + 3 / 4 + 1 / 4;  

6                 System.out.println(j);  

7             }  

8           

9         } 

 

 

结果:

7.0

 

分析:

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

 

五、假构造函数

代码:

Java代码

1         public class Constructor {  

2           

3             private int a, b, c;  

4           

5             public void Constructor() {  

6                 a = 3;  

7                 b = 5;  

8                 c = a + b;  

9             }  

10       

11         public void test() {  

12             System.out.println("The value of c :" + c);  

13         }   

14       

15         public static void main(String[] args) {  

16             Constructor c = new Constructor();  

17             c.test();  

18         }  

19     } 

 

 

结果:

The value of c :0

 

分析:

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

 

 

六、提前引用

代码:

Java代码

1         public class ForwardReference {  

2           

3             static int first = test();  

4             static int second = 2;  

5           

6             static int test() {  

7                 return second;  

8             }  

9           

10         public static void main(String[] args) {  

11             System.out.println("first = " + first);  

12         }  

13       

14     } 

 

 

结果:

first = 0

 

分析:

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

 

 

七、对象引用

代码:

Java代码

1         public class TestRef {  

2           

3             public static void main(String[] args) {  

4                 StringBuffer a = new StringBuffer("a");  

5                 StringBuffer b = new StringBuffer("b");  

6                 append(a, b);  

7                 System.out.println(a.toString() + "," + b.toString());  

8                 b = a;  

9                 System.out.println(a.toString() + "," + b.toString());  

10         }  

11       

12         public static void append(StringBuffer a, StringBuffer b) {  

13             a.append(b);  

14             b = a;  

15         }  

16     } 

 

 

结果:

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的引用生效,。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值