9.9日第一节课问题解答
一、从测试看不足
1、JAVA的基本运行单位是类还是方法?
答java的基本单位是类,因为没有一个方法可以独立存在。
2、类由什么组成?
答:类由两部分组成:成员变量和成员方法
3、变量的类型,相互之间可以转换吗?浮点数?
答:可以
(1)自动类型转换(无数据的丢失):byte->short->int->long->float->double,char->int->long->float->double。
(2)强制类型转换(会出现精度降低或溢出):如果希望自动转换中,箭头右边的类型转换成左边的类型,则必须进行强制类型转换。
强制类型转换的语法格式:(targetType)value,强制类型转换的运算符是圆括号(())。
(3)表达式类型的自动提升:(a)所有的byte类型,short类型和char类型将被提升到int类型。(b) 整个算术表达式的数据类型自动提升到表达式中最高等级操作数同样的类型。
(4)byte,short,char之间不会相互转换,boolen不参与转换
实线代表无精度损失,虚线代表有精度损失
4、String是一个数据类型吗?
答:java 中String 不是数据类型,而是个对象,是引用类型 ,基础类型与引用类型的区别是,基础类型只表示简单的字符或数字,引用类型可以是任何复杂的数据结构 ,基本类型仅表示简单的数据类型,引用类型可以表示复杂的数据类型,还可以操作这种数据类型的行为 。
java虚拟机处理基础类型与引用类型的方式是不一样的,对于基本类型,java虚拟机会为其分配数据类型实际占用的内存空间,而对于引用类型变量,他仅仅是一个指向堆区中某个实例的指针。
5、static代表的含义?
答:static:静态的,用于修饰成员(成员变量,成员方法)。
(1)被static所修饰的变量或者方法会储存在数据共享区。
(2)被static修饰后的成员变量只有一份。
(3)当成员被static修饰之后,就多了一种访问方式,除了可以被对象调用之外,还可以直接被类名调用,(类名.静态成员)。
二、动手动脑
仔细阅读示例: EnumTest.java,运行它,分析运行结果,你能得到什么结论?你掌握了枚举类型的基本用法了吗?
public class EnumTest {
public static void main(String[] args) {
Size s=Size.SMALL;
Size t=Size.LARGE;
System.out.println(s==t);
System.out.println(s.getClass().isPrimitive());
Size u=Size.valueOf("SMALL");
System.out.println(s==u);
for(Size value:Size.values()){
System.out.println(value);
}
}
}
enum Size{SMALL,MEDIUM,LARGE};
运行结果:
false
false
true
SMALL
MEDIUM
LARGE
结论:枚举类型是引用类型,枚举不属于原始数据类型,它的每个具体值都引用一个特定的对象。相同的值则引用同一个对象。可以使用“==”和equals()方法直接比对枚举变量的值,换句话说,对于枚举类型的变量,“==”和equals()方法执行的结果是等价的。
枚举类型的基本用法:(1)枚举类型是常量,不是变量,不能用赋值语句再对他进行赋值。(2)如果打算自定义自己的方法,那么必须在enum实例序列的最后添加一个分号。
三、动手实验
请运行以下代码(TestDouble.java)
public class TestDouble {
public static void main(String args[]) {
System.out.println("0.05 + 0.01 = " + (0.05 + 0.01));
System.out.println("1.0 - 0.42 = " + (1.0 - 0.42));
System.out.println("4.015 * 100 = " + (4.015 * 100));
System.out.println("123.3 / 100 = " + (123.3 / 100));
}
}
运行结果:
0.05 + 0.01 = 0.060000000000000005
1.0 - 0.42 = 0.5800000000000001
4.015 * 100 = 401.49999999999994
123.3 / 100 = 1.2329999999999999
结论:
使用double类型的数值进行计算,其结果是不精确的。
问题
(1)为什么double类型的数值进行运算得不到“数学上精确”的结果?
这个涉及到二进制与十进制的转换问题。
double类型的数值占用64bit,即64个二进制数,除去最高位表示正负符号的位,在最低位上一定会与实际数据存在误差(除非实际数据恰好是2的n次方)。
举个例子来说,比如要用4bit来表示小数3.26,从高到低位依次对应2的1,0,-1,-2次幂,根据最上面的分析,应当在二进制数11.01(对应十进制的3.25)和11.10(对应十进制的3.5)之间选择。
简单来说就是我们给出的数值,在大多数情况下需要比64bit更多的位数才能准确表示出来(甚至是需要无穷多位),而double类型的数值只有64bit,后面舍去的位数一定会带来误差,无法得到“数学上精确”的结果。
(2)怎样处理精度损失?
解决方法——使用BigDecimal类
例:
import java.math.BigDecimal;
public class TestBigDecimal
{
public static void main(String[] args)
{
BigDecimal f1 = new BigDecimal("0.05");
BigDecimal f2 = BigDecimal.valueOf(0.01);
BigDecimal f3 = new BigDecimal(0.05);
System.out.println("下面使用String作为BigDecimal构造器参数的计算结果:");
System.out.println("0.05 + 0.01 = " + f1.add(f2));
System.out.println("0.05 - 0.01 = " + f1.subtract(f2));
System.out.println("0.05 * 0.01 = " + f1.multiply(f2));
System.out.println("0.05 / 0.01 = " + f1.divide(f2));
System.out.println("下面使用double作为BigDecimal构造器参数的计算结果:");
System.out.println("0.05 + 0.01 = " + f3.add(f2));
System.out.println("0.05 - 0.01 = " + f3.subtract(f2));
System.out.println("0.05 * 0.01 = " + f3.multiply(f2));
System.out.println("0.05 / 0.01 = " + f3.divide(f2));
}
}
运行结果:
下面使用String作为BigDecimal构造器参数的计算结果:
0.05 + 0.01 = 0.06
0.05 - 0.01 = 0.04
0.05 * 0.01 = 0.0005
0.05 / 0.01 = 5
下面使用double作为BigDecimal构造器参数的计算结果:
0.05 + 0.01 = 0.06000000000000000277555756156289135105907917022705078125
0.05 - 0.01 = 0.04000000000000000277555756156289135105907917022705078125
0.05 * 0.01 = 0.0005000000000000000277555756156289135105907917022705078125
0.05 / 0.01 = 5.000000000000000277555756156289135105907917022705078125
(3)注意:在构建BigDecimal对象时应使用字符串而不是double数值,否则,仍有可能引发计算精度问题。为什么会这样呢?
double+、-不能准确的代表BigDecimal(String) 16位有效数以上的数字在使用BigDecimal时,应用*、/构造器创建对象才有意义。
BigDecimal所创建的对象不能使用传统的等算术运算符直接对其对象进行数学运算,而必须调用相对应的方法。方法中的参数必须是BigDecimal的对象。
四、动手动脑
1)以下代码的输出结果是什么?
int X=100;
int Y=200;
System.out.println("X+Y="+X+Y);
System.out.println(X+Y+"=X+Y");
结果:
X+Y=100200
300=X+Y
2)为什么会有这样的输出结果?
第一个输出+号是连接符,第二个输出+号是运算符,“+”表示是运算符还是连接符,应该取决于+左右两边的数据类型。
五、原码补码反码
原码:原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值。例如5的原码:0000 0101 负5的原码:1000 0101
反码:正数:正数的反码与原码相同。负数:负数的反码,符号位为“1”,数值部分按位取反。可见如果一个反码表示的是负数, 人脑无法直观的看出来它的数值. 通常要将其转换成原码再计算。例如5的反码:0000 0101 负5的补码:1111 1010
补码:正数的补码就是其本身。负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)。对于负数, 补码表示方式也是人脑无法直观看出其数值的. 通常也需要转换成原码在计算其数值。
关键点:
(1)二进制的最高位是符号位:0表示正数,1表示负数 (2)正数的原码,反码,补码都相同
(3)负数的反码=原码符号位不变,其他位取反 (4)负数的补码=他的反码+1 负数的反码=他的补码+1
(5)0的反码,补码都是0 (6)java没有无符号数,java中的数都是有符号的
(7)在计算机运算时,都是以补码的方式运算的 (8)当我们看运算结果的时候,要看他的原码