比较器和浅谈深浅拷贝
文章目录
- 比较器和浅谈深浅拷贝
- 前言
- 一.比较器
- 方法一
- 方法二
- 二.深浅拷贝
- 2.1 浅拷贝
- 2.2 深拷贝
- 2.3 总结
前言
我们学习完接口以后,我在这里会介绍一个比较器的接口,至于他是来干嘛的,比较器顾名思义就是来比较的,其他的倒是没什么讲究的。
一.比较器
我们还是用具体的例子来解释这个东西具体的用法。我下面会给出一个student的类,main方法中我会给出student的数组,存入数据,然后我需要你对这个数组进行排序并输出。代码如下:
public class Student {
public String name;
public int age;
public int score;
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
}
public class Test
{
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("zhagnsan",10,19);
students[1] = new Student("abc",8,78);
students[2] = new Student("lisi",15,57);
Arrays.sort(students);
}
当然我们了解过数组,自然而然就想到数组的sort方法,但实际上是不可行的,因为student是一个对象,不是一个单纯的数据,因此你这样做的话,会出现一个错误。如下:
当然我们还是要想办法,java当然也给我们提供了方法去比较,有以下俩种方法。
方法一
分俩步走
1.学生类去实现Comparable接口。
2.重写接口的compareTo方法。这个方法就是写比较方法的。
public class Student implements Comparable<Student> {
public String name;
public int age;
public int score;
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
@Override
public int compareTo(Student s) {
return s.age - this.age;
}
}
public class Test
{
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("zhagnsan",10,19);
students[1] = new Student("abc",8,78);
students[2] = new Student("lisi",15,57);
Arrays.sort(students);
System.out.println(Arrays.toString(students));
}
}
结果:
当然肯定有人说,重写compareTo方法遇见了String怎么办,字符串貌似没法用相减的方法,不必担心,string类已经重写了compareto的方法,我们可以直接用,代码如下:
public int compareTo(Student o) {
if (this.name.compareTo(o.name)>0){
return 1;
}else if (this.name.compareTo(o.name)<0){
return -1;
}
return 0;
}
string类重写的方法如下:
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;
}
说了这么久compare这个接口,大家应该对这个接口熟悉,但是我要说的是这个东西我觉得不够好,因为我们每一次的比较都写死了,如果是比较年龄,我们就只能比较年龄,你如果想比较其他的东西,就必须要修改student类的方法,这基本上的是不好的,有没有解决方案呢?当然是有的,接下来的方法二就是我们要说的。
方法二
我们会用到一个Comparator的接口,具体的用法,入下:
1.生成一个类,比如说我们按照年龄比较,我们就定义一个年龄类,然后实现Comparator接口
2.重写compare的方法
代码如下:
public class Student {
public String name;
public int age;
public int score;
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
}
//比较名字的
class NameComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.name.compareTo(o2.name);
}
}
//按照分数比较的
class ScoreComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.score - o2.score;
}
}
//按照年龄比较的
class AgeComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.age - o2.age;
}
}
测试方法:
public class Test
{
Student[] students = new Student[3];
students[0] = new Student("zhagnsan",10,19);
students[1] = new Student("abc",8,78);
students[2] = new Student("lisi",15,57);
//比较器
AgeComparator ageComparator = new AgeComparator();
ScoreComparator scoreComparator = new ScoreComparator();
NameComparator nameComparator = new NameComparator();
//第二个参数就是传入比较器,然后就按照比较器的方式进行比较。
Arrays.sort(students,ageComparator);
System.out.println(Arrays.toString(students));
}
}
总结:
这种方法的好处更灵活了,我们想按照哪种方式去比较,我们直接传入相应的比较器就可以了,就不需要去类体中修改方法了,增加代码的复用性。
二.深浅拷贝
在进入这个拷贝的讲解之前呢?先介绍一个接口,这个接口就是Cloneable,为啥要说这个呢?那是因为我们要对一个对象进行拷贝的时候,我们必须去实现这个cloneable接口,才能进行拷贝,我们也可以看一下这个cloneable接口的内容
这里面是什么都没有的,简单来说,我们去实现这个接口,就是为这个类打上了标记,说明这个类是可以被拷贝的。
一般来说我们拷贝的步骤如下:
1.我们必须给类实现一个Cloneable接口
2.重写父类object的clone的方法
2.1 浅拷贝
浅拷贝对于基本数据类型就是直接进行值传递,在内存的另一个空间内存放,修改这个值不会影响到拷贝源的值
浅拷贝对于引用数据类型就是进行的是地址传递,并没有对该对象重新开辟一个内存空间进行存放,所以对于引用数据类型的浅拷贝就相当于两个引用指向了同一个内存地址,那么就显而易见了修改拷贝后的值就会影响到拷贝源的值。 简单点来说就是浅拷贝就是怎么变都会影响另一个变量的值。
我这里用一个学生类来解释这个浅拷贝的例子,附代码
//学生类
public class Student implements Cloneable {
public String name;
public Money m=new Money();
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", m=" + m +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
//money类
public class Money implements Cloneable {
public double money=12.25;
}
//测试类
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Student student1 = new Student();
Student student2 = (Student)student1.clone();
System.out.println(student1.m.money);
System.out.println(student2.m.money);
student2.m.money = 99;
System.out.println(student1.m.money);
System.out.println(student2.m.money);
}
}
运行结果:
为啥会出现这样的结果呢?我画个图大家理解一下。
为啥会出现这样的一种现象呢?原因就是我们在克隆时重写clone方法的时候,你仔细看。
我们仅仅只是克隆了整个对象,并没有对money这个类进行复制,所以money的引用自始至终都指向了同一个对象。
2.2 深拷贝
我们怎么才能改变这种现状呢?那必定要在重写clone的方法上做手脚,我的代码修改如下:
public class Student implements Cloneable {
public String name;
public Money m=new Money();
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", m=" + m +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
//下面这一行代码只是克隆了,父类的值
Student student=(Student) super.clone();
//这行代码要克隆student 里面money的对象
student.m=(Money) this.m.clone();
//克隆完成之后,我们返回student对象
return student;
// return super.clone();
}
}
public class Money implements Cloneable {
public double money=12.25;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
然后我们再去运行程序后:
具体上面的代码修改后,发生了什么事情呢,我再画个图,大家再去深刻的理解一下:
2.3 总结
1.我们进行拷贝之前,必须让类实现Cloneable
2.实现接口后,必须重写类的clone方法。
3.重写方法的方式,不同,造就的结果不同,就导致了,浅拷贝和深拷贝
这次到这里,我们就结束了,哈哈,是不是弄清楚了呢?嘻嘻。