精华内容
下载资源
问答
  • 对象被动的接受依赖类
    2021-03-13 23:45:29

    说说依赖注入

    在面向对象编程中,我们经常处理处理的问题就是解耦,程序的耦合性越低表明这个程序的可读性以及可维护性越高。控制反转(Inversion of Control或IoC)就是常用的面向对象编程的设计原则,使用这个原则我们可以降低耦合性。其中依赖注入是控制反转最常用的实现。

    什么是依赖

    依赖是程序中常见的现象,比如类Car中用到了GasEnergy类的实例energy,通常的做法就是在Car类中显式地创建GasEnergy类的实例,并赋值给energy。如下面的代码

    interface Energy {

    }

    class GasEnergy implements Energy {

    }

    class Car {

    Energy energy = new GasEnergy();

    }

    存在问题

    类Car承担了多余的责任,负责energy对象的创建,这必然存在了严重的耦合性。举一个现实中的例子,一辆汽车使用哪种能源不是由汽车来决定,而是由汽车制造商(CarMaker)来决定,这是汽车制造商的责任。

    可扩展性,假设我们想修改能源为电动力,那么我们必然要修改Car这个类,明显不符合开放闭合原则。

    不利于单元测试。

    依赖注入

    依赖注入是这样的一种行为,在类Car中不主动创建GasEnergy的对象,而是通过外部传入GasEnergy对象形式来设置依赖。 常用的依赖注入有如下三种方式

    构造器注入

    将需要的依赖作为构造方法的参数传递完成依赖注入。

    class Car {

    Energy mEnergy;

    public Car(Energy energy) {

    mEnergy = energy;

    }

    }

    Setter方法注入

    增加setter方法,参数为需要注入的依赖亦可完成依赖注入。

    class Car {

    Energy mEnergy;

    public void setEnergy(Energy energy) {

    mEnergy = energy;

    }

    }

    接口注入

    接口注入,闻其名不言而喻,就是为依赖注入创建一套接口,依赖作为参数传入,通过调用统一的接口完成对具体实现的依赖注入。

    interface EnergyConsumerInterface {

    public void setEnergy(Energy energy);

    }

    class Car implements EnergyConsumerInterface {

    Energy mEnergy;

    public void setEnergy(Energy energy) {

    mEnergy = energy;

    }

    }

    接口注入和setter方法注入类似,不同的是接口注入使用了统一的方法来完成注入,而setter方法注入的方法名称相对比较随意。

    框架取舍

    依赖注入有很多框架,最有名的就是Guice,当然Spring也支持依赖注入。Guice采用的是运行时读取注解,通过反射的形式生成依赖并进行注入。这种形式不太适合Android移动设备,毕竟这些操作都在运行时处理,对性能要求较高。

    Dagger则是Android开发适合的依赖注入库,其同样采用类注解的形式,不同的是它是在编译时生成辅助类,等到在运行时使用生成的辅助类完成依赖注入。

    用还是不用

    其实注入框架用还是不用,是一个问题,如若使用框架,则要求团队每一个人都要遵守说明来编写代码解决依赖注入。而这些框架其实也并非很容易就能上手,学习系数相对复杂,难以掌握,这也是需要考虑的问题。

    个人观点为不推荐也不反对使用这些框架,但是觉得有些时候我们寄希望于一个框架,不如平时注意这些问题,人为避免何尝不是对自己的一种基本要求呢?

    依赖查找

    依赖查找和依赖注入一样属于控制反转原则的具体实现,不同于依赖注入的被动接受,依赖查找这是主动请求,在需要的时候通过调用框架提供的方法来获取对象,获取时需要提供相关的配置文件路径、key等信息来确定获取对象的状态。

    以上就是对依赖注入的资料详细介绍,后续继续补充相关资料,谢谢大家对本站的支持!

    更多相关内容
  • 依赖注入

    千次阅读 2019-02-22 16:47:02
    一、什么是依赖注入? 1. 依赖 如果在 Class A 中,有 ...例如下面 Human 中用到一个 Father 对象,我们就说 Human 对 Father 有一个依赖。 public class Human { ... Father father; ... public H...

    一、什么是依赖注入?

    1. 依赖 如果在 Class A 中,有 Class B 的实例,则称 Class A 对 Class B 有一个依赖。例如下面类 Human 中用到一个 Father 对象,我们就说类 Human 对类 Father 有一个依赖。

    public class Human {
        ...
        Father father;
        ...
        public Human() {
            father = new Father();
        }
    }

    仔细看这段代码我们会发现存在一些问题:
    (1). 如果现在要改变 father 生成方式,如需要用new Father(String name)初始化 father,需要修改 Human 代码;
    (2). 如果想测试不同 Father 对象对 Human 的影响很困难,因为 father 的初始化被写死在了 Human 的构造函数中;
    (3). 如果new Father()过程非常缓慢,单测时我们希望用已经初始化好的 father 对象 Mock 掉这个过程也很困难。

    2. 依赖注入 上面将依赖在构造函数中直接初始化是一种 Hard init 方式,弊端在于两个类不够独立,不方便测试。我们还有另外一种 Init 方式,如下:

    public class Human {
        ...
        Father father;
        ...
        public Human(Father father) {
            this.father = father;
        }
    }

    上面代码中,我们将 father 对象作为构造函数的一个参数传入。在调用 Human 的构造方法之前外部就已经初始化好了 Father 对象。像这种非自己主动初始化依赖,而通过外部来传入依赖的方式,我们就称为依赖注入。
    现在我们发现上面 1 中存在的两个问题都很好解决了,简单的说依赖注入主要有两个好处:
    (1). 解耦,将依赖之间解耦。
    (2). 因为已经解耦,所以方便做单元测试,尤其是 Mock 测试。

    二、依赖注入原理--IoC框架

    1 IoC理论的背景
        我们都知道,在采用面向对象方法设计的软件系统中,它的底层实现都是由N个对象组成的,所有的对象通过彼此的合作,最终实现系统的业务逻辑。

    图1:软件系统中耦合的对象


        如果我们打开机械式手表的后盖,就会看到与上面类似的情形,各个齿轮分别带动时针、分针和秒针顺时针旋转,从而在表盘上产生正确的时间。图1中描述的就是这样的一个齿轮组,它拥有多个独立的齿轮,这些齿轮相互啮合在一起,协同工作,共同完成某项任务。我们可以看到,在这样的齿轮组中,如果有一个齿轮出了问题,就可能会影响到整个齿轮组的正常运转。
        齿轮组中齿轮之间的啮合关系,与软件系统中对象之间的耦合关系非常相似。对象之间的耦合关系是无法避免的,也是必要的,这是协同工作的基础。现在,伴随着工业级应用的规模越来越庞大,对象之间的依赖关系也越来越复杂,经常会出现对象之间的多重依赖性关系,因此,架构师和设计师对于系统的分析和设计,将面临更大的挑战。对象之间耦合度过高的系统,必然会出现牵一发而动全身的情形。

    图2:对象之间复杂的依赖关系


        耦合关系不仅会出现在对象与对象之间,也会出现在软件系统的各模块之间,以及软件系统和硬件系统之间。如何降低系统之间、模块之间和对象之间的耦合度,是软件工程永远追求的目标之一。为了解决对象之间的耦合度过高的问题,软件专家Michael Mattson提出了IOC理论,用来实现对象之间的“解耦”,目前这个理论已经被成功地应用到实践当中,很多的J2EE项目均采用了IOC框架产品spring


    2 什么是控制反转(IoC) 
        IOC是Inversion of Control的缩写,多数书籍翻译成“控制反转”,还有些书籍翻译成为“控制反向”或者“控制倒置”。 
        1996年,Michael Mattson在一篇有关探讨面向对象框架的文章中,首先提出了IOC 这个概念。对于面向对象设计及编程的基本思想,前面我们已经讲了很多了,不再赘述,简单来说就是把复杂系统分解成相互合作的对象,这些对象类通过封装以后,内部实现对外部是透明的,从而降低了解决问题的复杂度,而且可以灵活地被重用和扩展。IOC理论提出的观点大体是这样的:借助于“第三方”实现具有依赖关系的对象之间的解耦,如下图:

     

     图3:IOC解耦过程


        大家看到了吧,由于引进了中间位置的“第三方”,也就是IOC容器,使得A、B、C、D这4个对象没有了耦合关系,齿轮之间的传动全部依靠“第三方”了,全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成“粘合剂”的由来。
    我们再来做个试验:把上图中间的IOC容器拿掉,然后再来看看这套系统:
     

    图4:拿掉IoC容器后的系统


        我们现在看到的画面,就是我们要实现整个系统所需要完成的全部内容。这时候,A、B、C、D这4个对象之间已经没有了耦合关系,彼此毫无联系,这样的话,当你在实现A的时候,根本无须再去考虑B、C和D了,对象之间的依赖关系已经降低到了最低程度。所以,如果真能实现IOC容器,对于系统开发而言,这将是一件多么美好的事情,参与开发的每一成员只要实现自己的类就可以了,跟别人没有任何关系!
        我们再来看看,控制反转(IOC)到底为什么要起这么个名字?我们来对比一下:
        软件系统在没有引入IOC容器之前,如图1所示,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。
        软件系统在引入IOC容器之后,这种情形就完全改变了,如图3所示,由于IOC容器的加入,对象A与对象B之间失去了直接联系,所以,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。
        通过前后的对比,我们不难看出来:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转”这个名称的由来。

     

    3 IOC的别名:依赖注入(DI) 
       2004年,Martin Fowler探讨了同一个问题,既然IOC是控制反转,那么到底是“哪些方面的控制被反转了呢?”,经过详细地分析和论证后,他得出了答案:“获得依赖对象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入。于是,他给“控制反转”取了一个更合适的名字叫做“依赖注入(Dependency Injection)”。他的这个答案,实际上给出了实现IOC的方法:注入。所谓依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。

     

    所以,依赖注入(DI)和控制反转(IOC)是从不同的角度的描述的同一件事情,就是指通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦。
        我们举一个生活中的例子,来帮助理解依赖注入的过程。大家对USB接口和USB设备应该都很熟悉吧,USB为我们使用电脑提供了很大的方便,现在有很多的外部设备都支持USB接口。

     

    图6:USB接口和USB设备

     

        现在,我们利用电脑主机和USB接口来实现一个任务:从外部USB设备读取一个文件。
        电脑主机读取文件的时候,它一点也不会关心USB接口上连接的是什么外部设备,而且它确实也无须知道。它的任务就是读取USB接口,挂接的外部设备只要符合USB接口标准即可。所以,如果我给电脑主机连接上一个U盘,那么主机就从U盘上读取文件;如果我给电脑主机连接上一个外置硬盘,那么电脑主机就从外置硬盘上读取文件。挂接外部设备的权力由我作主,即控制权归我,至于USB接口挂接的是什么设备,电脑主机是决定不了,它只能被动的接受。电脑主机需要外部设备的时候,根本不用它告诉我,我就会主动帮它挂上它想要的外部设备,你看我的服务是多么的到位。这就是我们生活中常见的一个依赖注入的例子。在这个过程中,我就起到了IOC容器的作用。
        通过这个例子,依赖注入的思路已经非常清楚:当电脑主机读取文件的时候,我就把它所要依赖的外部设备,帮他挂接上。整个外部设备注入的过程和一个被依赖的对象在系统运行时被注入另外一个对象内部的过程完全一样。
    我们把依赖注入应用到软件系统中,再来描述一下这个过程:
        对象A依赖于对象B,当对象 A需要用到对象B的时候,IOC容器就会立即创建一个对象B送给对象A。IOC容器就是一个对象制造工厂,你需要什么,它会给你送去,你直接使用就行了,而再也不用去关心你所用的东西是如何制成的,也不用关心最后是怎么被销毁的,这一切全部由IOC容器包办。
        在传统的实现中,由程序内部代码来控制组件之间的关系。我们经常使用new关键字来实现两个组件之间关系的组合,这种实现方式会造成组件之间耦合。IOC很好地解决了该问题,它将实现组件间关系从程序内部提到外部容器,也就是说由容器在运行期将组件间的某种依赖关系动态注入组件中。

     

      4 IOC为我们带来了什么好处

         我们还是从USB的例子说起,使用USB外部设备比使用内置硬盘,到底带来什么好处?
         第一、USB设备作为电脑主机的外部设备,在插入主机之前,与电脑主机没有任何的关系,只有被我们连接在一起之后,两者才发生联系,具有相关性。所以,无论两者中的任何一方出现什么的问题,都不会影响另一方的运行。这种特性体现在软件工程中,就是可维护性比较好,非常便于进行单元测试,便于调试程序和诊断故障。代码中的每一个Class都可以单独测试,彼此之间互不影响,只要保证自身的功能无误即可,这就是组件之间低耦合或者无耦合带来的好处。
         第二、USB设备和电脑主机的之间无关性,还带来了另外一个好处,生产USB设备的厂商和生产电脑主机的厂商完全可以是互不相干的人,各干各事,他们之间唯一需要遵守的就是USB接口标准。这种特性体现在软件开发过程中,好处可是太大了。每个开发团队的成员都只需要关心实现自身的业务逻辑,完全不用去关心其它的人工作进展,因为你的任务跟别人没有任何关系,你的任务可以单独测试,你的任务也不用依赖于别人的组件,再也不用扯不清责任了。所以,在一个大中型项目中,团队成员分工明确、责任明晰,很容易将一个大的任务划分为细小的任务,开发效率和产品质量必将得到大幅度的提高。
         第三、同一个USB外部设备可以插接到任何支持USB的设备,可以插接到电脑主机,也可以插接到DV机,USB外部设备可以被反复利用。在软件工程中,这种特性就是可复用性好,我们可以把具有普遍性的常用组件独立出来,反复利用到项目中的其它部分,或者是其它项目,当然这也是面向对象的基本特征。显然,IOC不仅更好地贯彻了这个原则,提高了模块的可复用性。符合接口标准的实现,都可以插接到支持此标准的模块中。
         第四、同USB外部设备一样,模块具有热插拔特性。IOC生成对象的方式转为外置方式,也就是把对象生成放在配置文件里进行定义,这样,当我们更换一个实现子类将会变得很简单,只要修改配置文件就可以了,完全具有热插拨的特性。
    以上几点好处,难道还不足以打动我们,让我们在项目开发过程中使用IOC框架吗?

    5 IOC容器的技术剖析
        IOC中最基本的技术就是“反射(Reflection)”编程,目前.Net C#、Java和PHP5等语言均支持,其中PHP5的技术书籍中,有时候也被翻译成“映射”。有关反射的概念和用法,大家应该都很清楚,通俗来讲就是根据给出的类名(字符串方式)来动态地生成对象。这种编程方式可以让对象在生成时才决定到底是哪一种对象。反射的应用是很广泛的,很多的成熟的框架,比如象Java中的hibernate、Spring框架,.Net中 NHibernate、Spring.NET框架都是把“反射”做为最基本的技术手段。
        反射技术其实很早就出现了,但一直被忽略,没有被进一步的利用。当时的反射编程方式相对于正常的对象生成方式要慢至少得10倍。现在的反射技术经过改良优化,已经非常成熟,反射方式生成对象和通常对象生成方式,速度已经相差不大了,大约为1-2倍的差距。
        我们可以把IOC容器的工作模式看做是工厂模式的升华,可以把IOC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用编程语言的的反射编程,根据配置文件中给出的类名生成相应的对象。从实现来看,IOC是把以前在工厂方法里写死的对象生成代码,改变为由配置文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。
    6 IOC容器的一些产品
        Sun ONE技术体系下的IOC容器有:轻量级的有Spring、Guice、Pico Container、Avalon、HiveMind;重量级的有EJB;不轻不重的有JBoss,Jdon等等。Spring框架作为Java开发中SSH(Struts、Spring、Hibernate)三剑客之一,大中小项目中都有使用,非常成熟,应用广泛,EJB在关键性的工业级项目中也被使用,比如某些电信业务。
        .Net技术体系下的IOC容器有:Spring.Net、Castle等等。Spring.Net是从Java的Spring移植过来的IOC容器,Castle的IOC容器就是Windsor部分。它们均是轻量级的框架,比较成熟,其中Spring.Net已经被逐渐应用于各种项目中。
    7 使用IOC框架应该注意什么
        使用IOC框架产品能够给我们的开发过程带来很大的好处,但是也要充分认识引入IOC框架的缺点,做到心中有数,杜绝滥用框架。
        第一、软件系统中由于引入了第三方IOC容器,生成对象的步骤变得有些复杂,本来是两者之间的事情,又凭空多出一道手续,所以,我们在刚开始使用IOC框架的时候,会感觉系统变得不太直观。所以,引入了一个全新的框架,就会增加团队成员学习和认识的培训成本,并且在以后的运行维护中,还得让新加入者具备同样的知识体系。
        第二、由于IOC容器生成对象是通过反射方式,在运行效率上有一定的损耗。如果你要追求运行效率的话,就必须对此进行权衡。
        第三、具体到IOC框架产品(比如:Spring)来讲,需要进行大量的配制工作,比较繁琐,对于一些小的项目而言,客观上也可能加大一些工作成本。
        第四、IOC框架产品本身的成熟度需要进行评估,如果引入一个不成熟的IOC框架产品,那么会影响到整个项目,所以这也是一个隐性的风险。
        我们大体可以得出这样的结论:一些工作量不大的项目或者产品,不太适合使用IOC框架产品。另外,如果团队成员的知识能力欠缺,对于IOC框架产品缺乏深入的理解,也不要贸然引入。最后,特别强调运行效率的项目或者产品,也不太适合引入IOC框架产品,象WEB2.0网站就是这种情况。

     

    转载地址:

    https://www.cnblogs.com/jhli/p/6019895.html

    https://www.cnblogs.com/sunzhao/p/8334008.html

    展开全文
  • JVM加载机制和创建对象的过程

    千次阅读 2018-02-13 14:29:39
    1、JVM运行和加载过程一、的加载机制加载机制:JVM把class文件加载到内存,并对数据进行校验,解析和初始化,最终形成JVM 可以直接使用的Java类型的过程。主要有三步:加载、连接、初始化。其中连接又可以细分...

    1、JVM运行和类加载过程

    一、类的加载机制

    类加载机制:JVM把class文件加载到内存,并对数据进行校验,解析和初始化,最终形成JVM 可以直接使用的Java类型的过程。

     

    主要有三步:加载、连接、初始化。其中连接又可以细分为:验证、准备和解析。

    加载:类的加载是指把.class文件中的二进制数据读入到内存(方法区)中,将字节流代表的静态存储结构转化成方法区的运行时数据结构,之后在堆区创建一个(java.lang.Class)Class对象,作为访问方法区的类信息的接口。

    链接:将Java类的二进制代码合并到JVM的运行时数据的过程

    (1)验证

      确保加载的类信息符合JVM规范,没有安全方面的问题。

    (2)准备

      正式为类变量(static 变量)分配内存,并设置类变量的初始值

    (3)解析

      虚拟机常量池内的符号引用替换为直接引用的过程

    初始化:执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static块)中的语句合并而成的。

     

    (重点)类的主动引用和被动引用的区别

    类的主动引用(一定会发生类的初始化)

    —— new一个类的对象

    —— 调用类的静态成员(除了final常量)和静态方法

    —— 使用java.lang.reflect包的方法对类进行反射调用

    —— 当虚拟机启动,先启动main方法所在的类

    —— 当初始化一个类,如果父类没有被初始化,则先初始化它的父类

     

    类的被动引用(不会发生类的初始化)

    —— 当访问一个静态域时,只有真正声明这个域的类才会被初始化

        通过子类引用父类的静态变量,不会导致子类初始化

    —— 通过数组定义类引用,不会触发此类的初始化

    —— 引用final常量不会触发此类的初始化(常量在编译阶段就存入类的常量池中)

     

    二、JVM的内存结构

    (1)方法区/永久代(元数据区)

        永久代(Permanent Generation), 用于存储被 JVM 加载的类信息、常量、静 态变量、即时编译器编译后的代码等数据.。

        1、静态变量

        2、静态方法

        3、静态代码块

        4、运行时常量池

    (2)堆存放对象本身

       创建的对象和数组都保存在 Java 堆内存中,也是垃圾收集器进行 垃圾收集的最重要的内存区域

    (3)虚拟机栈(线程私有)

       每个方法在执行的同时都会创建一个栈帧(Stack Frame) 用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成 的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。 

    (4) 程序计数器(线程私有)

        一块较小的内存空间, 是当前线程所执行的字节码的行号指示器,每条线程都要有一个独立的 程序计数器,这类内存也称为“线程私有”的内存。 这个内存区域是唯一一个在虚拟机中没有规定任何OutOfMemoryError情况的区域。 

    (5)本地方法栈

      本地方法区和函数栈作用类似, 区别是虚拟机栈为执行Java方法服务, 而本地方法栈则为 Native方法服务, 如果一个VM实现使用C-linkage模型来支持Native调用, 那么该栈将会是一个 C栈,但HotSpot VM直接就把本地方法栈和虚拟机栈合二为一。 

    注意:

        1、线程私有数据区域生命周期与线程相同, 依赖用户线程的启动/结束

        2、Java程序的每个线程都与操作系统的本地线程直接映射

       3、线程共享区域随虚拟机的启动/关闭而创建/销毁。 

       4、直接内存并不是JVM运行时数据区的一部分,。 在JDK 1.4引入的NIO提 供了基于 Channel 与 Buffer 的 IO 方式, 它可以使用 Native 函数库直接分配堆外内存, 然后使用 DirectByteBuffer 对象作为这块内存的引用进行操作, 这样就避免了在 Java 堆和Native堆中来回复制数据, 因此在一些场景中可以显著提高性能。 

     

    三、 案例分析

    packagecom.oracle.List;

    /**

     * 类的加载和初始化只执行一次!

     * @author zhegao

     */

    public class M {

      public static void main(String[] args) throws ClassNotFoundException,InstantiationException, IllegalAccessException {

            /**类的主动引用(一定会发生类的初始化),

             *初始化的顺序:初始化静态信息(先父再子)

             */

            //第一种: new一个类的对象

             //A a1 = new A(); 

            /*

             * 结果:

             * Father is very happy

                A is so happy!

                I am A's father...

                A is good

             */   

            //第二种:调用类的静态成员(除了final常量)和静态方法

            // System.out.println(A.age);

       /*

             * 结果:

             * Father is very happy

                A is so happy!

                27

             */   

            //第三种:使用java.lang.reflect包的方法对类进行反射调用

            //Class.forName("com.oracle.List.A");

           

            /**

             *类的被动引用(不会发生类的初始化)

             */

            //第一种:当访问一个静态域时,只有真正声明这个域的类才会被初始化,通过子类引用父类的静态变量,不会导致子类初始化

            //System.out.println(B.age); //这里只有A初始化了,而由于静态域ageA的属性,所以B未被初始化!

           

            //第二种:通过数组

            A[] as = new A[10];

           

            //第三种:引用常量不会触发此类的初始化

            System.out.println(A.height);

             /*

             *结果

             *185

             */      

        }

    }

    class B extends A{

        public B() {

            System.out.println("B is also good.");

        }

    }

    class A extends Father{

        public static int age = 27;

        public static final intheight = 185;

        static {

            System.out.println("A is so happy!");

        }

        public A() {

            System.out.println("A is good");

        }

    }

    class Father{

        static {

            System.out.println("Father is very happy");

        }

        public Father() {

            System.out.println("I am A's father...");

        }

    }

     

    2、对象的创建过程(这部分参考于博客https://www.cnblogs.com/chenyangyao/p/5296807.html,书籍可以参考《深入理解java虚拟机 第二版》P44-49)

      当一个对象被创建时,虚拟机就会为其分配内存来存放对象自己的实例变量及其从父类继承过来的实例变量(即使这些从超类继承过来的实例变量有可能被隐藏也会被分配空间)。其中要注意的是,实例字段包括自身定义的和从父类继承下来的(即使父类的实例字段被子类覆盖或者被private修饰,都照样为其分配内存)。在为这些实例变量分配内存的同时,这些实例变量也会被赋予默认值(零值)。在内存分配完成之后,Java虚拟机就会开始对新创建的对象按照程序猿的意志进行初始化。在Java对象初始化过程中,主要涉及三种执行对象初始化的结构,分别是 实例变量初始化实例代码块初始化 以及 构造函数初始化。当然,构造函数的调用顺序会一直上溯到Object类。

        至此,一个对象就被创建完毕,此时,一般会有一个引用指向这个对象。在JAVA中,存在两种数据类型,一种就是诸如int、double等基本类型,另一种就是引用类型,比如类、接口、内部类、枚举类、数组类型的引用等。引用的实现方式一般有两种,具体请看图3。

                                

                                                                图1  对象的创建过程

          在这里,楼主做了一个问题的思考,对于这段java代码: Instance  instance = new Instance(),在单例模式下的线程安全是如何产生的?

         答:基于图一,我们可以知道new Instance()主要进行了三个步骤(假设类已经被加载、链接和初始化):

    (1)为对象分配内存空间,并初始化为零值  ---->  (2) 调用对象的实例化init方法  ----> (3)将内存地址指向instance变量。

     (1)(2)(3)这是我们接受的执行顺序,但是由于CPU在执行时做了局部的优化,称之为“重排序”。最终,导致执行的顺序可能是(1)(3)(2)。该顺序在“多例”模式下没有影响,毕竟最终结果都一样,但是在并发环境的“单例”模式下会引发“线程”安全问题,即多个线程新建的对象不一致,或者更严重的是,新建的对象没有被实例化,所以需要“DCL”的双重检查锁并配合volatile关键字使用。具体参考http://blog.csdn.net/qq_29864971/article/details/79321095。

    结论:new Instance()并非是一个严格意义的“原子性”操作!,它包含了一些不同的步骤,所以在多线程的“单例”模式下,需要考虑其线程安全问题。这样的问题在一些框架中也会出现,例如:Spring整合Struts1中,原本由Struts控制的action,会兼并到spring容器中(因为struts中,action默认为单例,而兼并到Spring容器后,action的 scope="prototype")

           

                                                                     图2 对象的组成结构

     

              

                                                          图3  对象引用的两种实现方式

         通过图3可见,如果通过“直接指针”访问对象,那么对象的布局中,对象头将持有指向“对象类型数据”/类元数据的指针。

    但是这两种方式各有优势:

    1)方式一(句柄池)的优缺点

        优点:栈中的reference存储的是“句柄”地址,它比较稳定不易改变,即使对象被移动或者被垃圾回收器回收,只会改变句柄池中的实例对象的指针,reference本身不会被修改

        缺点:创建句柄池,增加内存的开销。

     2)方式二(直接指针)的优缺点

       优点:节省内存,并且直接指针的速度更快

        缺点:指针移动,reference也会做修改

     

     

     

    展开全文
  • Java 依赖注入标准(JSR-330,Dependency Injection for Java)1.0 规范已于2009年10 月份发布。该规范主要是面向依赖注入使用者,而对注入器实现、配置并未作详细要求。Spring、Guice 已经开始兼容该规范,JSR-299...

    IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序,而Dagger2框架是依赖注入思想践行者的优秀代表。

    依赖注入框架Dagger2详解(一),依赖注入和控制反转的深入理解
    依赖注入框架Dagger2详解(二),Java依赖注入标准JSR-330规范
    依赖注入框架Dagger2详解(三),Java注解处理器APT入门
    依赖注入框架Dagger2详解(四),初级篇
    依赖注入框架Dagger2详解(五),中级篇
    依赖注入框架Dagger2详解(六),高级篇

    这阵子,一直打算写几篇关于关于依赖注入框架Dagger2的文章,我也在网上看了很多这方面的文章,大部分文章都是直接讲Dagger2怎么使用的,而很少有讲它背后的思想,要么有讲,也是寥寥几笔,一带而过,我们不但要学习这个框架怎么用,更重要的是要知道他背后所体现的编程思想,否则你永远都是一个框架的调用者,而不是一名程序设计师,我记得上高中的时候,我们的物理老师说过一句话:“物理学的尽头是数学,数学的尽头是哲学,哲学的尽头是哲学”,越是抽象的东西越高深,思想是行动的指南,因此我们今天先从理论开始,用单独一篇文章的篇幅来讲Dagger2框架背后所体现的编程思想:依赖注入和反转控制。

    学习过Spring框架的人一定都会听过Spring的IoC(Inversion of Control ,控制反转) 、DI(Dependency Injection,依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC 、DI这两个概念是模糊不清的,是很难理解的,今天和大家分享网上的一些技术大牛们对Spring框架的IoC的理解。

    1.分享Iteye的一位大神对Ioc的精彩讲解

    首先要分享的是Iteye的这位技术牛人对Spring框架的Ioc的理解,上个周我在网上查阅相关资料的时候,偶然间发现了这篇文章,我觉得这位大神写的非常好,以至于我爱不释手的看了好几篇,最后我决定把它分享出来因此,写得非常通俗易懂,原文地址:http://jinnianshilongnian.iteye.com/blog/1413846

    1.1 IoC是什么

    Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:

    • 谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。

    • 为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

    用图例说明一下,传统程序设计如图2-1,都是主动去创建相关对象然后再组合起来:
    在这里插入图片描述

    当有了IoC/DI的容器后,在客户端类中不再主动去创建这些对象了,如图2-2所示:
    在这里插入图片描述

    1.2 IoC能做什么

    IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。

    其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。

    IoC很好的体现了面向对象设计法则之一——好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

    1.3 IoC和DI

    DI—Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

    理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:

    • 谁依赖于谁:当然是应用程序依赖于IoC容器;
    • 为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
    • 谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
    • 注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。

    IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。

    看过很多对Spring的Ioc理解的文章,好多人对Ioc和DI的解释都晦涩难懂,反正就是一种说不清,道不明的感觉,读完之后依然是一头雾水,感觉就是开涛这位技术牛人写得特别通俗易懂,他清楚地解释了IoC(控制反转) 和DI(依赖注入)中的每一个字,读完之后给人一种豁然开朗的感觉。我相信对于初学Spring框架的人对Ioc的理解应该是有很大帮助的。

    2.分享Bromon的blog上对IoC与DI浅显易懂的讲解

    2.1 IoC(控制反转)

    首先想说说IoC(Inversion of Control,控制反转)。这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………,想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。

    那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。

    2.2 DI(依赖注入)

    IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。

    理解了IoC和DI的概念后,一切都将变得简单明了,剩下的工作只是在spring的框架中堆积木而已。

    3.分享bestone0213对IoC(控制反转)和DI(依赖注入)的理解

    在平时的java应用开发中,我们要实现某一个功能或者说是完成某个业务逻辑时至少需要两个或以上的对象来协作完成,在没有使用Spring的时候,每个对象在需要使用他的合作对象时,自己均要使用像new object() 这样的语法来将合作对象创建出来,这个合作对象是由自己主动创建出来的,创建合作对象的主动权在自己手上,自己需要哪个合作对象,就主动去创建,创建合作对象的主动权和创建时机是由自己把控的,而这样就会使得对象间的耦合度高了,A对象需要使用合作对象B来共同完成一件事,A要使用B,那么A就对B产生了依赖,也就是A和B之间存在一种耦合关系,并且是紧密耦合在一起,而使用了Spring之后就不一样了,创建合作对象B的工作是由Spring来做的,Spring创建好B对象,然后存储到一个容器里面,当A对象需要使用B对象时,Spring就从存放对象的那个容器里面取出A要使用的那个B对象,然后交给A对象使用,至于Spring是如何创建那个对象,以及什么时候创建好对象的,A对象不需要关心这些细节问题(你是什么时候生的,怎么生出来的我可不关心,能帮我干活就行),A得到Spring给我们的对象之后,两个人一起协作完成要完成的工作即可。

    所以控制反转IoC(Inversion of Control)是说创建对象的控制权进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,比如转移交给了IoC容器,它就是一个专门用来创建对象的工厂,你要什么对象,它就给你什么对象,有了 IoC容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖IoC容器了,通过IoC容器来建立它们之间的关系。

    DI(依赖注入)其实就是IOC的另外一种说法,DI是由Martin Fowler 在2004年初的一篇论文中首次提出的。他总结:控制的什么被反转了?就是:获得依赖对象的方式反转了。

    小结

    对于Spring Ioc这个核心概念,我相信每一个学习Spring的人都会有自己的理解。这种概念上的理解没有绝对的标准答案,仁者见仁智者见智。

    注:如果想要更加深入的了解IoC和DI,请参考大师级人物Martin Fowler的一篇经典文章《Inversion of Control Containers and the Dependency Injection pattern》,原文地址:http://www.martinfowler.com/articles/injection.html

    展开全文
  • IOC 的理解与解释

    2020-03-29 21:52:00
    在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转...
  • C#基础知识之依赖注入一、依赖注入的引入二、探究依赖注入 一、依赖注入的引入 1、讨论会 话说有一个叫IGame的游戏公司,正在开发一款ARPG游戏(动作&角色扮演游戏,如魔兽世界、梦幻西游这一的游戏)。一般...
  • go 版本依赖注入
  • @Autowired注入Spring Bean,则当前必须也是Spring Bean才能调用它,不能用new xxx()来获得对象,这种方式获得的对象无法调用@Autowired注入的Bean。
  • C#依赖注入

    千次阅读 2018-10-19 14:23:44
    依赖注入(Dependency Injection),是这样一个过程:由于某客户依赖于服务的一个接口,而不依赖于具体服务,所以客户只定义一个注入点。在程序运行过程中,客户不直接实例化具体服务实例,而是客户...
  • Spring的Ioc和DI依赖注入的理解

    千次阅读 2014-12-19 14:51:12
    (1) 使用Ioc,对象被动接受依赖类,而不是自己主动去找。容器在实例化的时候主动将它的依赖类注入给它。    亦可以这样理解:控制反转将类的主动权转移到借口上,依赖注入通过xml配置文件在类实例化是将其依赖...
  •  使用IoC,对象被动接受依赖类,而不是自己主动的去找。容器在实例化的时候主动将它的依赖类注入给它。可以这样理解:控制反转将类的主动权转移到接口上,依赖注入通过xml配置文件在类实例化时将其依赖类注入。...
  • 被动定位

    千次阅读 2018-06-07 18:30:32
    被动定位方式有严重局限性,定位依赖其它应用致无法控定位实时性。一段时间内无任何其它应用主动请求位置则被动定位不会收到位置更新,只能继续等待。急于获位则该方式不适用。 有时特定需求下宁可牺牲电...
  • 控制反转和依赖注入的理解(通俗易懂)

    万次阅读 多人点赞 2018-05-13 11:37:38
    学习过Spring框架的人一定都会听过Spring的IoC(控制反转) 、DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC 、DI这两个概念是模糊不清的,是很难理解的,今天和大家分享网上的一些技术大牛们对Spring...
  • 学习过Spring框架的人一定都会听过Spring的IoC(控制反转) 、DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC 、DI这两个概念是模糊不清的,是很难理解的,今天和大家分享网上的一些技术大牛们对Spring...
  • 依赖关系的体现,是在虚拟机加载的时候进行动态链接,而不是编译的时候。因此,所有的在编译时不会处理文件在内存的布局信息,只有到了加载的时候才会涉及“如何把符号引用翻译到具体的内存地址中”。 ...
  • 面向对象测试

    千次阅读 2020-04-01 17:03:52
    一、面向对象影响测试  传统的测试软件是从“小型测试”开始,逐步过渡到“大型测试”,即从单元测试开始,逐步进入集成测试,最后进行确认测试和系统测试。对于传统的软件系统来说,单元测试集中测试最小的可编译...
  • 控制反转与依赖注入

    千次阅读 2019-03-13 08:26:02
    我们都知道,在采用面向对象方法设计的软件系统中,它的底层实现都是由N个对象组成的,所有的对象通过彼此的合作,最终实现系统的业务逻辑。 图1:软件系统中耦合的对象 如果我们打开机械式手表的后盖,就会看到与...
  • 依赖注入、控制反转还能这么浅显易懂? 之前学习Spring框架时,接触到过这两个概念,但一直感觉模模糊糊的。今天在学习AngularJS的依赖注入时,决定停下来将其搞清楚,就在网上查看了网上大神们对这两个的高见,...
  • DI(DependencyInjection)依赖注入:就是指对象被动接受依赖类而不是自己主动去找,换句话说就。是指对象不是从容器中查找它依赖的类,而是在容器实例化对象的时候主动将它依赖的类注入给它。 1.set方法注入 public...
  • Spring试题和答案.pdf

    2020-10-30 10:31:25
    对象被动接受依赖类 )对象主动的去找依赖类 )一定要用接口 )下面关于AOP的理解,正确的是(B C)(选择两项) )面向纵向的开发 )面向横向的开发 )AOP关注是面 )AOP关注的是点
  • Java应用中(无论像Applet的小应用还是多层结构的企业及应用),都存在大量的A对象需要调用B对象方法的情形,这种情形被Spring称为依赖,即A对象依赖B对象。Java应用大多由一些相互调用的对象构成的,Spring把这种...
  • 详解spring的IOC控制反转和DI依赖注入

    千次阅读 2018-06-05 15:45:34
    学习过Spring框架的人一定都会听过Spring的IoC(控制反转) 、DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC 、DI这两个概念是模糊不清的,是很难理解的,今天和大家分享网上的一些技术大牛们对Spring...
  • DI(DependencyInjection)依赖注入:就是指对象被动接受依赖类而不是自己主动去找,换句话说就。是指对象不是从容器中查找它依赖的类,而是在容器实例化对象的时候主动将它依赖的类注入给它。 1.set方法注入 public...
  • 依赖注入(DI)和控制反转(IoC)的理解

    万次阅读 多人点赞 2018-08-09 11:13:56
     学习过Spring框架的人一定都会听过Spring的IoC(控制反转) 、DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC 、DI这两个概念是模糊不清的,是很难理解的,今天和大家分享网上的一些技术大牛们对Spring...
  • Spring DI原理.docx

    2022-06-19 20:59:20
    DI(Dependency Injection)依赖注入:就是指对象被动接受依赖类而不是自己主动去找,换句话说就是指对象不是从容器中查找它依赖的类,而是在容器实例化对象的时候主动将它依赖的类注入给它。
  • Spring之依赖注入

    万次阅读 2018-01-10 11:49:42
    Spring框架的核心功能之一就是通过依赖注入的方式来管理Bean之间的依赖关系。那么我们今天的主角依赖注入到底有什么神奇之处呢?请往下继续看。 了解过设计模式的朋友肯定知道工厂模式吧,即所有的对象的创建都交给...
  • 依赖注入原理---IoC框架

    千次阅读 2018-04-21 15:15:11
    依赖注入原理---IoC框架先来讲一讲,一个简单的依赖注入例子。...例如下面 Human 中用到一个 Father 对象,我们就说 Human 对 Father 有一个依赖。public class Human { ... Father father; ...
  • Java依赖注入(DI)相关面试总结-Java基础 1)依赖注入 面向对象编程,经常需要处理解耦问题。程序耦合性越低则程序可读、可维护性越高。控制反转(Inversion of Control或IoC)是面向对象编程的常用设计原则,这个...
  • 什么是面向对象

    千次阅读 2017-07-31 19:27:18
    面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术[1] 发展到一定阶段后的产物。起初,“面向对象”是专指在程序设计中采用封装、继承、多态等设计方法。概念对象对象是人们要进行研究的任何事物,从最...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 19,835
精华内容 7,934
热门标签
关键字:

对象被动的接受依赖类