Java常用类
字符串相关的类:String
String的特性
- String类代表字符串,Java程序中所有字符串都是该类的实例化对象;
- String是一个final类,代表不可变的字符序列且不可被继承;
- 字符串是常量,一旦创建无法更改,并用双引号括起来;
- String对象的字符内容是存储在一个字符数组value[ ]中的;
- String实现了Serializable接口:表示字符串是支持序列化的;
- 实现了Comparable接口:表示String可以比较大小;
- 当对字符串重新赋值时,需要重新指定内存区域赋值,不能使用原有的value进行赋值;
- 当调用String中replace()方法修改指定字符或字符串时,也需要重新指定内存区域。
例
import org.junit.Test;
public class NewString {
@Test
public void test1 () {
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);
s1 = "hello";
System.out.println(s1);
System.out.println(s2);
System.out.println("****************************");
String s3 = "abc";
s3 += "def";
System.out.println(s3);
System.out.println(s2);
System.out.println("****************************");
String s4 = "abc";
String s5 = s4.replace('a', 'm');
System.out.println(s4);
System.out.println(s5);
}
}
String不同实例化方式对比
注意:字符串常量存储在字符串常量池,目的是共享字符串;非常量对象存储在堆中。
例
import org.junit.Test;
public class NewString {
/**
String的实例化方式:
方式一:通过字面量定义
方式二:通过new + 构造器
*/
@Test
public void test2() {
// 通过字面量定义的方式:此时的s1和s2的数据javaEE声明在方法区中的字符串常量池中。
String s1 = "javaEE";
String s2 = "javaEE";
//通过new +构造器的方式:此时的s3和s4保存时地址值,是数据在堆空间中开辟空间以后对应地址值
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类并且有姓名和年龄属性
Person person1 = new Person("Tom", 12);
Person person2 = new Person("Tom", 12);
System.out.println(person1.name.equals(person2.name));// true
// 注意和上面s3 == s4为false区分;s3和s4是两个不同对象,都在堆空间中。
// 而这里person1和person2两个位于堆空间的不同对象的name属性都是调用的常量池中同一个地址值
System.out.println(person1.name == person2.name);// true
}
}
面试题: string s = new String(“abc”); 方式创建对象,在内存中创建了几个对象?
两个:一个是堆空间中new结构,另一个是char[]对应的常量池中的数据: “abc”。
String不同拼接操作对比
public class NewString {
@Test
public void test3() {
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(s5 == s6);// false
System.out.println(s3 == s7);// false
System.out.println(s5 == s7);// false
System.out.println(s6 == s7);// false
String s8 = s5.intern();
System.out.println(s3 == s8);// true
}
}
注意:
- 常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。
- 只要其中有一个是变量,结果就在堆中。
- 如果拼接的结果调用intern()方法,返回值就在常量池中。
经典面试题
public class StringTest {
String str = new String( "good");
char[] ch = {'t', 'e', 's', 't'};
public void change(String str, char ch[]){
str = "test ok";
ch[0] = 'b';
}
public static void main(String[] args) {
StringTest ex = new StringTest();
ex.change(ex.str, ex.ch);
// String不可变性
System.out.println(ex.str);//good
System.out.println(ex.ch);//best
}
}
JVM设计字符串的内存结构
由于目前没有具体结合JVM,所以暂时一jdk8为例,认为字符串常量池在方法区即可。
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 startsWitn(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个,如果超过了,剩下的全部都放到最后一个元素中。 |
注意: indexOf和lastlndexOf方法如果未找到都是返回-1。
例
import org.junit.Test;
public class StringMethodsTest {
@Test
public void test2 () {
String s1 = "helloworld";
boolean b1 = s1.endsWith("ld");
boolean b2 = s1.startsWith("h");
boolean b3 = s1.startsWith("ll", 2);
boolean b4 = s1.contains("owo");
System.out.println(b1);//true
System.out.println(b2);//true
System.out.println(b3);//true
System.out.println(b4);//true
System.out.println(s1.indexOf("lo"));//3
System.out.println(s1.indexOf("lol"));//-1
System.out.println(s1.indexOf("lo", 5));//从第5个字符开始查找,结果:-1
System.out.println(s1.lastIndexOf("or"));//找字符串最后一次出现的位置,结果:7
System.out.println(s1.lastIndexOf( "or",6));//找字符串最后一次出现的位置,从前往后找,结果:4
}
@Test
public void test1 () {
String s1 = "helloworld";
System.out.println(s1.length());
System.out.println(s1.charAt(0));
System.out.println(s1.charAt(9));
//System.out.println(s1.charAt(10));
s1 = "";
System.out.println(s1.isEmpty());
s1 = "helloworld";
String s2 = s1.toUpperCase();
System.out.println(s2);//HELLOWORLD
String s3 = " h e llo world ";
String s4 = s3.trim();
System.out.println("---" + s3 + "---");//--- h e llo world ---
System.out.println("---" + s4 + "---");//---h e llo world---
String s5 = "abc";
String s6 = "abe";
System.out.println(s5.compareTo(s6));//-2(结果直接相减)
}
}
String类和其他结构的转换
import org.junit.Test;
public class StringTest1 {
/**
* String -->基本数据类型、包装类:调用包装类的静态方法: parseXxx(str)
* 基本数据类型、包装类--> String:调用String重载的value0f(xxx)或者用 + 号
*/
@Test
public void test1 () {
String s1 = "1234";
//int num = (int)s1;错误,无法强转
int num = Integer.parseInt(s1);
String s2 = String.valueOf(num);
String s3 = num + "";//有变量参与,故在堆空间存储
System.out.println(num);
System.out.println(s2);
System.out.println(s1 == s2);//false
System.out.println(s1 == s3);//false
}
}
String与char[]相互转换
import org.junit.Test;
public class StringTest1 {
/**
* String --> char[]: 调用String的toCharArray()
* char[] --> String: 调用String的构造器
*/
@Test
public void test1 () {
String s1 = "abc123";//思考:如果想转换成“a21cb3”应该怎么做?
String s4 = "abcdefghijklmn";//调换除首尾外的所有字符
char[] charArray = s1.toCharArray();
char[] charArray1 = s4.toCharArray();
for (char c : charArray) {
System.out.println(c);
}
char[] arr = new char[]{'h','e','l','l','o'};
String s2 = new String(arr);
System.out.println(s2);//hello
change(1, 4, charArray);
change(1, 12, charArray1);
String s3 = new String(charArray);
String s5 = new String(charArray1);
System.out.println(s3);
System.out.println(s5);//amlkjihgfedcbn
}
// 转换方法,这里不调用reverse方法AIPI
public void change (int start, int end, char[] charArray) {
int end1 = end;
for (int i = start; i <= (end + start) / 2; i++) {
char ch = charArray[i];
charArray[i] = charArray[end1];
charArray[end1--] = ch;
}
}
}
涉及String的一些算法
1.自行实现 trim() 方法
// 模拟trim()方法
public String trim1 (String s) {
int start = 0;
int end = s.length() - 1;
while(start<=end && s.charAt(start)==' ') {
start++;
}
while(start<=end && s.charAt(end)==' ') {
end--;
}
return s.substring(start, end + 1);
}
@Test
public void test3 () {
String s = " he llo, whorld ";
String s1 = trim1(s);
System.out.println("---" + s1 + "---");
System.out.println("---" + s + "---");
}
2.将一个字符串指定区域实现反转
// 实现反转指定位置字符串
public void reverse1 (int start, int end, char[] charArray) {
int end1 = end;
for (int i = start; i <= (end + start) / 2; i++) {
char ch = charArray[i];
charArray[i] = charArray[end1];
charArray[end1--] = ch;
}
}
@Test
public void test1 () {
String s1 = "abc123";//思考:如果想转换成“a21cb3”应该怎么做?
String s4 = "abcdefghijklmn";
char[] charArray = s1.toCharArray();
char[] charArray1 = s4.toCharArray();
change(1, 4, charArray);
change(0, 13, charArray1);
String s3 = new String(charArray);
String s5 = new String(charArray1);
System.out.println(s3);//a21cb3
System.out.println(s5);// nmlkjihgfedcba
}
3.获取一个字符串在另一个字符串中出现的次数
public int getCount(String str1,String str2){
int mainLen = str1.length();
int subLen = str2.length();
int count = 0;
//每次找到时的下标
int index = 0;
if(mainLen >= subLen){
//indexOf():找不到时默认返回-1
while((index = str1.indexOf(str2,index)) != -1){
//将str2在str1中出现的下标赋值给index,如果不等于-1,说明找到了
//计数+1
count++;
//将下次开始寻找的第一个位置赋值为这次位置+"do" 的长度
index += subLen;
}
}
//最后不管找没找到都返回count,因为count初始化值为0
return count;
}
@Test
public void test4 () {
String str1 = "abskfjdlabaljlkjbalkjlk";
String str2 = "ab";
int count = getCount(str1, str2);
System.out.println(count);//result: 2
}
字符串相关的类:StringBuffer和StringBuilder
String、StringBuffer. StringBuilder三者的异同?
- String:不可变的字符序列; 底层使用char[]存储
- StringBuffer:可变的字符序列;线程安全的,效率低; 底层使用char[]存储
- StringBuilder:可变的字符序列; jdk5. 0新增的,线程不安全的,效率高; 底层使用char[]存储
StringBuffer源码分析(重点****)
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());//
sb1.append('a');//value[0] = 'a';
sb1.append('b');//value[1] = 'b';
StringBuffer sb2 = new StringBuffer("abc");//char[] value = new char["abc". length() + 16]
//问题1. System.out.println(sb2.length());// 3
/**
*问题2.扩容问题:如果要添加的数据底层数组盛不下了,那就需要扩容底层的数组。
*默认情况下,扩容为原来容量的2倍+ 2,同时将原有数组中的元素复制到新的数组中。
*指导意义:开发中建议大家使用: StringBuffer(int capacity),即一开始使用就指定容量,避免不必要的扩容
*/
StringBuffer(StringBuilder)常用方法
StringBuffer append(xxx) | 提供了很多的append()方法,用于进行字符串拼接 |
---|---|
StringBuffer delete(int start, int end) | 删除指定位置的内容 |
StringBuffer replace(int start, int end, String str) | 把[start,end) 位置替换为str |
StringBuffer insert(int offset, xxx) | 在指定位置插入xxx |
StringBuffer reverse() | 把当前字符序列逆转 |
public int indexOf(String str) | 返回指定子字符串在此字符串中第一次出现处的索引 |
public String substring(int start, int end) | 返回一个新字符串,它是此字符串从start开始截取到end(不包含)的一个子字符串 |
public int length() | 返回字符串长度 |
public char charAt(int n ) | 取对应索引字符 |
public void setCharAt(int n , char ch) | 手动设置对应索引位置字符 |
对比String、StringBuffer、 StringBuilder三者的效率
从高到低排列: StringBuilder > StringBuffer > String
System类中获取时间戳
System.currentTimeMillis();
Java中两个Date类
/*
java.util.Date类
|---java.sql.bate类
1.两个构造器的使用
>构造器一: Date():创建一个对应当 前时间的Date对象
>构造器二:创建指定毫秒数的Date对象
2.两个方法的使用
>toString():显示当前的年、月、日、时分、秒
>getTime():获取当前Date对象对应的毫秒数。(时间载)
3. java.sql.Date对应者数据库中的日期类型的变量
>如何实例化
>如何将java.util.Date对象转换为java.sql.Date对象
*/
import org.junit.Test;
import java.util.Date;
public class JavaDateTest {
@Test
public void test() {
// 构造器一: Date(): 创建一个对应当前时间ate对家
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());
//创建java.sql.Date对象
java.sql.Date date3 = new java.sql.Date(35235325345L);
System.out.println(date3);//1971-02-11
//如何将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());
System.out.println(date7);
}
}
SimpleDateFormat
import org.junit.Test;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class JavaDateTest {
@Test
public void test() throws ParseException {
//实例化SimpleDateFormat:使用默认的构造器
SimpleDateFormat sdf = new SimpleDateFormat();
//格式化:日期--->字符串
Date date = new Date();
System.out.println(date);
String format = sdf.format(date);
System.out.println(format);
//解析:格式化的逆过程,字符串--->日期
String str = "19-12-18 上午11:43";
Date date1 = sdf.parse(str);
System.out.println(date1);
/***********按照指定的方式格式化和解析:调用带参的构造器**************/
//SimpleDateFormat sdf1 = new SimpleDateFormat( "yyyyy. MMM. dd GGG hh:mm aaa");
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
//格式化
String format1 = sdf1. format(date);
System.out.println(format1);//2019-02-18 11:48:27
//解析
Date date2 = sdf1.parse("2020-02-18 11 :48:27");
System.out.println(date2);
}
}
Java比较器
概述
Java实现对象排序的方式有两种;
自然排序:java.lang.Comparable
定制排序:java.util.Comparator
一、说明: Java 中的对象,正常情况下,只能进行比较: == 或 !=。不能使用 > 或 < 的,但是在开发场景中,我们需要对多个对象进行排序,言外之意, 就需要比较对象的大小。如何实现?使用两个接口中的任何一个: Comparable 或Comparator
Comparable接口的使用举例
- 像String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个对象的方法;
- 像String、包装类重写compareTo()方法以后,进行了从小到大的排列;
- 重写compareTo(obj)的规则:
- 如果当前对象this.大于形参对象obj,则返回正整数,
- 如果当前对象this小于形参对象obj,则返回负整数,
- 如果当前对象this等于形参对象obj,则返回零。
- 对于自定义类,如果需要排序,可以让自定义类实现Comparable接口,重写CompareTo(object)方法。即在CompareTo(object)中指明如何排序。
例
Goods类
public class Goods implements Comparable{
private String name;
private Double price;
public Goods() {
}
public Goods(String name, Double 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;
// 若价格一样,可以跟据名字继续排序,若定义从高到低排序,则如下写即可
// 注意这里由于name是String类型,所以调用的compareTo()方法是String中的
// return -this.name.compareTo(goods.name);
}
// 方式二
//return Double.compare(this.price, goods.price);
}
throw new RuntimeException("传入的数据类型不一致!");
}
}
Test类
import org.junit.Test;
import java.util.Arrays;
public class CompareTest {
@Test
public void test1 () {
Goods[] arr = new Goods[4];
arr[0] = new Goods("lenovo", 34.0);
arr[1] = new Goods("dell", 70.0);
arr[2] = new Goods("xiaomi", 12.0);
arr[3] = new Goods("huawei", 67.0);
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
}
}
使用Comparator实现定制排序
/**
* Comparator接口的使用:定制排序
*当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码,
* 或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,
* 那么可以考虑使用Comparator的对象来排序
*/
@Test
public void test2 () {
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));
}
例2
@Test
public void test1 () {
Goods[] arr = new Goods[4];
arr[0] = new Goods("lenovo", 34.0);
arr[1] = new Goods("dell", 70.0);
arr[2] = new Goods("xiaomi", 12.0);
arr[3] = new Goods("huawei", 67.0);
Arrays.sort(arr, new Comparator(){
// 指明商品比较大小的方式:按照产品名称从低到高,再根据价格从第高到低排序
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof Goods && o2 instanceof Goods){
Goods g1 = (Goods) o1;
Goods g2 = (Goods) o2;
if(g1.getName().equals(g2.getName())){
return -Double.compare(g1.getPrice(), g2.getPrice());
}else{
return g1.getName().compareTo(g2.getName());
}
}
//return 0;
throw new RuntimeException("输入的数据类型不一致");
}
});
System.out.println(Arrays.toString(arr));
}
Comparable接口与Comparator使用对比
- Comparable接口的方式一旦确定,保证Comparable 接口实现类的对象在任何位置都可以比较大小
- Comparator接口属于临时性的比较。
System类
- in
- out
- err
Math类
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可以表示不可变的任意精度的整数。
例
@Test
public void test3 () {
BigInteger bi = new BigInteger("1243324112234324324325235245346567657653");
BigDecimal bd = new BigDecimal("12435.351");
BigDecimal bd2 = new BigDecimal("11");
System.out.println(bi); // 1243324112234324324325235245346567657653
// System.out.println(bd.divide(bd2));
System.out.println(bd.divide(bd2, BigDecimal.ROUND_HALF_UP)); // 1130.486
System.out.println(bd.divide(bd2, 25, BigDecimal.ROUND_HALF_UP)); // 1130.4864545454545454545454545
}