String
-
String类,代表字符串,用一对" "来表示,Java 程序中的所有字符串字面值(如 “abc” )都作为此类的实例实现。
-
String是一个final类,代表不可变的字符序列(不可变性)。因此字符串是常量,其值在创建之后不能更改。
- 通过 字面量 的方式(区别于new)给一个字符串赋值,此时声明在 字符串常量池 中。
- 字符串常量池中不会存储相同内容的字符串。
String s1 = "abc";
s1 = "hello";//重新赋值时,会创建新的 内存区域 赋值,不会在原有的内存区域修改
String s2 = s1+"world";//连接操作 也一样,会创建新的
String s3 = s1.replace('h','d');//取代操作 也一样
//原因都在于String是一个final类
-
String对象的字符内容是存储在一个字符数组value[]中的。
-
String类实现了Serializable接口:表示字符串是支持序列化的。
实现了Comparable接口:表示String可以比较大小。
String不同实例化之间的对比
1.通过字面量 定义的方式
2.通过 new + 构造器的方式
//通过字面量定义的方式:此时的s1和s2的数据javaEE声明在 方法区中的字符串常量池 中。
String s1 = "javaEE";
String s2 = "javaEE";
//通过new + 构造器的方式:此时的s3和s4保存的地址值,是数据在堆空间中开辟空间以后对应的地址值,此地址值所指的内存空间里的value数组指向 字符串常量池 对应的value数组
String s3 = new String("javaEE");
String s4 = new String("javaEE");
System.out.println(s1 == s2);//true,比较内容,相同
System.out.println(s1 == s3);//false,分别对应内容和地址值,不同
System.out.println(s1 == s4);//false,分别对应内容和地址值,不同
System.out.println(s3 == s4);//false,比较地址值,不同
Person p1 = new Person("Tom","12");
Person p2 = new Person("Tom","12");
System.out.println(p1.name.equals(p2.name));//true,指的是对象里的属性,即是内容,比较的是内容
System.out.println(p1.name == p2.name);//true,因为属性name是String类型的,因此比较的是两个属性的内容是否相等
面试题
String s = new String(“abc”);方式创建对象,在内存中创建了几个对象?
两个:一个是堆空间中new结构,另一个是char[]对应的常量池中的数据:“abc”
public class StringTest {
String str = new String("good");
char[] ch = { 't', 'e', 's', 't' };
int i = 10;
public void change(String str, char ch[], int i ) {
str = "test ok";
ch[0] = 'b';
i = 20;
}
public static void main(String[] args) {
StringTest ex = new StringTest();
ex.change(ex.str, ex.ch,ex.i);
System.out.println(ex.i);//10
// 调用change方法时,传递过来的是值10,即相当于给方法里的i赋值为10,
// 后边给方法里的i赋值为20但并未影响成员变量i,因为基本数据类型传递的是值
System.out.println(ex.str);//good,
// 方法中将成员变量str传入到形参中,即成员变量和形参str指向同一个内容good
// 当修改为test ok时会因为String类的不可变性,导致原good不变,
// 新开辟一块内存区存放test ok,此时输出成员变量即还是good
System.out.println(ex.ch);//best
// 因为char型和String型不同,成员变量和形参指向同一个地址值,
// 当形参修改值,成员变量也同样指向修改后的值,即best
}
//基本数据类型传递的是值,引用数据类型传递的是地址值,但String却是特例
}
补充
public void test4(){
String s1 = "javaEEhadoop";
String s2 = "javaEE";
String s3 = s2 + "hadoop";
System.out.println(s1 == s3);//false
final String s4 = "javaEE";//s4:常量
String s5 = s4 + "hadoop";
System.out.println(s1 == s5);//true,因为s4是常量,常量与常量的拼接结果在常量池里
}
String常用方法
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):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索
注:indexOf和lastIndexOf方法如果未找到都是返回-1
替换:
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个,如果超过了,剩下的全部都放到最后一个元素中。
String与字符数组转换char[ ]
String --> char[]:调用String的toCharArray()
char[] --> String:调用String的构造器
@Test
public void test2(){
String str1 = "abc123"; //题目: a21cb3
char[] charArray = str1.toCharArray();//调用方法,将字符串存储在数组里
for (int i = 0; i < charArray.length; i++) {
System.out.println(charArray[i]);
}
char[] arr = new char[]{'h','e','l','l','o'};
String str2 = new String(arr);//调用构造器,将数组的数组合成字符串
System.out.println(str2);
}
String与字节数组转换byte[ ]
编码:String --> byte[]:调用String的getBytes()
字符串 -->字节 (看得懂 —>看不懂的二进制数据)
解码:byte[] --> String:调用String的构造器
字节 --> 字符串 (看不懂的二进制数据 —> 看得懂)
说明:解码时,要求解码使用的字符集必须与编码时使用的字符集一致,否则会出现乱码。
@Test
public void test3() throws UnsupportedEncodingException {
String str1 = "abc123中国";
byte[] bytes = str1.getBytes();//使用默认的字符集,进行编码。
System.out.println(Arrays.toString(bytes));
byte[] gbks = str1.getBytes("gbk");//使用gbk字符集进行编码。
System.out.println(Arrays.toString(gbks));
System.out.println("******************");
String str2 = new String(bytes);//使用默认的字符集,进行解码。
System.out.println(str2);
String str3 = new String(gbks);
System.out.println(str3);//出现乱码。原因:编码集和解码集不一致!默认为uft8
String str4 = new String(gbks, "gbk");
System.out.println(str4);//没有出现乱码。原因:编码集和解码集一致!
}
StringBuffer、StringBuilder
定义
String:不可变的字符序列;底层使用char[]存储
StringBuffer:可变的字符序列(因为其声明的数组本身有长度,当不够使用时会扩容);线程安全的,效率低;底层使用char[]存储,初声明可以存储16个元素
StringBuilder:可变的字符序列;jdk5.0新增的,线程不安全的,效率高;底层使用char[]存储
@Test
public void test1(){
StringBuffer sb1 = new StringBuffer("abc");
sb1.setCharAt(0,'m');//从内容上直接修改为mbc,因为其是可变的字符序列,如果是String,则是在内存中新开辟一块区域存放mbc,原abc不变
System.out.println(sb1);//mbc
StringBuffer sb2 = new StringBuffer();
System.out.println(sb2.length());//0
}
源码分析
String str = new String();//char[] value = new char[0];
String str1 = new String("abc");//char[] value = new char[]{'a','b','c'};
StringBuffer sb1 = new StringBuffer();
//char[] value = new char[16];底层创建了一个长度是16的数组。
System.out.println(sb1.length());//0,因为数据里边没有数据
sb1.append('a');//value[0] = 'a';
sb1.append('b');//value[1] = 'b';
StringBuffer sb2 = new StringBuffer("abc");
//char[] value = new char["abc".length() + 16];
System.out.println(sb2.length());//3
扩容问题:
如果要添加的数据底层数组盛不下了,那就需要扩容底层的数组。
默认情况下,扩容为原来容量的2倍 + 2,同时将原有数组中的元素复制到新的数组中。
指导意义:
开发中建议大家使用:StringBuffer(int capacity) 或 StringBuilder(int capacity),自定义char[ ]数组长度,再根据是否为多线程要求来选择Buffer还是Builder
StringBuffer方法
增:append(xxx),字符串的拼接(String没有这个方法)
删:delete(int start,int end),删除指定位置的内容,左闭右开
改:setCharAt(int n ,char ch) / replace(int start, int end, String str),修改单个字符/修改字符串,左闭右开
查:charAt(int n ),返回某索引处的字符return value[n]
插:insert(int offset, xxx),在指定位置插入xxx
长度:length();
*遍历:for() + charAt() / toString()
String、StringBuffer、StringBuilder三者的效率
从高到低排列:StringBuilder > StringBuffer > String
JDK8之前日期时间API
System类
currentTimeMillis( )
返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差,称为时间戳
@Test
public void test1(){
long time = System.currentTimeMillis();
System.out.println(time);
}
Date类
java.util.Data类
两个构造器的使用
构造器一:Date():创建一个对应当前时间的Date对象
构造器二:创建指定毫秒数的Date对象
两个方法的使用
toString():显示当前的年、月、日、时、分、秒
getTime():获取当前Date对象对应的毫秒数。(时间戳)
//构造器一:Date():创建一个对应当前时间的Date对象
Date date1 = new Date();
System.out.println(date1.toString());//Sat Feb 16 16:35:31 GMT+08:00 2019
System.out.println(date1.getTime());//1550306204104,返回时间戳
//构造器二:创建指定毫秒数的Date对象
Date date2 = new Date(155030620410L);
System.out.println(date2.toString());//返回的格式如同Sat Feb 16 16:35:31 GMT+08:00 2019
java.sql.Date类(父类是util)
java.sql.Date对应着数据库中的日期类型的变量
//创建sql.Date对象
java.sql.Date date3 = new java.sql.Date(35235325345L);
System.out.println(date3);//1971-02-13
如何将java.util.Date对象转换为java.sql.Date对象
//情况一:多态的强转
// Date date4 = new java.sql.Date(2343243242323L);
// java.sql.Date date5 = (java.sql.Date) date4;
//情况二:主要
Date date6 = new Date();
java.sql.Date date7 = new java.sql.Date(date6.getTime());
SimpleDateFormate
SimpleDateFormat对日期Date类的格式化和解析
两个操作
- 格式化:日期–>字符串
- 解析:字符串–>日期
//实例化SimpleDateFormat:使用默认的构造器(先实例化)
SimpleDateFormat sdf = new SimpleDateFormat();
//格式化:日期 --->字符串
Date date = new Date();//构造Date类对象
System.out.println(date);//Wed Aug 04 10:05:06 CST 2021(原Date类标准格式)
String format = sdf.format(date);//对象调用format方法,进行格式化
System.out.println(format);//21-8-4 上午10:05(格式化后的默认格式)
//解析:格式化的逆过程,字符串 ---> 日期
String str = "19-12-18 上午11:43";//需要与Date标准格式对应
Date date1 = sdf.parse(str);//调用解析方法
System.out.println(date1);//Wed Dec 18 11:43:00 CST 2019
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");//(调用带参构造器)
//格式化
Date date = new Date();
String format1 = sdf1.format(date);
System.out.println(format1);//2021-08-04 10:05:06
//解析:要求字符串必须是符合SimpleDateFormat识别的格式(通过构造器参数体现),
//否则,抛异常
Date date2 = sdf1.parse("2021-08-04 10:05:06");
System.out.println(date2);
Calendar日历类(抽象类)的使用
//1.实例化
//方式一:创建其子类(GregorianCalendar)的对象
//方式二:调用其静态方法getInstance(),常用
Calendar calendar = Calendar.getInstance();
//2.常用方法
//get()
int days = calendar.get(Calendar.DAY_OF_MONTH);//获取当前月的第几天
System.out.println(days);
System.out.println(calendar.get(Calendar.DAY_OF_YEAR));//获取当前年的第几天
//set(),体现了calendar的可变性,和String相反
calendar.set(Calendar.DAY_OF_MONTH,22);//将当前月的天数修改为第22天
days = calendar.get(Calendar.DAY_OF_MONTH);//再次获取天数
System.out.println(days);
//add()
calendar.add(Calendar.DAY_OF_MONTH,3);//给当前月的天数增加三天
days = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days);
//getTime():日历类---> Date
Date date = calendar.getTime();
System.out.println(date);
//setTime():Date ---> 日历类
Date date1 = new Date();
calendar.setTime(date1);
days = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days);
JDK8中新日期时间API
LocalDate、LocalTime、LocalDateTime
**java.time – 包含值对象的基础包 **
java.time.chrono – 提供对不同的日历系统的访问
**java.time.format – 格式化和解析时间和日期 **
java.time.temporal – 包括底层框架和扩展特性
java.time.zone – 包含时区支持的类
大多数开发者只会用到基础包和format包
-
LocalDate代表IOS格式(yyyy-MM-dd)的日期,可以存储 生日、纪念日等日期。
-
LocalTime表示一个时间,而不是日期。
-
LocalDateTime是用来表示日期和时间的,这是一个最常用的类之一。
上述三者的实例都是 不可变的对象
@Test
public void test1(){
//now():获取当前的日期、时间、日期+时间
LocalDate localDate = LocalDate.now();
LocalTime localTime = LocalTime.now();
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDate);//2021-08-04,当前日期
System.out.println(localTime);//14:35:30.075,当前时间
System.out.println(localDateTime);//2021-08-04T14:35:30.075,当前日期+时间
//of():设置指定的年、月、日、时、分、秒。没有偏移量
LocalDateTime localDateTime1 = LocalDateTime.of(2020, 10, 6, 13, 23, 43);
System.out.println(localDateTime1);//2020-10-06T13:23:43
//getXxx():获取相关的属性
System.out.println(localDateTime.getDayOfMonth());//4
System.out.println(localDateTime.getDayOfWeek());//WEDNESDAY
System.out.println(localDateTime.getMonth());//AUGUST
System.out.println(localDateTime.getMonthValue());//8,以前的方法都是从0起算,0为一月
System.out.println(localDateTime.getMinute());//35
//体现不可变性
//withXxx():设置相关的属性
LocalDate localDate1 = localDate.withDayOfMonth(22);
System.out.println(localDate);//2021-08-04
System.out.println(localDate1);//2021-08-22
LocalDateTime localDateTime2 = localDateTime.withHour(4);
System.out.println(localDateTime);//2021-08-04T14:35:30.075
System.out.println(localDateTime2);//2021-08-04T04:35:30.075
instant(瞬时)
//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);
Java比较器
自然排序:java.lang.Comparable
Comparable接口强行对实现它的每个类的对象进行整体排序。这种排序被称 为类的自然排序。
实现Comparable接口的对象列表(和数组)可以通过 Collections.sort 或 Arrays.sort进行自动排序。
(默认都是从小到大排列的)
-
String:按照字符串中字符的Unicode值进行比较
-
Character:按照字符的Unicode值来进行比较
-
数值类型对应的包装类以及BigInteger、BigDecimal:按照它们对应的数值大小进行比较
-
Boolean:true 对应的包装类实例大于 **false **对应的包装类实例
-
Date、Time等:后面的日期时间比前面的日期时间大
说明
-
像String、包装类等实现了Comparable接口,重写了方法,可以直接调用来比较两个对象的大小。
重写compareTo(obj)的规则:
如果当前对象this大于形参对象obj,则返回正整数,
如果当前对象this小于形参对象obj,则返回负整数,
如果当前对象this等于形参对象obj,则返回零。 -
对于自定义类来说,如果需要排序,我们可以让自定义类实现Comparable接口,重写compareTo(obj)方法。
在compareTo(obj)方法中指明如何排序
@Test
public void test2(){
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);//sort方法用到了compareTo方法,因为传入的是Goods类的对象,同时还有多态的缘故,因此调用了Goods类重写的compareTo方法
System.out.println(Arrays.toString(arr));
}
//Goods类中重写的方法
//指明商品比较大小的方式:按照价格从低到高排序,再按照产品名称从高到低排序
@Override
public int compareTo(Object o) {
// System.out.println("**************");
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);
}
// return 0;
throw new RuntimeException("传入的数据类型不一致!");
}
定制排序:java.util.Comparator
当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码,
或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,
那么可以考虑使用 Comparator 的对象来排序
规则和compareTo一样
@Test
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));
}
对比
Comparable接口的方式一旦一定,保证Comparable接口实现类的对象在任何位置都可以比较大小。
Comparator接口属于临时性的比较。
System类
Math类
java.lang.Math提供了一系列静态方法用于科学计算。其方法的参数和返回值类型一般为double型。
BigInteger类
java.math包的BigInteger可以表示不可变的任意精度的整数。
BigDecimal类
一般的Float类和Double类可以用来做科学计算或工程计算,但在商业计算中, 要求数字精度比较高,故用到java.math.BigDecimal类。