我们可以通过查看String源码看到它里面有一个compareTo
的方法,它能够帮助我们去比较引用类型的大小。
根据compareTo的源码我们可以写出比较这两个字符串大小的代码,首先我们要先理解compareTo这个方法的逻辑,我们将我们的str2作为参数传入这个方法内,然后通过str1去调用compareTo这个方法,如果str1 > str2 则返回一个大于0的int 类型的整数打印我们的第一条语句,否则返回一个小于等于0的数,打印else后面的语句。 由于我们给的str1和str2中字母A的Unicode值是65,字母M的是77,很明显A的值小于M的,因此str1.compareTo(str2)返回的结果是一个负数,所以输出else后面的语句。
根据String的源码我们可以知道java里面string执行了Comparable这个接口,并且在这个接口里面重写了compareTo这个方法,所以我们能够去比较字符串的大小,所以我们如果需要比较年龄的大小也要·跟他的·源码一样去执行这个comparAble接口,重写这个compareTo方法。我们从源码里面可知执行comparA这个接口需要将这个对象的类型写到<>里面,所以修改后的代码如下所示:
这个重写方法是系统提示给的。
引用类型的数据比较需要通过重写compareTo方法将比较方式写明白。比如说我们想通过年龄去比较,其代码可以修改如下:
以年龄为比较标准去比较
package demointerface;
class Student implements Comparable<Student>{
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
//以年龄为标准比较大小
return this.age - o.age;
}
}
public class Test {
public static void main(String[] args) {
Student student1 = new Student("张山",20);
Student student2 = new Student("李四",15);
if (student1.compareTo(student2) > 0) {
System.out.println("student1 > student2");
}else {
System.out.println("student1 <= student2");
}
}
运行结果:
根据运行结果我们可以知道我们重写的compareTo方法是正确的,
Student
类,实现了Comparable<Student>
接口,以便根据年龄比较学生对象。该类包含两个属性:name(学生姓名)
和age(学生年龄)
,并通过构造函数进行初始化。toString
方法,以便在输出学生对象时显示其姓名和年龄。在compareTo
方法中,我们基于年龄来比较两个Student
对象的大小。当调用compareTo
方法时,this.age
代表当前对象的年龄(即student1的年龄)
,而o.age
代表传入的对象的年龄(即student2的年龄)
。通过返回年龄差值来确定学生的大小关系。Test
类的main
方法中,我们创建了两个学生对象,student1
和student2
,并通过比较它们的年龄输出结果。如果student1
的年龄大于student2
,则输出“student1 > student2”
,否则输出“student1 <= student2”
。以字符串长度为比较标准去比较
package demointerface;
class Student implements Comparable<Student>{
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
// //以姓名为标准来比较大小
return this.name.compareTo(o.name);
}
}
public class Test {
public static void main(String[] args) {
Student student1 = new Student("Aileen",20);
Student student2 = new Student("Mike",15);
if (student1.compareTo(student2) > 0) {
System.out.println("student1 > student2");
}else {
System.out.println("student1 <= student2");
}
}
}
运行结果:
这里的比较原理和上面字母的比较原理是一样的,只不过是重写了compareTo方法。
package demointerface;
import java.util.Arrays;
class Student /*implements Comparable<Student>*/{
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test {
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("Aileen",20);
students[1] = new Student("Orange",22);
students[2] = new Student("Sandy",26);
Arrays.sort(students);
System.out.println(Arrays.toString(students));
}
}
根据上面的执行结果,我们可以知道如果我们直接使用sort去进行排序,会发生报错(类型转换异常),我们点击源码报错信息可以看到,sort关键字调用了Comparable接口中的compareTo方法,但是我们的自定义类Student里面并没有实现这个接口,所以才会发生报错,我们可以得出以下结论:在使用排序的时候,要排序的数据一定是可以比较的,为了实现可比较,我们需要让Student这个类去执行Comparable这个接口,所以我们的代码可以修改成如下所示:
Arrays.sort(students);
import java.util.Arrays;
class Student implements Comparable<Student>{
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
// //以姓名为标准来比较大小
return this.name.compareTo(o.name);
}
}
public class Test {
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("Aileen",20);
students[1] = new Student("Orange",22);
students[2] = new Student("Sandy",26);
Arrays.sort(students);
System.out.println(Arrays.toString(students));
}
运行结果:
package demointerface;
import java.util.Arrays;
class Student implements Comparable<Student>{
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
// //以年龄为标准比较大小
return this.age - o.age;
}
}
public class Test {
//用冒泡排序写自己的排序
//将Student的数组的接口Comparable作为参数传入
public static void mySort(Comparable[] comparables){
//比较趟数
for (int i = 0; i < comparables.length - 1; i++) {
//比较次数
//循环控制每趟比较次数 , 因为每一次比上一次少比较一趟
for (int j = 0; j < comparables.length - 1 - i ; j++) {
//因为comparables引用指向的是students数组,数组是引用类型的数据,所以我们不能用不等号去比较他们之间的大小
//而是需要利用compareTo方法进行大小的比较。
if (comparables[j].compareTo(comparables[j+1]) > 0) {
//交换
Comparable temp = comparables[j];
comparables[j] = comparables[j + 1];
comparables[j + 1] = temp;
}
}
}
}
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("Aileen",20);
students[1] = new Student("Orange",22);
students[2] = new Student("Sandy",10);
System.out.println(Arrays.toString(students));
System.out.println("--------------------------");
Arrays.sort(students);
System.out.println(Arrays.toString(students));
System.out.println("--------------------------");
mySort(students);
System.out.println(Arrays.toString(students));
}
}
此外,我们还可以根据sort的源代码知道它是调用了comparable的接口的方法,我们可以根据这个去实现自己的排序方法,上面的mysort是我们通过冒泡排序实现的自己的排序方法。
上面的代码会根据比较的条件不同其对应的代码块也会受到相应的影响,所以为了解决这问题,我们引入了:Comparator接口。
Comparator接口的应用
在构造器Comparator
中我们需要重写compare方法才能实现我们的需求,其它方法要么是static要么是default的,我们无法进行重写。以下是通过comparator实现的代码:我们想根据什么去比较就可以通过构造器直接引用哪个。
package demointerface;
import java.util.Arrays;
import java.util.Comparator;
import java.util.NavigableMap;
class Student implements Comparable<Student>{
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
// //以姓名为标准来比较大小
// return this.name.compareTo(o.name);
// //以年龄为标准比较大小
return this.age - o.age;
}
}
//1.定义年龄比较器进行年龄比较
class AgeComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return o1.age - o2.age;
}
}
//1.定义字符串比较器进行姓名比较
class NameComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return o1.name.compareTo(o2.name);
}
}
public class Test {
public static void main(String[] args) {
Student student1 = new Student("Aileen",20);
Student student2 = new Student("Mike",15);
AgeComparator ageComparator = new AgeComparator();
System.out.println(ageComparator.compare(student1, student2));
NameComparator nameComparator = new NameComparator();
System.out.println(nameComparator.compare(student1,student2));
System.out.println("============================== ");
}
通过sort方法进行排序
Student[] students = new Student[3];
students[0] = new Student("Aileen",20);
students[1] = new Student("Orange",22);
students[2] = new Student("Sandy",10);
Arrays.sort(students);
System.out.println(Arrays.toString(students));
我们在数组里面通过sort方法实现数组排序,但是这样的排序需要通过调用sort方法里面的comparable接口将数组强转成comparable类型,这会导致它会根据我们重写的comparTo方法中的比较条件(年龄)来进行排序,输出结果就会根据年龄进行从小到大排序。
我们通过查看sort的源代码(见下图)可以看到sort方法进行排序除了可以传入数组类型数据以外,还可以传入构造器类型数据,这样我们就可以通过sort方法,直接传入我们需要比较对应的构造器就可以进行相应的排序。其修改代码如下所示:
package demointerface;
import java.util.Arrays;
import java.util.Comparator;
import java.util.NavigableMap;
class Student implements Comparable<Student>{
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
// //以年龄为标准比较大小
return this.age - o.age;
}
}
//1.定义年龄比较器进行年龄比较
class AgeComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return o1.age - o2.age;
}
}
//2.定义字符串比较器进行姓名比较
class NameComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return o1.name.compareTo(o2.name);
}
}
public class Test {
public static void main(String[] args) {
Student student1 = new Student("Aileen",20);
Student student2 = new Student("Mike",15);
AgeComparator ageComparator = new AgeComparator();
// System.out.println(ageComparator.compare(student1, student2));
NameComparator nameComparator = new NameComparator();
// System.out.println(nameComparator.compare(student1,student2));
System.out.println("============================== ");
Student[] students = new Student[3];
students[0] = new Student("Aileen",20);
students[1] = new Student("Orange",22);
students[2] = new Student("Sandy",10);
Arrays.sort(students,nameComparator);
System.out.println(Arrays.toString(students));
System.out.println("**************************************** ");
Arrays.sort(students,ageComparator);
System.out.println(Arrays.toString(students));
System.out.println("**************************************** ");
}
Comparator
接口可以适应多标准的排序情况,为排序带来更大的灵活性和可扩展性,但是代码量会增多。总结:
Clonable接口
...able
:adj.可…的
我们想克隆person1这个对象,但是无法直接通过person1这个对象直接调用clone这个方法,那是因为这个Person类是我们自己创建的,但是它默认继承了Object这个类,在Object这个类里面我们能够找到clone这个方法。
所以我们可以通过调用父类的clone方法去进行访问,由于Object这个父类和我们的Person是在不同包中的,并且有protect关键字修饰,所以只能在person这个子类中通过super去调用父类的clone方法。
通过重写父类的clone方法,我们可以知道当我们调用clone方法时它的返回值是一个Object类型的,所以在我们用person1调用这个方法的时候需要将其强转成person类型,并且在main方法中也要加上抛出的异常信息,其修改后的代码如下所示:
package demointerface;
class Person{
public String name;
public Person(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Test2 {
public static void main(String[] args) throws CloneNotSupportedException{
Person person1 = new Person("Aileen");
//通过person这个引用来克隆一个对象
Person person2 = (Person) person1.clone();
}
}
运行结果:
编译器报错信息提示:不支持克隆。但如果我们给Persson加上一个执行的接口Cloneable
,他就能够完成克隆,其结果如下:
package demointerface;
class Person implements Cloneable{
public String name;
public Person(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Test2 {
public static void main(String[] args) throws CloneNotSupportedException{
Person person1 = new Person("Aileen");
//通过person这个引用来克隆一个对象
Person person2 = (Person) person1.clone();
System.out.println(person2.name);
}
}
浅拷贝
浅拷贝
:只克隆对象的其中一部分。
class Money{
public double money = 12.5;
}
class Person implements Cloneable{
public String name;
public Money q;
public Person(String name) {
this.name = name;
q = new Money();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Test2 {
public static void main(String[] args) throws CloneNotSupportedException{
Person person1 = new Person("Aileen");
//通过person这个引用来克隆一个对象
Person person2 = (Person) person1.clone();
System.out.println("修改之前: " + person1.q.money);
System.out.println("修改之前: " + person2.q.money);
person2.q.money = 1000;
System.out.println("修改之后: " + person1.q.money);
System.out.println("修改之后: " + person2.q.money);
}
}
peson2是克隆person1的对象,由于person2只克隆了person1所指向的对象,这个对象原本指向money,但person2并未克隆money,所以person2的对象也指向money;所以我们通过person2去修改money的值,打印出来person1和person2的结果是一样的,因为这两个对象指向同一个值。
深拷贝
深拷贝
:每一个对象都能有一个独立于原来对象的内容(每一个对象的对象都得克隆),是否是深拷贝需要由程序员实现,与我们的使用的方法无关。
由于我们首先要克隆的是person1的对象(它包括name和q),然而person1是Person类型的,我们在调用默认父类Object的clone方法时,由于他是Object类型所以我们要将其进行向下转型。
为了实现深拷贝,然后我们克隆了person1的对象q的对象Money,从而能够确保person1和person2拥有各自独立的Money对象。
然后我们将我们创建的temp对象指向person2,从而实现person1所有对象的独立克隆。
由于temp是局部变量,当它执行完clone函数以后,它的内存就会被回收。
现在person1和person2都能够有独立的对象,每个对象都独立于原对象以外,这就是深拷贝,他们之间互不干扰。
实现代码:
package demointerface;
class Money implements Cloneable{
public double money = 12.5;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Person implements Cloneable{
public String name;
public Money q;
public Person(String name) {
this.name = name;
q = new Money();
}
@Override
protected Object clone() throws CloneNotSupportedException {
// return super.clone();//通过super.clone()克隆出来的就是person
Person temp = (Person) super.clone();
temp.q =(Money) this.q.clone();//克隆person1的对象q并将其指向temp,因为这个函数返回值是Object类型所以需要向下转型成Money。
return temp;
}
}
public class Test2 {
public static void main(String[] args) throws CloneNotSupportedException{
Person person1 = new Person("Aileen");
Person person2 = (Person) person1.clone();
System.out.println("修改之前: " + person1.q.money);//12.5
System.out.println("修改之前: " + person2.q.money);//12.5
person2.q.money = 1000;
System.out.println("修改之后: " + person1.q.money);//12.5
System.out.println("修改之后: " + person2.q.money);//1000
}
}
运行结果:
图书
通过向上转型将两个不同的子类对象指向父类User这个类型的引用,然后通过判断语句返回选中的对象
user
一定会指向一个对象,一定会对IOPeration
进行初始化。
虽然Java已经覆盖掉了红楼梦这本书但是原来位置的Java还是跟现在的一样指向相同的内存地址,但是这块空间已经没有任何作用,却还指向对象,这会导致内存泄漏。所以我们可以通过垃圾回收器GC进行回收,可以通过Java中的set方法将其置为空。