面向对象。继承

一. 面向对象继承

  1. 什么是继承

只是描述一种关系,跟现实中的父子关系一样;

孩子继承父亲嘛,继承的特性。父亲有的东西,儿子可以直接拿来用!而儿子有的,父亲不一定可以用!

2.怎么来的继承

我们先设计两个类

学生和工人

现在再来老师类,医生类,每个类中都有姓名,年龄字段,这样是不是很麻烦,那么姓名年龄就是他们的共性部分,我们可以把他们的共性部分提取出来单独有一个类来描述

用一个人类来描述,是人都有姓名和年龄。然后让我们的学生类,工人类,跟这个Person类相关联一下;这里的关系就是继承关系。让学生,工人继承人类。然后学生和工人就相当于也拥有了人类的姓名,年龄字段;儿子可以用父亲的嘛

3.提取共性的好处:

1.首先子类里边可以不用写name,age字段,直观地来说,代码变少了;

2.他们之间有一种继承关系(至于这种关系的好处,以后慢慢会遇到)

3.子类可以使用父类的属性-直观地描述了我们现实中的关系。

思考:

因为学生类和工人类都有姓名,年龄。那么可不可以让工人类继承学生类?

不能,因为代码来源于生活!咱们生活中学生和工人有继承关系吗?没有!而工人和学生都有一个共性的部分就是都属于人类!那么可以把共性的属性提取到人类里边,让学生类,工人类都继承这个Person类!

所以注意:千万不要为了获取其他类的功能,或简化代码而继承。必须是类与类之间有所属关系才可以用继承。

我们举个例子:比如说来了一帅哥,手拿iphone7,你现在很想玩一把,那么这个时候你不可能为了了一个iphone7就继承他吧。总不能为了一手机叫人家爹吧。所以注意,必须要像我们现实中有那种所属关系的;

4.什么时候用继承

例:车—-小汽车,大卡车,摩托车。 他们都属于车,才能继承车

用代码来演示一下:我现在方便大家观看,就把类写在一个文件中。你们别这么干就行。

class A{
void a1(){}
void a2(){}
}
class B{
void a1(){}
void b(){}
}
这时候如果B如果继承A 那么B里边可以少写一个a1的方法,他可以用A里面的方法。但是现在B里边有3个方法了 a1 a2 b 可以父类的所有方法!这样就违背了我们定义类的时候的一个特性,我们定义的B里边只有两个方法 不科学!

那么这个时候我们就把共性的方法a1提取出来放在他们的父类中

class A{
void a2(){}
}
class B{
void b(){}
}
class C{
void a1(){}
}
现在是提取出来了,而且A和B里面的代码少了,但是他们有所属关系吗?没有,java中表示继承关系会用一个关键字

5.extends关键字

现在我们要的是A继承C B继承C那么

class A extends C{
void a2(){}
}
class B extends C{
void b(){}
}
这时候A,B和C就有继承关系,相当于C就有了两个孩子。我们来看一下可不可以用父类的成员;

A a = new A();
B b = new B();
a.a1();
b.a1();
可以访问的!

思考一下:先有父类还是先有子类?

现实生活中是先有父亲,再有孩子。Java中呢,也是先有父类哈。虽然我们演示的时候是先有子类然后提取共性到父类。其实我们写代码的时候是先定义父类的,然后在扩展功能定义他的子类强化父类的功能。

比如说:我现在要定义一个生物类,首先先定义所有生物最共性的东西,比如吃,动!现在生物的功能就只能吃,动。后来慢慢有了人类。人类可以吃,动,还能跑,跳。功能多了,这时候就直接添加到生物类的子类。不管以后出现什么新物种都可以添加上去!

6.多继承

多继承就是C extendsA,B 就是一个儿子有多个爹。这是不允许的,所以java中不支持多继承。C++里边就支持多继承,这个不靠谱!演示

请问这时候java运行的是哪个show方法。不确定,所以java不支持这样的,C++在这方面有安全隐患!

Java不支持多继承。但是java支持多层继承。

爷—-父—–子 子继承父,父继承爷。多层继承

所以说:java跟现实中一样啊:一个父亲可以有多个儿子,一个儿子不能有多个父亲;

7.如何使用一个继承体系的功能。比如以后我们查API使用java内库的时候

