【Java 基础】基础总结(一)

摘要:本系列基础总结的文章主要是自己在写 Java 过程中对问题的整理和归纳,以便于自己不断的回顾和总结相关知识。本篇文章主要汇总了Java 语言一次编译,到处运行的跨平台原理, JVM、JRE 和 JDK 的关系,Java 的几种数据类型汇总以及在工作中遇到浮点数精度损失的问题,最终给出了使用 BigDecimal 中参数为 String 类型的构造方法来避免精度损失。

Java语言跨平台原理

所谓跨平台性,是指Java语言编写的程序,一次编译后,可以在多个系统平台上运行(注意不是能在所有的系统平台上运行,关键在于该系统平台上是否安装相应的虚拟机)。

Java程序并非是直接运行的,它通过Java虚拟机在系统平台上运行的。Java程序的运行分为两步,先编译再解释执行,Java编译器将Java源程序编译成与平台无关的字节码文件(.class文件),然后由Java虚拟机(JVM)对字节码文件解释执行。因此在不同的操作系统下,只需安装不同的Java虚拟机即可实现Java程序的跨平台。

Java运行过程:
Java程序的运行分为两步:先编译再解释执行,实际上为一次编译,到处运行。
② 编译器的作用:通过编译器将Java源程序编译成Java字节码文件(.class)(字节码文件采用结构中立的中间文件格式)。
③ 虚拟机的作用:通过不同的虚拟机将.class字节码文件解释为对应机器语言并执行。

Java可以跨所有的平台吗?

只有提供并安装了相应的虚拟机,就可以跨平台。

虚拟机和解释器的关系?

解释器是虚拟机的一个重要组成部分。Java是先编译成字节码再执行。Java虚拟机就是字节码运行的环境,通过Java虚拟机可以实现平台无关性,而Java解释器是将字节码解释为操作系统可以理解的原语执行。 可以说,Java解释器是虚拟机的一个实现部分。

JVM、JRE和JDK的关系

JVM(Java Virtual Machine),Java虚拟机。
JRE(Java Runtime Environment),Java运行环境,包含了JVMJava的核心类库(Java API) 。
JDK(Java Development Kit)称为Java开发工具,包含了JRE和开发工具。

三者关系:
JVM + 核心类库(Java API) = JRE
JRE + java开发工具(javac.exe/jar.exe) = JDK
JVM不能单独搞定class的执行,解释class的时候JVM需要调用解释所需要的类库lib。在JDK下面的的JRE目录里面有两个文件夹binlib,在这里可以认为bin里的就是JVMlib中则是JVM工作所需要的类库,而JVMlib和起来就称为JRE

Java的数据类型

Java是一个强类型语言,Java中的数据必须明确数据类型。在Java中的数据类型包括基本数据类型和引用数据类型两种。

Java中的基本数据类型:
六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。

