String
String概述
- 在Java语言中,所有类似“ABC”这样用双引号引起来的字符串,都是String类的对象
- String类位于java.lang包下,是Java语言的核心类
- String类提供了字符串表示、比较、查找、截取、大小写转换等各种针对字符串的操作
构造方法(constructor): 多数情况下不用构造方法创建字符串,而是直接赋值
- 使用String尤其要注意导包问题,导错包会直接导致不能运行main方法
//创建空字符串对象,需要注意的是null != ""
public String()
//把字节数组中的元素转换成字符串,字节数组中可以是字符,也可以是ASCII码值
public String(byte[] bytes)
//同上,不过指定了开始下标和长度
public String(byte[] bytes,int offset,int length)
//同字节数组
public String(char[] value)
//同上,不过指定了开始下标和长度
public String(char[] value,int offset,int count)
//套娃
public String(String original)
String对象的最大特征
引例
键盘输入接收一个字符串s,并用一个temp字符串引用指向它
现在修改原先字符串s(拼接一个字符串),比较s和temp
public class Demo {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
String temp = str;
System.out.println(temp == str); // true
str = str + " is a random word";
System.out.println(temp == str); // false,因为会创建新的对象
}
}
-
字符串是常量,它的值在创建之后不能更改,也就是说String对象不可变
-
String对象不可变指的是对象的状态不可变,而不是引用中的地址不可变
-
原因是String中的字符串都是由字符数组装着的,而该数组是final修饰的
-
字符串常量池
Java当中所有双引号引起来的字符串都是字符串对象
- 每一个字符串字面值都作为一个对象存储在堆上的字符串常量池中
- 字面值常量编译时期,就能确定其取值,编译时期加入常量池
- 当后续再使用字面值创建相同内容的字符串对象时,直接将该对象返回给引用
- 如果使用new关键字创建相同内容字符串对象,对象不共享,**但是字符数组仍然共享 **
字符串是JVM堆内存中最多的对象,字符串不可变后,就可以共享,节省了大量的堆内存空间
不可变后还变得更安全(多线程,网络传输中可以体现)
不可变后效率提升(字符串不可变不需要频繁计算hashCode()值,所以String是Map中最常见的key)
小试牛刀
-
看程序说结果
-
String s = "hello"; s += "world"; System.out.println(s); //hello world
-
-
下面两种赋值方式有什么区别?
-
String s1 = "我是猪"; //直接创建了常量池,然后常量池指向一个final修饰的char类型数组 String s = new String("我是猪");//创建String对象,然后也指向和上面同一个数组
-
-
看程序说结果
-
String s1 = new String("hello"); String s2 = new String("hello"); System.out.println(s1 == s2); //false System.out.println(s1.equals(s2));//true String s3 = new String("hello"); String s4 = "hello"; System.out.println(s3 == s4); //false System.out.println(s3.equals(s4));//true String s5 = "hello"; String s6 = "hello"; System.out.println(s5 == s6); // true,因为都在常量池中 System.out.println(s5.equals(s6)); // true
-
-
看程序说结果
-
String s1 = "hello"; String s2 = "world"; String s3 = "helloworld"; System.out.println(s3==(s1+s2));//false,s1和s2是两个变量拼接,拼接的结果在堆上,而s3在常量池中,两个都是常量拼接结果才会在常量池中 //s1和s2编译时还不能确定它们中的值是什么,但如果你拼接“hello”和“world”,编译时就能确定取值 System.out.println(s3.equals(s1+s2)); // true System.out.println(s3==("hello"+"world")); //true System.out.println(s3.equals("hello"+"world"));
-
使用加号对字符串进行拼接操作,会有下述两种结果
- 直接在常量池中创建新的拼接对象
- 在堆上创建新的拼接对象
经过测试我们发现
- 当参与字符串拼接的两个字符串中,至少有一个是以引用变量的形式出现时
- 必然会在堆上创建新的字符串对象
- 原因是变量参与了运算,无法在编译时期确定其值,就不能在编译时期加入常量池
- 只有参与字符串拼接运算的两个字符串,都是字符串字面值常量的时候
- 此时不会在堆上创建新的字符串对象,而是在常量池中直接拼接创建对象
- 如果已存在,则不创建新的对象
Clone()的例子中,String也是引用类型,但是其对象不需要深度克隆,因为只要对象改变了,就会创建新的对象,所以不需要我们手动创建新的对象
String API
String是开发中最常见的需要操作的对象,没有之一
String天天见,所以我们要对String的操作至少有个脸熟,大概知道String类能做什么
以下方法不要求都背住,但是应该能够了解有这些方法,并可以在实际开发中随着使用而熟记
String类的判断功能
//用来比较字符串的内容,注意区分大小写
boolean equals(Object obj)
//忽略字符串大小写比较字符串内容,常见用于比较网址URL
boolean equalsIgnoreCase(String str)
//判断当前字符串对象是否包含,目标字符串的字符序列,常见用于确定是否有盗链行为
boolean contains(String str)
//判断当前字符串对象,是否已目标字符串的字符序列开头
boolean startsWith(String str)
//判断当前字符串,是否以目标字符串对象的字符序列结尾,常用于确定文件后缀名格式
boolean endsWith(String str)
//判断一个字符串,是不是空字符串
boolean isEmpty()
String类的获取功能
// 获取当前字符串对象中,包含的字符个数
int length()
//获取字符串对象代表字符序列中,指定位置的字符
char charAt(int index)
//在当前字符串对象中查找指定的字符,如果找到就返回字符,首次出现的位置,如果没找到返回-1
//也可以填字符
int indexOf(int ch)
//指定从当前字符串对象的指定位置开始,查找首次出现的指定字符的位置,(如果没找到返回-1)
//可以填入字符
int indexOf(int ch,int fromIndex)
//查找当前字符串中,目标字符串首次出现的位置(如果包含),找不到,返回-1
//这里的位置是指目标字符串的第一个字符,在当前字符串对象中的位置
int indexOf(String str)
//指定,从当前字符串对象的指定位置开始,查找首次出现的指定字符串的位置(如果没找到返回-1)
//这里的位置是指目标字符串的第一个字符,在当前字符串对象中的位置
int indexOf(String str,int fromIndex) ,
//返回字符串,该字符串只包含当前字符串中,从指定位置开始(包含指定位置字符)到结束的那部分字符串
String substring(int start)
//返回字符串,只包含当前字符串中,从start位置开始(包含),到end(不包含)指定的位置的字符串
String substring(int start,int end)
String类的转换功能
//获取一个用来表示字符串对象字符序列的,字节数组
byte[] getBytes()
//获取的是用来表示字符串对象字符序列的,字符数组
char[] toCharArray()
//使用指定字符集,将字符编码成字节序列,并将结果存储到一个新的 byte 数组中
getBytes(String charsetName)
//将字符从此字符串复制到目标字符数组
getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
//把字符数组转换成字符串
static String valueOf(char[] chs)
//把各种基本数据类型和对象转换成字符串
static String valueOf(int i/double...)
//把字符串全部转化为小写
String toLowerCase()
//把字符串全部转换为大写
String toUpperCase()
//字符串拼接,作用等价于 + 实现的字符串拼接
String concat(String str)
String类的其他功能
-
替换功能
-
// 在新的字符串中,用新(new)字符,替换旧(old)字符 String replace(char old,char new) //在新的字符串中,用新的字符串(new), 替换旧(old)字符串 String replace(String old,String new)
-
需要注意的是,替换不是在原对象上替换,而是创建了新的对象
-
-
去除空格字符
-
//在新的字符串中,去掉开头和结尾的空格字符 String trim()
-
-
比较功能
-
String类的比较功能 int compareTo(String str) int compareToIgnoreCase(String str)
-
详解字符串如何能够比较大小?怎么比较呢?
-
首先要明确字符串是由字符组成的,比较字符串的大小,首先要知道字符的大小如何比较
- 单个字符如何比较大小呢?
- 单个字符按照字典顺序去比较,实际上比较基于Unicode的编码值
- 处在字典后面的字符编码值要大,就认为这个字符大
参考一下源码
public int compareTo(String anotherString) { int len1 = value.length; int len2 = anotherString.value.length; int lim = Math.min(len1, len2); char v1[] = value; char v2[] = anotherString.value; int k = 0; while (k < lim) { char c1 = v1[k]; char c2 = v2[k]; if (c1 != c2) { return c1 - c2; } k++; } return len1 - len2; }
- 单个字符如何比较大小呢?
-
-
String类的compareTo()方法在比较时,通过查看源码,不难得出它是这么比较的
- 从左往右逐个比较对应字符,如果出现某单个字符不相同,那么返回它们的编码值之差
- 例如"bcd"和"abc"比较,直接在第一位b就能够确定,b的编码值比a大1
- 所以该方法返回1,大于0的数
- 认为"bcd"比"abc"大
- 如果逐个字符都相同,则返回它们的长度之差
- 例如"abc"和"abcd"比较,abc都是相同的,得不出结果
- 由于"abcd"比"abc"长1位
- 所以该方法返回-1,小于0的数
- 认为"abc"比“abcd”小
- 只有完全相同的字符串,才会返回0,认为它们相等
- 比如"abc"和"abc"方法就会返回0
- 认为它两相等
- 从左往右逐个比较对应字符,如果出现某单个字符不相同,那么返回它们的编码值之差
Comparable接口
关于comparable接口和comparator如何实现的,可以暂时理解为sort方法需要一个规则来进行排序,而你自己写的实现了comparable接口或者comparator的类,继承的方法就是为了重写这个排序规则的
一个类实现了Comparable接口,就可以对这个类的对象(容器或集合)进行排序
称之为自然排序,其中的compareTo方法被称为它的自然比较方法
-
实现此接口的类,其对象数组(array)或对象容器(collection)
- 就可以通过**Arrays.sort()或Collections.sort()**进行自动排序
-
对于实现该接口的A类来说,其对象a1.compareTo(a2)方法返回值
- 小于0,表示a1对象小于a2,在自然排序中处于前面的位置
- 大于0,表示a1对象大于a2,在自然排序中处于后面的位置
- 等于0,表示a1对象等于a2
-
除了compareTo方法,类中还有equals方法判断两个对象是否相等
- 建议这两个方法同true同0,同false非0,这样从逻辑上更顺畅
- 比如一个有序集合,compareTo认为不相等,equals方法认为相等,可能导致集合添加元素失败
- 实际上,所有实现 Comparable 的 Java 核心类都具有与 equals 一致的自然排序
- 两个方法都用成员变量的取值重写即可
几个代码示例
String s = "abc"; String s11 = "bcd"; String s22 = "abccc"; String s33 = "abddd"; String s44 = "abc"; String s55 = "ddd"; //以上对象从大到小排序: s5,s1,s3,s2,s,s4 String[] arr0 = {s, s11, s22, s33, s44, s55}; System.out.println("排序之前: " + Arrays.toString(arr0)); Arrays.sort(arr0); System.out.println("排序之后: " + Arrays.toString(arr0)); /* 排序之前: [abc, bcd, abccc, abddd, abc, ddd] 排序之后: [abc, abc, abccc, abddd, bcd, ddd] */
- 建议这两个方法同true同0,同false非0,这样从逻辑上更顺畅
Comparator接口
一个类实现了Comparable接口,往往需要同时重写equals方法,这会增加一些思考量
并且也不是什么时候都能够去随心所欲的修改源码,或者实现一个接口
假如仅仅只是做一次比较,那么用匿名内部类或者lambda表达式
去使用带Comparator比较器的sort方法(Arrays和Conlections中都有该方法)会比较好
- Comparator接口中的compare方法表示一种排序规则,方法会返回一个int值
- 该sort方法逐个比较容器中的每两个对象
- 方法返回负数表示排序中,处在前面的位置
- 方法返回正数表示排序中,处在后面的位置
- 最终的效果和Comparable接口一样,但是这种方式需要直接改实体类代码,更灵活
- 该sort方法逐个比较容器中的每两个对象
- Comparator接口中看起来有两个抽象方法compare和equals
- 但实际上只需要实现compare就可以了,因为任何类都有默认的equals实现
- 它仍然是一个功能接口,可以使用lambda表达式
直接创建一个工具类,然后把工具类对象传入sort中
public class Demo2 {
public static void main(String[] args) {
Cat c = new Cat(1, 500, "小白", 2000);
Cat c1 = new Cat(3, 3000, "小黄yyds", 3000);
Cat c2 = new Cat(4, 7500, "小黑hxd", 2000);
Cat c3 = new Cat(5, 1000, "小红dd", 3000);
Cat c4 = new Cat(3, 4000, "小绿", 5988);
Cat c5 = new Cat(2, 5000, "大黄mm", 2345);
Cat c6 = new Cat(6, 2000, "大白", 3245);
Cat c7 = new Cat(6, 2000, "大白", 3245);
Cat c8 = new Cat(17, 1000, "大绿绿了", 3000);
Cat c9 = new Cat(17, 300, "Tom的舔狗", 6547);
Cat c10 = new Cat(17, 800, "大绿绿了", 2000);
Cat c11 = new Cat(17, 300, "Tom的舔狗的舔狗", 1000);
Cat c12 = new Cat(17, 300, "大绿绿了", 6547);
Cat c13 = new Cat(17, 300, "Tom的舔狗的舔狗", 6547);
Cat c14 = new Cat(17, 300, "Tom的舔狗的舔狗的舔狗", 6547);
Cat[] arr = {c, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14};
//普通的做法,朴素的做法,直接手写实现子类
System.out.println("排序前:" +Arrays.toString(arr));
//该方法不能传入null
Arrays.sort(arr, new Ruler());
System.out.println("排序后:" +Arrays.toString(arr));
}
}
class Cat {
int age;
double price;
String name;
double weight;
public Cat() {
}
public Cat(int age, double price, String name, double weight) {
this.age = age;
this.price = price;
this.name = name;
this.weight = weight;
}
@Override
public String toString() {
return "Cat{" +
"age=" + age +
", price=" + price +
", name='" + name + '\'' +
", weight=" + weight +
'}' + '\n';
}
}
//朴素的方法获取接口的实现类对象
class Ruler implements Comparator<Cat> {
@Override
public int compare(Cat o1, Cat o2) {
//重写的规则是
//按照价格的从高到低排序
return ((int) ((o2.price - o1.price) * (100)));
}
//@Override
//public int compare(Object o1, Object o2) {
// //重写的规则是什么?
// //按照价格的从高到低排序
// return 0;
//}
}
匿名对象法
//高级一点的做法: 使用匿名内部类
System.out.println("排序前:" + Arrays.toString(arr));
Arrays.sort(arr, new Comparator<Cat>() {
@Override
public int compare(Cat o1, Cat o2) {
//排序规则: 按年龄的从大到小排序
return o2.age - o1.age;
}
});
System.out.println("排序后:" + Arrays.toString(arr));
lambda表达式
//高端的写法: lambda表达式
//System.out.println("排序前:" + Arrays.toString(arr));
/*Arrays.sort(arr, (cat1, cat2) -> {
//排序的规则: 重量大的在前面
return ((int) ((cat2.weight - cat1.weight) * 100));
});*/
//lambada表达式简化一下
//Arrays.sort(arr, (cat1, cat2) -> ((int) ((cat2.weight - cat1.weight) * 100)));
//System.out.println("排序后:" + Arrays.toString(arr));
System.out.println("排序前:" + Arrays.toString(arr));
Arrays.sort(arr, Demo2::myCompare);
System.out.println("排序后:" + Arrays.toString(arr));
public static int myCompare(Cat c1, Cat c2) {
//具体的比较规则
//首先按照猫的年龄排序,年龄大的在前面
//如果年龄是一样的,价格低的在前面
//如果价格也一样的,谁重谁就在前面
//如果上面的都一样的,谁的名字长谁就在前面
if (c1.age != c2.age) {
//年龄大的在前面
return c2.age - c1.age;
}
if (c1.price != c2.price) {
//价格低的在前面
return ((int) ((c1.price - c2.price) * 100));
}
if (c1.weight != c2.weight) {
//重量大的在前面
return ((int) ((c2.weight - c1.weight) * 100));
}
return c2.name.length() - c1.name.length();
}
可变字符串
String对象是不可变的字符串对象,如果用String直接 “ + ” 拼接频繁创建对象,会有效率问题
可以用以下代码进行测试
String s = "";
for (int i = 0; i < 100000; i++) {
s += "str";
}
System.out.println(s);
// 出结果很慢,要很久才能出来
不难得出结论
- 进行字符串拼接时,如果是简单几次拼接,直接用String无伤大雅,并不会有多大的效率问题
- 但是如果频繁多次修改某个字符串仍然使用String,不仅效率堪忧而且过于浪费空间
- 为了解决这一问题,Java提供了可变的字符串来解决这个问题
当需要对字符串频繁修改时,需要使用StringBuffer和 StringBuilder 类。
- 和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改
- 并且不产生新的未使用对象,不会产生效率问题和空间浪费问题
- StringBuffer是线程安全的,StringBuilder是线程不安全的
- StringBuilder的效率会比StringBuffer效率更高,单线程的程序推荐使用StringBuilder
- 在多线程的程序中,应该优先考虑使用StringBuffer,安全性要更重要
- 它们的效率都比String高很多
- 例子:
public class Test{
public static void main(String args[]){
StringBuffer sb = new StringBuffer("王道在线论坛官网:");
sb.append("www");
sb.append(".cskaoyan");
sb.append(".com");
System.out.println(sb);
}
}
以上实例编译运行结果如下:
王道在线论坛官网:www.cskaoyan.com
StringBuffer
首先是学习构造方法
// public StringBuffer() 无参构造方法
// public StringBuffer(int capacity) 指定容量的字符串缓冲区对象
// public StringBuffer(String str) 指定字符串内容的字符串缓冲区对象
总结StringBuffer可变长字符串的原理
- StringBuilder 或 StringBuffer的初始化分配的空间大小取决于调用的构造方法:
- 无参构造默认大小为 16
- 调用int单参数构造方法,初始化大小为指定的int值
- 调用 String 类型的构造方法,初始化大小为:字符串的长度 + 16,比如初始化字符是hello,那长度为21,count=5;
- 扩容机制每次扩容大小为:原数组大小 * 2 + 2
- 源码中的count代表已经存在的字符串的长度,即当前缓冲区已存在字符串的长度
StringBuffer常见方法
- 获取功能
// public int capacity() 返回当前容量,数组的长度,理论值
// public int length() 返回长度(字符的个数),实际值
- 添加功能
// public StringBuffer append(String s) 将指定的字符串(其他类型有重载方法)追加到此字符序列的尾部
//在指定位置把任意类型的数据插入到字符串缓冲区里面
// public StringBuffer insert(int offset,String str)
- 删除功能
// public StringBuffer deleteCharAt(int index):删除指定位置的字符
// public StringBuffer delete(int start,int end):删除从指定位置开始指定位置结束的内容
- 替换功能
// 使用给定String中的字符替换词序列的子字符串中的字符
// public StringBuffer replace(int start,int end,String str)
- 反转功能
// public StringBuffer reverse():将此字符序列用其反转形式取代,返回对象本身
- 截取功能
// public String substring(int start):返回一个新的String,开头到结束
// public String substring(int start,int end):返回一个新的String,指定区间
StringBuffer对象用char类型value数组存放字符串,但是value数组没有用final修饰
Java日期类
java.util包下提供了 Date 类来封装当前的日期和时间,这个类中绝大多数方法已经过时了
但是作为Java日期类的基本类,仍然有学习的必要
-
Date类表示一个特定的瞬间时间,精确到毫秒
-
构造方法:目前仍没有过时的只有两个
-
//该构造函数使用当前日期和时间来初始化对象 Date()
-
//第二个构造函数接收一个参数,该参数是从1970年1月1日起的毫秒数 Date(long millisec)
-
无参构造方法调用底层的native方法System.currentTimeMillis()方法
- 该方法会根据操作系统来获取当前的时间戳
-
时间戳:从格林威治时间(GMT)1970 年 1 月 1 日 00:00:00 到现在的毫秒数
public class Demo { public static void main(String[] args) { //1,Date类的无参构造 Date d = new Date(); //Thu Jun 24 16:34:27 CST 2021 中国标准时间 = 北京时间 System.out.println(d); //2,long构造方法 Date d2 = new Date(1218196800000L); System.out.println(d2); 用自1970年1月1日00:00:00 GMT以后time毫秒数设置时间和日期。 //void setTime(long time) 返回自1970年1月1日00:00:00 GMT以来此Date对象表示的毫秒数。 //long getTime( ) long time = d.getTime(); System.out.println(time); d2.setTime(1249732800000L); System.out.println(d2); //创建一个Date对象,然后传入参数0 Date d3 = new Date(0L); System.out.println(d3); }}
-
-
成员方法:了解两个即可
-
//用自1970年1月1日00:00:00 GMT以后time毫秒数设置时间和日期。 void setTime(long time)
-
//返回自1970年1月1日00:00:00 GMT以来此Date对象表示的毫秒数。 long getTime( )
-
-
中国处在东八区,格林威治时间1970 年 1 月 1 日 00:00:00是中国的1970 年 1 月 1 日 08:00:00
显然这种日期格式和我们生活中的日期格式,相去甚远
为了以更贴近生活中的方式去描述时间,实现直接把一个字符串转换成Date的需求
因此考虑使用别的日期类实现需求
DateFormat是日期和时间格式化子类的抽象类,它能够以自定义的格式解析或者格式化日期和时间
由于其是一个抽象类,所以使用其子类SimpleDateFormat实现日期和字符串的相互转换。
-
SimpleDateFormat的构造方法
-
//以传入的字符串格式进行解析或者格式化日期 public SimpleDateFormat(String pattern)
-
pattern的书写格式
- y:表示年,例如yyyy,表示千年年份
- M:表示月份,例如MM,表示月份(最多12,两位数)
- d:表示月份中的天数,例如dd,表示天数(最多31,两位数)
- H:表示一天中的小时数,例如HH,表示小时数(最多24,两位数)
- m:表示小时中的分钟数,例如mm,表示分钟数(最大59,两位数)
- s:表示分钟里的秒数,例如ss,表示秒数(最大59,两位数)
-
该对象仅仅只是描述日期的格式,并不代表时间
-
想要表示时间,仍然要使用Date对象
-
-
SimpleDateFormat的成员方法
-
//将传入的Date对象按照pattern格式,格式化成一个字符串 public final String format(Date date)
-
//将传入的字符串按照pattern格式,解析成一个Date对象 public Date parse(String source)
-
parse方法会抛出一个编译时异常ParseException,需要显式处理
- 当传入的字符串和解析的格式不同,就会抛出该异常
-
format方法不会抛出编译时异常,不需要显式处理
- 当pattern格式不正确,会抛出异常,非法参数异常
-
public class DatePattern {
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date();
String time = sdf.format(date);
System.out.println(time);
date.setTime(1624599926691L);
System.out.println(sdf.format(date));
String time1 = "2021-08-23 23:45:23";
Date d = sdf.parse(time1);
System.out.println(d);
}
}
作业
编写一个Cat类
成员变量:int age,String name,double price
按以下要求进行自然排序:
1,直接实现Comparable接口,按照年龄的从小到大对Cat数组进行自然排序
2,利用Comparator接口,用 匿名内部类和lambda分别对Cat数组进行自然排序
1,按照price的大到小排序
2,name的长短排序,name越长对象越大
3,综合age,name,price进行排序,要求用lambda表达式指向一个方法。
排序的规则是age越小对象越小,age相同比较name长短,name越短对象越小,name长度也相等,比较price,price越大对象越小
// 1,直接实现Comparable接口,按照年龄的从小到大对Cat数组进行自然排序
public class Demo01 {
public static void main(String[] args) {
Cat c1 = new Cat(10, "大白", 2932.7);
Cat c2 = new Cat(3, "二花", 6000);
Cat c3 = new Cat(5, "Tom", 4000);
Cat c4 = new Cat(18, "Jerry", 200);
Cat c5 = new Cat(100, "66", 8000);
Cat c6 = new Cat(1, "77", 10);
Cat[] cats = new Cat[]{c1, c2, c3, c4, c5, c6};
System.out.println("未排序前:" + Arrays.toString(cats));
Arrays.sort(cats);
System.out.println("按照年龄从小到大排序:" + Arrays.toString(cats));
}
}
class Cat implements Comparable{
int age;
String name;
double price;
@Override
public int compareTo(Object o) {
Cat cat = (Cat) o;
return this.age - cat.age;
}
public Cat(int age, String name, double price) {
this.age = age;
this.name = name;
this.price = price;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Cat cat = (Cat) o;
return age == cat.age &&
Double.compare(cat.price, price) == 0 &&
Objects.equals(name, cat.name);
}
@Override
public String toString() {
return this.age + "";
}
}
// 2,利用Comparator接口,用 匿名内部类和lambda分别对Cat数组进行自然排序
//. 1,按照price的大到小排序(匿名内部类)
public class Demo01_version2 {
public static void main(String[] args) {
Cat1 c1 = new Cat1(10, "大白", 2932.7);
Cat1 c2 = new Cat1(3, "二花", 6000);
Cat1 c3 = new Cat1(5, "Tom", 4000);
Cat1 c4 = new Cat1(18, "Jerry", 200);
Cat1 c5 = new Cat1(100, "66", 8000);
Cat1 c6 = new Cat1(1, "77", 10);
Cat1[] cats = new Cat1[]{c1, c2, c3, c4, c5, c6};
System.out.println("原Cat数组:" + Arrays.toString(cats));
Arrays.sort(cats, new Comparator<Cat1>() {
@Override
public int compare(Cat1 o1, Cat1 o2) {
return (int)(o2.price - o1.price)*10;
}
});
class Cat1{
int age;
String name;
double price;
public Cat1(int age, String name, double price) {
this.age = age;
this.name = name;
this.price = price;
}
public Cat1() {
}
@Override
public String toString() {
return this.price + "";
}
}
// name的长短排序,name越长对象越大
public class Demo01_version2 {
public static void main(String[] args) {
Cat1 c1 = new Cat1(10, "Jess", 2932.7);
Cat1 c2 = new Cat1(3, "lalalala", 6000);
Cat1 c3 = new Cat1(5, "Tom", 4000);
Cat1 c4 = new Cat1(18, "Jerry", 200);
Cat1 c5 = new Cat1(100, "Brand", 8000);
Cat1 c6 = new Cat1(1, "Maylinda", 10);
Cat1[] cats = new Cat1[]{c1, c2, c3, c4, c5, c6};
System.out.println("原Cat数组:" + Arrays.toString(cats));
Arrays.sort(cats, (cat1, cat2)-> cat1.name.length() - cat2.name.length());
System.out.println(Arrays.toString(cats));
}
}
class Cat1{
int age;
String name;
double price;
public Cat1(int age, String name, double price) {
this.age = age;
this.name = name;
this.price = price;
}
public Cat1() {
}
@Override
public String toString() {
return this.name;
}
}
/**
3,综合age,name,price进行排序,要求用lambda表达式指向一个方法。
排序的规则是age越小对象越小,age相同比较name长短,name越短对象越小,name长度也相等,比较price,price越大对象越小
*/
public class Demo01_version2 {
public static void main(String[] args) {
Cat1 c1 = new Cat1(10, "Jess", 2932.7);
Cat1 c2 = new Cat1(3, "lalalala", 8000);
Cat1 c3 = new Cat1(5, "Tom", 4000);
Cat1 c4 = new Cat1(18, "Jerry", 200);
Cat1 c5 = new Cat1(3, "Brand", 8000);
Cat1 c6 = new Cat1(1, "Maylinda", 10);
Cat1[] cats = new Cat1[]{c1, c2, c3, c4, c5, c6};
System.out.println("原Cat数组:" + Arrays.toString(cats));
Arrays.sort(cats, Demo01_version2::compare);
System.out.println(Arrays.toString(cats));
}
public static int compare(Cat1 c1, Cat1 c2){
if(c1.age != c2.age) return c1.age - c2.age;
else{
if (c2.price != c1.price) return (int)(c2.price - c1.price)*10;
else return c1.name.length() - c2.name.length();
}
}
}
class Cat1{
int age;
String name;
double price;
public Cat1(int age, String name, double price) {
this.age = age;
this.name = name;
this.price = price;
}
public Cat1() {
}
@Override
public String toString() {
return "Cat{ " + age +
", '" + name + '\'' +
", " + price +
'}';
}
}
统计大串中小串出现的次数
举例:
在字符串” woaijavawozhenaijavawozhendeaijavawozhendehenaijavaxinbuxinwoaijavagun”中
java出现了5次
public class Demo02 {
public static void main(String[] args) {
String str = "woaijavawozhenaijavawozhendeaijavawozhendehenaijavaxinbuxinwoaijavagun";
// char[] chars = str.toCharArray();
String substr = "java";
System.out.println(getCount(str,substr));
}
public static int getCount(String str, String subStr){
int count = 0;
int index = 0;
// 这个indexof的方法,是从index位置开始找substr
while((index = str.indexOf(subStr, index)) != -1){
count++;
index += 4;
}
return count;
}
}
制作一个工具类,算一下你来到这个世界多少天?
package homework;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo03 {
public static void main(String[] args) throws ParseException {
String birth = "1997/02/21";
System.out.println("您已经活了" + CalculateDays.days(birth) + "天");
}
}
class CalculateDays{
public CalculateDays(){
}
public static int days(String birth) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
Date birthDate = sdf.parse(birth);
Date todayDate = new Date();
long birthday = birthDate.getTime();
long today = todayDate.getTime();
return (int)((today - birthday)/(1000*60*60*24));
}
}