🐵本篇文章继续对接口相关知识进行讲解
一、排序
1.1 给一个对象数组排序:
class Student {
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return "name:"+name+" age:"+age;
}
}
public class Test {
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("Sans", 18);
students[1] = new Student("Frisk", 8);
students[2] = new Student("Chara", 9);
Arrays.sort(students); //给数组排序
System.out.println(Arrays.toString(students)); //将排序后数组排序后转化为字符串打印
//Arrays类中有一个toString静态方法,但这个方法并不是重写的Object类的方法,
//在这个方法中,间接调用了Object中的toString方法
}
}
可以发现,这样写编译会报错,原因就是Student类中有name和age两种属性,但在进行排序时编译器并不知道按什么排序,错误信息显示如下:
在ComparableTimSort.java文件的320行代码中有这样一条语句:
311 private static int countRunAndMakeAscending(Object[] a, int lo, int hi) {
314 assert lo < hi;
315 int runHi = lo + 1;
316 if (runHi == hi)
317 return 1;
318
319 // Find end of run, and reverse range if descending
320 if (((Comparable) a[runHi++]).compareTo(a[lo]) < 0) { // Descending
... ...
在311行代码的形参有一个Object[]a,这个参数其实接收的就是我们要排序的数组(这个方法是由Arrays.sort间接调用的)在320行代码可以看到数组被强制类型转换为了Comparable这个接口,我们的数组是Student类,Student类并没有实现Comparable这个接口,所以不能进行强转,那么现在就要实现这个接口,实现一个接口就必须重写其包含的抽象方法,在Comparable接口中有下面方法:
public interface Comparable<T> {
public int compareTo(T o);
}
也就是要在Student类中对compareTo方法根据我们的需求进行重写,如果根据学生的姓名进行比较:
class Student implements Comparable<Student>{
.......
public int compareTo(Student student) {
return this.name.compareTo(student.name);
}
<>中用来写要比较的对象所属的类,在compareTo方法中也调用了compareTo方法,这里是调用的String类中的方法,因为this.name是String类型,在String类中也重写了compareTo方法,它的作用就是用来比较字符串
现在完整代码显示如下:
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;
}
public String toString() {
return "name:"+name+" age:"+age;
}
public int compareTo(Student student) {
return this.name.compareTo(student.name);
}
}
public class Test {
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("Sans", 18);
students[1] = new Student("Frisk", 8);
students[2] = new Student("Chara", 9);
Arrays.sort(students); //给数组排序
System.out.println(Arrays.toString(students)); //将排序后数组排序后转化为字符串打印
//Arrays类中有一个toString静态方法,但这个方法并不是重写的Object类的方法,
//在这个方法中,间接调用了Object中的toString方法
}
}
简单来说compareTo就是我们给编译器提供的比较方法,使其按照这个进行排序
也可以自己模拟一个排序方法(冒泡排序思想):
public static void mySort(Comparable[] comparables) { //任何实现Comparable接口的类都可以通过这个方法排序,类似于向上转型
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) { //这一条语句发生了动态绑定,调用的是Student类中的compareTo方法
//交换
Comparable tmp = comparables[j];
comparables[j] = comparables[j+1];
comparables[j+1] = tmp;
}
}
}
}
1.2 Comparator接口
Comparator接口中的compare方法也可以用来比较两个对象
public interface Comparator<T> {
int compare(T o1, T o2);
}
举一个例子
import java.util.Comparator;
class Student {
public String name;
public Student(String name) {
this.name = name;
}
}
class NameComparator implements Comparator<Student> {
public int compare(Student student1, Student student2) {
return student1.name.compareTo(student2.name);
//前者>后者:返回大于0的数
//前者<后者:返回小于0的数
//前者=后者:返回0
}
}
public class Test {
public static void main(String[] args) {
Student student = new Student("Sans");
Student student1 = new Student("Frisk");
NameComparator nameComparator = new NameComparator();
System.out.println(nameComparator.compare(student, student1)); //13
}
}
二、Cloneable接口
clone()方法是Object类中的一个成员方法,下面举一个克隆对象的实例:
class Person implements Cloneable{
public String name;
public Person(String name) {
this.name = name;
}
public Object clonePerson() throws CloneNotSupportedException {
return super.clone();
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Person person1 = new Person("Sans");
Person person2 = (Person) person1.clonePerson()
System.out.println(person2.name);
}
}
1. Person类实现Cloneable接口才能对这个类的对象进行克隆
2. person1调用clone()方法,并强转为Person赋给perosn2
3. 由于Object类是父类,所以应该用super来调用clone()方法
4. main方法为静态方法,所以必须在Person类中再写一个方法来使用super来调用clone()方法
5. throws CloneNotSupportedException暂且不管,先模仿
2.1 浅拷贝
浅拷贝是指创建一个新对象,并将原始对象的值拷贝到新对象中,但是这里原始对象中引用类型的成员的值也拷贝到了新对象中,这也就说明,原始对象和新对象中的原始引用和新引用都指向了同一个对象
class Money {
public double money = 12.5;
}
class Person implements Cloneable{ //必须实现Cloneable这个接口才能克隆这个Person类
public String name;
public Money m;
public Person(String name) {
this.name = name;
}
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException{
Person person1 = new Person("Sans");
Person person2 = (Person) person1.clone(); //将person1所指向的对象拷贝一份给person2
System.out.println("修改前:"+ person1.m.money); //12.5
System.out.println("修改前:"+ person2.m.money); //12.5
person2.m.money = 99.9;
System.out.println("修改后:"+ person1.m.money); //99.9
System.out.println("修改后:"+ person2.m.money); //99.9
}
}
上述代码执行完perosn2.m.money = 99.9后person1.m.money和person2.m.money都变成了99.9
2.2 深拷贝
深拷贝就是在浅拷贝的基础上,新对象的引用字段所指向的对象也是原始对象的被拷贝引用字段所指向对象的拷贝,可以简单理解为将一个对象完全拷贝了一份
class Money implements Cloneable{
public double money = 12.5;
public Object cloneMoney() throws CloneNotSupportedException {
return super.clone(); //由于要拷贝一份m所指对象,所以Money类也要写一个方法并使用super调用clone()方法
}
}
class Person implements Cloneable{ //必须实现Cloneable这个接口才能克隆这个Person类
public String name;
public Money m;
public Person(String name) {
this.name = name;
m = new Money();
}
public Object clonePerson() throws CloneNotSupportedException {
Person tmp = (Person)super.clone(); //拷贝一份person1所指对象
tmp.m = (Money) this.m.cloneMoney(); //拷贝一份m所指对象并赋值给新对象的m引用
return tmp;
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException{
Person person1 = new Person("Sans");
Person person2 = (Person) person1.clonePerson(); //将person1所指向的对象拷贝一份给person2
System.out.println("修改前:"+ person1.m.money);
System.out.println("修改前:"+ person2.m.money);
person2.m.money = 99.9;
System.out.println("修改后:"+ person1.m.money);
System.out.println("修改后:"+ person2.m.money);
}
}
深拷贝后,执行完person2.m.money,person1.m.money的值就不会改变
三、内部类
内部类定义在类中,相当于在类中嵌套了一个类
3.1 静态内部类
静态内部类由static修饰的一个类,如下:
public class Test {
private static int a;
public int b;
static class Inner {
public int c;
public void test() {
Test test = new Test();
System.out.println(a); //内部类只能访问外部类的静态成员
System.out.println(test.b); //访问其它成员必须实例化外部类的对象
//并用外部类对象的引用来访问非静态成员
System.out.println(c);
}
}
public static void main(String[] args) {
Test.Inner inner = new Test.Inner(); //实例化静态内部类对象
inner.test(); //用内部类对象的引用访问内部类的test方法
}
}
3.2 匿名内部类
public class Test {
public void func() {
System.out.println("test()");
}
public static void main(String[] args) {
new Test().func(); //匿名对象
new Test().func(); //如果有对象只使用一次,则使用匿名对象
//这里创建了两个对象
}
}
接下来看匿名内部类:
interface InterFace {
void func();
}
class A implements InterFace{
public void func() {
System.out.println("A");
}
}
public class Test {
public static void main(String[] args) {
InterFace a = new InterFace() {
public void func() {
System.out.println("B");
}
};
a.func(); //打印结果为B
}
下图红框部分即为匿名内部类