在面向对象的设计过程中,类是基本的逻辑单位。但是对于这些基本的逻辑单位需要考虑到重用的设计问题,所以在面向对象的设计里提供有继承,并利用这一特点实现类的可重用性定义。
类继承定义
严格来讲,继承性是指扩充一个类已有的功能。Java中,如果要实现继承的关系,可以使用以下的语法完成。
class 子类 extends 父类 {}
例如:学生是一个人类,人类的基本属性有名字和年龄,学生类除了有人类的基本属性外,还有学校和班级。
class Person {
private String name;
private int age;
public void setName(String name){
this.name = name;
}
public String getName() {
return this.name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
}
class Student extends Person {
private String school;
private String grades;
public void setSchool(String school) {
this.school = school;
}
public String getSchool() {
return this.school;
}
public void setGrades(String grades) {
this.grades = grades;
}
public String getGrades() {
return this.grades;
}
}
class JavaDemo {
public static void main(String[] args) {
Student stu = new Student();
stu.setName('刘备');
stu.setAge(34);
stu.setSchool('三国');
stu.setGrades('蜀');
System.out.println('姓名:' + stu.getName +',年龄:' + stu.getAge + ',学校:' + stu.getSchool + ',班级:')
}
}
子类对象实例化流程
在继承结构中,子类需要重用父类中的结构,所以在进行子类对象实例化之前往往都会默认调用父类中的无参构造方法,为父类对象实例化(属性初始化),而后再进行子类结构调用,为子类对象实例化(属性初始化)。
class Person {
public Person() {
System.out.println('【Person父类】构造方法')
}
}
class Student extends Person {
public Student() {
System.out.println('【Student子类】构造方法')
}
}
class JavaDemo {
public static void main(String[] args) {
Student stu = new Student();
}
}
// 程序执行结果
// 【Person父类】构造方法
// 【Student子类】构造方法
通过运行程序可以得出结论:子类对象实例化前一定会实例化父类对象,实际上这个时候就相当于子类的构造方法里隐含了一个super()的形式。
class Student extends Person {
public Student() {
super();
System.out.println('【Student子类】构造方法')
}
}
继承限制
继承是类重用的一种重要手段,而在Java中针对类继承的合理性设置了相关限制。
- 一个子类只能继承一个父类,存在单继承局限。
- 在一个子类继承的时候,实际上会继承父类的所有操作(属性、方法),但是需要注意的是,对于所有的非私有操作属于显示继承(可以直接利用对象操作),而所有的私有操作属于隐式继承(简介完成)
覆写
在继承关系中,父类作为最基础的类存在,其定义的所有结构都是为了完成本类的需求而设计的,但是在很多时候由于某些特殊的需要,子类有可能会定义与父类名称相同的方法或属性,此类情况在面向对象设计中被称为覆写。
class Person {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
public void getInfo() {
System.out.println('姓名:' + this.getName() + ",年龄:" + this.getAge());
}
}
class Student extends Person {
private String school;
public void setSchool(String school) {
this.school = school;
}
public String getSchool() {
return this.school;
}
// 覆写父类中的getInfo方法
public void getInfo() {
System.out.println('姓名:' + super.getName() + ",年龄:" + super.getAge() + ',学校' + this.getSchool());
}
}
方法覆写限制
子类利用方法覆写可以扩充父类方法的功能,但是也有一定的限制。
- 被子类所覆写的方法不能拥有比父类更严格的访问控制权限,目前已接触到的3种访问控制权限大小关系为private < default【默认】 < public
- 子类无法覆写父类中定义的private方法
属性覆盖
子类除了可以对父类中的方法进行覆写外,也可以对非private定义的父类属性进行覆盖,此时只需定义与父类中成员属性相一致的名称即可。
class Father {
String info = 'www.uihtml.cn';
}
class Son extends Father {
int info = 4;
public void getInfo() {
System.out.println('【父类中的info值为:】' + super.info);
System.out.println('【子类中的info值为:】' + this.info);
}
}
class JavaDemo {
public static void main(String[] args) {
Son s1 = new Son();
s1.getInfo();
}
}
final关键字
final在程序中描述为终结器的概念,在Java中使用final关键字可以实现以下功能。
定义不能被继承的类
final class DemoClass {}
定义不能被覆写的方法
class DemoClass {
public final fun(){} // 此方法不能被子类覆写
}
定义常量【全局常量】
class DemoClass {
public final int NO = 1; // 全局常量ON表示数字1
public final int OFF = 0; // 全局常量OFF表示数字1
}
Annotation注解
Annotation是通过注解配置简化程序配置代码的一种技术手段。
准确覆写【@Override】
当子类继承父类之后,如果发现父类中的某些方法无法满足子类需求,往往会采用覆写的形式来扩充父类方法的功能。
class Father {
public void getInfo(){}
}
class Son extends Father {
@Override
public void getInfo() {}
}
本程序在子类覆写父类getInfo()方法时使用了@Override注解,这样,就可以在不清楚父类结构的情况下立刻分辨出哪些是覆写的方法,哪些是子类扩充的方法。同时利用@Override注解也可以在编译时检测出因为子类拼写错误所造成的方法覆写错误。
过期声明【@Deprecated】
现代的软件项目开发已经不再是一次编写的过程 ,几乎所有的项目都会出现迭代更新的过程。每一次更新都会涉及代码结构、性能与稳定性的提升,所以经常会出现某些程序结构不再适合新版本的情况。在这样的背景下,如果在新版本中直接取消某些类或某些方法也有可以能造成部分稳定程序的出错。为了解决此类问题,可以在新版本更新时对那些不再推荐使用的操作使用@Deprecated注解声明,这样在程序编译时如果发现使用了此类结构会提示警告信息。
class Channel {
/**
* 进行通道的连接操作,此操作在新项目中不建议使用,建议使用connection()方法
*/
@Deprecated
public void connect() {
System.out.println('进行通道连接……')
}
public String connection() {
return '获取通道连接……'
}
}
class JavaDemo {
public static void main(String[] args) {
new Channel().connect(); // 编辑时会出现警示信息
}
}
本程序在Channel.connect()方法上使用了@Deprecated注解,项目开发者在编写新版本程序代码时就可以清楚的知道此为过期操作,并且可以根据注解的描述更换使用的方法。
压制警告【@SuppressWarnings】
为了代码的严格性,往往会在编译时给出一些错误的提示信息(非致命错误),但是有些错误提示信息并不是必要的。为了防止这些提示信息的出现,Java提供了@SuppressWarnings注解来进行警告信息的压制,在此注解中可以通过value属性设置要压制的警告类型。
例如在【过期声明】的程序中 ,不想在编译时出现警示信息。
class JavaDemo {
@SuppressWaranings(value = {'deprecation'})
public static void main(String[] args) {
new Channel().connect(); // 编辑时不会出现警示信息
}
}
面向对象多态性
在面向对象设计中多态性描述的是同一结构在执行时会根据不同的形式展现出不同的效果。
方法的多态性
展现形式有两种:方法的覆写及方法的重载
方法的覆写:同一个方法可能根据实现子类的不同有不同的实现。
class Father {
public void getInfo(){
System.out.println('我是父亲');
}
}
class Son extends Father {
@Override
public void getInfo() {
System.out.println('我是儿子');
}
}
class Daughter extends Father {
@Override
public void getInfo() {
System.out.println('我是女儿');
}
}
方法的重载:同一个方法可以根据传入的参数的类型或个数的不同实现不同功能。
class Message {
public void print() {
System.out.println('www.uihtml.cn');
}
@Overload
public void print(String str) {
System.out.println(str);
}
}
对象的多态性【父类与子类实例之间的转换处理】
展现形式有两种:对象向上转型及对象向下转型。
对象向上转型:父类 父类实例 = 子类实例,自动转型
在子类对象实例化之前一定会自动实例化父类对象,所以此时将子类对象的实例通过父类进行接受即可实现对象的自动向上转型。而此时的本质还是子类实例,一旦子类中覆写了父类方法,并且调用该方法时,所调用的一定是被子类覆写的方法。
class Father {
public void getInfo(){
System.out.println('我是父类');
}
}
class Son extends Father {
@Override
public void getInfo() {
System.out.println('我是子类');
}
}
class JavaDemo {
public static void main(String[] args) {
Father f1 = new Son();
f1.getInfo(); // 调用被子类覆写的方法,输入‘我是子类’
}
}
对象向上转型的最大特点在于其可以通过父类对象自动接收子类实例,而在实际的项目开发中,就可以利用这一原则实现方法接受或返回参数类型的统一。
class Message {
public void print() {
System.out.println('www.uihtml.cn');
}
}
class NetMessage extends Message {
@Override
public void print() {
System.out.println('发送网络消息……');
}
}
class DatabaseMessage extends Message {
@Override
public void print() {
System.out.println('发送数据库消息……');
}
}
class Channel {
/**
* 定义发送消息静态方法,方便类直接调用;
* 接受参数为Message类对象,由于存在对象向上转型,所有可以接受所有Message类的子类实例
*/
public static void send(Message msg) {
msg.print();
}
}
class JavaDemo {
public static void main(String[] args) {
Channel.send(new NetMessage());
Channel.send(new DatabaseMessage());
}
}
对象向下转型
子类继承父类后可以对已有的父类功能进行扩充,除了采用方法覆写这一机制外,子类也可以定义自己的方法,而对子类扩充的方法只有具体的子类实例才可以调用。在这样的情况下,如果子类已经发生了向上转型后就需要通过强制性向下转型来实现子类扩充方法调用。
class Monkey {
public void say() {
System.out.println('猴子爬树……');
}
}
class GreatSage extends Monkey {
@Override
public void say() {
System.out.println('齐天大圣腾云驾雾……');
}
public void change() {
System.out.println('齐天大圣七十二变……');
}
}
class JavaDemo {
public static void main(String[] args) {
Monkey m1 = new GreatSage(); // 对象自动向上转型,只能调用父类中定义的方法
m1.say();
GreatSage g1 = (GreatSage) m1; // 强制转为子类实例,可以调用子类中自定义的方法
g1.say();
g1.change();
}
}
注意:必须先发生向上转型,之后才可以进行向下转型。
instanceof关键字
对象向下转型存在安全隐患,为了保证转换的安全性,可以在转换之前通过instanceof关键字进行对象所属类型的判断,该关键字的使用语法如下。
对象 instanceof 类
该判断将返回一个Boolean类型数据,如果是true表示实例是指定类的对象。
以之前【对象向下转型】的程序为例
class JavaDemo {
public static void main(String[] args) {
Monkey m1 = new GreatSage(); // 对象自动向上转型,只能调用父类中定义的方法
m1.say();
if (m1 instanceof GreatSage) {
GreatSage g1 = (GreatSage) m1; // 强制转为子类实例,可以调用子类中自定义的方法
g1.say();
g1.change();
}
}
}
Object类
在Java语言设计过程中,为了方便操作类型的统一,也为了方便为每一个类定义一些公共操作,所以专门设计了一个公共的Object父类(此类是唯一一个没有父类的类,但却是所有类的父类),所有利用class关键字定义的类全部都默认继承自Object类。
获取对象信息
在Object类中提供有一个toString()方法,利用此方法可以实现对象信息的获取,而该方法是在直接进行对象输出时默认被调用的。
class Person {
private String name;
private int age;
public Person(String name,int age) {
this.name = name;
this.age = age
}
@Override
public void toString() {
return '姓名:' + this.name + '、年龄:' + this.age;
}
}
class JavaDemo {
public static void main(String[] args) {
Person per = new Person('刘备', 25);
System.out.println(per); // 直接输出对象调用toString()方法
}
}
对象比较
Object类中另外一个比较重要的方法就在于对象比较的处理上,所谓对象比较的主要功能是比较两个对象的内容是否完全相同。假设有两个Person对象,这两个对象由于分别使用了new关键字开辟堆内存空间,所以想要确认这两个对象是否一致,就需要将每一个成员属性依次比较,对于这样的比较,在Object类中有一个标准的方法:public boolean equals(Object obj)。
Object类中考虑到设计的公共性,所以equals()方法中两个对象的比较是基于地址数值判断(“对象 == 对象”地址数值判断)实现的,如果子类有对象比较的需求,那么只需要覆写此方法即可实现。
class Person {
private String name;
private int age;
public Person(String name,int age) {
this.name = name;
this.age = age
}
@Override
public void toString() {
return '姓名:' + this.name + '、年龄:' + this.age;
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof Person)) {
return false;
}
if (obj == null) {
return false;
}
Person per = (Person) obj;
return this.name.equals(per.name) && this.age.equals(per.age);
}
}
class JavaDemo {
public static void main(String[] args) {
Person per1 = new Person('刘备', 25);
Person per2 = new Person('刘备', 25);
System.out.println(per1.equals(Per2));
}
}
原创文章,作者:ZERO,如若转载,请注明出处:https://www.edu24.cn/course/java/java-inherit.html