ALT+SHIFT+0:生成构造器或setter、getter







String源码
public final class String implements java.io.Serializable, Comparable<String>, CharSequence{
private final char value[];
private int hash;
...
}
String的实例化
String s1="hello";//字面量实例化
String s2=new String();//本质是this.value=new char[0];
String s3=new String(String original);//本质是this.value=original.value;
String s4=new String(char[] a);//this.value=Arrays.copyOf(value, value.length);
String s5=new String(char[] a, int startIndex, int count);
总体来说:
String的实例化方式:
* 方式1:通过字面量定义的方式:存放到方法区中的字符串常量池中
* 方式2:通过new + 构造器的方式:存放到堆中
String:字符串,使用一对""引起来表示
s1+="def";则内存地址已经变了String s3="abc"; String s4=s3.replace('a', 'm');面试题:String s=new String(“abc”);方式创建对象,在内存中创建了几个对象?
2个:1个是堆空间中new的String的结构value,另一个是char[]对应的字符串常量池中的数据,即"abc"



1.常量与常量的拼接结果在常量池中,且常量池中不会存在相同内容的常量
2.只要其中有一个是变量,结果就在堆中
3.intern()方法可以返回字符串常量池中的地址值
String s1="hello";
String s2="world";
String S3="helloworld";
String s4="hello"+"world";
String s5=s1+"world";
String s6=s1+s2;
String s7=s5.intern();//此时返回的s7是常量池中已经存在的"helloworld"的地址值
解析:

