文章目录
- 前言
- 一 比较器
- 1.1 关于两个对象的比较
- 1.2 Comparable接口:
- 1.3 Arrays.sort方法的实现
- 1.4 比较器的实现
- Comparator接口
- 二 深拷贝与浅拷贝
- 2.1 浅拷贝:
- Cloneable接口:
- clone方法:
- 实现拷贝:
- 浅拷贝:
- 2.2 深拷贝:
前言
上一篇博客并没有将接口的内容阐述完毕,这篇博客继续阐述!
一 比较器
1.1 关于两个对象的比较
在比较单个基本数据类型时,我们可以通过关系运算符进行比较。
public class Test {
public static void main(String[] args) {
int age1 = 10;
int age2 = 8;
System.out.println(age1 > age2);
}
但是当比较对象等引用数据类型时,便不能仅仅通过关系运算符进行比较了。
class Student {
String name;
int age;
public Student(int age, String name) {
this.age = age;
this.name = name;
}
}
public class Test {
public static void main(String[] args) {
Student student1 = new Student(10,"张三");
Student student2 = new Student(12,"李四");
System.out.println(student1>student2);
}
}
要进行对象间的比较,需要确定进行比较的规则是什么,比较哪一个属性。
1.2 Comparable接口:
Comparable接口中的compareTo 方法用于进行对象间属性的比较。
compareTo是一个抽象方法,我们需要重写:
//创建一个Student类,实现Comparable接口
class Student implements Comparable<Student> {
String name;
int age;
public Student(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//重写compareTo方法
@Override
public int compareTo(Student o) {
//比较年龄:
if(this.age == o.age){
return 0;
}else if(this.age>o.age){
return 1;
}else {
return -1;
}
}
//比较姓名
// return this.name.compareTo(o.name);
}
public class Test {
public static void main(String[] args) {
Student student1 = new Student(10,"张三");
Student student2 = new Student(12,"李四");
System.out.println(student1.compareTo(student2));
结果为-1 ,表明student1的年龄比student2的年龄小。
代码分析:
Student类实现了Comparable接口,
<>是泛型的标记,以后会阐述到,比较那个类,就将类名填写在<>中!
对象间的比较本质上依然是对象属性之间的比较。
1.3 Arrays.sort方法的实现
如果创建一个对象数组,使得数组中的这些对象按照某种规则进行排序
则可以使用Array.sort方法
class Student implements Comparable<Student> {
String name;
int age;
public Student(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
//比较年龄:
if(this.age == o.age){
return 0;
}else if(this.age>o.age){
return 1;
}else {
return -1;
}
}
//比较姓名
// return this.name.compareTo(o.name);
}
public class Test {
public static void main(String[] args) {
Student[] arr1 = new Student[3];
arr1[0] = new Student(10, "王五");
arr1[1] = new Student(8, "李四");
arr1[2] = new Student(9, "张三");
Student[] arr1 = {student1,student2,student3};
System.out.println("排序前:"+Arrays.toString(arr1));
Arrays.sort(arr1);
System.out.println("排序后:"+Arrays.toString(arr1));
}
}
排序后,年龄从小到大,依次递增。
如果不实现Comparable接口,会怎么样呢?
结果表明Student类型,不能转换成Comparable类型,这是怎么回事,我们调用Arrays.sort方法进行排序,与转换类型有什么关系?
这里我们需要手动实现一下Arrays.sort方法
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++){
if(comparables[j].compareTo(comparables[j+1])>0){
//如果数组前面元素的值大于数组后面元素的值,则交换引用的值,这是升序
Comparable tmp = comparables[j];
comparables[j] = comparables[j+1];
comparables[j+1] = tmp;
}
}
}
}
代码分析: 问题就在于 if(comparables[j].compareTo(comparables[j+1])>0) 这条语句
我们通过接口类型数组来接收实现了接口的数组,并且对compareTo方法进行调用!
在上个例子中,因为没有Student没有实现Comparable接口,所以会发生Student类型无法发生向上转型成Comparable接口类型的情况。
调用自己实现的mysort方法:
class Student implements Comparable<Student> {
String name;
int age;
public Student(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public int compareTo(Student o) {
//比较年龄:
if(this.age == o.age){
return 0;
}else if(this.age>o.age){
return 1;
}else {
return -1;
}
}
//比较姓名
// return this.name.compareTo(o.name);
}
public class Test {
public static void main(String[] args) {
Student[] arr1 = new Student[3];
arr1[0] = new Student(10, "王五");
arr1[1] = new Student(8, "李四");
arr1[2] = new Student(9, "张三");
System.out.println("排序前:" + Arrays.toString(arr1));
mysort(arr1);
System.out.println("排序后:" + Arrays.toString(arr1));
}
}
1.4 比较器的实现
在上面实现compareTo方法时,我们只能比较某一固定的属性,比如年龄或者名字,
这比较有局限性,总不能当需要比较某一属性时,再去修改类的实现。
解决方案:当需要比较某一属性时,就调用相关的类!
Comparator接口
如图所示:Comparator接口中有一个compare抽象方法。
我们可以创建不同的类来实现此接口,当需要比较不同的属性值时,调用不同的类:
举例:
//在单独一个java文件中
package demo1;
import java.util.Comparator;
//创建一个NameComparator类,实现Comparator接口
public class NameComparator implements Comparator<Student> {
@Override
//实现接口中的抽象方法,用于进行名字之间的比较!
public int compare(Student o1, Student o2) {
return o1.name.compareTo(o2.name);
}
}
o1.name之所以可以引用compareTo方法是因为String类中重写了compareTo方法:
//在另一个java文件中
package demo1;
import java.util.Comparator;
//实现Comparator接口
public class AgeComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.age - o2.age;
}
}
package demo1;
import java.util.Arrays;
import java.util.Comparator;
//接口的应用!
// 比较两个对象的尝试!
// 实现关于comparable接口!
class Student implements Comparable<Student> {
String name;
int age;
public Student(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public int compareTo(Student o) {
//比较年龄:
if(this.age == o.age){
return 0;
}else if(this.age>o.age){
return 1;
}else {
return -1;
}
}
//比较姓名
// return this.name.compareTo(o.name);
}
public class Test {
public static void main(String[] args) {
Student[] arr1 = new Student[3];
arr1[0] = new Student(10, "王五");
arr1[1] = new Student(8, "李四");
arr1[2] = new Student(9, "张三");
System.out.println("排序前:" + Arrays.toString(arr1));
//创建一个NameComparator对象。
NameComparator nameComparator = new NameComparator();
//进行名字间的比较!
//Arrays.sort方法可以接收第二个参数!
Arrays.sort(arr1,nameComparator);
System.out.println("排序后:" + Arrays.toString(arr1));
}
}
这是按照姓名排序的结果:
按照年龄排序,则调用AgeComparator类:
System.out.println("排序前:" + Arrays.toString(arr1));
AgeComparator ageComparator = new AgeComparator();
//根据年龄进行比较!
Arrays.sort(arr1,ageComparator);
System.out.println("排序后:"+Arrays.toString(arr1));
排序后,年龄从小到大!
我们通过创建不同的实现Comparator的类,并实现抽象方法compare,
当需要比较某一属性时,即调用某一属性对应类进行比较,这就是比较器的思想与实现!
二 深拷贝与浅拷贝
2.1 浅拷贝:
所谓拷贝即将一个对象复制一份,由另一个引用指向新复制出的对象。
Cloneable接口:
要进行拷贝的类,需要先实现Cloneable接口.
Cloneable接口是一个空接口,代表着实现此接口的类可以被拷贝!
clone方法:
clone方法是Object类中用来拷贝对象的方法。
此方法被Native修饰,说明它是由C/C++等其他编程语言实现,不能查看其具体实现。
实现拷贝:
package demo1;
public class Person implements Cloneable{ //实现空接口的类,代表可以拷贝
String name;
int age ;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package demo1;
// 浅拷贝
public class Test {
public static void main(String[] args) {
Person person1 = new Person("张三",10);
Person person2 = person1.clone();
}
}
(1)此时编译器报警告:
尽管clone方法的访问权限是protected且Test也是Object的子类,但是当person1调用clone方法时,
是在Person类的外部,所以报错。
解决这个问题,我们需要在子类中重写clone方法:
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
(2) 此时编译器又报警告:
这是异常的问题,以后会阐述到,解决这个问题,在main方法后,加上一条语句即可:
(3)编译器又报警告的原因是:
方法的返回值类型为Object类型,我们需要将其强制为Person类型。
package demo1;
// 浅拷贝
public class Test {
public static void main(String[] args) throws CloneNotSupportedException{
Person person1 = new Person("张三",10);
Person person2 = (Person) person1.clone();
System.out.println(person1);
System.out.println(person2);
}
}
此刻,内存中情况如下:
浅拷贝:
拷贝的情况讲完了,那什么是浅拷贝呢?
当对象中有对象的创建时,此时只拷贝外部的对象,而不拷贝内部的对象,称为浅拷贝!
举例:
package demo1;
//创建一个Person类,实现接口
public class Person implements Cloneable{ //实现空接口的类,代表可以拷贝
String name;
int age ;
Money money1 = new Money(10);
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//重写克隆方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
package demo1;
//创建一个Money类
public class Money {
int moneycount ;
public Money(int moneycount) {
this.moneycount = moneycount;
}
}
package demo1;
// 浅拷贝
public class Test {
public static void main(String[] args) throws CloneNotSupportedException{
Person person1 = new Person("张三",10);
Person person2 = (Person) person1.clone();
System.out.println("修改前:"+person1.money1.moneycount);
System.out.println("修改前:"+person2.money1.moneycount);
//仅仅修改person2中的money对象的值,会不会改变person1中的值?
person2.money1.moneycount = 20;
System.out.println("修改后:"+person1.money1.moneycount);
System.out.println("修改后:"+person2.money1.moneycount);
}
}
在内存中情况:
这种未将对象 中的对象 拷贝的不彻底拷贝,我们称为浅拷贝!
2.2 深拷贝:
深拷贝也就是将对象中的对象也进行拷贝,
这需要对Person类中的clone方法进行重写:
并且对Money类按照Person类中的格式进行重写编写
代码:
//Person类
package demo1;
public class Person implements Cloneable{ //实现空接口的类,代表可以拷贝
String name;
int age ;
Money money1 = new Money(10);
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
//重写后的方法
protected Object clone() throws CloneNotSupportedException {
Person tmp = (Person) super.clone(); //谁调用了super方法,不需要this指定对象。
//对于对象中的对象也进行拷贝!
tmp.money1 = (Money) this.money1.clone();
return tmp;
}
}
//Money类
package demo1;
public class Money implements Cloneable {
int moneycount ;
public Money(int moneycount) {
this.moneycount = moneycount;
}
//也重写clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
//测试类
package demo1;
// 浅拷贝
public class Test {
public static void main(String[] args) throws CloneNotSupportedException{
Person person1 = new Person("张三",10);
Person person2 = (Person) person1.clone();
System.out.println("修改前:"+person1.money1.moneycount);
System.out.println("修改前:"+person2.money1.moneycount);
person2.money1.moneycount = 20;
System.out.println("修改后:"+person1.money1.moneycount);
System.out.println("修改后:"+person2.money1.moneycount);
}
结果表明,此时深拷贝成功,修改person2中的money值,不会改变person1中的值!
在内存中的展示: