1. 包装类
1.1. 什么是包装类
在开发中,经常用到的基本数据类型不是对象,但在实际应用中需要将基本数据转化成对象,以便于操作。
为了解决这个不足,Java为每个基本数据类型设计了一个对应的类,这样八个和基本数据类型对应的类称为包装类。位于java.lang包中
1.2. 包装类的优点
- 某些方法的参数必须是对象,为了让基本数据类型的数据能作为参数,提供包装类
- 包装类还可提供更多的功能
- 其他重要的功能:比如可以实现字符串和基本数据类型之间的转换
public class TestWrapper1 {
public static void main(String[] args) {
List list = new ArrayList();
list.add(new Integer(56));
System.out.println(list); // [56]
System.out.println(Integer.SIZE); // 32
System.out.println(Integer.MAX_VALUE); // 2147483647
System.out.println(Integer.MIN_VALUE); // -2147483648
System.out.println(Integer.toBinaryString(123)); // 1111011
System.out.println(Integer.toOctalString(123)); // 173
System.out.println(Integer.toHexString(123)); // 7b
String str = "123";
int num = Integer.parseInt(str);
System.out.println(num); // 123
String str2 = "123.45";
double d = Double.parseDouble(str2);
System.out.println(d); // 123.45
}
}
- 注意:
- 包装类的对象需要占用栈内存和堆内存,而基本数据类型变量只占用栈内存;基本数据类型变量占用空间少,更简单灵活高效
- 作为成员变量,初始值不同,int 0; Integer null
- 除了Character和Boolean外,其他都是数字型,数字型都是java.lang.Number的子类
1.3. 使用包装类
1.3.1. 自动装箱和自动拆箱
自动装箱和拆箱就是将基本数据类型和包装类之间进行自动的相互转换
- 自动装箱:基本类型的数据处于需要对象的环境中,会自动转为对象
- 自动拆箱:每当需要一个值时,对象会自动转成基本数据类型,没必要显示调用intValue()、doubleValue()等转型方法
自动装箱过程是通过调用包装类的valueOf()方法实现的,自动拆箱过程是通过调用包装类的xxxValue()方法实现的(如intValue()、doubleValue())
public class TestWrapper2 {
public static void main(String[] args) {
Integer in = 5;
Integer in2 = new Integer(5);
System.out.println(in == in2); // true
int i = in2;
int i2 = in2.intValue();
System.out.println(i2); // 5
Integer in3 = new Integer(56);
Integer in4 = new Integer(56);
System.out.println(in3 == in4); // false
System.out.println(in3.equals(in4)); // true
Integer in5 = 25;
Integer in6 = 25;
System.out.println(in5 == in6); // true
System.out.println(in5.equals(in6)); // true
Integer in7 = 256;
Integer in8 = 256;
System.out.println(in7 == in8); // false
System.out.println(in7.equals(in8)); // true
}
}
1.3.2. 理解Integer源码
Integer的父类是Number类;底层就是封装了一个int类型的value常量,可以通过构造方法、intValue()等赋值取值
public class Integer extends Number{
private final int value;
public Integer(int value) {
this.value = value;
}
@Override
public int intValue() {
return value;
}
}
Integer类提供了一个静态内部类IntegerCache,对于定义一个静态数组cache,长度为256,赋值为-128-127。对于自动装箱,如果是-128-127范围内的数据,直接获取数组的指定值;范围之外的数据,通过new Integer()重新创建对象。
public class Integer extends Number{
public static class IntegerCache {
static final int low = -128;
static final int high;
static final java.lang.Integer cache[];
static {
int h = 127;
high = h;
cache = new java.lang.Integer[(high - low) + 1];
int j = low;
for (int i = 0; i < cache.length; i++) {
cache[i] = new java.lang.Integer(j++);
}
}
}
public static java.lang.Integer valueOf(int i) {
if(i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)];
return new java.lang.Integer(i);
}
}
- 注意:
- JDK1.5之后,增加了自动装箱和拆箱功能
- 自动装箱调用的是valueOf()方法,而不是new Integer()方法
- 自动拆箱调用的是xxxValue()方法
- 包装类在自动装箱时为了提高效率,对于-128-127之间的值会进行缓存处理。超过范围,对象之间不能使用==比较,而是使用equals方法
2. String类
字符串相关的类:String类、StringBuilder类、StringBuffer类
- String类:代表不可变的字符序列
- StringBuilder类和StringBuffer类:代表可变字符序列
2.1. String类的使用
public class TestString1 {
public static void main(String[] args) {
String str = "wyb王一博";
System.out.println(str.length()); // 6
System.out.println(str.isEmpty()); // false
System.out.println(str.startsWith("w")); // true
System.out.println(str.endsWith("b")); // true
System.out.println(str.toUpperCase()); // WYB王一博
System.out.println(str.toLowerCase()); // wyb王一博
// str = str.toUpperCase();
// System.out.println(str); // WYB
System.out.println(str.charAt(1)); // y
System.out.println(str.substring(3,6)); // 王一博
System.out.println(str.substring(1)); // yb王一博
System.out.println(str.indexOf("y")); // 1
byte[] bytes = str.getBytes();
System.out.println(bytes.length); // 12
System.out.println(Arrays.toString(bytes)); // [119, 121, 98, -25, -114, -117, -28, -72, -128, -27, -115, -102]
String s = new String(bytes, 1, 2);
System.out.println(s); // yb
System.out.println(str); // wyb王一博
boolean flag = str.contains("wyb");
System.out.println(flag); // true
str = str.concat("xz").concat("肖战");
System.out.println(str); // wyb王一博xz肖战
str = str.replace("wyb", "一啵");
System.out.println(str); // 一啵王一博xz肖战
String s1 = " x z ";
System.out.println(s1.length()); // 6
System.out.println(s1.trim()); // x z
String str1 = "bjyx";
String str2 = "bjyx";
System.out.println(str1 == str2); // true
System.out.println(str1.equals(str2)); // true
String str3 = new String("bjyx");
String str4 = new String("bjyx");
System.out.println(str3 == str4); // false
System.out.println(str3.equals(str4)); // true
String str5 = "";
String str6 = null;
System.out.println(str5.isEmpty()); // true
// System.out.println(str6.isEmpty()); // Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.isEmpty()" because "str6" is null
}
}
- 方法
- charAt(int index):返回指定索引处的char值
- compareTo(String anotherString):按字典顺序比较两个字符串
- concat(String str):将指定字符串连接到此字符串的结尾
- contains(CharSequence s):当且仅当此字符串包含指定char序列时,返回true
- endsWith(String suffix):是否以指定的后缀结束,返回boolean
- startsWith(String prefix):测试此字符串是否以指定前缀开始,返回boolean
- startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始,返回boolean
- equals(Object anObject):此字符串与指定的对象比较,返回boolean
- equalsIgnoreCase(String anotherString):将String与另一个String比较,忽略大小写,返回boolean
- hashCode():返回此字符串的哈希码
- indexOf(int ch):返回指定字符在此字符串中第一次出现的索引
- indexOf(int ch, int fromIndex):返回指定字符在此字符串中第一次出现的索引,从指定的索引开始搜索
- indexOf(String str):返回指定子字符串在此字符串中第一次出现的索引
- indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现的索引,从指定索引开始
- isEmpty():当且仅当length()为0时,返回true
- lastIndexOf(int ch):返回指定字符在此字符串中最后一次出现的索引值
- lastIndexOf(int ch, int fromIndex):返回指定字符在此字符串中最后一次出现的索引值,从指定索引处开始进行反向搜索
- lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现的索引值
- length():返回此字符串的长度
- replace(char oldChar, char newChar):返回一个新的字符串,它是通过用newChar替换此字符串中出现的所有oldChar得到的
- replace(CharSequence target, CharSequence replacement):使用指定的字面了值替换序列替换此字符串所有匹配字面值目标序列的子字符串
- replaceAll(String regex, String replacement):使用给定的replacement替换此字符串所有匹配字面值目标序列的子字符串
- split(String regex):根据给定正则表达式的匹配拆分字符串
- substring(int beginIndex):返回一个新的字符串,它是此字符串的一个子字符串
- substring(int beginIndex, int endIndex):返回一个新的字符串,它是此字符串的一个子字符串
- toLowerCase():将字符串中所有字符转为小写
- toUpperCase():将字符串中所有字符转为大写
- toString():返回此对象本身(它已经是一个字符串)
- trim():返回字符串的副本,忽略前导空白和尾部空白
2.2. 理解String类源码
- String类是一个final类,意味着该类不能再有子类
- String类底层是一个字符数组value,各种方法的操作其实都是对该数组的操作
public final class String implements java.io.Serializable, Comparable, CharSequence {
private final char value[];
public String() {
this.value = "".value;
}
public String(java.lang.String original) {
this.value = original.value;
}
public boolean isEmpty() {
return value.length == 0;
}
public int length() {
return value.length;
}
public char charAt(int index) {
if(index < 0 || index >= value.length) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
public java.lang.String toString() {
return this;
}
}
- String类的equals()方法就是比较底层的字符数组各个元素是否相同,只要发现一个字符不相同,就返回false。如果所有字符都相同,返回true。如果两个变量指向了同一个字符数组,直接返回true
public boolean equals(Object anObject) {
if(this == anObject) return true;
if(anObject instanceof java.lang.String) {
java.lang.String anotherString = (java.lang.String)anObject;
int n = value.length;
if(n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if(v1[i] != v2[i]) return false;
i++;
}
return true;
}
}
return false;
}
- String类的concat()是创建一个新的字符数组,存放原来字符数组和新加入的字符数组内容,然后以该新数组创建一个新的字符串
public java.lang.String concat(java.lang.String str) {
int otherLen = str.length();
if(otherLen == 0) return this;
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new java.lang.String(buf, true);
}
3. StringBuffer和StringBuilder
StringBuffer和StringBuilder均代表可变的字符序列,这两个类都是抽象类AbstractStringBuilder的子类,方法几乎一样
- StringBuffer是JSK1.0提供的类,线程安全,做线程同步检查,效率较低
- StringBuilder是JDK1.5提供的类,线程不安全,不做线程同步检查,效率较高,建议采用该类
3.1. 使用StringBuilder类
public class TestStringBuilder {
public static void main(String[] args) {
StringBuilder builder = new StringBuilder("wyb");
builder.append("xz");
System.out.println(builder); // wybxz
builder.insert(3, "bjyx");
System.out.println(builder); // wybbjyxxz
builder.setCharAt(3, 'B');
System.out.println(builder); // wybBjyxxz
builder.replace(3,6, "HHK");
System.out.println(builder); // wybHHKxxz
builder.deleteCharAt(6);
System.out.println(builder); // wybHHKxz
builder.delete(6, builder.length());
System.out.println(builder); // wybHHK
builder.reverse();
System.out.println(builder); // KHHbyw
String string = builder.toString();
System.out.println(string); // KHHbyw
}
}
- 注意:实际开发中StringBuilder的使用场合:字符串的拼接(SQL语句)
- 方法
- append(boolean b):将boolean参数的字符串表示形式追加到序列
- capacity():返回当前容量
- charAt(int index):返回此序列中指定索引处的char值
- delete(int start, int end):移除此序列的子字符串中的字符
- deleteCharAt(int index):移除此序列指定位置上的char
- ensureCapacity(int minimumCapacity):确保容量至少等于指定的最小值
- indexOf(String str):返回第一次出现的指定字符串在该字符串中的索引
- indexOf(String str, int fromIndex):从指定的索引处开始,返回第一次出现的指定字符串在该字符串中的索引
- lastIndexOf(String str):返回最右边出现的指定字符串在该字符串中的索引
- lastIndexOf(String str, int fromIndex):从指定的索引处开始,返回最后一次出现的指定字符串在该字符串中的索引
- insert(int offset, boolean b):将boolean参数的字符串表示形式插入此序列中
- length():返回长度(字符数)
- replace(int start, int end, String str):使用给定String中的字符替换此序列的子字符串中的字符
- reverse():将此字符序列用其反转形式取代
- substring(int start):返回一个新的String,它包含此字符序列当前所包含字符的子序列
- substring(int start, int end):返回一个新的String,它包含此字符序列当前所包含字符的子序列
- toString():返回此序列中数据的字符串表示形式
3.2. 理解StringBuilder源码
- StringBuilder类底层和String一样,也是一个字符串数组value,但不是final的。变量count表示的是底层字符数组元素的真实个数,不是底层字符数组的长度
- 默认数组的长度是16。也可以通过构造方法直接指定初始长度。length()方法返回的字符数组元素的真实个数,capacity()返回的是底层数组的长度
public class StringBuilder extends AbstractStringBuilder implements Appendable, java.io.Serializable, Comparable, CharSequence {
char[] value;
int count;
public StringBuilder() {
super(16);
}
public StringBuilder(int capacity) {
super(capacity);
}
public int length() {
return count;
}
public int capacity() {
return value.length;
}
public String toString() {
return new String(value, 0, count);
}
}
- 每次添加字符串时要扩容,扩容默认策略是增加到原来长度的2倍+2
public AbstractStringBuilder append(String str) {
if (str == null) {
return appendNull();
}
int len = str.length();
ensureCapacityInternal(count + len);
str.getCharts(0, len, value, count);
count += len;
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
// 如果长度不足,就扩容
int oldCapacity = value.length >> coder;
if (minimumCapacity - oldCapacity > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity) << coder);
}
}
private int newCapacity(int minCapacity) {
int oldLength = value.length;
int newLength = minCapacity << coder;
int growth = newLength - oldLength;
int length = ArraysSupport.newLength(oldLength, growth, oldLength + (2 << coder));
if (length == Integer.MAX_VALUE) {
throw new OutOfMemoryError("Required length exceeds implementation limit");
}
return length >> coder;
}
4. 日期类
用long类型表示时间,如果想获得现在时刻的值,可以使用:long now = System.currentTimeMillis();
4.1. Date类
import java.util.Date;
public class TestDate {
public static void main(String[] args) {
Date now = new Date();
System.out.println(now.toString()); // Thu Aug 01 08:40:07 CST 2024
System.out.println(now.toLocaleString()); // 2024年8月1日 08:40:20
System.out.println(now.getYear()); // 124 1900+124=2024
System.out.println(now.getMonth()); // 7 0-11
System.out.println(now.getDate()); // 1
System.out.println(now.getDay()); // 4-星期
System.out.println(now.getHours()); // 8
System.out.println(now.getMinutes()); // 41
System.out.println(now.getSeconds()); // 8
long l = now.getTime();
System.out.println(l); // 1722520811936
System.out.println(System.currentTimeMillis()); // 1722520811967
System.out.println(System.nanoTime()); // 1735948366006333
Date date1 = new Date(-(long)(1000*60*60*24)*31);
System.out.println(date1.toLocaleString()); // 1969年12月1日 08:00:00
java.sql.Date sDate = new java.sql.Date(System.currentTimeMillis());
System.out.println(sDate); // 2024-08-01
java.sql.Date sDate2 = java.sql.Date.valueOf("2024-08-01");
System.out.println(sDate2); // 2024-08-01
}
}
查看API文档可以看到Date类中很多方法都已经过时了。JDK1.1之后,日期操作一般使用Calendar类,而字符串的转化使用DateFormat类
4.2. 理解Date类的源码
public class Date implements java.io.Serializable, Cloneable, Comparable<Date>{
private transient long fastTime;
public Date() {
this(System.currentTimeMillis());
}
public Date(long date) {
fastTime = date;
}
public long getTime() {
return getTimeImpl();
}
public final long getTimeImpl() {
if(cdate != null && !cdate.isNormalized()) {
normalize();
}
return fastTime;
}
}
Date类中提供一个成员变量fastTime,表示相应时间对应的毫秒数
4.3. DateFormat类
DateFormat是一个抽象类,一般使用他的子类SimpleDateFormat类来实现。作用是把时间对象转化成指定格式的字符串,反之,把指定格式的字符串转为时间对象
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestDateFormat {
public static void main(String[] args) {
// int age = java.lang.Integer.parseInt("123abc");
// System.out.println(age); // 报错
String str1 = "1999-12-23";
java.sql.Date date1 = java.sql.Date.valueOf(str1);
System.out.println(date1.toString()); // 1999-12-23
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String str = "1999-12-23 09:23:45";
Date date = null;
try {
date = dateFormat.parse(str);
}catch (ParseException e) {
e.printStackTrace();
}
System.out.println(date.toString()); // Thu Dec 23 09:23:45 CST 1999
System.out.println(date.toLocaleString()); // 1999年12月23日 09:23:45
DateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh时mm分ss秒"); // 1999年12月23日 09时23分45秒
String str2 = sdf.format(date);
System.out.println(str2);
}
}
4.4. Calendar类
Calendar类是一个抽象类,提供了关于日期计算的功能。GregorianCalendar是Calendar类的一个子类,提供了大多数国家/地区使用的标准日历
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
public class TestCalendar {
public static void main(String[] args) {
Calendar cal = new GregorianCalendar();
System.out.println(cal.get(Calendar.YEAR)); // 2024
System.out.println(cal.get(Calendar.MONTH)); // 7
System.out.println(cal.get(Calendar.DATE)); // 1
System.out.println(cal.get(Calendar.HOUR)); // 10
System.out.println(cal.get(Calendar.MINUTE)); // 22
System.out.println(cal.get(Calendar.SECOND)); // 18
System.out.println(cal.get(Calendar.DAY_OF_WEEK)); // 5
cal.set(Calendar.YEAR, 2000);
int max = cal.getActualMaximum(Calendar.DATE);
System.out.println(max);
for (int i = 1; i <= max; i++) {
System.out.println(cal.get(Calendar.DAY_OF_WEEK)); // 从 2000 年 8 月 1 日开始,依次打印每一天的星期几,直到 8 月 31 日
cal.add(Calendar.DATE, 1);
}
System.out.println(cal.get(Calendar.DAY_OF_WEEK)); // 6
System.out.println(cal.getActualMinimum(Calendar.DATE)); // 1
Date now = new Date();
cal.setTime(now); // Date -> Calendar
Date now2 = cal.getTime(); // Calendar -> Date
}
}
4.5. 使用Calendar类实现万年历
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Scanner;
/**
* 可视化日历 Calendar
* 可以判断这个月一共多少天、某一天是星期几
* 1.实现思路
* 1.按照提示输入任何一个日期
* 2.打印日历
* 1.打印日历头:日 一 二 三 四 五 六
* 2.打印1日之前的空格(循环)
* 3.打印每一天(循环),周六换行
* 2.涉及技能点
* 1.字符串转换成Date:DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Date date = sdf.parse(sdate);
* 2.Date转换成Calendar:cal.setTime(sdate);
* 3.把1999-12-23修改为1999-12-1:cal.set(Calendar.DATE,1);
* 4.判断1999-12-1是星期几:cal.get(Calendar.DAY_OF_WEEK)
* 5.获取当前月的最大天数:cal.getActualMaximum(Calendar.DATE)
* 6.如何判断每天是不是星期六,如果是周六,换行:cal.get(Calendar.DAY_OF_WEEK)==7 cal.get(Calendar.DAY_OF_WEEK)==Calendar.SATURDAY
* 7.判断是不是当天,如果是,打印*:cal.get(Calendar.DATE) *
* 8.如何日期增加1天:cal.add(Calendar.DATE, 1);
*/
public class VisualCalendar {
public static void main(String[] args) {
System.out.println("请输入日期(按照格式:2030-3-10):");
Scanner scanner = new Scanner(System.in);
String temp = scanner.nextLine();
DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
try {
// 1. 将字符串转为日期
Date date = format.parse(temp);
// 2. 将日期转为日历
Calendar calendar = new GregorianCalendar();
calendar.setTime(date);
// 3. 把日期中的Date取出
int day = calendar.get(Calendar.DATE);
// 4. 将日历变为当月的1日
calendar.set(Calendar.DATE, 1);
// 5. 打印日历头信息
System.out.println("日\t一\t二\t三\t四\t五\t六");
// 6. 打印1日之前的空格(要知道1日是星期几)
for (int i = 1; i < calendar.get(Calendar.DAY_OF_WEEK); i++) {
System.out.print("\t");
}
// 7. 获取当月的最大天数
int maxDate = calendar.getActualMaximum(Calendar.DATE);
// 8. 打印日历 1-31/28/30
for (int i = 1; i <= maxDate; i++) {
// 8.1. 如果是当天,打印*
if(i == day) {
System.out.print("*");
}
// 8.2. 打印该天
System.out.print(i + "\t");
// 8.3. 如果是周六,换行
int w = calendar.get(Calendar.DAY_OF_WEEK);
if(w == Calendar.SATURDAY) {
System.out.print("\n");
}
// 8.4. 日历改为下一天
calendar.add(Calendar.DATE, 1);
}
}catch (ParseException e) {
e.printStackTrace();
}
}
}