查看原文
其他

@Autowired 和 @Resource 的区别?

编程导航 2023-07-23

The following article is from 面试鸭 Author 编程导航和鱼友们

大家好呀,今天继续来给大家分享一些优质面试题及解答,希望对正在求职的小伙伴有所帮助。

后端

题目一

有哪些注解可以注入 Bean?@Autowired 和 @Resource 的区别?

官方解析

在 Spring 框架中,常用的注入 Bean 的注解包括:

  • @Autowired:自动注入按照类型自动装配,如果有多个同类型的 Bean,则需要通过 @Qualifier 指定具体的 Bean。
  • @Resource:Java 自带的注入方式按照名称自动装配,默认是按照属性名称进行匹配,如果需要按照 Bean 的名称进行匹配,可以使用 @Resource(name="beanName")。
  • @Inject:和 @Autowired 类似,也是按照类型进行自动装配,但是 @Inject 注解是 JSR-330 提供的,而 @Autowired 注解是 Spring 框架提供的。
  • @Value:用于注入配置文件中的属性值,可以指定默认值。
  • @Component:用于声明一个 Bean,作用类似于 XML 中的 <bean> 标签。

以上注解都可以用于注入 Bean,不同的注解之间的区别主要在于注入方式和实现方式的不同。@Autowired 和 @Resource 最常用,其中 @Autowired 按照类型自动装配更为常用,而 @Resource 按照名称自动装配则比较适合需要明确指定 Bean 名称的情况。

需要注意的是,注入 Bean 的时候最好使用接口类型作为注入对象,这样可以避免因为具体实现类变更导致注入失败的问题。

鱼友的精彩回答

Starry 的回答

Spring 内置的 @Autowired 以及 JDK 内置的 @Resource 和 @Inject 都可以用于注入 Bean。

一般在工作中,@Autowired和 @Resource 使用的比较多一些。

  • @Autowired 是 Spring 提供的注解, @Resource 是 JDK 提供的注解。
  • Autowired 默认的注⼊⽅式为 byType (根据类型进⾏匹配), @Resource 默认注⼊⽅式为byName (根据名称进⾏匹配)。
  • 当⼀个接⼝存在多个实现类的情况下, @Autowired 和 @Resource 都需要通过名称才能正确匹配到对应的 Bean。Autowired 可以通过 @Qualifier 注解来显示指定名称, @Resource 可以通过 name 属性来显示指定名称。

题目二

请你介绍下 JVM 内存模型,分为哪些区域?各区域的作用是什么?

官方解析

JVM 内存模型分为以下几个区域:

  1. 程序计数器(Program Counter Register):每个线程都有自己的程序计数器,用于指示当前线程执行的字节码指令的行号,以便线程执行时能够回到正确的位置。
  2. 虚拟机栈(JVM Stack):也称为 Java 方法栈,用于存储方法执行时的局部变量表、操作数栈、动态链接、方法出口等信息。每个线程在执行一个方法时,都会为该方法分配一个栈帧,并将该栈帧压入虚拟机栈,当方法执行完毕后,虚拟机会将其出栈。
  3. 本地方法栈(Native Method Stack):与虚拟机栈类似,用于存储本地方法的执行信息。
  4. (Heap):用于存储对象实例,是 JVM 中最大的一块内存区域。堆是被所有线程共享的,当创建一个新对象时,对象实例存储在堆中,堆中存储的对象实例都有一个标记用于标记对象是否存活。垃圾回收器会周期性地回收那些没有被标记为存活的对象。
  5. 方法区(Method Area):用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法区也是被所有线程共享的。
  6. 运行时常量池(Runtime Constant Pool):是方法区的一部分,用于存储编译期间生成的各种字面量和符号引用,这些内容在类加载后进入常量池中。

其中,程序计数器、虚拟机栈、本地方法栈是线程私有的,堆、方法区、运行时常量池是线程共享的。

鱼皮补充:这题一般是 JVM 必考题,也是 JVM 学习的基础

鱼友的精彩回答

苏打饼干的回答

运行时数据区,包括程序计数器、Java 虚拟机栈、本地方法栈、Java 堆、方法区等。

我很忙的回答

