Vue3 源码解读静态提升详解

目录
  • 什么是静态提升
  • transform 转换器
  • hoistStatic 静态提升
  • walk 函数
  • walk 函数流程图
  • 总结

什么是静态提升

静态提升是Vue3编译优化中的其中一个优化点。所谓的静态提升,就是指在编译器编译的过程中,将一些静态的节点或属性提升到渲染函数之外。下面,我们通过一个例子来深入理解什么是静态提升。

假设我们有如下模板:

<div>
  <p>static text</p>
  <p>{{ title }}</p>
</div>

在没有静态提升的情况下,它对应的渲染函数是:

function render() {
  return (openClock(), createClock('div', null,  [
    createVNode('p', null, 'static text'),
    createVNode('p', null, ctx.title, 1 /* TEXT */)
  ]))
}

从上面的代码中可以看到,在这段虚拟DOM的描述中存在两个 p 标签,一个是纯静态的,而另一个拥有动态文本。当响应式数据 title 的值发生变化时,整个渲染函数会重新执行,并产生新的虚拟DOM树。在这个过程中存在性能开销的问题。原因是纯静态的虚拟节点在更新时也会被重新创建一次,其实这是没有必要的。因此我们需要使用静态提升来解决这个问题。

所谓的 “静态提升”,就是将一些静态的节点或属性提升到渲染函数之外。如下面的代码所示:

// 把静态节点提升到渲染函数之外
const hoist1 = createVNode('p', null, 'text')
function render() {
  return (openBlock(), createBlock('div', null, [
    hoist1, // 静态节点引用
    createVNode('p', null, ctx.title, 1 /* TEXT */)
  ]))
}

可以看到,经过静态提升后,在渲染函数内只会持有对静态节点的引用。当响应式数据发生变化,并使得渲染函数重新执行时,并不会重新创建静态的虚拟节点,从而避免了额外的性能开销。

transform 转换器

在模板编译器将模板编译为渲染函数的过程中,transform 函数扮演着十分重要的角色。它用来将模板AST转换为 JavaScript AST。下面,我们来看看 transform 函数做了什么。

// packages/compiler-core/src/transform.ts
// 将 模板AST 转换为 JavaScript AST
export function transform(root: RootNode, options: TransformOptions) {
  // 创建转换上下文
  const context = createTransformContext(root, options)
  // 遍历所有节点,执行转换
  traverseNode(root, context)
  // 如果编译选项中打开了 hoistStatic 选项,则进行静态提升
  if (options.hoistStatic) {
    hoistStatic(root, context)
  }
  // 创建 Block,收集所有动态子节点
  if (!options.ssr) {
    createRootCodegen(root, context)
  }
  // finalize meta information
  // 确定最终的元信息
  root.helpers = [...context.helpers.keys()]
  root.components = [...context.components]
  root.directives = [...context.directives]
  root.imports = context.imports
  root.hoists = context.hoists
  root.temps = context.temps
  root.cached = context.cached
  if (__COMPAT__) {
    root.filters = [...context.filters!]
  }
}

从上面的源码中可以看到,transform 函数的实现并不复杂。

  • 首先,调用 createTransformContext 函数创建了一个转换上下文对象,该对象存储着转换过程中的一些上下文数据。例如当前正在转换的节点 currentNode、当前转换节点的父节点parent、用于替换当前正在转换的节点的 replaceNode 函数、用于移除当前访问的节点的 removeNode 函数等。
  • 接着调用 traverseNode 函数,递归遍历模板AST,将模板AST 转换为 JavaScript AST。
  • 然后判断编译选项options中是否开启了 hoistStatic,如果是,则进行静态提升。
  • 接下来则创建 Block,收集模板中的动态子节点。
  • 最后做的事情则是确定最终的元信息。

由于本文主要是介绍静态提升,因此我们围绕静态提升的代码继续往下探索,其余部分代码将在其它文章中介绍。

hoistStatic 静态提升

hoistStatic 函数的源码如下所示:

