程序地带

多态



多态
基本数据类型的对象缓存字符串缓存池面向对象设计思想的要点
多态性对象的多态性多态在代码中的体现
类间多态和类内多态类间多态性类内多态性
方法的重写和重载方法的重写(覆盖)方法的重载多态的优点


基本数据类型的对象缓存

在Integer类定义中查看源代码可以发现一个定义


private static class IntegerCache {}这实际上就是Integer的cache
Integer num1 = 12; //自动装箱
Integer num2 = 12; //块相等,<=127都是真的
System.out.println(num1 == num2);

是因为在Integer中包含有一个缓存池,缓存值为-128到127之间。


定义Integer k1=12是先在缓存池中查找12这个对象,如果有则直接使用new Integer(12)一定会引发对象的创建,而不管缓存池中是否包含这个对象
Integer k1 = 12;
Integer k2 = 12;
System.out.println(k1 == k2);//true,k1和k2都是引用类型,所以==比较的是地址
Integer k3=new Integer(12);
System.out.println(k1==k3);//false
Integer k1 = 129;
Integer k2 = 129;
System.out.println(k1 == k2);//false,因为缓存只缓存-128到127之间的数据

IntegerLongShortByte中都有缓存池


字符串缓存池

String中包含一个缓存池,当使用某个字符串对象时会首先在缓存池中进行查找,如果存在则直接返回这个对象的地址;如果不存在则会在缓存池中进行创建,创建完成后返回地址


String s1="abc";
String s2="abc";
String s3=new String("abc");//这里会创建2个对象,一个在缓存池中,一个是new导致的新创建对象
System.out.println(s1==s2);//true
System.out.println(s1==s3);//false
String s1="abc";
String s3="ab";
String s2=s3+"c";
System.out.println(s1==s2); //false

注意:如果通过字串拼接所得内容和某个字串内容一致,但是地址不同


面向对象设计思想的要点

认为客观世界由各种对象组成,任何事物都是对象,复杂的对象可以由比较简单的对象以某种方式组合而成


把所有对象都划分成各种对象类,每个对象类都定义了一组数据和一组方法按照子类与父类的关系,把若干个对象类组成一个层次结构的系统对象彼此之间仅能通过传递消息互相联系

依赖关系 一个类中,使用到了另外一个类 B类方法中使用了A类,此时B类依赖于A类


public class B{
public void pp(){
A a=new A();
a.abc();
}
}

关联关系 某个类的属性是另外一个类 B类中的属性是A类的引用,此时B类和A类发生了关联关系


public class B{
private A a;
}

继承关系 某个类继承了另外一个类 B类继承了A类,此时B类和A类的关系是继承关系


多态性

多态性是发送消息给某个对象,让该对象自行决定响应何种行为。通过将子类对象引用赋值给超类对象引用变量来 实现动态方法调用


public class Test1 {
public static void main(String[] args) {
Fa cc=new Fa();
//调用的是一个名称为pp的方法,但是参数不同执行的处理不同
cc.pp();//Fa...pp
cc.pp(12);//Fa.pp(int)
}
}
class Fa{
public void pp(){
System.out.println("Fa...pp");
}
public void pp(int k){
System.out.println("Fa.pp(int)");
}
}

多态性通过允许同一界面指定一类动作减少了程序的复杂度,编译器工作就是选择适用于各个情况的特定动作,而程序员则无须手动进行选择,使用者仅仅是记得以及利用这个统一的界面


多态可以用三个定义和两个方法来总结。三个定义分别是父类定义子类构建、接口定义实现类构建和抽象类定义实体类构建,而两个方法分别是方法重载和方法重写。


多态分为两种:


编译时多态:方法的重载运行时多态:JAVA运行时系统根据调用该方法的实例的类型来决定选择调用哪个方法则被称为运行时多态
对象的多态性

继承表示的是is a的语义


public class 汽车 extends 车库{} //错误的
public class 学生 extends 人{} //正确的,因为任何一个学生都是人