查阅父类功能,创建子类对象调用方法。

我们先通过父类的共性描述可以了解整个体系中的基本功能,因为父类是最共性的描述。

为什么要创建子类对象呢1.有一些父类可能是抽象类,不能创建对象。2.子类的方法比父类多,可以用自己独有的方法,也可以用所有父类的方法。

接下来我们通过代码来看一下子类继承父类的规律

class Fu{
inti = 1;
}
class Zi extends Fu{
inti = 5;
publicvoid show(){
System.out.println(i);
}
}
new Zi().show();
继承的时候记得要写extends,别只看Fu Zi 通过名字就可以说他们是继承关系吗,一定要看关键字哈

首先调用子类的成员,现在子类中找有没有,如果有就直接用子类的,如果没有就用父类的;

思考:如果现在我就想访问父类的i呢?

这时候就要引入一个关键字

8.super关键字

super:超级的意思,父类也称为超类。所以我们要告诉虚拟机,我们访问的是哪个i

java有个就近原则。我们刚才的i先在离自己最近的去找,肯定是i=5离他最近。在一个类中,比不在一个类中近。

现在我们要访问父类的:

System.out.println(super.i);
加上super就去父类中去找i变量;

如果父类中没有呢?

class Fu{
}
就报错呗!

9.重写(复写)

刚才我们子类父类有相同的变量名,现在我们来看一下有相同的方法名

class Fu{
publicvoid show(){
System.out.println(“我是父类的show”);
}
}
class Zi extends Fu{
publicvoid show(){
System.out.println(“我是子类的show”);
}
}
现在我们用子类调用show的时候基本上跟父类的show没什么关系,他有没有都不影响。相当于我子类的show覆盖了父类的show 这一现象用专业的说法就叫重写。等于把父类的方法重新改写了;

刚才可以用super. 的方式访问父类的变量那现在我们怎么访问父类的show方法呢?

没办法,除非new一个父类对象 用父类对象来调用show

重写的实用场景:

class Phone2010{
publicvoid tell(){
System.out.println(“打电话”);
}
}
2010年的手机,就打个电话完事,现在是2017年,以前的手机已经过时了,以前的tell方法也是过时了,我现在可以打视频电话。那么现在是不是要把这个tell方法改了!可以把原来的Phone2010的tell方法改了吗?不能啊,2010年我们写的代码,现在去改,首先我们不知道它关联了多少类,而且实际开发中,改源码是最不建议干的事情;改一处可能要动很多处。那么现在我们可以用继承的重写来改

class Phone2017 extends Phone2010{
publicvoid tell(){
System.out.println(“打视频电话”);
}
}
这就是实际中的应用。

重写还能扩展功能。比如说:以前的手机只能打电话,我们现在还能聊微信,约陌陌,打游戏等。一样的复写;

class Phone2017 extends Phone2010{
publicvoid tell(){
System.out.println(“打电话”);
System.out.println(“聊微信”);
System.out.println(“约陌陌”);
System.out.println(“打游戏”);
}
}
嘿!我们发现打电话,其实以前的手机也可以打电话,这里父类帮我们做了打电话这个事情,我们可以直接调用父类的方法简化代码!

class Phone2017 extends Phone2010{
publicvoid tell(){
super.tell();
System.out.println(“聊微信”);
System.out.println(“约陌陌”);
System.out.println(“打游戏”);
}
}
他说:这也没简化什么代码啊,但是其实方法中肯定有很多代码,我这就一句,如果有很多代码就可以体会到它的好处了;

10.重写的注意事项:

1.子类重写父类方法时,必须保证子类的权限大于或等于父类权限,否则报错!

  1. 静态只能重写静态;

11.重载和重写的区别

重载:只看同名函数的参数列表;

重写:子类父类的方法要一模一样(包括返回值类型和参数列表)

重载:

publicclass Person {
public Person(){
}
public Person(String name){
}
publicvoid show(){
}
publicvoid show(inti){
}
}
只要方法名一样,参数列表不一样的,就叫重载;

重写:就是有继承关系,子类重写父类的方法。方法名和参数列表包括返回值都得一模一样

思考下面的运行结果:

publicclass Person {
publicint show(){
return 1;
}
}
class Student extends Person{
publicvoid show(){

}

}
new Student().show();
首先子类复写了父类的show方法没有?没有,因为返回值类型不一样,不是复写。

