多态
// 打印形状
class Shape{
public void draw(){
}
}
// 打印矩形
class Rect extends Shape{
@Override
public void draw(){
System.out.println("♦");
}
}
// 打印圆圈
class Circle extends Shape{
@Override
public void draw(){
System.out.println("⭕");
}
}
class Flower extends Shape{
@Override
public void draw(){
System.out.println("❀");
}
}
public class Demo {
public static void drawMap(Shape shape){
shape.draw();
}
public static void main(String[] args) {
Rect rect = new Rect();
drawMap(rect);
Circle circle = new Circle();
drawMap(circle);
Flower flower = new Flower();
drawMap(flower);
}
}
多态中主要的问题就是向上转型和运行时绑定两个关键点:
-
向上转型:格式为:
父类名称 对象名 = new 子类名称()
含义为:右侧创建一个子类对象,把它当作父类来看待使用(把子类对象赋值给父类引用) -
运行时绑定:使用了向上转型新建了一个对象,由于子类对父类的一个方法进行了重写,如果使用新建的这个对象调用此方法,则在编译时该方法会调用父类方法,但运行时回绑定子类方法(通俗理解为:访问成员方法的规则是,new的是谁,优先使用谁,没有该方法再想上找、编译看左,运行看右)
多态的优点:
- 1、类调用者对类的使用成本进一步降低
- 2、能够降低代码的“圈复杂度”,避免使用大量的 if-else
- 3、可扩展能力更强
抽象类
- 抽象类:包含抽象方法的类
- 1、抽象类不能进行实例化
- 2、抽象类当中,可以拥有与普通类一样的数据成员和方法
- 3、抽象类可以被继承,可以方法向上转型
- 4、当一个普通类继承了一个抽象类,则当前这个普通类必须要重写抽象类
- 5、当普通类A继承了抽象类且不想不重写抽象类的方法,则将该普通类也变为abstract,即为抽象类A,就不需要进行实现了
- 6、如果这个普通类B,继承了抽象类A,此时就要实现抽象方法(出来混,迟早要还)
- 7、抽象方法不能是私有的(因为私有的不能被重写,抽象方法一定是会被重写的
- 8、抽象类的出现,最大意义就是为了被继承
abstract class Shape{
public abstract void drwa(); // 如果该方法被abstract修饰了,只能通过重写实现
// 2、抽象类当中可以拥有与普通类一样的数据成员和方法
public int age;
public void func(){}
}
// 3、抽象类可以被继承
class Rect extends Shape{
@Override
public void draw(){
System.out.println("♦")
}
}
public class TestDemo{
public static void drawMap(Shape shape){
shape.draw(); // 可以通过shape引用调用抽象方法里的draw
}
public static void main(String[] args){
// 1、抽象方法不能进行实例化,下面一行代码不正确
// Shape shape = new Shape(); ×
// 抽象类也可以发生向上转型
drawMap(new Rect());
}
}
接口
- 接口:使用关键字
interface
修饰 - 1、接口当中的方法不能有具体的的实现,接口当中的方法,默认是:
public abstract
- 2、接口当中的成员变量,默认是
public static final
- 3、JDK1.8引入的新特性。使用default修饰的方法,默认方法,可以有具体实现
- 4、接口不可以进行实例化:
IShape ishape = new IShape();
- 5、类和接口之间的关系是
implements
```java
interface IShape{
void draw();
}
class Rect implements IShape{
@Override
public void draw(){
System.out.println("⚪")
}
}
public class TestDemo{
// 接口也可以向上转型
public static void drawMap(IShape shape){ //动态绑定
shape.draw();
}
public static void main(String[] args){
drawMap(new Rect); //向上转型
}
}
- 6、一个类可以实现多个接口
- 7、一个类可以继承类且同时实现多个接口
class Test extends TestAabstract implements A,B,C{
- 8、接口可以扩展多个接口
- 所以接口的出现,就是为了解决java多继承的问题
interface A{
void funcA();
}
interface B{
void func();
}
interface C{
void funcC();
}
// 接口之间的关系(不叫继承叫扩展)
interface D extends A{
void funcD();
}
class F implements D{
// 此处F重写了D中的两个方法,可以看到,D确实得到的扩展
@Override
public void funcA() {
}
@Override
public void funcD() {
}
}
// 不一定必须是抽象类,普通类也可以,但只能继承一个类
abstract class TestAabstract{
public abstract void funcAabstract();
}
class Test extends TestAabstract implements A,B,C{
@Override
public void funcA() {
}
@Override
public void func() {
}
@Override
public void funcC() {
}
@Override
public void funcAabstract() {
}
}
举例来实现多个接口
// 在写代码时,应当一个类(接口)就是一个java文件
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.prinrln(this.name + "正在跑!")
}
}
public class TestDemo{
public static void main(String[] args){
}
}
常见的三个接口
1、Comparable
class Student{
public String name;
public int score;
//构造方法
public Student(String name, int score){
this.name = name;
this.score = score;
}
// 重写toString方法
@Override
public String toString(){
return "Student{" +
"name = " + name + '\' +
", score =" + score + '}';
}
}
public class TestDemo{
public static void main(String[] args){
Student[] students = new Student[3]; //定义一个数组
// 把值扔进去
students[0] = new Student("bit", 81);
students[1] = new Student("bit", 63);
students[2] = new Student("bit", 79);
System.out.println(Arrays.toString(students));
// 想要对数组进行排序
Arrays.sort(students);
System.out.println(Arrays.toString(students)); // 此时会报错
}
}
这里放遇到的错误
错误中我们可以看到,此处是将每个数组都强制转换成了Comparable
,每一个学生student都是一个对象,这里不知道对啥进行比较,所以会报错,我们可以实现一个接口,这个接口叫做Comparable
class Student implements Comparable<Student>{ //<>中填的是想要比较的类型
public String name;
public int score;
//构造方法
public Student(String name, int score){
this.name = name;
this.score = score;
}
// 重写toString方法
@Override
public String toString(){
return "Student{" +
"name = " + name + '\' +
", score =" + score + '}';
}
// 重写Comparable中的campareTo方法
public int compareTo(Student o){ // 这个方法在此出的意思就是 学生与学生之间进行比较
if(this.score > o.score){
return 1;
}else if(this.score == o.score){
return 0;
}else{
return -1;
}
}
}
- 对于
Comparable
接口来说,一般是用在类的内部的
2、比较器(Comparator)
class Student2{
public String name;
public int score;
//构造方法
public Student2(String name, int score){
this.name = name;
this.score = score;
}
// 重写toString方法
@Override
public String toString(){
return "Student{" +
"name = " + name + '\' +
", score =" + score + '}';
}
}
// 单独写一个类,是根据分数的比较器
public class ScoreComparator implements Comparator<Student2>{
// 实现重写该接口中的compare方法
public int compare(Student2 o1, Student o2){
// if(o1.score > o2.score){ // 以他们的分数作为比较
// return 1;
// }else if(o1.score == o2.score){
// return 0;
// }else{
// return -1;
// }
// 可以直接写成
return o1.score - o2.score;
}
}
// 根据姓名进行排序
public class ScoreComparator implements Comparator<Student2>{
// 实现重写该接口中的compare方法
public int compare(Student2 o1, Student o2){
// 对String类型比较大小,使用.compareTo()方法
return o1.name.compareTo(o2.name);
}
}
public TestDemo{
public static void main(String[] args){
student2[] students = new Student2[3];
students[0] = new Student2("bit", 81);
students[1] = new Student2("bit", 63);
students[2] = new Student2("bit", 79);
System.out.println(Arrays.toString(students));
// 想要对数组进行排序
// 根据分数进行排序
ScoreComparator scoreComparator = new ScoreComparator();
Array.sort(students, scoreComparator);
// 在sort的源码中,有两个参数,第一个是数组名,第二个就是comparator,即以什么样的方式进行比较。
}
}
3、
- 1、如何拷贝对象
class Person implements Clonable{
public String name;
public Person(String name){
this.name = name;
}
@Override
public String toString(){
return "PersonP" +
"name = " + name + '\' +
'}';
}
// alt+ins -> Override Methods -> clone()
@Override
protected Object clone() throws CloneNotSupportedExeception{
return super.clone();
}
}
public class TestDemo03{
public static void main(String[] args throws CloneNotSupportedExeception){
// 涉及异常相关知识暂不做了解
Person person1 =new Person("bit");
Person person2 = (Person)persion1.clone(); //因为clone方法本身是Object类型为超级父类,所以要向下转型为Person
}
}
- 接口什么内容都没有,此时这个接口叫做:空接口,标记接口。此处的
Clonable
接口的作用是标记当前这个类是可以被克隆的 - 克隆回拷贝一份副本