常用类型_数字..

1.基本类型的缺陷

1.缺陷一(不能表示不存在的值)
现在又这么一个需求:
定义一个int类型数组 数组元素表示每一天的盈利金额 其中有一种情况是没开店的情况 很多人会误以为没开店时盈利金额是0 其实不然 还有一种情况是开店的同时盈利金额为0 如果这两种情况都为0的话 就会引起歧义 所以前者不能用0表示 但是在基本类型中又不能够表示不存在的值 我们这时候有一种解决方案就是将基本类型数组改成引用类型数组 即String类型数组

public class Main {
    public static void main(String[] args){
        int[] arrays = new int[]{100, 0, -100};// 无法表示不存在的值
        String[] arrays2 = new String[]{"100", "0", "-100", null};// 在String等引用类型中 可以用null表示不存在的值
    }
}

但是其实用字符串数组的方式去实现这个需求是不太合适的 因为获取到的数字是可以进行算数运算的 但是显然取出字符串元素以后还要转换成int才能够进行算术运算 有没有一种更优化的方式 答案是有的 就是将基本类型包装成引用类型

public class IntObject {
	private int value;// 这是该包装类型对应的基本类型
	public IntObject(int value){
		this.value = value;  
	}
	public int getValue(){
		return value;  
	}
	public void setValue(int value){
		this.value = value;  
	}
}
public class Main {
    public static void main(String[] args){
        IntObject[] arrays = new IntObject[]{new IntObject(100), new IntObject(-100), new IntObject(0), null};
    }
}

2.缺陷二(不能通过面向对象的方式去操作基本类型)
比如我们不能通过基本类型去调用方法
但是我们可以依赖基本类型对应的包装类型去解决这个缺陷

public class IntObject {
	private int value;// 这是该包装类型对应的基本类型
	public IntObject(int value){
		this.value = value;  
	}
	public int getValue(){
		return value;  
	}
	public void setValue(int value){
		this.value = value;  
	}
}
public class Main {
    public static void main(String[] args){
        IntObject[] arrays = new IntObject[]{new IntObject(100), new IntObject(-100), new IntObject(0), null};
        System.out.println(arrays[0].getValue());
    }
}

3.缺陷三(当方法参数是引用类型的时候 我们不能够传递基本类型 注意这是在没有编译器的自动装箱的前提下 这个结论是成立的)

对于上述三个缺陷 其实有着这共同的解决方案 那就是将基本类型包装成引用类型

2.包装类

其实Java种已经内置了包装类(都在java.lang下) 不用我们手动去实现包装类了
常见的基本类型和包装类类型的对应情况如下所示:
byte - Byte
short - Short
char - Character
int - Integer
long - Long
float - Float
double - Double
boolean - Boolean
其中数字类型的包装类都继承自java.lang.Number
所以说定义不存在的盈利金额的需求可以这样实现

public class Main {
    public static void main(String[] args){
        Integer[] arrays = new Integer[]{new Integer(100), new Integer(-100), new Integer(0), null};
        System.out.println(arrays[0].intValue());
    }
}

3.自动装箱、自动拆箱操作

何为自动装箱 何又为自动拆箱呢 具体解释一下
所谓自动装箱 就是Java会自动将基本类型通过包装类类型.valueOf()转换为对应的包装类类型
所谓自动拆箱 就是Java会自动将包装类类型通过(基本类型)xxxValue转换为对应的基本类型

值得注意的是 valueOf方法的返回值是包装类类型对象

1.实例一

这个行为中 首先int类型通过自动调用valueOf方法转换为Integer类型 接着由于引用类型继承自Object类的缘故 在多态中父类引用指向子类对象的行为是允许的 所以是可以直接将Integer对象赋值给Object引用的

public class Main {
    public static void main(String[] args){
        Object o = 1;
    }
}

2.实例二

这个行为中 首先Java通过自动调用valueOf方法将int转换为Integer 接着Java在通过自动调用intValue将Integer转换为int参与到取模运算中 所以这就是为什么第一个需求用包装类类型优于字符串类型的地方 因为他的算术运算方便

