API(部分)
基础API是指JRE核心类库中已经定义好的一些类、方法等,Java程序员可以直接使用。
基础API有:
(1)数学相关的类和方法
(2)日期时间相关的类和方法
(3)字符串相关的类和方法
(4)数组工具类相关的类和方法
(5)系统相关的类和方法
数学相关
1、java.lang.Math类
Math 类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。
(1)常量E和PI
(2)求绝对值 abs
(3)三角函数
(4)static double ceil(double a):向上取整
static double floor(double a):向下取整
static long round(double a):四舍五入的效果
(5)平方根:sqrt
(6)求幂次方:pow
(7)求最大值,最小值:max,min
(8)求对数
...
java.math包
(1)BigInteger:大整数
(2)BigDecimal:任意精度的小数
BigInteger和BigDecimal引用数据类型,和包装类又不同,不支持自动装箱与拆箱。
计算: (1)加 (2)减 (3)乘 (4)除 (5)模 ...
public class TestMath {
@Test
public void test05(){
//之前表示小数,double类型最大
System.out.println(Double.MAX_VALUE);
//double类型的精度范围:科学计数法小数点后大概15-16位
//如果想要表示比 Double.MAX_VALUE 还大的小数,或者小数点16位以上 怎么办?
//使用BigDecimal
BigDecimal big1 = new BigDecimal("9.22337205878558368547758075648995");
BigDecimal big2 = new BigDecimal("5.8522233720368547758075648995");
//计算
System.out.println("和:" + big1.add(big2)) ;
System.out.println("差:" + big1.subtract(big2)) ;
System.out.println("积:" + big1.multiply(big2)) ;
// System.out.println("商:" + big1.divide(big2)) ;//这个方法,仅适用于可以除尽的
//java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
//上面求商的代码报错了,算术异常, 无法得到确定的结果,因为是无限循环小数
//改用divide(BigDecimal divisor, int scale, int roundingMode)
//divisor是除数,scale表示商的结果最终保留到小数点后几位
//roundingMode表示舍入模式,如何处理第51位,可以选择直接舍去,也可以选择进位等方案
//舍入模式的选择可以用RoundingMode枚举类的常量对象,也可以用BigDecimal自己的常量对象
System.out.println("商:" + big1.divide(big2,47, RoundingMode.CEILING)) ;
System.out.println("商:" + big1.divide(big2,52, RoundingMode.CEILING)) ;
System.out.println("余:" + big1.remainder(big2)) ;
}
@Test
public void test04(){
//之前表示整数,long类型最大
System.out.println(Long.MAX_VALUE);//9223372036854775807
//如果想要表示比 9223372036854775807 还大的整数怎么办?
//使用BigInteger
BigInteger big1 = new BigInteger("92233720368547758075648995");
BigInteger big2 = new BigInteger("58522233720368547758075648995");
//计算
// System.out.println("和:" +( big1 + big2)) ;//错误
System.out.println("和:" + big1.add(big2)) ;
System.out.println("差:" + big1.subtract(big2)) ;
System.out.println("积:" + big1.multiply(big2)) ;
System.out.println("商:" + big1.divide(big2)) ;
System.out.println("余:" + big1.remainder(big2)) ;
}
@Test
public void test03(){
System.out.println(Math.max(4,7));
System.out.println(Math.min(4,7));
}
@Test
public void test02(){
System.out.println(Math.sqrt(16));
System.out.println(Math.pow(2,5));
}
@Test
public void test01(){
System.out.println(Math.ceil(2.7));//3.0
System.out.println(Math.floor(2.7));//2.0
System.out.println(Math.round(2.7));//3
System.out.println("---------------------------");
System.out.println(Math.ceil(2.3));//3.0
System.out.println(Math.floor(2.3));//2.0
System.out.println(Math.round(2.3));//2
}
}
随机数
java.util.Random:用来获取随机值
之前:java.lang.Math类中random():产生的随机数范围[0,1)
现在:java.util.Random,获取各种类型的随机数
public class TestRandom {
public static void main(String[] args) {
// Math.random() 产生的随机数范围[0,1)
System.out.println(Math.random());
Random random = new Random();
System.out.println(random.nextDouble());//[0,1)
System.out.println(random.nextInt());//任意int的值
System.out.println(random.nextInt(100));//[0,100)
System.out.println(random.nextBoolean());//false/true
}
}
日期时间API
JDK1.8之前
1、第一代日期时间API
java.util.Date类
java.text.DateFormat:因为Date类的格式显示不符合中国等非美国的其他国家和地区的阅读习惯,所以Java提供了DateFormat类来共Java程序员自己设定格式。
通常不直接针对DateFormat使用,而是使用它的直接子类SimpleDateFormat 。
SimpleDateFormat 是一个以与语言环境有关的方式来格式化和解析日期的具体类。
(1)parse:解析 把字符串转为日期时间对象
(2)format:格式化 把日期时间对象转为字符串
2、第二代日期时间API
java.util.Calendar:
它是抽象类,有一个直接子类 GregorianCalendar。
Calendar类中提供了静态方法,来获取 Calendar的这个子类对象。
Calendar中有很多常量对象,分别对应各个日期的值
JDK1.8之后
3、第三代日期时间API
本地化日期时间:LocalDate、LocalTime、LocalDateTime
和时区有关:ZonedDateTime
日期时间间隔:
Period:日期间隔
Duration:时间间隔
日期时间格式:DateTimeFormatter
(1)预定义标准:DateTimeFormatter类中有几个常量值,例如:ISO_DATE_TIME
(2)本地化相关的格式:
(3)自定义
public class TestDate {
@Test
public void test15() {
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒 SSS毫秒 E 是这一年的D天");
String str = df.format(now);
System.out.println(str);
}
@Test
public void test14() {
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter df = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);
System.out.println(df.format(now));//21-7-15 上午11:02
DateTimeFormatter df2 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);
System.out.println(df2.format(now));//2021年7月15日 上午11时03分06秒
DateTimeFormatter df3 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);
System.out.println(df3.format(now));//2021-7-15 11:03:31
}
@Test
public void test13(){
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter df = DateTimeFormatter.ISO_DATE_TIME;//2021-07-15T11:01:31.226
String str = df.format(now);
System.out.println(str);
}
@Test
public void test12() {
LocalDate today = LocalDate.now();
LocalDate kai = LocalDate.of(2021,6,22);
Period p = Period.between(today, kai);
System.out.println(p);//P-23D
System.out.println("------------------------");
LocalDate old = LocalDate.of(2020,10,1);
Period p2 = Period.between(today, old);
System.out.println(p2);//P-9M-14D
}
@Test
public void test11() {
ZonedDateTime t = ZonedDateTime.now();//默认本地
System.out.println(t);
ZonedDateTime t1 = ZonedDateTime.now(ZoneId.of("America/New_York"));//美国纽约
System.out.println(t1);
}
@Test
public void test10() {
LocalDate today = LocalDate.now();
System.out.println(today.isLeapYear());
}
@Test
public void test09() {
LocalDate today = LocalDate.now();
LocalDate newDate = today.withMonth(5);//直接修改月份值
System.out.println(today);
System.out.println(newDate);
LocalDate after5Month = today.plusMonths(5);
System.out.println(after5Month);
LocalDate before5Month = today.minusMonths(5);
System.out.println(before5Month);
LocalDate future = today.plusDays(1345);
System.out.println(future);//2025-03-21
}
@Test
public void test08() {
LocalTime offLesson = LocalTime.of(12, 0, 0);
System.out.println(offLesson);
}
@Test
public void test07() {
LocalDate birthday = LocalDate.of(1998, 5, 1);
System.out.println(birthday);
System.out.println(birthday.getYear());
System.out.println(birthday.getMonth());
System.out.println(birthday.getMonth().getValue());
System.out.println(birthday.getDayOfMonth());
System.out.println(birthday.getDayOfWeek());
System.out.println(birthday.getDayOfYear());
}
@Test
public void test06() {
LocalDate today = LocalDate.now();
LocalTime now = LocalTime.now();
LocalDateTime todayNow = LocalDateTime.now();
System.out.println(today);
System.out.println(now);
System.out.println(todayNow);
}
@Test
public void test05() {
//public static Calendar getInstance(TimeZone zone, Locale aLocale)
Calendar instance = Calendar.getInstance(TimeZone.getTimeZone("America/Los_Angeles"), Locale.US);
System.out.println("年:" + instance.get(Calendar.YEAR));
System.out.println("月:" + (instance.get(Calendar.MONTH) + 1));//月份是从0开始
System.out.println("日:" + instance.get(Calendar.DAY_OF_MONTH));
System.out.println("时:" + instance.get(Calendar.HOUR));
System.out.println("分:" + instance.get(Calendar.MINUTE));
System.out.println("秒:" + instance.get(Calendar.SECOND));
//获取时区的id的方式
/*String[] availableIDs = TimeZone.getAvailableIDs();
for (int i = 0; i < availableIDs.length; i++) {
System.out.println(availableIDs[i]);
}*/
}
@Test
public void test04(){
Calendar instance = Calendar.getInstance();
System.out.println(instance);
System.out.println("年:" + instance.get(Calendar.YEAR));
System.out.println("月:" + (instance.get(Calendar.MONTH) + 1));//月份是从0开始
System.out.println("日:" + instance.get(Calendar.DAY_OF_MONTH));
}
@Test
public void test03(){
// Date(int year, int month, int date)
Date d = new Date(2021,7,15);
System.out.println(d);//Mon Aug 15 00:00:00 CST 3921
//year参数是 减 1900 的年份。
// month参数 0-11 的月份
}
@Test
public void test02(){
Date date = new Date();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS D");
//yyyy:四位数字的年份
//MM:月份
//dd:日期
//HH:24小时制的小时值
//mm:分钟
//ss:秒
//SSS:毫秒
//D:是一年中的第几天
System.out.println(sf.format(date));
try {
String str = "1990-1-25 23:25:21 785 25";
Date d = sf.parse(str);
System.out.println(d);
} catch (ParseException e) {
e.printStackTrace();
}
}
@Test
public void test01(){
Date date = new Date();
System.out.println(date);//Thu Jul 15 10:13:47 CST 2021
//星期四,7月15日 上午10:13:47,公历2021年
//精确到毫秒
System.out.println(date.getTime()); //date这个日期时间对象,对应 的毫秒值
//1626315338711 距离1970年1月1日的0:0:0 0毫秒
}
}
系统相关类
1 java.lang.System类:
(1)常量对象:out,in,err
out:PrintStream类型,后面IO章节还会介绍PrintStream,用于输出,默认情况下输出到控制台
in:InputStream类型,后面IO章节还会介绍InputStream,用于输入,默认情况下用于从键盘输入
err:PrintStream类型,后面IO章节还会介绍PrintStream,用于输出,默认情况下输出到控制台。
它与out的区别,就是一个是打印普通信息,一个是打印错误信息。
out和err是分别由两个线程打印的。谁先出来不一定。
(2)long System.currentTimeMillis();
(3)static Properties getProperties() :获取系统属性
(4)public static void arraycopy(Object src, int srcPos, Object dest,int destPos,int length):用于数组复制或移动效果的方法
(5)static void gc() :调用gc方法,表示通知垃圾回收器来收拾垃圾。但是它不是立刻就能响应。
(6)static void exit(int status) :用于结束Java程序,退出JVM。根据惯例,非 0 的状态码表示异常终止。
2 java.lang.Runtime类:
这个类是一个单例类。
用于表示JVM虚拟机的运行环境,可以获取内存大小等信息
public class TestSystem {
public static void main(String[] args) {
System.out.println("hello");
System.err.println("error");
System.out.println("----------------");
long time = System.currentTimeMillis();
System.out.println(time);//1626319255384,距离1970年1月1日凌晨的毫秒值
System.out.println("----------------");
Properties properties = System.getProperties();
System.out.println(properties);
System.out.println("-------------------------");
/*
public static void arraycopy(
Object src, 源数组
int srcPos, 源数组起始位置
Object dest, 目标数组
int destPos, 模板数组的起始位置
int length) 要复制或移动的元素的个数
奇怪:src和dest不是Object[]或int[]
因为如果写Object[],只能支持对象数组
如果写int[],只能支持int[]数组
如果写Object类型,可以接收任意类型的数组
*/
String[] arr = {"hello","java","world","atguigu"};
System.arraycopy(arr, 0, arr, 1, 2);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
// {"hello","hello","java","atguigu"};
System.out.println("----------------------");
String[] arr2 = {"hello","java","world","atguigu"};
System.arraycopy(arr2, 2, arr2, 1, 2);
for (int i = 0; i < arr2.length; i++) {
System.out.println(arr2[i]);
}
// {"hello","world","atguigu","atguigu"};
System.out.println("----------------");
Runtime runtime = Runtime.getRuntime();
System.out.println(runtime.maxMemory());//最大内存
System.out.println(runtime.freeMemory());//空闲内存
}
}
数组工具类
java.util.Arrays工具类
(1)二分查找
public static int binarySearch(byte[] a,byte key)
public static int binarySearch(int[] a,int key)
public static int binarySearch(...[] a,... key) 要求数组是有序的,如果无序,结果是不正确的。返回值是负数表示不存在;返回值是0或正数,表示存在
(2)public static int[] copyOf(int[] original,int newLength)
public static int[] copyOfRange(int[] original,int from, int to)
复制数组,下标范围左闭右开 [from, to) to可以超过数组的最大下标,from不能越界,from<=to
(3)public static String toString(int[] a)
(4)public static boolean equals(int[] a, int[] a2)
(5)public static void fill(int[] a,int val)
(6)public static void sort(int[] a) 各种基本数据类型的数组排序
public static void sort(Object[] a):给对象数组排序,要求元素实现Comparable接口
public static void sort(T[] a, Comparator<? super T> c) :给对象数组排序,必须要求单独有个类实现Comparator接口
public class TestArray {
@Test
public void test08(){
Circle[] arr = new Circle[5];
arr[0] = new Circle(2.3);
arr[1] = new Circle(3.3);
arr[2] = new Circle(1.3);
arr[3] = new Circle(2.6);
arr[4] = new Circle(3.3);
Arrays.sort(arr, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return Double.compare(((Circle)o1).getRadius(), ((Circle)o2).getRadius());
}
});
System.out.println(Arrays.toString(arr));
}
@Test
public void test07(){
Circle[] arr = new Circle[5];
arr[0] = new Circle(2.3);
arr[1] = new Circle(3.3);
arr[2] = new Circle(1.3);
arr[3] = new Circle(2.6);
arr[4] = new Circle(3.3);
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
}
@Test
public void test06(){
int[] arr = {5,6,7,2,1};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
}
@Test
public void test05(){
int[] arr = new int[5];
Arrays.fill(arr, 10);
System.out.println(Arrays.toString(arr));
}
@Test
public void test04(){
int[] arr1 = {1,2,3};
int[] arr2 = {1,2,3,4};
System.out.println(Arrays.equals(arr1,arr2));
/*
怎么比较?
(1)先比较地址
(2)考虑null
(3)考虑长度
(4)考虑元素
*/
}
@Test
public void test03(){
int[] arr = {4,6,8,16,17};
int[] arr1 = Arrays.copyOfRange(arr, 2,4);
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.toString(arr1));
int[] arr2 = Arrays.copyOfRange(arr, 2, 2);
System.out.println(Arrays.toString(arr2));
}
@Test
public void test02(){
int[] arr = {4,6,8,16,17};
int[] arr1 = Arrays.copyOf(arr, 3);
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.toString(arr1));
int[] arr2 = Arrays.copyOf(arr, 10);
System.out.println(Arrays.toString(arr2));
}
@Test
public void test01(){
int[] arr = {4,6,8,16,17};
int value = 4;
System.out.println(Arrays.binarySearch(arr, value));
}
//自己模拟写二分查找
//数组是从小到大
public int binaryFind(int[] arr, int value){
int left = 0;
int right = arr.length-1;
while(left <= right){
//int mid = (left + right)/2;
int mid = left + (right-left)/2;
if(arr[mid] == value){
return mid;
}else if(arr[mid] >value){//value在左
right = mid - 1;
}else{
left = mid + 1;
}
}
return -1;
}
}
class Circle {//implements Comparable{
private double radius;
public Circle(double radius) {
this.radius = radius;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
@Override
public String toString() {
return "Circle{" +
"radius=" + radius +
'}';
}
/* @Override
public int compareTo(Object o) {
return Double.compare(radius, ((Circle)o).radius);
}*/
}
数组的一些补充算法
1 反转
/* 数组的算法补充: (1)数组的反转 A:整个数组全部反转 B:数组的其中一段反转 合起来:public static void reverse(int[] arr, int from ,int to) 反转[from, to)范围的元素 */ public class TestReverse { public static void main(String[] args) { /* int[] arr = {1,2,3,4,5,6}; reverse(arr, 0, arr.length); System.out.println(Arrays.toString(arr)); System.out.println("------------------------"); reverse(arr, 1, 4); System.out.println(Arrays.toString(arr));*/ System.out.println("------------------------"); int[] arr = {1,2,3,4,5,6}; arr = reverse2(arr, 0, arr.length); System.out.println(Arrays.toString(arr)); System.out.println("------------------------"); arr = reverse2(arr, 1, 4); System.out.println(Arrays.toString(arr)); } //反转整个数组 public static void reverse(int[] arr){ //思路:首尾对应位置交换 for(int i=0; i<arr.length/2; i++){//循环的次数就是交换的次数 int temp = arr[i]; arr[i] = arr[arr.length-1-i]; arr[arr.length-1-i] = temp; } } /* 假设arr的长度是10,arr.length=10 交换的范围 [2, 7) arr[2] ~ arr[6] arr[3] ~ arr[5] */ public static void reverse(int[] arr, int from ,int to){ //思路:反转部分 首尾对应位置交换 for(int i=from,j=to-1; i<j; i++,j--){ int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } public static int[] reverse2(int[] arr, int from ,int to){ //思路:反转部分 借助一个新数组 //(1)先创建一个新数组 int[] newArr = new int[arr.length]; //(2)把原来的数组的元素依照新的要求放到新数组中 //[0, from)之前的照搬 for (int i=0; i<from; i++){ newArr[i] = arr[i]; } //处理[from,to)如何搬到新数组 for(int i=from,j=to-1; i<to; i++,j--){ newArr[j] = arr[i]; } //[to, arr.length)之后的照搬 for (int i=to; i<arr.length; i++){ newArr[i] = arr[i]; } return newArr; } }2 扩容
/* 数组的扩容: 数组的特点:(1)访问效率很高,根据[下标]可以直接定位元素 (2)数组的长度是固定的 如果数组的长度一开始设计的不够,需要存储更多的元素,那么怎么办? 就是需要进行数组的扩容。 思路:再创建一个更大的数组,把原来的数据搬过去 */ public class TestArrayGrow { public static void main(String[] args) { int[] arr = {1,2,3,4,5}; //有更多的数据了,需要对arr进行扩容 int value = 6;//假设这是新数据 //(1)创建一个新数组 //新数组的长度:(1)可以在原来的长度上+1 (2)是原来的2倍 (3)原来的1.5倍(4).... //如何选择? 长度扩的大,可能造成浪费,但是扩容次数少 // 长度扩的小,不会浪费,但是可能需要再次扩容 //假设这里选择1.5倍 // int[] newArr = new int[(int)(arr.length * 1.5)]; // int[] newArr = new int[arr.length * 3 / 2]; // int[] newArr = new int[arr.length + arr.length / 2]; int[] newArr = new int[arr.length + (arr.length >> 1)]; //(2)搬家 for (int i=0; i<arr.length; i++){ newArr[i] = arr[i]; } //(3)让新数据住到新数组中 newArr[arr.length] = value; //(4)让arr指向新数组 arr = newArr; //(5)遍历arr System.out.println(Arrays.toString(arr)); } }3 插入
/* 数组元素的插入: 在arr数组中,插入一个元素,位置是[index]。 (1)分为两种情况 原来数组是满的,要先扩容再插入 原来数组是未满的,不需要扩容,直接插入 (2)如何插入 先把[index]以及往后的元素先右移,然后把新元素放到[index] */ public class TestArrayInsert { public static void main(String[] args) { int[] arr = {1,2,3,4,5};//假设是满的 int index = 1;//下标 int value = 6; //把6插入到[1]位置,最后数组{1,6,2,3,4,5...} int total = arr.length;//实际的元素的个数 //(1)先扩容+搬家 arr = Arrays.copyOf(arr, arr.length *2); //(2)移动元素 /* arraycopy方法的几个参数: (1)原数组 (2)原数组起始下标 (3)目标数组 (4)目标起始位置 (5)移动元素的个数 index = 1; int[] arr = {1,2,3,4,5} 移动 arr[1],arr[2],arr[3],arr[4]分别是2,3,4,5 */ System.arraycopy(arr, index, arr, index+1, total-index); //(3)把新数据放到[index] arr[index] = value; //(4)显示结果 System.out.println(Arrays.toString(arr)); } }4 删除
/* 假设删除[index]位置的元素。 数组元素的删除: (1)先搬家,把[index]后面的元素往前移动 (2)然后把刚才最后一个元素置为空 */ public class TestArrayDelete { public static void main(String[] args) { int[] arr = {1,2,3,4,5}; int index = 1;//被删除元素的下标 int total = arr.length; //实际元素的个数 //移动元素 System.arraycopy(arr, 2, arr ,1, total-index-1); //把刚才最后一个元素置为空 arr[total-1] = 0;//默认值是0、 System.out.println(Arrays.toString(arr)); } }5 直接选择排序
/* 排序的方法: (1)冒泡 (2)直接选择排序 思路:每一轮从当前未排序的数据中,找到“最小”值,把“它”放到它应该在的位置。 */ public class TestSelectSort { public static void main(String[] args) { int[] arr = {3,5,4,1,7}; /* 第一轮:所有元素都未排序,找它们的最小值1,1应该在[0]位置,现在1不在[0],就把1与[0]位置的元素直接交换 {1,5,4,3,7} 第二轮:除了1,剩下的元素未排序,找它们的最小值3,3应该在[1]位置,现在3不在[1]位置,就把3与[1]位置的元素直接交换 {1,3,4,5,7} 第三轮:除了1和3,剩下的元素未排序,找它们的最小值4,4应该在[2]位置,现在4在[2]位置不动 第四轮:除了1和3,4,剩下的元素未排序,找它们的最小值5,5应该在[3]位置,现在5在[3]位置不动 */ for(int i=0; i<arr.length-1; i++){//外循环控制轮数 = arr.length-1 //(1)找本轮未排序元素中最小值及其下标 /* 第1轮:范围[0, arr.length-1] 第2轮:范围[1, arr.length-1] 第3轮:范围[2, arr.length-1] 第4轮:范围[3, arr.length-1] 如何找最小值? 用一个min来存储最小值,一开始假设本轮的第一个元素最小,用剩下的元素与min比较 */ int min = arr[i]; int minIndex = i; for(int j=i+1; j<arr.length; j++){ if(arr[j] < min){ min = arr[j]; minIndex = j; } } //(2)判断本轮最小值是否在它应该在的位置,如果不在,就交换 /* 第1轮:范围[0, arr.length-1],min应该在[0] 第2轮:范围[1, arr.length-1],min应该在[1] 第3轮:范围[2, arr.length-1],min应该在[2] 第4轮:范围[3, arr.length-1],min应该在[3] */ if(minIndex != i){//min不在[i]的位置 int temp = arr[i]; arr[i] = arr[minIndex]; arr[minIndex] = temp; } } //显示结果 System.out.println(Arrays.toString(arr)); } }
字符串-String
java.lang.String类型
1、这个类,这个类的对象有什么特点?
(1)String类是final修饰的
(2)所有程序中,""引起来的都是String类的对象
(3)字符串对象不可变
考题类型有两种:
A:String类型的形参, 在方法里面对String类型形参的修改,如果没有返回的话,是和实参无关
B:String类型的对象修改后,没有接收
(4)因为 String 对象是不可变的,所以可以共享。
字符串常量对象是可以共享的
注意:非常量对象是不可以共享的
(5)String对象底层是用什么存储的?
JDK1.9之前:char[]
"hello" 等价于 char[] value = {'h','e','l','l','o'}
JDK1.9之后:byte[]
为什么要修改?
因为char类型的数据,占2个字节。大部分程序中出现的String对象都是由ASCII表范围内的字符组成的,例如:hello。 而这个范围的字符编码都在[0,127]范围内,使用1个字节就可以表示了。这样的话,如果把String底层改为byte[],可以节约很多内存。 如果你的String对象中的字符是在1个byte表示以外,仍然用2个byte来表示。
(6)String类型的对象是唯一一种支持“+”的引用数据类型。 +:表示拼接,这是因为底层对+运算符进行了“重载”。
基本数据类型的包装类对象可以进行+的基本运算,是因为发生了自动拆箱,并不是包装类对象之间进行的运算。
2、面试题:
(1)String类型的对象为什么不可变?
因为String内部使用char[]或byte[]数组来存储字符串内容,而这个char[]或byte[]数组是final和private修饰。
A:private:这个value数组在String类外面无法直接操作(不可见)。
B:final:value不能变,即char[] value数组不能进行扩容等操作。因为如果要扩容等,会需要创建新数组,value指向新数组,现在无法进行这个操作。
C:按理说value数组的元素是可以改变,这其实是String类中所有涉及到修改value元素值的方法,例如:replace等,都会复制一个新数组,然后修改,然后返回新数组构成的新字符串对象,不会在原来value数组中直接修改,所以String类型的对象不可变。
(2)字符串常量对象存在哪里?
JDK1.7之前:方法区
JDK1.7:堆
JDK1.8:元空间
3、字符串对象的创建方式
方式一:
String类型的构造器:
(1)无参构造 new String();
(2)public String(String original)
(3)String(char[] value)
(4)String(byte[] bytes)
方式二:
String类型是唯一一种,可以直接=字符串常量值,而不用构造器创建对象的。
方式三:
String类型的对象与其他类型的值进行“+”结果也是一个新的 String对象。
方式四:
所有对象调用toString结果都是一个String。
public class TestStringCreate {
@Test
public void test05(){
String str = "hello";
Integer i = 1;//自动装箱
}
@Test
public void test04(){
byte[] arr = {97,98,48,50};
String str = new String(arr);
System.out.println(str);//ab02
}
@Test
public void test03(){
char[] value = {'h','e','l','l','o'};
String str = new String(value);
//问?String中的value数组与 现在test03方法中的value数组什么关系?
//源码:String的this.value = Arrays.copyOf(value, value.length);
//两个不同的数组,只是长度和元素一样
//为什么呢? this.value=value;这样写就会导致String和test03方法中的value数组是同一个,就无法保证“不可变”
value[0] = 'a';
System.out.println(str);
}
@Test
public void test02(){
String str = new String("hello");
//这是两个字符串
//一个是常量池中的"hello"
//一个是堆中的new String(xx)
//这两个字符串对象有什么关系呢?
//它俩的value是相等的,但是是属于两个不同的字符串对象
String str2 = "hello";
System.out.println(str == str2);//false
System.out.println(str.equals(str2));//true
}
@Test
public void test01(){
String str = new String();//""
//长度为0的字符串,底层char[]/byte[] 长度为0的数组{}
char[] value = new char[0];//长度为0的char数组
}
}
4、字符串的比较
结论:
如果参与拼接的都是指向常量池中的常量 ,结果还是常量池中。
如果有“堆”中的常量或变量参与,结果都在堆中。
无论前面是什么,只要最后调了intern(),结果都在常量池中
public class TestStringCompare {
@Test
public void test06(){
String str1 = "hello";
String str2 = "world";
String str3 = "helloworld";
String str4 = "hello" + "world"; //常量池中的字符串常量 + 常量池中的字符串常量 结果 仍然是常量池中的字符串常量
String str5 = (str1 + "world").intern();// 无论前面是什么,只要最后调了intern(),结果都在常量池中
String str6 = (str1 + str2).intern(); //
System.out.println(str3 == str4);//true
System.out.println(str3 == str5);//true
System.out.println(str3 == str6);//true
System.out.println(str5 == str6);//true
}
@Test
public void test05(){
final String str1 = new String("hello");
final String str2 = new String("world");
String str3 = "helloworld";
String str4 = "hello" + "world"; //常量池中的字符串常量 + 常量池中的字符串常量 结果 仍然是常量池中的字符串常量
String str5 = str1 + "world";// str1本身是常量,但是它指向了堆中的字符串常量
//指向堆中常量 + 常量池中常量 结果在堆中
String str6 = str1 + str2; //指向堆中常量 + 指向堆中常量 结果在堆中
System.out.println(str3 == str4);//true str3和str4都是代表常量池中的字符串常量“helloworld"
System.out.println(str3 == str5);//false
System.out.println(str3 == str6);//false
System.out.println(str5 == str6);//false
System.out.println(str3);
System.out.println(str4);
System.out.println(str5);
System.out.println(str6);
}
@Test
public void test04(){
final String str1 = "hello";
final String str2 = "world";
String str3 = "helloworld";
String str4 = "hello" + "world"; //常量池中的字符串常量 + 常量池中的字符串常量 结果 仍然是常量池中的字符串常量
String str5 = str1 + "world";// str1本身是常量,并且它指向了常量池中的字符串常量
//指向常量池中常量 + 常量池中常量 结果在常量池中
String str6 = str1 + str2; //指向常量池中常量常量 + 指向常量池中常量 结果在常量池中
System.out.println(str3 == str4);//true str3和str4都是代表常量池中的字符串常量“helloworld"
System.out.println(str3 == str5);//true
System.out.println(str3 == str6);//true
System.out.println(str5 == str6);//true
System.out.println(str3);
System.out.println(str4);
System.out.println(str5);
System.out.println(str6);
}
@Test
public void test03(){
String str1 = "hello";
String str2 = "world";
String str3 = "helloworld";
String str4 = "hello" + "world"; //常量池中的字符串常量 + 常量池中的字符串常量 结果 仍然是常量池中的字符串常量
String str5 = str1 + "world";// str1本身是本量,只是它指向了常量池中的字符串而已
//变量 + 常量 结果在堆中
String str6 = str1 + str2; //变量 + 变量 结果在堆中
System.out.println(str3 == str4);//true str3和str4都是代表常量池中的字符串常量“helloworld"
System.out.println(str3 == str5);//false
System.out.println(str3 == str6);//false
System.out.println(str5 == str6);//false
System.out.println(str3);
System.out.println(str4);
System.out.println(str5);
System.out.println(str6);
}
@Test
public void test02(){
String str1 = new String("hello");
String str2 = "hello";
System.out.println(str1 == str2);//false
System.out.println(str1.equals(str2));//true
}
@Test
public void test01(){
String str1 = "hello";
String str2 = "hello";
System.out.println(str1 == str2);//true
System.out.println(str1.equals(str2));//true
}
}
5、字符串对象的个数
(1)字符串常量对象
String str1 = "hello";
//1个 在常量池里
(2)字符串的普通对象和常量对象一起
String str2 = new String("hello");
//str2首先指向堆中的一个字符串对象,然后堆中字符串的value数组指向常量池中的常量对象的value数组
6、字符串对象的内存分析
String s;
String s = null;
String s = "";
String s = new String();
String s = new String("");
String s = "abc";
String s = new String("abc");
char[] arr = {'a','b'};
String s = new String(arr);
char[] arr = {'a','b','c','d','e'};
String s = new String(arr,0,3);
\第10章 基础API与常见算法\imgs\1562945799274.png)
7、空字符串的比较
(1)哪些是空字符串
String str1 = "";String str2 = new String();String str3 = new String("");
(2)判断某个字符串是否为空
if("".equals(str))//为什么不使用str.equals("")?//str.equals("")容易报空指针异常if(str!=null && str.isEmpty())if(str!=null && str.equals(""))if(str!=null && str.length()==0)
8、字符串的常用方法
常规
(1)boolean isEmpty():字符串是否为空
(2)int length():返回字符串的长度
(3)String concat(xx):拼接,等价于+
(4)boolean equals(Object obj):比较字符串是否相等,区分大小写
(5)boolean equalsIgnoreCase(Object obj):比较字符串是否相等,不区分大小写
(6)int compareTo(String other):比较字符串大小,区分大小写,按照Unicode编码值比较大小
这个方法是重写实现Comparable接口的抽象方法,说明字符串支持比大小,可以直接用Arrays.sort(字符串数组)进行排序。
(7)int compareToIgnoreCase(String other):比较字符串大小,不区分大小写
(8)String toLowerCase():将字符串中大写字母转为小写
(9)String toUpperCase():将字符串中小写字母转为大写
(10)String trim():去掉字符串前后空白符
查找
(11)boolean contains(xx):是否包含xx
(12)int indexOf(xx):从前往后找当前字符串中xx,即如果有返回第一次出现的下标,要是没有返回-1
(13)int lastIndexOf(xx):从后往前找当前字符串中xx,即如果有返回最后一次出现的下标,要是没有返回-1
字符串截取
(14)String substring(int beginIndex) :返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。
(15)String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。
和字符相关
(16)char charAt(index):返回[index]位置的字符
(17)char[] toCharArray(): 将此字符串转换为一个新的字符数组返回
(18)String(char[] value):返回指定数组中表示该字符序列的 String。
(19)String(char[] value, int offset, int count):返回指定数组中表示该字符序列的 String。
(20)static String copyValueOf(char[] data): 返回指定数组中表示该字符序列的 String
(21)static String copyValueOf(char[] data, int offset, int count):返回指定数组中表示该字符序列的 String
(22)static String valueOf(char[] data, int offset, int count) : 返回指定数组中表示该字符序列的 String
(23)static String valueOf(char[] data) :返回指定数组中表示该字符序列的 String
编码与解码
(24)byte[] getBytes():编码,把字符串变为字节数组,按照平台默认的字符编码进行编码
byte[] getBytes(字符编码方式):按照指定的编码方式进行编码
(25)new String(byte[] ) 或 new String(byte[], int, int):解码,按照平台默认的字符编码进行解码
new String(byte[],字符编码方式 ) 或 new String(byte[], int, int,字符编码方式):解码,按照指定的编码方式进行解码
常见的编码方式:
(1)ISO08859
(2)GBK或GB2312
(3)UTF-8
(1)绝对不支持中文,(2)(3)支持中文
开头与结尾
(26)boolean startsWith(xx):是否以xx开头
(27)boolean endsWith(xx):是否以xx结尾
正则匹配
(28)boolean matchs(正则表达式):判断当前字符串是否匹配某个正则表达式
常用正则表达式:
字符类
[abc]:a、b 或 c(简单类)
[^abc]:任何字符,除了 a、b 或 c(否定)
[a-zA-Z]:a 到 z 或 A 到 Z,两头的字母包括在内(范围)
预定义字符类
.:任何字符(与行结束符可能匹配也可能不匹配)
\d:数字:[0-9]
\D:非数字: [^0-9]
\s:空白字符:[ \t\n\x0B\f\r]
\S:非空白字符:[^\s]
\w:单词字符:[a-zA-Z_0-9]
\W:非单词字符:[^\w]
POSIX 字符类(仅 US-ASCII)
\p{Lower} 小写字母字符:[a-z]
\p{Upper} 大写字母字符:[A-Z]
\p{ASCII} 所有 ASCII:[\x00-\x7F]
\p{Alpha} 字母字符:[\p{Lower}\p{Upper}]
\p{Digit} 十进制数字:[0-9]
\p{Alnum} 字母数字字符:[\p{Alpha}\p{Digit}]
\p{Punct} 标点符号:!"#$%&'()*+,-./:;<=>?@[]^_`{|}~
\p{Blank} 空格或制表符:[ \t]
边界匹配器
^:行的开头
$:行的结尾
Greedy 数量词
X?:X,一次或一次也没有
X*:X,零次或多次
X+:X,一次或多次
X{n}:X,恰好 n 次
X{n,}:X,至少 n 次
X{n,m}:X,至少 n 次,但是不超过 m 次
Logical 运算符
XY:X 后跟 Y
X|Y:X 或 Y
(X):X,作为捕获组
特殊构造(非捕获)
(?:X) X,作为非捕获组
(?=X) X,通过零宽度的正 lookahead
(?!X) X,通过零宽度的负 lookahead
(?<=X) X,通过零宽度的正 lookbehind
(?<!X) X,通过零宽度的负 lookbehind
(?>X) X,作为独立的非捕获组
1.验证用户名和密码,要求第一个字必须为字母,一共6~16位字母数字下划线组成:(^[a-zA-Z]\w{5,15}$)
2.验证电话号码:xxx/xxxx-xxxxxxx/xxxxxxxx:(^(\d{3,4}-)\d{7,8}$)
3.验证手机号码:( ^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$ )
4.验证身份证号: (^\d{15})|(^\d{17}(\d|X|x)$)
5.验证Email地址:(^\w+([-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)*$)
6.只能输入由数字和26个英文字母组成的字符串:(^[A-Za-z0-9]+$)
7.整数或者小数:(^[0-9]+(.[0-9]+){0,1}$)
8.中文字符的正则表达式:([\u4e00-\u9fa5])
9.金额校验(非零开头的最多带两位小数的数字):(^([1-9][0-9]*)+(.[0-9]{1,2})?$)
10.IPV4地址:(((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))
替换
(29)String replace(xx,xx):不支持正则
(30)String replaceFirst(正则,value):替换第一个匹配部分
(31)String repalceAll(正则, value):替换所有匹配部分
拆分
(32)String[] split(正则):按照某种规则进行拆分
特殊情况(需要转义)
(1)"."
他是一个正则表达式,
\.//错误的因为在java中\是转义字符,所以这个表示的意义也和我们想要表达的意义不同。
想要表示使用.进行分割需要
\\.(2)"|"
同样的,正确的正则表达式
\\|
可变字符串-StringBuffer和StringBuilder
分为两种:
StringBuffer
StringBuilder
1 线程安全性
这两个API几乎一致,唯一的区别在于:
StringBuffer是线程安全的;
StringBuilder是线程不安全的。
和String之间的区别:
StringBuffer、StringBuilder是可变的,String是不可变的。
2 效率
相同情况下效率:StringBuilder>StringBuffer>String
StringBuilder效率比StringBuffer效率高。String效率最低。
相同情况下空间消耗:StringBuilder=StringBuffer>String
3 方法
(1)拼接方法
append(xx):支持各种数据类型
(2)插入
insert
(3)删除
delete(int start,int end);
deleteCharAt(int index);
(4)反转字符串
reverse
(5)其他
indexOf()
lastIndexOf()
...
4 StringBuffer和StringBuilder存储字符串内容的方式
底层也是char[]存储
5 char[]数组的初始化长度
默认情况下,char[]一开始长度为16,也可以手动指定char[]初始长度
6 char[]数组满了怎么解决?
会copy到一个新char[]数组,也就是自动扩容。
但是这个数组不会自动缩减。如果想要缩减,可以调用trimToSize()方法,这个方法用于尝试减少存储空间。