Home >Java >javaTutorial >How to use Java abstract classes and interfaces
What is an abstract class? As the name suggests, abstract classes are very abstract. That is, when we don't have enough information to describe this class, we don't need to describe it. Such a class is an abstract class.
Use code to give an example:
class Shape { public void draw() { System.out.println("我要画图形!!"); } } class circle extends Shape { @Override public void draw() { System.out.println("我要画圆形!!!"); } } class rect extends Shape { @Override public void draw() { System.out.println("我要画矩形!!!"); } } public class TestDemo3 { public static void drawShape(Shape shape) { shape.draw(); } public static void main(String[] args) { drawShape(new circle()); drawShape(new rect()); } }
Just use this small case to review polymorphism. We create a Shape parent class, and then create two subclasses called rect classes. And the circle class uses the same method using these two classes. The resulting shapes are different. This is polymorphism, but
we can find that I only use these two subclasses to draw graphics, which are circles. For shapes and rectangles, I did not call the parent class to implement this method. Why? ? ? Just because this parent class cannot draw graphics, then the implementation of the draw method of this parent class is useless. I only need the parent class to have this method and rewrite it through the subclass. There is no need for the parent class. Methods have specific implementations.
Yes, this is introduced into our today’s theme abstract class
The draw method of the parent class above is useless because of its specific implementation, so we don’t need to write it, which is why In line with the definition of an abstract class, when there is not enough information to describe it, then we call it an abstract class.
If the draw method of the above parent class has no specific implementation, we can call it an abstract method. Use abstract to modify
public abstract void draw();
but why will an error be reported? ?
The reason is that the method modified by abstract is called an abstract method. If there is an abstract method in a class, then the class must be an abstract class, so the class must also be modified by abstract.
But on the contrary, is there necessarily an abstract method in an abstract class? ? ?
The compiler will not report an error, so the answer is not necessarily. There can be abstract methods or no abstract methods in abstract classes.
Can abstract classes be instantiated? ? ?
So, abstract classes cannot be instantiated.
Can abstract classes define member variables, methods, and constructors like ordinary classes? ? ?
The difference between abstract classes and ordinary classes is that abstract classes cannot be instantiated, and other definitions of member variables, member methods, constructors, etc. are the same.
Since an abstract class cannot be instantiated, what is the meaning of its existence? ? ?
Lao Tie’s thinking makes sense. In fact, the biggest meaning of abstract classes is to be inherited, because abstract classes cannot instantiate objects and can only rely on subclasses to override the methods of parent classes (that is, abstract classes). Business needs.
How to inherit abstract class? ? ? What are the points to pay attention to? ? ?
# Why does inheritance go wrong like this? ? The reason is that when a subclass inherits an abstract class, the subclass needs to override all methods of the parent class or the subclass needs to be modified with abstract (the abstract class is inherited by the abstract class or the subclass overrides all methods of the abstract class (parent class)) .
That’s correct.
Also note here that when we do not override the method of the parent class but use abstract to modify it, this is what happens when you inherit this subclass again. The subclass becomes a parent class, and the next subclass must continue to rewrite the methods of this parent class and the methods of this parent class's parent class.
Can abstract methods be modified by static and final? ? ?
Abstract methods cannot be modified by static and final, because the subclass must override the method of the parent class, and the access modification qualifier can be omitted. The default is public.
We call classes that cannot describe an object clearly enough as abstract classes.
The method modified by abstract is called abstract method, and the class modified by abstract is called abstract class
Abstract method must be in abstract class , that is to say, as long as there is an abstract method, the class name must also be modified with abstract. On the contrary, an abstract class may not have an abstract method, or it may have an abstract method.
抽象类不能实例化,除了不能实例化之外其他与普通类一样可以定义成员变量,成员方法,构造方法等。同时构造方法和类方法(被static修饰的方法)不能被abstract来修饰
抽象方法的访问修饰限定符不能是private,如果省略默认是public,同时抽象方法不能被final修饰。
子类继承抽象类的时候,子类必须重写抽象类的所有方法并且要有方法的具体实现,如果重写那子类还是抽象类,必须用abstract来修饰。
抽象类中的方法没有具体实现,要通过子类重写在子类中实现。
一个类只能继承一个抽象类
说到接口我们会想到什么呢???我一开始想到的就是充电接口插排等等,比如充电接口,只要符合那个插口的标准,我们都可以使用那个接口来进行充电。这就是把标准进行统一起来,然后大家就可以根据不同的标准来使用不同的接口,比如苹果手机与安卓手机的手机接口就是不一样的,他们就是两种不同的标准,安卓手机用安卓的接口,苹果用苹果的接口这就将标准统一起来。
而在Java中也是一样的,我们把这个标准或者可以说是一种公共的规范叫做接口,只要符合这一接口的标准我们就可以使用它。
听着这抽象的概念你可能现在还是不太明白这到底是什么,我在来拿代码举个例子:
class Animal { public String name; public int age; public void eat() { System.out.println(this.name+"吃饭***!"); } }
这里我定义了一个动物类,这个动物有名字,年龄,还有吃饭的行为。
class Animal { public String name; public int age; public void eat() { System.out.println(this.name+"吃饭***!"); } public void swim() { System.out.println("我要游泳~~~~"); } } class Fish extends Animal { }
接着我又定义了一个鱼类(子类)继承这个动物类(父类),我想让这个鱼有这个游泳的行为,但是这就会有一个问题,在父类定义了一个swim方法,接着我还要定义很多类,难道所有的类都要有这个游泳的行为么???答案是不可以的。所以我们就不能把这个swim方法定义在父类里面,那我们就可以把这个方法定义在子类(鱼类)里面,这样就符合了,但是如果我还要定义1000个动物都会游泳难道都要在自己类中写这个游泳的方法么??这样做显然是不可行的。那我们该怎么做呢??
我们可以就提供一个公共的接口,这也是一种标准,只要符合这一标准都可以使用这个接口或者可以说可以实现这一功能。
看了上面的引例我们应该接口是干什么的了,接下来我们来学一下接口的语法。
定义接口要使用interface关键字:
//创建一个接口 //创建接口要是用interface关键字 接口的命名最好是形容词其他的也可以 interface IFlying{ //这就是一个flying接口 }
接口中的成员变量:
这样会报错,接口中的成员变量都是常量,所以必须初始化,接口中的成员变量会被隐式指定为public static final 修饰的。
interface IFlying{ //接口中的成员变量默认都是被public static final修饰的常量 //这里的成员变量不可改变 public static final int a =10; }
接口中的成员方法:
interface IFlying{ //接口中的成员方法都是抽象方法,默认是public abstract //其中public abstract 可以被省略 //接口中的成员方法不能有具体的实现 public abstract void eat(); //一般就写成: //void eat(); default void sleep(){ //接口中的方法想要具体实现,要加上default修饰 } //接口中可以有静态方法的具体实现 public static void method() { System.out.println("我是静态的方法!!"); } }
接口能否实例化???
接口是不能被实例化的。
怎么使用接口???
实现的接口如下:
interface IFlying{ void eat(); } interface IRunning{ void run(); } interface ISwimming{ void swim(); } interface IClimbing{ void climb(); }
//创建一个Ant类 //利用implements关键字来实现接口 //一个类可以实现多个接口,接口之间利用逗号连接 //实现了接口必须要在接口中重写接口中的方法 //重写方法快捷键:鼠标移动到implements关键字上然后 alt+enter class Ant implements IClimbing,IRunning{ public String name; @Override public void run() { System.out.println(this.name+"要跑步"); } @Override public void climb() { System.out.println(this.name+"爬山"); } }
接口的使用时利用implements关键字与类连接,类与接口之间使用implements连接的。
一个类可以实现多个接口,多个接口之间利用逗号连接。
class Ant implements IClimbing,IRunning.的意思是类Ant可以实现两个功能,既可以爬又可以跑。
类实现接口时,必须要重写接口中的方法。如果不重写该类还是抽象类,要用abstract来修饰。
接口能否有静态方法和代码块呢???
接口中是不能有静态代码块和构造方法的。
我们这里总结一下类与接口之间的联系
类与类之间是继承关系利用extends来连接 代表子类继承了父类
类与接口之间是利用implements来连接, 代表类能实现某个功能
接口与接口之间也可以进行联系,利用extends 接口A和接口B interface A enxtends B 代表接口A拓展了接口B的功能。
这里来讲一下接口与接口之间的继承
我们利用extends关键字将两个接口连接起来,这样就实现了接口之间的继承。
例如:
interface IRunning extends IFlying{ //类IRunning拓展了IFlying的功能 void run(); //接口与接口之间继承后IRunning拓展了IFlying功能 //有了IRunning的功能的类也要重写IFlying这个方法 }
类IRunning拓展了IFlying的功能
接口与接口之间继承后IRunning拓展了IFlying功能
有了IRunning的功能的类也要重写IFlying这个方法
我们这里举一个例子:
比如我们要进行给一个学生进行排序,我们之前学过Arrays的sort方法,好我们来尝试一下这个方法对学生进排序。
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 + '}'; } } public class TestDemo { public static void main(String[] args) { Student[] student = new Student[4]; student[0] = new Student("张三",18,88); student[1] = new Student("李四",98,98); student[2] = new Student("王二麻子",8,18); student[3] = new Student("赵老八",58,38); System.out.println(Arrays.toString(student)); Arrays.sort(student); System.out.println(Arrays.toString(student)); } }
从这里发现Arrays.sort方法是比较具体的数字大小的,而我们这里比较学生这个对象并没有指定,我们究竟依靠什么来比较这个学生对象,目前这个学生有名字,分数,年龄,我们到底依靠什么比较是根据我们的需求来定,那我们要具体的比较学生对象的某一个学生怎么比较呢·???看报错信息也就是这个异常,我们需要提供这个comparable这个接口然后重写这个comparable方法。
怎样提供接口????
我们根据类要实现一个接口是利用关键字implements来连接的。
然后使用comparable这个接口,尖括号里面写上你要排序的类。
好这样我们就实现了这个接口,当然看前面那个红线就知道会有报错,这也就是当我们实现一个接口我们必须重写这个接口中的方法,然后Alt+enter重写这个接口中的方法。
好,我们就重写了这样的一个方法,比如我们要比较年龄按照升序排列:
然后调用Arrays.sort方法就可以进行排序了。
同样我们还可以根据名字排序:
由于,名字是String类型也就是引用类型所以我们要调用compareTo方法来进行比较。
同样的我们还可以根据分数来排序这个学生对象。
我们刚才使用comparable这个接口会有一个缺陷,就比如当我们已经实现按照年龄排序好了,但是有个人突然把他改成了名字比较,如果是未来做项目开发的时候,那就会给程序猿造成很大的困扰,就怕有一天别人修改了那段代码,所以我们就有了这个comparator这个比较器,我们还是把他封装起来,不用在去学生这个类中去修改。
对学生年龄排序:
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); } }
我们还是利用Arrays.sort方法里面再加一个比较器的这个参数就可以比较了。
public static void main(String[] args) { Student[] student = new Student[4]; student[0] = new Student("张三",18,88); student[1] = new Student("李四",98,98); student[2] = new Student("王二麻子",8,18); student[3] = new Student("赵老八",58,38); System.out.println(Arrays.toString(student)); AgeComparator ageComparator = new AgeComparator(); Arrays.sort(student,ageComparator); System.out.println(Arrays.toString(student)); }
这样我们将根据什么排序,实例化对应的对象,通过对象调用重写comparator的方法就可以进行比较,不需要担心类中被修改。
我们之前学过数组中的克隆方法,就是把一个数组中的内容全部拷贝到另外一个数组中去。
今天我们学的cloneable接口可以将一个对象的属性拷贝到另外一个对象里面去。
我们创建一个人这个类,人这个类中有两个属性,一个是分数,一个是smartphone这个对象(引用类型)。
class SmartPhone{ public int money = 9999; } class Person{ public int score = 96; SmartPhone smartPhone = new SmartPhone(); } public class TestDemo { public static void main(String[] args){ Person person1 = new Person(); } }
我们现在要将这个person这个类实现cloneable接口,利用implements连接,同样的我们要重写这个cloneable这个接口中的方法。
这里我们要注意·重写cloneable这个方法,它的返回类型是object,object是Java中所有类的父类。
好,完成了接口的操作,我们接下来完成克隆的工作,我们怎么使用clone这个方法来进行克隆呢???
//浅拷贝 class SmartPhone{ public int money = 9999; } class Person implements Cloneable{ public int score = 96; SmartPhone smartPhone = new SmartPhone(); @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "Person{" + "score=" + score + ", smartPhone=" + smartPhone + '}'; } } public class TestDemo { public static void main(String[] args) throws CloneNotSupportedException { Person person1 = new Person(); Person person2 = (Person)person1.clone(); System.out.println(person1.smartPhone.money); System.out.println(person2.smartPhone.money); person1.smartPhone.money=9; System.out.println("===============拷贝之后================="); System.out.println(person1.smartPhone.money); System.out.println(person2.smartPhone.money); } }
原因是什么呢??画一下图理解一下
这就是浅拷贝,对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此
为浅拷贝。只要将money值进行修改两个对象的money的值都改变,没有彻底的拷贝。
//深拷贝 class SmartPhone implements Cloneable{ public int money = 9999; @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "SmartPhone{" + "money=" + money + '}'; } } class Person implements Cloneable{ public int score = 96; SmartPhone smartPhone = new SmartPhone(); @Override protected Object clone() throws CloneNotSupportedException { Person tmp = (Person)super.clone(); tmp.smartPhone= (SmartPhone) this.smartPhone.clone(); return tmp; } @Override public String toString() { return "Person{" + "score=" + score + ", smartPhone=" + smartPhone + '}'; } } public class TestDemo { public static void main(String[] args) throws CloneNotSupportedException { Person person1 = new Person(); Person person2 = (Person)person1.clone(); System.out.println(person1.smartPhone.money); System.out.println(person2.smartPhone.money); person1.smartPhone.money=9; System.out.println("===============拷贝之后================="); System.out.println(person1.smartPhone.money); System.out.println(person2.smartPhone.money); } }
此时为深拷贝,进行了彻底的拷贝。对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。
The above is the detailed content of How to use Java abstract classes and interfaces. For more information, please follow other related articles on the PHP Chinese website!