本文主要内容为介绍Java核心类,包括字符串、StringBuilder、包装类型、JavaBean等。
String
String
是一个引用类型,它本身也是一个class
。
1 | String s1 = "Hello"; |
字符串不可变,不可变性是通过内部的private final char[]
字段,以及没有任何修改char[]
的方法实现的。
字符串比较
实际上是想比较字符串的内容是否相同,必须使用equals()
方法而不能用==
。
Java编译器在编译期,会自动把所有相同的字符串当作一个对象放入常量池。
1 | public class Main { |
输出:
1 | false |
要忽略大小写比较,使用equalsIgnoreCase()
方法。
equal>
public boolean equals(Object obj)
equals 方法在非空对象引用上实现相等关系:
- 自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true。
- 对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。
- 传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回true。
- 一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改。
- 对于任何非空引用值 x,x.equals(null) 都应返回 false。
注意:>当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常
规协定,该协定声明相等对象必须具有相等的哈希码。
参数:
obj - 要与之比较的引用对象。
返回:
如果此对象与 obj 参数相同,则返回 true;否则返回 false。
返回字符索引、提取子字符串
1 | // 是否包含子串: |
去除首尾空白
trim()
移除字符串首尾的空格,\t
,\r
,\n
1 | String s3 = " \tHello\r\n ".trim(); // "Hello" |
trim()
并没有改变字符串的内容,而是返回了一个新字符串。(s3=”Hello”)
strip()
JAVA11(JDK11)中的strip()方法,适用于字符首尾空白是Unicode空白字符的情况。
类似**中文的空格字符\u3000
**也会被移除。
Character c = ‘\u2000’;中’\u2000’就是Unicdoe空白字符。
isEmpty() 和 isBlank()
isEmpty()判断字符串长度是否为0
isBlank()判断字符串是否由空白字符组成
1 | "".isEmpty(); // true,因为字符串长度为0 |
替换子串
根据字符串替换
1 | String s = "hello"; |
正则表达式替换
1 | String s = "A,,B;C ,D"; |
分割字符串
1 | String s = "A,B,C,D"; |
使用split()方法,传入的参数也是正则表达式。
字符串拼接
1 | String[] arr = {"A", "B", "C"}; |
join()方法用指定的字符串连接字符数组各元素。
转换类型
转换为字符串
把任意基本类型或引用类型转换为字符串,可以使用静态方法valueOf()
。这是一个重载方法,编译器会根据参数自动选择合适的方法:
1 | String.valueOf(123); // "123" |
字符串转换为int
1 | int n1 = Integer.parseInt("123"); // 123 |
字符串转换为boolean
1 | boolean b1 = Boolean.parseBoolean("true"); // true |
转换为char[]
1 | char[] cs = "Hello".toCharArray(); // String -> char[] |
通过new String(char[])
创建新的String
实例时,不会直接引用传入的char[]
数组,而是会复制一份,所以,修改外部的char[]
数组不会影响String
实例内部的char[]
数组,因为这是两个不同的数组。
小结
- Java字符串
String
是不可变对象; - 字符串操作不改变原字符串内容,而是返回新字符串;
- 常用的字符串操作:提取子串、查找、替换、大小写转换等;
- Java使用Unicode编码表示
String
和char
; - 转换编码就是将
String
和byte[]
转换,需要指定编码; - 转换为
byte[]
时,始终优先考虑UTF-8
编码。
StringBuilder
使用+连接字符串时,每次都会创建新的字符串对象,影响GC效率。
Java标准库提供了StringBuilder
,它是一个可变对象,可以预分配缓冲区,这样,往StringBuilder
中新增字符时,不会创建新的临时对象:
1 | public class Main { |
输出:
1 | 0,1,2,3,4,5,6,7,8,9, |
StringBuilder
进行链式操作:
1 | public class Main { |
输出:
1 | Hello, Mr Bob! |
查看StringBuilder
的源码,定义的append()方法会返回this,这样可以不断调用自身的其他方法。
1 | public class Main { |
输出:
1 | 19 |
adder.add(3)
.add(5)
.inc()
.add(10);
add方法返回this,即哪个对象调用add方法,则返回哪个对象,此处返回adder对象,因此可以继续调用自身的其他方法。
StringBuffer
是Java早期的一个StringBuilder
的线程安全版本,通过同步来保证多个线程操作StringBuffer
是安全的,但是同步会带来执行速度的下降。
StringBuilder
和StringBuffer
接口完全相同,推荐使用StringBuilder。
StringJoiner
实际上是运用了StringBuilder的一个拼接字符串的封装处理,用于构造由分隔符分隔的字符序列,并可选择性地从提供的前缀开始和以提供的后缀结尾。
1 | import java.util.StringJoiner; |
输出:
1 | [George:Sally:Fred] |
StringJoiner类源码:
1 | package java.util; |
String.join()
String
还提供了一个静态方法join()
,方法在内部使用了StringJoiner
来拼接字符串。在不需要指定“开头”和“结尾”的时候,推荐String.join()
:
1 | import java.util.StringJoiner; |
输出:
1 | Bob, Alice, Grace |
包装类型
Java的数据类型分两种:
- 基本类型:
byte
,short
,int
,long
,boolean
,float
,double
,char
- 引用类型:所有
class
和interface
类型
引用类型可以赋值为null
,表示空,但基本类型不能赋值为null
。
Java核心库为每种基本类型都提供了对应的包装类型:
基本类型 | 对应的引用类型 |
---|---|
boolean | java.lang.Boolean |
byte | java.lang.Byte |
short | java.lang.Short |
int | java.lang.Integer |
long | java.lang.Long |
float | java.lang.Float |
double | java.lang.Double |
char | java.lang.Character |
1 | public class Main { |
自动装箱(Auto Boxing)和自动拆箱(Auto Unboxing)
把int
变为Integer
的赋值写法,称为自动装箱(Auto Boxing);
把Integer
变为int
的赋值写法,称为自动拆箱(Auto Unboxing)。
注意:自动装箱和自动拆箱只发生在编译阶段,目的是为了少写代码。
int和
Integer`之间转型:
1 | int i = 100; |
Java编译器可以帮助我们自动在int
和Integer
之间转型:
1 | Integer n = 100; // 编译器自动使用Integer.valueOf(int) |
不变类
所有的包装类型都是不变类。
Integer
的核心代码如下:
1 | public final class Integer { |
因此,一旦创建了Integer
对象,该对象就是不变的。
对两个Integer
实例进行比较要特别注意>:不能用==
比较,因为Integer
是引用类型,必须使用equals()
比较
1 | public class Main { |
解释:
可以发现,==
比较,较小的两个相同的Integer
返回true
,较大的两个相同的Integer
返回false
,这是因为Integer
是不变类,编译器把Integer x = 127;
自动变为Integer x = Integer.valueOf(127);
为了节省内存,Integer.valueOf()
对于较小的数,始终返回相同的实例,因此,==
比较“恰好”为true
。
绝不能因为Java标准库的Integer
内部有缓存优化就用==
比较,必须用equals()
方法比较两个Integer
。
自己创建Integer
的时候,以下两种方法:
- 方法1:
Integer n = new Integer(100);
- 方法2:
Integer n = Integer.valueOf(100);
推荐方法2,因为方法1总是创建新的Integer
实例,方法2为缓存实例。
能创建“新”对象的静态方法称为静态工厂方法。Integer.valueOf()
就是静态工厂方法,它尽可能地返回缓存的实例以节省内存。
创建新对象时,优先选用静态工厂方法而不是new操作符。
进制转换
1 | public class Main { |
所有的整数和浮点数的包装类型都继承自Number
,因此,可以非常方便地直接通过包装类型获取各种基本类型。
处理无符号整型
在Java中,并没有无符号整型(Unsigned)的基本数据类型。byte
、short
、int
和long
都是带符号整型,最高位是符号位。无符号整型和有符号整型的转换在Java中就需要借助包装类型的静态方法完成。
1 | public class Main { |
JavaBean
JavaBean 与其它 Java 类相比而言独一无二的特征:
- 提供一个默认的无参构造函数。
- 需要被序列化并且实现了 Serializable 接口(新增)。
- 可能有一系列可读写属性。
- 可能有一系列的 getter 或 setter 方法。
- 属性必须私有化。
- 私有化的属性必须通过public类型的方法暴露给其它程序,并且方法的命名也必须遵守一定的命名规范
1 | public class Ma { |
JavaBean的作用
JavaBean主要用来传递数据,即把一组数据组合成一个JavaBean便于传输。此外,JavaBean可以方便地被IDE工具分析,生成读写属性的代码,主要用在图形界面的可视化设计中。
通过IDE,可以快速生成getter
和setter
。例如,在Eclipse中,先输入以下代码:
1 | public class Person { |
然后,点击右键,在弹出的菜单中选择“Source”,“Generate Getters and Setters”,在弹出的对话框中选中需要生成getter
和setter
方法的字段,点击确定即可由IDE自动完成所有方法代码。
enum枚举类
首先,enum
常量本身带有类型信息,即Weekday.SUN
类型是Weekday
,编译器会自动检查出类型错误。
其次,不可能引用到非枚举的值,因为无法通过编译。
最后,不同类型的枚举不能互相比较或者赋值,因为类型不符。
编译器可以在编译期自动检查出所有可能的潜在错误。
引用类型比较,要使用equals()
方法,如果使用==
比较,它比较的是两个引用类型的变量是否是同一个对象。因此,引用类型比较,要始终使用equals()
方法。
enum
定义的枚举类是一种引用类型,enum
类型可以例外,因为enum
类型的每个常量在JVM中只有一个唯一实例,所以可以直接用==
比较:
1 | if (day == Weekday.FRI) { // ok! |
enum类型
enum
定义的类型就是class
,有以下几个特点:
- 定义的
enum
类型总是继承自java.lang.Enum
,且无法被继承; - 只能定义出
enum
的实例,而无法通过new
操作符创建enum
的实例; - 定义的每个实例都是引用类型的唯一实例;
- 可以将
enum
类型用于switch
语句。
定义的Color
枚举类:
1 | public enum Color { |
编译器编译出的class
大概就像这样:
1 | public final class Color extends Enum { // 继承自Enum,标记为final class |
工具类
Math
求绝对值:
1 | Math.abs(-100); // 100 |
取最大或最小值:
1 | Math.max(100, 99); // 100 |
计算xy次方:
1 | Math.pow(2, 10); // 2的10次方=1024 |
计算√x:
1 | Math.sqrt(2); // 1.414... |
计算ex次方:
1 | Math.exp(2); // 7.389... |
计算以e为底的对数:
1 | Math.log(4); // 1.386... |
计算以10为底的对数:
1 | Math.log10(100); // 2 |
三角函数:
1 | Math.sin(3.14); // 0.00159... |
Math还提供了几个数学常量:
1 | double pi = Math.PI; // 3.14159... |
生成一个随机数x,x的范围是0 <= x < 1
:
1 | Math.random(); // 0.53907... 每次都不一样 |
Random
Random
用来创建伪随机数,只要给定一个初始的种子,产生的随机数序列是完全一样的。
Math.random()
实际上内部调用了Random
类
要生成一个随机数,可以使用nextInt()
、nextLong()
、nextFloat()
、nextDouble()
:
1 | Random r = new Random(); |
SecureRandom
SecureRandom
就是用来创建安全的随机数的:
1 | SecureRandom sr = new SecureRandom(); |