一、字符串相关的类
1. String的特性
- String类:代表字符串。Java 程序中的所有字符串字面值(如 "abc" )都作为此类的实例实现。
- String是一个final类,代表不可变的字符序列。
- 字符串是常量,用双引号引起来表示。他们的值在创建之后不能更改。
- String对象的字符内容是存储在一个字符数组value[]中的。
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
//我们存储的字符串内容就存储在此。
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
2.String类的使用
1. String字符串字面量的定义方式
//字面量的定义方式
String s1 = "abc";
String s2 = "abc";
s1 = "hello";
该定义方式在内存中的解析:
- 首先我们采用字面量创建的字符串是在字符串常量池中定义的。
- 字符串常量池中不存储字符串内容相同的两个字符串,即:若两个字符串相同,则实际上就是一个字符串,只是用两个引用指向了它。
- 从图中所示,s1和s2的内容都为"abc",所以s1和s2都指向了同一个地址,当s1="hello"时,由于字符串的不可变性,常量池中创建了新的内存"hello",且s1值向了"hello".
2. String对象的创建
String类有多种构造器,我们可以使用多种方法进行创建字符串:
//使用字符串的字面量
String str = "hello";
//本质上this.value = new char[0];
String s1 = new String();
//this.value = original.value;
String s2 = new String(String original);
//this.value = Arrays.copyOf(value, value.length);
String s3 = new String(char[] a);
String s4 = new String(char[] a,int startIndex,int count);
字面量方式和构造器方式的区别?
String str1 = "abc"
这种方式直接在常量池中创建,其地址也指向了常量池。String str2 = new String("abc");
这种方式是通过new的方式创建的,所以一定是在堆中有一个String类的对象,对象的属性value指向了常量池中的"abc",而str2本身指向的是堆中的地址。
练习-1:
String s1 = "javaEE";
String s2 = "javaEE";
String s3 = new String("javaEE");
String s4 = new String("javaEE");
s1 == s2 ?//true
s1 == s3 ?//false
s1 == s4 ?//false
s3 == s4 ?//false
内存情况:
练习-2:
Person p1 = new Person();
p1.name = "atguigu";
Person p2 = new Person();
p2.name = "atguigu";
System.out.println(p1.name .equals( p2.name)); //true
System.out.println(p1.name == p2.name); //true
System.out.println(p1.name == "atguigu"); //true
String s1 = new String("bcde");
String s2 = new String("bcde");
System.out.println(s1==s2); //false
对象中的字符串类型的属性是直接指向常量池中的地址。
判断:
String s1 = "javaEE";
String s2 = "hadoop";
String s3 = "javaEEhadoop";
String s4 = "javaEE" + "hadoop";
String s5 = s1 + "hadoop";
String s6 = "javaEE" + s2;
String s7 = s1 + s2;
System.out.println(s3 == s4);//true
System.out.println(s3 == s5);//false
System.out.println(s3 == s6);//false
System.out.println(s3 == s7);//false
System.out.println(s5 == s6);//false
System.out.println(s5 == s7);//false
System.out.println(s6 == s7);//false
//intern方法的返回值一定是在常量池中声明的,所以s3 == s8
String s8 = (s1 + s2).intern();
System.out.println(s3 == s8);//true
//定义了一个常量s9
final String s9 = "hadoop";
//s10 = 常量 + 常量;所以s3 == s10 是true
String s10 = "javaEE" + s9;
System.out.println(s3 == s10);//true
- 常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。
- 只要其中有一个是变量,结果就在堆中
- 如果拼接的结果调用intern()方法,返回值就在常量池中
3. String使用陷阱
//在字符串常量池中创建了一个字面量为"a"的字符串。
String s1 = "a";
//实际上原来的“a”字符串对象已经丢弃了,现在在堆空间中产生了一个字符串s1+"b"(也就是"ab")。如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能。
s1 = s1 + "b";
由于字符串的不可变性,在经常对字符串进行处理或修改的时候会极大的影响效率:
练习-1:
public class StringTest{
String str = new String("good");
char[] ch = { 't', 'e', 's', 't' };
//此时,str和实参不是一个字符串,而是一个新的字符串,只是两个字符串同时指向了同一块地址=good
public void change(String str, char ch[]) {
//由于字符串的不可变性,在修改str时,常量池中创建了新的地址,且str指向了新地址,而实参ex.str此时仍然指向了旧的地址=good。
str = "test ok";
//数组是可变的,传递其地址,所以就可以修改其内容
ch[0] = 'b';
}
public static void main(String[] args) {
//创建了类对象,其属性一个为String对象,一个为字符数组。
StringTest ex = new StringTest();
//调用change方法,考察值传递
ex.change(ex.str, ex.ch);
System.out.print(ex.str);//good
System.out.println(ex.ch);//best
}
}
4. String类的常用方法:
具体查看String类的API,常用方法太多了!
int length()
:返回字符串的长度: return value.lengthchar charAt(int index)
: 返回某索引处的字符return value[index]boolean isEmpty()
:判断是否是空字符串:return value.length == 0String 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 contains(CharSequence s)
:当且仅当此字符串包含指定的 char 值序列 时,返回 trueint indexOf(String str)
:返回指定子字符串在此字符串中第一次出现处的索引int indexOf(String str, int fromIndex)
:返回指定子字符串在此字符串中第一次出 现处的索引,从指定的索引开始int lastIndexOf(String str)
:返回指定子字符串在此字符串中最右边出现处的索引int lastIndexOf(String str, int fromIndex)
:返回指定子字符串在此字符串中最后 一次出现处的索引,从指定的索引开始反向搜索注:indexOf和lastIndexOf方法如果未找到都是返回-1boolean endsWith(String suffix)
:测试此字符串是否以指定的后缀结束boolean startsWith(String prefix)
:测试此字符串是否以指定的前缀开始boolean startsWith(String prefix, int toffset)
:测试此字符串从指定索引开始的 子字符串是否以指定前缀开始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个,如果超过了,剩下的全部都放到最后一个元素中。
5. String与基本数据类型转换
- 字符串 ----- 基本数据类型、包装类
- Integer包装类的
public static int parseInt(String s)
:可以将由“数字”字 符组成的字符串转换为整型。 - 类似地,使用java.lang包中的
Byte、Short、Long、Float、Double
类调相应 的类方法可以将由“数字”字符组成的字符串,转化为相应的基本数据类型。
- Integer包装类的
- 基本数据类型、包装类 ----- 字符串
- 调用String类的
public String valueOf(int n)
可将int型转换为字符串。 - 相应的
valueOf(byte b)、valueOf(long l)、valueOf(float f)、valueOf(double d)、valueOf(boolean b)
可由参数的相应类型到字符串的转换。
- 调用String类的
- 字符数组 ----- 字符串
- String 类的构造器:
String(char[]) 和 String(char[],int offset,int length)
分别用字符数组中的全部字符和部分字符创建字符串对象。
- String 类的构造器:
- 字符串 ----- 字符数组
public char[] toCharArray()
:将字符串中的全部字符存放在一个字符数组 中的方法。public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
:提供了将指定索引范围内的字符串存放到数组中的方法。
- 字节数组 ----- 字符串
String(byte[])
:通过使用平台的默认字符集解码指定的 byte 数组,构 造一个新的 String。String(byte[],int offset,int length)
:用指定的字节数组的一部分, 即从数组起始位置offset开始取length个字节构造一个字符串对象。
- 字符串 ----- 字节数组
public byte[] getBytes()
:使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。public byte[] getBytes(String charsetName)
:使用指定的字符集将 此 String 编码到 byte 序列,并将结果存储到新的 byte 数组。
6. 常见算法题目
1. 将一个字符串进行反转。将字符串中指定部分进行反转。比如“abcdefg”反转为”abfedcg”
public static String reverse(int start,int end,String string){
if (string != null && !string.isEmpty()){
StringBuilder stringBuilder = new StringBuilder(string.length());
stringBuilder.append(string.substring(0,start));
for (int i = end; i >= start; i--){
stringBuilder.append(string.charAt(i));
}
stringBuilder.append(string.substring(end+1));
return stringBuilder.toString();
}
return null;
}
2. 获取一个字符串在另一个字符串中出现的次数。 比如:获取“ ab”在 “abkkcadkabkebfkabkskab” 中出现的次数
private static int getCount(String str,String subStr){
int count = 0;
int mainLen = str.length();
int subLen = subStr.length();
if (mainLen < subLen){
return 0;
}
int index = str.indexOf(subStr, 0);
while (index != -1){
index = str.indexOf(subStr,index+1);
count++;
}
return count;
}
3. 获取两个字符串中最大相同子串。
**比如: str1 = "abcwerthelloyuiodef“;str2 = "cvhellobnm" **
提示:将短的那个串进行长度依次递减的子串与较长的串比较。
private static List<String> getMaxSameString(String str1,String str2){
if (str1 == null || str1.isEmpty()){
return null;
}
if (str2 == null || str2.isEmpty()){
return null;
}
//长的字符串
int maxLen = str1.length();
//短的字符串
int minLen = str2.length();
//首先保证str1一定是长的,str2一定是短的。
if (minLen > maxLen){
String temp = str1;
str1 = str2;
str2 = temp;
}
boolean flag = false;
List<String> list = new ArrayList<>();
//subLength指的是子串的长度,最开始就是最长的10个字符
for (int subLength = str2.length(); subLength > 0; subLength--){
//start起始索引,end终止索引,最开始起始索引为0,终止索引
for (int start = 0,end = subLength; end <= str2.length(); end++,start++){
String subString = str2.substring(start,end);
if (str1.contains(subString)){
list.add(subString);
flag = true;
}
}
if (flag){
break;
}
}
return list;
3. StringBuffer和StringBuilder类
java.lang.StringBuffer
和java.lang.StringBuilder
代表可变的字符序列,JDK1.0中声明,可以对字符串内容进行增删,此时不会产生新的对象。以下均以StringBuffer举例:- 很多方法与String相同
- 作为参数传递时,方法内部可以改变值。
- StringBuffer类不同于String,其对象必须使用构造器生成。有三个构造器:
StringBuffer()
: 初始容量为16的字符串缓冲区StringBuffer(int size)
:构造指定容量的字符串缓冲区StringBuffer(String str)
:将内容初始化为指定字符串内容
1. 常用方法:
StringBuffer append(xxx)
:提供了很多的append()方法,用于进行字符串拼接StringBuffer delete(int start,int end)
:删除指定位置的内容StringBuffer replace(int start, int end, String str)
:把[start,end)位置替换为strStringBuffer insert(int offset, xxx)
:在指定位置插入xxxStringBuffer reverse()
:把当前字符序列逆转- 当append()和insert()时,如果原来value数组长度不够,可以扩容。
- 支持方法链,即str.append("1").append("2").append("3");
public int indexOf(String str)
public String substring(int start,int end)
public int length() public char charAt(int n)
public void setCharAt(int n ,char ch)
2. StringBuilder、StringBuffer、String三者的区别:
- String(JDK 1.0):不可变字符序列
- StringBuffer(JDK 1.0):可变字符序列,效率低,线程安全
- StringBuilder(JDK 5.0):可变字符序列,效率高,线程不安全
- 作为参数进行传递,String不会改变其值,StringBuffer和StringBuilder会改变其值。
- 效率测试:
//初始设置
long startTime = 0L;
long endTime = 0L;
String text = "";
StringBuffer buffer = new StringBuffer("");
StringBuilder builder = new StringBuilder("");
//开始对比
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
buffer.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuffer的执行时间:" + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
builder.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuilder的执行时间:" + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
text = text + i;
}
endTime = System.currentTimeMillis();
System.out.println("String的执行时间:" + (endTime - startTime));
执行结果:
StringBuffer的执行时间:5
StringBuilder的执行时间:2
String的执行时间:284
练习-1:
String str = null;
StringBuffer sb = new StringBuffer();
//相当于 sb.append(str) --> 源码中的appendNull() ---> "null"
sb.append(str);
//4
System.out.println(sb.length());
//"null"
System.out.println(sb);
//因为源码中该构造器中调用父类的构造器,创建一个空数组
//行号151 : 调用了 ---> str.length(); ----> 抛出NullPointerException
StringBuffer sb1 = new StringBuffer(str);
System.out.println(sb1);
练习-2:
public class TestStr{
public static void main(String[] args){
String a = "123";
String b = "123";
String c = new String("123");
String d = new String("123");
System.out.println(a.equals(b));//true
System.out.println(a == b);//true
System.out.println(c.equals(d));//true
System.out.println(c == d);//false
System.out.println(a.equals(c));//true
System.out.println(a == c);//false
}
}
4. 编码与解码
编码:字符串-----字节
解码:字节-----字符串
格式化:日期-----字符串
解析:字符串-----日期
二、JDK8之前的日期时间API
1.Java.lang.System类
System类提供的public static long currentTimeMillis()用来返回当前时 间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差。
-
此方法适于计算时间差。
-
计算世界时间的主要标准有:
- UTC(Coordinated Universal Time)
- GMT(Greenwich Mean Time)
- CST(Central Standard Time)
2. Java.util.Date类
表示特定的瞬间,精确到毫秒
- 构造器
- Date():使用无参构造器创建的对象可以获取本地当前时间
- Date(long date)
- 常用方法
- getTime():返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象 表示的毫秒数。
- toString():把此 Date 对象转换为以下形式的 String: dow mon dd hh:mm:ss zzz yyyy 其中: dow 是一周中的某一天 (Sun, Mon, Tue, Wed, Thu, Fri, Sat),zzz是时间标准。
- 其它很多方法都过时了。
Date date = new Date();
System.out.println(date);
System.out.println(System.currentTimeMillis());
System.out.println(date.getTime());
Date date1 = new Date(date.getTime());
System.out.println(date1.getTime());
System.out.println(date1.toString());
3. Java.text.SimpleDateFormat类
- Date类的API不易于国际化,大部分被废弃了,
java.text.SimpleDateFormat
类是一个不与语言环境有关的方式来格式化和解析日期的具体类。 - 它允许进行格式化:日期----文本、解析:文本-----日期
- 格式化:
SimpleDateFormat()
:默认的模式和语言环境创建对象public SimpleDateFormat(String pattern)
:该构造方法可以用参数pattern 指定的格式创建一个对象,该对象调用:public String format(Date date)
:方法格式化时间对象date
- 解析:
public Date parse(String source)
:从给定字符串的开始解析文本,以生成 一个日期。
Date date = new Date(); // 产生一个Date实例
// 产生一个formater格式化的实例
SimpleDateFormat formater = new SimpleDateFormat();
// 打印输出默认的格式
System.out.println(formater.format(date));
SimpleDateFormat formater2 = new SimpleDateFormat("yyyy年MM月dd日 EEE
HH:mm:ss");
System.out.println(formater2.format(date));
try {
// 实例化一个指定的格式对象
Date date2 = formater2.parse("2008年08月08日 星期一 08:08:08");
// 将指定的日期解析后格式化按指定的格式输出
System.out.println(date2.toString());
} catch (ParseException e) {
e.printStackTrace();
}
4. Java.util.Calendar类
- Calendar是一个抽象基类,主用用于完成日期字段之间相互操作的功能。
- 获取Calendar实例的方法
- 使用
Calendar.getInstance()
方法 - 调用它的子类
GregorianCalendar
的构造器。
- 使用
- 一个Calendar的实例是系统时间的抽象表示,通过
get(int field)
方法来取得想 要的时间信息。比如YEAR、MONTH、DAY_OF_WEEK、HOUR_OF_DAY 、 MINUTE、SECONDpublic void set(int field,int value)
public void add(int field,int amount)
public final Date getTime()
public final void setTime(Date date)
- 注意
- calendar有一个弊端:可变性
- 获取月份时:一月是0,二月是1,以此类推,12月是11
- 获取星期时:周日是1,周二是2 , 。。。。周六是7
Calendar calendar = Calendar.getInstance();
// 从一个 Calendar 对象中获取 Date 对象
Date date = calendar.getTime();
// 使用给定的 Date 设置此 Calendar 的时间
date = new Date(234234235235L);
calendar.setTime(date);
calendar.set(Calendar.DAY_OF_MONTH, 8);
System.out.println("当前时间日设置为8后,时间是:" + calendar.getTime());
calendar.add(Calendar.HOUR, 2);
System.out.println("当前时间加2小时后,时间是:" + calendar.getTime());
calendar.add(Calendar.MONTH, -2);
System.out.println("当前日期减2个月后,时间是:" + calendar.getTime());
三、JDK8中新日期时间API
1. 出现的背景
JDK 1.0中包含了 一个java.util.Date类,但是它的大多数方法已经在JDK 1.1引入Calendar类之后被弃用 了。而Calendar并不比Date好多少。它们面临的问题是:
- 可变性:像日期和时间这样的类应该是不可变的。
- 偏移性:Date中的年份是从1900开始的,而月份都从0开始。
- 格式化:格式化只对Date有用,Calendar则不行。
- 此外,它们也不是线程安全的;不能处理闰秒等。
- 对日期和时间的操作一直是Java程序员最痛苦的地方之一。
2. 新的时间日期API
Java 8 吸收了 Joda-Time 的精华,以一个新的开始为 Java 创建优秀的 API。 新的 java.time 中包含了所有关于
- 本地日期(LocalDate)
- 本地时间 (LocalTime)
- 本地日期时间(LocalDateTime)
- 时区(ZonedDateTime)
- 持续时间(Duration)的类。
历史悠久的 Date 类新增了 toInstant() 方法, 用于把 Date 转换成新的表示形式。这些新增的本地化时间日期 API 大大简 化了日期时间和本地化的管理。
1. LocalDate、LocalTime、LocalDateTime
LocalDate、LocalTime、LocalDateTime 类是其中较重要的几个类
它们的实例是不可变的对象,分别表示使用 ISO-8601日历系统的日期、时间、日期和时间。
它们提供了简单的本地日期或时间,并不包含当前的时间信息,也不包含与时区 相关的信息。
- LocalDate代表IOS格式(yyyy-MM-dd)的日期,可以存储 生日、纪念日等日期。
- LocalTime表示一个时间,而不是日期。
- LocalDateTime是用来表示日期和时间的,这是一个最常用的类之一。
ISO-8601日历系统是国际标准化组织制定的现代公民的日期和时间的表示法,也就是公历。
常用方法
2. 瞬时:Instant
- Instant:时间线上的一个瞬时点。 这可能被用来记录应用程序中的事件时间戳。
- 在处理时间和日期的时候,我们通常会想到年,月,日,时,分,秒。然而,这只是 时间的一个模型,是面向人类的。第二种通用模型是面向机器的,或者说是连 续的。在此模型中,时间线中的一个点表示为一个很大的数,这有利于计算机 处理。在UNIX中,这个数从1970年开始,以秒为的单位;同样的,在Java中, 也是从1970年开始,但以毫秒为单位。
- java.time包通过值类型Instant提供机器视图,不提供处理人类意义上的时间单位。Instant表示时间线上的一点,而不需要任何上下文信息,例如,时区。 概念上讲,它只是简单的表示自1970年1月1日0时0分0秒(UTC)开始的秒数。因为java.time包是基于纳秒计算的,所以Instant的精度可以达到纳秒级。
(1 ns = 10-9 s) 1秒 = 1000毫秒 =106微秒=109纳秒
常用方法
3. 格式化与解析日期或时间
java.time.format.DateTimeFormatter
类:该类提供了三种格式化方法:
- 预定义的标准格式。如: ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME
- 本地化相关的格式。如:ofLocalizedDateTime(FormatStyle.LONG)
- 自定义的格式。如:ofPattern(“yyyy-MM-dd hh:mm:ss”)
4. 其他API
具体查看API文档
四、Java比较器
- 在Java中经常会涉及到对象数组的排序问题,那么就涉及到对象之间的比较问题。
- Java实现对象排序的方式有两种:
- 自然排序:java.lang.Comparable
- 定制排序:java.util.Comparator
方式一:自然排序
- Comparable接口强行对实现它的每个类的对象进行整体排序。这种排序被称 为类的自然排序。
- 实现 Comparable 的类必须实现 compareTo(Object obj) 方法,两个对象即 通过 compareTo(Object obj) 方法的返回值来比较大小。
- 如果当前对象this大 于形参对象obj,则返回正整数
- 如果当前对象this小于形参对象obj,则返回负整数
- 如果当前对象this等于形参对象obj,则返回零。
- 实现Comparable接口的对象列表(和数组)可以通过 Collections.sort 或 Arrays.sort进行自动排序。实现此接口的对象可以用作有序映射中的键或有 序集合中的元素,无需指定比较器。
- 对于类 C 的每一个 e1 和 e2 来说,当且仅当 e1.compareTo(e2) == 0 与 e1.equals(e2) 具有相同的 boolean 值时,类 C 的自然排序才叫做与 equals 一致。建议(虽然不是必需的)最好使自然排序与 equals 一致。
典型实现
- String:按照字符串中字符的Unicode值进行比较
- Character:按照字符的Unicode值来进行比较
- 数值类型对应的包装类以及BigInteger、BigDecimal:按照它们对应的数值 大小进行比较
- Boolean:true 对应的包装类实例大于 false 对应的包装类实例
- Date、Time等:后面的日期时间比前面的日期时间大
class Goods implements Comparable {
private String name;
private double price;
//按照价格,比较商品的大小
@Override
public int compareTo(Object o) {
if(o instanceof Goods) {
Goods other = (Goods) o;
if (this.price > other.price) {
return 1;
} else if (this.price < other.price) {
return -1;
}
return 0;
}
throw new RuntimeException("输入的数据类型不一致");
}
//构造器、getter、setter、toString()方法略
}
public class ComparableTest{
public static void main(String[] args) {
Goods[] all = new Goods[4];
all[0] = new Goods("《红楼梦》", 100);
all[1] = new Goods("《西游记》", 80);
all[2] = new Goods("《三国演义》", 140);
all[3] = new Goods("《水浒传》", 120);
Arrays.sort(all);
System.out.println(Arrays.toString(all));
}
}
方式二:定制排序
- 当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码, 或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,那 么可以考虑使用 Comparator 的对象来排序,强行对多个对象进行整体排序的比较。
- 重写compare(Object o1,Object o2)方法,比较o1和o2的大小:
- 如果方法返回正整数,则表示o1大于o2;
- 如果返回0,表示相等;
- 返回负整数,表示 o1小于o2。
- 可以将 Comparator 传递给 sort 方法(如 Collections.sort 或 Arrays.sort), 从而允许在排序顺序上实现精确控制。
- 还可以使用 Comparator 来控制某些数据结构(如有序 set或有序映射)的 顺序,或者为那些没有自然顺序的对象 collection 提供排序。
Goods[] all = new Goods[4];
all[0] = new Goods("War and Peace", 100);
all[1] = new Goods("Childhood", 80);
all[2] = new Goods("Scarlet and Black", 140);
all[3] = new Goods("Notre Dame de Paris", 120);
Arrays.sort(all, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Goods g1 = (Goods) o1;
Goods g2 = (Goods) o2;
return g1.getName().compareTo(g2.getName());
}
});
System.out.println(Arrays.toString(all));
五、System类
- System类代表系统,系统级的很多属性和控制方法都放置在该类的内部。 该类位于java.lang包。
- 由于该类的构造器是private的,所以无法创建该类的对象,也就是无法实 例化该类。其内部的成员变量和成员方法都是static的,所以也可以很方便 的进行调用。
- 成员变量
- System类内部包含in、out和err三个成员变量,分别代表标准输入流 (键盘输入),标准输出流(显示器)和标准错误输出流(显示器).
- 成员方法
native long currentTimeMillis()
: 该方法的作用是返回当前的计算机时间,时间的表达格式为当前计算机时 间和GMT时间(格林威治时间)1970年1月1号0时0分0秒所差的毫秒数。void exit(int status)
: 该方法的作用是退出程序。其中status的值为0代表正常退出,非零代表 异常退出。使用该方法可以在图形界面编程中实现程序的退出功能等。void gc()
: 该方法的作用是请求系统进行垃圾回收。至于系统是否立刻回收,则 取决于系统中垃圾回收算法的实现以及系统执行时的情况。String getProperty(String key)
: 该方法的作用是获得系统中属性名为key的属性对应的值。系统中常见 的属性名以及属性的作用如下表所示:
//获得java版本号
String javaVersion = System.getProperty("java.version");
System.out.println("java的version:" + javaVersion);
//获得java_home的位置
String javaHome = System.getProperty("java.home");
System.out.println("java的home:" + javaHome);
//操作系统的名字
String osName = System.getProperty("os.name");
System.out.println("os的name:" + osName);
//操作系统版本
String osVersion = System.getProperty("os.version");
System.out.println("os的version:" + osVersion);
//用户名
String userName = System.getProperty("user.name");
System.out.println("user的name:" + userName);
//用户的位置
String userHome = System.getProperty("user.home");
System.out.println("user的home:" + userHome);
String userDir = System.getProperty("user.dir");
System.out.println("user的dir:" + userDir);
六、Math类
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) 角度—>弧度
七、BigInteger与BigDecimal
BigInteger类
- Integer类作为int的包装类,能存储的最大整型值为2 31-1,Long类也是有限的, 最大为2 63-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) 的 BigIntegerBigInteger subtract(BigInteger val)
:返回其值为 (this - val) 的 BigIntegerBigInteger multiply(BigInteger val)
:返回其值为 (this * val) 的 BigIntegerBigInteger 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。
BigDecimal类
- 一般的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));
}
八、包装类
1. 包装类基本知识
Java是面向对象的语言,但并不是"纯面向对象"的,因为我们经常用到的基本数据类型不是对象。但是我们实际应用中经常需要将数据类型转换成对象,以便于操作。比如:将基本数据类型存储到Object[]数组或集合中的操作等等。
为了解决这个不足,Java在设计类时,为每一个基本数据类型设计了一个对应的类进行列表,这样八个和基本数据类型对应的类统称为包装类(Wrapper Class)。
八种包装类都位于java.lang
包下,对应关系如下:
基本数据类型 | 包装类 |
---|---|
byte | Byte |
boolean | Boolean |
short | Short |
char | Character |
int | Integer |
long | Long |
float | Float |
double | Double |
- 在这八个类名中,除了Integer和Character之外,只需要将首字母大写即可。
- 除了Character和Boolean之外,其余的六个都是
数字型
,即都是java.lang.Number的子类。Number是个抽象类,因此他的抽象方法,所有的子类都需要提供实现。 - Number提供了抽象方法:
intValue()、longValue()、floatValue()、doubleValue()
意味着所有的“数字类型”都可以互相转型。
Number类的源码分析
-
Number类下有11个子类,其中Float、Long、Double、Short、Byte、Integer是我们基本数据类型的包装类。
-
BigDecimal类和BigInteger类
这两个类可以处理任意长度的数值,BigInteger实现了任意精度的整数运算。BigDecimal实现了任意精度的浮点运算。 -
如何进行包装类的转换
//基本数据类型转换为包装类 Integer a = new Integer(3); Integer b = Integer.valueOf(3); //包装类转换为基本数据类型 int c = b.intValue(); doueble d = b.doubleValue(); //把字符串转换为包装类对象 Integer e = new Integer("999");