文章目录
Java有8种基本类型,每种基本类型都有一个对应的包装类。包装类是什么呢?
包装类是一个类,内部有一个实例变量,保存对应的基本类型的值,这个类一般还有一些静态方法、静态变量和实例方法,以方便对数据进行操作
基本类型 | 包装类 |
---|---|
boolean | Boolean |
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
1. 基本用法
将基本类型转换为包装类的过程,一般称为“装箱”,而将包装类型转换为基本类型的过程,则称为“拆箱”。
Integer a = Integer.valueOf(1); // 装箱
int b = a.intValue(); // 拆箱
// 构造
Integer a = new Integer(1);
从字节码中,我们发现装箱其实就是调用了 包装类的valueOf()
方法,拆箱其实就是调用了 xxxValue()
方法。
因此,
Integer i = 10
等价于Integer i = Integer.valueOf(10)
int n = i
等价于int n = i.intValue()
;
1.1 包装类的共同点
-
重写Object方法
-
实现Comparable接口
-
包装类和String方法
-
常用常量
-
共同的父类Number,通过父类中定义的各种方法,包装类实例可以返回任意的基本数值类型。
-
不可变性,包装类都是不可变类。所谓不可变是指实例对象一旦创建,就没有办法修改了。
BigDecimal
《阿里巴巴Java开发手册》中提到:浮点数之间的等值判断,基本数据类型不能用==来比较,包装数据类型不能用 equals 来判断。 具体原理和浮点数的编码方式有关,这里就不多提了,我们下面直接上实例:
float a = 1.0f - 0.9f;
float b = 0.9f - 0.8f;
System.out.println(a);// 0.100000024
System.out.println(b);// 0.099999964
System.out.println(a == b);// false
我们在使用BigDecimal时,为了防止精度丢失,推荐使用它的 BigDecimal(String) 构造方法来创建对象。《阿里巴巴Java开发手册》对这部分内容也有提到如下图所示。
2. 剖析String
2.1 内部原理
String类内部用一个字符数组表示字符串,实例变量定义为:
private final char value[];
String中的大部分方法内部也都是操作的这个字符数组。
2.2 编码转换
2.3 常量字符串
String a = "11"; // a指向常量字符串 "11"的位置
String b = "11";
a == b; // true,两者指向同一个位置
String c = new String("11");
String d = new String("11");
c == d; // false,两者指向不同的对象
字符串的创建
- String s = “a” + “b" + "c"创建了多少个对象?
创建了1个,因为在编译时JVM会合并 “a”、“d”、"c"成为一个对象,放入常量池,然后让s指向它
- String s = new String(“a” + " b") 创建了多少个对象?
String s = new String(“a” + " b") 创建了多少个对象?
创建了两个,一个是字符串常量,一个是String对象
3. 剖析StringBuilder
3.1 基本用法
StringBuilder sb = new StringBuilder();
sb.append("2");
sb.append("3");
String a = ab.toString();
3.2 内部原理
还是用字符数组,不同于String,此数组不是final的,他保存一个实例变量count表示数组已使用的字符个数。
- 在构造时调用父类构造生成特定长度的char数组,
- append方法会直接复制字符到字符数组,数组长度不够时进行扩容。
- toString方法会生成一个String对象,保存字符数组的内容
- insert方法会在确保有足够长度后,将原数组插入位后字符后移,然后插入。
3.3 String的+和+=
String的+法一般会生成StringBuilder对象,通过append方法实现String的加减
3.4 String,StringBuilder和StringBuffer
可变性:
String类中使用final字符数组保存字符串,因此String不可变
private final char value[];
而其他两者都继承自AbstractStringBuilder类,此类中字符数组未用final修饰。
线程安全性:
String对象不可变,可以理解为常量,线程安全。
StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,因此是线程安全的。
StringBuilder并没有对方法加同步锁,所以是非线程安全的。
性能:
String类型每次操作时都会生成新的String对象,并改变指针指向新的String对象。
StringBuffer每次都会对Stringbuffer对象本身进行操作,而不是生成新的对象并改变引用。
StringBuilder性能比StringBuffer高10%~15%,但线程不安全。
4. 随机
4.1 Math.random
double a = Math.random(); // 无参构造
double b = Math.random(seed); // 有参构造
Math.Random(seed1) == Math.random(seed1);
种子相同,产生的随机数序列就是相同的。
4.2 随机的基本原理
public Random(long seed) {
if (getClass() == Random.class)
this.seed = new AtomicLong(initialScramble(seed));
else {
// subclass might have overriden setSeed
this.seed = new AtomicLong();
setSeed(seed);
}
}
synchronized public void setSeed(long seed) {
this.seed.set(initialScramble(seed));
haveNextNextGaussian = false;
}
private static long initialScramble(long seed) {
return (seed ^ multiplier) & mask;
}
4.3 洗牌
一种常见的随机场景是洗牌,就是将一个数组或序列随机重新排列
int[] arr = {1,2,3,4,5,6,7,8,9};
shuffle(arr);
System.out.println(Arrays.toString(arr));
private static void swap(int[] arr, int i, int j){
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
private static void shuffle(int [] arr){
Random rnd = new Random();
for(int i = arr.length; i> 1; i--){
swap(arr, i-1, rnd.nextInt(i));
}
}
4.4 抢红包算法
public class RandomRedPacket {
private int leftMoney;
private int leftNum;
private Random rnd;
public RandomRedPacket(int totle, int num){
this.leftMoney = totle;
this.leftNum = num;
this.rnd = new Random();
}
public synchronized int nextMoney(){
if(this.leftNum <= 0){
throw new IllegalStateException("抢光了");
}
if(this.leftNum == 1){
return this.leftMoney;
}
double max = this.leftMoney / this.leftNum * 2d;
int money = (int) (rnd.nextDouble()*max);
money = Math.max(1,money);
this.leftMoney -= money;
this.leftNum--;
return money;
}
}
// test
@Test
public void test_1(){
RandomRedPacket redPacket = new RandomRedPacket(100, 5);
for(int i =0; i < 5; ++i){
System.out.println(redPacket.nextMoney());
}
}
5. 习题
- 无论如何,Integer与new Integer不会相等。不会经历拆箱过程,
- 两个都是非new出来的Integer,如果数在-128到127之间,则是true,否则为false
java在编译Integer i2 = 128的时候,被翻译成-> Integer i2 = Integer.valueOf(128);而valueOf()函数会对-128到127之间的数进行缓存 - 两个都是new出来的,都为false
- int和integer(无论new否)比,都为true,因为会把Integer自动拆箱为int再去比