如" h ello "返回的是"h ello"String—>char[]:调用String的一个方法toCharArray():char[] arr=str.toCharArray();
char[]—>String:调用String的构造器:String str = new String(arr);
String—>byte[]:调用String的一个方法getBytes():byte[] b = str.getBytes();
byte[]—>String:调用String的构造器:String str = new String(b);
String s1="abc123中国";
byte[] b = s1.getBytes();//使用默认的字符集进行转换编码,因为我们之前给idea设置的是utf-8,所以默认的是utf-8
System.out.println(Arrays.toString(b));
byte[] b2 = s1.getBytes("gbk");//使用gbk字符集来进行编码
System.out.println(Arrays.toString(b2));
//utf-8和gbk都可以把汉字编码成数字,前者用三个数字来表述一个汉字,后者用两个数字表述一个汉字;二者存放英文字符的编码都是一样的
String s2 = new String(b);//使用默认的字符集进行解码,即使用utf-8来解码
System.out.println(s2);
String s3 = new String(b2, "gbk");//需要使用gbk来解码,若使用utf-8解码,则会出现乱码
System.out.println(s3);
正是因为String是不可变的字符序列,所以它的对字符串操作的一些方法都有返回值类型,如String toUpperCase()
而StringBuffer和StringBuilder是可变的字符序列,所以对字符串操作的一些方法虽然有返回值类型的,如StringBuffer append(xxx),但是一般不接受,如str.append(),因为str已经变了,很少用另一个字符串去接收
对比String,StringBuffer,StringBuilder的效率:
从高到低排列:StringBuilder > StringBuffer > String
String源码分析:
String s=new String();/底层是char[] value=new char[0];
String s=new String(“abc”);底层是char[] value=new char[]{'a', 'b', 'c'};
StringBuffer源码分析(StringBuilder类似):
StringBuffer s=new StringBuffer();char[] value=new char[16];底层创建了一个长度是16的char数组
s.append(‘a’);value[0]='a';
StringBuffer s=new StringBuffer(“abc”);char[] value=new char["abc".length()+16];底层创建了一个长度为3+16=19的char数组
StringBuffer常见问题和错误(StringBuilder类似)
s1.append(1);s1.append('1');s1.delete(2,4);s2.replace(2,4,"hello");s3.insert(2,false);s3.reverse();由于append()等方法返回的是StringBuffer或StringBuilder字符串本身,因此可以使用方法链原理,即多次调用:s.append().append().append().append();
总结:
import org.junit.Test;
import java.util.Arrays;
/**
* @author JiaMing
* @create 08-22 18:05
* @description 获取两个字符串中最大相同字串。比如:str1="abcwerthelloyuiodef12345",str="cvghellobnm12345"
* 提示:将短的那个串进行长度依次递减的子串与较长串比较
*/
public class Exer5 {
public String maxSubString(String s1, String s2){
//由于可能存在多个长度相同的最大相同子串,因此创建builder等会方便存进去,不用数组存是因为不知道有几个最大相同子串,所以不知道数组的长度该创建多大
StringBuilder builder = new StringBuilder();
//先选出哪个长哪个短
String maxStr = s1.length() >= s2.length() ? s1 : s2;
String minStr = s1.length() < s2.length() ? s1 : s2;
//外层循环逐次加一
for (int i = 0; i < maxStr.length(); i++) {
//内层循环的意思是:第一次比较的是subStr即"cvghellobnm12345",maxStr不包含subStr
//第二次比较的是:subStr="cvghellobnm1234","vghellobnm12345",maxStr不包含subStr
//第三次比较的是:subStr="cvghellobnm123","vghellobnm1234","ghellobnm12345",maxStr不包含subStr
//...这样依次循环
for (int x=0,y=minStr.length()-i; y<=minStr.length(); x++,y++) {
String subStr=minStr.substring(x,y);
if(maxStr.contains(subStr)){
//如果maxStr包含subStr,则存到builder中,并且把此次循环继续执行,看看是否有多个长度相同的最大相同子串并存到builder中
builder.append(subStr+",");
}
}
//当这一次的内层循环结束并且builder中已经有最大子串时,则结束外层循环,不需要再找了
if(builder.length()!=0){
break;
}
}
//先把builder转换成String类型,再去掉最后的"," 然后并切片成多份,命名到数组中
String[] split = builder.toString().replaceAll(",$", "").split("\\,");
return Arrays.toString(split);//将数组转换成String类型并返回
}
@Test
public void test() {
String str1="abcwerthelloyuiodef12345";
String str2="cvghellobnm12345";
String maxSubString = maxSubString(str1, str2);
System.out.println(maxSubString);
}
}
System类提供的public static long currentTimeMillis()用来返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差
①两个构造器的使用
->空参构造器:Date():创建一个对应当前时间的Date对象。Date d1 = new Date();
->参数为long型整数的构造器:Date(long date):创建指定毫秒数的Date对象,参数中的date是与1970年1月1日0时0分0秒之间以毫秒为单位的时间差。Date d2 = new Date(1661156872226L); System.out.println(d2.toString());//输出是Mon Aug 22 16:27:52 CST 2022
②两个方法的使用
->toString():显式当前的年、月、日、星期几、时、分、秒。
->getTime():获取当前Date对象对应的毫秒数(时间戳),和java.lang.System.currentTimeMillis()功能一致。System.out.println(d1.getTime());
java.sql.Date类是对应数据库中的日期类型的变量,只有在和数据库交互中才会使用这个(注:java.sql.Date是java.util.Date的子类)
->创建java.sql.Date对象。java.sql.Date d3 = new java.sql.Date(135646872313L);
java.sql.Date也可以调用toString()方法
->如何将java.util.Date对象转换为java.sql.Date对象。
java.util.Date d1 = new java.util.Date();
java.sql.Date d4 = new java.sql.Date(d1.getTime());
SimpleDateFormat的使用:SimpleDateFormat是对日期Date类的格式化和解析的类
①当使用默认构造器进行SimpleDateFormat的实例化,按照默认的方式格式化和解析:SimpleDateFormat sdf = new SimpleDateFormat();
两个操作:
->格式化:日期—>字符串
Date d1 = new Date();
String s2 = sdf.format(d1);
System.out.println(s2);//输出的是中文:2022/8/22 下午7:42
->解析:格式化的逆过程,字符串—>日期
String s3="2022/9/27 上午11:20";//字符串必须是这种格式,如果不是这种格式,会抛"ParseException"异常
Date d2=sdf.parse(s3);
System.out.println(d2);//此时输出的就又是Tue Sep 27 11:20:00 CST 2022
②当调用带参构造器进行SimpleDateFormat的实例化,按照指定的方式格式化和解析:SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
其中,yyyy代表4位数是年份,MM代表2位数的月份,dd代表2位数的日期,hh代表2位数的小时,mm代表2位数的分钟,ss代表2位数的秒
当然构造器的参数还有其他格式,如"yyyyy.MMMMM.dd GGG hh:mm:aaa"输出的格式是"02001.July.04 AD 12:08 PM"等等,具体可见java API
两个操作:
->格式化:日期—>字符串
String s4 = sdf2.format(d1);//输出2022-08-22 07:55:07,
//这个格式和构造器中的参数类型是相同的
->解析:格式化的逆过程,字符串—>日期
Date d3 = sdf2.parse("2021-05-05 2:12:12");//要求字符串必须符合
//SimpleDateFormat的构造器的参数格式,否则会抛异常,
//输出为Wed May 05 02:12:12 CST 2021
java.util.Calendar类是一个抽象类,主要用于完成日期字段之间相互操作的功能
①实例化
方式一:创建其子类(GregorianCalendar)的对象
方式二:调用其静态方法getInstance()
Calendar c1 = Calendar.getInstance();//因为Calendar是抽象类,
//所以getInstance得到的不是Calendar类,而是其子类GregorianCalendar,
//这是一个匿名子类的非匿名对象
②常用方法
int days = c1.get(Calendar.DAY_OF_MONTH);//本月的第几天:此时为8月22日,所以是第22天c1.set(Calendar.DAY_OF_MONTH,23);//修改本月的第几天为第23天c1.add(Calendar.DAY_OF_MONTH,3);//把本月的第几天加上3即23+3=26 c1.add(Calendar.DAY_OF_MONTH,-1);//把本月的第几天减去1天即25Date d1 = c1.getTime();Date d2 = new Date(); c1.setTime(d2);//没有返回值,直接把d2的时间赋给c1注意:Calendar有偏移量
即获取月份时:一月是0,二月是1…十二月是11。
获取星期时,周日是1,周一是2…周六是7
/**
* @author JiaMing
* @create 08-22 21:50
* @description 练习二:从1990-01-01开始”三天打鱼两天晒网“,在2022年5月15日是打鱼还是晒网
* 分析:可知五天一循环,则可以求这一天是五天中的第几天(总天数%5==1,2,3:打鱼,总天数%5==4,0:晒网),即求出2022-05-15与1990-01-01中间隔了多少天
*/
public class DaYu480 {
@Test
public void test() throws ParseException {
String s1="1990-01-01";
String s2="2022-05-15";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date d1 = sdf.parse(s1);
Date d2 = sdf.parse(s2);
long time=d2.getTime()-d1.getTime();//两个时间段中间隔了多少毫秒
long days=0;//两个时间段中间隔了多少天
//一天有24*60*60*1000毫秒
if(time%(24*60*60*1000)==0){
//当全部除尽时,那么中间隔了天就是算出来的商
days=time/(24*60*60*1000);
}else {
//当除不尽时就向上取整
days=time/(24*60*60*1000)+1;
}
long doWhat=days%5;//五天循环中的第几天
if(doWhat==1 || doWhat==2 || doWhat==3){
System.out.println("打鱼");
}else System.out.println("晒网");
}
}
上述几个时间类之间的关系