数据类型 别名 关键字 占用内存(byte)(bit) 取值范围 备注
整数型 字节型 byte 1个字节,8位 最小值是 -128(-2^7)- 最大值是 127(2^7-1) 默认值为0,byte 类型用在大型数组中节约空间,主要代替整数,因为 byte 变量占用的空间只有 int 类型的四分之一。
整数型 短整型 short 2个字节,16位 最小值是 -32768(-2^15)- 最大值是 32767(2^15 - 1) 默认值为0,一个short变量是int型变量所占空间的二分之一。
整数型 整型 int 4个字节,32位 最小值是 -2,147,483,648(-2^31 - 最大值是 2,147,483,647(2^31 - 1) 默认值为0,一般地整型变量默认为 int 类型。
整数型 长整型 long 8个字节,64位 最小值是-2^63 - 最大值是 2^63 -1 默认值是0L,这种类型主要使用在需要比较大整数的系统上。
浮点型 单精度 float 4个字节,32位 默认值是 0.0f,浮点数不能用来表示精确的值,如货币;float 在储存大型浮点数组的时候可节省内存空间。
浮点型 双精度 double 8个字节,64位 默认值是 0.0d,double类型同样不能表示精确的值,如货币;浮点数的默认类型为double类型。
字符型 字符串型 char 2个字节,16位 最小值是 \u0000(即为0)- 最大值是 \uffff(即为65,535) char 数据类型可以储存任何字符。
布尔类型 布尔型 boolean 1个字节,8位 true 和 false boolean类型不能与其他基本数据类型相互转换

注:

java中整数默认是int类型,浮点数默认是double类型。
定义long类型的变量时,需要在整数的后面加L(大小写均可,建议大写)。因为整数默认是int类型,整数太大可能超出int范围。
定义float类型的变量时,需要在小数的后面加F(大小写均可,建议大写)。因为浮点数的默认类型是doubledouble的取值范围是大于float的,类型不兼容。

java常见的引用类型:

类(class)、接口类型(interface)、数组类型(array)、枚举类型、注解类型。

常见的类class引用:ObjectStringDateVoid(不可实例化的占位符类)。
常见的接口interface引用:List<E>Map<K,V>
数组array:存储在一个连续的内存块中的相同数据类型(引用数据类型)的元素集合,Java中数组必先初始化后才能使用,初始化就是给数组元素分配内存,并为每个元素赋初始值。

数组的定义:
第一种方式:类型[] 数组名; 如int[] nums;
第二种方式:类型数组名[]; 如int nums[]

基本数据类型和引用数据类型的区别:

引用类型在堆里,基本类型在栈里。

基本数据类型在被创建时,在栈上给其划分一块内存,将数值直接存储在栈上。
引用数据类型在被创建时,首先要在栈上给其引用(句柄)分配一块内存,而对象的具体信息都存储在堆内存上,然后由栈上面的引用指向堆中对象的地址。

总结:

Java中的数据类型分为基本数据类型和引用数据类型。

基本数据类型:

包括4类8种,分别是4个整数型,2个浮点型,1个字符类型,1个布尔类型:

  • 4个整数型:字节型(Byte,1个字节),短整型(short,2个字节),整型(int,4个字节,默认类型),长整型(Long,8个字节)
  • 2个浮点型:单精度(float,4个字节),双精度(double,8个字节,默认类型)
  • 1个字符型:字符型(char,2个字节)
  • 1个布尔型:布尔型(boolean,1个字节)

引用数据类型:

包括类(class)、接口类型(interface)、数组类型(array)、枚举类型、注解类型。

  • 常见的类 class 引用:Object、String(字符串类型)、Date、Void(不可实例化的占位符类)
  • 常见的接口 interface 引用:List、Map<K,V>
  • 常见的数组类型:类型数组名[]; 如int nums[]
  • 常见的枚举类型:枚举是类,属于引用类型。
public enum Color {
RED, GREEN, BLANK, YELLOW 
}
  • 常见的注解类型:如下所示
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Page {
    /**
     * 用于绑定的请求参数名字
     */
    String value() default "";
}

区别:

  • 引用类型一般都是通过new关键字来创建对象,然后把这个对象赋予给相应的变量,基本类型就是直接赋值就行。
  • 引用类型创建在堆内存里,基本类型的创建在栈内存里。

精度损失

场景还原:

工作中使用浮点类型的数据,在进行除以100的时候,发现精度损失。原浮点数据为0.03,当除以100之后变为0.0299999999...,导致查询范围无法精确至0.0003。

出现原因:

对于 Java 的 float 和 double 类型,都存在精度损失的问题,精度损失产生的原因在于 Java 的数据存储采用的都是二进制形式,二进制不能准确的表示1/10等分数,只能无限趋近。

解决方案:

使用 BigDecimal 中参数为 String 类型的构造方法可以避免精度损失。

Float commissionPerMin;
Float commissionPerMax;
BigDecimal  commissionPerMinBig =  new BigDecimal(Float.toString(commissionPerMin)).divide(BigDecimal.valueOf(100));
BigDecimal  commissionPerMaxBig = new BigDecimal(Float.toString(commissionPerMax)).divide(BigDecimal.valueOf(100));

通过案例分析对比 float,double,BigDecimal 的精度:

public static void main(String[] args){
    float a = (float) 1.0;
    float b = (float) 0.965;
    double a1 = 1.0;
    double b1 = 0.965;
    BigDecimal a2 = new BigDecimal(a1);
    BigDecimal b2 = new BigDecimal(b1);
    BigDecimal a3 = new BigDecimal(new String("1.0"));
    BigDecimal b3 = new BigDecimal(new String("0.965"));

    System.out.println("float:"+(a-b));
    System.out.println("double:"+(a1-b1));
    System.out.println("BigDecimal use Double:"+a2.subtract(b2));
    System.out.println("BigDecimal use String:"+a3.subtract(b3));
}

运行结果如下:

float:0.035000026
double:0.03500000000000003
BigDecimal use Double:0.03500000000000003108624468950438313186168670654296875
BigDecimal use String:0.035

总结:

  • float 的精度损失最严重,然后依次是 double ,紧接着是使用参数类型为 double 的 BigDecimal。
  • 使用 BigDecimal 中参数为 String 类型的构造方法可以避免精度损失。

注:使用 BigDecimal(double val) 构造函数时仍会存在精度丢失问题,建议使用 BigDecimal(String val) 。

相关推荐

微信扫一扫,分享到朋友圈

【Java 基础】基础总结(一)
返回顶部

显示

忘记密码?

显示

显示

获取验证码

Close