本文章继续来介绍类和对象的知识。重点介绍抽象类和接口,Object类只做简单介绍。

现在,定义一个Shape类(形状类),当不同的对象去调用的时候,就会画出不同的图形,使用圆这个对象去调用,就会画出⚪来,使用三角形这个对象去调用,就会画出▲来。使用多态的思想来完成。

class Shape {public void draw() {System.out.println("画形状");}
}class Cycle extends Shape {@Overridepublic void draw() {System.out.println("画⚪;");}
}class Triangle extends Shape {@Overridepublic void draw() {System.out.println("画▲;");}
}public class test_04_07_02 {//本人的主类public static void drawMap(Shape shape) {shape.draw();} public static void main(String[] args) {Cycle cycle = new Cycle();Triangle triangle = new Triangle();drawMap(cycle);drawMap(triangle);}
}

我们发现,在Shape类中的draw()方法是不需要有具体的实现的。具体的实现可以在子类中实现。如果直接在父类中删去draw()方法的具体实现会报错。这,就引出了抽象类的概念。

一、抽象类

1、什么是抽象类

在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。比入上面的Shape类。里面的draw()方法没有什么具体的工作,它的任务都是子类来完成的。

2、抽象类语法

//抽象类
abstract class Shape {public void abstractCommonMethod() {System.out.println("抽象类普通方法");}//抽象方法public abstract void draw();
}

一个类,被abstract修饰,叫抽象类。一个方法,被abstract修饰,称为抽象方法。abstract不能修饰成员变量,同时,被abstract修饰的方法不能有具体的实现,否则会报错。

3、抽象类特点

(1)抽象类不能被实例化。

(2)一个抽象类除了加abstract关键字和类中的abstract方法不能有具体的实现外,其他的和普通类没有区别。

(3)一个普通的类继承了抽象类,需要在普通类中重写这个抽象类的所有抽象方法。否则,这个普通的类也是一个抽象类,需要用abstract来修饰。

(4)抽象类可以向上转型,进一步发生多态。

(5)一个抽象类B继承了抽象类A,可以不用重写抽象类A中的方法。

(6)当一个普通类继承了第(5)中的抽象类B,要重写所有的抽象方法。子类继承了抽象父类,抽象父类又继承了抽象父类(暂时称为爷爷类),那么这个子类要重写父类和爷爷类中的所有的抽象方法。

(7)final不能修饰abstract修饰的方法和类。final修饰类意味着不能被继承,而抽象类最大的作用就是为了被继承。

(8)抽象方法默认权限是public。不能在抽象方法中使用private权限。

(9)一个抽象类中不一定有抽象方法,但是一个方法中是抽象方法,这个类一定是抽象类。

(10)抽象类可以有构造方法,让子类创建父类的时候,初始化父类成员变量。

4、抽象类的作用

抽象类最大的作用是被继承,普通类也可以被继承。但是使用抽象类相当于多了一重的校验。Java的很多语法存在的意义是为了“预防出错”,这也是为什么Java是比较安全的原因之一。如果本来是子类完成的任务,不小心误用了父类完成,编译器是不会报错的。使用抽象的父类,编译器就会报错,让我们尽早发现问题。

二、接口

1、什么是接口

生活中的接口很常见,比如计算机上的接口、插座等等。这些接口是一种公共的行为规范标准。接口是公共的行为规范标准。大家在实现的时候,只要符合规范标准,就可以通用。Java中,接口可以看成多个类的公共规范,是一种引用数据类型。接口的出现是为了解决Java中一个类不能有多继承的情况。

2、语法

public interface 接口名称{//也可以不写public// 抽象方法
public abstract void method1();
public void method2();
abstract void method3();
void method4();
}

将class换成interface关键字,就定义了一个接口。

现在将上面的Shape类换成接口。

//接口
interface IShape {public abstract void draw();
}

3、接口特性

(1)接口中的方法只能是抽象方法。