由于JDK8之前的关于时间的包、类等具有可变性(如Calendar可以设置改变)、偏移性(如Date中的年份是从1900开始算的,月份是从0开始的,这和生活中不符)、格式化麻烦、线程不安全、不能处理闰秒等,因此JDK8之后有了更合适的关于时间操作的API:java.time,java.time.chrono,java.time.format,java.time.temporal,java.time.zone等
java.time使用的最频繁,包含本地日期(LocalDate)、本地时间(LocalTime)、本地日期时间(LocalDateTime)、时区(ZonedDateTime)、持续时间(Duration)、时间线上的瞬时点(Instant)。其中LocalDateTime使用的频率更高,以下以LocalDateTime为例使用其方法,LocalDate、LocalTime类似
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);//2022-08-23T10:32:54.146038400
LocalDateTime ldt2 = LocalDateTime.of(2022, 8, 23, 10, 6, 23);
//也可以只设置时分秒,具体选择of()方法中的参数
System.out.println(ldt.getDayOfMonth());//获取本月的第几天:23
System.out.println(ldt.getMinute());//获取现在是这个小时的几分:11
System.out.println(ldt.getMonth());//获取月份:AUGUST
System.out.println(ldt.getMonthValue());//获取月份的的数字:8
System.out.println(ldt.getDayOfWeek());//获取星期几:TUESDAY
LocalDateTime ldt3 = ldt.withDayOfMonth(30);
//此时ldt是今天的日期23,ldt3是返回修改过的日期30,因为ldt没有变,
//所以是不可变性
LocalDateTime ldt5 = ldt.plusMonths(3);//ldt5是ldt月份往后推3个月的日期
LocalDateTime ldt6 = ldt.minusDays(6);//ldt6是ldt往前推6天的日期
Instant:时间线上的一个瞬时点。Instant的使用类似于java.util.Date类
Instant i1 = Instant.now();System.out.println(i1);//2022-08-23T02:50:28.259934400Z----输出的是此时本初子午线那的时间Instant i3 = Instant.ofEpochMilli(1661223459724L);OffsetDateTime i2 = i1.atOffset(ZoneOffset.ofHours(8));//我们这是东八区,所以参数是ZoneOffset.ofHours(8)
System.out.println(i2);//输出2022-08-23T10:53:49.070431400+08:00`
long l = i1.toEpochMilli();由以上可知,一般实例化的方法都是静态的,其他的方法都是非静态的
格式化或解析日期、时间的类,类似于SimpleDateFormat
该类提供了三种格式化方法:
DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE_TIME;LocalDateTime ldt = LocalDateTime.now();String s1 = dtf.format(ldt);TemporalAccessor p1 = dtf.parse("2022-08-23T11:32:22.6186321");//标准格式实例化的解析只能是这种格式,否则会抛异常DateTimeFormatter dtf2 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);//格式化之后输出2022/8/23 上午11:41
DateTimeFormatter dtf2 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);//2022年8月23日 上午11:41:14
DateTimeFormatter dtf2 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);//2022年8月23日 上午11时41分14秒
格式化和解析的操作:
String s2 = dtf2.format(ldt);
TemporalAccessor p2 = dtf2.parse("2022/8/23 上午11:45");//实例化时参数是SHORT时的格式
DateTimeFormatter dtf3 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
①格式化
String s3 = dtf3.format(LocalDateTime.now());//输出2022-08-23 11:50:21
②解析
TemporalAccessor p3 = dtf3.parse("2022-08-23 11:50:21");
ZoneId:该类中包含了所有的时区信息,一个时区的ID,如 Europe/Paris
ZonedDateTime:一个在ISO-8601日历系统时区的日期时间,如 2007-12-03T10:15:30+01:00 Europe/Paris。其中每个时区都对应着ID,地区ID都为“{区域}/{城市}”的格式,例如:
Asia/Shanghai等
Clock:使用时区提供对当前即时、日期和时间的访问的时钟。
持续时间:Duration,用于计算两个“时间”间隔
日期间隔:Period,用于计算两个“日期”间隔
TemporalAdjuster : 时间校正器。有时我们可能需要获取例如:将日期调整
到“下一个工作日”等操作。
TemporalAdjusters : 该类通过静态方法(firstDayOfXxx()/lastDayOfXxx()/nextXxx())提供了大量的常用
TemporalAdjuster 的实现。