强调的一个点:编程是对现实世界的模拟体现


class 动物{}
class 猫 extends 动物{}
猫 x=new 猫();
动物 x=new 猫(); //正确的,因为所有的猫都是动物,因为定义class 猫 extends 动物{}
猫 x=new 动物(); //语法错误,不是所有的动物都是猫,因为动物是猫的父类

一个对象x有两种形态,猫这类事物即具有猫的形态,又具有着动物的形态,这就是对象的多态性。简单说就是一个对象对应着不同类型


Object x=new Student();
Person x=new Student();
Student x=new Student();
多态在代码中的体现

父类或者接口的引用指向其子类的对象。例如:动物 x=new 猫()


public class Test1 {
public static void main(String[] args) {
动物 x1 = new 动物();
猫 x3=new 猫();//语法正确
// 猫 x4=new 动物(); 语法错误
动物 x2 = new 猫();// 猫extends动物,表示猫具有动物的所有特性,同时猫具备一些自己都有的特性
// x2.play();猫的类中定义了方法play()但是语法报错。语法报错的原因是:编译期类型和运行时类型的问题。因x2声明的是动物类型,所以编译器识别x2是动物类型,编译时会检查动物中是否定义play方法,如果没有定义则报错
if(x2 !=null && x2 instanceof 猫) //instanceof用于判断x2是否为指定的类型,如果是返回
true,否则false
((猫)x2).play();
}
}
class 动物 {}
class 猫 extends 动物 {
public void play() {
System.out.println("使劲的抓老鼠....");
}
}
public class Test1 {
public static void main(String[] args) {
动物 x2 = new 猫();// 猫extends动物,表示猫具有动物的所有特性,同时猫具备一些自己都有的特性
x2.play();
}
}
class 动物 {
public void play() {
System.out.println("使劲的睡觉....");
}
}
class 猫 extends 动物 {
}
public class Test1 {
public static void main(String[] args) {
动物 x2 = new 猫();// 猫extends动物,表示猫具有动物的所有特性,同时猫具备一些自己都有的特性
x2.play(); //使劲的抓老鼠...
//这里语法不报错的原因是动物类中定义了play方法,但是真正运行时系统则会发现实际上是猫,所以需要调用的不是动物类中的方法,而是猫中定义方法
}
}
class 动物 {
public void play() {
System.out.println("使劲的睡觉....");
}
}
class 猫 extends 动物 {
public void play() { //子类中覆盖父类中的方法定义
System.out.println("使劲的抓老鼠...");
}
}
动物 dw = new 猫();
dw.play();//如果猫类中没有定义play方法,则执行的是从动物类中继承的play方法;如果猫类中也定义play方法,则最终执行的是猫类中定义的方法
//dw.抓苍蝇()语法错误,因为在编译时系统识别dw为动物类型,所以没有这个方法的定义
if (dw instanceof 猫) {
((猫)dw).抓苍蝇(); //强制类型转换后则可以调用猫类中所定义的特殊方法,否则只能调用动物类中定义的
方法
}
静态方法中不能直接访问非静态成员,但是普通方法允许直接调用其他成员方法,当然也允许直接调用静态方法
public class A{
public static void pp(){}
public void aa(){
this.pp();
}
}
A a=new A(); a.pp();
A.pp()

对象多态性可以使程序有良好的扩展,并可以对所有类的对象进行通用处理


public class Test1 {
public static void main(String[] args) {
需要使用动物的类 tt=new 需要使用动物的类();
tt.pp(new 动物());
tt.pp(new 猫());
}
}
class 需要使用动物的类 {
public void pp(动物 dongwu){ //假设定义方法时参数使用pp(猫 dongwu),当前程序就和猫类耦合了,如果需要切换到狗则必须修改源代码。但是参数类型为动物,则不管是猫还是狗都能接收
dongwu.play();//不能直接调用特殊方法,必须是动物类中声明的方法
}
}
多态引用时,构造子类对象时的构造方法的调用顺序

