接口与继承回顾

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

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

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

接口

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

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

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

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

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

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

父类转子类(向下转型)

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

什么时候行呢?

  1.    Hero h =new Hero();
    
  2.    ADHero ad = new ADHero();
    
  3.    h = ad;
    
  4.    ad = (ADHero) h;
    

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

什么时候转换不行呢?

  1.    Hero h =new Hero();
    
  2.    ADHero ad = new ADHero();
    
  3.    Support s =new Support();
    
  4.    h = s;
    
  5.    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类型有的,如果没有,就报编译错误

类的多态

观察类的多态现象:

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

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

类的多态条件

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

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

隐藏

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

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

实例化一个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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值