一.继承关系
1.引出继承
为了解决代码复用的问题
2.继承关系
1>.什么是继承
基于某个父类对对象的定义加以拓展,而产生新的子类定义,子类可以继承父类原来的某些定义,也可以增加原来父类所没有的定义,或者覆写父类中的某些特性;
从面相对象的角度来说:
继承是一种从一般到特殊的关系;是一种”is a”的关系,即子类是对父类的拓展,是一种特殊的父类;
在Java语言中,存在多个类的时候,我们使用”extends
“关键字来表示子类和父类之间的关系;
语法格式:
在定义子类的时候来表明自己需要拓展于哪一个父类
public class 子类类名 extents 父类类名{
编写自己特有的状态和行为
}
注意:
在Java中,类和类之间的继承关系,只允许单继承,不允许多继承;也就是说一个类只能有一个直接的父类;但是Java中允许多重继承;在Java中除了Object类之外,每个类都有一个直接的父类;Object类要么是一个类的直接父类,要么是一个类的间接父类;
2>.继承关系的作用
- 1.解决了代码重复的问题;
- 2.真正的作用,表示出一个体系;
1).子类继承了那些父类的成员
子类继承父类之后可以拥有父类的某一些状态和行为(子类复用了父类的功能或状态);
子类到底继承了父类的那些成员(根据访问修饰符来判断)
- 1.如果父类中的成员使用public修饰,子类继承;
- 2.如果父类中的成员使用protected修饰,子类继承,即使父类和子类不在同一个包中;
- 3.如果父类和子类在同一个包中,此时子类可以继承父类中缺省修饰符的成员;
- 4.如果父类中的成员使用private修饰,子类则无法继承,因为private只能在本类中访问;
- 5.父类的构造器,子类无法继承,因为构造器必须和当前类的类名相同;
3.方法覆盖
方法覆盖的原则(一同两小一大): Override
一同:
1): 实例方法签名必须相同(方法签名=方法名+方法的参数列表);
两小:
2): 子类方法的返回值类型和父类方法的返回值类型相同或者是其子类;
3): 子类方法声明抛出的异常类型和父类方法声明抛出的异常类型相同或者是其子类;
注:
子类方法中声明抛出的异常小于或等于父类方法声明抛出的异常类型;
子类方法可以同时声明抛出多个属于父类方法声明抛出异常类的子类(RuntimeException类型除外);
一大:
4): 子类方法的访问权限比父类方法的访问权限更大或者相等;
注: private修饰的方法不能被子类所继承,也就不存在覆盖的概念;
判断是否覆写方法的注解: @Override标签
若方法是覆写方法,在方法前或上贴上该标签,编译通过表示覆写,否则编译报错;
注意: 只有方法存在覆盖的概念,字段没有覆盖;
方法覆盖解决的问题:
当父类的某一个行为不符合子类具体的特征的时候,此时子类需要重新定义父类的方法,并重写方法体;
/*方法覆盖的代码示例*/
// 鸟类
class Bird extents Object{
public void fly(){
System.out.println("飞翔!");
}
}
// 企鹅
class Penguin extents Bird{
// 重新定义了fly(覆写)
public void fly(){
System.out.println("不能飞翔!");
}
}
1).方法重载和方法覆盖的区别
方法重载: Overload
方法覆盖: Override
方法重载(Overload)
作用:
解决了同一个类中,相同功能的方法名不同的问题;既然是相同的功能,那么方法的名字就应该相同;
规则:两同一不同
同类中,方法名相同,方法参数列表不同(参数类型,参数个数,参数顺序);
方法覆盖(Override)
作用:
解决子类继承父类之后,可能父类的某一个方法不满足子类的具体特征,此时需要重新在子类中定义该方法,并重写方法体;
规则:一同两小一大
一同:父类和子类的方法签名是相同的,所以建议直接拷贝父类中方法的定义到子类中,再重写方法体就可以了;
4.super关键字
this:
当前对象,谁调用this所在的方法,this就是哪一个对象;
super:
当前对象的父类对象;
1).子类的初始化过程
在创建子类对象时的执行顺序是: 先进入子类构造器,然后在构造器里面会先调用父类构造器(创建父类对象),再执行子类构造器代码;
2).创建子类对象的过程
在创建子类对象之前会先创建父类对象;
调用子类构造器之前,在子类构造器中会先调用父类的构造器,默认调用的是父类无参的构造器;
1): 如果父类不存在可以被子类访问的构造器,则不能存在子类
2): 如果父类没有提供无参数的构造器,此时子类必须使用super语句去调用父类带参数的构造器;
注意:
子类构造器使用super调用父类构造器,必须作为构造器里面的第一句话;必须先有父类对象,然后才能有子类对象;
必须先调用父类构造器,而后再调用子类构造器;
5.”隐藏”问题
所谓隐藏就是”遮蔽”的意思;
1): 满足继承的访问权限下,隐藏父类的静态方法: 若子类定义的静态方法的签名和父类中的静态方法签名相同,那么此时就是隐藏父类方法; 注意: 仅仅是静态方法;
2): 隐藏本类字段: 若同类中某局部变量和字段名相同,此时就是隐藏本类字段,此时只能通过this访问被隐藏的字段;
1).super关键字的使用场景
1: 可以使用super解决子类隐藏父类的字段情况,该情况,我们一般不讨论,破坏封装;
2: 在子类方法中,调用父类被覆盖的方法,引出super的例子,此时必须使用super;
3: 在子类构造器中,调用父类构造器,此时必须使用super语句: super([实参]);
注意:
static不能和super以及this共存;
6.Object类
1).基本概念
Object类是Java语言的根类,要么是一个类的直接父类,要么是一个类的间接父类;
所有的对象(包括数组)都实现这个类的方法;
引用数据类型: 类/接口/数组; 引用类型又称之为对象类,所谓对象的数组变量名称,应该指数组对象;
2).Object的常见方法
1: protected void finalize(): 当垃圾回收器确定不存在该对象的更多引用时,由对象的垃圾回收器调用此方法;
垃圾回收器在回收某一个对象之前,会先调用该方法,做扫尾操作,该方法我们不要去调用;
2: getClass(): 返回当前对象的真实类型;
3: int hashCode(): 返回该对象的哈希码值,hashCode决定了对象在哈希表中存储的位置,不同的对象hashCode是不同的;
4: boolean equals(Object obj): 拿当前对象(this)和参数obj做比较;
在Object类中的equals方法,本身和”==”相同,都是比较对象的内存地址;
官方建议: 每个类都应该覆盖equals方法,不要比较内存地址,而去比较我们关心的数据,因为我们关心的是内容数据,而不是内存地址;
5: String toString(): 表示把一个对象转换为字符串;
打印对象时,其实就是打印的就是对象的toString方法;
System.out.println(obj对象); 等价于 System.out.println(obj对象.toString());
默认情况下打印对象,打印的是对象的十六进制的hashCode值,但是我们更关心对象中存储的数据;
官方建议: 应该每个类都覆盖toString,返回我们关心的数据;
二.多态思想
1.理解多态
编译类型: 声明对象变量的类型;
运行类型: 对象的真实类型;
编译类型必须是真实类型的父类或相同;
当编译类型和真实类型不同时多态就出现了;
所谓多态: 对象具有多种形态,对象可以存在不同的形式;
Animal a = null;
a = new Dog(); // a此时表示Dog类型的形态;
a = new Cat(); // a此时表示Cat类型的形态;
多态的前提: 可以是继承关系(类和类)/也可以是实现关系(接口和实现类),在开发中多态一般都指第二种;
多态的特点: 把子类对象赋给父类变量,在运行时期表现出具体的子类特征;
2.多态的好处
多态的作用: 可以把不同的子类对象都当作父类类型看待,可以屏蔽不同子类对象之间的实现差异,从而写出通用的代码达到通用编程的目的,以适应需求的不断变化;
3.多态时方法调用问题
前提: 必须先存在多态情况;
存在父类: SuperClass, 子类: SubClass, 方法: doWork.
测试代码:
SuperClass clz = new SuperClass(); // 多态
clz.doWork(); // ? 输出什么结果
情况1: doWork方法存在于SuperClass中,不存在于SubClass中;
// 父类
class SuperClass{
public void doWork(){
System.out.println("Super.doWork")
}
}
// 子类
class SubClass extends SuperClass{
}
此执行结果: 编译通过,执行SuperClass的doWork方法,应该先从SubClass类中去找doWork方法,再去父类SuperClass类中找;
情况2: doWork方法存在于SuperClass中,不存在于SubClass中;
// 父类
class SuperClass{
}
// 子类
class SubClass extends SuperClass{
public void doWork(){
System.out.println("SubClass.doWork")
}
}
此执行结果: 编译错误; 编译时期,会去SuperClass中找是否有doWork方法: 找到则编译通过,找不到则编译报错;
情况3: doWork方法存在于SuperClass中和SubClass中;
// 父类
class SuperClass{
public void doWork(){
System.out.println("SuperClass.doWork")
}
}
// 子类
class SubClass extends SuperClass{
public void doWork(){
System.out.println("SubClass.doWork")
}
}
此执行结果: 编译通过; 执行SubClass的doWork方法,在运行时期,调用运行类型SubClass中的方法;
情况4: doWork方法存在于SuperClass中和SubClass中,但是doWork是静态方法;
此时这种情况我们称之为隐藏而不是方法的覆盖;
// 父类
class SuperClass{
public static void doWork(){
System.out.println("SuperClass.doWork")
}
}
// 子类
class SubClass extends SuperClass{
public static void doWork(){ // 隐藏关系
System.out.println("SubClass.doWork")
}
}
此执行结果: 编译通过; 执行SubClass的doWork方法;
静态方法的调用只需要类即可;
如果使用对象来调用静态方法,其实使用的是对象的编译类型来调用静态方法,和对象没有关系;
4.引用类型转换
1).基本数据类型转换:
自动类型转换: 把小类型的数据赋给大类型的变量(此时的大和小表示容量的范围)
byte b = 12; // byte 是一个字节
int i = b; // int是4个字节
强制类型转换: 把大类型的数据赋给小类型的变量;
short s = (short) i; // short是2个字节
2).引用类型的转换:
引用类型的大和小,指的是父类和子类的关系;
自动类型转换: 把子类的对象赋给父类的变量(多态);
Anjmal a = new Dog();
Object是所有类的跟类:
Object obj = new Dog();
强制类型转换: 把父类类型赋给子类类型变量(当时该父类类型变量的真实类型应该是子类类型);
Animal a = new Dog();
Dog d = (Dog)a;
3).instanceof运算符
判断一个对象是否是一个类的实例;
- 若对象是类的实例返回true;
- 若对象是类的父类的实例也返回true;
语法格式:
boolean b = 对象A instanceof 类B; // 判断A对象是否是B类的实例,如果是,返回true;
在开发中,有时候我们只想判断真实类型的实例而不想判断为编译类型的实例;
System.out.println(obj instanceof Object); // true
System.out.println(obj instanceof String); // true
// 真实类型的比较
System.out.println(obj.getClass() == String.class); // true
System.out.println(obj.getClass() == Object.class); // true
三.组合关系
如果A类为了得到B的功能行为;
如果A类是B类的一种特殊情况,我们应该采用继承的来实现,否则使用组合方式;
// 组合关系
class CombinationDemo{
private ArrayUtil = new ArrayUtil(); // 组合关系
public void test(){
int[] arr = {-2,9,0,5};
util.sort(arr); // 使用组合关系中的排序功能
}
public static void main(String[] arg){
new CombinationDemo().test();
}
}
反馈与建议
- 博客:我的CSDN博客
- 邮箱:pengtdy@gmail.com
- QQ交流群:644402856(新)
感谢你阅读这篇博客。如果您喜欢这篇博客就请关注我和朋友一起分享吧!