** 父类 bi=new 子类(); bi可以直接调用父类中声明的方法,但是具体执行的方法取决于new 的是谁。如果需要调用子类中的特殊方法,则必须先进行强制类转换


**先调用超类的构造方法,多重超类首先调用最远超类的方法


**然后再执行当前子类的构造方法


类间多态和类内多态

多态是同一个行为具有多个不同表现形式或形态的能力。多态就是同一个接口,使用不同的实例而执行不同操作


类间多态性

父子类之间的一种多态型,例如:动物 x = new 猫()


public class Test1 {
public static void main(String[] args) {
Animal a1=new Cat();
A a=new A();
a.eee(a1);
}
}
class A{
public void eee(Animal animal){
animal.eat();
}
}
class Animal{
public void eat(){
System.out.println("Animal.eat()");
}
}
class Cat extends Animal{
@Override
public void eat() {
System.out.println("就是爱老鼠");
}
}
class Dog extends Animal{
@Override
public void eat() {
System.out.println("就是爱肉骨头");
}
}
方法调用中的限制: 针对一个类对象有两种类型,一种称之为编译期类型,编译时系统识别对象的类型,”动物 x = new 猫()”在编译时,系统识别x是动物类别的,所以只能调用动物类定义的方法,而不能调用猫中特殊的方法。另外一种称之为运行时类型,也就是当程序运行系统识别的类型,new谁就是谁覆盖的方法一定不能是private的
类内多态性

在一个类对象上调用相同名称的方法,但是当参数不同时执行不同的动作


public class Test2 {
public static void main(String[] args) {
A2 a=new A2();
// a.pp(); 调用的方法为pp()
// a.pp(12); 调用的方法为pp(int)
a.pp("shitou"); //调用的方法为pp(String)
}
}
//在Java中定位一个方法需要方法名称和参数类型列表两部分,和参数名称无关
class A2 {
public void pp() {
System.out.println("A2.pp()");
}
public void pp(int k) {
System.out.println("A2.pp(int)");
}
public void pp(String kk) {
System.out.println("A2.pp(String)");
}
public void pp(int k1,String k2){}
public void pp(String k1,int k2){}
}
public class Test1 {
public static void main(String[] args) {
Fa ff=new Fa();
ff.pp(123);//调用pp(Integer)
ff.pp(new Integer(123)); //原则:最佳匹配原则
ff.pp(1.234);
}
}
class Fa {
public void pp() {
System.out.println("Fa.pp()");
}
public void pp(Number kk){ //Number是Integer的父类,所以任意的Integer对象都是Number类型的
System.out.println("Fa.pp(Number)");
}
public void pp(Integer kk){
System.err.println("Fa.pp(Integer)");
}
}

Java中定义方法允许使用…表示不确定个数的参数


不确定个数的参数必须是方法声明时最后一个参数,一个方法中只允许一个不确定个数的参数
public class Test1 {
public static void main(String[] args) {
Fa ff = new Fa();
ff.pp(); //最佳匹配原则
}
}
class Fa {
public void pp() {
System.out.println("Fa.pp()");
}
public void pp(int... pages) {// 不确定个数的参数
System.out.println("Fa.pp(int...)");
}
}
方法的重写和重载
方法的重写(覆盖)

要求:方法的名称一致 方法的重写(覆盖)一定发生在父子类之间


