Java面向对象三大特性详解

我们可以发现,如果我们直接用System.out.print(对象)输出一个对象,则运行结果输出的是对象的对象地址字符串,也称为哈希code码。如:

哈希码是通过哈希算法生成的一个字符串,它是用来唯一区分我们对象的地址码,就像我们的身份证一样。

b. 可以通过重写toString()方法表示出对象的属性。

如果我们希望输出一个对象的时候,不是它的哈希码,而是它的各个属性值,那我们可以通过重写toString()方法表示出对象的属性。

2、equals()

a、equals()----返回值是布尔类型。

b、默认的情况下,比较的是对象的引用是否指向同一块内存地址-------对象实例化时,即给对象分配内存空间,该内存空间的地址就是内存地址。使用方法如:dog.equals(dog2);

c、 如果是两个对象,但想判断两个对象的属性是否相同,则重写equals()方法。

以Dog类为例,重写后的equals()方法如下(当然你可以根据自己想比较的属性来重写,这里我以age属性是否相同来重写equals()方法):

上面有四个判断,它们的含义分别是:

1.判断地址是否相同----if (this == obj),相同则返回true

2.判断对象是否为空----if (obj == null),为空则返回false

3.getClass()可以得到类对象,判断类型是否一样-----if (getClass() != obj.getClass()),不一样则返回false

4.判断属性值是否一样----if (age != other.age),不一样返回false

5.如果地址相同,对象不为空,类型一样,属性值一样则返回true

这里要注意的是,理解obj.getClass()得到的类对象和类的对象的区别,以下用图形表示:

可以看到,对于类对象我们关心它属于哪个类,拥有什么属性和方法,比如我和你都是属于“人”这个类对象;而类的对象则是一个类的实例化的具体的一个对象。比如我和你是两个不同的人。

三、多态

面向对象的最后一个特性就是多态,那么什么是多态呢?多态就是对象的多种形态。

java里的多态主要表现在两个方面:

1.引用多态

父类的引用可以指向本类的对象;

父类的引用可以指向子类的对象;

这两句话是什么意思呢,让我们用代码来体验一下,首先我们创建一个父类Animal和一个子类Dog,在主函数里如下所示:

注意:我们不能使用一个子类的引用来指向父类的对象,如:

这里我们必须深刻理解引用多态的意义,才能更好记忆这种多态的特性。为什么子类的引用不能用来指向父类的对象呢?我在这里通俗给大家讲解一下:就以上面的例子来说,我们能说“狗是一种动物”,但是不能说“动物是一种狗”,狗和动物是父类和子类的继承关系,它们的从属是不能颠倒的。当父类的引用指向子类的对象时,该对象将只是看成一种特殊的父类(里面有重写的方法和属性),反之,一个子类的引用来指向父类的对象是不可行的!!

2.方法多态

根据上述创建的两个对象:本类对象和子类对象,同样都是父类的引用,当我们指向不同的对象时,它们调用的方法也是多态的。

创建本类对象时,调用的方法为本类方法;

创建子类对象时,调用的方法为子类重写的方法或者继承的方法;

使用多态的时候要注意:如果我们在子类中编写一个独有的方法(没有继承父类的方法),此时就不能通过父类的引用创建的子类对象来调用该方法!!!

注意: 继承是多态的基础。

A、引用类型转换

了解了多态的含义后,我们在日常使用多态的特性时经常需要进行引用类型转换。

引用类型转换:

1. 向上类型转换(隐式/自动类型转换),是小类型转换到大类型。

就以上述的父类Animal和一个子类Dog来说明,当父类的引用可以指向子类的对象时,就是向上类型转换。如:

2. 向下类型转换(强制类型转换),是大类型转换到小类型(有风险,可能出现数据溢出)。

将上述代码再加上一行,我们再次将父类转换为子类引用,那么会出现错误,编译器不允许我们直接这么做**,虽然我们知道这个父类引用指向的就是子类对象,但是编译器认为这种转换是存在风险的。**如:

那么我们该怎么解决这个问题呢,我们可以在animal前加上(Dog)来强制类型转换。如:

但是如果父类引用没有指向该子类的对象,则不能向下类型转换,虽然编译器不会报错,但是运行的时候程序会出错,如:

其实这就是上面所说的子类的引用指向父类的对象,而强制转换类型也不能转换!!

还有一种情况是父类的引用指向其他子类的对象,则不能通过强制转为该子类的对象。如:

这是因为我们在编译的时候进行了强制类型转换,编译时的类型是我们强制转换的类型,所以编译器不会报错,而当我们运行的时候,程序给animal开辟的是Dog类型的内存空间,这与Cat类型内存空间不匹配,所以无法正常转换。这两种情况出错的本质是一样的,所以我们在使用强制类型转换的时候要特别注意这两种错误!!下面有个更安全的方式来实现向下类型转换。。。。

