接口与继承回顾

it2023-11-10  74

Java里类是单继承的,接口是可以多继承的,用关键字extends。

子类能继承父类的所有成员

子类是继承了父类的私有方法的(不管是否是final),只是直接调用父类的私有方法是不可以的,但是利用反射的方式可以调用。

接口

接口就像是一种约定,我们约定某些英雄是物理系英雄,那么他们就一定能够进行物理攻击 实现某个接口,就相当于承诺了某种约定

判别子转父(向上转型)能否成功的办法

所谓的转型,是指当引用类型和对象类型不一致的时候,才需要进行类型转换

Hero h = new Hero(); ADHero ad = new ADHero(); h = ad;

所有的子类转换为父类,都是说得通的。比如你身边的例子

苹果手机 继承了 手机,把苹果手机当做普通手机使用 怡宝纯净水 继承了 饮品, 把怡宝纯净水 当做饮品来使用

父类转子类(向下转型)

父类转子类,有的时候行,有的时候不行,所以必须进行强制转换。 强制转换的意思就是 转换有风险,风险自担。

什么时候行呢?

Hero h =new Hero(); ADHero ad = new ADHero(); h = ad; ad = (ADHero) h;

第3行,是子类转父类,一定可以的 第4行,就是父类转子类,所以要进行强转。 h这个引用,所指向的对象是ADHero, 所以第4行,就会把ADHero转换为ADHero,就能转换成功。

什么时候转换不行呢?

Hero h =new Hero(); ADHero ad = new ADHero(); Support s =new Support(); h = s; ad = (ADHero)h;

第4行,是子类转父类,是可以转换成功的 第5行,是把h引用所指向的对象 Support,转换为ad引用的类型ADHero。 从语义上讲,把物理攻击英雄,当成辅助英雄来用,说不通,所以会强制转换失败,并且抛出异常

没有继承关系的两个类,互相转换

没有继承关系的两个类,互相转换,一定会失败 虽然ADHero和APHero都继承了Hero,但是彼此没有互相继承关系 “把魔法英雄当做物理英雄来用”,在语义上也是说不通的

实现类转换成接口(向上转型)

引用ad指向的对象是ADHero类型,这个类型实现了AD接口 10行: 把一个ADHero类型转换为AD接口 从语义上来讲,把一个ADHero当做AD来使用,而AD接口只有一个physicAttack方法,这就意味着转换后就有可能要调用physicAttack方法,而ADHero一定是有physicAttack方法的,所以转换是能成功的。

接口转换成实现类(向下转型)

10行: ad引用指向ADHero, 而adi引用是接口类型:AD,实现类转换为接口,是向上转型,所以无需强制转换,并且一定能成功 12行: adi实际上是指向一个ADHero的,所以能够转换成功 14行: adi引用所指向的对象是一个ADHero,要转换为ADAPHero就会失败。

假设能够转换成功,那么就可以使用magicAttack方法,而adi引用所指向的对象ADHero是没有magicAttack方法的

