在了解抽象类之前我们要先知道什么是普通类。普通类具有完整的类功能,不但可以直接产生实例化对象,而且在普通类中可以包含有构造方法、普通方法、static(静态)方法、常量和变量等内容。而抽象类是指在普通类的结构里面增加抽象方法的组成部分。
那么什么叫抽象方法呢?在所有的普通方法上面都会有一个“{}”,这个表示方法体,有方法体的方法一定可以被对象直接使用。而抽象方法,是指没有方法体的方法,同时抽象方法还必须使用关键字abstract做修饰。而拥有抽象方法的类就是抽象类,抽象类要使用abstract关键字声明。
**注意:abstract修饰符
1.abstract修饰的类为抽象类,此类不能有对象,(无法对此类进行实例化,说白了就是不能new);
2.abstract修饰的方法为抽象方法,此方法不能有方法体(就是什么内容不能有);
范例:定义一个抽象类
abstract class student{//定义一个抽象类
public void sleep(){//普通方法
System.out.println("我喜欢学习");
}
public abstract void play();//抽象方法,没有方法体,有abstract关键字做修饰
}
在这里我们先看一个例子:
package Work;
abstract class Student{//定义一个抽象类
public void sleep(){//普通方法
System.out.println("学生喜欢学习");
}
public abstract void paly();//抽象方法,没有方法体,有abstract关键字做修饰
}
public class Work11_111 {
public static void main(String[] args) {
Student Me= new Student();
}
}
运行结果:
由此可知,Student是抽象的,无法直接进行实例化操作。什么是实例化?当一个类实例化之后,就意味着这个对象可以调用类中的属性或者方法,但在抽象类中存在抽象方法,而抽象方法没有方法体,没有方法体就无法进行调用。既然无法进行方法调用的话,自然无法产生实例化对象。
进而·可以得到抽象类的使用原则:
(1)抽象类不能直接使用,需要子类去实现抽象类,然后使用其子类的实例。(需要依靠子类采用向上转型的方式处理)
(2)抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public;
(3)抽象类必须有子类,使用extends继承,一个子类只能继承一个抽象类;
(4)子类(如果不是抽象类)则必须覆写抽象类之中的全部抽象方法(如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类。);
*注:
重写范例:
package Work;
abstract class Student{//定义一个抽象类
public void sleep(){//普通方法
System.out.println("学生喜欢学习");
}
public abstract void paly();//抽象方法,没有方法体,有abstract关键字做修饰
}
class People extends Student{
@Override
public void paly() {
System.out.println("我喜欢学习");
}
}
public class Work11_111 {
public static void main(String[] args) {
Student Me= new People();
Me.paly();
Me.sleep();
}
}
运行结果如下:
我喜欢学习
学生喜欢学习
1)抽象类不可以用final声明:因为抽象类必须有子类,而final定义的类不能有
子类。
2)抽象类中存在有构造方法:抽象类里会存在一些属性,所以抽象类中一定存在构造方法,其存在目的是为了属性的初始化。并且子类对象实例化的时候,依然满足先执行父类构造,再执行子类构造的顺序。
3)抽象类能否使用static声明?
先看一个关于外部抽象类的范例:
package Work;
static abstract class Student{//定义一个抽象类
public void sleep(){//普通方法
System.out.println("学生喜欢学习");
}
public abstract void paly();//抽象方法,没有方法体,有abstract关键字做修饰
}
class People extends Student{
@Override
public void paly() {
System.out.println("我喜欢学习");
}
}
public class Work11_111 {
public static void main(String[] args) {
Student Me= new People();
Me.paly();
Me.sleep();
}
}
package Work;
abstract class Student{//定义一个抽象类
static abstract class B{//static定义的内部类属于外部类
public abstract void play();
}
//抽象方法,没有方法体,有abstract关键字做修饰
}
class People extends Student.B{
@Override
public void play() {
System.out.println("我喜欢学习");
}
}
public class Work11_111 {
public static void main(String[] args) {
Student.B Me= new People();
Me.play();
}
}
运行结果:
我喜欢学习
由此可见,外部抽象类不允许使用static声明,而内部的抽象类运行使用static声明。使用static声明的内部抽象类相当于一个外部抽象类,继承的时候使用“外部类.内部类”的形式表示类名称。
Java里面由于不允许多重继承,所以如果要实现多个类的功能,则可以通过实现多个接口来实现。接口可以看作是抽象类的变体,接口中所有的方法都是抽象的。接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
我们使用interface关键字定义接口,一般使用接口声明方法或常量,接口中的方法只能是声明,不能是具体的实现,这一点和抽象类是不一样的。接口是更高级别的抽象。接口的定义格式是:
public interface 接口名称{
//可以定义常量
//方法只有方法声明,而且是公共的。
public void 方法名称();
...
}
类要实现接口,只需要使用implements关键字,实现类必须要实现接口中的所有的方法
public class 实现类名 implements 接口{
//实现接口的方法
}
示例代码如下:
// 定义方法的接口
public interface People {
// 定义程序使用的常量的接口,接口中只能有常量。
public static final double count = 1314.00;
public static final int age = 5;
//接口中所有的方法都没有方法体。
public void add(int x, int y);
public void volume(int x,int y, int z);
}
代码如下:
//实现 接口
public class Student implements People {
@Override
public void add(int x, int y) {
}
@Override
public void volume(int x, int y, int z) {
}
}
一个类是可以实现多个接口,因为java是单继承的,这点接口可以弥补。我们可以再定义一个接口,如下:
public interface People2 {
public void num();
}
修改上面的实现类,要实现多个接口,可以使用逗号隔开,当然所有的接口的方法都要实现。
//实现 接口1,接口2
public class Student implements People ,People2{
@Override
public void add(int x, int y) {
}
@Override
public void volume(int x, int y, int z) {
}
@Override
public void num() {
}
}
代码如下:
package Work;
interface Computer{
int count(int n,int m);
}
//加法
class Add implements Computer {
@Override
public int count(int n, int m) {
return n + m;
}
}
//减法
class Sub implements Computer {
@Override
public int count(int n, int m) {
return n - m;
}
}
//除法
class Div implements Computer{
@Override
public int count(int n, int m) {
if(m!=0){
return n/m;
}
else {
System.out.println("分母不能为0");
return 0;
}
}
}
//乘法
class Mul implements Computer{
@Override
public int count(int n, int m) {
return n*m;
}
}
//接口是不能直接实例化的,需要定义一个实现类,将接口的引用作为example的参数,利用Realize类实现接口。
class Realize{
public void example(Computer s,int n, int m){
System.out.println(s.count(n,m));
}
}
public class TestDemo11_2 {
public static void main (String[] args){
Realize s=new Realize();
s.example(new Add(),1,2);
s.example(new Sub(),1,2);
s.example(new Div(),4,2);
s.example(new Mul(),1,2);
}
}
1)抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
2)抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
3)接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
4)一个类只能继承一个抽象类,而一个类却可以实现多个接口。
1)不能够new创建对象
2)如果要去创建一个接口对应的对象,则需要通过它的具体实现类, 使用implements关键字去实现接口 。
3) 接口被编译之后也会产生对应的字节码文件
4)接口之间也是可以相互继承 extends
5) 接口对应是多实现 eg.class C implements A,B
6) 抽象类中设置的都是通用的方法,功能与实体都是一种"是"的关系,接口中功能与实体都是一种"有"的关系
7)接口中定义的属性为什么是static final的?
static 原因主要是接口的实现是多实现,为了区分不同接口当中的相同变量
final 原因是因为如果是变量,接口的存在就失去其意义,同时接口来说指的 是统一的协议,Java设计者直接规定接口中的属性只能是public static final的。
8)接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
*注意:内部比较器位于 java.lang包下
下面看看Comparable接口的源码:
public interface Comparable<T>
{
public int compareTo(T o);
1)如果此对象(调用比较器方法的对象)大于指定对象(目标比较对象),返回正整数
2)如果此对象小于指定对象,返回负整数
3)如果此对象等于指定对象,返回零
}
Comparable接口的使用:
package Work;
class Student implements Comparable<Student>{
private String name;
private int age;
private float score;//得分
public Student(String name, int age, float score) {
this.name = name;
this.age = age;
this.score = score;
}
public String toString()
{
return name+"\t\t"+age+"\t\t"+score;
}
@Override
public int compareTo(Student o) {
if(this.score>o.score)//score是private的,为什么能够直接调用,这是因为在Student类内部
return -1;//由高到底排序
else if(this.score<o.score)
return 1;//当后一个对象比当前对象大,返回结果值为1时,前后交换,说明是倒序排列。(从高到低)
else{
if(this.age>o.age)
return 1;//当后一个对象比当前对象小,返回结果值为1时,前后交换,说明是升序排列。(从低到高)
else if(this.age<o.age)
return -1;
else
return 0;
}
}
}
public class Blog {
/**
* @name 博客
* 先比较score在比较age
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Student stu[]={new Student("zhangsan",20,90.0f),
new Student("lisi",22,90.0f),
new Student("wangwu",20,99.0f),
new Student("sunliu",22,100.0f)};
java.util.Arrays.sort(stu);//Java的Arrays类中有一个sort()方法,该方法是Arrays类的静态方法,在需要对数组进行排序时,可以直接调用。
for(Student s:stu)
{
System.out.println(s);
}
}
}
运行结果为:
sunliu 22 100.0
wangwu 20 99.0
zhangsan 20 90.0
lisi 22 90.0
注意:Comparator位于包java.util下。
下面看看Comparator接口的源码:
package java.util;
public interface Comparator<T> {
int compare(T o1, T o2);
1)如果o1大于o2,则返回正整数;
2)如果o1小于o2,则返回负整数
3)如果o1等于o2,则返回零
}
Comparator的使用:
package Work;
import java.util.*;
class Student {
private String name;
private int age;
private float score;
public Student(String name, int age, float score) {
this.name = name;
this.age = age;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public float getScore() {
return score;
}
public void setScore(float score) {
this.score = score;
}
public String toString()
{
return name+"\t\t"+age+"\t\t"+score;
}
}
class StudentComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
// TODO Auto-generated method stub
if(o1.getScore()>o2.getScore())
return -1;
else if(o1.getScore()<o2.getScore())
return 1;//当后一个对象比当前对象大,返回结果值为1时,前后交换,说明是倒序排列。(从高到低)
else{
if(o1.getAge()>o2.getAge())
return 1;//当后一个对象比当前对象小,返回结果值为1时,前后交换,说明是升序排列。(从低到高)
else if(o1.getAge()<o2.getAge())
return -1;
else
return 0;
}
}
}
public class Blog {
public static void main(String[] args) {
// TODO Auto-generated method stub
Student stu[]={new Student("zhangsan",20,90.0f),
new Student("lisi",22,90.0f),
new Student("wangwu",20,99.0f),
new Student("sunliu",22,100.0f)};
java.util.Arrays.sort(stu,new StudentComparator());
for(Student s:stu)
{
System.out.println(s);
}
}
}
运行结果如下:
sunliu 22 100.0
wangwu 20 99.0
zhangsan 20 90.0
lisi 22 90.0
1)Comparator位于包java.util下,而Comparable位于包 java.lang下
2)Comparable 是一个对象本身就已经支持自比较所需要实现的接口(如 String、Integer 自己就可以完成比较大小操作,已经实现了Comparable接口)而 Comparator 是一个专用的比较器,当这个对象不支持自比较或者自比较函数不能满足你的要求时,你可以写一个比较器来完成两个对象之间大小的比较。
可以说一个是自已完成比较,一个是外部程序实现比较的差别而已。
Java的Arrays类中有一个sort()方法,该方法是Arrays类的静态方法,在需要对数组进行排序时,可以直接调用。具体使用方法可以参照下面这篇文章。
https://blog.csdn.net/weixin_43761659/article/details/97442067Arrays.sort()用法理解
如果一个类实现了Cloneable接口,就表示该类中的对象是可以被clone的,同时实现Cloneable则需要重写Object.clone方法。
具体实现可以参考如下文章:
https://blog.csdn.net/qq_37113604/article/details/81168224?Cloneable接口的作用与深入理解深度克隆与浅度克隆
面试题:为什么clone方法在Object类中定义为protected,而不是public?