Java 类和对象——抽象类、接口、Object类
本文章继续来介绍类和对象的知识。重点介绍抽象类和接口,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类相关推荐
- java 抽象类 普通类_java的抽象类,接口,普通类是否可以直接NEW,并且复写方法?
我看那个内部匿名类的写法(代码1),接口可以直接new,代码2中抽象类可以直接NEW,那是否可以直接 new 一个普通类然后复写其中方法比如代码3? 代码1 interface Person { pu ...
- JAVA day13,14 API、Object类、日期时间类(long,Date,Calendar,DateFormat)、String类(字符串,可变长字符串)、正则表达式、包装类
1.API API(Application Programming Interface),应⽤程序编程接⼝.Java API是⼀本程序员的"字 典",是JDK提供给我们使⽤的类的说 ...
- Kotlin学习之路(4)——类,对象和接口
类,对象和接口 之前的篇章已经简单的介绍过如何声明一个类,也知道了一些简单的主构造方法来声明方法和属性,以及使用枚举类. 类和接口 接口 和Java相同的是,我们同样用interface来定义一个接口 ...
- Java面试题:final和Object类常见的方法
final 关键字的理解 final关键字主要用在三个地方:变量.方法.类. 对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改:如果是引用类型的变量,则在对其初始化之 ...
- Java核心技术第五章——2.Object类
Object类:所有类的超类 Object类是Java中所有类的始祖,在Java中每个类都是由它扩展而来的.但是并不需要这样写: public class Emloyee extends Object ...
- Kotlin 学习笔记(八)—— Kotlin类与对象之接口
Kotlin 学习笔记(八)-- Kotlin类与对象之接口 Kotlin学习笔记系列教程 Kotlin 学习笔记(一)-- 概述.学习曲线.开发工具.参考资料 Kotlin 学习笔记(二)-- 基础 ...
- JAVA类与对象(三)----类定义关键字详解
static 表示静态,它可以修饰属性,方法和代码块. 1.static修饰属性(类变量),那么这个属性就可以用类名.属性名来访问,也就是使这个属性成为本类的类变量,为本类对象所共有.这个属性就是全类 ...
- Object类 任何类都是object类的子类 用object对象接收数组 object类的向上向下转型...
任何类都是object类的子类 用object对象接收数组 object类的向上向下转型 转载于:https://www.cnblogs.com/qingyundian/p/7744351.html
- Java基础知识强化26:Object类之hashCode()方法、getClass()方法
1. Object类的hashCode()方法,如下: public int hashCode():返回该对象的哈希码值,这个值和地址值有关,但是不是实际地址值(哈希码值是根据实际地址值转化过来的 ...
- python 类继承object_python-面向对象-17-继承/object类/mro方法
1.继承的基本概念 继承是面向对象程序设计的重要特征,也是实现"代码复用"的重要手段. 如果一个新类继承自一个设计好的类,就直接具备已有类的特征,这样就大大降低了工作难度,因为很多 ...
最新文章
- 在Lucene.net实现自定义排序
- RocketMQ错误消息重试策略之Consumer的重试机制(Exception情况)
- YbtOJ#883-最大的割【带修线性基】
- mysql偏差聚集_My SQL聚合函数
- SQL中 left join 左表合并去重实用技巧
- 关于Win32 DialogBox的一些收获
- Java Base64与图片互转操作测试
- android 微信支付:统一下单接口获取
- BFD和NQA技术原理
- H5设备运动事件 DeviceMotionEvent 实现摇一摇功能
- 1158: 零基础学C/C++158——删除中间的*
- qs计算机圣安排名,圣安德鲁斯大学世界排名及专业排名汇总(QS世界大学排名版)...
- 【SpringBoot整合Dubbo和Zookeeper】
- 【fread/fwrite】C语言API之fread/fwrite函数详解
- 大数据系列 之 学习准备
- C语言编程练手必备,C 语言快速实现五子棋!
- 利用C#脚本来处理Excel
- 2018计算机考试报名官网,2018年上半年全国计算机等级考试报名系统入口地址
- 支付宝公布2019集五福攻略,准备好薅羊毛的正确姿势...
- springboot的html页面放哪,SpringBoot跳转到静态html页面静态文件放置位置