**3. instanceof运算符,**来解决引用对象的类型,避免类型转换的安全性问题。

instanceof是Java的一个二元操作符,和==,>,<是同一类东东。由于它是由字母组成的,所以也是Java的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回boolean类型的数据。

我们来使用instanceof运算符来规避上面的错误,代码修改如下:

利用if语句和instanceof运算符来判断两个对象的类型是否一致。

**补充说明:**在比较一个对象是否和另一个对象属于同一个类实例的时候,我们通常可以采用instanceof和getClass两种方法通过两者是否相等来判断,但是两者在判断上面是有差别的。Instanceof进行类型检查规则是:你属于该类吗?或者你属于该类的派生类吗?而通过getClass获得类型信息采用==来进行检查是否相等的操作是严格的判断,不会存在继承方面的考虑

**总结:**在写程序的时候,如果要进行类型转换,我们最好使用instanceof运算符来判断它左边的对象是否是它右边的类的实例,再进行强制转换。

B、抽象类

定义:抽象类前使用abstract关键字修饰,则该类为抽象类。

使用抽象类要注意以下几点:

1. 抽象类是约束子类必须有什么方法,而并不关注子类如何实现这些方法。

2. 抽象类应用场景:

a. 在某些情况下,某个父类只是知道其子类应该包含怎样的方法,但无法准确知道这些子类如何实现这些方法(可实现动态多态)。

b. 从多个具有相同特征的类中抽象出一个抽象类,以这个抽象类作为子类的模板,从而避免子类设计的随意性。

3. 抽象类定义抽象方法,只有声明,不需要实现。抽象方法没有方法体以分号结束,抽象方法必须用abstract关键字来修饰。如:

4、包含抽象方法的类是抽象类。抽象类中可以包含普通的方法,也可以没有抽象方法。如:

5、抽象类不能直接创建,可以定义引用变量来指向子类对象,来实现抽象方法。以上述的Telephone抽象类为例:

1 public abstract class Telephone {

2 public abstract void call();//抽象方法,方法体以分号结束,只有声明,不需要实现

3 public void message(){

4 System.out.println(“我是抽象类的普通方法”);

5 }//抽象类中包含普通的方法

6 }

1 public class Phone extends Telephone {

2

3 public void call() {//继承抽象类的子类必须重写抽象方法

4 // TODO Auto-generated method stub

5 System.out.println(“我重写了抽象类的方法”);

6 }

7

8 }

以上是Telephone抽象类和子类Phone的定义,下面我们看main函数里:

运行结果(排错之后):

C、接口

1、概念

接口可以理解为一种特殊的类,由全局常量和公共的抽象方法所组成。也可理解为一个特殊的抽象类,因为它含有抽象方法。

如果说类是一种具体实现体,而接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部数据,也不关心这些类里方法的实现细节,它只规定这些类里必须提供的某些方法。(这里与抽象类相似)

2.接口定义的基本语法

[修饰符] [abstract] interface 接口名 [extends父接口1,2…](多继承){

0…n常量 (public static final)

0…n 抽象方法(public abstract)

}

其中[ ]里的内容表示可选项,可以写也可以不写;接口中的属性都是常量,即使定义时不添加public static final 修饰符,系统也会自动加上;接口中的方法都是抽象方法,即使定义时不添加public abstract修饰符,系统也会自动加上。

3.使用接口

一个类可以实现一个或多个接口,实现接口使用implements关键字。java中一个类只能继承一个父类,是不够灵活的,通过实现多个接口可以补充。

继承父类实现接口的语法为:

[修饰符] class 类名 extends 父类 implements 接口1,接口2…{

类体部分//如果继承了抽象类,需要实现继承的抽象方法;要实现接口中的抽象方法

}

注意:如果要继承父类,继承父类必须在实现接口之前,即extends关键字必须在implements关键字前

补充说明:通常我们在命名一个接口时,经常以I开头,用来区分普通的类。如:IPlayGame

以下我们来补充在上述抽象类中的例子,我们之前已经定义了一个抽象类Telephone和子类Phone,这里我们再创建一个IPlayGame的接口,然后在原来定义的两个类稍作修改,代码如下:

1 public interface IPlayGame {

2 public void paly();//abstract 关键字可以省略,系统会自动加上

3 public String name=“游戏名字”;//static final关键字可以省略,系统会自动加上

4 }

1 public class Phone extends Telephone implements IPlayGame{

2

3 public void call() {//继承抽象类的子类必须重写抽象方法

4 // TODO Auto-generated method stub

5 System.out.println(“我重写了抽象类的方法”);

6 }

7

8 @Override

9 public void paly() {

10 // TODO Auto-generated method stub

11 System.out.println(“我重写了接口的方法”);

12 }

13

14 }

1 public class train {

2

3

4 public static void main(String[] args) {

5 // TODO Auto-generated method stub

6 IPlayGame i=new Phone();//用接口的引用指向子类的对象

7 i.paly();//调用接口的方法

8 System.out.println(i.name);//输出接口的常量

9

10

11

12 }

13 }

