String的深入&时间&比较器&System/数学相关类
文章目录
- String的深入&时间&比较器&System/数学相关类
- 前言
- 一、字符串String不可变
- 1.1 String理解&赋值&存储
- 1.2 String构造器&不同类型转换
- 1.3 String常用API
- 1.4 常见的算法题目(反转/字符排序/最大相同子串/trim/出现次数)
- 二、字符串StringBuilder/StringBuffer可变
- 2.1 可变序列理解StringBuilder/StringBuffer
- 2.2 常用的API
- 三、日期时间API
- 3.1 JDK8之前:System/Date/SimpleDateFormat/Calendar日历
- 3.2 JDK8:LocalDate/LocalTime/LocalDateTime/Instant/DateTimeFormatter
- 四、Java比较器(对象排序)
- 4.1 自然排序:Comparable接口(compareTo方法)得到比较类
- 4.2 定制排序:Comparetor接口(compare方法)得到比较器
- 五、系统相关类System/Runtime
- 六、和数学相关的类Math
- 七、企业真题
前言
初步理解Comparable和Comparator接口
Comparable理解为自然排序会调用类中的方法进行比较,比如TreeMap/HashMap添加元素会调用元素对象的comparaTo()方法比较大小,实现排序。
Comparator理解为定制排序,比如Collections工具类中sort(List,Comparator)方法可以实现定制排序。
回顾JVM的内存划分
- 方法区(Method Area)——抽象概念/规范上(具体实现可能不同) :当虚拟机要使用一个类时,它需要读取并解析 Class 文件获取相关信息,再将信息存入到方法区。存放已经被虚拟机加载的类的版本、字段、方法、接口等描述信息、常量(运行时-字面量/引用、
字符串
)、静态变量
、即时编译器编译后的代码缓存等数据。 - 堆(Heap) :new出来的结构——①数组实体、对象实体;②对象的属性(类的非静态成员变量)。Java 堆是垃圾收集器管理的主要区域,因此也被称作 GC 堆。
- 虚拟机栈(Stack) :以栈帧为基本单位,有入栈和出栈的操作;每个栈帧操作对应一个方法的执行。每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法返回地址
- 本地方法栈:虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。
关于JVM的内存划分:方法区定义了一组规范,JDK7及之前使用永久代实现(使用JVM内存空间),JDK7之后使用元空间实现(即使用本地内存)。关于字符串常量池、静态域从JDK6中的方法区到JDK7的堆
中,以便GC回收。
一、字符串String不可变
1.1 String理解&赋值&存储
1、String特性
final:不可被继承。
Serializable:可序列化的接口。凡是实现此接口的类的对象就可以通过网络或本地流进行数据的传输。
Comparable:凡是实现此接口的类,其对象都可以比较大小。
Java 语言提供对字符串串联符号(“+”)以及将其他对象转换为字符串的特殊支持(toString()方法)。
字符串的不可变性理解:对字符串重新赋值/拼接/replace()等操作都是需要重新开辟空间保存新的字符串,不能在原有的位置修改
jdk8以后的中的String源码:提供了多种构造函数,JDK8及之前private final char value[];存储字符串数据的容器
jdk8以后优化点:使用byte[]来存储。好处:java底层采用unicode编码,对于unicode字符集中字符采用2字节存储,包括ASCII码。改为byte[]后,对于ASCII码只用1字节存储,对于中文用2字节拼在一起存储。如果存储汉字通过使用平台的默认字符集编码/解码当前参数中的字节数组。
比如存"helloA"是5个字节byte即[72,101,108,108,111,65],如果存储汉字"张颖",UTF-8中1个汉字对应3个byte,那么存储的是-27-68-96-23-94-106
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
@Stable
//byte[]比如a:97,字符a存进去就是97
private final byte[] value;//JDK9开始:存储字符串数据的容器,final指明value数组一旦初始化,其地址就不可变。私有化不能获得
private final byte coder;
/** Cache the hash code for the string */
private int hash; // Default to 0
public String() {
this.value = "".value;
this.coder = "".coder;
}
@IntrinsicCandidate
public String(String original) {//从这里可以看到只有出现字符串肯定是从常量池中拿过来的
this.value = original.value;
this.coder = original.coder;
this.hash = original.hash;
}
public String(char value[]) {
this(value, 0, value.length, null);
}
2、关于String的赋值方式
字面量赋值:String str1 = “hello”//在字符串常量池创建一个对象(其value属性存储字面量的地址)
new String:String str2 = new String(“hello”)//在堆中创建一个对象(其属性value指向常量池中字面量的地址)
问题:new方式创建了几个对象?1/2个,对空间中new的对象、在字符串常量池中生成的字面量
3、字符串常量的存储位置
字符串常量存储在字符串常量池(StringTable)中,它不允许存放两个相同的字符串常量。因为字符串对象的不可变性JDK7及以后存放在堆中。
字符串常量池中存储:“字符串型的字面量”、字符串对象(属性value存储字面量的地址)
堆中存储:new出来的对象
为什么字符串常量存储在字符串常量池中?目的是共享,无论哪种方式,最终都是存储字符串常量池中的地址
4、常量:字面量、final修饰的常量
,字符串非常量对象存储在堆中。
"+"连接:只有"常量+常量"结果是存储在常量池,其他的底层都会通过new的方式创建一个新的字符串,返回堆空间中此字符串对象的地址
concat(xxx)、replace()、repeat()、valueOf()等等方法,底层源码是用new String()的方式,因此都返回一个新new的对象
intern():返回的是字符串常量池中字面量的地址
“==”:比的对象地址
equals():底层实现一个一个比较byte,理解为比的是内容
String str1 = "hello";//字面量,在常量池中new对象
String str2 = "world";
final String str3 = "hello";
System.out.println("hello"==str1);//false,因为str1是变量存储在堆中
System.out.println("hello"==str1.intern());//true,因为调用intern()返回常量池中字面量的地址
System.out.println("hello"==str3);//true,因为str3是final修饰的常量
System.out.println(("hello"+str2)=="helloworld")//false,只要有一个变量参与连接就是堆中的对象
String str4 = new String("hello");//在堆空间中new对象
5、如何遍历字符串中的每个字符
- 法1:charAt(index)根据序号获取字符串中的每个index位置的字符,然后可以使用substring(start,end)截取[start,end)
- 法2:str.toCharArray()将字符串转换为char[]数组,操作完之后再将他们使用new String(char[])构造器转换为字符串,或者使用valueOf(char[],start,count)转换为字符串
涉及到一些判断的:contains(str)、indexOf(str,index)
应用:——具体的实现见1.4中常见的算法题目
- 反转字符串:指定范围
- 对字符串中字符进行排序:String→char[],对数组进行排序(选择、冒泡、Array.sort()可以自然/定制),再转为String
- 获取两个字符串str1、str2中最大相同字串:考虑短的str依次去掉0-str.length()-1个字符,字串每次都与较长的串比较
- 统计str1在另一个字符串中出现的次数:利用方法indexOf(str,index),每找到一个就去掉再进行下一次的比较
实现问题1:反转字符串为例,
注意String中没有reverse()函数,StringBuilder、StringBuffer中有
//法2:利用charAt(index)获得字符串中指定索引位置实现便利
//因为是指定范围的[start,end),因此先把start之前的的字符串截取下来
String newStr = str.substring(0, start);// ab
//从后往前遍历一个一个拼接到newStr后面
for (int i = end; i >= start; i--) {
newStr += str.charAt(i);
} // abfedc
//将后半部分拼接过来
newStr += str.substring(end + 1);
return newStr;
//法2:String与char[]互转实现遍历
//功能:反转指定范围的字符串
if (str != null) {
//将字符串转换为char[]数组
char[] charArray = str.toCharArray();
//遍历char[]字符串数组
for (int i = start, j = end; i < j; i++, j--) {
char temp = charArray[i];
charArray[i] = charArray[j];
charArray[j] = temp;
}
//构造新的字符串:反转后的
return new String(charArray);
}
1.2 String构造器&不同类型转换
1、String构造器
- public String() :""空字符串
- publi String(String original): 新建字符串是original的副本,新创建了一个对象
说明:gbk中1个汉字占2个字节byte,UTF-8中1个汉字占3个字节byte,1byte=8bit,他们都向下兼容ASCII码,因此对于ASCII字符集都是采用ASCII码解码/编码的,不会出现乱码的情况,1个字母占用一个字节。
2、String与其他结构之间的转换:调用目标类型中的方法
- 字符串 --> 基本数据类型、包装类:
- Integer包装类的public static int parseInt(String s):可以将由“数字”字符组成的字符串转换为整型。
- 类似地,使用java.lang包中的Byte、Short、Long、Float、Double类调相应的类方法可以将由“数字”字符组成的字符串,转化为相应的基本数据类型。
- 基本数据类型、包装类 --> 字符串:valueOf()
- 调用String类的public String valueOf(int n)可将int型转换为字符串
- valueOf(byte b)、valueOf(long l)、valueOf(float f)、valueOf(double d)、valueOf(boolean b)可由参数的相应类型到字符串的转换。
- 字符串 --> 字符数组:
public char[] toCharArray()
:将此字符串转换为一个新的字符数组返回。- public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin):提供了将指定索引范围内的字符串存放到数组中的方法。
char charAt(index)
:返回[index]位置的字符
- 字符数组 --> 字符串:
使用String的构造器
、valueOf()public String(char[] value)
:字符数组→String类型- public String(char[] value,int offset, int count) :字符数组的一部分→String类型
- static String valueOf(char[] data) :返回指定数组中表示该字符序列的 String
- static String valueOf(char[] data, int offset, int count) : 返回指定数组中表示该字符序列的 String,
注意这里是count不是end索引
- static String copyValueOf(char[] data): 返回指定数组中表示该字符序列的 String
- static String copyValueOf(char[] data, int offset, int count):返回指定数组中表示该字符序列的 String
- 字符串 --> 字节数组:(编码)String—>字节或字节数组
- public byte[] getBytes() :使用默认字符集编码String字符串→byte序列,结果存在byte数组中
- public byte[] getBytes(String charsetName) :使用指定字符集编码String字符串→byte序列,结果存在byte数组中
- 字节数组 --> 字符串:(解码)字节或字节数组----> String。使用String类的
构造器
- public String(byte[] bytes) :使用默认字符集解码字节数组→String类型
- public String(byte[] bytes,String charsetName) :使用指定的字符集解码字节数组→String
不乱码:(1)保证编码与解码的字符集名称一样(2)不缺字节
@Test
public void test01() throws Exception {
String str = "中国";
// ISO8859-1把所有的字符都当做一个byte处理,处理不了多个字节
System.out.println(str.getBytes("ISO8859-1").length);// 2
System.out.println(str.getBytes("GBK").length);// 4 每一个中文都是对应2个字节
System.out.println(str.getBytes("UTF-8").length);// 6 常规的中文都是3个字节
/*
* 不乱码:(1)保证编码与解码的字符集名称一样(2)不缺字节
*/
System.out.println(new String(str.getBytes("ISO8859-1"), "ISO8859-1"));// 乱码
System.out.println(new String(str.getBytes("GBK"), "GBK"));// 中国
System.out.println(new String(str.getBytes("UTF-8"), "UTF-8"));// 中国
}
1.3 String常用API
在Java中涉及到索引范围都是[a,b),Python也是这样
- 常用的方法
int length()
:返回字符串的长度- String concat(xx):拼接
String trim()
:去掉字符串前后空白符- public String intern():结果在常量池中共享,不存在就创建一个在返回
- 判断:底层是一个一个字符比
boolean isEmpty()
:字符串是否为空boolean equals(Object obj)
:比较字符串是否相等,区分大小写- boolean equalsIgnoreCase(Object obj):比较字符串是否相等,不区分大小写
- 比较:因为String实现了Comparable接口因此可以使用这两个方法,比较到第一个不同的字符相减得到结果
- int compareTo(String other):比较字符串大小,区分大小写,按照Unicode编码值比较大小(Java底层采用这个)
- int compareToIgnoreCase(String other):比较字符串大小,不区分大小写
- 转换大小写
- String toLowerCase():将字符串中大写字母转为小写
- String toUpperCase():将字符串中小写字母转为大写
- 查找
boolean contains(xx)
:是否包含xx- int indexOf(xx):首次出现的索引,没有返回-1
int indexOf(String str, int fromIndex)
:从指定的索引开始找首次出现的索引- int lastIndexOf(xx):从后往前找,最后一次出现处的索引位置。“加油中国加油!!!”.lastIndexOf(“加油”),结果是4
- int lastIndexOf(String str, int fromIndex):从指定索引处从后往前找。
- 字符串截取
- String substring(int beginIndex) :返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。
String substring(int beginIndex, int endIndex)
:截取范围[a,b)
- 开头和结尾
- boolean startsWith(xx):测试此字符串是否以指定的前缀开始
- boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始
- boolean endsWith(xx):测试此字符串是否以指定的后缀结束
- 替换
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 替换此字符串匹配给定的正则表达式的第一个子字符串。
总结:最长结合str.toCharArray()方法或者charAt(index)对字符串中的每个字符得以操作
1.4 常见的算法题目(反转/字符排序/最大相同子串/trim/出现次数)
实现问题1:对字符串中字符进行自然顺序排序
涉及到的方法charAt(index)、Arrays.sort()、new String(char[])
public String sort(String str){
char[] cc = str.toCharArray();
Arrays.sort(cc);
String str1 = new String(cc);
return str1;
}
实现问题2:获取一个字符串在另一个字符串中出现的次数。比如:获取“ ab”在 “abkkcadkabkebfkabkskab” 中出现的次数
涉及到的方法:length()、indexOf(start,end)
public int getCount(String mainstr,String substr){
if(mainstr.length() < substr.length()) return 0;
int count = 0;
int index = 0;
while((index = mainstr.indexOf(substr,index)) != -1){//从index
count ++;
index += substr.length();
}
return count;
}
实现问题3:> 获取两个字符串中
最大相同子串
。比如:str1 = "abcwerthelloyuiodef“;str2 = “cvhellobnm”
提示:将短的那个串进行长度依次递减的子串与较长的串比较。涉及到指定范围移动遍历
用到的方法:contains(str)、substring(start,end)、length()
情形2用到StringBuffer可变长度
//情形1:只存在一个最大长度的相同子串
public String getMaxSameSubString(String str1,String str2){
String maxstr = str1.length()>=str2.length()?str1:str2;
String minstr = str1.length()<str2.length()?str1:str2;
int len = minstr.length();
for(int i = 0; i < len;i++){//控制去掉minstr几个字符
//指定范围的子串,遍历该minstr字符串,逐个与maxstr比较
for(int x = 0, y = len-i; y <= len; x++, y++){//[x,y)这个范围移动
if(maxstr.contains(minstr.substring(x,y))){
return minstr.substring(x,y);
}
}
}
return null;
}
//情形2:如果存在多个长度相同的最大相同子串:用StringBuffer暂存
//情形2:用ArrayList暂存
public List<String> getMaxSameSubString(String str1, String str2) {
if (str1 != null && str2 != null) {
String maxStr = (str1.length() > str2.length()) ? str1 : str2;
String minStr = (str1.length() > str2.length()) ? str2 : str1;
int len = minStr.length();
List<String> list = new ArrayList<String>();
for (int i = 0; i < len; i++) {// 0 1 2 3 4 此层循环决定要去几个字符
for(int x = 0, y = len-i; y < len; x++,y++){
String subString = minStr.substring(x, y);
if(maxStr.contains(subString)){
list.add(subString);
}
}
if(list.size() != 0){
break;
}
}
return list;
}
return null;
}
实现问题4:模拟trim()方法的实现
用到的方法:carAt(index)、substring(start,end)注意截取范围[a,b)
public String myTrim(String str){
if(str != null){
int start = 0;
int end = str.length()-1;
while(start < end && str.charAt(start) == ' '){
start++;
}
while(start < end && str.charAt(end) == ' '){
end--;
}
if(str.charAt(start) == ' '){
return "";
}
return str.substring(start,end+1);
}
return null;
}
二、字符串StringBuilder/StringBuffer可变
2.1 可变序列理解StringBuilder/StringBuffer
先分析源码实现
StringBuilder、StringBuffer的父类AbstractStringBuilder 源码
abstract class AbstractStringBuilder implements Appendable, CharSequence {
byte[] value;//存储字符序列,没加final,说明地址可变——底层的缓冲区
byte coder;
int count;//实际存储的个数
private static final byte[] EMPTYVALUE = new byte[0];
AbstractStringBuilder() {
value = EMPTYVALUE;
}
AbstractStringBuilder(int capacity) {
if (COMPACT_STRINGS) {
value = new byte[capacity];
coder = LATIN1;
} else {
value = StringUTF16.newBytesFor(capacity);
coder = UTF16;
}
}
//如果使用构造器StringBuilder(null),到这里判断的时候就会出现null.length出现空指针异常NullPointerException!!
AbstractStringBuilder(String str) {
int length = str.length();
int capacity = (length < Integer.MAX_VALUE - 16)
? length + 16 : Integer.MAX_VALUE;
final byte initCoder = str.coder();
coder = initCoder;
value = (initCoder == LATIN1)
? new byte[capacity] : StringUTF16.newBytesFor(capacity);
append(str);
}
public AbstractStringBuilder append(String str) {
if (str == null) {
return appendNull();//如果append(null)那么就是下面的实现appendNull()方法
}
int len = str.length();
ensureCapacityInternal(count + len);
putStringAt(count, str);
count += len;
return this;
}
//将append(null)一步一步添加,所以存了4个字符
private AbstractStringBuilder appendNull() {
ensureCapacityInternal(count + 4);
int count = this.count;
byte[] val = this.value;
if (isLatin1()) {
val[count++] = 'n';
val[count++] = 'u';
val[count++] = 'l';
val[count++] = 'l';
} else {
count = StringUTF16.putCharsAt(val, count, 'n', 'u', 'l', 'l');
}
this.count = count;
return this;
}
}
StringBuilder源码,因为StringBuffer与之区别就是synchronized修饰的区别,其他基本一样
public final class StringBuilder extends AbstractStringBuilder
implements java.io.Serializable, Comparable<StringBuilder>, CharSequence{
@IntrinsicCandidate
public StringBuilder() {
super(16);
}
@IntrinsicCandidate
public StringBuilder(int capacity) {
super(capacity);
}
@IntrinsicCandidate
public StringBuilder(String str) {
super(str);
}
public StringBuilder(CharSequence seq) {
super(seq);
}
@Override
public int compareTo(StringBuilder another) {
return super.compareTo(another);
}
@Override
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
@Override
@IntrinsicCandidate
public StringBuilder append(String str) {
super.append(str);
return this;
}
}
分析源码得到的面试题,append(null)时、使用构造器StringBuilder(null)分别会出现什么
String str = null;
StringBuffer sb = new StringBuffer();
sb.append(str);
System.out.println(sb.length());//4
System.out.println(sb);//"null",将null一个一个字符添加进去
StringBuffer sb1 = new StringBuffer(str);
System.out.println(sb1);//源码:在构造器中会调用null.length会出现空指针异常
1、String、StringBuffer、StringBuilder对比
String:不可变的字符序列。
StringBuilder:可变的字符序列,JDK5.0声明的,线程不安全,效率高。
StringBuffer:可变的字符序列,JDK1.0声明的,线程安全,效率低。
他们的底层存储字符串数据的容器使用char,使用byte。
效率从高到低:StringBuilder→StringBuffer→String
2、StringBuffer/stringBuilder的可变性分析(结合源码):
自动扩容机制——StringBuilder的底层是byte[]数组:
new空的对象,底层数组自动分配长度为16的byte[]数组,可以使用append()方法不断添加元素,一旦count要超过value.length时,就需要扩容:默认扩容为原有容量的2倍+2(如果不够就采用现有添加的长度),并将原有valve数组中的元素复制到新的数组中。
new StringBuilder(“ab”),非空,子层自动分配byte[]数组长度为16+“ab”.len,后面还是一样的。
StringBuilder的初始化和扩容分析
源码中定义的属性:char[]value;//存储字符序列,没有final修饰,说明value可以指向新的数组 int count;//实际存储的字符的个数。
//StringBuilder初始化底层byte[]数组分配的长度
StringBuilder sBuffer1 = new StringBuilder();//char[] valve = new char[16];
SfingBuilder sBuffer2 = new StringBuilder("abc"); //char[] value = new char[16 + "abc".len]
//append("ac")添加元素时,一旦count要超过value.length时byte[]就会自动扩容为原有容量*2+2,并复制到新数组中。
sBuffer1.append("ac");//value[0]='a'; value[1]='c';
sBuffer1.append("b");//value[2]='b';
//String初始化的不同
String s1 =new string();//char[] value = new char[0];//一旦初始化长度就固定了就不可增加元素
String s2 = new string("abc");// char[] value = new char[]{'a','b','c'};
3、源码启示:
- 如果开发中需要频繁的针对于字符串进行增、删、改等操作,建议使用StringBuffer或StringBuilder替换String,因为使用string效率低。
- 如果开发中,不涉及到线程安全问题,建议使用stringBuilder替换StringBuffer。因为使用StringBuilder效率高
- 如果开发中大体确定要操作的字符的个数,建议使用带int capacity参数的构造器。因为可以避免底层多次扩容操作,性能较高(底层无需扩容啥的)
StringBuilder/StringBuffer的一些操作,注意它的本质是对象,如果对内容操作的话,改变的是自身。这与String不同!!!
2.2 常用的API
因为他们是可变的,因此比String多了一些关于增删改的操作
StringBuilder与StringBuffer的主要区别就是线程安全的问题,他们的方法基本是一样的。下面方法中没写返回值的就是StringBuilder/StringBuffer。调用相关方法都是当前对象本身修改的,与String不同。
- 增加
- append(xx):提供了很多的append()方法,用于进行字符串追加的方式拼接
- 删
- delete(int start, int end):删除
[start,end)
之间字符 - deleteCharAt(int index):删除指定索引的位置字符
- delete(int start, int end):删除
- 改
- replace(int start, int end, String str):替换[start,end)范围的字符序列为str
- void setCharAt(int index, char c):替换指定索引的字符
reverse():反转
,String是没有这个方法的!
- 查
- char charAt(int index):查找指定index位置上的字符
- 插
- insert(int index, xx):在[index]位置插入xx
- void setLength(int newLength) :设置当前字符序列长度为newLength。源码中把count改为设置的newLength
- 长度
- int length():返回存储的字符数据的长度——实际存储的
- 其他与String差不多的
- 查找str在当前字符串中出现的下标:int indexOf(String str)、int indexOf(String str, int fromIndex)、lastIndexOf(String str)、int lastIndexOf(String str, int fromIndex)
- 截取子串:String substring(int start)、String substring(int start, int end)
- 转换成String类型
- String toString():返回此序列中数据的字符串表示形式
三、日期时间API
3.1 JDK8之前:System/Date/SimpleDateFormat/Calendar日历
1、JDK8之前的日期API
- java.lang.System类
- public static long currentTimeMillis():获取当前时间对应的毫秒数,long类型,时间戳当前时间与1970年1月1日0时0分0秒之间的毫秒数。常用来计算时间差
- java.util.Date:两个构造器的使用,两个方法的使用:①tostring()② long getTime()
- Date():获取本地当前时间对象
- Date(long 毫秒数):毫秒数→日期时间对象
- getTime():日期对象→毫秒数
- toString():Date对象→指定格式的String(dow mon dd hh:mm:ss zzz yyyy)
- java.sql.Date:是java.util.Date的子类。对应着数据库中的date类型,注意没有无参构造器!!不用这个类型存入数据库也可以,使用符合格式规范的String字符串也可以直接存数据库,在数据库中会有一个隐式数据类型的转换!
实现两者之间的类型转换:sql/util中的Date关联点在于可以使用构造器传入毫秒数构造日期对象,不能强转!!! - SimpleDateFormat类:用于日期时间的格式化和解析,即日期时间与格式化的String之间的转换。使用步骤:创建格式化对象再调用
- SimpleDateFormat() :默认的模式和语言环境创建对象
- public SimpleDateFormat(String pattern):该构造方法可以用参数pattern指定的格式创建一个对象
- 格式化:日期—>字符串:public String format(Date date):时间对象按指定的格式化为字符串
- 解析:字符串—>日期:public Date parse(String source):要求字符串符合规定的格式
- Calendar类(日历类):抽象类,用于完成日期字段之间相互操作的功能。
日历字段
对应的时间值: YEAR、MONTH、DAY_OF_WEEK、HOUR_OF_DAY 、MINUTE、SECOND等- 获取实例:Calendar.getInstance():一个Calendar的实例是系统时间的抽象表示,可以修改或获取
日历字段
对应的时间值。 - 常用方法:get(int field)/ set(int field,xx)/ add(int field,xx)/ getTime()/ setTime(Date)
- 获取实例:Calendar.getInstance():一个Calendar的实例是系统时间的抽象表示,可以修改或获取
/**
* java.util.Date类
*/
Date date = new Date()//获取本地当前时间,默认格式:Wed May 01 20:00:52 SGT 2024
long milliTimes = date.getTime();//1714565097391,单位是毫秒
Date date1 = new Date(milliTimes);//Wed May 01 20:06:19 SGT 2024
date1.toString();//和上面一样只不过是String类型的
/**
* java.util.Date与java.sql.Date相互转换
*/
java.sql.Date date2 = new java.sql.Date(date.getTime());//将java.util.Date得到毫秒数作为参数构造java.sql.Date对象
/**
* SimpleDateFormat类进行格式化、解析日期对象
*/
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//用这种格式对象去格式化日期对象或解析字符串为日期对象
sdf.format(date)//2024-05-01 20:04:57
sdf.parse("2024-05-01 20:04:57")//解析得到日期对象Wed May 01 20:04:57 SGT 2024
/**
* Calendar类(日历类)
*/
Calendar calendar =Calendar.getInstance();//获取Calendar类的实例
//返回给定日历字段的值
calendar.get(Calendar.DAY_0F_MONTH);//5
calendar.get(Calendar.DAY_0F_YEAR));//2024
//将给定的日历字段设置为指定的值
calendar.set(Calendar.DAY_0F_MONTH,23);//23
//根据日历的规则,为给定的日历字段添加或者减去指定的时间量
calendar.add(Calendar.DAY_0F_MONTH,-3);//20
Date date4 = calendar.getTime();//将Calendar转成Date对象:Wed May 01 20:04:57 SGT 2024
calendar.setTime(date4)//使用当前date4重置Calendar
关于String→java.util.Date→java.sql.Date的转换
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse("2024-05-01 20:04:57");
java.sql.Date date1 = new java.sql.Date(date.getTime());
3.2 JDK8:LocalDate/LocalTime/LocalDateTime/Instant/DateTimeFormatter
1、Calendar、Date的缺点
- 可变性:像日期和时间这样的类应该是不可变的。比如calendar.set(Calendar.DAY_0F_MONTH,23)导致当前实例也改变了
- 偏移性:Date中的年份是从1900开始的,而月份都从0开始。
- 格式化:格式化只对Date有用,Calendar则不行。
- 线程不安全的;不能处理闰秒等。
Date date = new Date(2022,12,3);
System.out.println(date);//会有偏移:Wed Jan 03 00:00:00 SGT 3923
2、Java8中新的日期时间API包含:一般用到用到基础包和format包
java.time
– 包含值对象的基础包。包含了所有关于时钟(Clock),本地日期(LocalDate)、本地时间(LocalTime)、本地日期时间(LocalDateTime)、时区(ZonedDateTime)和持续时间(Duration)的类。java.time.chrono
– 提供对不同的日历系统的访问。java.time.format
– 格式化和解析时间和日期java.time.temporal
– 包括底层框架和扩展特性java.time.zone
– 包含时区支持的类
3、本地日期时间类:日期LocalDate、时间LocalTime、日期时间LocalDateTime→年月日、时分秒。–→类似于Calendar
实例化、getxxx、withxxx、plusxxx、minusxxx
- 实例化:获取相关的日期时间对象
now()
/ now(ZoneId zone)of(xx,xx,xx,xx,xx,xxx)
:没有偏移量
- 获取年、月、日、时、分、秒,其中日分为年/月/周(枚举值DayOfWeek)的哪一日,其中月有枚举值Month和value之分,其余返回值都是int类型
- getYear()、【getMonth()、getMonthValue()】、【getDayOfYear()、getDayOfMonth()、getDayOfWeek()】、getHour()、getMinute()、getSecond()
- 修改:除了上面设置的枚举类改不了,其他都有对应方法修改,返回值类型是对应实例的类类型。注意本身实例不可变,要么重新指向,调用修改方法后其本身不变。
- withYear(int)、【withMonth(int)】、【withDayOfYear(int)、withDayOfMonth(int)】、withHour(int)、withMinute(int)、withSecond(int)
- with(TemporalAdjuster t):将当前日期时间设置为校对器指定的日期时间
- 添加/减少:年、月、日、时、分、秒、周,减就是替换为对应的minus,返回值类型对应实例的类类型,传入int类型参数
- plusYears()、plusMonths()、plusDays()、plusHours()、plusMinutes()、plusSeconds()、plusWeeks()
- plus(TemporalAmount t)/minus(TemporalAmount t):添加或减少一个 Duration 或 Period
- 其余
- isBefore()/isAfter()比较两个 LocalDate
- isLeapYear()判断是否是闰年(在LocalDate类中声明)
//不可变性示例
LocalDateTime localDateTime3 = localDateTime2.withDay0fMonth(15);
System.out.println(localDateTime2);//2022-12-05T15:48:48.399
System.out.println(localDateTime3);//2022-12-15T15:48:48.399
4、瞬时:Instant–→类似于Date
java.time.Instant:表示时间线上的一个瞬时点,可以被用来记录应用程序中的事件时间戳。
概念上讲,它只是简单的表示自1970年1月1日0时0分0秒(UTC)开始的秒数。
实例化:now()、ofEpochMilli(long epochMilli)都是静态方法,返回类型Instant;方法 toEpochMilli()
5、日期的格式化DateTimeFormatter(针对于LocalDate/LocalTime/LocalDateTime)----->类似于SimpleDateFormat(针对于Date)
用于格式化和解析:LocalDate/LocalTime/LocalDateTime
三种格式化方法
- 预定义的标准格式。如:ISO_LOCAL_DATE_TIME、ISO_LOCAL_DATE、ISO_LOCAL_TIME
- 本地化相关的格式。如:ofLocalizedDate(FormatStyle.LONG)
- 自定义:
- 格式化:日期时间→String:String format(LocalDate/LocalTime/LocalDateTime):本地日期、时间,返回一个字符串
- 解析:String→日期时间:TemporalAccessor parse(Charsequence text):将指定格式的字符串解析为日期、时间
- ofPattern(String pattern):静态方法,返回一个指定字符串格式的DateTimeFormatter。如:ofPattern(“yyyy-MM-dd hh:mm:ss”)
//方式三:自定义的方式(关注、重点)
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
//格式化
String strDateTime = dateTimeFormatter.format(LocalDateTime.now());
System.out.println(strDateTime); //2022/12/04 21:05:42
//解析
TemporalAccessor accessor = dateTimeFormatter.parse("2022/12/04 21:05:42");
LocalDateTime localDateTime = LocalDateTime.from(accessor);
System.out.println(localDateTime); //2022-12-04T21:05:42
6、其他API:指定时区日期时间:ZondId和ZonedDateTime、持续日期/时间:Period和Duration、Clock、时间校正器等等
还有一些与传统日期处理的转换
四、Java比较器(对象排序)
1、引出:
基本数据类型的数据(除boolean类型外):使用比较运算符
引用数据类型如何比较大小呢?实现对象排序:用它的属性比较,又回到了基本数据类型中
- 自然排序:java.lang.Comparable:“可什么什么的”——让这个类具备可比较的功能
- 定制排序:java.util.Comparator。当元素的类型没有实现又不方便修改代码,或者不想按照预定义的方法比大小时
2、两种方式对比
- 角度一:
自然排序:单一的,唯一的
定制排序:灵活的,多样的 - 角度二:
自然排序:一劳永逸的定制排序:临时的
先看源码:比较器、包装类/String/Date等等的比较方式 - 角度三:细节
自然排序:对应的接口是Comparable,对应的抽象方法compareTo(0bjectobj)
定制排序:对应的接口是Comparator,对应的抽象方法compare(0bjectob1,0bject obj2)
Comparable、Comparator接口的源码
//Comparable接口
public interface Comparable<T> {
public int compareTo(T o);
}
//Comparator接口
@FunctionalInterface
public interface Comparator<T> {
//这个方法必须要重写
int compare(T o1, T o2);
boolean equals(Object obj);
default Comparator<T> reversed() {
return Collections.reverseOrder(this);
}
//.....还有一些default方法、static方法.....
default Comparator<T> thenComparing(Comparator<? super T> other) {
Objects.requireNonNull(other);
return (Comparator<T> & Serializable) (c1, c2) -> {
int res = compare(c1, c2);
return (res != 0) ? res : other.compare(c1, c2);
};
}
}
Double比较器部分的源码分析:Double、Character、Integer、Boolean,还有String类
Comparable 的典型实现:源码的实现:(默认都是从小到大排列的
)
- String:按照字符串中字符的Unicode值进行比较。对于String只能用"xx1.compareTo(xx2)"比较,String属于类,已经重写了这个方法。
- Character:按照字符的Unicode值来进行比较
- 数值类型对应的包装类以及BigInteger、BigDecimal:按照它们对应的数值大小进行比较
- Boolean:true 对应的包装类实例大于 false 对应的包装类实例
- Date、Time等:后面的日期时间比前面的日期时间大
@jdk.internal.ValueBased
public final class Double extends Number implements Comparable<Double>, Constable, ConstantDesc {
public int compareTo(Double anotherDouble) {
return Double.compare(value, anotherDouble.value);//底层还是调用compare(xx1,xx2)
}
//对于包装类型比较的本质还是调用基本数据类型的比较运算符实现
public static int compare(double d1, double d2) {
if (d1 < d2)
return -1; // Neither val is NaN, thisVal is smaller
if (d1 > d2)
return 1; // Neither val is NaN, thisVal is larger
// Cannot use doubleToRawLongBits because of possibility of NaNs.
long thisBits = Double.doubleToLongBits(d1);
long anotherBits = Double.doubleToLongBits(d2);
return (thisBits == anotherBits ? 0 : // Values are equal
(thisBits < anotherBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN)
1)); // (0.0, -0.0) or (NaN, !NaN)
}
//举例sum(xx1,xx2)函数,本质还是调用比较运算符
public static double sum(double a, double b) {
return a + b;
}
}
/**
* Character包装类的源码分析
*/
@jdk.internal.ValueBased
public final class Character implements java.io.Serializable, Comparable<Character>, Constable {
public int compareTo(Character anotherCharacter) {
return compare(this.value, anotherCharacter.value);
}
public static int compare(char x, char y) {
return x - y;
}
}
/**
* Integer包装类的源码分析
*/
@jdk.internal.ValueBased
public final class Integer extends Number implements Comparable<Integer>, Constable, ConstantDesc {
public int compareTo(Integer anotherInteger) {
return compare(this.value, anotherInteger.value);
}
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
}
/**
* Boolean包装类的源码分析
*/
@jdk.internal.ValueBased
public final class Boolean implements java.io.Serializable, Comparable<Boolean>, Constable{
public int compareTo(Boolean b) {
return compare(this.value, b.value);
}
public static int compare(boolean x, boolean y) {
return (x == y) ? 0 : (x ? 1 : -1);
}
}
/**
* String类的源码分析
*/
public final class String implements java.io.Serializable, Comparable<String>, CharSequence, Constable, ConstantDesc {
public int compareTo(String anotherString) {
byte v1[] = value;
byte v2[] = anotherString.value;
byte coder = coder();
if (coder == anotherString.coder()) {
return coder == LATIN1 ? StringLatin1.compareTo(v1, v2)
: StringUTF16.compareTo(v1, v2);
}
return coder == LATIN1 ? StringLatin1.compareToUTF16(v1, v2)
: StringUTF16.compareToLatin1(v1, v2);
}
private static class CaseInsensitiveComparator implements Comparator<String>, java.io.Serializable {
// use serialVersionUID from JDK 1.2.2 for interoperability
@java.io.Serial
private static final long serialVersionUID = 8575799808933029326L;
public int compare(String s1, String s2) {
byte v1[] = s1.value;
byte v2[] = s2.value;
byte coder = s1.coder();
if (coder == s2.coder()) {
return coder == LATIN1 ? StringLatin1.compareToCI(v1, v2)
: StringUTF16.compareToCI(v1, v2);
}
return coder == LATIN1 ? StringLatin1.compareToCI_UTF16(v1, v2)
: StringUTF16.compareToCI_Latin1(v1, v2);
}
}
}
/**
* StringLatin1类中的compareToCI方法定义,可以看到底层还是一个一个字符的比较大小
*/
public static int compareToCI(byte[] value, byte[] other) {
int len1 = value.length;
int len2 = other.length;
int lim = Math.min(len1, len2);
for (int k = 0; k < lim; k++) {
if (value[k] != other[k]) {
char c1 = (char) CharacterDataLatin1.instance.toUpperCase(getChar(value, k));
char c2 = (char) CharacterDataLatin1.instance.toUpperCase(getChar(other, k));
if (c1 != c2) {
c1 = Character.toLowerCase(c1);
c2 = Character.toLowerCase(c2);
if (c1 != c2) {
return c1 - c2;
}
}
}
}
return len1 - len2;
}
4.1 自然排序:Comparable接口(compareTo方法)得到比较类
1、自然排序:Comparable接口强行对实现它的每个类的对象进行整体排序
(1)实现步骤:默认排序是从小到大,如果想从大到小就"-"
- 具体的类A实现Comparable接口
- 重写compareTo(xx)方法,在此方法中指明比较类A的对象的大小的标准
本质还是比较对象各个属性→回归到基本/包装类的比较"包装.compare(xx1,xx2)“、String采用"xx1.compareTo(xx2)”,因为String类已重写- 返回值为正数:当前对象this大
- 返回值为负数:当前对象this小
- 返回值为0:一样大
- 创建类A的多个实例,进行大小的比较/排序
(2)用途:实现Comparable接口的类创建的一系列对象列表(和数组)
- 可以通过 Collections.sort 或 Arrays.sort进行自动排序。
- 可以用作有序映射中的键或有序集合中的元素,无需指定比较器。比如TreeSet和TreeMap的添加元素对象add(obj)时、使用TreeSet、TreeMap构造器构造集合对象时。
2、判断类 C 的对象e1和e2是否相等的标准:调用CompareTo(obj)方法判断,最好在方法内部调用"e1.equals(e2)"判断是否相等(equals重写判断对象内容而非地址)→最好使自然排序与 equals 一致
(但是由于属性已经参与比较,本质上equals也没有必要调用,用this==o即可!)
实现比较器Comparable接口的实体类原理分析:——如何实现重写的compareTo(xx)方法?——最后还是回归到基本/包装类的比较
- 基本数据类型比大小:比较运算符、使用包装类的方式(由于自动装箱/拆箱的存在)
- 包装类比大小:①调用"包装类.compare(xx1,xx2)"[此方法是static];②调用xx1.compareTo(xx2)[非static];③加减直接得到正负结果。因为包装类都实现了Comparable接口,提供了compare(基本/包装)、compareTo(要求xx1是包装类类型,底层调用的还是compare(xx1,xx2))→这也就说明了实体类的属性定义为包装类的必要性
实现Comparable接口的实体类,代码示例:
//注意:为了省略代码,这里不用get/set方法,equals、hashCode也省了
public class Product implements Comparable{
String name;
Double price;//使用包装类型,便于调用里面的方法,比如"xx1.compareTo(xx2)"要求xx1是包装类
public Product(String name, Double price){
this.name = name;
this.price = price;
}
@Override
public String toString() {
return "Product{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
@Override
//比较的标准:先比较价格(从大到小),价格相同,进行名字的比较(从小到大)
public int compareTo(Object o){
if(this == o){//this.equals(o)没有必要,下面的属性已经在比较了,所以内容也不可有相同的
return 0;
}
if(o instanceof Product){//只有是他的子类才能强转
Product product = (Product)o;
//因为是包装类,因此可以使用xx1.compareTo(xx2)方法比较大小,但是他的底层调用到还是compare(xx1,xx2)
//this.price.compareTo(product.price);
//建议直接使用compare(xx1,xx2)
int res = Double.compare(this.price,product.price);
//对于基本类型:直接相减即可
//int res = this.price-product.price;
if(res != 0){//默认是从小到大
return -res;//"-"成为从大到小
}
//否则价格相等时,再比较name
return this.name.compareTo(product.name);//String只能用这个,String属于类,已经重写了这个方法
}
throw new RuntimeException("类型不匹配");
}
}
测试代码:Arrays.sort(xx)、比较2个对象的大小
@Test
public void test1(){
Product pro1 = new Product("huawei",12d);
Product pro2 = new Product("xiaomi",12d);
Product pro3 = new Product("iphone",11d);
Product pro4 = new Product("zapai",14d);
Product[] arr = new Product[]{pro1,pro2,pro3,pro4};
Arrays.sort(arr);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
//得到结果:价格从大到小、价格相同,名字从小到大
/**
Product{name='zapai', price=14.0}
Product{name='huawei', price=12.0}
Product{name='xiaomi', price=12.0}
Product{name='iphone', price=11.0}
*/
public void test1(){
Product pro1 = new Product("huawei",12d);
Product pro2 = new Product("xiaomi",12d);
//比较两个对象的大小
int compare = pro1.compareTo(pro2);
if(compare>0){
System.out.println("p1");//输出大的p1
}
}
4.2 定制排序:Comparetor接口(compare方法)得到比较器
相当于new一个比较器对象作为参数传入
1、定制排序:java.util.Comparator接口。强行对多个对象进行整体排序的比较。
需求场景:当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码(例如:只有.class文件,没有源文件,如Date);或者一个类实现了Comparable接口,也指定了两个对象的比较大小的规则,但不想按照它预定义的方法比较大小,又不能随意修改,因为会影响其他地方的使用。
(1)实现步骤:
- ①创建一个实现了Comparator接口的实现类A
- ②实现类A要求重写Comparator接口中的抽象方法compare(0bject o1,0bject o2),在此方法中指明要比较大小的对象的大小关系。(比如,string类、Product类,不是类A的对象)
- ③创建此实现类A的对象,并将此对象传入到相关方法的参数位置即可。(比如:Arrays.sort(…,类A的实例))
CompareTor构造器的定义方式:定义一个类实现CompareTor接口、定义匿名内部类(即一个类实现这个接口,但是这个类是匿名的)、直接匿名内部类匿名对象
比较器是针对某个类而定义的
//法1:定义定制比较器类:实现功能:价格从小打到排序,架构
public class ProductComparator implements Comparator {
@Override
public int compare(Object o1, Object o2) {
//方法里面的逻辑和下面的匿名内部类方式是一样的
}
}
//法2:匿名内部类方式
Comparator comparetor = new Comparator(){
@Override
public int compare(Object o1, Object o2){
if(o1==o2){
return 0;
}
if(o1 instanceof Product && o2 instanceof Product){
Product p1 = (Product)o1;
Product p2 = (Product)o2;
//调用包装类中的compare方法
//int res = Double.compare(p1.price,p2.price);
//建议:直接使用加减法
int res = p1.price-p2.price;
if(res != 0){
return res;//按照价格的从小到大排序
}
//价格相同的情况下,名字按照从大到小的方式,String类就采用String类里面重写的compareTo(xx)方法即可
return -p1.name.compareTo(p2.name);//从大到小排序
}
throw new RuntimeException("类型不匹配");
}
};
/法3:匿名内部类匿名对象方式
Arrays(arr,new Comparator(){
@Override
public int compare(Object o1, Object o2){
//方法里面的逻辑和上面的匿名内部类方式是一样的
}
});
调用方式
//构造器实现类:需创建对象,而匿名内部类不需要
ProductComparator comparetor = new ProductComparator();
comparetor.compare(arr[j], arr[j + 1]);//单独比较两个对象
Arrays.sort(arr, comparetor);
//使用举例:按照价格从小到大,价格相同,按照name从大到小========效果等于Arrays.sort(arr,comparetor);
System.out.println("按照成绩排序");
for (int i = 1; i < arr.length; i++) {
for (int j = 0; j < arr.length - i; j++) {
if (comparetor.compare(arr[j], arr[j + 1]) > 0) {
Product temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
String数组的从大到小排序实现
@Test
public void test3(){
String[] strs = new String[]{"123","sff","rtg","zhang","shi"};
Arrays.sort(strs);//默认排序方式:从小到大
Arrays.sort(strs,new Comparator(){
@Override
public int compare(Object o1, Object o2){
if(o1 == o2){
return 0;
}
if(o1 instanceof String && o2 instanceof String){
String s1 = (String) o1;
String s2 = (String) o2;
int res = s1.compareTo(s2);//s1、s2是String类,其中已经重写给了compareTo(xx)方法,调用即可
if(res != 0){
return -res;//实现从大到小排序
}
}
throw new RuntimeException("类型不匹配");
}
});
for (int i = 0; i < strs.length; i++) {
System.out.println(strs[i]);
}
}
五、系统相关类System/Runtime
1、java.lang.System类:定义系统级的属性、控制方法(static
),构造器为private无法创建该类的对象。
- 成员变量:3个 Scanner scan = new Scanner(System.in);
in
:标准输入流(键盘输入)out
:标准输出流(显示器)err
:标准错误输出流(显示器)
- 成员方法
native long currentTimeMillis()
:返回当前的计算机时间,毫秒数。void exit(int status)
:退出程序。status=0时正常退出,非零代表异常退出。可在图形界面编程中实现程序的退出功能等。void gc()
:请求系统进行垃圾回收。(取决于系统中垃圾回收算法的实现以及系统执行时的情况)String getProperty(String key)
:获得系统中属性名为key的属性对应的值。
系统中常见的属性名及其作用:java.version/home:java运行时环境版本/安装目录;os.name/version:操作系统的名称/版本;user.name/home/dir:用户的账户名称/主目录/当前工作目录- static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length):从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。常用于数组的插入和删除
2、java.lang.Runtime类:每个 Java 应用程序都有一个 Runtime
类实例,使应用程序能够与其运行的环境相连接。
public static Runtime getRuntime()
: 不能创建Runtime类实例,需要调用该方法。public long totalMemory()
:返回 Java 虚拟机中初始化时的内存总量。(取决于主机环境,默认为物理电脑内存的1/64)public long maxMemory()
:返回 Java 虚拟机中最大程度能使用的内存总量。默认为物理电脑内存的1/4。public long freeMemory()
:回 Java 虚拟机中的空闲内存量。调用 gc 方法可能导致 freeMemory 返回值的增加。
六、和数学相关的类Math
1、java.lang.Math:包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。
类似这样的工具类,其所有方法均为静态方法,并且不会创建对象,调用起来非常简单。
public static double abs(double a)
:返回 double 值的绝对值。public static double ceil(double a)
:返回大于等于参数的最小的整数。public static double floor(double a)
:返回小于等于参数最大的整数。public static long round(double a)
:返回最接近参数的 long。(相当于四舍五入方法)- public static double pow(double a,double b):返回a的b幂次方法
- public static double sqrt(double a):返回a的平方根
public static double random()
:返回[0,1)的随机值- public static final double PI:返回圆周率
- public static double max(double x, double y):返回x,y中的最大值
- public static double min(double x, double y):返回x,y中的最小值
- 其它:acos,asin,atan,cos,sin,tan 三角函数
2、java.math.BigInteger:可以表示不可变的任意精度的整数
。提供所有 Java 的基本整数操作符的对应物,并提供 java.lang.Math 的所有相关方法。还提供以下运算:模算术、GCD 计算、质数测试、素数生成、位操作以及一些其他操作。
Integer类作为int的包装类,能存储的最大整型值为231-1,Long类也是有限的最大为263-1。如果要表示再大的整数,不管是基本数据类型还是他们的包装类都无能为力,更不用说进行运算了。
- 构造器
- 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) :返回其值为 (this^exponent) 的 BigInteger。
- public BigInteger
@Test
public void test01(){
//long bigNum = 123456789123456789123456789L;
BigInteger b1 = new BigInteger("12345678912345678912345678");
BigInteger b2 = new BigInteger("78923456789123456789123456789");
//System.out.println("和:" + (b1+b2));//错误的,无法直接使用+进行求和
System.out.println("和:" + b1.add(b2));
System.out.println("减:" + b1.subtract(b2));
System.out.println("乘:" + b1.multiply(b2));
System.out.println("除:" + b2.divide(b1));
System.out.println("余:" + b2.remainder(b1));
}
2、java.math.BigDecimal:在商业计算中,要求数字精度比较高。BigDecimal类支持不可变的、任意精度的有符号十进制定点数。
一般的Float类和Double类可以用来做科学计算或工程计算。
- 构造器
- 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):divisor是除数,scale指明保留几位小数,roundingMode指明舍入模式(ROUND_UP :向上加1、ROUND_DOWN :直接舍去、ROUND_HALF_UP:四舍五入)
- public BigDecimal
@Test
public void test03(){
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));
}
3、java.util.Random:用于产生随机数
boolean nextBoolean()
:返回下一个伪随机数,它是取自此随机数生成器序列的均匀分布的 boolean 值。void nextBytes(byte[] bytes)
:生成随机字节并将其置于用户提供的 byte 数组中。double nextDouble()
:返回下一个伪随机数,它是取自此随机数生成器序列的、在 0.0 和 1.0 之间均匀分布的 double 值。float nextFloat()
:返回下一个伪随机数,它是取自此随机数生成器序列的、在 0.0 和 1.0 之间均匀分布的 float 值。double nextGaussian()
:返回下一个伪随机数,它是取自此随机数生成器序列的、呈高斯(“正态”)分布的 double 值,其平均值是 0.0,标准差是 1.0。int nextInt()
:返回下一个伪随机数,它是此随机数生成器的序列中均匀分布的 int 值。int nextInt(int n)
:返回一个伪随机数,它是取自此随机数生成器序列的、在 0(包括)和指定值(不包括)之间均匀分布的 int 值。long nextLong()
:返回下一个伪随机数,它是取自此随机数生成器序列的均匀分布的 long 值。
@Test
public void test04(){
Random r = new Random();
System.out.println("随机整数:" + r.nextInt());
System.out.println("随机小数:" + r.nextDouble());
System.out.println("随机布尔值:" + r.nextBoolean());
}
七、企业真题
1、以下两种方式创建的String对象有什么不同?
String str = new String("test");
String str = "test";
new对象定义:在堆空间创建一个对象,再去常量池中如果没有就创建,有就不需要了
字面量定义方式:直接去常量池中创建一个
2、String s = new String(“xyz”);创建了几个String Object? (新陆)
1/2个
3 String a=“abc” String b=“a”+“bc” 问a==b?(网邮箱)
是!
4、String 中 “+” 怎样实现?(阿)
常量 + 常量 :略
变量 + 常量 、变量+变量:创建一个StringBuilder的实例,通过append()添加字符串,最后调用toString()返回一个字符串。(toString()内部new 一个String的实例)
5、Java中String是不是final的?(凡科技)
是
6、String为啥不可变,在内存中的具体形态?(阿)
规定不可变。
String:提供字符串常量池。
7、String 可以在 switch中使用吗?(上海睿)
可以。从jdk7开始可以使用
8、subString()到底做了什么?(银数据)
String str = “hello”;
String subStr = str.subString(1,3); //底层是new的方式返回一个subStr,实体内容是"el"
9、Java中操作字符串有哪些类?他们之间有什么区别。(南电网)
类似问题:
> String 和 StringBuffer区别?(亿*国际、天*隆、*团)
> StringBuilder和StrignBuffer的区别?(平*金服)
> StringBuilder和StringBuffer的区别以及实现?(*为)
> String:不可变的字符序列;底层使用char[] (jdk8及之前),底层使用byte[] (jdk9及之后)
> StringBuffer:可变的字符序列;JDK1.0声明,线程安全的,效率低;底层使用char[] (jdk8及之前),底层使用byte[] (jdk9及之后)
> StringBuilder:可变的字符序列;JDK5.0声明,线程不安全的,效率高;底层使用char[] (jdk8及之前),底层使用byte[] (jdk9及之后)
10、String的线程安全问题(闪购)
线程不安全的
11、简单说说 Comparable 和 Comparator 的区别和场景?(软力)
Comparable:自然排序
Comparator:定制排序