一、说明:java中的对象,正常情况下,只能进行比较地址(==、!=),不能使用 > 或 < 。但是在开发中,我们需要对多个对象进行排序,言外之意,就需要比较对象的大小。如何实现?使用两个接口中的任何一个:Comparable或Comparator
二、Comparable接口的使用(自然排序:强行对实现它的每个类的对象进行整体排序)
public class CompareTest488 {
@Test
public void test1(){
String[] arr=new String[]{"AA","MM","GG","DD","KK","CC"};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
}
@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);
System.out.println(Arrays.toString(arr));
}
}
class Goods implements Comparable{
private String name;
private double price;
public Goods(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Goods{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
@Override
public int compareTo(Object o) {
//指明按照什么方式进行排序:如按照价格从低到高排序,如果价格相等,则按照产品名称从低到高排序
//方式1:自己写个方法进行排序
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 this.name.compareTo(goods.name);//调用name(String类型)的compareTo()方法进行比较
//return -this.name.compareTo(goods.name);//前面加一个"-"号,则是按照产品名称从高到低排序
}
//方式2:使用包装类中的比较方法compare
//return Double.compare(this.price, goods.price);
}
throw new RuntimeException("传入的数据类型不一致");
}
}
三、Comparator接口的使用(定制排序)
public class CompareTest491 {
@Test
public void test1(){
String[] arr=new String[]{"AA","MM","GG","DD","KK","CC"};
//将上述字符串数组从大到小排
//由于默认的Arrays.sort()是调用String的compareTo()方法的(String实现了Comparable接口),而String的compareTo()方法是从小到大的,
//要想临时进行一次从大到小的排序,可以在参数里面写一个Comparator的匿名实现类的匿名对象,然后重写compare方法,进而设置从大到小排
Arrays.sort(arr, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return -o1.compareTo(o2);
}
});
System.out.println(Arrays.toString(arr));
}
@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);
//这时如果想按照名称从低到高排序,再按照价格从高到低排序
//由于Goods类中的compareTo()方法的排序和想要的排序方式不一样,因此我们可以使用定制的排序
//因此在Arrays.sort()参数中new一个Comparator并重写compare()方法,实现想要的排序方式
Arrays.sort(arr, new Comparator<Goods>() {
@Override
public int compare(Goods o1, Goods o2) {
if(o1.getName().equals(o2.getName())){
return -Double.compare(o1.getPrice(),o2.getPrice());//前面有个负号,表示从高到低排序
//一般情况下,String、包装类等重写的compareTo()或compare()方法都是从小到大排序的,所以想要从大到小排序的话需要在前面加"-"
}else{
return o1.getName().compareTo(o2.getName());
}
}
});
System.out.println(Arrays.toString(arr));
}
}
四、Comparable接口与Comparator接口使用的对比