public class Test4 {
public static void main(String[] args) {
F4 f=new F4(); f.pp(10);
S4 s=new S4(); s.pp(10);
F4 fs=new S4(); fs.pp(10);
}
}
class F4 {
public void pp(int k) {
System.out.println("F4.pp(int)");
}
}
class S4 extends F4 {
public void pp(int k) { //子类中定义的同名同参数的方法覆盖了父类中的方法定义,如果需要调用父
类中的方法则需要使用super.pp(k)进行调用
System.out.println("S4.pp(int)");
}
}
执行规则:new谁运行谁的方法,和声明的类型无关,由具体创建对象的类型决定方法的覆盖定义要求方法名称一致
public class Test1 {
public static void main(String[] args) {
Fa f2 = new Son();
f2.pp(12);// Son中的pp方法定义覆盖了Fa中的pp方法,new的是Son,所以执行的是Son中的
方法
}
}
class Fa {
public void pp(Number kk) {
System.out.println("Fa...");
}
// public void pp(Integer kk) {
// System.out.println("Fa...pp");
// }
}
class Son extends Fa {
public void pp(Integer kk) {
System.out.println("Son...");
}
}
//子类中方法范围要求大于等于父类中的方法范围,不允许private //返回类型一致,子类方法可以小于等于父类类型,例如父类Number,子类Integer //参数类型一致
@Override//注解可以使IDE工具在编译源代码时进行检查,如果有手写错误则IDE工具报错
方法的参数一致(个数、类型、顺序),和参数名称无关
//类型一致的问题
class Fa {
public void eat(Integer kk) {
System.out.println("Animal.eat()");
}
}
class Son extends Fa {
@Override
public void eat(Number kk) { //类型必须一致,即使父类类型都不可以,int和Integer简单类型和包装类型也不可以。这里去除@override注解则不会有语法错误,这里不是方法的重写,是方法的重载
System.out.println("就是爱老鼠");
}
}
//顺序一致的问题,系统识别方法依靠是方法名称和参数类型列表,和方法参数的名称无关。例如这里系统
识别的方法为ear(String,String)。要识别顺序还得依靠类型的区别,例如eat(int,double)和
eat(double,int)
class Fa {
public void eat(String s1,String s2) {
System.out.println("Animal.eat()");
}
}
class Son extends Fa {
@Override
public void eat(String s2,String s1) {//系统不能识别变量名称
System.out.println("就是爱老鼠");
}
}
返回数据类型一致【面试】(因为如果返回类型不一致,则无法进行语法检查,例如父类返回Double,而子类返回Integer,调用处语法检查是按照Double进行检查还是按Integer检查?允许父类中返回的是父类型,而子类中返回子类型,例如父类中返回的是Number类型,而子类中返回的是Integer)
class Fa {
public Number eat(double s1,int s2) {
System.out.println("Animal.eat()");
return 10.;
}
}
class Son extends Fa {
@Override
public Integer eat(double s2,int s1) { //允许子类中返回值类型是父类返回值类型的子类型
System.out.println("就是爱老鼠");
return 99;
}
}

抛出异常一致,注意实际上允许子类抛出比父类更少的异常


静态方法覆盖和调用::用谁声明则调用谁的静态方法
只有静态方法覆盖定义父类中的静态,实际上系统不建议这种做法
//The method pp() of type Son must override or implement a supertype method
public class Test1 {
public static void main(String[] args) {
// 直接使用具体类调用静态方法没有任何问题,使用哪个类就调用的是哪个类中定义的静态方法
// Fa.pp();Fa...pp
// Son.pp();Son...pp
// 事实上静态方法也可以通过创建对象后,使用对象进行调用。声明变量的类型和具体构建的类
型一致,也不会有问题
// Fa ff=new Fa();
// ff.pp();
//如果调用静态方法,则用谁声明调用谁的方法
Fa ff = new Son();
ff.pp();//执行结果是Fa...pp
}
}
class Fa {
public static void pp() {
System.out.println("Fa...pp");
}
}
class Son extends Fa {
public static void pp() {
System.out.println("Son...pp");
}
}
方法的重载

方法的名称相同,参数不同,和返回值类型无关。可以在一个类内或者父子类之间 调用规则:类型最佳匹配原则


