目录
- 1.抽象类
- 1.1什么是抽象类
- 1.2抽象类语法
- 1.3抽象类与普通类的区别
- 2.抽象类的特性
- 2.1 特性
- 2.2抽象类的作用
- 3.接口
- 3.1什么是接口
- 3.2语法规范
- 3.3接口的使用
- 3.4接口的特性
- 3.5 实现多个接口
- 3.6 接口的继承
- 4.接口的实例
- 5.Clonable 接口和深拷贝
- 5.1 浅拷贝
- 5.2深拷贝
- 6. 抽象类和接口的区别
1.抽象类
1.1什么是抽象类
在我们Java面对对象的概念中,所有的对象都是通过类来描述的,但是有一些对象不能通过类来描述。
如果一个类中没有足够的系信息去描绘一个具体的对象,这个类就是抽象类。
说明:
1.Animal是动物类,每个动物都有叫的方法,但由于Animal不是一个具体的动物,因此其内部eat()方法无法具体实现
2.Dog是狗类,首先狗是动物,因此与Animal是继承关系,其次狗是一种具体的动物,狗叫:汪汪汪,其eat()可以实现
3.Cat是猫类,首先猫是动物,因此与Animal是继承关系,其次猫是一种具体的动物,猫叫:崛瞄瞄,其eat()可以实现
4.因此:Animal可以设计为“抽象类"
我们可以看出来,父类的eat()方法,并不是一个实际的工作,主要的各种子类的eat()方法来实现的,像这种没有实际工作的方法,我们一般可以设为一个抽象方法。包含抽象方法的类称为抽象类。
1.2抽象类语法
如果一个类,被abstract修饰,那么这个类就是一个抽象类,这个类中,被abstract修饰的方法,称为抽象方法。
注意:抽象类也是类,内部可以包含普通方法和属性,甚至构造方法
abstract class A
{
abstract public void func();
}
class B extends A
{
@Override
public void func() {
System.out.println("B的方法");
}
}
public class Test {
public static void main(String[] args) {
A a=new B();
a.func();
}
}
1.3抽象类与普通类的区别
1.抽象类不能实例化,普通类可以实例化
2.抽象类中,可以包含抽象方法和普通方法,但是普通类只能包含非抽象方法。
2.抽象类的特性
2.1 特性
1.抽象类使用abstract修饰类
2.抽象类不能被实例化。
3.此时在抽象类当中,可以有抽象方法,或者非抽象方法!
4.什么是抽象方法,一个方法被abstract修饰,没有具体的实现。只要包含抽象方法,这个类必须是抽象类!
5.当一个普通类继承了这个抽象类,必须重写抽象类当中的抽象方法!
6.抽象类存在的最大的意义,就是为了被继承!
7.抽象方法不能被private , final , static修饰。所以一定要满足方法重写的规则!!
8.当一个子类没有重写抽象的父类的方法,可以把当前子类变为abstract修饰。
9.抽象类中,不一定包含抽象方法
2.2抽象类的作用
抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法.
确实如此. 但是使用抽象类相当于多了一重编译器的校验.
使用抽象类的场景就如上面的代码, 实际工作不应该由父类完成, 而应由子类完成. 那么此时如果不小心误用成父类了, 使用普通类编译器是不会报错的. 但是父类是抽象类就会在实例化的时候提示错误, 让我们尽早发现问题.
3.接口
3.1什么是接口
接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用。
在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型
3.2语法规范
我们接口的定义跟类差不多,就是把class换位interface关键字,就可以定义了一个接口。
interface IShape{
public int age=2;
public String name="1";
//默认是这个
public static final String sex="1";
//方法默认这个
public abstract void draw();
//也可以
default public void func()
{
System.out.println("66666666");
}
public static void staticfunc()
{
System.out.println("staticfunc");
}
}
注意:
- 创建接口时, 接口的命名一般以大写字母 I 开头.
- 接口的命名一般使用 “形容词” 词性的单词.
- 阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性.
3.3接口的使用
接口不能直接的使用,必须要有一个类来实现这个接口,实现接口中的抽象方法
子类和父类之间是extends 继承关系,类与接口之间是 implements 实现关系。
public class 类名称 implements 接口名称{
// ...
}
public class Mouse implements USB{
@Override
public void openDevice() {
System.out.println("打开鼠标");
}
@Override
public void closeDevice() {
System.out.println("关闭鼠标");
}
public void click()
{
System.out.println("点击鼠标");
}
}
3.4接口的特性
大家如果想用这个东西你必须实现一下我的规范。
1.使用关键字interface来定义接口
2.接口不能被实例化
3.接口当中的成员默认是public static final
4.接口当中的方法不写也是默认为public abstract的
5.接口当中的方法不能有具体的实现,但是从JDK8开始可以写一个default修饰的方法
6.接口当中不能有构造方法
7.接口需要被类实现,使用关键字implements
8.接口当中可以有static修饰的方法!
9. 重写接口中方法时,不能使用默认的访问权限
10.接口中不能有静态代码块和构造方法
3.5 实现多个接口
在Java中,类和类之间是单继承的,一个类只能有一个父类,即Java中不支持多继承,但是一个类可以实现多个接口。
下面通过类来表示一组动物.。
并且在定义几个接口表示跑,游泳,飞等等。
接下来我们创建几个具体的动物,具体实现这几个接口
注意:一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类。
abstract class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
}
interface IRunning {
void run();
}
interface ISwimming {
void swim();
}
interface IFly {
void fly();
}
class Dog extends Animal implements IRunning {
public Dog(String name) {
super(name);
}
@Override
public void run() {
System.out.println(name +" 正在用四条狗腿跑!");
}
}
class Fish extends Animal implements ISwimming{
public Fish(String name) {
super(name);
}
@Override
public void swim() {
System.out.println(name + "正在游泳!");
}
}
class Bird extends Animal implements IFly {
public Bird(String name) {
super(name);
}
@Override
public void fly() {
System.out.println(name + "正在飞!");
}
}
class Duck extends Animal implements IRunning,IFly,ISwimming {
public Duck(String name) {
super(name);
}
@Override
public void run() {
System.out.println(name + "正在用两条腿跑!");
}
@Override
public void swim() {
System.out.println(name + "正在用两条腿在游泳!");
}
@Override
public void fly() {
System.out.println(name + "正在用翅膀飞!");
}
}
class Robot implements IRunning{
@Override
public void run() {
System.out.println("机器人在用机器腿跑步!");
}
}
public class Test4 {
/**
* 只要实现了 IRunning 接口的 都可以接收
* @param iRunning
*/
public static void walk(IRunning iRunning) {
iRunning.run();
}
public static void swim(ISwimming iSwimming) {
iSwimming.swim();
}
/**
* 一定是一个动物
* @param animal
*/
public static void func(Animal animal) {
}
public static void main(String[] args) {
walk(new Dog("旺财"));
walk(new Duck("唐老鸭"));
walk(new Robot());
System.out.println("======");
swim(new Fish("七秒"));
swim(new Duck("唐老鸭2号"));
}
}
这样设计有什么好处呢? 时刻牢记多态的好处, 让程序猿忘记类型. 有了接口之后, 类的使用者就不必关注具体类型,而只关注某个类是否具备某种能力.
3.6 接口的继承
在Java中,类和类之间是单继承的,一个类可以实现多个接口,接口与接口之间可以多继承。即:用接口可以达到多继承的目的。
接口可以继承一个接口, 达到复用的效果. 使用 extends 关键字.
interface IRunning {
void run();
}
interface ISwimming {
void swim();
}
// 两栖的动物, 既能跑, 也能游
interface IAmphibious extends IRunning, ISwimming {
}
class Frog implements IAmphibious {
...
}
4.接口的实例
当我们定义了一个Stydent类,里面定义了两个属性,一个姓名,一个年纪。
当我们 new 了三个对象的时候,我们想给他排序,这应该怎么办?当我们只用原本的sort()方法会发现报错。
Exception in thread "main" java.lang.ClassCastException: Student cannot be cast to java.lang.Comparable
那么我们应该怎么办?当我们的对象里面有两个属性,这个时候我们不会知道按照那个属性去排序,就会出现错误。
所以我们就要重写方法,让这个方法按照我们想排序的属性去进行比较。
package demo3;
import java.util.Arrays;
import java.util.Comparator;
class Student
{
public String name;
public int age;
public int score;
public Student(String name, int age, int 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 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 test {
public static void main(String[] args) {
Student[] students=new Student[3];
students[0]=new Student("zhangsan",10,10);
students[1]=new Student("lisi",20,20);
students[2]=new Student("wangwu",30,30);
Agecomparator getcomparator=new Agecomparator();
Arrays.sort(students,getcomparator);
System.out.println(Arrays.toString(students));
System.out.println("===========");
Scorecomparator scorecomparator=new Scorecomparator();
Arrays.sort(students,scorecomparator);
System.out.println(Arrays.toString(students));
System.out.println("===============");
Namecomparator namecomparator=new Namecomparator();
Arrays.sort(students,namecomparator);
System.out.println(Arrays.toString(students));
}
}
5.Clonable 接口和深拷贝
Java 中内置了一些很有用的接口, Clonable 就是其中之一.
Object 类中存在一个 clone 方法, 调用这个方法可以创建一个对象的 “拷贝”. 但是要想合法调用 clone 方法, 必须要先实现 Clonable 接口, 否则就会抛出 CloneNotSupportedException 异常
5.1 浅拷贝
class Money
{
public double money=12.25;
}
class Student implements Cloneable
{
public String name;
public Money m=new Money();
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class test {
public static void main(String[] args) throws CloneNotSupportedException {
Student student=new Student();
student.name="123";
Student student1=(Student) student.clone();
System.out.println(student);
System.out.println(student1);
System.out.println("==========");
student1.m.money=99;
System.out.println(student.m.money);
System.out.println(student1.m.money);
}
}
如上代码,我们可以看到,通过clone,我们只是拷贝了Student对象。但是Student对象中的Money对象,并
没有拷贝。通过Student1这个引用修改了m的值后,Student1这个引用访问m的时候,值也发生了改变。这里
就是发生了浅拷贝。
5.2深拷贝
package demo4;
class Money implements Cloneable
{
public double money=12.25;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Student implements Cloneable
{
public String name;
public Money m=new Money();
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Student student=(Student) super.clone();
student.m=(Money)this.m.clone();
return student;
//return super.clone();
}
}
public class test {
public static void main(String[] args) throws CloneNotSupportedException{
Student student=new Student();
Student student1=(Student) student.clone();
System.out.println(student.m.money);
System.out.println(student1.m.money);
System.out.println("=============");
student1.m.money=99;
System.out.println(student.m.money);
System.out.println(student1.m.money);
}
public static void main1(String[] args) throws CloneNotSupportedException {
Student student=new Student();
student.name="123";
Student student1=(Student) student.clone();
System.out.println(student);
System.out.println(student1);
}
}
如上代码,我们可以看到,通过clone,我们只是拷贝了Student对象。并且Student对象中的Money对象也进行了拷贝。通过Student1这个引用修改了m的值后,Student个引用访问m的时候,值也发生了改变。这里
就是发生了深拷贝。
6. 抽象类和接口的区别
核心区别: 抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中不能包含普通方法,子类必须重写所有的抽象方法.
如之前写的 Animal 例子. 此处的 Animal 中包含一个 name 这样的属性, 这个属性在任何子类中都是存在的. 因此此处的 Animal 只能作为一个抽象类, 而不应该成为一个接口。