🐇包装类
八大基本数据类型对应的包装类
基本数据类型 | 包装类 |
---|---|
boolean | Boolean |
char | character |
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
🌳装箱拆箱
jdk5之前,是手动装箱拆箱
装箱:int -> Integer; 拆箱:Integer -> int
public class Integer01 {
public static void main(String[] args) {
//手动装箱
int n1 = 100;
Integer integer = new Integer(n1);
Integer integer1 = Integer.valueOf(n1);
//手动拆箱
int i = integer.intValue();
//自动装箱
int n2 = 200;
Integer integer2 = n2;//底层使用的是Integer.valueOf(n2);
//自动拆箱
int n3 = integer2;//底层使用的是integer2.intValue();
}
}
习题:
public class WrapperExercise01 {
public static void main(String[] args) {
Double d = 1.4d;//自动装箱,相当于Double.valueOf(1.4d)
Float v = 1.5f;//自动装箱,相当于Float.valueOf(1.5f)
Object obj1 = true ? Integer.valueOf(1) : Double.valueOf(2);
System.out.println(obj1);//这里要输出1.0,三元运算符要看做一个整体
Object obj2;
if(true) {
obj2 = Integer.valueOf(1);
} else {
obj2 = Double.valueOf(2);
}
System.out.println(obj2);//这里要输出1,而不是1.0
}
}
🌳包装类(Integer) 与 String相互转换
举例:包装类(Integer) -> String
public class WrapperVsString {
public static void main(String[] args) {
Integer i1 = 100;//自动装箱
//方式一
String str1 = i1 + "";
//方式二
String str2 = i1.toString();
//方式三
String str3 = String.valueOf(i1);
}
}
举例:String -> 包装类(Integer)
Object->Integer:Object+“”->String->Integer
public class WrapperVsString {
public static void main(String[] args) {
String str4 = "1234";
//方式一
Integer i2 = Integer.parseInt(str4);//源码返回int类型,体现了自动装箱
//方式二
Integer i3 = new Integer(str4);//调用了构造器
}
}
🌳Integer面试题
- 示例一:两个对象,所以是false
public class WrapperExercise02 {
public static void main(String[] args) {
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j);//两个对象,所以是false
}
}
- 示例二:if (i >= -128 && i <= 127)时不创建对象
public class WrapperExercise02 {
public static void main(String[] args) {
//示例二
Integer m = 1;//底层 Integer.valueOf(1)
Integer n = 1;//底层 Integer.valueOf(1)
//这里要看范围,在-128~127之间直接返回,否则就new Integer(xx)
System.out.println(m == n);//所以返回true
}
}
- 示例三:大小超过了127,直接new Integer
public class WrapperExercise02 {
public static void main(String[] args) {
//示例三
Integer x = 128;
Integer y = 128;
//大小超过了127,直接new Integer
System.out.println(x == y);//所以返回false
}
}
自动装箱底层源码:
@IntrinsicCandidate
public static Integer valueOf(int i) {
//cache values in the range -128 to 127
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
- 示例四:只要有基本数据类型存在,则==判断的是值是否相等
public class WrapperExercise02 {
public static void main(String[] args) {
//示例四,只要有基本数据类型存在,则==判断的是值是否相等
Integer i1 = 127;
int i2 = 127;
System.out.println(i11 == i12);//true
Integer i13 = 128;
int i14 = 128;
System.out.println(i13 == i14);//true
}
}
🐇String类
🌳String继承关系
- String实现了Serializable接口,说明String可以串行化,可以在网络中传输;
- String实现了Comparable,说明String对象可以比较;
🌳String本质
- String:private final char value[];
- value不可以指向一个新的地址
1.String 对象用于保存字符串,也就是一组字符序列;“john” 是字符串常量,双引号括起来的字符序列;
2.字符串的字符使用Unicode字符编码,一个字符(包括汉字和字母)占两个字节;
3.String 类有很多构造器,构成构造器的重载;
4.String 是一个final类,不能被其它的类继承;
5. String 中有一个属性private final char value[];用于存放字符串内容;value值不可修改,指的是value指向的地址不可修改;
6. 即value不可以指向一个新的地址,但是单个内存空间的数据是可以改变的;即约束你指向一个新的地址
public class String01 {
public static void main(String[] args) {
String name = "john";
final char[] value = {'1','2','3'};
value[0] = 'a';//正确
char[] value2 = {'4','5','6'};
//value = value2;//错误
}
}
🌳String对象创建
- 直接赋值 String s = “zzw”;
方式一:先从常量池查看是否有“zzw”数据空间,如果有,直接指向;如果没有则重新创建,然后指向。s最终指向的是常量池的空间地址;
- 调用构造器 String s2 = new String(“zzw”);
方式二:先在堆中创建空间,里面维护了value属性,指向常量池的“zzw”数据空间。如果常量池没有“zzw”,则重新创建;如果有,直接通过value指向,最终指向的是堆中的空间地址。
练习:
- equals()比较的是内容;
- a和b都是指向的常量池;
- a指向的是常量池中的字符串,c指向的堆
- intern()最终返回的是常量池的地址
- p1.name == "zzw"比较的是地址,p1.name指向的地址和"zzw"返回的地址是一个地址,返回true
🌳String特性
1.以下语句创建了几个对象?画出内存布局图。(2个)
String s1 = “hello”; s1 = “haha”;
2.String a = “hello” + “abc”; 创建了几个对象?(1个)
底层优化==>String a = “helloabc”;
3、String a = “hello”; String b = “abc”; String c = a + b; 创建了几个对象?(3个)画出内存布局图。
- 先创建一个 StringBuilder sb = new StringBuilder();
- 执行 sb.append(“hello”);
- 执行 sb.append(“abc”);
- sb. toString();
所以最后一步相当于String c = new String (“helloabc”);
最后c其实是指向堆中的对象(String)value[ ] ->常量池中 “helloabc”>5.内存布局图
多个字符串常量相加底层会优化,只会开辟一个地址;
字符串相加时,如果加数中有字符串变量,则会转换成StringBuilder,最终返回的地址指向堆;
练习:
- 下面代码输出什么,并说明原因;
- 下列程序运行的结果是什么 ,画出内存布局图。
tip:str每次更新都会重新开辟空间;
🌳String常用方法
⭐indexOf方法
- indexOf 获取字符在字符串对象中第一次出现的索引,如果没有,返回-1
⭐substring方法
- substring(5) 从索引5开始截取后面所有的内容
⭐toUpperCase方法
- toUpperCase()转换成大写
⭐concat方法
- concat 拼接字符串, 本身不会改变str的值
⭐replace方法
- replace() 替换字符串的内容,不会改变str的值
⭐split方法
- split() 分割字符串,返回字符数组;如果有特殊字符,需要加入转义字符
⭐toCharArray方法
- toCharArray 将字符串转换成字符数组
⭐compareTo方法
- compareTo 比较两个字符串的大小
⭐格式化字符串
- 格式化字符串
🐇StringBuffer类
🌳StringBuffer继承关系
- StringBuffer 的直接父类是AbstractStringBuilder;
- StringBuffer 实现了Serializable接口,说明StringBuffer可以串行化;
🌳StringBuffer本质
- StringBuffer只有四个构造器
- StringBuffer是一个final类,不可以被继承
- StringBuffer的父类 AbstractStringBuilder 中有属性char[] value;
value用于存放字符串,不是final类型;value值可以修改,并且存放在堆中;
🌳String与StringBuffer比较
String:private final char value[];
- String保存的是字符串常量,里面的值不能更改,每次String类的更新实际上就是更改地址,原因:private final char value[];
StringBuffer:char[] value;
- StringBuffer保存的是字符串变量,里面的值可以更改,每次StringBuffer的更新实际上可以更新内容,不用每次更新地址,效率较高,原因:char value[];
🌳StringBuffer构造器
⭐StringBuffer()
- 构造一个不带字符的字符串缓冲区,初始容量为16个字符;
创建一个大小为 16的 char[], 用于存放数据
⭐StringBuffer(int capacity)
- 通过构造器指定 char[] 大小
⭐StringBuffer(String)
- 通过 给一个String 创建StringBuffer,char[] 大小就是str.length + 16
🌳String和StringBuffer转换
- String 转成 StringBuffer
- 方式一:使用StringBuffer构造器
方式二:使用StringBuffer的append方法
- StringBuffer 转成 String
- 方式一:使用StringBuffer的toString方法
方式二:通过String的构造器
🌳StringBuffer常用方法
⭐append方法
- true会被转成字符串,直接输出stringBuffer对象本质是调用toString()方法
System.out.println(stringBuffer);执行流程:
⭐delete方法
- 删除索引>= start && < end范围内的字符串
[start, end),左闭右开
⭐replace方法
- replace(16,20,“蔡文静”),使用“蔡文静”替换 索引为 16-19的字符,[16,20)
⭐indexOf方法
- 查找指定的子串在字符串中第一次出现的索引,如果没有则返回-1;
⭐insert方法
- 在索引为16处插入字符串",",原来索引为9的内容自动后移
⭐length方法
- 返回长度
🌳StringBuffer练习
- 练习1
练习2:
NullPointerException:StringBuffer构造器里不能传一个空值
练习三:将小数-价格的小数点前面每隔三位用逗号分开
改进:
🐇StringBuilder类
- StringBuilder类的直接父类是AbstractStringBuilder
- StringBuilder类实现了Serializable接口,可以串行化
- StringBuilder类被final修饰,不能被继承
- 对象字符序列存在于父类AbstractStringBuilder char[] value中;
- StringBuilder中的方法没有做互斥的处理,没有synchronized关键字,推荐在单线程中使用;
- 如果我们需要对String做大量修改,我们需要使用StringBuffer,不要使用String;原因:
public class StringVsStringBufferVsStringBuilder {
public static void main(String[] args) {
//效率:StringBuilder > StringBuffer > String
String text = "";
long startTime = 0L;
long endTime = 0L;
StringBuffer stringBuffer = new StringBuffer("");
StringBuilder stringBuilder = new StringBuilder("");
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
stringBuffer.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuffer的执行时间:" + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
stringBuilder.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuilder的执行时间:" + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
text += String.valueOf(i);
}
endTime = System.currentTimeMillis();
System.out.println("String的执行时间:" + (endTime - startTime));
}
}
🌳总结
- 如果字符串存在大量的修改,一般使用StringBuffer或者StringBuilder;
- 如果字符串存在大量的修改操作,并处在单线程中,使用StringBuilder;
- 如果字符串存在大量的修改操作,并处在多线程中,使用StringBuffer;
- 如果字符串很少被修改,被多个对象引用,使用String,比如配置信息等;
🐇Math类
- Math常用方法(都是静态方法);abs()方法求绝对值;
- pow(2,3)方法求幂;2^3
- ceil()方法向上取整,返回 >= 参数的最小整数
- floor,向下取整,返回 <= 该参数的最大整数
- round 四舍五入, 等价于Math.floor ( 该参数 + 0.5 );
- sqrt 开平方根
- random()方法, 返回 0 <= x < 1,即[0,1)之间的随即小数
题目:获取a-b之间的一个随机整数,[a,b]
公式:int num = (int)(a + Math.random() * (b-a+1)) ;
- max(参数1,参数2)取最大值
🐇Arrays类
- Arrays类中包含了一系列静态方法,用于管理或操作数组(比如排序或搜索);
⭐Arrays.toString()
- Arrays.toString(),显示数组
⭐Arrays.sort() ->Comparator 定制排序
2.1 Arrays.sort(数组, 匿名内部类)(定制排序)
代码
public class ArrayMethod01 {
public static void main(String[] args) {
Integer arr[] = {1, -1, -7, -8, 0, 2};
//1.可以使用冒泡排序, 也可以使用Arrays提供的sort方法排序
//2.因为数组是引用类型, 所以通过sort排序后, 会直接影响到 实参
//3.sort方法是重载的, 也可以通过传入一个接口 Comparator 实现定制排序
//4.调用 定制排序 时, 传入两个参数
// (1)排序的数组
// (2)实现了Comparator接口的匿名内部类, 要求实现 compare 方法
//5.源码分析
// (1)Arrays.sort(arr, new Comparator() {})
// (2)TimSort.sort(a, 0, a.length, c, null, 0, 0);
// (3)binarySort(a, lo, hi, lo + initRunLen, c);
// (4)执行到binarySort方法, 会根据动态绑定机制, c.compare(pivot, a[mid])会执行我们传入的的匿名内部类
// while (left < right) {
// int mid = (left + right) >>> 1;
// if (c.compare(pivot, a[mid]) < 0)
// right = mid;
// else
// left = mid + 1;
// }
// (5)这是我们传入的的匿名内部类
// Arrays.sort(arr, new Comparator() {
// @Override
// public int compare(Object o1, Object o2) {
// Integer i1 = (Integer) o1;
// Integer i2 = (Integer) o2;
// return i1 - i2;
// }
// });
// (6)public int compare(Object o1, Object o2) 返回值, 会影响整个排序的结果
// 这就充分体现了 接口编程+动态绑定+匿名内部类的综合使用
// 将来的底层框架和源码的使用, 会非常常见.
/*
基于接口的匿名内部类
传统方法: 想使用Comparator接口, 使用一个类实现该接口, 并创建该对象
匿名内部类的方法: 匿名内部类的名字是: 外部类名 + $1
class ArrayMethod01$1 implements Comparator() {
@Override
public int compare(Object o1, Object o2) {
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return i1 - i2;
}
}
*/
Arrays.sort(arr, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return i1 - i2;
}
});
System.out.println("===排序后===");
System.out.println(Arrays.toString(arr));
}
}
debug:(1) 进入到Arrays类的sort方法
(2)进入到TimSort类的sort方法
(3)跳转到TimSort类的binarySort方法
(4)在这里调用匿名内部类
注意:public int compare(Object o1, Object o2) 这个方法返回值的正负会影响整个排序结果
2.2 自定义(定制排序/模拟排序):结合冒泡+定制
public class ArraySortCustom {
public static void main(String[] args) {
int[] arr = {-1, 1, 1, -8, 3, -4};
//bubble01(arr);
bubble02(arr, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return i2 - i1;// return i2 - i1;
}
});
System.out.println("====排序后的情况====");
System.out.println(Arrays.toString(arr));
}
//使用冒泡完成排序
public static void bubble01(int[] arr) {
int temp = 0;
for (int i = 0; i < arr.length - 1; i++) {
for (int j = i + 1; j < arr.length; j++) {
//从小到大排序
if (arr[i] > arr[j]) {
temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
}
}
}
//结合冒泡 + 定制
public static void bubble02(int[] arr, Comparator c) {
int temp = 0;
for (int i = 0; i < arr.length - 1; i++) {
for (int j = i + 1; j < arr.length; j++) {
//从小到大排序
//数组排序由 (c.compare(arr[i], arr[j])返回的值决定
if (c.compare(arr[i], arr[j]) > 0) {
temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
}
}
}
}
⭐Arrays.binarySearch()
- Arrays.binarySearch() ;(二叉查找;二分搜索法)
⭐Arrays.copyOf()
- Arrays.copyOf(数组, key) => 数组元素的拷贝,拷贝指定个元素返回到新数组
4.1 拷贝长度 > 原数组长度,添null
4.2 拷贝长度 < 0,报异常
4.3 底层使用的是: System.Array()
⭐Arrays.fill()
- Arrays.fill();数组元素的填充
⭐Arrays.equals()
- Arrays.equals(数组1,数组2); 比较数组1、数组2内容是否完全一致
⭐Arrays.asList()
- Arrays.asList()方法会将一组值转换成一个List集合
asList的运行类型:java.util.Arrays$ArrayList,即Arrays类中的静态内部类
练习:
- 发
🐇System类
- System.exit(0) 表示程序退出;0 表示正常
- System.arraycopy(src, srcPos, dest, destPos, length); 适合底层,一般使用Arrays.copyOf()
src:代表源数组
srcPos:从源数组的哪索引位置开始拷贝
dest:目标数组,即把源数组的元素拷贝到哪个数组
destPos:把源数组的元素拷贝到目标数组的哪个索引位置
length:从源数组拷贝多少个数据到目标数组
- System.currentTimeMillis(); 返回1970年1月1日0时0分0秒到今天的毫秒数(0时区)
- System.gc(); 主动触发垃圾回收机制;
🐇BigInteger类(BigDecimal类)
🌳BigInteger类
- BigInteger 适合比较大的整数
- 当编程中遇到很大的数,long不够用,可以使用BigInteger类
- 在对 BigInteger进行加减乘除的时候,需要使用对应的方法,不能直接+ - * /
🌳BigDecimal类
- 适合保存精度更高的浮点型
- 当我们需要保留一个精度很高的数时,可以使用BigDecimal来解决
- 当我们对BigDecimal进行运算(比如加减乘除)时,需要使用对应的方法,不能直接: + - * /
- 除法可能抛出算术异常:(Non-terminating decimal expansion; no exact representable decimal result.)
解决方案:在调用divide()方法时,指定精度即可 BigDecimal.ROUND.CEILING
如果有无限循环小数,就会保留分子的精度