// packages/compiler-core/src/transforms/hoistStatic.ts
export function hoistStatic(root: RootNode, context: TransformContext) {
  walk(
    root,
    context,
    // Root node is unfortunately non-hoistable due to potential parent
    // fallthrough attributes.
    // 根节点作为 Block 角色是不能被提升的
    isSingleElementRoot(root, root.children[0])
  )
}

可以看到,hoistStatic 函数接收两个参数,第一个参数是根节点,第二个参数是转换上下文。在该函数中,仅仅只是调用了 walk 函数来实现静态提升。

并且在 walk 函数中调用 isSingleElementRoot 函数来告知 walk 函数根节点是不能提升的,原因是根节点作为 Block 角色是不可被提升的。

我们接下来继续探究 walk 函数的源码。

walk 函数

静态提升的真正实现逻辑在 walk 函数内,其源码如下所示:

function walk(
  node: ParentNode,
  context: TransformContext, // 转换上下文对象
  doNotHoistNode: boolean = false  // 节点是否可以被提升
) {
  const { children } = node
  // 子节点的数量
  const originalCount = children.length
  // 可提升节点的数量
  let hoistedCount = 0
  for (let i = 0; i < children.length; i++) {
    const child = children[i]
    // only plain elements & text calls are eligible for hoisting.
    // 只有普通元素和文本才能被提升
    if (
      child.type === NodeTypes.ELEMENT &&
      child.tagType === ElementTypes.ELEMENT
    ) {
      // 如果节点不能被提升,则将 constantType 赋值为 NOT_CONSTANT 不可被提升的标记
      // 否则调用 getConstantType 获取子节点的静态类型:ConstantTypes 定义了子节点的静态类型
      const constantType = doNotHoistNode
        ? ConstantTypes.NOT_CONSTANT
        : getConstantType(child, context)
      // 如果获取到的 constantType 枚举值大于 NOT_CONSTANT
      if (constantType > ConstantTypes.NOT_CONSTANT) {
        // 如果节点可以被提升
        if (constantType >= ConstantTypes.CAN_HOIST) {
          // 则将子节点的 codegenNode 属性的 patchFlag 标记为 HOISTED ,即可提升
          ;(child.codegenNode as VNodeCall).patchFlag =
            PatchFlags.HOISTED + (__DEV__ ? ` /* HOISTED */` : ``)
            // 提升节点,将节点存储到 转换上下文context 的 hoist 数组中
          child.codegenNode = context.hoist(child.codegenNode!)
          // 提升节点数量自增1
          hoistedCount++
          continue
        }
      } else {
        // node may contain dynamic children, but its props may be eligible for
        // hoisting.
        // 包含动态绑定的节点本身不会被提升,该动态节点上可能存在纯静态的属性,静态的属性可以被提升
        const codegenNode = child.codegenNode!
        if (codegenNode.type === NodeTypes.VNODE_CALL) {
          // 获取 patchFlag 补丁标志
          const flag = getPatchFlag(codegenNode)
          // 如果不存在 patchFlag 补丁标志 或者 patchFlag 是文本类型
          // 并且该节点的 props 是可以被提升的
          if (
            (!flag ||
              flag === PatchFlags.NEED_PATCH ||
              flag === PatchFlags.TEXT) &&
            getGeneratedPropsConstantType(child, context) >=
              ConstantTypes.CAN_HOIST
          ) {
            // 获取节点的 props,并在转换上下文对象中执行提升操作,
            // 将被提升的 props 添加到转换上下文context 的 hoist 数组中
            const props = getNodeProps(child)
            if (props) {
              codegenNode.props = context.hoist(props)
            }
          }
          // 将节点的动态 props 添加到转换上下文对象中
          if (codegenNode.dynamicProps) {
            codegenNode.dynamicProps = context.hoist(codegenNode.dynamicProps)
          }
        }
      }
    } else if (
      // 如果是节点类型是 TEXT_CALL,并且节点可以被提升
      child.type === NodeTypes.TEXT_CALL &&
      getConstantType(child.content, context) >= ConstantTypes.CAN_HOIST
    ) {
      // 提升节点
      child.codegenNode = context.hoist(child.codegenNode)
      hoistedCount++
    }
    // walk further
    if (child.type === NodeTypes.ELEMENT) {
      // 如果子节点的 tagType 是组件,则继续遍历子节点
      // 以判断插槽中的情况
      const isComponent = child.tagType === ElementTypes.COMPONENT
      if (isComponent) {
        context.scopes.vSlot++
      }
      // 执行 walk函数,继续判断插槽中的节点及节点属性是否可以被提升
      walk(child, context)
      if (isComponent) {
        context.scopes.vSlot--
      }
    } else if (child.type === NodeTypes.FOR) {
      // Do not hoist v-for single child because it has to be a block
      // 带有 v-for 指令的节点是一个 Block
      // 如果 v-for 的节点中只有一个子节点,则不能被提升
      walk(child, context, child.children.length === 1)
    } else if (child.type === NodeTypes.IF) {
      // 带有 v-if 指令的节点是一个 Block
      for (let i = 0; i < child.branches.length; i++) {
        // Do not hoist v-if single child because it has to be a block
        // 如果只有一个分支条件,则不进行提升
        walk(
          child.branches[i],
          context,
          child.branches[i].children.length === 1
        )
      }
    }
  }
  // 将被提升的节点序列化,即转换成字符串
  if (hoistedCount && context.transformHoist) {
    context.transformHoist(children, context, node)
  }
  // all children were hoisted - the entire children array is hoistable.
  if (
    hoistedCount &&
    hoistedCount === originalCount &&
    node.type === NodeTypes.ELEMENT &&
    node.tagType === ElementTypes.ELEMENT &&
    node.codegenNode &&
    node.codegenNode.type === NodeTypes.VNODE_CALL &&
    isArray(node.codegenNode.children)
  ) {
    node.codegenNode.children = context.hoist(
      createArrayExpression(node.codegenNode.children)
    )
  }
}