public class Main {
    public static void main(String[] args){
        Integer i = 10;
        System.out.println(i % 2);
    }
}

3.使用注意

基本类型数组和包装类型数组之间是不存在自动装箱和拆箱的操作

public class Main {
    public static void main(String[] args){
        Integer[] arrays1 = new Integer[]{11, 22, 33};
        // int[] arrays2 = arrays1;// error
        int[] arrays2 = new int[arrays1.length];
        for(int i = 0; i < arrays2.length; ++i){
        	arrays2[i] = arrays1[i];
        }
    }
}

但是你可以通过以上的操作完成包装类数组对基本类型数组的赋值操作 同理反过来也是一样的 因为编译器不能自动的将包装类和基本类型之间的赋值操作转换为以上的写法 所以才不允许包装类和基本类型的自动装箱和拆箱

4.包装类的判等

包装类的判等操作 不要使用==、!= 而应该使用equals方法
我们都知道 对于引用类型来说 = =比较的是地址值 而真正比较包装类对应的基本类型值实际上是用的是equals方法

public class Main {
    public static void main(String[] args){
        Integer i1 = 88;
        Integer i2 = 88;
        Integer i3 = 888;
        Integer i4 = 888;
        // 不要使用==对包装类型进行判等操作
        System.out.println(i1 == i2);// true
        System.out.println(i3 == i4);// false
        // 应该使用equals方法进行判等操作
        System.out.println(i1.equals(i2));// true
        System.out.println(i3.equals(i4));// true
    }
}

从上述结果可以得知:
i1 == i2的结果超出了我们的预知 这是因为我们之前对于valueOf方法的实现得理解有偏差 之前我们说过valueOf等价于包装类对象 其实不完全正确 这个结论是在不满足前置条件的前提下成立的
那么前置条件是什么呢 其实就是需要查找当前值是否位于IntegerCache类的缓存范围内 如果存在 直接从缓存数组中取出指定值的对象 如果不存在 就由此创建一个新对象并返回
这个缓存范围为[-128, 127] 为什么要定义一个缓存范围呢 原因在于这些比较小的数字比较常用 所以将其储存起来以备使用

总之valueOf这个方法的实现 总结起来就是:
会优先去IntegerCache缓存中查找Integer对象 如果没有找到 再自行创建一个对象返回

5.Math

在java.lang.Math中提供了常见的数字计算的功能

public class Main {
    public static void main(String[] args){
        // 使用E和PI
        System.out.println(Math.E);// 2.71……
        System.out.println(Math.PI);// 3.14……
        // 绝对值功能
        System.out.println(Math.abs(-100));// 100
        // 最大值和最小值功能
        System.out.println(Math.max(1, 2));// 2
        System.out.println(Math.min(1, 2));// 1
        // 向下和向上取整功能
        System.out.println(Math.floor(1.1));// 1.0
        System.out.println(Math.ceil(1.1));// 2.0
        // 四舍五入功能
        System.out.println(Math.round(5.5));// 6
        // 指数运算功能
        System.out.println(Math.pow(2, 4));// 16.0
        // 开平方运算功能
        System.out.println(Math.sqrt(16));// 4.0
        // 求e的n次方功能
        System.out.println(Math.exp(2));// 7.38……
        // lna称之为自然对数运算 
        System.out.println(Math.log(8));// 求的是ln8的结果 2.07……
        // 角度转弧度功能
        double degree = 90;
        double radian = Math.toRadians(degree);
        System.out.println(radian);// 1.57…… 90--π/2--1.57……
        // 三角函数 参数要求弧度制
        System.out.println(Math.sin(Math.toRadians(90)));// 1.0
    }
}

6.Random

如果我们想要生成随机数的话 那么我们可以有两种渠道:一种是调用Math类中的random方法 一种是调用更为方便的util包中的Random类