分两块儿讲,线程共有,线程私有。堆,方法区,直接内存,程序计数器,虚拟机栈,本地方法栈

  1. 程序计数器:字节码解释器可以改变程序计数器来读取指令,控制代码的流程;多线程的话,能记录线程当前运行的位置(唯一一个不出现outofmemoryerror的数据区)
  2. 虚拟机栈:为调用java方法服务的,每一个被线程执行的方法,为该栈中的栈帧,即每个方法对应一个栈帧。调用一个方法,就会向栈中压入一个栈帧;一个方法调用完成,就会把该栈帧从栈中弹出;若栈内存大小不允许动态扩容,可能会出现StackOverflowerror;如果栈内存大小可以动态扩容,则可能出现outofmemoryerror;
  3. 本地方法栈:native方法通过这个实现
  4. 堆:Java堆是Java虚拟机所管理内存中最大的一块,存放实例对象与数组;容易出现outofmemoryerror
  5. 方法区:存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码数据。即永久代,在jdk1.8中不存在方法区了,被称为元空间(Metaspace)。原方法区被分成两部分;1:加载的类信息,2:运行时常量池;加载的类信息被保存在元数据区中,运行时常量池保存在堆中

HeiHei 的回答

堆:

  • 堆是运行时数据区,所有类的实例和数组都是在堆上分配内存。它在 JVM 启动的时候被创建。对象所占的堆内存是由自动内存管理系统也就是垃圾收集器回收。

虚拟机栈:每个线程运行时所需要的内存,称为虚拟机栈 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法

本地方法栈:

  • 与虚拟机栈发挥的作用相似,相比于虚拟机栈为Java方法服务,本地方法栈为虚拟机使用的Native方法服务,执行每个本地方法的时候,都会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息。

程序计数器(PC寄存器):

  • 程序计数器区域是一块较小的区域,它用于存储线程的每个执行指令,每个线程都有自己的程序计数器

方法区(元空间):

  • 方法区是可提供各条线程共享的运行时内存区域。存储了每一个类的结构信息,例如运行时常量池(Runtime Constant Pool)、字段和方法数据、构造函数和普通方法的字节码内容,还包括一些在类、实例、接口初始化时用到的特殊方法。在JDK1.8之前的版本里,代表JVM的一块区域。在1.8版本以后,这块区域的名字改了,叫做“Matespace”

题目三

Linux 中的硬链接和软连接是什么,二者有什么区别?

官方解析

在 Linux 文件系统中,硬链接(hard link)和软链接(symbolic link)都是一种文件链接的方式,可以用于将一个文件链接到另一个文件上。它们的主要区别在于创建方式、所占空间和使用限制等方面。

硬链接是通过在文件系统中创建一个新的目录项(directory entry)指向同一文件 inode 的位置来实现的。因为硬链接实际上是指向同一 inode,所以如果原文件被删除,硬链接依然能够访问到原文件的内容。硬链接的使用范围比较受限,因为硬链接只能指向同一个文件系统内的文件,不能跨文件系统创建。

软链接是通过在文件系统中创建一个新的文件来实现的,该文件中包含指向另一个文件的路径。软链接可以跨文件系统创建,并且可以指向任何类型的文件。但是,当原文件被删除时,软链接将会失效。

总的来说,硬链接更加高效,因为它只是添加了一个新的目录项,所以对磁盘空间的消耗比软链接要小。但是,硬链接不能跨文件系统,所以在实际应用中需要根据具体的需求来选择使用哪种链接方式。

鱼友的精彩回答

林风的回答

Linux 系统下提供 ln 指令来进行文件链接

ln -s bar.txt foo.txt

ls 查看当前目录下的文件

➜ ll
total 1.2M
-rw-rw-r--. 1 Nova Nova    0 Aug 11 14:43  bar.txt
lrwxrwxrwx. 1 Nova Nova    7 Aug 11 14:43  foo.txt -> bar.txt

这个foo.txt,就是一个文件链接,文件链接主要分为硬链接和软链接,通过查看ln --help

➜  Desktop ln --help
Usage: ln [OPTION]... [-T] TARGET LINK_NAME
Create hard links by default, symbolic links with --symbolic.

Mandatory arguments to long options are mandatory for short options too.
  -s, --symbolic              make symbolic links instead of hard links

ln 指令默认创建的是硬链接,如果加入了-s参数,则会生成一个软链接。

硬链接

Linux 下的文件是通过索引节点-Inode来识别文件。硬链接当中,多个文件名可以指向同一个索引节点。当创建一个硬链接的时候,文件系统会维护一个引用计数,只要有文件指向这个区块,它就不会从硬盘上消失。两个文件拥有相同的 inode,通过查看文件内容也会发现是一个文件。只删除一个连接并不影响索引节点本身和其它的连接,只有当最后一个链接被删除后,文件的数据块及目录的连接才会被释放,也就是说,文件才会被真正删除

软链接

软链接又叫符号链接,这个文件包含了另一个文件的路径名,例如在上图中,foo.txt 就是 bar.txt 的软连接,bar.txt 是实际的文件,foo.txt包含的是对于 bar.txt 的 inode 的记录。

