黑马程序员-面向对象(继承与多态)
2013-10-30 21:42
316 查看
----------------------
ASP.Net+Android+IOS开发、.Net培训、期待与您交流!----------------------
java的封装原因和好处
在PersonDemo类当中,直接操作new了一个Person,那么可以直接通过对象名.属性去给这个对象赋值,但是为什么这样不好,需要把类内部的属性封装起来,不然外部直接操作呢。这点一直不太理解,总有多此一举的感觉。因为封装了,通过set或get也一样外部能够操作和更改值,而且这样还比较麻烦。之前看视频的时候,说原因是可以添加检查,排除一些非法的输入,不怎么理解,现在理解了,如果设置一个set方法的话,那么方法体里面可以增加一些if语句去检查输入是否合乎常理,避免出现非法数据,这样程序就没意义,而暴露在外部的话,if语句不能添加这样的功能,区别就在这。
主函数的定义:
public:代表着该函数访问权限是最大的。
static:代表主函数在类的加载前就已经存在了。
void:主函数没有具体的返回值。
main:不是关键字,但是是一个特殊的单词,可以被jvm识别。
(String[]arr):函数的参数,参数类型是一个数组,该数组中的元素是字符串。字符串类型的数组。
jvm在调用主函数时,传入的是newString[0];
主函数是固定格式的:jvm识别。(除开args这个变量可以不一样之外,其余都必须一样)
主函数的格式是固定的,所以在一个类当中,虚拟机首先会查找存不存在main(String[]args),如果不存在,则不会启动这个类,上面的代码提示,,所以不算重载,程序也不能运行
运行时,
(1)从左到右执行,先在栈内存中为变量p1开辟一个空间
(2)因为new用到了person.class,所以虚拟机会先找到person.class这个文件加载进来
(3)首先进行的是静态初始化,为类进行初始化
(4)然后为对象在堆内存分配相应的空间,分配内存地址
(5)在堆内存中建立对象的特有属性,然后进行默认初始化
(6)对象内部的成员属性开始显式初始化
(7)对象开始执行构造代码块,为对象初始化
(8)构造函数开始执行,为特定对象初始化
(9)把该对象的堆内存地址传给栈内存中的p1
不是父类中所有方法都被继承,构造方法不能继承
父类的私有属性不能被继承(实际上是继承了,只是不能被子类直接使用)
如果子类中存在与父类同名的属性或者方法时,在子类创建对象的时候,默认调用的是子类中的属性和方法,但是如果想要调用父类的属性或者方法,可以通过super()关键字
我的理解:我是不是你产生的
格式:对象instanceof类返回值是boolean型
假设存在classA,存在classBextendsA
即A是B的父类
Aa1=newB();
System.out.println(a1instanceofA);
System.out.println(a1instanceofB);
Aa2=newA();
System.out.println(a2instanceofA);
System.out.println(a2instanceofB);
输出为:
True
True
False
False
(1)多态的体现
父类的引用指向了自己的子类对象
换句话说,父类的引用也可以接收自己的子类对象
语法实现形式:Animala=newcat();
(2)
多态的前提,什么时候使用多态
必须是类与类之间有关系,要么继承,要么实现,通常可以把多个类的共同属性封装好,然后再去进行继承,继而实现多态
还有一个大前提,就是覆盖
(3)多态的好处
多态的出现大大提高了程序的扩展性
(4)多态的应用
(5)多态的弊端:多态有好处也有局限性
提高了扩展性的同时,弊端在于只能使用父类的引用访问父类中的成员
abstractclassAnimal
{
abstractvoideat();//抽象方法不用实现,也没有大括号
}
抽象类作为父类,里面只有一个成员函数,局限性就在此体现,不能为别的动物提供特有的个性化的成员函数
可以说优点因为这个,缺点也是因为这个
类里面的方法如果不确定的话,就定义为抽象类,由使用者自己去覆写实现
此时编译应该是不通过的,提示错误f.method3();找不到
所以这一点应该是有所区别的,我们在思考的时候应该注意区分编译和运行时代码的情况
错误的写法:
假设A是B的父类
Aa=newA();
Bb=(B)a;
此时编译报错
正确的写法:
Aa=newB();//必须先存在向上转型,才可以向下转型
Bb=(B)a;
在运行时期:参阅对象所属的类中是否有调用的方法
简单总结就是:静态成员函数在多态调用时,编译看左边,运行时看左边。
非静态成员函数在多态调用时,编译看左边,运行时看右边。
以上总结只正对类中的方法来说,如果是针对属性变量,下面再讲解
这个的输出是:
5
8
无论编译和运行,都参考左边(其实静态和非静态都适用)。
在多态中,静态成员函数的特点:(区分静态和非静态)
静态:编译时期,检查参考左边引用变量的类,在运行阶段,还是访问左边
非静态:编译时期,检查参考左边引用变量的类,但在运行阶段,访问的实际上是右边对象中的方法
java中的继承中的问题,不支持多继承,但是支持多层继承,两者有什么区别呢,下面代码表示的是多继承,但是会出问题
这个比较好理解,B继承了A,那么B里面具有A的功能,接着C继承了B,那么C里面也就有了B的内容,那么这个继承里面就相当于有了三层,可以理解为C直接继承了B间接继承了A
这个举个例子会比较好理解,一个人只能有一个生物学父亲,不可能同时有几个生物学上的父亲,但是,你父亲也有自己的父亲,也就是你爷爷,你父亲继承你爷爷,然后你继承你父亲,这就是多层继承
聚合:学生是班级的一部分
组合:手是身体的一部分
两者在逻辑上并没有严格的区分,但是两者在联系程度上组合比聚合要紧密地多,有不可分割的意思
但是子类虽具备该功能,但是功能的内容却和父类不一致,
这时,没有必要定义新功能,而是使用覆盖特殊,保留父类的功能定义,并重写功能内容。
覆盖:
(1)子类覆盖父类,必须保证子类权限大于等于父类权限,才可以覆盖,否则编译失败。
(2)静态只能覆盖静态,
重载:只看同名函数的参数列表。
重写:子父类方法要一模一样。(除开方法体内容以外)
子父类中的构造函数。
在对子类对象进行初始化时,会先行自动调用父类的构造函数,然后再调用子类的构造函数
那是因为子类的构造函数默认第一行有一条隐式的语句super();
super():会访问父类中空参数的构造函数。
而且子类中所有的构造函数默认第一行都是super();
所以有个建议,如果要自定义一个父类,这个父类的构造函数是带参数的,然后用子类继承,那么最好给父类手动加上一个空参数的构造函数,否则,子类在初始化的时候可能会报错,因为子类在实例化时会自动执行父类中的空参构造函数
为什么子类一定要访问父类中的构造函数。
因为父类中的数据子类可以直接获取。所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的。
所以子类在对象初始化时,要先访问一下父类中的构造函数。
如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。
注意:super语句一定定义在子类构造函数的第一行。
结论:
子类的所有的构造函数,默认都会访问父类中空参数的构造函数。
因为子类每一个构造函数内的第一行都有一句隐式super();
当父类中没有空参数的构造函数时,子类必须手动通过super语句形式来指定要访问父类中的构造函数。
当然,子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数。
子类中至少会有一个构造函数会访问父类中的构造函数。
----------------------
ASP.Net+Android+IOS开发、.Net培训、期待与您交流!----------------------详细请查看:http://edu.csdn.net
ASP.Net+Android+IOS开发、
java的封装原因和好处
classPerson{ privateintage; publicvoidsetAge(inta){ if(a>0&&a<130){ age=a; speak(); }else System.out.println("feifaage"); } publicintgetAge(){ returnage; } privatevoidspeak(){ System.out.println("age="+age); } } classPersonDemo{ publicstaticvoidmain(String[]args){ Personp=newPerson(); //p.age=-20; p.setAge(-40); //p.speak(); } }
在PersonDemo类当中,直接操作new了一个Person,那么可以直接通过对象名.属性去给这个对象赋值,但是为什么这样不好,需要把类内部的属性封装起来,不然外部直接操作呢。这点一直不太理解,总有多此一举的感觉。因为封装了,通过set或get也一样外部能够操作和更改值,而且这样还比较麻烦。之前看视频的时候,说原因是可以添加检查,排除一些非法的输入,不怎么理解,现在理解了,如果设置一个set方法的话,那么方法体里面可以增加一些if语句去检查输入是否合乎常理,避免出现非法数据,这样程序就没意义,而暴露在外部的话,if语句不能添加这样的功能,区别就在这。
主函数的定义:
public:代表着该函数访问权限是最大的。
static:代表主函数在类的加载前就已经存在了。
void:主函数没有具体的返回值。
main:不是关键字,但是是一个特殊的单词,可以被jvm识别。
(String[]arr):函数的参数,参数类型是一个数组,该数组中的元素是字符串。字符串类型的数组。
jvm在调用主函数时,传入的是newString[0];
主函数是固定格式的:jvm识别。(除开args这个变量可以不一样之外,其余都必须一样)
classMainDemo{
publicstaticvoidmain(String[]args){
main(1);
}
publicstaticvoidmain(intargs)
{
System.out.println(args);
}
}
这个算不算重载呢,或者会不会发生重载?
那么到底虚拟机运行的时候会执行哪一个主函数的格式是固定的,所以在一个类当中,虚拟机首先会查找存不存在main(String[]args),如果不存在,则不会启动这个类,上面的代码提示,,所以不算重载,程序也不能运行
当句子执行时候,虚拟机的内部的运行执行机制是如何的?
例:Personp1=newPerson("zhangsan",17);运行时,
(1)从左到右执行,先在栈内存中为变量p1开辟一个空间
(2)因为new用到了person.class,所以虚拟机会先找到person.class这个文件加载进来
(3)首先进行的是静态初始化,为类进行初始化
(4)然后为对象在堆内存分配相应的空间,分配内存地址
(5)在堆内存中建立对象的特有属性,然后进行默认初始化
(6)对象内部的成员属性开始显式初始化
(7)对象开始执行构造代码块,为对象初始化
(8)构造函数开始执行,为特定对象初始化
(9)把该对象的堆内存地址传给栈内存中的p1
继承extends
被final修饰的类不能被继承不是父类中所有方法都被继承,构造方法不能继承
父类的私有属性不能被继承(实际上是继承了,只是不能被子类直接使用)
如果子类中存在与父类同名的属性或者方法时,在子类创建对象的时候,默认调用的是子类中的属性和方法,但是如果想要调用父类的属性或者方法,可以通过super()关键字
Instancedof关键字:
判断一个对象到底属于哪个类的实例我的理解:我是不是你产生的
格式:对象instanceof类返回值是boolean型
假设存在classA,存在classBextendsA
即A是B的父类
Aa1=newB();
System.out.println(a1instanceofA);
System.out.println(a1instanceofB);
Aa2=newA();
System.out.println(a2instanceofA);
System.out.println(a2instanceofB);
输出为:
True
True
False
False
(1)多态的体现
父类的引用指向了自己的子类对象
换句话说,父类的引用也可以接收自己的子类对象
语法实现形式:Animala=newcat();
(2)
多态的前提,什么时候使用多态
必须是类与类之间有关系,要么继承,要么实现,通常可以把多个类的共同属性封装好,然后再去进行继承,继而实现多态
还有一个大前提,就是覆盖
classCat
{
publicvoideat()
{
System.out.println("吃鱼");
}
publicvoidcatchMouse()
{
System.out.println("抓老鼠");
}
}
classDog
{
publicvoideat()
{
System.out.println("吃骨头");
}
publicvoidkanJia()
{
System.out.println("看家");
}
}
//观察以上两个类,作为动物,都具有吃的本能,所以这个功能可以抽取出来,封装成抽象类,然后再进行继承
abstractclassclassAnimal
{
abstractvoideat();
}
(3)多态的好处
多态的出现大大提高了程序的扩展性
(4)多态的应用
(5)多态的弊端:多态有好处也有局限性
提高了扩展性的同时,弊端在于只能使用父类的引用访问父类中的成员
abstractclassAnimal
{
abstractvoideat();//抽象方法不用实现,也没有大括号
}
抽象类作为父类,里面只有一个成员函数,局限性就在此体现,不能为别的动物提供特有的个性化的成员函数
可以说优点因为这个,缺点也是因为这个
类里面的方法如果不确定的话,就定义为抽象类,由使用者自己去覆写实现
多态的编译和运行的结果情况分析
classFu
{
staticintnum=5;
staticvoidmethod1()
{
System.out.println("fumethod_1");
}
staticvoidmethod2()
{
System.out.println("fumethod_2");
}
staticvoidmethod4()
{
System.out.println("fumethod_4");
}
}
classZiextendsFu{
staticintnum=8;
staticvoidmethod1()
{
System.out.println("zimethod_1");
}
staticvoidmethod3()
{
System.out.println("zimethod_3");
}
staticvoidmethod4()
{
System.out.println("zimethod_4");
}
}
classDuoTaiDemo4
{
publicstaticvoidmain(String[]args)
{
Fuf=newZi();//用父类的引用接收子类对象
f.method1();
f.method2();
f.method3();
}
}
此时编译应该是不通过的,提示错误f.method3();找不到
那是为什么会出现这个情况呢(重点理解记忆)
应该要从编译的机制解释,java虚拟机编译的时候,语法检查首先会在f他的Fu类中查找method1();method2();method3();这三个方法,如果都存在,并且语法没有错误,则编译通过,但是在实际运行时,再检查类中的方法有没有被static修饰,如果被static修饰,(假设编译通过的情况下)调用的是父类中的method1();method3();,如果没有被static修饰,调用的是子类Zi中的method1();method3();而不是父类中的所以这一点应该是有所区别的,我们在思考的时候应该注意区分编译和运行时代码的情况
重点注意,关于向上转型和向下转型:
在对对象发生向下转型的时候,必须是已经发生了向上转型的前提下,才可以向下转型,否则编译会报错。打个比方,一个对象好比一个人,转型就好比上楼梯,刚开始都是在平地上,我必须上了一级楼梯,我才能往下走一级楼梯,在平地上是不能再往下的了错误的写法:
假设A是B的父类
Aa=newA();
Bb=(B)a;
此时编译报错
正确的写法:
Aa=newB();//必须先存在向上转型,才可以向下转型
Bb=(B)a;
在多态中成员函数的特点:(需要区分静态函数和非静态函数)
在编译时期:参阅引用型变量所属的类中是否有调用的方法如果有,编译通过,如果没有编译失败。在运行时期:参阅对象所属的类中是否有调用的方法
简单总结就是:静态成员函数在多态调用时,编译看左边,运行时看左边。
非静态成员函数在多态调用时,编译看左边,运行时看右边。
以上总结只正对类中的方法来说,如果是针对属性变量,下面再讲解
classFu
{
staticintnum=5;
}
classZiextendsFu
{
staticintnum=8;
}
classDuoTaiDemo4
{
publicstaticvoidmain(String[]args)
{
Fuf=newZi();//父类引用接收子类对象
System.out.println(f.num);//输出结果为父类的nun值
Ziz=newZi();
System.out.println(z.num);
}
}
这个的输出是:
5
8
其实这里是多态需要注意一下的地方
准确来说,继承后子类实现要分情况讨论,一般是指没有发生转型的情况下,子类引用接收子类对象,如今发生了向上转型,父类引用接收子类对象,那么在编译和运行的时候,因为左右不统一了,所以要参考左边的类所属变量,这个是比较特殊的情况结论:
在多态中,成员变量的特点:无论编译和运行,都参考左边(其实静态和非静态都适用)。
在多态中,静态成员函数的特点:(区分静态和非静态)
静态:编译时期,检查参考左边引用变量的类,在运行阶段,还是访问左边
非静态:编译时期,检查参考左边引用变量的类,但在运行阶段,访问的实际上是右边对象中的方法
java中的继承中的问题,不支持多继承,但是支持多层继承,两者有什么区别呢,下面代码表示的是多继承,但是会出问题
classA
{
voidshow()
{
System.out.println("a");
}
}
classB
{
voidshow()
{
System.out.println("b");
}
}
classCextendsA,B
{}
Cc=newC();
c.show();
//如果java允许支持这个多继承的话,上面的代码会让虚拟机出错,因为并不知道C中的show()是继承哪一个类,所以会报错
//下面说到多层继承,把以上代码修改一下
classA
{
voidshow()
{
System.out.println("a");
}
}
classBextendsA
{
voidmianPrint()
{
System.out.println("b");
}
}
classCextendsB
{}
Cc=newC();
c.show();
这个比较好理解,B继承了A,那么B里面具有A的功能,接着C继承了B,那么C里面也就有了B的内容,那么这个继承里面就相当于有了三层,可以理解为C直接继承了B间接继承了A
这个举个例子会比较好理解,一个人只能有一个生物学父亲,不可能同时有几个生物学上的父亲,但是,你父亲也有自己的父亲,也就是你爷爷,你父亲继承你爷爷,然后你继承你父亲,这就是多层继承
事物间的关系
除开继承之外,还有聚合和组合聚合:学生是班级的一部分
组合:手是身体的一部分
两者在逻辑上并没有严格的区分,但是两者在联系程度上组合比聚合要紧密地多,有不可分割的意思
重写(覆盖)
当子类继承父类,沿袭了父类的功能,到子类中,但是子类虽具备该功能,但是功能的内容却和父类不一致,
这时,没有必要定义新功能,而是使用覆盖特殊,保留父类的功能定义,并重写功能内容。
覆盖:
(1)子类覆盖父类,必须保证子类权限大于等于父类权限,才可以覆盖,否则编译失败。
(2)静态只能覆盖静态,
重载:只看同名函数的参数列表。
重写:子父类方法要一模一样。(除开方法体内容以外)
子父类中的构造函数。
在对子类对象进行初始化时,会先行自动调用父类的构造函数,然后再调用子类的构造函数
那是因为子类的构造函数默认第一行有一条隐式的语句super();
super():会访问父类中空参数的构造函数。
而且子类中所有的构造函数默认第一行都是super();
所以有个建议,如果要自定义一个父类,这个父类的构造函数是带参数的,然后用子类继承,那么最好给父类手动加上一个空参数的构造函数,否则,子类在初始化的时候可能会报错,因为子类在实例化时会自动执行父类中的空参构造函数
示例:
classCar
{
StringMyColor;
Car(){}//此处可能需要定义一个空参的构造方法,否则下面子类创建对象时可能会编译不通过
Car(StringMyColor)//有参数的构造方法
{
this.MyColor=MyColor;
}
publicvoidrun()
{
System.out.println("模型开动");
}
}
classBaoMaCarextendsCar
{
BaoMaCar(StringMyColor)
{
this.MyColor=MyColor;//如果用这一句,则对象初始化的时候会调用父类的空参构造方法,因为父类中已经定义了有参的构造方法,所以在编译的时候,编译器不会自动添加空参构造方法,所以必须要给父类定义一个空参的构造方法,否则编译报错
//super(MyColor);//可以如果用这一句,父类中可以没有空参的构造方法
}
publicvoidrun()
{
System.out.println("宝马开动");
}
}
classMyCarDemo
{
publicstaticvoidmain(String[]args)
{
Carc=retCar(1);
c.run();
}
publicstaticCarretCar(intnum)
{
if(num==1)
returnnewBaoMaCar("白色宝马");
if(num==2)
returnnewBaoShiJieCar("红色保时捷");
elsereturnnewCar("白色模型车");
}
}
为什么子类一定要访问父类中的构造函数。
因为父类中的数据子类可以直接获取。所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的。
所以子类在对象初始化时,要先访问一下父类中的构造函数。
如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。
注意:super语句一定定义在子类构造函数的第一行。
结论:
子类的所有的构造函数,默认都会访问父类中空参数的构造函数。
因为子类每一个构造函数内的第一行都有一句隐式super();
当父类中没有空参数的构造函数时,子类必须手动通过super语句形式来指定要访问父类中的构造函数。
当然,子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数。
子类中至少会有一个构造函数会访问父类中的构造函数。
----------------------
ASP.Net+Android+IOS开发、
相关文章推荐
- 黑马程序员学习日记 (四)面向对象三大特征: 封装 继承 多态
- 黑马程序员------面向对象之封装 继承 多态
- 黑马程序员---java基础之面向对象(一)三大特征(封装,继承,多态)
- 黑马程序员_面向对象(继承、多态、内部类)
- 黑马程序员_java封装-继承-多态为面向对象的三大基石
- 黑马程序员——java-面向对象二(继承,多态,抽象,接口,包,内部类)
- 黑马程序员—面向对象(static,封装,继承,多态,内部类,抽象类,接口)总结
- 黑马程序员-面向对象(上)--继承、实现及多态
- 黑马程序员-------面向对象的封装、继承、多态一些学习笔记
- 黑马程序员-面向对象(封装,继承,多态)
- 黑马程序员——OC语言学习——OC面向对象的三大特性:封装、继承、多态,OC字符串——NSString
- 黑马程序员—面向对象的一些特征、继承、接口与多态
- 黑马程序员-面向对象三大原则——封装、继承、多态
- 黑马程序员----Java基础之面向对象(封装 继承 多态)
- 黑马程序员——Java面向对象的特征:继承与多态
- 黑马程序员_JAVA之面向对象(封装,继承和多态)
- 黑马程序员java学习笔记——面向对象的特征封装、继承和多态
- 黑马程序员-----------------面向对象_封装、继承、多态
- 黑马程序员----------面向对象之封装继承多态
- 黑马程序员——Java基础---面向对象(继承、多态、抽象类、接口、内部类)