这个时候Student类中有两个方法,int show()和void show(),那么java虚拟机知道要调用哪一个show方法吗?java虚拟机都不知道,所以必须报错!一个类当中能有两个同名并且同参数列表的方法吗?不行哈

二. 构造函数的继承

直接上代码:

class Fu{
Fu(){
System.out.println(“父类的构造函数”);
}
}
class Zi extends Fu{
Zi(){
System.out.println(“子类的构造函数”);
}
}
这个时候new Zi(); 我们发现不仅调用了子类的构造函数,还执行了父类的构造函数,而且父类的还先执行!为什么会出现这个情况呢?

因为我们的子类构造函数中默认第一行有一条隐式的语句super();

Super()的意思是调用父类的无参构造函数。我们之前的this();就是调用自己的构造函数。其实super和this的用法一致,一个代表自己,一个代表父类;

注意是第一行哈;放下来就不行!跟this();一样都必须是第一行。

如果现在我们把父类的无参构造函数改成有参的构造函数;

Fu(inti){
System.out.println(“父类的构造函数”);
}
这样super();就访问不到了吧,就报错!这时候怎么办,我们可以手动写出调用父类的有参构造函数

Zi(){
super(1);
System.out.println(“子类的构造函数”);
}
接下来,继续变魔术了啊!

class Fu{
public String name;
Fu(String name){
this.name = name;
}
}
class Zi extends Fu{
Zi(String name){

}

}
这时候报错了,所以我们要加上super(“”);随便传一个字符串,我们这是不是也可以给字段赋值,this.name= name;子类继承父类,那么子类相当于也有了name 字段,所以这个this.name= name;是对的哈,有的可能不理解。这时候我们发现父类也做了赋值动作,我们子类没必要再做一次,那么就可以调用父类的这个有参构造函数。诶!我们子类本来就要调用父类的构造函数super(“”);已经调用了嘛,那么直接把name传过去让父类赋值就行了!

Zi(String name){
super(name);
}
跟this(name);差不多嘛!

现在又改一下:

class Fu{
public String name;
Fu(){}
Fu(String name){
this.name = name;
}
}
class Zi extends Fu{
Zi(){}
Zi(String name){
this();
super(name);
}
}
我们发现不管怎么弄都报错,因为this语句和super语句调用构造函数都必须在第一句,这儿就矛盾了,你要第一句,我也要第一句。他们就打架了。那么这时候又怎么办?随便去掉一句。去掉super(name);为什么不报错!我们子类的构造函数必须要访问到父类的构造函数!不然就报错,那这里为什么没报错。因为这里this();调用了自己的无参构造函数,

而无参构造函数里面有一句隐式的super();所以也拿到父类的构造函数!

总结就是:子类的构造函数必须拿到父类的任意一个构造函数!

三. final关键字

1.final:最终的意思

可以修饰类,函数,变量

被final修饰的类不可以被继承

我们之前用继承的时候,其实继承是破坏了封装性的.

class Person{
privatevoid show(){
System.out.println(“Person的show”);
}
}
class Student extends Person{
void show(){
System.out.println(“子类的show”);
}
}
虽然Person把show方法给私有化了,但是我们子类只要权限大于等于它就可以复写这个方法.我们私有化的目的就是不让外界访问,不让外界来修改,可是通过继承复写的方式打破了它的封装性!如果说是java底层的方法,我们随随便便就继承来复写了,那java就玩完了!那么这时候就引入一个关键字

2.final关键字

当一个方法用final修饰的时候,此方法不能被子类复写.

class Person{
publicfinalvoid show(){
System.out.println(“Person的show”);
}
}
想要方法不被复写就用final,跟权限没关系,跟你用private,public都没关系.

3.final修饰类

当final修饰类的时候,这个类是最终的,不能被继承

finalclass Person{
publicfinalvoid show(){
System.out.println(“Person的show”);
}
}
这个没什么可多说的就这么一个用处!实际的时候用得比较少,也不排除会有这个需求!比如说,我定义一个类,我不需要扩展功能了,他有很少的关联.以后一般也不会去改变了.可以用final修饰!(实际开发一般不用)

4.final修饰变量

