Java笔记六——Java核心类

it2025-12-09  8

字符串和编码

String

String是引用类型,它也是一个class,"…"表示一个字符串 String s = "Hello!"; String内部是通过一个char[ ]数组表示的 String s = new String(new char[ ]{‘H’, ‘e’, ‘l’, ‘l’, ‘o’, ‘!’} );`

字符串比较

比较字符串内容是否相同,必须用equal()方法而不能用==。 Java字符串具有不可变性,字符串生成后就存放在常量池中,引用的指向可以改变,但是字符串内容不可变

String s1 = "hello";//s1指向“hello” String s2 = "hello";//s2指向“hello” String s3 = "HELLO".toLowerCase();//创建新对象“HELLO” System.out.println(s1==s2);//true,因为s1、s2都指向hello这同一个对象 System.out.println(s1==s3);//false,s1、s3的引用不同 System.out.println(s1.equals(s3));//true,字符串内容相同

String类常用方法

搜索子串

"Hello".contains("ll");//true,注意contains()方法的参数是CharSequence,是String的父类 "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",提取到4-1=3那个字符

去除首尾空白字符

*下面这些方法返回的是新字符串,旧字符串还在常量池里,字符串内容具有不变性 * trim()方法可以移除字符串首位空白字符,空白字符包括空格,\t, \r, \n strip()方法类似中文的空格字符\u3000也会被移除 String提供isEmpty()和isBlank()来判断字符串是否为空和空白字符串

" \tHello\r\n".trim();//"Hello" "\u3000Hello\n".strip();//"Hello" " Hello ".stripLeading();//"Hello " 去除开头空白字符 " Hello ".stripTrailing();//" Hello" 去除尾部空白字符 "".isEmpty();//true,因为字符串长度为0,这里注意一点和C语言不同,Java字符串没有以'\0'结尾 " \n".isBlank();//true,只包含空白字符

替换子串

根据字符或字符串替换,通过正则表达式替换

String s = "hello"; s.replace('l','w');//"hewwo" s.replace("ll","~~");//"he~~o" String s = "A,,B;C ,D"; s.replaceAll("[\\,\\;\\s]+", ","); // "A,B,C,D"

上面的代码通过正则表达式,把匹配的子串同意替换为","

分割字符串

使用split()方法,传入的也是正则表达式

String s = "A,B,C,D"; String[] ss = s.split("\\,"); // {"A", "B", "C", "D"}

拼接字符串

静态方法join(),用指定的字符串连接字符串数组

String[] arr = {"A", "B", "C"}; String s = String.join("***", arr); // "A***B***C"

格式化字符串

字符串提供了formatted()方法和format()静态方法,可以传入其他参数,替换占位符,然后生成新的字符串:

public class Main(){ public static void main(String[] args){ String s = "Hi %s, your score is %d!"; System.out.println(s.formatted(Alice, 80)); System.out.println(String.format("Hi %s, your score is %.2f!", "Bob", 59.5)); } }

占位符和参数一致(类型,数量)。常用的占位符: %s:显示字符串 %d:显示整型 %x:显示十六进制整数 %f:显示浮点数

类型转换

使用静态方法valueOf(),把任意基本类型或者引用类型转换为字符串

String.valueOf(123);//"123" String.valueOf(45.67); // "45.67" String.valueOf(true); // "true" String.valueOf(new Object()); // 类似java.lang.Object@636be97

把字符串转换为其他类型

int n1 = Integer.parseInt("123");//123 int n2 = Integer.parseInt("ff",16);//按十六进制转换,255 //字符串转换为boolean类型 boolean b1 = Boolean.parseBoolean("ture");//true boolean b2 = Boolean.parseBoolean("FALSE");//false

要特别注意,Integer有个getInteger(String)方法,它不是将字符串转换为int,而是把该字符串对应的系统变量转换为Integer: String和char[ ]类型可以互相转换

char[] cs = "Hello".toCharArray(); // String -> char[] String s = new String(cs); // char[] -> String

字符编码

Java的String和char在内存中总是以Unicode编码表示。 关于字符编码的详细解释可以参考廖雪峰老师的Java教程 字符编码

小结 Java字符串String是不可变对象; 字符串操作不改变原字符串内容,而是返回新字符串; 常用的字符串操作:提取子串、查找、替换、大小写转换等; Java使用Unicode编码表示String和char; 转换编码就是将String和byte[]转换,需要指定编码; 转换为byte[]时,始终优先考虑UTF-8编码。

StringBuilder

直接用+拼接字符串,每次拼接都会创建新的String对象,浪费内存 高效拼接内存->StringBuilder(Java标准库提供)

StringBuilder sb = new StringBuilder(1024);//参数是缓冲区大小 for(int i = 0; i < 1000; i++){ sb.append(','); sb.append(i); } String s = sb.toString();

StringBuilder还可以进行链式操作,每次append返回this对象

public class Main { public static void main(String[] args) { var sb = new StringBuilder(1024); sb.append("Mr ") .append("Bob") .append("!") .insert(0, "Hello, "); System.out.println(sb.toString()); } } //Hello, Mr Bob!

小结 StringBuilder是可变对象,用来高效拼接字符串; StringBuilder可以支持链式操作,实现链式操作的关键是返回实例本身; StringBuffer是StringBuilder的线程安全版本,现在很少使用

StringJoiner

用分隔符拼接字符串数组的需求很常见,Java标准库提供了StringJoiner来实现,还可以指定“开头”和“结尾”

import java.util.StringJoiner; public class Main { public static void main(String[] args) { String[] names = {"Bob", "Alice", "Grace"}; var sj = new StringJoiner(", ", "Hello ", "!"); for (String name : names) { sj.add(name); } System.out.println(sj.toString()); } }

包装类型

Java数据类型

基本类型:byte,short,int,long,boolean,float,double,char引用类型:所有class和interface类型,引用类型可以复制为null,

将基本类型变成引用类型->包装类型

public class Main { public static void main(String[] args) { int i = 100; // 通过new操作符创建Integer实例(不推荐使用,会有编译警告): Integer n1 = new Integer(i); // 通过静态方法valueOf(int)创建Integer实例: Integer n2 = Integer.valueOf(i); // 通过静态方法valueOf(String)创建Integer实例: Integer n3 = Integer.valueOf("100"); System.out.println(n3.intValue()); } }

自动装箱和拆箱

自动装箱和拆箱发生在编译阶段,目的是少写代码

Integer n = 100; // 编译器自动使用Integer.valueOf(int) int x = n; // 编译器自动使用Integer.intValue()

引用类型的比较一定要用equals() ** 创建新对象时,优先选用静态工厂方法而不是new操作符。** 例如 优先方法2 方法1:Integer n = new Integer(100); 方法2:Integer n = Integer.valueOf(100);

进制转换

Interger类中的静态方法parsenInt()可以把字符串解析成整数

int x1 = Integer.parseInt("100"); // 100 int x2 = Integer.parseInt("100", 16); // 256,因为按16进制解析

所有的整数和浮点数都继承自Number

处理无符号数

Java并没有无符号整型(Unsigned)的基本数据类型。无符号整型和有符号整型的转换需要借助包装类型的静态方法完成 例如byte是有符号整形,范围是-128+127,但是如果把byte看成无符号整型,范围就是0255。

public class Main { public static void main(String[] args) { byte x = -1; byte y = 127; System.out.println(Byte.toUnsignedInt(x)); // 255 System.out.println(Byte.toUnsignedInt(y)); // 127 } }

小结 Java核心库提供的包装类型可以把基本类型包装为class; 自动装箱和自动拆箱都是在编译期完成的(JDK>=1.5); 装箱和拆箱会影响执行效率,且拆箱时可能发生NullPointerException; 包装类型的比较必须使用equals(); 整数和浮点数的包装类型都继承自Number; 包装类型提供了大量实用方法。

JavaBean

JavaBean是一种符合命名规范的class,它通过getter和setter来定义属性; 属性是一种通用的叫法,并非Java语法规定; 可以利用IDE快速生成getter和setter; 使用Introspector.getBeanInfo()可以获取属性列表。

枚举类

使用enum来定义枚举类

enum Weekday { SUN, MON, TUE, WED, THU, FRI, SAT; }

使用enum定义枚举的好处:num常量带有类型信息,赋值时如果类型不匹配,编译器会报错

enum的比较

==比较的是两个引用类型的变量指向是否一致(是否指向同一个对象) equals()比较的是变量值 引用类型的比较一定要用equals()方法,但是enum类型可以例外,因enum类型的每个常量在JVM中只有一个唯一实例,所以可以直接用==比较

enum类型的方法

name()

返回常量名

String s = Weekday.SUN.name(); // "SUN"

ordinal()

返回定义的常量的顺序,从0开始计数 int n = weekday.MON.ordinal();//1

enum类型用于switch语句

public class Main { public static void main(String[] args) { Weekday day = Weekday.SUN; switch(day) { case MON: case TUE: case WED: case THU: case FRI: System.out.println("Today is " + day + ". Work at office!"); break; case SAT: case SUN: System.out.println("Today is " + day + ". Work at home!"); break; default: throw new RuntimeException("cannot process " + day); } } } enum Weekday { MON, TUE, WED, THU, FRI, SAT, SUN; }

加上default语句,可以在漏写某个枚举常量时自动报错,从而及时发现错误。

小结 Java使用enum定义枚举类型,它被编译器编译为final class Xxx extends Enum { … }; 通过name()获取常量定义的字符串,注意不要使用toString(); 通过ordinal()返回常量定义的顺序(无实质意义); 可以为enum编写构造方法、字段和方法 enum的构造方法要声明为private,字段强烈建议声明为final; enum适合用在switch语句中。

记录类

String,Integer都是不变类 Java 14开始,引入了新的Record类,使用关键字record定义Record类 例如定义一个Point类,他是Record类

public class Main(){ public static void main(String[ ] args) { Point p = new point(123,456); System.out.println(p.x()); System.out.println(p.y()); System.out.println(p); } } public record Point(int x, int y){}/

自动用final修饰class以及每个地段,自动创建构造方法,重写toString()、equals()、hashCode()方法名 小结 小结 从Java 14开始,提供新的record关键字,可以非常方便地定义Data Class: 使用record定义的是不变类; 可以编写Compact Constructor对参数进行验证; 可以定义静态方法。

BigInteger

BigInteger内部用一个int[ ]数组来模拟超大整数

import java.math.BigInteger BigInteger bi = new BigInteger("1234567890"); System.out.println(bi.pow(5)); // 2867971860299718107233761438093672048294900000

只能用实例方法对BigInteger做运算(不能用±这些)

BigInteger i1 = new BigInteger("1234567890"); BigInteger i2 = new BigInteger("12345678901234567890"); BigInteger sum = i1.add(i2); // 1234567890246913578

BigInteger继承自Number类,Number定义了转换为基本类型的几个方法 转换为byte:byteValue() 转换为short:shortValue() 转换为int:intValue() 转换为long:longValue() 转换为float:floatValue() 转换为double:doubleValue() BigInteger转换成基本类型会有高位丢失的风险(溢出),可以使用intValueExact()、longValueExact()等方法,在转换时如果超出范围,将直接抛出ArithmeticException异常。

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

小结 BigInteger用于表示任意大小的整数; BigInteger是不变类,并且继承自Number; 将BigInteger转换成基本类型时可使用longValueExact()等方法保证结果准确。

BigDecimal

BigDecimal可以表示一个任意大小且精度完全准确的浮点数。 同样有各种方法,详细参考廖老师的Java教程 BigDecimal

常用工具类

Math

求绝对值: Math.abs(-100);//100 Math.abs(-7.8); // 7.8 取最大或最小值: Math.max(100, 99); // 100 Math.min(1.2, 2.3); // 1.2 计算x的y次方: Math.pow(2, 10); // 2的10次方=1024 计算√x Math.sqrt(2); // 1.414… 计算e的x次方: Math.exp(2); // 7.389… 计算以e为底的对数: Math.log(4); // 1.386… 计算以10为底的对数: Math.log10(100); // 2 三角函数: Math.sin(3.14); // 0.00159… Math.cos(3.14); // -0.9999… Math.tan(3.14); // -0.0015… Math.asin(1.0); // 1.57079… Math.acos(1.0); // 0.0 Math还提供了几个数学常量: double pi = Math.PI; // 3.14159… double e = Math.E; // 2.7182818… Math.sin(Math.PI / 6); // sin(π/6) = 0.5 生成一个随机数x,x的范围是0 <= x < 1: Math.random(); // 0.53907… 每次都不一样 如果我们要生成一个区间在[MIN, MAX)的随机数,可以借助Math.random()实现,计算如下:

// 区间在[MIN, MAX)的随机数 public class Main { public static void main(String[] args) { double x = Math.random(); // x的范围是[0,1) double min = 10; double max = 50; double y = x * (max - min) + min; // y的范围是[10,50) long n = (long) y; // n的范围是[10,50)的整数 System.out.println(y); System.out.println(n); } }

Random

Random用来创建伪随机数,伪随机数是指只要种子一致,产生的随机数序列是完全一样的 生成随机数。默认使用系统当前时间戳作为种子

Random r = new Random(); r.nextInt();//15798153,每次都不一样 r.nextInt(10);//5,生成一个[0,10]之间的int r.nextLong(); r.nextFloat();//生成一个[0,1]之间的float r.nextDouble();//生成一个[0,1]之间的double

Math.random()实际上内部调用了Random类,只是我们无法指定种子

SecureRandom

真正的真随机数只能通过量子力学原理来获取 SecureRandom用来创建安全的不可预测的随机数 SecureRandom无法指定种子

SecureRandom的安全性是通过操作系统提供的安全的随机种子来生成随机数。这个种子是通过CPU的热噪声、读写磁盘的字节、网络流量等各种随机事件产生的“熵”。 在密码学中,安全的随机数非常重要。如果使用不安全的伪随机数,所有加密体系都将被攻破。因此,时刻牢记必须使用SecureRandom来产生安全的随机数。

SecureRandom sr = new SecureRandom(); System.out.println(sr.nextInt(100));

实际使用,有限获得高强度的安全随机数生成器,如果没有提供,再使用普通等级的安全随机数生成器:

import java.util.Arrays; import java.security.SecureRandom; import java.security.NoSuchAlgorithmException; public class Main { public static void main(String[] args) { SecureRandom sr = null; try { sr = SecureRandom.getInstanceStrong(); // 获取高强度安全随机数生成器 } catch (NoSuchAlgorithmException e) { sr = new SecureRandom(); // 获取普通的安全随机数生成器 } byte[] buffer = new byte[16]; sr.nextBytes(buffer); // 用安全随机数填充buffer System.out.println(Arrays.toString(buffer)); } }
最新回复(0)