(2)接口中的成员方法默认是public abstract,成员变量默认是public static final,而且需要初始化。

在编译器上,这里的public abstract 和 public static final是灰色的,说明它们是没有用的,可以省去。

(3)接口中的方法如果想要实现,就需要default来修饰。

(4)接口中的静态方法可以有具体的实现。

(5) 一个类想要实现接口,使用implements关键字,同时,要重写接口中的所有抽象方法。

(6)接口不能被实例化。

(7) 一个类可以实现多个接口,使用“,”来隔开。

(8)类和接口之间的关系是implements,接口和接口之间的关系是extends。

interface C extends A,B {}

表示C拓展了A和B的方法,对C实例化,要同时重写A、B和C的所有抽象方法。

(9)接口不是类,但是接口编译完成后的字节码文件后缀也是.class。

现在我们写一个Animal类,Animal类中有name属性和age属性,同时还有一个eat方法(假设给每个动物都起了名字和年龄。每一个动物都要进食)。在写一个Duck类(鸭子类),Duck会跑,会吃,会游泳,还会飞。使用封装、继承、接口来完成。

class Animal {//动物有名字和年龄protected String name;protected int age;//构造方法public Animal(String name, int age) {this.name = name;this.age = age;}//动物有进食的本能public void eat() {System.out.println(this.name + " 进食");}
}//飞
interface IFlying {void fly();
}//跑
interface IRunning {void run();
}//游泳
interface ISwimming {void swimming();
}class Duck extends Animal implements IRunning,ISwimming, IFlying {public Duck(String name, int age) {super(name, age);}@Overridepublic void fly() {System.out.println(this.name + " 在飞");}@Overridepublic void run() {System.out.println(this.name + " 在跑");}@Overridepublic void swimming() {System.out.println(this.name + " 在游泳");}
}//------------以上是类的实现者写的代码------------
//------------以下是类的调用者写的代码------------public class test_04_07_06 {//本人的主类public static void fun(Duck duck) {((IFlying) duck).fly();((ISwimming) duck).swimming();((IRunning) duck).run();duck.eat();}public static void main(String[] args) {Duck duck = new Duck("鸭子",1);duck.run();fun(duck);}
}

4、接口的实例

(1)使用Comparable实现对象数组的比较

现在写一个学生类,包括姓名和年龄,给对象数组按照年龄从小到大排序。

class Student {protected String name;protected int age;public Student(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "[" + name + ',' + age +']';}
}public class test_04_08_01 {//本人主类public static void main(String[] args) {Student[] stu = new Student[3];stu[0] = new Student("zhangsan", 16);stu[1] = new Student("lisi", 18);stu[2] = new Student("wangwu", 14);Arrays.sort(stu);System.out.println(Arrays.toString(stu));}
}

报错说学生类不能被强制转换为Comparable类。查看源码发现,它的底层实现是compareTo,所以要在学生类实现这个Compare接口,在类中重写这个方法。

这样,就能够实现对学生类进行排序。 但是这样写不够灵活。在代码中使用了年龄比较,就只能年龄比较,使用姓名比较,就只能姓名比较。能不能既可以按年龄比较,还能换成按姓名比较呢?我们采用比较器来比较。

(2)使用Comparator实现对象数组的比较

class Student {protected String name;protected int age;public Student(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "[" + name + ',' + age +']';}
}
//年龄比较器
class AgeComparator implements Comparator<Student> {@Overridepublic int compare(Student o1, Student o2) {return o1.age - o2.age;}
}
//姓名比较器
class NameComparator implements Comparator<Student> {@Overridepublic int compare(Student o1, Student o2) {return o1.name.compareTo(o2.name);}
}public class test_04_08_01 {public static void main(String[] args) {Student[] stu = new Student[3];stu[0] = new Student("zhangsan", 16);stu[1] = new Student("lisi", 18);stu[2] = new Student("wangwu", 14);AgeComparator ageComparator = new AgeComparator();Arrays.sort(stu, ageComparator);System.out.println(Arrays.toString(stu));NameComparator nameComparator = new NameComparator();Arrays.sort(stu, nameComparator);System.out.println(Arrays.toString(stu));}
}

