一、包装类
(一)包装类和基本数据类型的转换
编辑
(二)包装类型和String类型的相互转换
(三)Integer类和Character类的常用方法
二、String
(一)创建String对象的两种方式
(二)字符串的特性
(三)String类的常见方法
三、StringBuffer
(一)String VS StringBuffer
(二)StringBuffer的构造器
(三)String和StringBuffer相互转换
(四)StringBuffer类常见方法
四、StringBuilder
String、StringBuffer和StringBuilder的比较
五、Math
六、Date、Calendar、LocalDate
(一)第一代日期类
(二)第二代日期类
(三)第三代日期类
七、System
八、Arrays
九、BigInteger、BigDecimal
包装类的分类
1、针对八种基本数据类型,定义相应的引用类型——包装类
2、有了类的特点,就可以调用类中的方法
以int和Integer为例
1、jdk5前的手动装箱和拆箱方式,装箱:基本数据类型->包装类型 拆箱:反之
2、jdk5以后(含jdk5)的自动装箱和拆箱方式
3、自动装箱底层调用的是valueOf方法,比如Integer.valueOf()
public class Integer01 {
public static void main(String[] args) {
// 手动装箱
int n1 = 100;
// 方法1
Integer integer = new Integer(n1);
// 方法2
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 WrapperVsString {
public static void main(String[] args) {
// 包装类(Integer)->String
Integer i = 100; // 自动装箱
// 方式一
String str1 = i + "";
// 方式二
String str2 = i.toString();
// 方式三
String str3 = String.valueOf(i);
// String->包装类(Integer)
// 方式一
String str = "12345";
Integer i2 = Integer.parseInt(str); // 使用到自动装箱
// 方式二
Integer i3 = new Integer(str); // 构造器
}
}
1、String对象用于保存字符串,也就是一组字符序列
2、字符串常量对象是用双引号括起的字符序列。例如:"你好"、"12.97"、"boy"等
3、字符串的字符使用Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节
4、String类有很多构造方法
较常用的构造方法:
String s1 = new String();
String s2 = new String(String original);
String s3 = new String(char[] a);
String s4 = new String(char[] a, int startIndex, int count);
5、String类是final类,不能被其他类继承
6、String有属性private final char value[];用于存放字符串内容,value是一个final类型,一旦赋值就不可以修改(指地址不能修改,但是单个字符内容是可以变化的)
方式一:直接赋值 String s = "hsp";
方式二:调用构造器 String s2 = new String("hsp");
1、方式一直接赋值:先从常量池查看是否有"hsp"数据空间,如果有就直接指向;如果没有则重新创建,然后指向。s最终指向的是常量池的空间地址。
2、方式二调用构造器:先在堆中创建空间,里面维护了value属性,指向常量池的hsp空间。如果常量池没有"hsp",重新创建,如果有,直接通过value指向。最终指向的是堆中的空间地址。
1、String是一个final类,代表不可变的字符序列
2、字符串是不可变的。一个字符串对象一旦被分配,其内容是不可变的。
String类是保存字符串常量的。每次更新都需要重新开辟空间,效率较低。因此,java设计者还提供了StringBuilder和StringBuffer来增强String的功能,并提高效率。
比如:
String s = new String("");
for(int i = 0; i < 80000; i++) {
s += "hello";
}
常用方法:
String str = "hello";
// str[0] ×
// str.charAt(0) -> h √
1、java.lang.StringBuffer代表可变的字符序列,可以对字符串内容进行增删
2、很多方法与String相同,但StringBuffer是可变长度的。
3、StringBuffer是一个容器。
1、StringBuffer是一个final类,不能被继承
2、在父类AbstractStringBuilder中,有属性char[] value,没有final修饰,该value数组存放字符串内容,是存放在堆中的。
1、String保存的是字符串常量,里面的值不能更改,每次String类的更新实际上就是更改地址,效率较低。(private final char value[];)
2、StringBuffer保存的是字符串变量,里面的值可以更改,每次StringBuffer的更新实际上可以更新内容,不用每次更新地址(即不是每次创建新对象,在空间不够时扩展空间时才更新地址)。效率较高。(char[] value;存放在堆中)
public class StringBuffer01 {
public static void main(String[] args) {
// 创建一个大小为16字符的char[],用于存放字符内容
StringBuffer sb = new StringBuffer();
// 通过构造器指定char[]大小
StringBuffer sb2 = new StringBuffer(100);
// 通过给一个String创建StringBuffer,char[]大小就是str.length()+16
StringBuffer sb3 = new StringBuffer("hello");
}
}
public class StringBuffer02 {
public static void main(String[] args) {
// String -> StringBuffer
String str = "hello";
// 方式一:使用构造器
// 返回的才是StringBuffer对象,对str本身没有影响
StringBuffer sb = new StringBuffer(str);
// 方式二:使用append方法
StringBuffer sb2 = new StringBuffer();
sb2 = sb2.append(str);
// StringBuffer -> String
StringBuffer sb3 = new StringBuffer("world");
// 方式一:使用构造器
String s = new String(sb3);
// 方式二:使用StringBuffer提供的toString方法
String s2 = sb3.toString();
}
}
练习:将小数点前面每三位用逗号隔开,比如:116668259.2563->116,668,259.2563
public class StringBufferExercise {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String price = scanner.next();
StringBuffer price_sb = new StringBuffer(price);
for (int i = price_sb.lastIndexOf(".")-3; i > 0; i-=3) {
price_sb = price_sb.insert(i, ",");
}
System.out.println(price_sb);
}
}
1、一个可变的字符序列。此类提供一个与StringBuffer兼容的API,但不保证同步(StringBuilder不是线程安全的)。该类被设计用作StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候。如果可能,建议优先采用该类,因为在大多数实现中,它比StringBuffer要快。
2、在StringBuilder上的主要操作是append和insert方法,可重载这些方法,以接受任意类型的数据。
3、StringBuilder和StringBuffer均代表可变的字符序列,方法是一样的,所以使用和StringBuffer一样。
1、StringBuilder和StringBuffer非常类似,均代表可变的字符序列,而且方法也一样
2、String:不可变字符序列,效率低,但复用率高
3、StringBuffer:可变字符序列、效率较高(增删)、线程安全
4、StringBuilder:可变字符序列、效率最高、线程不安全
5、String使用注意说明:
String s = "a";
s += "b";
实际上原来的"a"字符串对象已经丢弃了,现在又产生了一个字符串"ab"。如果多次执行这些改变串内容的操作,会导致大量副本字符串对象留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能。(结论:如果对String做大量修改,不要使用String)
效率测试:
public class StringVsStringBufferVsStringBuilder {
public static void main(String[] args) {
String text = "";
long startTime = 0L;
long endTime = 0L;
StringBuffer buffer = new StringBuffer("");
StringBuilder builder = new StringBuilder("");
startTime = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
buffer.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuffer的执行时间:" + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
builder.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuilder的执行时间:" + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
text = text + i;
}
endTime = System.currentTimeMillis();
System.out.println("String的执行时间:" + (endTime - startTime));
}
}
使用原则:
1、如果字符串存在大量的修改操作,一般使用StringBuffer或StringBuilder
2、如果字符串存在大量的修改操作,并在单线程的情况,使用StringBuilder
3、如果字符串存在大量的修改操作,并在多线程的情况,使用StringBuffer
4、如果字符串很少修改,被多个对象引用,使用String,比如配置信息等
Math类包含用于执行基本数学运算的方法,如初等函数、对数、平方根和三角函数
Math的常用方法(静态方法)
public class MathMethod {
public static void main(String[] args) {
// 获取a-b之间的一个随机整数,a=2,b=7
// random返回的是0 <= x < 1之间的一个随机小数
int a = 2;
int b = 7;
for (int i = 0; i < 10; i++) {
// (Math.random()*(b-a)返回的数范围在0~b-a
int num = (int)(a + Math.random()*(b-a+1));
System.out.println(num);
}
}
}
1、Date:精确到毫秒,代表特定的瞬间
2、SimpleDateFormat:格式和解析日期的类,允许进行格式化(日期-->文本)、解析(文本-->日期)的规范化
public class Date01 {
public static void main(String[] args) throws ParseException {
// 这里的Date类是在java.utils包
// 默认输出的日期格式是国外的方式,因此通常需要进行格式转换
Date date1 = new Date(); // 获取当前系统的时间
System.out.println("当前日期=" + date1);
Date date2 = new Date(9234567); // 通过毫秒数得到时间
System.out.println(date2);
System.out.println(date2.getTime());
// 格式化日期
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E");
String formatDate1 = sdf.format(date1);
System.out.println("格式化后的当前日期=" + formatDate1);
// 可以把一个格式化的String转成对应的Date
// 在把String-->Date,使用的sdf格式需要和给的String格式一样,否则会抛出转换异常
String s = "1996年01月01日 10:20:30 星期一";
Date parse = sdf.parse(s);
System.out.println("parse=" + parse); // 输出仍然是国外的格式
System.out.println("parse=" + sdf.format(parse));
}
}
1、主要就是Calendar类(日历),是一个抽象类,并且构造器是私有化的,可以通过getInstance()来获取实例
2、Calendar类是一个抽象类,它为特定瞬间与一组诸如YEAR、MONTH、DAY_OF_MONTH、HOUR等日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法
public class Calendar_ {
public static void main(String[] args) {
Calendar c = Calendar.getInstance(); // 创建日历类对象
// 获取日历对象的某个日历字段
System.out.println("年:" + c.get(Calendar.YEAR));
System.out.println("月:" + (c.get(Calendar.MONTH)+1)); // Calendar返回月时是按照0开始编号
System.out.println("日:" + c.get(Calendar.DAY_OF_MONTH));
// 如果需要按照24小时进制获取时间,Calendar.HOUR-->Calendar.HOUR_OF_DAY
System.out.println("小时:" + c.get(Calendar.HOUR));
System.out.println("分钟:" + c.get(Calendar.MINUTE));
System.out.println("秒:" + c.get(Calendar.SECOND));
// Calendar没有专门的格式化方法,所以需要自己来组合显示
System.out.println(c.get(Calendar.YEAR) + "年" + (c.get(Calendar.MONTH)+1) + "月" + c.get(Calendar.DAY_OF_MONTH) + "日");
}
}
前面两代日期类不足分析:
JDK1.0中包含了一个java.util.Date类,但是它的大多数方法已经在JDK1.1引入Calendar类之后被弃用了。而Calendar也存在问题是:
1、可变性:像日期和时间这样的类应该是不可变的
2、偏移性:Date中的年份是从1900开始的,而月份都从0开始
3、格式化:格式化只对Date有用,Calendar则不行
4、此外,它们也不是线程安全的;不能处理闰秒等(每隔2天,多出1s)
第三代日期类常见方法:
1、LocalDate(日期/年月日)、LocalTime(时间/时分秒)、LocalDateTime(日期时间/年月日时分秒) JDK8加入
2、DateTimeFormatter格式日期类
public class LocalDate_ {
public static void main(String[] args) {
// 使用now()返回表示当前日期时间的对象
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
System.out.println("年:" + ldt.getYear());
System.out.println("月:" + ldt.getMonth());
System.out.println("月:" + ldt.getMonthValue());
System.out.println("日:" + ldt.getDayOfMonth());
System.out.println("小时:" + ldt.getHour());
System.out.println("分钟:" + ldt.getMinute());
System.out.println("秒:" + ldt.getSecond());
// 使用DateTimeFormatter格式化对象
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH小时mm分钟ss秒");
String format = dtf.format(ldt);
System.out.println("格式化的日期=" + format);
}
}
3、Instant时间戳
类似于Date,提供了一系列和Date类转换的方式
public class Instant_ {
public static void main(String[] args) {
Instant now = Instant.now();
System.out.println(now);
// Instant --> Date
Date date = Date.from(now);
// Date --> Instant
Instant instant = date.toInstant();
}
}
4、第三代日期更多方法:
LocalDateTime类
MonthDay类:检查重复事件
是否闰年
增加日期的某个部分
使用plus方法测试增加时间的某个部分
使用minus方法测试查看一年前和一年后的日期
// 提供plus和minus方法对当前时间进行加减
LocalDateTime ldt2 = LocalDateTime.now().plusDays(890);
System.out.println("890天后=" + dtf.format(ldt2));
LocalDateTime ldt3 = LocalDateTime.now().minusMinutes(3456);
System.out.println("3456分钟前=" + dtf.format(ldt3));
1、exit:退出当前程序
2、arraycopy:复制数组元素,比较适合底层调用,一般使用Arrays.copyOf完成复制数组
3、currentTimeMillens:返回当前时间距离1970-01-01的毫秒数
4、gc:运行垃圾回收机制
public class System_ {
public static void main(String[] args) {
// System.out.println("ok1");
// // exit(0)表示程序退出,0表示一个状态,正常的状态
// System.exit(0);
// System.out.println("ok2");
// System.arraycopy
int[] src = {1, 2, 3};
int[] dest = new int[3]; // 当前是{0,0,0}
System.arraycopy(src, 0, dest, 0, src.length);
System.out.println(Arrays.toString(src));
System.out.println(Arrays.toString(dest));
// System.currentTimeMillis
System.out.println(System.currentTimeMillis());
}
}
Arrays里面包含了一系列静态方法,用于管理或操作数组(比如排序和搜索)
1、toString返回数组的字符串形式
2、sort排序(自然排序和定制排序)
public class ArraysMethod01 {
public static void main(String[] args) {
Integer[] integers = {1, 20, 90};
// 1、直接使用Arrays.toString方法显示数组信息
System.out.println(Arrays.toString(integers));
// 2、sort方法排序
// (1)默认排序
Integer[] arr1 = {1, -1, 7, 0, 89};
Arrays.sort(arr1);
System.out.println(Arrays.toString(arr1));
// (2)定制排序
// sort方法是重载的,也可以通过传入一个接口Comparator实现定制排序
// 两个参数:排序的数组arr2,实现了Comparator接口的匿名内部类
Integer[] arr2 = {1, -1, 7, 0, 89};
Arrays.sort(arr2, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Integer i1 = (Integer)o1;
Integer i2 = (Integer)o2;
return i2 - i1;
}
});
System.out.println(Arrays.toString(arr2));
}
}
对定制排序的深入理解:
public class ArraysSortCustom {
public static void main(String[] args) {
int[] arr1 = {1, -1, 8, 0, 20};
bubble01(arr1);
System.out.println(Arrays.toString(arr1));
int[] arr2 = {1, -1, 8, 0, 20};
bubble02(arr2, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Integer i1 = (Integer)o1;
Integer i2 = (Integer)o2;
return i2 - i1;
}
});
System.out.println(Arrays.toString(arr2));
}
//普通的冒泡排序
public static void bubble01(int[] arr) {
int temp = 0;
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length-1-i; j++) {
if(arr[j] > arr[j+1]){
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
//结合冒泡排序+定制排序
public static void bubble02(int[] arr, Comparator c) {
int temp = 0;
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length-1-i; j++) {
// 数组排序由c.compare(arr[j], arr[j+1])返回的值决定
if(c.compare(arr[j], arr[j+1]) > 0){
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
}
3、binarySearch通过二分搜索法进行查找,要求必须排好序
4、copyOf数组元素的复制
5、fill数组元素的填充
6、equals比较两个数组元素内容是否完全一致
7、asList将一组值转成成list
public class ArraysMethod02 {
public static void main(String[] args) {
// 1、Arrays.binarySearch
// 要求数组是有序的,如果数组是无序的无法使用二分查找
// 如果数组中不存在该元素,就返回-(low + 1)
Integer[] arr = {1, 2, 90, 123, 567};
int index = Arrays.binarySearch(arr, 90);
System.out.println("index=" + index);
// 2、Arrays.copyOf
// 从arr数组中,拷贝arr.length个元素到newArr数组中
// 如果拷贝的长度大于arr.length,就在新数组的后面增加null
// 如果拷贝的长度小于0,就抛出异常NegativeArraySizeException
Integer[] newArr = Arrays.copyOf(arr, arr.length);
System.out.println("==拷贝执行完毕后==");
System.out.println(Arrays.toString(newArr));
// 3、Arrays.fill
// 使用99去填充num数组,可以理解成是替换原来的元素
Integer[] num = new Integer[]{9,3,2};
Arrays.fill(num, 99);
System.out.println("==num数组填充后==");
System.out.println(Arrays.toString(num));
// 4、Arrays.equals
// 如果arr和arr2数组元素完全一样,则返回true;
Integer[] arr2 = {1, 2, 90, 123, 567};
boolean equals = Arrays.equals(arr,arr2);
System.out.println("equals=" + equals);
// 5、Arrays.asList
// 会将(2,3,4,5,6,1)数据转成一个List集合
// asList的编译类型是List(接口)
// asList的运行类型是java.util.Arrays$ArrayList(Arrays类的静态内部类)
List asList = Arrays.asList(2,3,4,5,6,1);
System.out.println("asList=" + asList);
System.out.println("asList的运行类型:" + asList.getClass());
}
}
应用场景:
1、BigInteger适合保存比较大的整型
public class BigInteger_ {
public static void main(String[] args) {
BigInteger bigInteger1 = new BigInteger("66666666666666666666");
BigInteger bigInteger2 = new BigInteger("100");
System.out.println(bigInteger1);
// 在对BigInteger进行加减乘除,需要使用相应的方法,不能直接+-*/
System.out.println(bigInteger1.add(bigInteger2));
System.out.println(bigInteger1.subtract(bigInteger2));
System.out.println(bigInteger1.multiply(bigInteger2));
System.out.println(bigInteger1.divide(bigInteger2));
}
}
2、BigDecimal适合保存精度更高的浮点型
public class BigDecimal_ {
public static void main(String[] args) {
BigDecimal bigDecimal1 = new BigDecimal("1.11111119999999999999999");
BigDecimal bigDecimal2 = new BigDecimal("1.666");
System.out.println(bigDecimal1);
System.out.println(bigDecimal1.add(bigDecimal2));
System.out.println(bigDecimal1.subtract(bigDecimal2));
System.out.println(bigDecimal1.multiply(bigDecimal2));
//可能抛出异常ArithmeticException(除不尽)
// 解决方案:调用divide方法时指定精度,BigDecimal.ROUND_CEILING就会保留分子的精度
System.out.println(bigDecimal1.divide(bigDecimal2, BigDecimal.ROUND_CEILING));
}
}