java中多态的理解参考

多态存在的三个前提:
1.要有继承关系
2.子类要重写父类的方法
3.父类引用指向子类对象

在向上引用时,子类的同名非静态成员方法与成员变量会覆盖父类的同名非静态成员方法和成员变量,子类的同名静态成员方法会被父类的同名静态成员方法隐藏,也就是说被覆盖。例如下面例子中static修饰的子类sleep方法
可以通过 super 关键字在子类中显式调用父类中被覆盖的非静态成员方法和成员变量。

首先我们定义两个类,一个父类Animal,一个子类Cat。父类Animalclass

class Animal {
    int num = 10;
    int age = 20;

    public void eat() {
        System.out.println("动物吃饭");
    }

    public static void sleep() {
        System.out.println("动物在睡觉");
    }

    public void run() {
        System.out.println("动物在奔跑");
    }
}

子类Cat

class Cat extends Animal {
    int num = 80;
    int age = 90;
    String name = "tomCat";

    public void eat() {
        System.out.println("猫吃饭");
    }

    public static void sleep() {
        System.out.println("猫在睡觉");
    }

    public void catchMouse() {
        System.out.println("猫在抓老鼠");
    }
}

测试类Demo_Test1

class Demo_Test1 {
    public static void main(String[] args) {
        Animal am = new Cat();
        am.eat();
        am.sleep();
        am.run();
        //am.catchMouse();这里先注释掉,等会会说明
        //System.out.println(am.name);//这里先注释,待会说明
        System.out.println(am.num);
        System.out.println(am.age);
        System.out.println("---------------");
        Cat ct=(Cat)am;
        System.out.println(ct.name);
        ct.catchMouse();
    }
}

以上的三段代码充分体现了多态的三个前提,即:
1、存在继承关系Cat类继承了Animal类
2、子类要重写父类的方法子类重写(override)了父类的两个成员方法eat(),sleep()。其中eat()是非静态的,sleep()是静态的(static)。
3、父类数据类型的引用指向子类对象。
测试类Demo_Test1中 Animal am = new Cat();语句在堆内存中开辟了子类(Cat)的对象,并把栈内存中的父类(Animal)的引用指向了这个Cat对象。到此,满足了Java多态的的必要三个前提。

------------------------------------华丽的分割线---------------------------------

如果再深究一点呢,我们可以看看上面测试类的输出结果,或许对多态会有更深层次的认识。猜一猜上面的结果是什么。
可以看出来子类Cat重写了父类Animal的非静态成员方法—am.eat();的输出结果为:猫吃饭。
子类重写了父类(Animal)的静态成员方法 —am.sleep();的输出结果为:动物在睡觉
未被子类(Cat)重写的父类(Animal)方法—am.run()输出结果为:动物在奔跑

System.out.println(am.num);//输出结果为10
System.out.println(am.age);//输出结果为20

那么我们可以根据以上情况总结出多态成员访问的特点:

        在多态中,成员变量的特点: 无论编译和运行,都参考左边(引用型变量所属的类)
        在多态中,静态成员函数的特点:无论编译和运行,都参考做左边。
        在多态中,非静态成员函数的特点:编译看左边,运行看右边。

我们总结了一套口诀:“成员变量,静态方法看左边;非静态方法:编译看左边,运行看右边”意思是:当父类变量引用子类对象时(Fu f = new Zi();),在这个引用变量 f 指向的变量/方法中,他对成员变量和静态方法的调用与父类是一致的,他调用非静态方法时,在编译时是与父类一致的(查看父类有没有该函数,没有就会发生编译错误,提示fu 中找不到要调用函数),运行时如果子类中发生了复写就与子类一致。(如果右边没有再看左边。若都没有才会报错)

(静态和类相关,算不上重写,所以,访问还是左边的)
只有非静态的成员方法,编译看左边,运行看右边

------------------------------------华丽的分割线----------------------------------