import java.util.Random;
public class Main {
    public static void main(String[] args){
        // 生成[0, 1)中的任意数
    	System.out.println(Math.random());
    	// 生成指定类型范围内的任意数
    	System.out.println(new Random().nextInt());
    }
}

除了nextInt 还有nextDouble、nextFloat等操作
现在有个需求 就是生成指定范围内的随机整数
比如[10, 99]范围内的整数

import java.util.Random;
public class Main {
    public static void main(String[] args){
        // 第一种方式
        int i1 = 10 + (int)(Math.random() * 90);
        // 第二种方式
        int i2 = 10 + new Random().nextInt(90);
        System.out.println(i1);
        System.out.println(i2);
    }
}

还有个需求 就是生成四位的大写字母验证码 我们都知道字符的本质就是ASCII码值 其实就是整数 大写字母对应的整数是65-90(值得注意的是Z和a的ASCII码值并不相连)

import java.util.Random;
public class Main {
    public static void main(String[] args){
        // 第一种方式
        for(int i = 0; i < 4; ++i){
        	char ch = (char)(65 + (int)(Math.random() * 26));
        	System.out.print(ch);
        }
        System.out.println();
        // 第二种方式
        for(int i = 0; i < 4; ++i){
        	int ch = new Random().nextInt(26) + 65;
        	System.out.print((char)ch);
        }
    }
}

7.UUID

标题所示单词 意思为通用唯一标示符
他的作用就是可以让分布式系统中的所有元素都有唯一的标识符(现在就是有这么一个需求 就是我要添加用户进入数据库中 那么肯定需要通过一个用户标识符进行区分 如果只是在一台主机上操作的话 那么分配用户标识符是很容易进行的
但是如果你针对的是一个分布式系统的主机 也就是可以对分布式系统中的任意一台主机进行用户添加操作 那么如何才能保证每一台主机所添加的用户对应的标识符是不一样的呢 这就不好办 所以我们需要引进通用唯一标示符这个概念 就可以解决这个问题了)

我们也可以通过java.util.UUID类调用randomUUID生成一个随机的UUID UUID是由32位16进制位组成的 即128位二进制位 128bit

import java.util.UUID;
public class Main {
    public static void main(String[] args){
        System.out.println(UUID.randomUUID());
    }
}

8.数字格式化

我们可以通过System.out.printf()或者System.out.format()输出格式化字符串(这是因为printf函数内部调用的就是format函数作为返回值的)
我们也可以通过String.format()定义格式化字符串

而且在格式化字符串中 存在几个标记 他们的意思我们需要清除一下:
08 – 表示字符宽度为8 并且前面用0补齐
±-显示符号(正号为+ 负号为-)
, – 作为千位分隔符存在
负号 – 左对齐 默认是右对齐
.3 – 保留三位小数
10.3 – 字符宽度为10 保留3位小数

public class Main {
    public static void main(String[] args){
        long l = 111111;
        System.out.printf("%d%n", l);// 111111
        System.out.printf("%08d%n", l);// 00111111
        System.out.printf("%+8d%n", l);// +111111
        System.out.printf("%-8d%n", l);// 111111
        System.out.printf("%,8d%n", l);// 111,111
        System.out.printf("%+,8d%n", l);// +111,111
        double pi = Math.PI;
        System.out.printf("%f%n", pi);// 3.14……
        System.out.printf("%.3f%n", pi);// 3.142
        System.out.printf("%8.3f%n", pi);// 3.142
        System.out.printf("%08.3f%n", pi);// 0003.142
        System.out.printf("%-8.3f%n", pi);// 3.142
    }
}

9.DecimalFormat

使用java.text.DecimalFormat可以控制数字输出的格式

import java.text.DecimalFormat;
public class Main {
    public static void main(String[] args){
        customFormat("#####.##", 123456.33);
        customFormat("###,###.##", 123456.33);
        customFormat("00000000.00", 123456.33);
        customFormat("$###,###.##", 123456.33);
    }
    // 定义一个方法 用于定制数字格式 并且将指定的数字按照指定格式进行输出
    public static void customFormat(String format, double value){
        DecimalFormat df = new DecimalFormat(format);
        System.out.println(df.format(value));
    }
}

