字符串
字符串很常用,且字符串具有不可变特性.
用于比较字符串是否相同,用字符串自带的equals方法才能进行内容上的比较.
java编译器,在编译的时候,会自动把相同字符串当作一个对象放入常量池.
public class Main {
public static void main(String[] args) {
String s1 = "hello";
String s2 = "HELLO".toLowerCase();
System.out.println(s1 == s2); // false
System.out.println(s1.equals(s2)); // true
}
}
忽忽略大小写的方法:equalsIgnoreCase()方法.
String类提供了常用的方法:搜索子串,提取子串.
"Hello".contains("ll"); // 是否包含子串:true
"Hello".indexOf("l"); // "l"在"Hello"的位置:2
"Hello".lastIndexOf("l"); // 最后一个"l"在"Hello"的位置: 3
"Hello".startsWith("He"); // 是否以"He"开头
"Hello".endsWith("lo"); // 是否以"lo"结尾
String类还可以利用trim(),strip(),stripLeading(),stripTrailing()去除首位空白字符.空白字符包括空格,\t,\r,\n.
" \tHello\r\n ".trim(); // 代表"Hello"
"\u3000Hello\u3000".strip(); // "Hello",/u3000是中文的空格字符
" Hello ".stripLeading(); // "Hello "
" Hello ".stripTrailing(); // " Hello"
String类用replace替换子串.
String s = "hello";
s.replace('l', 'w'); // "hewwo",所有字符'l'被替换为'w'
s.replace("ll", "~~"); // "he~~o",所有子串"ll"被替换为"~~"
String类用splite()分割字符串,分割之后的字符串是一个字符串数组.
String s = "A,B,C,D";
String[] ss = s.split("\\,"); // {"A", "B", "C", "D"} , 按照参数隔开
String拼接除了用+,还可以用join()实例方法.
String[] arr = {"A", "B", "C"};
String s = String.join("***", arr); // "A***B***C"
String类型转换可以将基本类型转换成String类型,用valueOf()实现
.将字符串类型转换为其他类型,用parseInt()或者parseBoolean()等等.
String类型还可以转换为char[]数组类型.
String.valueOf(123); // "123"
String.valueOf(45.67); // "45.67"
String.valueOf(true); // "true"
int n1 = Integer.parseInt("123"); // 123
boolean b1 = Boolean.parseBoolean("true"); // true
char[] cs = "Hello".toCharArray(); // String -> char[]
StringBuilder
StringBuilder实例化之后会分配一个缓冲区来存放字符串,通过append(…),insert(…,…)来增加内容,delete(…,…)来删除内容.有点儿像链表一样.而且定义的append()方法会返回自身.
append()方法是依次增加内容.StringBuilder类是用来高效拼接字符串.
var sb = new StringBuilder();
sb.append("Mr ")
.append("Bob")
.append("!")
.insert(0, "Hello, "); // insert是在具体位置插入
StringJoiner
用分割符号,来高效拼接字符串,使用StringJoiner类.
String[] names = {"Bob", "Alice", "Grace"};
var sj = new StringJoiner(", ", "Hello ", "!"); // 构造参数分别是分隔符号 , 开头 , 结尾.
for (String name : names) {
sj.add(name); // 利用add(String)来拼接.
}
包装类型
如何将基本类型变成对象,可以利用包装类型Integer,Integer等等如下图:
包装类型也可以赋值为null,也可以用new初始化:
Integer n = null;
Integer n2 = new Integer(99);
int n3 = n2.intValue(); // 将n2转换为基本类型
Integer n4 =Integer.valueOf(n3); // 将n3转换为包装类型
Integer n = 100; // 编译器自动使用Integer.valueOf(int)
int x = n; // 编译器自动使用Integer.intValue()
将int通过静态方法valueOf()转换为Integer称为自动装箱,反之称为自动拆箱.如果将null自动拆箱,就会产生异常.所有的包装类型都具有不可变特性,和String类很像.
我们把能创建新对象的静态方法称为静态工厂方法,Integer.valueOf()就是静态工厂方法,它是尽可能返回缓存的实例来节省内存,意思就是说,如果使用Integer.valueOf(),会利用缓存来查找是否存在相同的值,如果存在则返回该实例,不存在就new一个.
包装类本身还存在大量方法,还可以用其静态方法进行进制转化.
int x1 = Integer.parseInt("100"); // 100
int x2 = Integer.parseInt("100", 16); // 256,因为按16进制解析
System.out.println(Integer.toString(100, 2)); // 转换为2进制数:1100100
System.out.println(Integer.toString(100, 8)); // 转换为8进制数:144
System.out.println(Integer.toString(100, 16)); // 转换为16进制数:64
枚举类型
和普通的枚举意义一样,在java中,枚举也是类.
例如:
public class Main {
public static void main(String[] args) {
Weekday day = Weekday.SUN;
if (day == Weekday.SAT || day == Weekday.SUN) {
System.out.println("Work at home!");
} else {
System.out.println("Work at office!");
}
}
}
enum Weekday {
SUN, MON, TUE, WED, THU, FRI, SAT;
}
使用enum定义的枚举类是一种引用类型。引用类型比较,要使用equals()方法,如果使用==比较,它比较的是两个引用类型的变量是否是同一个对象。因此,引用类型比较,要始终使用equals()方法,但enum类型可以例外。
enum类型和类没有区别,有以下特点:
- 定义的enum类型总是继承自java.lang.Enum,且无法被继承;
- 只能定义出enum的实例,而无法通过new操作符创建enum的实例;
- 定义的每个实例都是引用类型的唯一实例;
- 可以将enum类型用于switch语句。
所以,每一个枚举值都是对象或者实例.
enum Weekday {
SUN, MON, TUE, WED, THU, FRI, SAT;
}
所以枚举也存在一系列方法,来做一些事情.
String s = Weekday.SUN.name(); // "SUN",返回常量名
int n = Weekday.MON.ordinal(); // 1,返回定义的常量的顺序,从0开始计数
由于每个枚举值都是实例,而且枚举值()括号中的内容会映射到java.lang.Enum类来构造枚举值,简单来说就是枚举类型中的枚举值都会对应调用一次构造函数.
自己定义的枚举构造函数一定是私有的,因为是自己内部访问,枚举是不能让外部来进行实例化.
enum Weekday {
MON(1), TUE(2), WED(3), THU(4), FRI(5), SAT(6), SUN(0);
public final int dayValue;
private Weekday(int dayValue) {
this.dayValue = dayValue;
}
}
或者
enum Weekday {
MON(1, "星期一"), TUE(2, "星期二"), WED(3, "星期三"), THU(4, "星期四"), FRI(5, "星期五"), SAT(6, "星期六"), SUN(0, "星期日");
public final int dayValue;
private final String chinese;
private Weekday(int dayValue, String chinese) {
this.dayValue = dayValue;
this.chinese = chinese;
}
public String getNmae() {
return this.chinese;
}
}
BigInteger
在java中表示最大整数的基本类型就是long,如果超过了long,就用BigInteger来表示人一大小的整数,但是计算速度会较慢.
BigInteger bi = new BigInteger("1234567890");
System.out.println(bi.pow(5)); // pow用来计算幂次方的数: 2867971860299718107233761438093672048294900000
BigInteger会转换成long型,但是如果超出long的范围就会抛出异常.通过longalue()方法转换.
BigInteger i = new BigInteger("123456789000");
System.out.println(i.longValue()); // 123456789000
System.out.println(i.multiply(i).longValueExact()); // java.lang.ArithmeticException: BigInteger out of long range
同理,也可以转换为其它基本类型:
转换为byte:byteValue()
转换为short:shortValue()
转换为int:intValue()
转换为long:longValue()
转换为float:floatValue()
转换为double:doubleValue()
BigDecimal
和BigInteger类似,BigDecimal可以表示一个任意大小且京都完全正确的浮点数.
BigDecimal bd = new BigDecimal("123.4567");
System.out.println(bd.multiply(bd)); // multiply(bd)表示乘bd,表示:15241.55677489
BigDecimal用scale()表示小数点,例如:
BigDecimal d1 = new BigDecimal("123.45");
BigDecimal d2 = new BigDecimal("123.4500");
BigDecimal d3 = new BigDecimal("1234500");
System.out.println(d1.scale()); // 2,两位小数
System.out.println(d2.scale()); // 4
System.out.println(d3.scale()); // 0
对于一个BigDecimal可以设置精度,设置为直接截断和四射五入.
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Main {
public static void main(String[] args) {
BigDecimal d1 = new BigDecimal("123.456789");
BigDecimal d2 = d1.setScale(4, RoundingMode.HALF_UP); // 四舍五入,123.4568
BigDecimal d3 = d1.setScale(4, RoundingMode.DOWN); // 直接截断,123.4567
System.out.println(d2); // 123.4568
System.out.println(d3); // 123.4567
}
}
对于BigDecimal做加减乘的时候,精度不会丢失,但是作除法除不尽的情况下,必须设置精度,否则就会抛出异常.
BigDecimal d1 = new BigDecimal("123.456");
BigDecimal d2 = new BigDecimal("23.456789");
BigDecimal d3 = d1.divide(d2, 10, RoundingMode.HALF_UP); // 保留10位小数并四舍五入
BigDecimal d4 = d1.divide(d2); // 报错:ArithmeticException,因为除不尽
调用调用divideAndRemainder()方法时,返回的数组包含两个BigDecimal,分别是商和余数.
BigDecimal n = new BigDecimal("12.75");
BigDecimal m = new BigDecimal("0.15");
BigDecimal[] dr = n.divideAndRemainder(m);
System.out.println(dr[0]); // 85
System.out.println(dr[1]); // 0.00
两个Bignteger的值是否相同,用equals()方法比较,不仅大小相同,而且精度必须一样.
BigDecimal d1 = new BigDecimal("123.456");
BigDecimal d2 = new BigDecimal("123.45600");
System.out.println(d1.equals(d2)); // false,因为scale不同
System.out.println(d1.equals(d2.stripTrailingZeros())); // true,因为d2去除尾部0后scale变为2
System.out.println(d1.compareTo(d2)); // 0,compareTo()方法比较,用负数,0,正数,分别表示大于等于小于.
生成随机数
和c/c++类似,java有自动生成伪随机数的函数Random类下的函数生成随机数,如果不传入参数来确定一个种子,就会自动用操作系统上的时间辍来作为种子,因此得到的随机数列也不一样.
要生成一个随机数,可以使用Random类中的nextInt()、nextLong()、nextFloat()、nextDouble():
Random r = new Random();
r.nextInt(); // 2071575453,每次都不一样
r.nextInt(10); // 生成一个[0,10)之间的int
r.nextLong(); // 8811649292570369305,每次都不一样
r.nextFloat(); // 0.54335...生成一个[0,1)之间的float
r.nextDouble(); // 0.3716...生成一个[0,1)之间的double
如果指定了种子,那么为随机数列的特性也体现出来了,每次的随机数列都是一样的.
Random r = new Random(1024);
r.nextInt(); // 2071575453,每次都不一样
r.nextInt(10); // 生成一个[0,10)之间的int
r.nextLong(); // 8811649292570369305,每次都不一样
r.nextFloat(); // 0.54335...生成一个[0,1)之间的float
r.nextDouble(); // 0.3716...生成一个[0,1)之间的double
真随机数,用SecureRandom类实现,使用方法和Random类似,不过是比较安全的.
Random r = new Random(1024);
r.nextInt(); // 2071575453,每次都不一样
r.nextInt(10); // 生成一个[0,10)之间的int
r.nextLong(); // 8811649292570369305,每次都不一样
r.nextFloat(); // 0.54335...生成一个[0,1)之间的float
r.nextDouble(); // 0.3716...生成一个[0,1)之间的double