一.抽象类
抽象类特点:
1、使用abstract修饰的方法,就是抽象类。
2、包含抽象方法的类,必须设计为抽象类,使用abstract修饰这个类(即抽象类不一定有抽象方法,有抽象方法的一定是抽象类)。
3、抽象类是不能够进行实例化的。
4、抽象类虽然不可以进行实例化,但是可以被继承。(所以抽象类就是为了被继承)
5、当一个普通类继承这个抽象类之后,如果这个抽象类当中包含抽象方法,那么需要重写这个抽象方法,否则代码不能通过编译。
6、如果一个抽象类A继承另一个抽象类B,那么此时这个抽象类A可以不重写B当中的抽象方法。
7、抽象方法不能是private、static、final的。
二,接口
接口特点:
1、使用interface来修饰接口。
2、接口当中的成员变量,默认都是public static final修饰的。
3、接口当中的成员方法,默认都是抽象方法,public abstract修饰的。
4、接口当中的普通成员方法,是不能有具体的实现的。
5、接口当中的普通成员方法,如果要有具体的实现,必须加上default修饰(从JDK8开始,才有的)。
6、接口当中可以有静态的成员方法。但是不管是静态的方法还是default方法,都是默认public修饰的(即使你没写系统也会默认进行添加)。
7、接口也是不可以进行实例化的。
8、类和接口的关系是使用implements来关联的。
9、接口当中不能有静态,实例代码块,构造方法。
10、一个抽象类实现一个接口,可以不重写这个抽象方法
11、接口虽然不是类,但它最终编译形成的字节码文件也是.class文件。
众所周知,当你想要一个类去实现一个复杂的功能时,却发现只可以继承一个父类,这时候就会发现接口的重要作用了。一个类只可以继承一个父类,但却可以实现多个接口,但是接口之间却可以多继承。
三,重要接口举例
(1)comparable< >
class 类名 implements Comparable<比较的类型>
如下图:
class Student implements Comparable<Student>{ //<>中的为所要比较的类型:Student public String name; public int age; public double score; public Student(String name, int age, double score) { this.name = name; this.age = age; this.score = score; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", score=" + score + '}'; } //必须重写compareTo方法 @Override public int compareTo(Student o) { //compareTo:将此对象与指定的对象进行比较以进行排序。 //以age为比较对象,从小到大 return this.age-o.age; } }
mian函数实现:
public class Main { public static void main(String[] args) { //创建student数组并初始化 Student[] students=new Student[3]; students[0] = new Student("李四",15,88.8); students[1] = new Student("王五",16,99.9); students[2] = new Student("张三",14,37.7); //通过Arrays.toString输出,Arrays.sort比较student数组中的age //如果上面未重写compareTo方法,将会报错,即系统不知道通过哪个数据进行比较排序 System.out.println("排序前"+ Arrays.toString(students)); Arrays.sort(students); System.out.println("排序后"+Arrays.toString(students)); } }
运行结果:
如果你想通过age或者score两两比较两个学生对象,这时候就可以使用Comparator
(2)Comparator(比较器)
我们还是使用上面那个Student类,但是数据改变了一下,为了方便更好的理解
Student[] students=new Student[3]; students[0] = new Student("def",15,99.9); students[1] = new Student("abc",16,88.8); students[2] = new Student("ghi",14,37.7);
//分别重写compare方法进行比较 class AgeComparator implements Comparator<Student> { @Override public int compare(Student s1, Student s2) { return s1.age-s2.age; } } class ScoreComparator implements Comparator<Student> { @Override public int compare(Student s1, Student s2) { return (int)(s1.score-s2.score); } } class NameComparator implements Comparator<Student> { @Override public int compare(Student s1, Student s2) { //使用compareTo以字母顺序比较排序 return s1.name.compareTo(s2.name); } }
分别实例化这三个方法
AgeComparator ageComparator = new AgeComparator(); ScoreComparator scoreComparator = new ScoreComparator(); NameComparator nameComparator = new NameComparator();
mian函数实现:
public class Main { public static void main(String[] args) { //创建Student数组并且初始化 Student[] students=new Student[3]; students[0] = new Student("def",15,99.9); students[1] = new Student("abc",16,88.8); students[2] = new Student("ghi",14,37.7); System.out.println("排序前"+ Arrays.toString(students)); //Age AgeComparator ageComparator = new AgeComparator(); Arrays.sort(students,ageComparator); System.out.println("排序后Age"+Arrays.toString(students)); //Score ScoreComparator scoreComparator = new ScoreComparator(); Arrays.sort(students,scoreComparator); System.out.println("排序后Score"+Arrays.toString(students)); //Name NameComparator nameComparator = new NameComparator(); Arrays.sort(students,nameComparator); System.out.println("排序后Name"+Arrays.toString(students)); } }
实验结果:
(3)Cloneable
Ⅰ、浅拷贝:
class Person implements Cloneable{ public int age=10; @Override //重写克隆方法要抛出一个CloneNotSupportedException异常 protected Object clone() throws CloneNotSupportedException { //将传过来的对象在新的地址克隆一份,重写clone方法 return super.clone(); //因为方法被protected修饰,所以需要使用super调用 } @Override public String toString() { return "Person{" + "age=" + age + '}'; } } public class Main { public static void main(String[] args) throws CloneNotSupportedException { Person person=new Person(); //clone方法属于object,向上转型需要强转,同时还要抛出一个克隆异常 Person person1=(Person)person.clone(); System.out.println(person); System.out.println(person1); } }
下图就是克隆的过程:
结果:
但是这仅仅只是 浅拷贝,也就是说Person中如果引用了其他对象的变量,去改变任意一个person的那个引用变量,两个person中的这个变量的值都将发生改变,也就是说,这两个实例化的person和person1中的这个变量指的是同一个变量,并不是像age一样克隆拷贝成两个,那我们看看下面代码:
class Student{ public int score=90; } class Person implements Cloneable{ public int age=10; public Student s=new Student(); @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "Person{" + "age=" + age + '}'; } } public class Main { public static void main(String[] args) throws CloneNotSupportedException { Person person=new Person(); Person person1=(Person)person.clone(); System.out.println(person.s.score); System.out.println(person1.s.score); System.out.println("======================================"); //只更改person1的score的值,而person的score的值并没有修改 //那输出结果为什么呢???? person1.s.score=88; System.out.println(person.s.score); System.out.println(person1.s.score); } }
Ⅱ、深拷贝
而深拷贝就是连引用对象的变量也可以拷贝一份新的。
实现深拷贝其实我们只需要让Student也实现接口Cloneable,代码做一些小的修改。
class Student implements Cloneable{ public int score=90; //与Person一样重写一下克隆方法 @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } class Person implements Cloneable{ public int age=10; public Student s=new Student(); @Override protected Object clone() throws CloneNotSupportedException { Person tmp = (Person) super.clone(); //将Person类中实例化的s也克隆一份 tmp.s=(Student) this.s.clone(); return tmp; } @Override public String toString() { return "Person{" + "age=" + age + '}'; } }
其结果就发生了改变!!
四、接口与抽象类的异同
区别 | 抽象类 | 接口 | |
1 | 结构组成 | 普通类+抽象方法 | 抽象方法+全局变量 |
2 | 子类使用 | 使用extends关键字继承。 抽象类继承抽象类可以不用重写或者不重写全部抽象方法。 普通类继承要重写全部抽象方法。 |
使用implements关键字继承 |
3 | 关系 | 一个抽象类可以实现多个接口 | 接口不可以继承抽象类,但接口可以使用extends去继承多个父接口 。 |
4 | 子类限制 | 一个子类只可以继承一个抽象类 | 一个子类可以实现多个接口 |
5 | 成员变量 | 既可以有变量也可以有常量 | 只能是常量,而且默认修饰符为:public static final |
即:当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口。
ps:如果文章有什么错误,请各位大佬指点,同时期待你们的点赞和评论!!!