被final修饰的变量只能赋值一次

finalinti;
i = 1;
说明一赋值就不能改变了!用在什么地方呢?一般用在全局常量.PI圆形里面的PI,跟static一样搭配使用.

publicstaticfinaldoublepi = 3.14;
注意:修饰成员变量的时候,定义的时候就要赋值.修饰局部变量的时候可以先定义,再赋值!

publicstaticfinaldoublepi;
5.总结

final修饰类:不能被继承

final修饰方法:不能被复写

final修饰变量:只能赋值一次(成员变量定义和赋值,局部变量可以先定义再赋值!)

四.Object

咱们说过每一个子类的构造函数,至少要调用super();那么我们平时自己写的一个类也有super();语句,那么这个super();是调用谁的构造函数,我们写的这个类的父类是谁呢?

class Person{
Person(){
// super();
}
}
说明我们每创建一个对象都是有一个父类的.那么他们的父类就是Object

查看API中的Object,通过API描述我们知道他是java中的上帝.我们用到的所有的类,包括自己创建的也好,还是之前的String 数组啊都是这个类的直接或间接子类!

那么Object是所有类的超类,那么他里边的方法应该是所有类都具备的方法.所以类都可以调用他的方法.也可以复写他的方法(有一些用final修饰了的不能被复写,说明这些方法的规矩是定死了的)

来一个假设:如果Object在底层不小心被删除了.那么整个java就崩了,我们不能定义类,不能创建对象.什么事都不能干了!

Object中有一个无参的构造方法.我们每创建一个类都是调用的这个方法!那么思考一下:Object中的构造方法有super()语句吗?没有啊!你啥时候见过上帝还有父亲的?

1.toString()方法

上帝认为:所有对象都具备一个把对象变成字符串的形式!

什么意思呢?这里要提一下

System.out.println(1);
我们以前打印一个int类型的值,打印出来的这个1并不是把int类型打印出来了.他的底层是用这个int类型的1调用了自己的toString方法.把它转成字符串打印出来.所以我们看到的所有打印出来的都是字符串形式.并不是直接把int的值打印出来.

接下来我们打印一个对象

Person p = new Person();
System.out.println(p);
打印出来的这个是个什么东西呢?cn.zhm.asd.Person@46205df9

前面我们能看懂吧.全限定名:包名+类名 @:应该是一个分隔符 46205df9:16进制的地址值,在内存中的位置的标记!咱们创建的所有对象在内存中都有一个地址值,那么这个返回地址值方法也应该在Object里边.hashCode();返回该对象的哈希码值;

它是一个哈希码值,可以理解成底层的一种特殊算法得出来的数值;那么我们打印一下这个哈希值,看是不是跟之前一样!

Person p = new Person();
System.out.println(p);
System.out.println(p.hashCode());
诶!不一样.因为一个是16进制,一个是10进制.

2.进制的转换

这里我提供几个方法:10进制转2进制 转16进制 转8进制

转换得到是字符串!

把其他进制的字符串转换成10进制的int值

Person p = new Person();
String he = Integer.toHexString(p.hashCode());
inti = Integer.parseInt(he,16);//多少进制转就写一个int值
System.out.println(i);

3.复写toString方法

Person p = new Person();
System.out.println(p);
System.out.println(Integer.toHexString(p.hashCode()));
所以地址值就是这个hashCode()在控制;回来我们的toString方法.返回地址值等字符串,这对我们来说,没什么意义!看也看不懂.一般来说:比如人,我们要打印这个人,我们要看到的是他的属性信息,姓名,年龄,等.所以Object中的toString满足不了我们的需求.那么我们可以复写他的方法!

class Person{
private String name;
privateintage;
Person(String name,intage){
this.name = name;
this.age = age;
}
}
Person p = new Person(“张三”,20);
System.out.println(p);
我们打印出来是地址值.现在我们要复写Object中的toString方法

public String toString(){
returnname+”:”+age;
}
注意啊:为什么这里我没有调用p.toString()就可以打印出姓名,年龄!

我们的打印语句System.out.println();打印一个什么东西,就自动调用他的toString方法,就相当于我们不写他也给我们加上.

System.out.println(p.toString());
加上的效果也一样.

猜你喜欢

转载自blog.csdn.net/AHAI1078766113/article/details/80938491