软连接可以是任意文件或目录,可以链接不同文件系统的文件,在对符号文件进行读或写操作的时候,系统会自动把该操作转换为对源文件的操作,但删除链接文件时,系统仅仅删除链接文件,而不删除源文件本身,这一点类似于 Windows 操作系统下的快捷方式。

区别

另外一点,在删除文件时:硬链接不会直接删除掉文件,除非这个链接是最后一个指向这个文件的链接;软链接也不会直接删除掉文件,相当于删除一个快捷方式。

软链接应用 - 灵活切换不同版本的目标程序 - 动态库版本管理 - 快捷方式 硬链接应用 - 从不同角度对文件进行分类 - 文件多人共享 - 文件备份

Starry 的回答

linux中软链接和硬链接区别为:

1、软链接以路径的形式存在,硬链接以文件副本的形式存在;

2、软链接可以跨文件系统,硬链接不可以;

3、软链接可以对目录进行链接,硬链接不可以。

更详细的区别对比如图所示:

前端

题目一

CSS 中,有哪些方式可以隐藏页面元素?有什么区别?

官方解析

在 CSS 中,有以下几种方式可以隐藏页面元素:

  1. display: none;:完全隐藏元素,元素不占据任何空间。
  2. visibility: hidden;:隐藏元素,但元素在页面中仍占据空间。
  3. opacity: 0;:将元素的透明度设为 0,元素隐藏但仍占据空间。
  4. position: absolute; left: -9999px;:将元素移出屏幕范围之外,元素隐藏但仍占据空间。
  5. z-index: -1;:将元素的层级设置为负数,元素隐藏但仍占据空间。

这些方式的区别在于是否占据空间和是否可见,开发者可以根据实际情况选择不同的方式来隐藏页面元素。

鱼友的精彩回答

Et cetera 的回答

先举例如下:

  • display:none(元素不可见,不占据空间,无法响应点击事件,会触发重排重绘)

  • visibility:hidden(元素不可见,占据页面空间,无法响应点击事件,会触发重绘)

  • opacity:0(元素不可见,占据页面空间,可以响应点击事件,会触发重绘)

  • 设置height、width模型属性为0,有内容还要设置overflow:hidden;(元素不可见,不占据页面空间,无法响应点击事件)

  • position:absolute(脱标定位到页面外,元素不可见,不影响页面布局)

  • clip-path(元素不可见,占据页面空间,无法响应点击事件)

你还费解吗的回答

在 CSS 中,可以通过以下几种方式隐藏页面元素:

  1. display: none:把整个元素从页面中移除,从而达到隐藏的效果。由于 display 是非继承属性,所以后代节点也会随父节点的消失而消失,即便设置后代节点的 display 也无法显示。另外,此做法可能会影响页面的布局,导致浏览器发生重排和重绘,造成性能的浪费。元素消失后,自身绑定的事件不会触发。

  2. visibility: hidden:将元素设置为不可见。与 display: none 不同,不会移除元素,而是实现真正意义上的隐藏效果,元素在页面上依然占位。此外,通过 visibility 切换元素的隐藏与显示时不会触发重排,只会触发重绘。该方法的优势在于:visibility是继承属性,即使父元素隐藏了,子元素也可以通过指定 visibility 的值显示出来;隐藏的内容可以被读屏器读取;能配合 transition 属性实现延时显示效果,提高用户体验。但要注意,隐藏后的元素无法触发自身绑定的事件。

  3. 盒属性归 0:将一个元素的 margin、border、padding、width 和 height 设置为 0,如果元素内有子元素或内容,还应该设置 overflow:hidden 来隐藏其子元素,代码示例如下。在这种方式下,元素不占据空间,且无法响应事件。

.hiddenBox {
    overflow: hidden;
    width:0;
    height:0;
    padding:0;
    border:0;
    margin:0;           
}
  1. position: absolute:使元素脱离文档流,相对于最近的定位(除静态定位外)父元素进行定位,通过调整偏移值将元素移出可视区域。当需要对网站进行 SEO 优化时,可以将  标签设置的 keywords(关键字)通过此方式隐藏在页面中,增加关键字的密度。注意,绝对定位会使元素以 display:inline-block 的形式显示。

  2. position: relative:类似于绝对定位,也是通过调整各个方向上的偏移值来将元素移出可视区域,不同的是相对定位是相对于自己原来的位置,且元素不会脱离文档流,在页面中依然占有原位。

  3. opacity: 0:只是将元素调整为不透明,所以依然存在于页面上,自身的事件可以触发,但其子元素不能通过设置 opacity 来达到显示的效果。

  4. transform: scale(0,0):将元素缩放为 0。元素仍占位,但无法响应绑定的监听事件。

  5. clip-path:通过裁剪的方式来实现元素的隐藏。元素仍占位,但无法响应绑定的监听事件。代码示例如下:

.hide {
    clip-path: polygon(0px 0px,0px 0px,0px 0px,0px 0px);
}

对于上述方法,最常用的还是 1 和 2,其他的只能算是一种小妙招,不太推荐使用。

题目二

什么是 JS 对象的可枚举性(enumerable)?

官方解析

JS 对象的可枚举性指的是对象的某些属性是否可以被 for...in 循环枚举到。每个对象属性都有一个可枚举性标识,这个标识决定了这个属性是否可以被枚举。在对象属性创建时,可以通过 Object.defineProperty() 方法的 enumerable 属性来设置这个属性是否可枚举,默认为 true。

有些内置对象的属性是不可枚举的,例如 Object.prototype 上的属性,这些属性可以通过 Object.getOwnPropertyNames() 方法获取。

在 ES6 中,引入了 Object.keys()、Object.values() 和 Object.entries() 方法来获取对象可枚举属性的键、值和键值对。同时,也提供了 Reflect.enumerate() 方法来获取对象的所有属性(包括不可枚举属性),但该方法已经被废弃。

鱼皮补充:这里题解表达的不清晰,如果你用了 defineProperty,没传参数就相当于设置为 false。这些属性默认为 true

鱼友的精彩回答

luckythus 的回答

什么是 JS 对象的可枚举性 对象的可枚举型是指:

对象中某些属性是否可以被 for...in 循环或者 Object.keys()函数枚举到,如果一个属性是可枚举的,则会出现在枚举过程中,反之则不会。

每个属性都有一个名字和一个属性描述符,属性描述符里面包括了该属性的许多特性,如可枚举性、可写性、可配置性和值等。

默认情况下,使用字面量或Object()构造函数创建的对象的所有属性都是可枚举的,可以使用Object.defineProperty或Object.defineProperties()来设置enumerable特性为false,设置其不可枚举

tip:

Object.keys()作用:用于返回一个 给定对象其自身可枚举的属性 的数组,该方法接收一个对象作为参数,返回该对象中所有可枚举属性的名称组成的一个数组,该数组中的的属性名称按照对象属性定义顺序一致,如果该对象里面的属性没有一个是可枚举性的,则返回空数组。


Object.defineProperty()作用:用来定义一个新的属性或者修改原有的属性

Object.defineProperties()作用:用来定义或修改多个属性

这两种都是可以自定义属性的行为,例如定义或修改属性是否可写、可枚举、可配置等;

它们接收三个参数:1、obj对象;2、prop:要定义或修改的属性名称;3、descriptor:要定义或修改的属性描述符

descriptor的属性描述符包括的特性有:

  1. value:属性的值,默认为undefined
  2. writable:属性是否可写,默认为false
  3. enumerable:属性是否可枚举,默认为false
  4. configurable:属性是否可配置,默认为false
  5. get:获取属性值的方法
  6. set:设置属性值的方法

Kristen 的回答

可枚举性(enumerable)用来控制所描述的属性,是否将被包括在 for…in 循环之中。每个对象属性都有一个可枚举性标识,这个标识决定了这个属性是否可以被枚举。具体来说,如果一个属性的 enumerable 为 false,下面三个操作不会取到该属性。

  1. for...in 循环
  2. Object.keys 方法
  3. JSON.stringify 方法

可枚举属性是指那些内部 “可枚举” 标志设置为 true 的属性。对于通过直接的赋值和属性初始化的属性,该标识值默认为 true。但是对于通过 Object.defineProperty 等定义的属性,该标识值默认为 false。

var o = { a: 1, b: 2 }

o.c = 3
Object.defineProperty(o, 'd', {
  value: 4,
  enumerable: false,
})

o.d //4

for (var key in o) console.log(o[key])
// 1 2 3

Object.keys(o) //['a','b','c']

JSON.stringify(o) //"{a:1,b:2,c:3}"

上面代码中,d 属性的 enumerable 为 false,所以一般的遍历操作都无法获取该属性,但还是可以直接获取它的值。

至于 for...in 循环和 Object.keys 方法的区别,在于前者包括对象继承自原型对象的属性,而后者只包括对象本身的属性。如果需要获取对象自身的所有属性,不管 enumerable 的值,可以使用 Object.getOwnPropertyNames 方法。

题目三

Vue 中 computed 和 watch 区别?分别适用于什么场景?

官方解析

