摘自廖雪峰java教程
String
- 字符串不可变。这种不可变性是通过内部的private final char[]字段,以及没有任何修改char[]的方法实现的
- 两个字符串比较,必须总是使用equals()方法。要忽略大小写比较,使用equalsIgnoreCase()方法
- 方法
"Hello".contains("ll"); // true "Hello".indexOf("l"); // 2 "Hello".lastIndexOf("l"); // 3 "Hello".startsWith("He"); // true "Hello".endsWith("lo"); // true "Hello".substring(2); // "llo" "Hello".substring(2, 4); "ll" " \tHello\r\n ".trim(); // "Hello" "\u3000Hello\u3000".strip(); // "Hello" " Hello ".stripLeading(); // "Hello " " Hello ".stripTrailing(); // " Hello" "".isEmpty(); // true,因为字符串长度为0 " ".isEmpty(); // false,因为字符串长度不为0 " \n".isBlank(); // true,因为只包含空白字符 " Hello ".isBlank(); // false,因为包含非空白字符 "hello".replace('l', 'w'); // "hewwo",所有字符'l'被替换为'w' String s = "A,,B;C ,D"; s.replaceAll("[\\,\\;\\s]+", ","); // "A,B,C,D" String s = "A,B,C,D"; String[] ss = s.split("\\,"); // {"A", "B", "C", "D"} String[] arr = {"A", "B", "C"}; String s = String.join("***", arr); // "A***B***C" System.out.println(String.format("Hi %s, your score is %.2f!", "Bob", 59.5)); String.valueOf(123); // "123" int n2 = Integer.parseInt("ff", 16); // 按十六进制转换,255 char[] cs = "Hello".toCharArray(); // String -> char[] String s = new String(cs); // char[] -> String
- 如果一个类的构造函数使用外部传入的int[]等可能改变的对象,则要使用对象的clone,否则外部改变时会使内部改变
编码
- 英文字母、数字和常用符号的编码,占用一个字节,编码范围从0到127,最高位始终为0,称为ASCII编码
- GB2312标准使用两个字节表示一个汉字,其中第一个字节的最高位始终为1,以便和ASCII编码区分开
- 为了统一全球所有语言的编码,全球统一码联盟发布了Unicode编码,Unicode编码需要两个或者更多字节表示
- 英文字符的Unicode编码高字节总是00,包含大量英文的文本会浪费空间,所以,出现了UTF-8编码,它是一种变长编码,用来把固定长度的Unicode编码变成1~4字节的变长编码。通过UTF-8编码,英文字符’A’的UTF-8编码变为0x41,正好和ASCII码一致。UTF-8编码容错能力强。如果传输过程中某些字符出错,不会影响后续字符,因为UTF-8编码依靠高字节位来确定一个字符究竟是几个字节,它经常用来作为传输编码
- Java的String和char在内存中总是以Unicode编码表示
byte[] b1 = "Hello".getBytes(); // 按系统默认编码转换,不推荐 byte[] b2 = "Hello".getBytes("UTF-8"); // 按UTF-8编码转换 byte[] b2 = "Hello".getBytes("GBK"); // 按GBK编码转换 byte[] b3 = "Hello".getBytes(StandardCharsets.UTF_8); // 按UTF-8编码转换 byte[] b = ... String s1 = new String(b, "GBK"); // 按GBK转换
- 转换编码就是将String和byte[]转换,需要指定编码,转换为byte[]时,始终优先考虑UTF-8编码
StringBuilder
- 用+直接拼接字符串每次都会创造新对象,浪费内存
- 为了高效拼接字符串,Java标准库提供了StringBuilder,它是一个可变对象,可以预分配缓冲区,这样,往StringBuilder中新增字符时,不会创建新的临时对象
StringBuilder sb = new StringBuilder(1024); for (int i = 0; i < 1000; i++) { sb.append(','); sb.append(i); // 链式操作 sb.append(10).append(11); } String s = sb.toString(); // 链式操作的关键在于append返回this,可以不断调用自身的其他方法
- 对于普通的字符串+操作,并不需要我们将其改写为StringBuilder,因为Java编译器在编译时就自动把多个连续的+操作编码为StringConcatFactory的操作,循环中不用+
包装类型
- 基本类型:byte,short,int,long,boolean,float,double,char
- 引用类型:所有class和interface类型
- 引用类型可以赋值为null,表示空,但基本类型不能赋值为null
- 为了把基本类型变为引用类型,引入包装类
Integer n = null; Integer n2 = new Integer(99); int n3 = n2.intValue(); // 创建Integer的时候推荐以下静态工厂方法而不是new,尽可能地返回缓存的实例以节省内存 Integer n = Integer.valueOf(100);
- 所有的包装类型都是不变类,对两个Integer实例进行比较要特别注意:绝对不能用==比较,因为Integer是引用类型,必须使用equals()比较
- 其他用法
System.out.println(Integer.toString(100, 36)); // "2s",表示为36进制 int max = Integer.MAX_VALUE; // 2147483647 int min = Integer.MIN_VALUE; // -2147483648 // long类型占用的bit和byte数量: int sizeOfLong = Long.SIZE; // 64 (bits) int bytesOfLong = Long.BYTES; // 8 (bytes) byte x = -1; byte y = 127; System.out.println(Byte.toUnsignedInt(x)); // 255 System.out.println(Byte.toUnsignedInt(y)); // 127
JavaBean
- 如果一个类有若干private实例字段,读写方法符合命名规范:public Type getXyz()和public void setXyz(Type value),那么这种class被称为JavaBean
- boolean字段比较特殊,它的读方法一般命名为isXyz():public boolean isChild()
- 读方法和写方法称为属性只读,只写
枚举类
- 为了让编译器能自动检查某个值在枚举的集合内,并且,不同用途的枚举需要不同的类型来标记,不能混用,我们可以使用enum来定义枚举类
- enum类型的每个常量在JVM中只有一个唯一实例,所以可以直接用==比较
- 定义的enum类型总是继承自java.lang.Enum,且无法被继承
- 只能定义出enum的实例,而无法通过new操作符创建enum的实例
enum Weekday { SUN, MON, TUE, WED, THU, FRI, SAT; } String s = Weekday.SUN.name(); // "SUN" int n = Weekday.MON.ordinal(); // 1,无意义
不变类
- 不变类具有以下特点:
- 定义class时使用final,无法派生子类
- 每个字段使用final,保证创建实例后无法修改任何字段
- 覆写equals,hashcode等方法以方便使用
public final class Point { private final int x; private final int y; public Point(int x, int y) { this.x = x; this.y = y; } public int x() { return this.x; } public int y() { return this.y; } }
常用工具类
// ---Math---
Math.abs(-100); // 100
Math.max(100, 99); // 100
Math.pow(2, 10); // 2的10次方=1024
Math.sqrt(2); // 1.414...
Math.exp(2); // 7.389...e的x次方
Math.log(4); // 1.386...以e为底
Math.log10(100); // 2
Math.sin(3.14); // 0.00159...
double pi = Math.PI; // 3.14159...
double e = Math.E; // 2.7182818...
Math.random(); // 0.53907... 每次都不一样 0 <= x < 1
// ---Random---
// 默认用时间戳做种子
Random r = new Random();
r.nextInt(); // 2071575453,每次都不一样
r.nextInt(10); // 5,生成一个[0,10)之间的int
r.nextLong(); // 8811649292570369305,每次都不一样
r.nextFloat(); // 0.54335...生成一个[0,1)之间的float
r.nextDouble(); // 0.3716...生成一个[0,1)之间的double
Random r = new Random(12345);
SecureRandom sr = new SecureRandom();// 不能指定种子