运行结果:

4.接口和匿名内部类配合使用

接口在使用过程中还经常和匿名内部类配合使用。匿名内部类就是没有没名字的内部类,多用于关注实现而不关注实现类的名称。

语法格式:

1 Interface i =new interface(){

2 Public void method{

3 System.out.println(“利用匿名内部类实现接口1”);

4 }

5 };

6 i.method();

还有一种写法:(直接把方法的调用写在匿名内部类的最后)

1 Interface i =new interface(){

2 Public void method{

3 System.out.println(“利用匿名内部类实现接口1”);

4 }

5 }.method();

四、抽象类和接口的区别

我们在多态的学习过程中认识到抽象类和接口都是实现java多态特性的关键部分,两者都包含抽象方法,只关注方法的声明而不关注方法的具体实现,那么这两者又有什么区别呢??我们在编写java程序的时候又该如何抉择呢?

参考博文:http://www.cnblogs.com/felixzh/p/5938544.html

(1)语法层面上的区别

1.一个类只能继承一个抽象类,而一个类却可以实现多个接口。

2.抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;且必须给其初值,所以实现类中不能重新定义,也不能改变其值;抽象类中的变量默认是 friendly 型,其值可以在子类中重新定义,也可以重新赋值。

3.抽象类中可以有非抽象方法,接口中则不能有非抽象方法。

4.接口可以省略abstract 关键字,抽象类不能。

5.接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;

(2)设计层面上的区别

1)抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。举个简单的例子,飞机和鸟是不同类的事物,但是它们都有一个共性,就是都会飞。那么在设计的时候,可以将飞机设计为一个类Airplane,将鸟设计为一个类Bird,但是不能将 飞行 这个特性也设计为类,因此它只是一个行为特性,并不是对一类事物的抽象描述。此时可以将 飞行 设计为一个接口Fly,包含方法fly( ),然后Airplane和Bird分别根据自己的需要实现Fly这个接口。然后至于有不同种类的飞机,比如战斗机、民用飞机等直接继承Airplane即可,对于鸟也是类似的,不同种类的鸟直接继承Bird类即可。从这里可以看出,继承是一个 "是不是"的关系,而 接口 实现则是 "有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如鸟是否能飞(或者是否具备飞行这个特点),能飞行则可以实现这个接口,不能飞行就不实现这个接口。

2)设计层面不同,抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。什么是模板式设计?最简单例子,大家都用过ppt里面的模板,如果用模板A设计了ppt B和ppt C,ppt B和ppt C公共的部分就是模板A了,如果它们的公共部分需要改动,则只需要改动模板A就可以了,不需要重新对ppt B和ppt C进行改动。而辐射式设计,比如某个电梯都装了某种报警器,一旦要更新报警器,就必须全部更新。也就是说对于抽象类,如果需要添加新的方法,可以直接在抽象类中添加具体的实现,子类可以不进行变更;而对于接口则不行,如果接口进行了变更,则所有实现这个接口的类都必须进行相应的改动。

下面看一个网上流传最广泛的例子:门和警报的例子:门都有open( )和close( )两个动作,此时我们可以定义通过抽象类和接口来定义这个抽象概念:

1 abstract class Door {

2 public abstract void open();

3 public abstract void close();

4 }

或者:

1 interface Door {

2 public abstract void open();

3 public abstract void close();

4 }

但是现在如果我们需要门具有报警alarm( )的功能,那么该如何实现?下面提供两种思路:

1)将这三个功能都放在抽象类里面,但是这样一来所有继承于这个抽象类的子类都具备了报警功能,但是有的门并不一定具备报警功能;

2)将这三个功能都放在接口里面,需要用到报警功能的类就需要实现这个接口中的open( )和close( ),也许这个类根本就不具备open( )和close( )这两个功能,比如火灾报警器。

从这里可以看出, Door的open() 、close()和alarm()根本就属于两个不同范畴内的行为,open()和close()属于门本身固有的行为特性,而alarm()属于延伸的附加行为。因此最好的解决办法是单独将报警设计为一个接口,包含alarm()行为,Door设计为单独的一个抽象类,包含open和close两种行为。再设计一个报警门继承Door类和实现Alarm接口。

1 interface Alram {

2 void alarm();

3 }

4

5 abstract class Door {

6 void open();

7 void close();

8 }

9

10 class AlarmDoor extends Door implements Alarm {

11 void oepn() {

12 //…

13 }

14 void close() {

15 //…

16 }

17 void alarm() {

18 //…

19 }

20 }

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

16 }

17 void alarm() {

18 //…

19 }

20 }

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-rkfVa6u9-1714854483391)]

[外链图片转存中…(img-6LQl4RJ2-1714854483391)]

[外链图片转存中…(img-JyofYxuY-1714854483391)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值