疑问一:0.01+0.09的结果?
public class MathTest{ public static void main(String[]args){ double a = 0.01, b = 0.09; System.out.print(a+b); } }
打印的结果为什么是 0.09999999999999999 而非 0.1,发生精度损失, 无论是 double还是 float 都会遇到这个问题,就拿 float 说话, float 的 0.1 二进制形式是 00111101110011001100110011001101,根据符号位换算为 10 进制表达的值精确应该是这样计算110011001100110011001101 乘以 2 的负 27 次方,实际值是0.100000001490116119384765625 这样就产生了实际误差。
例
public float getLeftMoney() throws Exception { // TODO Auto-generated method stub float m = new MoneyDaoImpl().CountAllMoney(); float c = new DetailsDaoImpl().countDetailsMoney(); flaot less = m-c; System.out.println(m); System.out.println(c); System.out.println(less); return less; }
解决办法:
public float getLeftMoney() throws Exception { // TODO Auto-generated method stub float m = new MoneyDaoImpl().CountAllMoney(); float c = new DetailsDaoImpl().countDetailsMoney(); BigDecimal b1 = new BigDecimal(Float.toString(m)); BigDecimal b2 = new BigDecimal(Float.toString(c)); System.out.println(m); System.out.println(c); Float less = b1.subtract(b2).floatValue(); System.out.println(less); return less; }
疑问二:Null 属于什么类型?
解:
- Java 中 4 个系统定义的常量: NaN 非数值、 lnf 无穷大、 -lnf 负无穷大、 null 空。
- null 用来标识一个不确定的对象,可以赋给引用型变量,不可以赋给基本类型变量
- Object 是已知的存在所有类的超类,但不包含不存在的类,也不包含 null。基本数据类型不是类不包含在 Object 中。
疑问三:堆内存与栈内存的区别?
解:
①heep (堆)是一个可动态申请的内存空间,一般所有创建的对象都放在这里。stack (栈)是一个先进后出的数据结构,通常用于保存方法(函数)中的参数,局部变量。stack (栈)的空间小,但速度比较快, 存放对象的引用,通过栈中的地址索引可以找到堆中的对象。
②栈( java stacks)也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是 java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
堆( java Heap)是 java 虚拟机所管理的内存中最大的一块。 Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
疑问四:什么是实例变量?什么是类变量
解:
①类变量也叫静态变量,也就是在变量前加了 static 的变量,类变量在创建对象前就已经在内存中存在,随类的创建而创建;实例变量也叫对象变量,即没加 static 的变量。
②类变量是所有对象共有,其中一个对象将它值改变,其他对象得到的就是改变后的结果;而实例变量则属对象私有,某一个对象将其值改变,不影响其他对象。
③所有的实例对象都共用一个类变量,内存中只有一处空间是放这个类变量值的。因此,如果一个对象把类变量值改了,另外一个对象再取类变量值就是改过之后的了。在创建实例对象的时候,内存中会为每一个实例对象的每一个非静态成员变量开辟一段内存空间,用来存储这个对象所有的非静态成员变量值,即使两个不同的实例对象是属于同一个 class 类,但是它们的同名非静态成员变量在内存中占用的空间是不同的。
public class A{ static int a = 0; //类变量 private int b = 0; //实例变量 }
Public class B{ public void main (String[] args){ A a1 = new A(); A a2 = new A(); a1.a = 3; // 等同于 A.a = 3; a1.b = 4 ; System.out.println(a2.a); //结果为 3 //类变量是针对所有对象的,所以 a1 改变 a, a2 的 a 也改变 System.out.println(a2.b); //结果为 0 //实例只改变自身的,所以 a1 对象的 b 改变,不影响对象 a2 的 b 变量 } }
疑问五:重载和重写的区别?
解:
①重载 Overload 表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数或类型不同)。
条件: A)方法名必须相同(大小写必须一致才算是相同) B)参数列表不同,参数列表不同又分为:参数列表的个数不同;参数列表的排列顺序不同;参数列表的数据类型不同。必须同时满足条件 A 和 B 才叫方法的重载。
注意:
- 在使用重载时只能通过不同的参数样式。例如,不同的参数类型,不同的参数个数,不同的参数顺序(当然,同一方法内的几个参数类型必须不一样,例如可以是fun(int,float),但是不能为 fun(int,int));
- 不能通过访问权限、返回类型、抛出的异常进行重载;
- 方法的异常类型和数目不会对重载造成影响;
- 对于继承来说,如果某一方法在父类中是访问权限是 priavte,那么就不能在子类对其进行重载,如果定义的话,也只是定义了一个新方法,而不会达到重载的效果。
②重写 Override 表示子类中的方法可以与父类中的某个方法的名称和参数完全相同,通过子类创建的实例对象调用这个方法时,将调用子类中的定义方法,这相当于把父类中定义的那个完全相同的方法给覆盖了,这也是面向对象编程的多态性的一种表现。子类覆盖父类的方法时,只能比父类抛出更少的异常,或者是抛出父类抛出的异常的子异常,因为子类可以解决父类的一些问题,不能比父类有更多的问题。子类方法的访问权限只能比父类的更大,不能更小。如果父类的方法是 private 类型,那么,子类则不存在覆盖的限制,相当于子类中增加了一个全新的方法。
在覆盖要注意以下的几点: A) 覆盖的方法的标志必须要和被覆盖的方法的标志完全匹配,才能达到覆盖的效果; B) 覆盖的方法的返回值必须和被覆盖的方法的返回一致; C)覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类; D) 被覆盖的方法不能为 private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。