那么多态有什么弊端呢?有的,即多态后不能使用子类特有的属性和方法。往上面的代码看,子类Cat有一个特有的属性String name = “tomCat”; 并且还有一个特有的抓老鼠的方法catchMouse()。但是在测试类(Demo_Test)中,我们尝试调用子类特有的方法catchMouse()和打印子类特有的成员属性String name = “tomCat”; 就会报错。
am.catchMouse();
System.out.println(am.name);

原因就是多态的弊端,就是:不能使用子类特有的成员属性和子类特有的成员方法。
-------------------------------华丽的分割线-----------------------------------------------------
如果在代码执行过程中还想使用Cat类中特有的属性String name和它特有的成员方法catchMouse()了怎么办呢?那我们就可以把这个父类引用指向了子类对象的家伙am再强制变回Cat类型。这样am就是Cat类型的引用了,指向的也是Cat对象了,自然也能使用Cat类的一切属性和一切的成员方法。

很明显,执行强转语句Cat ct = (Cat)am;之后,ct就指向最开始在堆内存中创建的那个Cat类型的对象了。这就是多态的魅力吧,虽然它有缺点,但是它确实十分灵活,减少多余对象的创建,不用说为了使用子类的某个方法又去重新再堆内存中开辟一个新的子类对象。以上。。

接口引用指向实现类的对象https://blog.csdn.net/m0_37307255/article/details/79908109

IDEA中for循环的快捷键:https://blog.csdn.net/ck4438707/article/details/52243471

在哈希图里使用拉姆达表达式:

        Map<String , String> map = new HashMap<>();
        map.put("a","a");
        map.put("b","b");
        map.put("c","c");
        map.put("d","d");
        map.put("e","e");
        map.put("f","f");
        map.forEach((k,v)-> {
            System.out.println("k=" + k + ",v=" + v);
        });

在集合中使用拉姆达表示

        List<String> list = new ArrayList<>();
        list.add("Array1");
        list.add("Array2");
        list.add("Array3");
        System.out.println("拉姆达表达式:");      
        list.forEach(a-> System.out.println(a));

1、final修饰的类不可以被继承,但可以继承其他的类。

2、final修饰的方法子类可以继承但是不能重写。

3、子类重写父类的非final方法可以加上final。

4、被final修饰的基本数据类型的变量可以看作是常量,赋值后不能改变。

5、被final修饰的引用数据类型变量的引用内存地址值不能改变,可以改变引用数据类型变量的属性值。

6、被final修饰的成员变量必须在对象创建完成前进行赋值,可以直接赋值,如果没有直接赋值则需要用构造方法进行赋值,如果有多个构造方法则多个构造方法都要为其赋值,但是不能用set方法赋值。

7、成员变量在堆内存中是有默认值的,final固定的是成员变量的手动赋值不是内存中的默认值。

8、被final修饰的静态成员变量只能直接赋值或者通过静态代码块赋值。

A是一个类,我们如果对他进行实例化,需要这样写: A a = new A();
详细解释一下这个语句,首先等号左边做的事情:在JVM栈内存(stack)中定义了一个变量a。等号右边:在JVM堆内存(heap)中通过new关键字
开辟了一个空间,存放创建的实例,并得到了一个存放该实例的内存地址。
stack中的a会被赋值为heap中刚刚那个实例对象的内存地址,换句话说,这个变量a其实是reference
variable(对象引用),功能相当于指针,记录实例对象的地址。

复习链表时对声明方法有点疑问
理解完第一件事,我们就会明白A a;语句中的变量a相当于指向A的实例化对象的指针。
类中定义的成员变量(成员变量为方法体之外,内类的非静态变量)不用初始化,java会自动初始化。
初始化规则:如果是数字会自动初始化成0,字符会初始化成’o’,对象引用会初始化成null. 所以我们可以省略初始化new
A();这部分的写法。 结论:
class Node
{
Node next;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值