10.字符串转数字

存在两种方式 一种是使用包装类的valueOf方法 一种则是使用包装类的parseXX方法 这两种方法有着本质的区别就是前者返回的是包装类 后者返回的是基本类型
而且虽然Java可以自动进行装箱拆箱操作 但是尽量选择适合的变量进行储存 因为如果多了自动装箱和拆箱的操作的话 将会增加性能的消耗

public class Main {
    public static void main(String[] args) {
        Integer i1 = Integer.valueOf("12");
        System.out.println(i1);// 12
        int i2 = Integer.parseInt("13");
        System.out.println(i2);// 13
        int i3 = Integer.parseInt("FF", 16);// 以十六进制的形式解析要转换的字符串
        System.out.println(i3);// 255
        // 同理 既然支持字符串转换成整数的话 然后也支持字符串转换为浮点数了
        Double d1 = Double.valueOf("3.14");
        System.out.println(d1);// 3.14
        double d2 = Double.parseDouble("3.14");
        System.out.println(d2);// 3.14
    }
}

11.数字转换为字符串

存在两种方式 一种方式为String.valueOf方法 一种是包装类的toString方法

public class Main {
    public static void main(String[] args) {
        String s1 = String.valueOf(123);
        System.out.println(s1);// 123
        String s2 = Integer.toString(123);
        System.out.println(s2);// 123
        String s3 = Double.toString(3.14);
        System.out.println(s3);// 3.14
        String s4 = Integer.toString(255, 16);// 以十六进制的方式进行对数字进行解析
        System.out.println(s4);// ff
    }
}

12.高精度计算

float、double这些浮点类型的变量储存的都是小数的近似值 而不是精确值 因此不适合拿来进行高精度计算(针对部分的无限位数的小数来说 由于这些浮点类型变量能够储存的位数是有限的 所以他们遇到这些小数就会采取截断的措施 那么获取到的小数就是近似值 而不是真实值)

如何进行十进制小数和二进制小数之间的转换?
十进制转换为二进制:不断乘二取整

public class Main {
    public static void main(String[] args) {
        System.out.println(0.7 * 0.7);// 实际上计算的是0.6…… * 0.6……
    }
}

从上述这个案例中 我们可以发现浮点型字面量会进行截断操作 获取无限位数小数的近似值来进行乘法运算

如果我们想要进行高精度计算的话 就要使用java.math.BigDecimal

13.BigDecimal

BigDecimal的本质是将参数中的每一个字符作为字符串数组中的元素进行储存
比如你如果传递的是"0.7" 那么他储存的结果就是[‘0’, ‘.’, ‘7’] 但是如果你传递的是0.7的话 那么由于其是浮点型字面量的缘故 导致它实际储存在字符串数组中的字符是[‘0’, ‘.’, ‘6’……] 这是因为浮点数会对0.7的二进制形式进行截取操作 得到的十进制就是0.6……
所以我们要使用字符串去初始化BigDecimal
他对于参数的储存是储存在字符串数组中 然后参与运算的话 是逐位进行运算的
比如乘法运算:0.70.7 本质上是逐位相乘相加的操作 他会先那7和7相乘得到49 然后再根据小数点后面的数字位数来决定其在积中的位置
再比如12
12 如果是逐位计算的话 就是22 + 210 + 102 + 1010 = 144

import java.math.BigDecimal;
public class Main {
    public static void main(String[] args) {
        BigDecimal bd1 = new BigDecimal("0.7");
        BigDecimal bd2 = new BigDecimal("0.7");
        System.out.println(bd1.add(bd2));// 1.4
        System.out.println(bd1.subtract(bd2));// 0.0
        System.out.println(bd1.multiply(bd2));// 0.49
        System.out.println(bd1.divide(bd2));// 1
    }
}

你可能感兴趣的:(java)