public static void main(String[] args) { ADHero ad = new ADHero(); AD adi = ad;//10 ADHero adHero = (ADHero) adi;//12 ADAPHero adapHero = (ADAPHero) adi;//14 adapHero.magicAttack(); }

重写

重写Overriding是父类与子类之间多态性的一种表现,重载Overloading是一个类中多态性的一种表现。

方法重写应遵循两同两小一大原则: 两同:方法名、形参列表相同; 两小:返回值类型、抛出异常更小 一大:访问修饰符。

这里强调一下返回值类型小: 重写返回值类型如果是基本数据类型,必须相同,如果是引用数据类型,那么复写方法必须小于等于父类,比如父类方法返回值类型是object 那么子类就任意了

再强调一下 "两小一大"是为了加强记忆的方便,并不是非要 “小大”,相同也可以。

多态

操作符的多态

可以作为算数运算,也可以作为字符串连接

多态问题中,无论向上或向下转型,都记住一句话就可以了。 编译看左边,运行看右边。意思编译时候,看左边有没有该方法,运行的时候结果看 new 的对象是谁,就调用的谁。

举个例子

Base base = new Son();

编译看左边,调用的方法是否是Base类型有的,如果没有,就报编译错误

类的多态

观察类的多态现象:

i1和i2都是Item类型都调用effect方法输出不同的结果

多态: 都是同一个类型,调用同一个方法,却能呈现不同的状态

类的多态条件

要实现类的多态,需要如下条件

父类(接口)引用指向子类对象调用的方法有重写

隐藏

就是子类覆盖父类的类方法

实例化子类,父类的构造方法一定会被调用

实例化一个ADHero(), 其构造方法会被调用 其父类的构造方法也会被调用 并且是父类构造方法先调用 子类构造方法会默认调用父类的 无参的构造方法

子类显式调用父类带参构造方法

使用关键字super 显式调用父类带参的构造方法 super(name);

调用父类属性

public int getMoveSpeed2(){ return super.moveSpeed; }

调用父类方法

// 重写useItem,并在其中调用父类的userItem方法 public void useItem(Item i) { System.out.println("adhero use item"); super.useItem(i); }

Object类是所有类的父类

声明一个类的时候,默认是继承了Object

toString()

Object类提供一个toString方法,所以所有的类都有toString方法 toString()的意思是返回当前对象的字符串表达

finalize()

当一个对象没有任何引用指向的时候,它就满足垃圾回收的条件 当它被垃圾回收的时候,它的finalize() 方法就会被调用。 finalize() 不是开发人员主动调用的方法,而是由虚拟机JVM调用的

equals()

Object类的equals方法的实现代码如下:

boolean equals(Object o){ return this==o; }

==

这不是Object的方法,但是用于判断两个对象是否相同 更准确的讲,用于判断两个引用,是否指向了同一个对象(比较的是地址)

hashCode()

不同的对象可能会生成相同的hashcode值。虽然不能根据hashcode值判断两个对象是否相等,但是可以直接根据hashcode值判断两个对象不等

在有些情况下,程序设计者在设计一个类的时候为需要重写equals方法,比如String类,但是千万要注意,在重写equals方法的同时,必须重写hashCode方法。因为默认情况下,hashCode方法是将对象的存储地址进行映射,和我要判断相等的条件本意冲突了。

详情

线程同步相关方法

Object还提供线程同步相关方法 wait() notify() notifyAll() 这部分内容的理解需要建立在对线程安全有足够的理解的基础之上,所以会放在线程交互 的章节讲解

getClass() getClass()会返回一个对象的类对象,属于高级内容,不适合初学者过早接触,关于类对象的详细内容请参考反射机制

接口与继承之final

final修饰类 不能够被继承 其子类会出现编译错误

final修饰方法 不能够被重写

final修饰基本类型变量 表示该变量只有一次赋值机会

final修饰引用 h引用被修饰成final,表示该引用只有1次指向对象的机会

常量 常量指的是可以公开,直接访问,不会变化的值

抽象类的基本特性

1.当一个类有抽象方法的时候,该类必须被声明为抽象类 2.抽象类可以没有抽象方法 3.抽象类可以定义public,protected,package,private,静态和非静态属性final和非final属性 4.抽象方法中不能用private,static, synchronized,native等修饰词修饰.(细节要交给子类)

内部类分为四种

内部类其实和类的属性没什么区别,只是在声明的时候必须是Outer.Inner a,就像int a 一样,至于静态内部类和非静态内部类new的时候有点区别,Outer.Inner a=new Outer().new Inner()(非静态,先有Outer对象才能有属性) Outer.Inner a=new Outer.Inner()要把Outer.Inner看成一部分,就像类变量一样

注意:Outer和Inner是类名

非静态内部类 非静态内部类可以直接在一个类里面定义 语法: new 外部类().new 内部类() 作为外部类的非静态内部类,是可以直接访问外部类的private实例属性name的

静态内部类

static class EnemyCrystal{}

在一个类里面声明一个静态内部类 比如敌方水晶,当敌方水晶没有血的时候,己方所有英雄都取得胜利,而不只是某一个具体的英雄取得胜利。 与非静态内部类不同,静态内部类水晶类的实例化 不需要一个外部类的实例为基础,可以直接实例化 语法:new 外部类.静态内部类(); 因为没有一个外部类的实例,所以在静态内部类里面不可以访问外部类的实例属性和方法 除了可以访问外部类的私有静态成员外,静态内部类和普通类没什么大的区别

匿名类 匿名类指的是在声明一个类的同时实例化它,使代码更加简洁精练 通常情况下,要使用一个接口或者抽象类,都必须创建一个子类

有的时候,为了快速使用,直接实例化一个抽象类,并“当场”实现其抽象方法。 既然实现了抽象方法,那么就是一个新的类,只是这个类,没有命名。 这样的类,叫做匿名类

Hero h = new Hero(){ //当场实现attack方法 public void attack() { System.out.println("新的进攻手段"); } };

本地类 本地类可以理解为有名字的匿名类 内部类与匿名类不一样的是,内部类必须声明在成员的位置,即与属性和方法平等的位置。 本地类和匿名类一样,直接声明在代码块里面,可以是主方法,for循环里等等地方

在匿名类中使用外部的局部变量 在匿名类中使用外部的局部变量,外部的局部变量必须修饰为final

为什么要声明为final,其机制比较复杂

注:在jdk8中,已经不需要强制修饰成final了,如果没有写final,不会报错,因为编译器偷偷的帮你加上了看不见的final

什么是默认方法

默认方法是JDK8新特性,指的是接口也可以提供具体方法了,而不像以前,只能提供抽象方法

Mortal 这个接口,增加了一个默认方法 revive,这个方法有实现体,并且被声明为了default

default public void revive() { System.out.println("本英雄复活了"); }

为什么会有默认方法 假设没有默认方法这种机制,那么如果要为Mortal增加一个新的方法revive,那么所有实现了Mortal接口的类,都需要做改动。

但是引入了默认方法后,原来的类,不需要做任何改动,并且还能得到这个默认方法

通过这种手段,就能够很好的扩展新的类,并且做到不影响原来的类

面向对象的五大基本原则 单一职责原则(Single-Resposibility Principle):一个类,最好只做一件事,只有一个引起它的变化。 开放封闭原则(Open-Closed principle):软件实体应该是可扩展的,而不可修改的。也就是,对扩展开放,对修改封闭的。 Liskov替换原则(Liskov-Substituion Principle):子类必须能够替换其基类。这一思想体现为对继承机制的约束规范,只有子类能够替换基类时,才能保证系统在运行期内识别子类,这是保证继承复用的基础。 依赖倒置原则(Dependecy-Inversion Principle):依赖于抽象。具体而言就是高层模块不依赖于底层模块,二者都同依赖于抽象;抽象不依赖于具体,具体依赖于抽象。 接口隔离原则(Interface-Segregation Principle):使用多个小的专门的接口,而不要使用一个大的总接口

最新回复(0)