• 《Head First Java》读书笔记(2)


    1、了解继承

    对象继承实际上就是一种“is - a”的关系,如上图的“PantherMan is a SuperHero?”,是,那么便属于继承的理解。

    继承能避免大量重复的程序代码,同时非常利于程序扩展(子类可以在复用父类方法的同时定义额外的功能),也定义了共同的协议(确保子类拥有父类的方法)。

    另外,值得注意的是,继承是无法继承下父类的private权限的属性和方法的。
     

    2、多态的运行

    Java中的多态表现为:
    • 静态的多态:如方法重载,方法名相同,参数不同
    • 动态的多态:如子类覆盖父类方法,将子类实例传于父类的引用,调用的是子类的方法;如实现接口的实例,将其传于接口的引用,调用的是实现类的方法。

    另外,不光是引用,参数和返回类型也可以多态,但都限于子类到父类,以小到大,不能逆转。

    子类继承父类之后,对于父类方法的覆盖时,访问权限不得降低,且方法头必须相同,即方法名,参数和返回类型相同,例外的是返回类型其实也可以是该类型的子类,必须要记住,子类对象得保证能够执行父类的一切。


     

    3、深入多态

    我们说继承可以帮助我们定义共同类型的对象,比如Animal父类,Cat和Dog作为子类;

    当你new Cat我们知道出现了一只猫,当你new Dog我们知道出现了一只狗,当你new Animal?Animal是什么呢?

    所以,有些类本来就不应该被初始化,只是一个抽象的概念,但是我们又要用到它的继承来实现多态,怎么办?使用abstract对它进行抽象化。

    抽象类其实相对于接口不同的是,它可以有成员变量和具体的方法,甚至构造方法(尽管它不能被实例化),从上面这种抽象的解释来理解,其实也就比较容易了。它的最大的好处就是为了实现多态,如果某个方法我们的参数不使用父类,那么我们必须为每种子类各写一个相同内容仅仅是参数不同的方法,不光是大量重复的代码,一旦需要改动,那才是可怕。

    你要问,那干脆把所有的类型都设置为Object不就行了?反正是终极父类,不是吗?这样,实际上你很可能会意外地要求对象去执行错误类型的动作,所以Java设置了一个对代码的保护机制,即“类型安全检查”,当某个对象以父类作为引用时,Java会把它当作父类类型实例,这代表你只能调用父类中声明的方法(即,编译器是根据引用类型来判断有哪些method可以调用,而不是根据你确实的类型),比如:

    //编译器没有在Object中找到run()方法,所以第二行无法编译通过  
    //所以我们才有更多的范围更小的父类,而不是直接甩手用Object来处理
    Object cat = new Cat();
    cat.run(); // 编译无法通过

    4、认识接口

    接口的出现,解决了Java不能多重继承的特性,同时比较独特的是,接口相当于一个特殊的抽象类,它没有实例变量,也没有具体方法的实现,有的只是常量和抽象方法。

    可是这样一来,我们无法使用接口来完成所谓的“继承”,因为我们没办法复用代码,那么接口的意义在哪里呢?多态,多态,多态,重要的事情说三遍。它会要求你必须履行你们之间的“合约”,也让Java能够放心地把其实现类当作接口类型去使用。

    5、对象的生命周期

    在Java中,对象生存在堆(heap)上,实例变量是声明在类中,所以实例变量放在所属对象中。需要注意的是,如果实例变量是基本数据类型,那么变量所需要的空间是在对象中。如果实例变量是引用数据类型,那对象只会留下引用量,而引用对应的真正对象本身所要用到的空间则到堆上去取;

    方法调用及其参数,还有局部变量都生存在栈(stack)上,它们的生命周期直到方法调用至执行结束(如果方法中存在对象局部变量,忘了我们在Java基础中说过的吗?这不过是放了个遥控器般作用的引用,真正的对象只会存在堆上)。

    对象生存在堆(heap)上,它们的生命周期随着引用的结束而结束。如果一个对象没有引用(哪怕一个也没有的时候),那么它就等待着被垃圾回收器给带走。


    5.1 栈

    方法在栈中被 “堆” 在一起,当你调用一个方法时,方法就会放在栈顶,逐步堆叠,然后再逐步执行。 
     

    5.2 堆

    不管是实例变量还是局部变量,对象本身都会在堆上。而实例变量存在于对象所属的堆空间上。

    如果实例变量是基本数据类型,那么Java会根据数据类型的大小留下对应大小的空间。如果实例变量是引用数据类型,是对象,再次记住,也只是留下了对象的引用量的空间,Java不会在这个对象中为其引用对象再留出空间的。
     

    另外,实例变量是有默认值的,原始的默认值是 0 / 0.0 / false;引用的默认值是 null。而局部变量没有默认值

     

    6、构造函数和继承的关系

    每个父类都有一个构造函数,且每个构造函数都会在子类对象创建时执行。并且会随着继承链不断调用父类构造函数,最终会调用Object的构造函数。为什么会有如此的一个构造链?想一想,小孩能在父母之前出生吗?

    调用父类的构造函数,要使用 super(),如果我们没有编写,编译器也会帮我们自动加上调用。如果想要调用父类的代参构造函数,那么同样给super带参就可以了。

    如果想要调用自己类中的其他重载的构造函数,使用this(),但是this和super只能择一,不能同时调用。


  • 相关阅读:
    国产化硬件设备性能追踪
    遇到的 超时重传
    系统加载
    nginx 全景图 转载
    引擎国产化适配&重构笔记
    记录一次syn后只收到ack的情况 --timewait
    PCIe网卡查看工具
    短说 反向代理&透传代理如何关闭connect
    XDP/AF_XDP ? eBPF
    ipvs--eBPF转载
  • 原文地址:https://www.cnblogs.com/deng-cc/p/6812879.html
Copyright © 2020-2023  润新知