Java易错点3

532 阅读7分钟

这是我参与更文挑战的第10天,活动详情查看: 更文挑战

Java易错点2

如有理解错误的话,恳请大家指正!!!

数据类型转换

数据类型转换分为显示转换(强制类型转换)和隐式转换(自动转换)。

自动转换规则:

  • 数据类型兼容
  • 低级类型转高级类型(1、byte→short→int→long→float→double 2、char→int)

取值范围:

  • byte: -2^7 ~ 2^7-1,即-128 ~ 127。1字节。
  • short:-2^15 ~ 2^15-1,即-32768 ~ 32767。2字节。
  • 有符号int: -2^31 ~ 2^31-1,即-2147483648 ~ 2147483647。4字节。
  • 无符号int:0~2^32-1。
  • long:-2^63 ~ 2^63-1,即-9223372036854774808 ~ 9223372036854774807。8字节。
  • float:4字节。
  • double:8字节
  • char:2字节
  • boole

java9.png

上图的转换不会出现精度损失

示例代码 一

package com.wangscaler;

/**
 * @author wangscaler
 * @date 2021.06.09 14:37
 */
public class TestType {
    public static void main(String[] args) {
        long a = 2147483648;//此句有错误
        System.out.println(a);
        long b = 2147483648L;
        System.out.println(b);
    }
}

执行结果一

后两句的执行结果

-2147483648

解答一

在java中int的取值范围-2147483648 ~ 2147483647,而long a = 2147483648;中先执行代码右边的数字,默认认为是整合类型的字面量,但数值大小超过了整数的取值范围,所以报错。然而long类型是可以放2147483648的,那怎么赋值?在后边加L,就默认认为他是Long类型的字面量,此时再赋值就不会出错。

00000000 00000000 00000000 00000000 10000000 00000000 00000000 00000000 --->long 2148483648

强转为int类型即截取后32位1000 0000 0000 0000 0000 0000 0000 0000其中首位为符号位,1为负数,此二进制数为反码。

转为原码(除符号位外取反加1)1000 0000 0000 0000 0000 0000 0000 0000--->值为-2147483648

这也是为什么整数的范围是-2147483648 ~ 2147483647(负数比正数多一位),因为10000000 00000000 00000000 00000000不表示-0,而是表示-N,无论是+0还是-0都是0没必要浪费位置,所以只有0000 0000 0000 0000 0000 0000 0000 0000才表示0

示例代码二

package com.wangscaler;

/**
 * @author wangscaler
 * @date 2021.06.09 14:37
 */
public class TestType {
    public static void main(String[] args) {
         int a = 1999999999;
        float b = 1.0f;
        double c = 1.0d;
        System.out.println(a * b);
        System.out.println(a * c);
        short g = 222;
        System.out.println(g / 7);
        System.out.println(g / 7.0f);
        int h = 6789;
        System.out.println(h / 30);
        System.out.println(h / 30.);
        int i = 2147483642;
        i += 1.0f;
        System.out.println(i);
    }
}

执行结果二

2.0E9
1.999999999E9
31
31.714285
226
226.3
2147483647

解答二

a*b的过程:a会自动转为float型参与计算,此时会有精度损失。

a*c的过程:a会自动转为double型参与计算,不会出现精度的损失。

g/7:g会自动转成int类型参与计算,所以结果是整型的31。

g/7.0f:g会自动转为float类型参与计算,结果是浮点数,可以达到预期的结果。

h/30:整型除以整型的结果还是整型,所以是226。

h/30.:h会自动转换成doublet型参与计算,所以结果是double的。

i+=1.0f:i会先转为float型参与计算,计算完毕后,在转回int类型,会出现精度的损失。

总结

综上所述,在程序中,应避免隐式转换,该强制转换的强制转换。

类的使用

  • 重载:在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。

    重载规则:

    • 被重载的方法必须改变参数列表(参数个数或类型不一样);
    • 被重载的方法可以改变返回类型;
    • 被重载的方法可以改变访问修饰符;
    • 被重载的方法可以声明新的或更广的检查异常;
    • 方法能够在同一个类中或者在一个子类中被重载。
    • 无法以返回值类型作为重载函数的区分标准。
  • 重写:子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。

    方法的重写规则:

    • 参数列表与被重写方法的参数列表必须完全相同。
    • 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
    • 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
    • 父类的成员方法只能被它的子类重写。
    • 声明为 final 的方法不能被重写。
    • 声明为 static 的方法不能被重写,但是能够被再次声明。
    • 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
    • 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
    • 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
    • 构造方法不能被重写。
    • 如果不能继承一个类,则不能重写该类的方法。
  • 隐藏 :父类的静态方法和属性,在子类中出现,此时并不是重写,而是隐藏。调用时应该直接使用类名调用,避免混淆。

  • 遮蔽:当一个声明遮蔽了另一个声明时,简单名将引用到遮蔽声明中的实体(这里不做示例)

  • 遮掩:遮掩是指两个名字位于不同的名字空间的名字重用形式,名字空间包括:变量、包、方法或类型。

示例代码

People对象

package com.wangscaler;

public class People {
    public String word = "people";

    public static void eat() {
        System.out.println("吃饭");
    }

    public void sleep() {
        System.out.println("睡觉");
    }
}

Man

package com.wangscaler;

public class Man extends People {

    public String word = "man";

    @Override
    public void sleep() {
        System.out.println("打呼噜");
    }

    public static void eat() {
        System.out.println("光盘行动");
    }
}

main函数

package com.wangscaler;

/**
 * @author wangscaler
 * @date 2021.06.10 14:56
 */
public class TestClass {

    public static void main(String[] args) {
        People people = new People();
        People pman = new Man();
        Man man = new Man();
        people.sleep();
        pman.sleep();
        man.sleep();
        people.eat();
        pman.eat();
        Man.eat();
        man.eat();
        System.out.println(people.word);
        System.out.println(pman.word);
        System.out.println(man.word);
		
    }

}

执行结果

睡觉
打呼噜
打呼噜
吃饭
吃饭
光盘行动
光盘行动
people
people
man

总结

可以看到在man函数中eat上 是无法使用注解 @Override,因为他是隐藏,而不是重写。

为了避免混淆,应该直接使用类名调用,如上述的Man.eat();

至于遮蔽的情况如下:

String System;
System.out.println("a");

此时System就变成了我们自己定义的字符串,导致编译失败,所以我们定义变量的时候应该避免保留字和这些关键字。

关键字如下:

保留字包相关访问控制变量引用错误处理程序控制基本类型类方法变量修饰符
gotopackageprotectedvoidthrowsbreakbooleanabstract
constimportprivatethisthrowcontinuefalseclass1`
publicsupertryreturnfloatextends
catchdobytefinal
finallywhilecharimplements
ifdoubleinterface
断言elseintnative
assertforshortnew
instanceoflongstatic
casenullstrictfpp
switchtruesynchronized
defaulttransient
volatile