class A5 {
public void pp(){
System.out.println("A5.pp()");
}
public int pp(){} //语法错误,因为在系统中识别一个方法是用【方法名称+参数类型列表】进行,系
统会将这两个pp方法识别为同一个方法。注意:在一个类中不允许方法相同
public void pp(int k){
System.out.println("A5.pp(int)");
}
}
class B5 extends A5 {
public void pp(String k){
System.out.println("B5.pp(String)");
}
public void pp(String k,int k1){}
public void pp(int k,String k1){}
}
要求:方法名称相同并且参数不同。参数不同有3种情况:参数个数不同、参数类型不同、参数顺序不同 **和参数名称无关和返回类型无关 为什么返回类型不同判断不同的方法? 例如调用方法时不需要处理返回值pp(10),容易造成混淆
public double pp(int k){}
public int pp(int k){}
一个方法有返回值,调用处实际上可以不接收。假设系统可以按照返回类型将这两个方法识别为不同方法,则调用pp(10)就没办法判断到底执行哪个
和范围无关
public void pp(){}
protected void pp(){}//语法报错
和方法抛出的异常无关方法的重载可以出现父子类之间,也可以是在一个类内。但是方法的覆盖一定是父子类之间,不能在一个类内实现方法的覆盖
多态的优点
消除类型之间的耦合关系可替换性、可扩充性接口性、灵活性、简化性
public class Test1 {
public static void main(String[] args) {
Fa ff = new Son();
ff.pp(10);因为在父类中声明的方法为Integer类型,所以在执行前会自动执行装箱操作,所以调用
的是从父类中继承到的pp(Integer),而不是最佳匹配的pp(int)
}
}
class Fa {
protected void pp() {
System.out.println("Fa.pp()");
}
public void pp(Integer kk){
System.out.println("Fa.pp(Integer)");
}
}
class Son extends Fa {
public int pp(int k) {
System.out.println("Son.pp(int)");
return 10;
}
}
public class Test1 {
public static void main(String[] args) {
Fa ff = new Son();
ff.pp(10);
}
}
class Fa {
protected void pp() {
System.out.println("Fa.pp()");
}
public void pp(Integer kk){
System.out.println("Fa.pp(Integer)");
}
}
class Son extends Fa {
public void pp(Integer kk){
System.out.println("Son.pp(Integer)");
}
public int pp(int k) {
System.out.println("Son.pp(int)");
return 10;
}
}

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Liuxin2448/article/details/112689128

随机推荐

JVM系列-第7章-对象的实例化内存布局与访问定位

JVM系列-第7章-对象的实例化内存布局与访问定位

文章目录对象的实例化内存布局与访问定位对象的实例化对象创建的方式对象创建的步骤对象的内存布局对象的访问定位1、文章可能会优先更新在Github,个人博客,公众号【Githu...

youthlql 阅读(553)

Netty源码学习 - netty服务端通道的实例化流程

系列文章目录Netty源码学习-netty服务端通道的实例化流程Netty源码学习-netty服务端通道的初始化流程Netty源码学习-netty服务端通道的注册流程文章目录系列文章目录一、创建mav...

lang20150928 阅读(935)

php数组10000分割1000_PHP对MongoDB数据的处理

公司业务需求,对接MySQL和MongoDB合并使用,但是前台对数据格斯要求是按照一种格式取数据,MySQL关系型数据很简单就拼接成了,但是mo...

weixin_39630247 阅读(918)

static、final关键字、构造方法的定义

static关键字static:静态的1.用于修饰变量语法:publicstatic变量类型变量名[=初始值];在修饰变量时只能修饰全局变量(成员变量...

adsdsasada 阅读(715)

子集和问题。

问题描述:给定集合S,S中有n个正整数,M是一个正整数。子集和问题判定是否存在S的一个子集S1,使得S1中各元素之和等于M。请设计回溯法求解子集...

yidaa2 阅读(747)

经历过的有意思的技术问题-面试专用

硬件资源使用受限(nvu)简述:在使用硬件加速功能时,对于一个硬件来说,处理能力是有上限的,当两个模块都想使用同一个...

xiaonuo911teamo 阅读(309)