一、String类
1、概述
1、String 是字符串,用 “” 来表示。 Sting 类使用的final修饰,不可被继承,其中重写了equals等方法。
2、String 实现了Serializeable 接口,表示字符串支持序列化。
3、实现了Compareable接口,可以比较大小。 String
4、内部定义final char[]value数组存储字符串数据。 在字符串常量池中是不会存相同内容的字符串。
2、String 不可变性
2.1 具体说明
1.当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。
2.当对现的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
3.当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值
2.2 举例
String 不变性体现在使用了final char[]value 来存储字符串,故最后数值都在常量池中,并且不可改变其数组大小以及内容(只能重新开辟空间存储不同字符串),两种申明方式,一个是存储变量地址在堆中,一个是指向常量池。
package com.ma.java2;
import org.junit.Test;
public class StringTest {
@Test
public void test(){
System.out.println("String 两种申明方法区别******************");
// new 方式在内存中创建了两个对象,一个char[]value数组对应常量持,一个是对空间new对象
String s1 = "helloWorld";
String s2 = new String("helloWorld");
System.out.println(s1==s2);//false
}
public static void main(String[] args) {
String s1= "abc"; //字面量定义
String s2= "abc";
s1 = "hello";
System.out.println(s1==s2);
System.out.println(s1);
System.out.println(s2);
System.out.println("**************");
String s5 ="abc";
String s3= "abcdef";
// s5为变量 此时在堆空间
String s4= s5+ "def";
System.out.println(s3);//abcdef
System.out.println(s4);//abcdef
System.out.println(s3==s4);//false
System.out.println("申明为常量**************");
final String s6= "abc";
s4= s6+ "def";
System.out.println(s3==s4);//true
}
}
2.3 常用方法
int length():返回字符串的长度: return value.length
char charAt(int index): 返回某索引处的字符return value[index]
boolean isEmpty():判断是否是空字符串:return value.length == 0
String toLowerCase():使用默认语言环境,将 String 中的所有字符转换为小写
String toUpperCase():使用默认语言环境,将 String 中的所有字符转换为大写
String trim():返回字符串的副本,忽略前导空白和尾部空白
boolean equals(Object obj):比较字符串的内容是否相同
boolean equalsIgnoreCase(String anotherString):与equals方法类似,忽略大小写
String concat(String str):将指定字符串连接到此字符串的结尾。 等价于用“+”
int compareTo(String anotherString):比较两个字符串的大小
String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。
String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。
boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束
boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始
boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始
boolean contains(CharSequence s):当且仅当此字符串包含指定的 char 值序列时,返回 true
int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引
int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始
int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引
int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索
String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
String replace(CharSequence target, CharSequence replacement):使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。
String replaceAll(String regex, String replacement):使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
String replaceFirst(String regex, String replacement):使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
匹配:
boolean matches(String regex):告知此字符串是否匹配给定的正则表达式。
切片:
String[] split(String regex):根据给定正则表达式的匹配拆分此字符串。
String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中。
2.4 String类型转换
public void test2(){
String s1 ="123";
// 转换为整型
int i = Integer.parseInt(s1);
System.out.println(i);
String s2 = String.valueOf(i);
System.out.println(i);
// 转化为char[]数组
char[] arr = s1.toCharArray();
for (int j = 0; j < arr.length; j++) {
System.out.println(arr[j]);
}
// 转化为字节码
//使用utf进行编码 在解码
byte[] bytes = s1.getBytes(StandardCharsets.UTF_8);
System.out.println(bytes);
String str = new String(bytes);
System.out.println(str);
// 与StringBuffer、StringBuilder之间的转换
// String -->StringBuffer、StringBuilder:调用StringBuffer、StringBuilder构造器
// StringBuffer、StringBuilder -->String:①调用String构造器;②StringBuffer、StringBuilder的toString()
StringBuffer str1 =new StringBuffer(str);
System.out.println(str1.getClass());
String str3 = new String(str1);
String str4 = str1.toString();
System.out.println(str3.getClass());
System.out.println(str4.getClass());
}
对比String、StringBuffer、StringBuilder三者的效率:
从高到低排列:StringBuilder 不安全 > StringBuffer 安全 > String
2.5 常用算法
1)模拟一个trim方法,去除字符串两端的空格。
public void test(){
// 模拟trim
String s1 = " heee ";
int len = s1.length();
int st = 0;
char[] s2 = s1.toCharArray();
for (int i = 0; i < s2.length; i++) {
if (s2[i] == ' ') {
st++;
} else {
break;
}
}
for (int i = s2.length-1; i >0; i--) {
if(s2[i]==' '){
len--;
}else{
break;
}
}
System.out.println(s1);
String str = s1.substring(st,len);
System.out.println(str);
}
2)将一个字符串进行反转。将字符串中指定部分进行反转。比如“abcdefg”反转为”abfedcg”
public void test2(){
// 2)将一个字符串进行反转。将字符串中指定部分进行反转。比如“abcdefg”反转为”abfedcg”
String s1 = "abcde";
// 默认开始位置
int start =2;
int end = s1.length()-1;
if(s1!=null){
char[]arr = s1.toCharArray();
for(int x= start,y=end;x<y;x++,y--){
char temp = arr[x];
arr[x] = arr[y];
arr[y] = temp;
}
System.out.println(new String(arr));
}
}
3)获取一个字符串在另一个字符串中出现的次数。
比如:获取“ ab”在 “abkkcadkabkebfkabkskab” 中出现的次数
@Test
public void test3(){
// 3)获取一个字符串在另一个字符串中出现的次数。
// 比如:获取“ ab”在 “abkkcadkabkebfkabkskab” 中出现的次数
String str = "abkkcadkabkebfkabkskab";
String subStr = "abk";
int count =0;
int sublen = subStr.length();
int len = str.length();
for (int i = 0; i <len-sublen+1 ; i++) {
for (int j = 0; j < sublen; j++) {
if(str.charAt(i+j) == subStr.charAt(j)){
if(j==sublen-1){
count++;
}
}else{
break;
}
}
}
System.out.println("共有: "+ count);
count = 0;
System.out.println("**********************************");
// 利用固有方法
int index= 0;
// indexof 返回在字符串中第一个出现字串的索引位置,从指定索引开始
if(len > sublen){
while ((index= str.indexOf(subStr,index))!=-1 ){
count ++;
index +=sublen;
}
}
System.out.println("共有: "+ count);
}
4)获取两个字符串中最大相同子串。比如:
str1 = "abcwerthelloyuiodef“;str2 = “cvhellobnm”
提示:将短的那个串进行长度依次递减的子串与较长的串比较。
public void test4(){
/* 获取两个字符串中最大相同子串。比如:
str1 = "abcwerthelloyuiodef“;str2 = "cvhellobnm"
提示:将短的那个串进行长度依次递减的子串与较长的串比较。
*/
String str1 = "abcwerthelloyuiodef";
String str2 = "cvhellobnm";
int sublen = str2.length();
for (int i = 0; i <sublen ; i++) {
// 取子串
for (int x=0,y=sublen-i;y<=sublen; x++,y++) {
String minSub = str2.substring(x,y);
if(str1.contains(minSub)){
System.out.println(minSub);
System.exit(0);
}
}
}
}
5)对字符串中字符进行自然顺序排序。
提示:
1字符串变成字符数组。
2对数组排序,择,冒泡,Arrays.sort();
3将排序后的数组变成字符串。
二、StringBuffer 、StringBuilder类
1、概述
StringBuffer:可变的字符序列;线程安全的采用同步方法,效率低;底层使用char[]存储
StringBuilder:可变的字符序列;jdk5.0新增的,线程不安全的,效率高;底层使用char[]存储
三、时间类
1、概述
Java8之前
- 通过系统方式
- java.util.Date类与java.sql.Date类
- java.text.SimpleDataFormat类 对Date类格式化解析
- Calendar类:日历类、抽象类
旧的API的缺点:
可变性:像日期和时间这样的类应该是不可变的。
偏移性:Date中的年份是从1900开始的,而月份都从0开始。
格式化:格式化只对Date用,Calendar则不行。
此外,它们也不是线程安全的;不能处理闰秒等。
Java8新的API
- Java.time获取基础包
- Java.time.format 解析格式化时间
- LocalDate / LocalTime / LocalDateTime 本地日期 本地时间 本地时间日期
- Instant 时间线上的一个瞬时点。类似Date
- DateTimeFormatter 类似SimpleDataFormat
2、举例
// Jdk8之前
public void test5() throws Exception{
// 通过系统方式---获取当前时间的时间戳
long time = System.currentTimeMillis();
System.out.println("系统获取: "+time);//1621147733138
// 使用默认构造
SimpleDateFormat sdf = new SimpleDateFormat();
Date date = new Date();
System.out.println("Date :"+date); //Sun May 16 14:48:53 CST 2021
String format = sdf.format(date);
System.out.println("解析后: "+format); //21-5-16 下午2:48
//解析逆过程
String str = "20-5-16 下午2:48";
Date date1 = sdf.parse(str);
System.out.println("逆过程 "+date1); //Sat May 16 14:48:00 CST 2020
// *********************按指定格式解析********************
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd hh-mm:ss");
String format1 = sdf2.format(date);
System.out.println("Date :"+ format1);//2021-05-16 02-56:28
Date date2 = sdf2.parse("2020-02-18 11-48:27");
System.out.println(date2);//Tue Feb 18 11:48:27 CST 2020
Calendar calendar = Calendar.getInstance();
//get()
int days = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days);
//set()可变性
calendar.set(Calendar.DAY_OF_MONTH, 22);
days = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days);
// 其他方法 add()、 getTime()获取Date
}
public void testDate(){
//偏移量
Date date1 = new Date(2020 - 1900,9 - 1,8);
System.out.println(date1);//Tue Sep 08 00:00:00 GMT+08:00 2020
}
/*
LocalDate、LocalTime、LocalDateTime 的使用
说明:
1.LocalDateTime相较于LocalDate、LocalTime,使用频率要高
2.类似于Calendar
*/
@Test
public void test1(){
//now():获取当前的日期、时间、日期+时间
LocalDate localDate = LocalDate.now();
LocalTime localTime = LocalTime.now();
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDate);
System.out.println(localTime);
System.out.println(localDateTime);
//of():设置指定的年、月、日、时、分、秒。没有偏移量
LocalDateTime localDateTime1 = LocalDateTime.of(2020, 10, 6, 13, 23, 43);
System.out.println(localDateTime1);
//getXxx():获取相关的属性
System.out.println(localDateTime.getDayOfMonth());
System.out.println(localDateTime.getDayOfWeek());
System.out.println(localDateTime.getMonth());
System.out.println(localDateTime.getMonthValue());
System.out.println(localDateTime.getMinute());
//体现不可变性
//withXxx():设置相关的属性
LocalDate localDate1 = localDate.withDayOfMonth(22);
System.out.println(localDate);
System.out.println(localDate1);
LocalDateTime localDateTime2 = localDateTime.withHour(4);
System.out.println(localDateTime);
System.out.println(localDateTime2);
//不可变性
LocalDateTime localDateTime3 = localDateTime.plusMonths(3);
System.out.println(localDateTime);
System.out.println(localDateTime3);
LocalDateTime localDateTime4 = localDateTime.minusDays(6);
System.out.println(localDateTime);
System.out.println(localDateTime4);
}
/*
Instant的使用
类似于 java.util.Date类
*/
@Test
public void test2(){
//now():获取本初子午线对应的标准时间
Instant instant = Instant.now();
System.out.println(instant);//2019-02-18T07:29:41.719Z
//添加时间的偏移量
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
System.out.println(offsetDateTime);//2019-02-18T15:32:50.611+08:00
//toEpochMilli():获取自1970年1月1日0时0分0秒(UTC)开始的毫秒数 ---> Date类的getTime()
long milli = instant.toEpochMilli();
System.out.println(milli);
//ofEpochMilli():通过给定的毫秒数,获取Instant实例 -->Date(long millis)
Instant instant1 = Instant.ofEpochMilli(1550475314878L);
System.out.println(instant1);
}
/*
DateTimeFormatter:格式化或解析日期、时间
类似于SimpleDateFormat
*/
@Test
public void test3(){
// 方式一:预定义的标准格式。如:ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
//格式化:日期-->字符串
LocalDateTime localDateTime = LocalDateTime.now();
String str1 = formatter.format(localDateTime);
System.out.println(localDateTime);
System.out.println(str1);//2019-02-18T15:42:18.797
//解析:字符串 -->日期
TemporalAccessor parse = formatter.parse("2019-02-18T15:42:18.797");
System.out.println(parse);
// 方式二:
// 本地化相关的格式。如:ofLocalizedDateTime()
// FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT :适用于LocalDateTime
DateTimeFormatter formatter1 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);
//格式化
String str2 = formatter1.format(localDateTime);
System.out.println(str2);//2019年2月18日 下午03时47分16秒
// 本地化相关的格式。如:ofLocalizedDate()
// FormatStyle.FULL / FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT : 适用于LocalDate
DateTimeFormatter formatter2 = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);
//格式化
String str3 = formatter2.format(LocalDate.now());
System.out.println(str3);//2019-2-18
// 重点: 方式三:自定义的格式。如:ofPattern(“yyyy-MM-dd hh:mm:ss”)
DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
//格式化
String str4 = formatter3.format(LocalDateTime.now());
System.out.println(str4);//2019-02-18 03:52:09
//解析
TemporalAccessor accessor = formatter3.parse("2019-02-18 03:52:09");
System.out.println(accessor);
}
四、比较方法Comparable 或 Comparator
1、概述
Comparable
1.像String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个对象大小的方式。
2.像String、包装类重写compareTo()方法以后,进行了从小到大的排列
3. 重写compareTo(obj)的规则:
如果当前对象this大于形参对象obj,则返回正整数,
如果当前对象this小于形参对象obj,则返回负整数,
如果当前对象this等于形参对象obj,则返回零。
4. 对于自定义类来说,如果需要排序,我们可以让自定义类实现Comparable接口,重写compareTo(obj)方法。在compareTo(obj)方法中指明如何排序
Comparator接口,
当元素的类型没实现java.lang.Comparable接口而又不方便修改代码,或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,那么可以考虑使用
Comparator 的对象来排序
2、举例
goods 类
package com.ma.Java4;
public class Goods implements Comparable {
private String name;
private double price;
public Goods() {
}
public Goods(String name, int price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
public void setName(String name) {
this.name = name;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Goods{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
@Override
public int compareTo(Object o){
if(o instanceof Goods){
Goods goods = (Goods) o;
if(this.price > goods.price){
return 1;
}else if(this.price < goods.price){
return -1;
}else{
// return 0;
return -this.name.compareTo(goods.name);
}
// return Double.compare(this.price,goods.price);
}
throw new RuntimeException("传入数据不一致");
}
}
test类
public class CompareTest {
@Test
public void test(){
Goods[] arr = new Goods[5];
arr[0] = new Goods("lenovoMouse",34);
arr[1] = new Goods("dellMouse",43);
arr[2] = new Goods("xiaomiMouse",12);
arr[3] = new Goods("huaweiMouse",65);
arr[4] = new Goods("microsoftMouse",43);
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
}
Comparator
public void test3(){
String[] arr = new String[]{"AA","CC","KK","MM","GG","JJ","DD"};
Arrays.sort(arr,new Comparator(){
//按照字符串从大到小的顺序排列
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof String && o2 instanceof String){
String s1 = (String) o1;
String s2 = (String) o2;
return -s1.compareTo(s2);
}
// return 0;
throw new RuntimeException("输入的数据类型不一致");
}
});
System.out.println(Arrays.toString(arr));
}
3、比较
Comparable接口的方式一旦一定,保证Comparable接口实现类的对象在任何位置都可以比较大小。
Comparator接口属于临时性的比较。