可以看到,walk 函数的代码比较常,下面,我们将分步对其进行解析。

1、首先,我们来看 walk 函数的函数签名,代码如下:

function walk(
  node: ParentNode,
  context: TransformContext, // 转换上下文对象
  doNotHoistNode: boolean = false  // 节点是否可以被提升
)

从函数签名中可以知道,walk 函数接收三个参数,第一个参数是一个 node 节点,第二个参数是转换器的转换上下文对象 context,第三个参数 doNotHoistNode 是一个布尔值,用来判断传入节点的子节点是否可以被提升。

2、初始化两个变量,originalCount:子节点的数量;hoistedCount:可提升节点的数量,该变量将会用于判断被提升的节点是否可序列化。

  const { children } = node
  // 子节点的数量
  const originalCount = children.length
  // 可提升节点的数量
  let hoistedCount = 0

3、我们来看 for 循环语句里的第一个 if 语句分支,这里处理的是普通元素和静态文本被提升的情况。

    // only plain elements & text calls are eligible for hoisting.
    // 只有普通元素和文本才能被提升
    if (
      child.type === NodeTypes.ELEMENT &&
      child.tagType === ElementTypes.ELEMENT
    ) {
      // 如果节点不能被提升,则将 constantType 赋值为 NOT_CONSTANT 不可被提升的标记
      // 否则调用 getConstantType 获取子节点的静态类型:ConstantTypes 定义了子节点的静态类型
      const constantType = doNotHoistNode
        ? ConstantTypes.NOT_CONSTANT
        : getConstantType(child, context)
      // 如果获取到的 constantType 枚举值大于 NOT_CONSTANT
      if (constantType > ConstantTypes.NOT_CONSTANT) {
        // 如果节点可以被提升
        if (constantType >= ConstantTypes.CAN_HOIST) {
          // 则将子节点的 codegenNode 属性的 patchFlag 标记为 HOISTED ,即可提升
          ;(child.codegenNode as VNodeCall).patchFlag =
            PatchFlags.HOISTED + (__DEV__ ? ` /* HOISTED */` : ``)
            // 提升节点,将节点存储到 转换上下文context 的 hoist 数组中
          child.codegenNode = context.hoist(child.codegenNode!)
          // 提升节点数量自增1
          hoistedCount++
          continue
        }
      } else {
        // node may contain dynamic children, but its props may be eligible for
        // hoisting.
        // 包含动态绑定的节点本身不会被提升,该动态节点上可能存在纯静态的属性,静态的属性可以被提升
        const codegenNode = child.codegenNode!
        if (codegenNode.type === NodeTypes.VNODE_CALL) {
          // 获取 patchFlag 补丁标志
          const flag = getPatchFlag(codegenNode)
          // 如果不存在 patchFlag 补丁标志 或者 patchFlag 是文本类型
          // 并且该节点的 props 是可以被提升的
          if (
            (!flag ||
              flag === PatchFlags.NEED_PATCH ||
              flag === PatchFlags.TEXT) &&
            getGeneratedPropsConstantType(child, context) >=
              ConstantTypes.CAN_HOIST
          ) {
            // 获取节点的 props,并在转换上下文对象中执行提升操作,
            // 将被提升的 props 添加到转换上下文context 的 hoist 数组中
            const props = getNodeProps(child)
            if (props) {
              codegenNode.props = context.hoist(props)
            }
          }
          // 将节点的动态 props 添加到转换上下文对象中
          if (codegenNode.dynamicProps) {
            codegenNode.dynamicProps = context.hoist(codegenNode.dynamicProps)
          }
        }
      }

首先通过外部传入的 doNotHoistNode 参数来获取子节点的静态类型。如果 doNotHoistNode 为 true,则将 constantType 的值设置为 ConstantType 枚举值中的 NOT_CONSTANT,即不可被提升。否则通过 getConstantType 函数获取子节点的静态类型。如下面的代码所示:

// 如果节点不能被提升,则将 constantType 赋值为 NOT_CONSTANT 不可被提升的标记
// 否则调用 getConstantType 获取子节点的静态类型:ConstantTypes 定义了子节点的静态类型
const constantType = doNotHoistNode
  ? ConstantTypes.NOT_CONSTANT
  : getConstantType(child, context)

接下来通过判断 constantType 的枚举值来处理是需要提升静态节点还是提升动态节点的静态属性。

  • 如果获取到的 constantType 枚举值大于 NOT_CONSTANT,说明该节点可能被提升或序列化为字符串。
  • 如果该节点可以被提升,则将节点 codegenNode 属性的 patchFlag 标记为 PatchFlags.HOISTED ,即可提升。

然后执行转换器上下文中的 hoist 方法,将该节点存储到转换上下文context 的 hoist 数组中,该数组中存储的是可被提升的节点。如下面的代码所示:

// 如果获取到的 constantType 枚举值大于 NOT_CONSTANT
if (constantType > ConstantTypes.NOT_CONSTANT) {
  // 如果节点可以被提升
  if (constantType >= ConstantTypes.CAN_HOIST) {
    // 则将子节点的 codegenNode 属性的 patchFlag 标记为 HOISTED ,即可提升
    ;(child.codegenNode as VNodeCall).patchFlag =
      PatchFlags.HOISTED + (__DEV__ ? ` /* HOISTED */` : ``)
      // 提升节点,将节点存储到 转换上下文context 的 hoist 数组中
    child.codegenNode = context.hoist(child.codegenNode!)
    // 提升节点数量自增1
    hoistedCount++
    continue
  }
}

如果获取到的 constantType 枚举值不大于 NOT_CONSTANT,说明该节点包含动态绑定,包含动态绑定的节点上如果存在纯静态的 props,那么这些静态的 props 是可以被提升的

从下面的代码中我们可以看到,在提升静态的 props 时,同样是执行转换器上下文中的 hoist 方法,将静态的props存储到转换上下文context 的 hoist 数组中。

如下面的代码所示:

else {
  // node may contain dynamic children, but its props may be eligible for
  // hoisting.
  // 包含动态绑定的节点本身不会被提升,该动态节点上可能存在纯静态的属性,静态的属性可以被提升
  const codegenNode = child.codegenNode!
  if (codegenNode.type === NodeTypes.VNODE_CALL) {
    // 获取 patchFlag 补丁标志
    const flag = getPatchFlag(codegenNode)
    // 如果不存在 patchFlag 补丁标志 或者 patchFlag 是文本类型
    // 并且该节点的 props 是可以被提升的
    if (
      (!flag ||
        flag === PatchFlags.NEED_PATCH ||
        flag === PatchFlags.TEXT) &&
      getGeneratedPropsConstantType(child, context) >=
        ConstantTypes.CAN_HOIST
    ) {
      // 获取节点的 props,并在转换上下文对象中执行提升操作,
      // 将被提升的 props 添加到转换上下文context 的 hoist 数组中
      const props = getNodeProps(child)
      if (props) {
        codegenNode.props = context.hoist(props)
      }
    }
    // 将节点的动态 props 添加到转换上下文对象中
    if (codegenNode.dynamicProps) {
      codegenNode.dynamicProps = context.hoist(codegenNode.dynamicProps)
    }
  }
}

4、如果节点类型时是 TEXT_CALL 类型并且该节点可以被提升,则同样执行转换器上下文中的 hoist 方法,将节点存储到转换上下文context 的 hoist 数组中,如下面的代码所示:

else if (
  // 如果是节点类型是 TEXT_CALL,并且节点可以被提升
  child.type === NodeTypes.TEXT_CALL &&
  getConstantType(child.content, context) >= ConstantTypes.CAN_HOIST
) {
  // 提升节点
  child.codegenNode = context.hoist(child.codegenNode)
  hoistedCount++
}

5、如果子节点是组件,则递归调用 walk 函数,继续遍历子节点,以判断插槽中的节点及节点属性是否可以被提升。如下面的代码所示:

if (child.type === NodeTypes.ELEMENT) {
  // 如果子节点的 tagType 是组件,则继续遍历子节点
  // 以判断插槽中的情况
  const isComponent = child.tagType === ElementTypes.COMPONENT
  if (isComponent) {
    context.scopes.vSlot++
  }
  // 执行 walk函数,继续判断插槽中的节点及节点属性是否可以被提升
  walk(child, context)
  if (isComponent) {
    context.scopes.vSlot--
  }
}

6、如果节点上带有 v-for 指令或 v-if 指令,则递归调用 walk 函数,继续判断子节点是否可以被提升。如果 v-for 指令的节点只有一个子节点,v-if 指令的节点只有一个分支条件,则不进行提升。如下面的代码所示:

else if (child.type === NodeTypes.FOR) {
  // Do not hoist v-for single child because it has to be a block
  // 带有 v-for 指令的节点是一个 Block
  // 如果 v-for 的节点中只有一个子节点,则不能被提升
  walk(child, context, child.children.length === 1)
} else if (child.type === NodeTypes.IF) {
  // 带有 v-if 指令的节点是一个 Block
  for (let i = 0; i < child.branches.length; i++) {
    // Do not hoist v-if single child because it has to be a block
    // 如果只有一个分支条件,则不进行提升
    walk(
      child.branches[i],
      context,
      child.branches[i].children.length === 1
    )
  }
}

walk 函数流程图

总结

静态提升,就是指在编译器编译的过程中,将一些静态的节点或属性提升到渲染函数之外。它能够减少更新时创建虚拟 DOM 带来的性能开销和内存占用。

对于纯静态的节点和动态节点上的纯静态属性,则直接执行转换器上下文中的 hoist 方法,将节点或属性进行提升。如果节点是组件、节点上带有 v-for 指令或v-if 指令,则递归调用 walk 函数判断子节点是否可以被提升。

以上就是Vue3 源码解读静态提升详解的详细内容,更多关于Vue3 静态提升的资料请关注我们其它相关文章!

(0)

相关推荐

  • Vue 打包的静态文件不能直接运行的原因及解决办法

    问题 吾辈使用 vue-cli 直接生成的 vue 模板项目,在模板之上继续开发的.然而在使用 npm run build 打包项目时,却发现打包好的项目在浏览器中直接打开好像什么都没有? 原因 查看了一下打包后的 index.html 源码,终于发现了一个重要的点: 所有涉及到路径的地方全都是以 / 开头的 下面是吾辈打包后生成的 dist 目录 dist:. │ index.html │ └─static ├─css │ app.b7bce283257fbd427fb1dc3fea80cee

  • Vue3+TypeScript+Vite使用require动态引入图片等静态资源

    问题:Vue3+TypeScript+Vite的项目中如何使用require动态引入类似于图片等静态资源! 描述:今天在开发项目时(项目框架为Vue3+TypeScript+Vite)需要 动态引入静态资源,也就是img标签的src属性值为动态获取,按照以往的做法直接是require引入即可,如下代码: <img class="demo" :src="require(`../../../assets/image/${item.img}`)" /> 写上后

  • vue-cli3访问public文件夹静态资源报错的解决方式

    今天在项目中使用了public文件夹里的静态资源,在本地测试没有发现问题,但是项目部署到fat服务器却报了404错误. 我发现原因在于我的项目没有部署在域名的根部,而我引用public文件是通过绝对路径方式引用的,因为就出现了路径错误. 路径如下: <img :src="`/image1.png`"> 在官网文档中发现这种情况需要为 URL 配置 publicPath 前缀:process.env.BASE_URL 正确的引用路径是: <img :src="

  • 浅谈Vue static 静态资源路径 和 style问题

    我就废话不多说了,大家还是直接看代码吧~ // Paths assetsRoot: path.resolve(__dirname, '../dist'), // 静态资源输出到二级目录下 assetsSubDirectory: 'static', // 静态资源cdn地址 assetsPublicPath: '/', 引用的时候可直接,不用返回上一级去查找,因为编译输出后的 static 下的资源 跟 html 是在同一个目录 <img class="navbar-brand-logo&q

  • vue cli3.x打包后如何修改生成的静态资源的目录和路径

    目录 vue cli3.x打包后修改生成的静态资源的目录和路径 vue3.3打包后,路径出错问题 vue cli3.x打包后修改生成的静态资源的目录和路径 在vue cli 2.x版本修改目录和路径,都在'/config/index'文件里修改. 但是vue cli升级成3.x发现这些文件都不见啦,就一个'vue.config.js'文件,我们可以在这里修改: `module.exports = {   // 没有书写outputDir属性   默认'dist'  对应dev.assetsSub

  • vue项目之webpack打包静态资源路径不准确的问题

    目录 webpack打包静态资源路径不准确 问题 静态资源找不到如js文件 图片找不到 webpack中的静态资源处理 Webpacked 资源 资源处理规则 在JavaScript里获取资源路径 "真实的" 静态资源 webpack打包静态资源路径不准确 问题 1.将打包好的项目部署到服务器,发现报错说图片找不到. 2.静态资源如js访问不到 分析并且解决问题 明确一点的就是,看到报错404,找不到静态资源,很明显,路径错误了. 静态资源找不到如js文件 资源打包路径有误,打包后的资

  • Vue3 源码解读静态提升详解

    目录 什么是静态提升 transform 转换器 hoistStatic 静态提升 walk 函数 walk 函数流程图 总结 什么是静态提升 静态提升是Vue3编译优化中的其中一个优化点.所谓的静态提升,就是指在编译器编译的过程中,将一些静态的节点或属性提升到渲染函数之外.下面,我们通过一个例子来深入理解什么是静态提升. 假设我们有如下模板: <div> <p>static text</p> <p>{{ title }}</p> </di

  • nginx源码分析线程池详解

    nginx源码分析线程池详解 一.前言 nginx是采用多进程模型,master和worker之间主要通过pipe管道的方式进行通信,多进程的优势就在于各个进程互不影响.但是经常会有人问道,nginx为什么不采用多线程模型(这个除了之前一篇文章讲到的情况,别的只有去问作者了,HAHA).其实,nginx代码中提供了一个thread_pool(线程池)的核心模块来处理多任务的.下面就本人对该thread_pool这个模块的理解来跟大家做些分享(文中错误.不足还请大家指出,谢谢) 二.thread_

  • python源码剖析之PyObject详解

    一.Python中的对象 Python中一切皆是对象. ----Guido van Rossum(1989) 这句话只要你学过python,你就很有可能在你的Python学习之旅的前30分钟就已经见过了,但是这句话具体是什么意思呢? 一句话来说,就是面向对象中的"类"和"对象"在Python中都是对象.类似于int对象的类型对象,实现了"类的概念",对类型对象"实例化"得到的实例对象实现了"对象"这个概念.

  • Vue编译器源码分析compileToFunctions作用详解

    目录 引言 Vue.prototype.$mount函数体 源码出处 options.delimiters & options.comments compileToFunctions函数逐行分析 createFunction 函数源码 引言 Vue编译器源码分析 接上篇文章我们来分析:compileToFunctions的作用. 经过前面的讲解,我们已经知道了 compileToFunctions 的真正来源你可能会问为什么要弄的这么复杂?为了搞清楚这个问题,我们还需要继续接触完整的代码. 下面

  • Java源码解析之TypeVariable详解

    TypeVariable,类型变量,描述类型,表示泛指任意或相关一类类型,也可以说狭义上的泛型(泛指某一类类型),一般用大写字母作为变量,比如K.V.E等. 源码 public interface TypeVariable<D extends GenericDeclaration> extends Type { //获得泛型的上限,若未明确声明上边界则默认为Object Type[] getBounds(); //获取声明该类型变量实体(即获得类.方法或构造器名) D getGenericDe

  • Java源码解析之GenericDeclaration详解

    学习别人实现某个功能的设计思路,来提高自己的编程水平.话不多说,下面进入正题. GenericDeclaration 可以声明类型变量的实体的公共接口,也就是说,只有实现了该接口才能在对应的实体上声明(定义)类型变量,这些实体目前只有三个:Class(类).Construstor(构造器).Method(方法)(详见:Java源码解析之TypeVariable详解 源码 public interface GenericDeclaration { //获得声明列表上的类型变量数组 public T

  • java集合类源码分析之Set详解

    Set集合与List一样,都是继承自Collection接口,常用的实现类有HashSet和TreeSet.值得注意的是,HashSet是通过HashMap来实现的而TreeSet是通过TreeMap来实现的,所以HashSet和TreeSet都没有自己的数据结构,具体可以归纳如下: •Set集合中的元素不能重复,即元素唯一 •HashSet按元素的哈希值存储,所以是无序的,并且最多允许一个null对象 •TreeSet按元素的大小存储,所以是有序的,并且不允许null对象 •Set集合没有ge

  • JAVA 枚举单例模式及源码分析的实例详解

    JAVA 枚举单例模式及源码分析的实例详解 单例模式的实现有很多种,网上也分析了如今实现单利模式最好用枚举,好处不外乎三点: 1.线程安全 2.不会因为序列化而产生新实例 3.防止反射攻击但是貌似没有一篇文章解释ENUM单例如何实现了上述三点,请高手解释一下这三点: 关于第一点线程安全,从反编译后的类源码中可以看出也是通过类加载机制保证的,应该是这样吧(解决) 关于第二点序列化问题,有一篇文章说枚举类自己实现了readResolve()方法,所以抗序列化,这个方法是当前类自己实现的(解决) 关于

  • Java集合框架源码分析之LinkedHashMap详解

    LinkedHashMap简介 LinkedHashMap是HashMap的子类,与HashMap有着同样的存储结构,但它加入了一个双向链表的头结点,将所有put到LinkedHashmap的节点一一串成了一个双向循环链表,因此它保留了节点插入的顺序,可以使节点的输出顺序与输入顺序相同. LinkedHashMap可以用来实现LRU算法(这会在下面的源码中进行分析). LinkedHashMap同样是非线程安全的,只在单线程环境下使用. LinkedHashMap源码剖析 LinkedHashM

  • 设计模式系列之组合模式及其在JDK和MyBatis源码中的运用详解

    组合模式及其在JDK源码中的运用 前言组合和聚合什么是组合模式示例透明组合模式透明组合模式的缺陷安全组合模式 组合模式角色组合模式在JDK源码中的体现组合模式应用场景享元模式优缺点总结 前言 本文主要会讲述组合模式的用法,并会结合在JDK和MyBatis源码中的运用来进一步理解组合模式. 在编码原则中,有一条是:多用组合,少用继承.当然这里的组合和我们今天要讲的组合模式并不等价,这里的组合其实就是一种聚合,那么聚合和组合有什么区别呢? 组合和聚合 人在一起叫团伙,心在一起叫团队.用这句话来诠释组

随机推荐