Vue 中的 computed 和 watch 是两种用于响应式数据更新的方法。

computed 是计算属性,它会根据响应式数据的变化自动计算出新的值,并缓存结果,只有在计算属性所依赖的响应式数据发生改变时才会重新计算。computed 适用于需要根据响应式数据计算得出结果的场景,例如根据商品的数量和单价计算商品的总价,或者根据选中的过滤条件过滤出数据列表。

watch 是侦听器,它可以监听指定的响应式数据的变化,并在数据发生改变时执行指定的回调函数。watch 适用于需要执行一些异步或复杂操作的场景,例如监听表单输入框的变化并发送 Ajax 请求,或者监听路由变化并根据路由参数切换页面。

总的来说,computed 适用于计算数据的场景,而 watch 适用于需要执行异步或复杂操作的场景。

鱼皮补充:一旦面试官问你应用场景,都是希望你能结合自己项目中的实践经历去说

鱼友的精彩回答

codexgh 的回答

Vue中的computed和watch都是当数据发生变化时去执行对应的逻辑代码:

computed:computed计算属性,顾名思义就是如果页面中某个地方需要计算某个值其实可以用computed。

<template>
  <input v-model="firstNum" /> + <input v-model="secondNum" /> = {{ finalValue }}
</template>

<script setup>
  import { ref, computed } from 'vue'
  const firstNum = ref(0)
  const secondNum = ref(1)
  
  // 计算属性:
  const finalValue = computed(()=>{
    return firstNum + secondNum;
  })
</script>

fullName 他的回调函数中有两个依赖的值分别为firstName和lastName,当这两个值发生变化时,就会触发回调函数的执行,当每次执行完后,会将结果缓存起来,如果下次再去使用这个fullName但是所依赖的值没有发生变化,它会直接返回缓存的值。直到它所依赖的值发生改变时,才回去重新执行回调函数。需要注意的时computed必须要有返回值。

watch: watch 称为侦听器,就是说它会监听一个值的变化,当这个值发生变化时去执行一个对应的逻辑。

<template>
  姓:<input v-model="firstName" />
  名:<input v-model="lastName" />
  <p> 您的姓名为:{{ fullName }} </>
</template>

<script setup>
  import { ref, watch } from 'vue'
  const firstName = ref('')
  const lastName = ref('')
  const fullName = ref('')
  // 侦听器:
  watch([firstName, lastName], ([newFirstName, newSecondName], [oldFirstName, oldSecondName])=>{
    fullName.value = newFirstName + newSecondName;
  })
</script>
watch是不支持缓存的,只要有数据变化时,就会触发响应的操作,并且watch支持异步监听。监听的回调函数接收两个参数,第一个参数时最新的值,第二个参数时变化前的值。如果需要在组件加载时就出发回调函数可以使用immediate属性来设置;如果需要对对象进行深层的监听,可以使用deep属性开启深度监听。

总结:● 当需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时都要重新计算。● 当需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许执行异步操作 ( 访问一个 API ),限制执行该操作的频率,并在得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

星球活动

1.欢迎参与 30 天面试题挑战活动 ,搞定高频面试题,斩杀面试官!

2.欢迎已加入星球的同学 免费申请一年编程导航网站会员

3.欢迎学习 鱼皮最新原创项目教程,手把手教你做出项目、写出高分简历!

加入我们

欢迎加入鱼皮的编程导航知识星球,鱼皮会 1 对 1 回答您的问题、直播带你做出项目、为你定制学习计划和求职指导,还能获取海量编程学习资源,和上万名学编程的同学共享知识、交流进步。

💎 加入星球后,您可以:

1)添加鱼皮本人微信,向他 1 对 1 提问,帮您解决问题、告别迷茫!点击了解详情

2)获取海量编程知识和资源,包括:3000+ 鱼皮的编程答疑和求职指导、原创编程学习路线、几十万字的编程学习知识库、几十 T 编程学习资源、500+ 精华帖等!点击了解详情

3)找鱼皮咨询求职建议和优化简历,次数不限!点击了解详情

4)鱼皮直播从 0 到 1 带大家做出项目,已有 50+ 直播、完结 3 套项目、10+ 项目分享,帮您掌握独立开发项目的能力、丰富简历!点击了解详情

外面一套项目课就上千元了,而星球内所有项目都有指导答疑,轻松解决问题

星球提供的所有服务,都是为了帮您更好地学编程、找到理想的工作。诚挚地欢迎您的加入,这可能是最好的学习机会,也是最值得的一笔投资!

长按扫码领优惠券加入,也可以添加微信 yupi1085 咨询星球(备注“想加星球”):

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存