上篇重点总结了向上转型,以及向上转型中的重写。在了解了这些之后才能更进一步理解何为多态。先创建一个父类和一个方法,并创建复数个子类和同名的构造方法进行复写再进行继承。这样在main函数中就可以进行调用,但由于发生了动态绑定,这个时候实际上调用的是子类的方法而非父类的方法:
class Shape{
public void draw (){
}
}
class Square extends Shape{
@Override
public void draw() {
System.out.println("♦");
}
}
class Triangle extends Shape{
@Override
public void draw() {
System.out.println("▲");
}
}
public class test1 {
public static void main(String[] args) {
Square square = new Square();
square.draw();
}
}
运行程序可得到一个方片图片,因为发生了动态绑定,也可以将main函数中的引用及类型进行向上转型:
public class test1 {
public static void main(String[] args) {
Shape shape = new Square();
shape.draw();
}
这样也能运行得到方片。
如果再构造一个方法,统一调用draw方法,即:
public class test1 {
public void getDraw (Shape shape){
shape.draw();
}
这样就能只调用getDraw的情况下打印多个图形,就可以构成多态:
class Shape{
public void draw (){
}
}
class Square extends Shape{
@Override
public void draw() {
System.out.println("♦");
}
}
class Triangle extends Shape{
@Override
public void draw() {
System.out.println("▲");
}
}
public class test1 {
public static void getDraw (Shape shape){
shape.draw();
}
public static void main(String[] args) {
getDraw(new Square());
getDraw(new Triangle());
}
}
在Shape类中,其方法并未实现,只是单纯的用于了继承,那么可以将其方法设计为抽象方法,用abstract进行修饰,则其类也用abstract修饰成为抽象类:
abstract class Shape{
abstract public void draw ();
}
注意其中的一些细节:
1.抽象类不可以在主函数中被实例化
2.只能单纯用于继承
3.抽象类中可以包含普通成员方法
4.如果一个普通类要继承抽象类,就必须重写抽象类中的所有抽象方法
5.若抽象类B继承了抽象类A,那么就不必再重写抽象类A的抽象方法,但在此基础上若一个普通方法继承了抽象类B,则需要重写抽象类A B中的所有方法:
abstract class A{
int a;
String name;
abstract public void draw();
}
abstract class B extends A{
abstract public void func();
}
class C extends B{
@Override
public void draw() {
System.out.println("测试1");
}
@Override
public void func() {
System.out.println("测试2");
}
}
public class test2 {
public static void main(String[] args) {
C c = new C();
c.draw();
c.func();
}
}
6.抽象类及抽象方法不可被final修饰
再抽象类的基础上,可以进一步优化。因为Java中类的继承只能继承一个,但实际写代码过程中只能继承一个过于局限,因此可以将抽象类进一步简化为接口,接口跟在类的后面以extends来表示,不再意为继承,而是改为扩展,其中需要注意的点是:
1.接口不再是类,要以interface来修饰
2.接口当中的普通方法不能有具体的实现,若非要实现只能通过关键字default来修饰
3.接口当中可以有static静态方法
4.接口中的方法都是public的,因此可以省略不写
5.抽象方法默认是public abstract,也可以省略
6.接口也不能实例化
7.类和接口之间以implement实现,若有多个接口,接口间以逗号隔开即可
8.当一个类实现了一个接口后,就必须重写其中的所有抽象方法
9.接口当中的成员变量默认是public static final修饰,可省略但是切记必须初始化赋值
10.注意权限问题,当一个类重写某个接口的抽象方法时必须加上public,否则默认的是包访问权限,而抽象方法默认的是public,比包访问权限更广
11.若接口之间存在扩展,当类实现接口时,就要重写包括扩展前的所有接口的抽象方法
interface IA{
int a = 10;
default void func2(){
System.out.println("测试3");
}
static void func3(){
System.out.println("测试4");
}
void draw();
}
interface IB extends IA{
void func();
}
class C implements IA,IB{
@Override
public void draw() {
System.out.println("测试1");
}
@Override
public void func() {
System.out.println("测试2");
}
}
public class test2 {
public static void main(String[] args) {
C c = new C();
c.draw();
c.func();
c.func2();
}
}
简单介绍两个常用的接口。先来说说Comparable接口,还是以实例来说,先自定义一个数组,但是这个数组每个元素中所包含的属性不止一个:
class Student implements{
String name;
int age;
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 +
'}';
}
public class test3 {
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("小hhh", 15, 85.5);
students[1] = new Student("小hhhh", 12, 80);
students[2] = new Student("小h", 10, 98);
System.out.println(Arrays.toString(students));
平常比较数组的大小都是调用Array.sort来进行的。但是如这个代码中的自定义数组,每个元素所包含的内容不是唯一的,这种情况下就得先明确,是用什么来进行比较,因此就可以使用Comparable接口:
class Student implements Comparable<Student>
加入接口后摁住ctrl点击该接口,下拉会发现只有一个compareTo方法,由于是接口,所以要在类中对该方法进行重写,若要比较age,则调用age:
public int compareTo(Student o) {
return this.age - o.age;
}
this.所代指的就是调用的对象。
但是若要比较的是name这样的String类型,就得再调用一次compareTo方法,不能直接比较。因为本体的student只是一个引用,引用与引用直接比较Java是做不到的,所以最后代码写出来就是这样:
import java.util.Arrays;
class Student implements Comparable<Student>{
String name;
int age;
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) {
return this.name.compareTo(o.name);
}
}
public class test3 {
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("小hhh", 15, 85.5);
students[1] = new Student("小hhhh", 12, 80);
students[2] = new Student("小h", 10, 98);
System.out.println(Arrays.toString(students));
Arrays.sort(students);
System.out.println(Arrays.toString(students));
}
}
但是这个方法有一个非常局限的地方。其比较的属性是固定死了的,若要改变只能改变其compareTo中的代码,若随意改动,会造成一系列的连锁反应。因此更多推荐的是使用比较器Comparator进行比较。
例如,要对age进行比较,即可单独令一个类使用比较器接口,摁住ctrl再点击Comparator可再源码中看到其第一个抽象方法,在该类中进行重写即可获得相应的比较器:
class AgeComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return o1.age - o2.age;
}
}
同样的道理也可以写出比较name与比较score的比较器:
class ScoreComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return (int) (o1.score - o2.score);
}
}
class NameComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return o1.name.compareTo(o2.name);
}
}
这样的话需要比较什么就调用相应的比较器即可,无需对代码本身进行修改,对代码的破坏性大大降低:
import java.util.Arrays;
import java.util.Comparator;
class Student{
String name;
int age;
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 +
'}';
}
}
class AgeComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return o1.age - o2.age;
}
}
class ScoreComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return (int) (o1.score - o2.score);
}
}
class NameComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return o1.name.compareTo(o2.name);
}
}
public class test4 {
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("aaaaa", 15,66.3);
students[1] = new Student("bbbb", 18,75);
students[2] = new Student("ccc", 14,89);
System.out.println(Arrays.toString(students));
AgeComparator ageComparator = new AgeComparator();
Arrays.sort(students, ageComparator);
System.out.println(Arrays.toString(students));
}
}
在具体实践中使用哪一个跟具体的业务存在关系,但比较器是较为推荐的一个,其功能相比之下更为安全。
---------------------------------最后编辑于2023.4.14下午四点左右