(3)实现对象数组的拷贝

现在有这么一个想法,把其中一个学生对象给复制拷贝一下,能不能办到呢?数组是可以使用Arrays.clone()方法来实现数组的拷贝的,实例化的对象数组该如何拷贝?

class Student {public String name;public int age;public Student(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}
}public class ClassStudent {public static void main(String[] args) {Student[] students = new Student[3];students[0] = new Student("zhangsan", 18);}
}

在克隆的时候,首先,这个对象要能够被拷贝,也就是说,这个Student类要实现一个Cloneable接口。这个接口是一个空的接口,也叫标记接口,表示当前的类能够被拷贝。

根据前面个两个接口的经验,在这里,我们还是要重写一个方法clone()方法。

重写后的方法返回类型是一个Object类,它是所有类的父类。那么这里的students[0]就相当于是students[1]的父类,把父类给子类了,所以要进行强制类型转换。

在打印studets[1],就可以看见它克隆了students[0]。这个拷贝的students[1]是students[0]的副本。现在我又想写一个Money类,表示一个学生一个月的零花钱(假设是100元),在学生类中实例化这个Money类,然后进行拷贝。

class Money {public int money = 100;
}class Student implements Cloneable {public String name;public int age;public Money m = new Money();public Student(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Student{" +"name=" + name + '\'' +", age=" + age +'}';}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}

对students[0]的m重新赋值,在打印students[0]和students[1]看看情况。

发现,改了students[0]的,拷贝过去却连students[1]的也改了。这种拷贝实际上是一种浅拷贝。

从这个大致的内存引用图可以看出来,要让students[1]的m不指向students[0]的m,也要对Money类实现cloneable接口,然后重写Money类中的clone()方法。

①中,这个super.clone()表示用Object类的Clone()方法克隆出来一个副本,然后用tmp来接收。这个时候,虽然克隆拷贝出来了students[0]的副本,但是这个副本的m还是指向students[0]的m。通过②就可以在students[1]中克隆一份新的m(this是students[0]),然后给tmp中的m,这个过程需要进行强制类型转换。最后,返回的tmp被students[1]接收,让students[1]引用指向tmp指向的对象。

这样,就实现了深拷贝。实现深拷贝,并没有使用什么特定的深拷贝方法,而是在代码逻辑上去思考的,出现引用的地方,这个对象的引用所指的对象也进行了重写clone方法。

5、抽象类和接口区别

三、Object类

前面在clone()方法中我们说过,Object是所有的类的父类,所有的类都继承了Object类。所以在使用一些方法的时候,需要子类里面重写方法,比如打印对象的toString方法。子类是继承了Object类里toString方法,但是Object类的toString方法是打印这个类实例化对象的引用的哈希值。想要用这个方法来打印引用的内容,就需要重写Object类中的toString方法。

可以使用Object类来接收所有的数组类型,比如类,数组,接口等。

Java 类和对象——抽象类、接口、Object类相关推荐

  1. java 抽象类 普通类_java的抽象类,接口,普通类是否可以直接NEW,并且复写方法?

    我看那个内部匿名类的写法(代码1),接口可以直接new,代码2中抽象类可以直接NEW,那是否可以直接 new 一个普通类然后复写其中方法比如代码3? 代码1 interface Person { pu ...

  2. JAVA day13,14 API、Object类、日期时间类(long,Date,Calendar,DateFormat)、String类(字符串,可变长字符串)、正则表达式、包装类

    1.API API(Application Programming Interface),应⽤程序编程接⼝.Java API是⼀本程序员的"字 典",是JDK提供给我们使⽤的类的说 ...

  3. Kotlin学习之路(4)——类,对象和接口

    类,对象和接口 之前的篇章已经简单的介绍过如何声明一个类,也知道了一些简单的主构造方法来声明方法和属性,以及使用枚举类. 类和接口 接口 和Java相同的是,我们同样用interface来定义一个接口 ...

  4. Java面试题:final和Object类常见的方法

    final 关键字的理解 final关键字主要用在三个地方:变量.方法.类. 对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改:如果是引用类型的变量,则在对其初始化之 ...

  5. Java核心技术第五章——2.Object类

    Object类:所有类的超类 Object类是Java中所有类的始祖,在Java中每个类都是由它扩展而来的.但是并不需要这样写: public class Emloyee extends Object ...

  6. Kotlin 学习笔记(八)—— Kotlin类与对象之接口

    Kotlin 学习笔记(八)-- Kotlin类与对象之接口 Kotlin学习笔记系列教程 Kotlin 学习笔记(一)-- 概述.学习曲线.开发工具.参考资料 Kotlin 学习笔记(二)-- 基础 ...

  7. JAVA类与对象(三)----类定义关键字详解

    static 表示静态,它可以修饰属性,方法和代码块. 1.static修饰属性(类变量),那么这个属性就可以用类名.属性名来访问,也就是使这个属性成为本类的类变量,为本类对象所共有.这个属性就是全类 ...

  8. Object类 任何类都是object类的子类 用object对象接收数组 object类的向上向下转型...

    任何类都是object类的子类 用object对象接收数组 object类的向上向下转型 转载于:https://www.cnblogs.com/qingyundian/p/7744351.html

  9. Java基础知识强化26:Object类之hashCode()方法、getClass()方法

    1. Object类的hashCode()方法,如下: public  int  hashCode():返回该对象的哈希码值,这个值和地址值有关,但是不是实际地址值(哈希码值是根据实际地址值转化过来的 ...

  10. python 类继承object_python-面向对象-17-继承/object类/mro方法

    1.继承的基本概念 继承是面向对象程序设计的重要特征,也是实现"代码复用"的重要手段. 如果一个新类继承自一个设计好的类,就直接具备已有类的特征,这样就大大降低了工作难度,因为很多 ...

最新文章

  1. 在Lucene.net实现自定义排序
  2. RocketMQ错误消息重试策略之Consumer的重试机制(Exception情况)
  3. YbtOJ#883-最大的割【带修线性基】
  4. mysql偏差聚集_My SQL聚合函数
  5. SQL中 left join 左表合并去重实用技巧
  6. 关于Win32 DialogBox的一些收获
  7. Java Base64与图片互转操作测试
  8. android 微信支付:统一下单接口获取
  9. BFD和NQA技术原理
  10. H5设备运动事件 DeviceMotionEvent 实现摇一摇功能
  11. 1158: 零基础学C/C++158——删除中间的*
  12. qs计算机圣安排名,圣安德鲁斯大学世界排名及专业排名汇总(QS世界大学排名版)...
  13. 【SpringBoot整合Dubbo和Zookeeper】
  14. 【fread/fwrite】C语言API之fread/fwrite函数详解
  15. 大数据系列 之 学习准备
  16. C语言编程练手必备,C 语言快速实现五子棋!
  17. 利用C#脚本来处理Excel
  18. 2018计算机考试报名官网,2018年上半年全国计算机等级考试报名系统入口地址
  19. 支付宝公布2019集五福攻略,准备好薅羊毛的正确姿势...
  20. springboot的html页面放哪,SpringBoot跳转到静态html页面静态文件放置位置

热门文章

  1. 如何在三个月内成为一名全栈开发
  2. Wwise EBP管线
  3. Android_推送技术研究
  4. FingerprintJS 浏览器端实现指纹识别
  5. 09_ue4进阶末日生存游戏开发[动画重定向]
  6. Python以及app 的安装
  7. 从日志文件解决ArcGIS Server性能低下问题的步骤(1)
  8. Android黄油刀使用小记
  9. 中国三氧化钼市场现状调研与前景预测分析报告2022-2028年
  10. 删除重复的数据保留一条