目录
前言:
本篇文章主要讲述对于包,接口,以及多态的理解,以及一些常用接口的了解,如果对于你有所帮助或者启发,不要忘记给作者点个赞哦!
多态
1.1 向下转型(了解)
在前面一篇文章我们介绍了什么是向上转型,向下转型就是用子类的引用去引用父类引用所指的该子类对象,但是这个时候就必须的要进行强转,因为这个时候是要父类的引用被子类的引用所引用,接下来我们可以通过一个代码来理解一下
animal animal=new bird("fja",20);
bird bird=(bird) animal;
此时就是子类的引用引用了父类引用所指的该子类对象,其实向下转型还是不安全的,之前我们向上转型中,父类引用子类,可以理解为子类是属于父类的一种,而这里不能说父类是子类的一种。
1.2 instanceof关键字
对于向下转型,我们可以采取利用instanceof关键字来判断父类引用是否是子类的一个实例,具体用法如下
animal animal=new dog("fja",20);
if (animal instanceof dog){
System.out.println(333);
}
如果animal引用的是dog的一个子类对象,就会执行if语句里面的内容,反之就不会执行if语句里面的内容,这样子可以让向下转型更加安全的实现。
1.3 多态(理解)
对于多态我们可以结合一段代码来进行理解什么是多态!
class shape{
public void draw(){
}
}
class rect extends shape{
@Override
public void draw() {
System.out.println("(●ˇ∀ˇ●)");
}
}
class small extends shape{
@Override
public void draw() {
System.out.println("small");
}
}
public class test {
public static void func(shape shape){
shape.draw();
}
public static void main(String[] args) {
rect rect=new rect();
func(rect);
small small=new small();
func(small)
}
}
在这段代码中我们定义了一个shape类里面的draw方法可以帮助我们打印出不同的形状,我们结合之前所讲的动态绑定,继承,就可以来实现通过定义一个父类来实现打印不同的形状,在这里我们可以了解到,在调用函数中,我们不用关注shape引用了那个类型的实例。也就是说通过一个父类shape的引用,来调用同一个方法,表现出来的形式有不同(与shape对应的实例有关),这样的一个行为就被称为是多态。
1.4 多态的好处
之前我们学过private封装,封装的好处就是让类的调用者不需要知道类的实现细节,那么此时多态的作用就是让类的调用者不需要知道这个类的是什么类型,只需要这个对象的某个方法就可以了,这样就让类的调用者对类的使用成本进一步降低。
抽象类
2.1 abstract关键字
在多态中,我们发现通过动态绑定,父类中的方法是没有被执行的,那么这个时候我们可以通过abstract关键字将该方法变为抽象方法,那么包含抽象方法的类就被称为抽象类。
abstract class shape{
abstract public void draw();
}
2.2 语法规则
1 不能通过关键字new来实例化抽象类本身,但是可以发生向上转型
2 抽象类最大的作用是被继承
3 抽象类不能被final修饰,被final修饰的类是不能被继承的
4 抽象类中也是可以包含字段与普通方法
5 一个普通类继承了抽象类,那么这个普通类就需要重写抽象类里面的全部抽象方法
6 若一个抽象类A,继承了一个抽象类B,抽象类A可以不重写抽象类B中的抽象方法,此时若有一个普通类C,继承了抽象类A,那么此时就需要重写抽象类A与B中所有的抽象方法。
接口(重点)
3.1 语法规则
1 在抽象类的基础上(可以理解为是抽象类的plus版本),我们利用interface关键字去定义一个接口,abstract public在接口是可以省略的,与抽象类一样,接口也是不能被实例化的。
interface shape{
abstract public void draw();
}
2 接口中不能含有普通方法与字段(如果要写入普通方法,可以在普通方法前用关键字default修饰,表示为这个接口的默认方法)
3 接口中的成员变量都是被public static final(可以被省略)修饰的,并且都要被赋予初值
4 当一个类继承了接口,就要重写该接口中的所有抽象方法
5 接口的实现(implements关键字)
我们通过implements关键字来实现一个接口
interface shape{
abstract public void draw();
}
class cloud implements shape{
@Override
public void draw() {
System.out.println("这是一个接口实现");
}
}
public class test {
public static void main(String[] args) {
shape shape=new cloud();
shape.draw();
}
}
我们通过定义了一个cloud,通过关键字implements实现接口shape
6 当一个类实现了该接口,那么重写该接口的抽象方法前一定要加上public
接口中可以省略,但是重写时可不能不写,否则是包访问权限,就比接口中的访问权限就要小了。
3.2 实现多个接口
interface Is{
abstract public void eat();
}
interface shape{
abstract public void draw();
}
class cloud implements shape,Is{
@Override
public void draw() {
System.out.println("这是一个接口实现");
}
@Override
public void eat() {
System.out.println("实现多个接口!");
}
}
public class test {
public static void main(String[] args) {
shape shape=new cloud();
shape.draw();
}
}
通过代码我们可以知道一个类可以实现多个接口(本例子就列举了两个接口,其实可以多个接口),但要把每个接口的抽象方法要重写,并且接口之间用逗号隔开。
3.4 接口与接口之间的关系
interface Irunning{
abstract public void run();
}
interface Iwalk{
abstract public void walk();
}
interface IC extends Irunning{
abstract public void pdd();
}
从代码中我们可以知道,接口与接口之间是通过extends关键字来表示两个接口之间的关系,此时这里的extends不是继承的意思,而表示拓展的意思,一个接口可以拓展另一个接口,而实现两个接口的功能(如果要拓展多个接口,需要用逗号隔开)
3.5 接口的好处
1 弥补java中单继承的缺点,因为可以扩展多个接口,那么我们可以通过继承一个父类,利用implement关键字实现多个接口,从而实现更多的功能
2 多态能够让程序猿不必关注类的类型,那么接口就是让程序猿不必关注类的类型,而是关注类所能实现的功能
例如:
我们先创建三个接口,去实现跑,游,飞三种功能
interface Irun{
void run();
}
interface Iswim{
void swim();
}
interface Ifly{
void fly();
}
我们在定义一个父类animal
class Animal{
String name;
public Animal(String name){
this.name=name;
}
}
定义实现一个功能的
//定义一个跑的动物
class DOG extends Animal implements Irun{
public DOG(String name) {
super(name);
}
@Override
public void run() {
System.out.println(name+"四条腿跑");
}
}
定义一个实现两个功能的动物
//定义一个会跳回游的青蛙
class frog extends Animal implements Irun,Iswim{
@Override
public void run() {
System.out.println(name+"在跳");
}
@Override
public void swim() {
System.out.println(name+"游泳");
}
public frog(String name) {
super(name);
}
}
定义一个能满足三种功能的动物
//定义能满足这三个功能的动物
class Duck extends Animal implements Irun,Iswim,Ifly{
@Override
public void run() {
System.out.println(name+"跑");
}
@Override
public void swim() {
System.out.println(name+"游泳");
}
@Override
public void fly() {
System.out.println(name+"飞");
}
public Duck(String name) {
super(name);
}
}
在这里我们就可以看到,我们可以通过继承一个类,实现多种接口,从而实现更多的功能,弥补了java中单继承的缺陷。从这里也可以看出接口所要表达的含义就是:具有xxx特性
我们在这个基础上,可以实现一个方法专门用于跑
public static void fun(Irun Irun){
Irun.run();
}
在这个方法的内部我们不关心是什么类型,只要所给的参数会跑就行
public class node {
public static void fun(Irun Irun){
Irun.run();
}
public static void main(String[] args) {
DOG DOG=new DOG("狗");
fun(DOG);
}
}
接口的可扩展性非常强,只要一个类实现了该接口,就能通过该方法来实现,请看下列代码
lass Robot implements Irun {
private String name;
public Robot(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(this.name + "正在用轮子跑");
}
}
Robot robot = new Robot("机器人");
fun(robot);
只要是实现了Irun这个接口,就可以通过实现方法,来实现该类的功能。
3.6 接口与抽象类的区别(要点)
核心区别:抽象类中是可以包含字段与普通方法的,方法与字段是可以直接被子类使用的(不需要重写),接口中不含有普通方法,子类必须重写接口中包含的所有抽象方法
图解区别
接口常用实例
4.1 comparable接口
在java中,我们在数组中就学过了Arrays.sort()对一个整数类型的数组进行排序,那么对于在实际处理业务,有些时候排序需要我们指定一个指标进行排序,这个时候就要用到我们java中自带的comparable接口
class Student implements Comparable<Student>{
public String name;
public int age;
public double score;
//这里的this是谁调用了compareTo方法谁就是
@Override
public int compareTo(Student o) {
//return this.age-o.age;//通过年龄进行比较
//return (int)(this.score-o.score);//通过分数进行比较
return this.name.compareTo(o.name);//甚至可以通过名字进行排序
}
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 demo {
public static void main(String[] args) {
Student student=new Student("haha",24,55);
Student student1=new Student("heihei",20,89);
System.out.println(student.compareTo(student1));
}
}
我们甚至可以利用这个接口对更多个数组排序,实现这个接口之后,利用Arrays.sort()进行排序
public static void main(String[] args) {
Student[] students =new Student[3];
students[0]=new Student("zhan",21,98);
students[1]=new Student("hehe",17,87);
students[2]=new Student("keke",34,99);
Arrays.sort(students);
System.out.println(Arrays.toString(students));
}
这个类存在一定的缺点,对于类的侵害性大,所以下面我们在介绍comparator接口
4.2 comparator接口
先创立一个学生类
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 +
'}';
}
}
创立一个年龄比较器
class AgeComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return o1.age-o2.age;
}
}
创立一个分数比较器
//成绩比较器
class ScoreComrarator 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 demo {
public static void main1(String[] args) {
Student student=new Student("haha",24,55);
Student student1=new Student("heihei",20,89);
ScoreComrarator scoreComrarator=new ScoreComrarator();
scoreComrarator.compare(student1,student);
}
}
比较多个数组的一个成绩
public class demo {
public static void main(String[] args) {
Student[] students =new Student[3];
students[0]=new Student("zhan",21,98);
students[1]=new Student("hehe",17,87);
students[2]=new Student("keke",34,99);
ScoreComrarator scoreComrarator=new ScoreComrarator();
Arrays.sort(students,scoreComrarator);
System.out.println(Arrays.toString(students));
}
}
4.3 clonable接口和深拷贝
4.3.1 浅拷贝
class people{
public int age;
}
public class apple {
public static void main(String[] args) {
people people=new people();
people people1=people;
System.out.println(people);
System.out.println(people1);
}
}
也就是所浅拷贝就是简单来理解就是会有两个引用公用了一个对象。
4.3.2 深拷贝与clonable接口
class people implements Cloneable {
public int age;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class apple {
public static void main(String[] args)throws CloneNotSupportedException {
people people=new people();
people people1=(people) people.clone();
System.out.println(people);
System.out.println(people1);
}
}
所谓的深拷贝简单理解就是一个引用对应单独一个对象
4.3.3 clonable使用要点:
1 clonable接口源码是不包含任何抽象方法的,是一个空接口,对于这个空接口的作用就是表示当前类是可以被克隆的。
2 对于clone方法结合clonable我们必须要在实现接口中重写方法。
3 由于clone源码是一个object类,所以这里我们必须进行类型强转。
4 实现clone接口中需要我们抛异常,这个在后续的学习中,我们会讲解异常。
浅拷贝与深拷贝的详细理解参考这篇文章: