目录
三个特殊接口(Comparable,Comparator,Cloneable)
抽象类
认识抽象类
抽象类是指含有抽象方法的那种类,抽象类通常没有具体的实现;因为多个类中都需要重写同名方法,因此将这种方法定义为abstract修饰的抽象方法,存放在abstract修饰的抽象类当中,抽象类只能被继承。
例子
定义一个抽象方法方法draw且存放在存放在抽象类Shape当中,被多个实现画图的类继承。
//一个抽象类只能被继承
abstract class Shape {
public abstract void draw();
}
//矩形类
class Rect extends Shape {
@Override
public void draw(){
System.out.println("□");
}
}
//花类
class Flower extends Shape {
@Override
public void draw() {
System.out.println("❀");
}
}
//三角形类
class Triangle extends Shape {
@Override
public void draw() {
System.out.println("▲");
}
}
//圆形类
class Circle extends Shape {
@Override
public void draw() {
System.out.println("⚪");
}
}
实例化这几个类并打印
public class Test {
public static void drawMap(Shape shape){
shape.draw();
}
public static void main(String[] args) {
}
public static void main1(String[] args) {
//可以发生动态绑定
Shape shape = new Circle();
drawMap(shape);
//也可发生多态
Flower flower = new Flower();
drawMap(flower);
}
}
注意事项
1、抽象类不能被实例化,即关键字new修饰。
2、因为不能被实例化,因此这个类只能被继承。
3、抽象类当中也可以包含和普通类一样的成员和方法,能直接被继承或者调用。但是抽象类不能是privat的,private修饰则不能被继承。
4、一个普通类继承了抽象类,可以对抽象类当中普通方法或普通成员变量直接调用。也可调用在普通类中重写后的方法。
abstract class A {
public int a = 10;
public abstract void func1();
}
class D extends A {
@Override
public void func1() {
}
}
public class TestDemo {
public static void main(String[] args) {
D d = new D();
System.out.println(d.a);
}
}
//此时打印结构为10
5、普通类继承了抽象类,必须重写抽象类同名的方法。
6、抽象类A继承了抽象类B,抽象A当中可以不重写抽象类B中的抽象方法。
abstract class A {
public abstract void func1();
}
abstract class B extends A {
public abstract void func2();
}
7、结合第六点,当普通类C继承了抽象类B,必须重写抽象类A和抽象类B当中的抽象方法。
abstract class A {
public abstract void func1();
}
abstract class B extends A {
public abstract void func2();
}
//普通类继承抽象类B
class C extends B {
@Override
public void func2() {
}
@Override
public void func1() {
}
}
8、抽象类不能被final修饰,抽象类只能被继承,而final修饰的类是不能被继承的,二者相互矛盾。
接口
认识接口
接口是抽象类的更进一步抽象,接口被interface修饰。接口名一般为I开头,如interface IA{}
例子
用接口定义抽象方法draw来实现画多个图形的类。
interface IShape { //抽象方法 public abstract void draw(); } class React implements IShape { @Override public void draw() { System.out.println("□"); } } class Flower implements IShape { @Override public void draw() { System.out.println("❀"); } } class Triangle implements IShape { @Override public void draw() { System.out.println("▲"); } } class Circle implements IShape { @Override public void draw() { System.out.println("⚪"); } } public class TestDemo2 { public static void main(String[] args) { public static void drawMap (IShape iShape) { iShape.draw(); } public static void main(String[] args) { React react = new React(); drawMap(react); drawMap(new Flower()); } }
注意事项
1、接口当中的普通方法,不能有具体的实现。如要实现,必须有default来修饰这个方法,即这个方法为接口默认方法。
interface IShape {
//抽象方法
public abstract void draw();
//default修饰实现普通方法
default public void func1() {
int a = 10;
}
}
2、接口当中也可以有static方法。
3、接口当中所有方法都为public的(默认)。
4、接口当中的抽象方法是public abstract的。
5、接口不能被实例化,即关键字new修饰。
6、类和接口之间的关系是implements来实现的。
7、当一个普通类实现一个接口,普通类当中必须重写接口当中的抽象方法。
interface IShape {
//抽象方法
public abstract void draw();
}
class React implements IShape {
@Override
public void draw() {
System.out.println("□");
}
}
8、接口当中的成员变量默认为public static final 修饰的,方法默认为public abstract修饰的。
interface IShape {
//抽象方法
public abstract void draw();
//默认写为
void func1();
//成员变量
public static final int a = 10;
//默认写为
int a = 10;
}
9、当一个类实现了一个接口,这个类当中的必须重写接口当中的抽象方法,且只能用public修饰方法。
interface IA {
void func1();
}
class BClass implements IA {
@Override
public void func1() {
}
}
此时B类相当于IA接口的子类,重写的条件是:子类方法的访问权限必须大于父类方法的访问权限。
10、一个普通类只能继承一个抽象类,可以同时实现多个接口。且必须重写所有抽象方法。
interface IB {
}
interface IA {
void func1();
}
abstract class CClass {
abstract public void func2();
}
class Class extends CClass implements IA,IB {
@Override
public void func2() {
}
@Override
public void func1() {
}
}
11、接口和接口之间可以通过extends关键字来操作,意为拓展。A接口拓展了B接口,即A接口具有了B接口的功能。
interface IC {
void func5();
}
interface ID extends IC{
void func6();
}
class E implements ID {
@Override
public void func5() {
}
@Override
public void func6() {
}
}
实现多个接口
- 有的时候我们需要让一个类同时继承自多个父类. 这件事情在有些编程语言通过 多继承 的方式来实现的.
- 然而 Java 中只支持单继承, 一个类只能 extends 一个父类. 但是可以同时实现多个接口, 也能达到多继承类似的效果.
定义一个动物Animal类
class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
}
动物能力用接口实现
interface IFlying {
void fly();
}
interface IRunning {
void run();
}
interface ISwimming {
void swim();
}
猫能跑
class Cat extends Animal implements IRunning {
public Cat(String name) {
super(name);
}
@Override
public void run() {
System.out.println(super.name + "会飞");
}
}
鱼会游
class Fish extends Animal implements ISwimming {
public Fish(String name) {
super(name);
}
@Override
public void swim() {
System.out.println(super.name + "会游");
}
}
青蛙会跑也会游
class Flog extends Animal implements IRunning,ISwimming {
public Flog(String name) {
super(name);
}
@Override
public void run() {
System.out.println(super.name + "会跑");
}
@Override
public void swim() {
System.out.println(super.name + "会游");
}
}
实现这些类
public static void main(String[] args) {
Fish fish = new Fish("🐟");
fish.swim();
Cat cat = new Cat("🐱");
cat.run();
Flog flog = new Flog("🐸");
flog.run();
flog.swim();
}
三个特殊接口(Comparable,Comparator,Cloneable)
当我们对一个无序数组进行排序时,会用Arrays.sort函数。
public class TestDemo4 {
public static void main(String[] args) {
int[] array = {1,3,4,67,8,34};
System.out.println(Arrays.toString(array));
Arrays.sort(array);
System.out.println(Arrays.toString(array));
}
}
排序前后打印结果为:
当将类实例化了对象,如何将对象当中的数据进行排序?
如:定义一个学生类,有包括姓名,年龄,成绩字段
class 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 +
'}';
}
}
初始化一个数组,数组类型为Student类.
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("zhang san",18,95.5);
students[1] = new Student("li si",19,93.4);
students[2] = new Student("wang wu",20,96.5);
}
用数组的方法,将Student类数组排序并打印代码会报错。Arrays.sort不确定根据students哪个数据进行排序。
正确的做法应该是让类实现一个Comparable接口,接着重写其中的compareTo方法。
Comparable接口
class Student implements Comparable<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 +
'}';
}
@Override
public int compareTo(Student o) {
/*if (this.age > o.age) {
return 1;
} else if (this.age == o.age) {
return 0;
} else {
return -1;
}*/
return (int)(this.score - o.score);
}
}
其中Comparable后面的尖括号内容为要比较的数据类型;
重写的compareTo方法当中this代表的是调用这个方法的对象。
这时可以正常使用Arrays.sort了,再用其按分数排序。
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("zhang san",18,95.5);
students[1] = new Student("li si",19,93.4);
students[2] = new Student("wang wu",20,96.5);
Arrays.sort(students);
System.out.println(Arrays.toString(students));
}
也可以对姓名排序。又因为姓名为引用类型,所以需将campareTo方法改写:
@Override
public int compareTo(Student o) {
/*if (this.age > o.age) {
return 1;
} else if (this.age == o.age) {
return 0;
} else {
return -1;
}*/
return this.name.compareTo(o.name);
}
虽然comparable接口可以让数组元素按照自己的意愿进行排序,但这种写法对类的侵入性较强,如果要更改排序方式,就需要在Student类中修改compareTo()方法,这并不是我们想看到的,因此,我们引入了下一种接口:
Comparator接口
comparator接口也叫比较器,是根据给自定义比较实现的一个接口,实现这个接口需要重写compare方法。
定义一个ScoreComparator实现Comparator接口:
class ScoreComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return (int)(o1.score - o2.score);
}
}
将这个类实例化后使用:
通过sort的源码可以确定Arrays.sort此时的参数:
也可以定义一个NameCamparator来排序名字:
class NameComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.name.compareTo(o2.name);
}
}
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("zhang san",18,95.5);
students[1] = new Student("li si",19,93.4);
students[2] = new Student("wang wu",20,96.5);
NameComparator nameComparator = new NameComparator();
Arrays.sort(students,nameComparator);
System.out.println(students);
}
Cloneable接口
Cloneable接口是Java内置的接口,也较为常用的接口。Object类当中存在一个clone方法,调用这个方法可以创建一个对象的拷贝。
这时person1和person2在内存的存储为:
1、实例化person1对象后,将其字段该值,person2的同名字段为person1字段的拷贝。
public static void main(String[] args) throws CloneNotSupportedException {
Person person1 = new Person();
person1.age = 99;
Person person2 = (Person) person1.clone();
System.out.println(person2);
}
person2的打印结果也为99.
2、当实例化对象person1的拷贝person2后,修改person2字段的值,perso1字段的值不变。
public static void main(String[] args) throws CloneNotSupportedException {
Person person1 = new Person();
Person person2 = (Person) person1.clone();
person2.age = 199;
System.out.println(person1);
System.out.println(person2);
}
打印结果为99,199
所以这种方法是深拷贝还是浅拷贝?
需要注意的是,确定深拷贝还是浅拷贝的依据不是调用的方法,而是拷贝的过程属于什么拷贝、拷贝了什么内容。
以下情况为浅拷贝:
1.定义了一个Money类,同时在Person类当中实例化这个类。
class Money {
public double m = 12.5;
}
class Person implements Cloneable{
public int age;
public Money money = new Money();
public void eat() {
System.out.println("吃!");
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
其在内存的存储为:
1、分别通过person1对象和person2对象引用money对象打印m:
public static void main(String[] args) throws CloneNotSupportedException {
Person person1 = new Person();
Person person2 = (Person) person1.clone();
System.out.println(person1.money.m);
System.out.println(person2.money.m);
}
//此时打印结果都为12.5
2、通过person2对象引用money再修改m的值:
public static void main(String[] args) throws CloneNotSupportedException {
Person person1 = new Person();
Person person2 = (Person) person1.clone();
person1.money.m = 99.9;
System.out.println(person1.money.m);
System.out.println(person2.money.m);
}
//此时打印结果都为99.9
如何理解这种拷贝为浅拷贝?
这种拷贝没有将money引用指向的对象拷贝一份,而是使person2对象当中money引用指向已经实例的对象。
如何达到深拷贝效果?需将money引用所指的对象也再拷贝一份,这时Money类需要实现克隆接口,Person类需重新定义克隆方法。
此时在内存的存储为:
1、分别通过person1对象和person2对象引用money对象打印m:
public static void main(String[] args) throws CloneNotSupportedException {
Person person1 = new Person();
Person person2 = (Person) person1.clone();
System.out.println(person1.money.m);
System.out.println(person2.money.m);
}
//此时打印结果都为12.5
2、通过person2对象引用money再修改m的值:
public static void main(String[] args) throws CloneNotSupportedException {
Person person1 = new Person();
Person person2 = (Person) person1.clone();
person1.money.m = 99.9;
System.out.println(person1.money.m);
System.out.println(person2.money.m);
}
//这时person1.money.m打印结果为12.5;person2.money.m打印结果为99.9