• 第7章-复用类


    Think in java 读书笔记

    pikzas

    2019.03.06

    第七章 复用类

    知识点

    1.JAVA中类复用的几种方式

    • 组合
    • 继承
    • 代理

    1.1.组合 将类A作为类B的属性 表达的是B中有A这么一个概念

    例如汽车Car有引擎Engine这样的关系

    class Car{
        private Engine engine;
    }
    
    1.1.1.组合带来的初始化问题

    前面提过初始化的几个步骤,这里引入了组合
    基本数据类型会在加载完成之后置空(0或者0.0或者false等)
    引用数据类型会在加载完成之后置为null

    引用类型初始化的四种方式

    • 定义对象的地方 (此种方式能够在构造器被调用之前就执行完成)
    class Demo{
        private int i = 21;
    }
    
    
    • 类的构造器中
    class Demo{
        private int i;
        public Demo(){
            i = 22;
        }
    }
    
    • 正要使用这些对象之前(也成为惰性初始化)有些对象没必要在创建对象的时候就初始化,或者是消耗太多资源的情况下。
    class Demo{
        private SomeThingBig someThingBig;
        public void method(){
            if(someThingBig==null){
                someThingBig = new SomeThingBig();
                //一些消耗资源的初始化操作
            }
        }
    }
    
    
    • 实例初始化
    class Demo{
        private int x;
        {
            x = 123;
        }
    }
    

    1.2.继承 将类A作为类B的子类 表达的是类A是B类的一个子集的概念 所有类都是默认继承自Object类,所以Object里的方法都能用。

    例如红色Red是颜色Color的一种

    class Red extends Color{
        
    }
    
    1.2.1.继承的一些说明 子类对于父类的属性和方法的访问权限遵循包类访问权限控制

    所以推荐将父类的属性设置为private,仅仅是父类自己可以访问。
    将方法设置为public,那样所有人都可以访问

    1.2.2.protected关键字

    前一章提到过,如果出现了一个属性或者方法,除了给自己基类用之外,还想给子类访问,那么就是设置成protected,注意protected同时兼具默认包访问权限。

    1.2.3.父子类的初始化

    子类对象中其实包含有父类的对象
    编译器会自动在子类的构造器中调用父类的构造器(针对的是无参构造器)
    如果想要调用有参构造器或者是父类仅仅有有参构造器,那么在子类构造器中必须手动调用父类的构造方法。

    public class A {
    }
    public class B extends A {
        public B(){
            System.out.print("B");
        }
    }
    
    public class C extends B {
        public C(int i){
            System.out.print("C");
        }
    }
    
    public class D extends C {
        public D(int i) {
    //        System.out.println("D"); // 打印语句不能放在这里,应为对象D还未初始化完成,会提示编译错误。
            super(i);
            System.out.print("D");
        }
    
        public static void main(String[] args) {
            A t = new D(1);
        }
    }
    // 输出 BCD
    
    
    1.2.4.super关键字

    子类中如果想要获取父类的引用,可用super来实现。

    class Parent{
        public void f(){
            print("parent");
        }
    }
    
    class Son extends Parent{
        public void f(){
            print("son");
            super.f(); // 如果不用super来表明我要调用父类,该方法会调用自己,从而死循环
        }
        public static void main(String[] args){
            Son s = new Son();
            s.f();
        }
    }
    
    1.2.5.如果在父子类存在的情况下保证正确的清理顺序

    执行类的清理顺序的时候,顺序要求与生成顺序相反

    1.2.6.重载overload与重写(覆盖)override
    • overload指的是同一个类中方法名相同,但是方法的参数列表不同(参数的类型,个数,顺序),用来将同名方法区分开。
    • override指的是子类中方法名和参数列表与父类都相同,并且可以加上@override注解,表明子类对父类方法的修改。
    组合与继承的选择,组合表达的是xxx has a yyy的意思,继承表达的是xxx is a yyy的意思
    1.2.7.向上转型

    由于子类与父类在继承图上,父类位于上部,子类位于下部。方法的入参或者返回值为一个基类,那么我们可以传入或者返回一个该基类的子类。

    1.2.8.继承与初始化

    类的代码在初次使用的时候才会加载,通常指创建类的第一个对象的时候或者是访问类中static的属性或者方法的时候(构造方法是隐式static的)

    1.2.9.在继承结构中类初始化过程
    package com.pikzas.thinkinjava.chapterseven;
    
    public class Insect {
        private int i = 9;
        protected int j;
        Insect(){
            System.out.println("i = " + i +" , j = " + j);
            j = 47;
        }
        private static int x1 = printInit("static Insect x1 init");
        static int printInit(String s){
            System.out.println(s);
            return 99;
        }
    }
    
    
    
    public class Bettle extends Insect {
        private int k = printInit("None Static field init");
        public Bettle(){
            System.out.println("k = " + k );
            System.out.println("j = " + j );
        }
        private static int x2 = printInit("static Bettle x2 init");
    
        public static void main(String[] args) {
            System.out.println("Bettle main method");
            Bettle b = new Bettle();
        }
    }
    

    运行bettle 中main的结果是:

    static Insect x1 init
    static Bettele x2 init
    Bettle main method
    i = 9 , j = 0
    None Static field init
    k = 99
    j = 47
    

    分析加载过程

    • 第一步,要调用Bettle中main方法,导致Bettle.class被加载。
    • 第二步,发现Bettle继承自父类Insect.class被加载。
    • 第三步,先将最上层的类Insect中的static属性和方法在内存中初始化,非static的属性会置为默认值(打印static Insect x1 init)
    • 第四步,然后由上而下将类中的static属性和方法初始化非static的属性会置为默认值(打印static Bettele x2 init)
    • 第五步,调用Bettle的main方法(打印Bettle main method)
    • 第六步,发现调用Bettle的构造方法,此时会触发默认的父类Insert的构造方法
    • 第七步,想要调用Insect的构造方法,就需要将Insect类中所有成员初始化完成,然后才会调用构造方法(打印i = 9 , j = 0)
    • 第八步,由上往下调用构造器,初始化所有成员变量(打印None Static field init)
    • 第九步,调用最外层的构造器Bettle()(打印k = 99 j = 47)

    1.3.代理 调用类A的方法时,B作为A的一个属性,将其委托给B去执行

    例如发动汽车Car,实际执行者是Engine

    class Car{
        private Engine engine;
        
        public void start(){
            engine.start();
        }
    }
    

    2.final关键字 想表达的意识是无法改变的

    • 作用在属性上
    • 作用在方法上
    • 作用在class上

    2.1.加在属性上

    如果是基本数据类型,如果在编译的时候就给定了值,并且是static的,那么他就是编译期常量,如果对象初始化才开始给定值,一旦初始化就不会再变化。
    如果是引用数据类型(包括数组),指的是该变量所指向的内存地址不会再变动,但是该对象内部属性还是可变的。

    2.1.1编译期常量

    满足三点要求的变量可以成为编译期常量

    1. 基本数据类型 有初始值
    2. static修饰 只有一份
    3. final修饰 是个常量
      常用大写字母加下划线特别表明
    2.1.2.空白final

    java允许我们在声明一个变量为final的前提下但是不给定初始值,但是在使用前,必须要指定值,这样增加了灵活性,也就必然要使用到构造器初始化。

    class Demo{
        private int i;
        public Demo(){
            i = 1;
        }
        public Demo(int x){
            i = x;
        }
    }
    

    2.2.加在方法参数上

    准确的说是加在方法上的参数列表的修饰符上,表明这个对象不能在方法中变动。否则会报错。

    class Demo{
        public void method(final int i){
            i = 123; // 这里尝试修改i的值,会报错。
        }
    }
    

    2.3.加在方法

    加在方法上的表明该方法不能被重写

    class Demo{
        public final void method(int i){
            i = 123;
        }
    }
    
    class Son extends Demo{
        // 会提示编译器错误 因为demo中method为final 这里不能再重写
        
        public void method(int i){
    
        }
    }
    
    

    看另一个例子

    class Parent{
        // private 隐式包含了final的意思 但是也是可以同时写出来的
        private final void f(int x){}
    }
    
    class Son extends Parent{
        //这时候如果写了Override,此时会报编译错误,因为此时并不是子类对父类的f()做了重写,因为Parent中f方法为private的,重写指的是对父类接口的重新实现,而父类方法是一个private的私有方法,不是一个对外的接口,外部调用不到,更不存在需要复写的理由了。
        @Override
        private final void f(int y){}
    }
    
    2.3.1.final和private关键字

    private修饰的方法隐式的包含了final的意思

    2.4.加在类上

    表示该类不能被继承,其中的方法也不能被覆盖(都不能继承,咋覆盖),其中的属性依据需要,决定是否是final的。

  • 相关阅读:
    使用git bash提交代码到github托管
    eclipse中将java项目变成web项目
    程序员的兼职网站
    python 文件内容修改替换操作
    简单介绍下python中函数的基础语法
    python lambda简单介绍
    微信小程序 --01
    微信小程序开发 --02
    弹性盒模型flex
    HTML基础
  • 原文地址:https://www.cnblogs.com/Pikzas/p/11247630.html
Copyright © 2020-2023  润新知