String javaVersion = System.getProperty("java.version");
System.out.println("java的version:" + javaVersion);
java.lang.Math提供了一系列静态方法用于科学计算。其方法的参数和返回
值类型一般为double型
abs------>绝对值
acos,asin,atan,cos,sin,tan------>三角函数
sqrt------>平方根
pow(double a,doble b)------>a的b次幂
log------>自然对数
exp------>e为底指数
max(double a,double b)
min(double a,double b)
random()------>返回0.0到1.0的随机数
long round(double a)------>double型数据a转换为long型(四舍五入)
toDegrees(double angrad)------>弧度转换为角度
toRadians(double angdeg)------>角度转换为弧度
向上取整------>ceil(单词意思:天花板)
向下取整------>floor(单词意思:地板)
Integer类作为int的包装类,能存储的最大整型值为231-1,Long类也是有限的,最大为263-1。如果要表示再大的整数,不管是基本数据类型还是他们的包装类都无能为力,更不用说进行运算了。
java.math包的BigInteger可以表示不可变的任意精度的整数。BigInteger 提供所有 Java 的基本整数操作符的对应物,并提供 java.lang.Math 的所有相关方法。另外,BigInteger 还提供以下运算:模算术、GCD 计算、质数测试、素数生成、位操作以及一些其他操作。
构造器:BigInteger(String val):根据字符串构建BigInteger对象
public BigInteger abs():返回此 BigInteger 的绝对值的 BigInteger。
BigInteger add(BigInteger val) :返回其值为 (this + val) 的 BigInteger
BigInteger subtract(BigInteger val) :返回其值为 (this - val) 的 BigInteger
BigInteger multiply(BigInteger val) :返回其值为 (this * val) 的 BigInteger
BigInteger divide(BigInteger val) :返回其值为 (this / val) 的 BigInteger。整数相除只保留整数部分。
BigInteger remainder(BigInteger val) :返回其值为 (this % val) 的 BigInteger。
BigInteger[] divideAndRemainder(BigInteger val):返回包含 (this / val) 后跟(this % val) 的两个 BigInteger 的数组。
BigInteger pow(int exponent) :返回其值为 (thisexponent) 的 BigInteger。
一般的Float类和Double类可以用来做科学计算或工程计算,但在商业计算中,要求数字精度比较高,故用到java.math.BigDecimal类。
BigDecimal类支持不可变的、任意精度的有符号十进制定点数。
构造器
public BigDecimal(double val)
public BigDecimal(String val)
常用方法
public BigDecimal add(BigDecimal augend)
public BigDecimal subtract(BigDecimal subtrahend)
public BigDecimal multiply(BigDecimal multiplicand)
public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
public void testBigInteger() {
BigInteger bi = new BigInteger("12433241123");
BigDecimal bd = new BigDecimal("12435.351");
BigDecimal bd2 = new BigDecimal("11");
System.out.println(bi);
// System.out.println(bd.divide(bd2));
System.out.println(bd.divide(bd2, BigDecimal.ROUND_HALF_UP));
System.out.println(bd.divide(bd2, 15, BigDecimal.ROUND_HALF_UP));
}