JAVA-打怪必备

JAVA-打怪必备,第1张

JAVA-打怪必备

JAVA-打怪必备
  • java基础
    • @Autowired和@Resource注解的区别是什么?
  • 数据库
    • 唯一索引(UNIQUE INDEX)和普通索引(INDEX)
    • 给有重复值的列添加唯一索引可以成功吗
  • 多线程
    • ThreadLocal底层实现
  • JVM
    • 双亲委派机制以及是否可以打破
    • 补充 OSGi:tomcat是如何打破双亲委派的
  • Spring
    • 被final修饰的类可以被spring代理吗
  • 缓存
  • 消息队列
  • 分布式、微服务
  • 算法与数据结构
  • 设计模式
  • 场景解决

java基础 @Autowired和@Resource注解的区别是什么?

都可以写在字段或setter方法上

  • @Autowired注解由Spring提供,只按照byType注入;
    @Autowired默认按类型装配,默认情况下必须要求依赖对象存在,如果要允许null值,可以设置它的required属性为false。如果想使用名称装配可以结合@Qualifier注解进行使用。
@Autowired
@Qualifier("bean的名字") 
按名称装配Bean,与@Autowired组合使用,解决按类型匹配找到多个Bean问题
一般@Autowired和@Qualifier一起用,@Resource单独用。
或者如下:
@Autowired   
public void setUserDao(@Qualifier("userDao") UserDao userDao) {   
    this.userDao = userDao;   
} 
  • @resource注解由J2EE提供,默认按照byName自动注入。
    @Resource,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行名称查找。如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
数据库

InnoDB 相比 MyISAM 有两大特点,一是支持事务,二是支持行级锁

唯一索引(UNIQUE INDEX)和普通索引(INDEX)

change buffer只限于用在普通索引的场 景下,而不适用于唯一索引。
查询性能几乎差不多:
对于普通索引来说,查找到满足条件的第一个记录(5,500)后,需要查找下一个记录,直到碰到第一个不满足k=5条件的记录。
对于唯一索引来说,由于索引定义了唯一性,查找到第一个满足条件的记录后,就会停止继续检索。
上面的查询区别对于性能而言是微乎其微的,InnoDB读写数据是按页来的。它所在的数据就都在内存里了,这些都是在内存中 *** 作的,所以性能微乎其微。
更新和插入性能:
普通索引用了change buffer性能好一些。
change buffer因为减少了随机磁盘访问,所 以对更新性能的提升是会很明显的。

  • 这个记录要更新的目标页在内存中
    对于唯一索引来说,找到这个记录要更新的位置,判断有没有冲突,插入这个值,语句执行结束
    对于普通索引来说,找到这个记录要更新的位置,插入这个值,语句执行结束
  • 这个记录要更新的目标页不在内存中
    对于唯一索引来说,需要将数据页读入内存,判断有没有冲突,插入这个值,语句结束
    对于普通索引来说,则是将更新记录记录在change buffer,语句就结束了
    https://blog.csdn.net/qq_36087425/article/details/89490460
给有重复值的列添加唯一索引可以成功吗

有一种情况是表中已经有n个重复的记录,这时候我们才想起来要添加唯一索引,再执行上面的 *** 作时,数据库会告诉你已经有重复的记录了,建立索引失败,这时候,我们可以用下面的 *** 作:

alter ignore table '表名' add unique index(user_id,user_name);

它会删除重复的记录(别怕,会保留一条),然后建立唯一索引,高效而且人性化。
特别注意:有些版本innodb的bug导致alter ignore不起作用
不支持的时候解决方案:

ALTER TABLE tableA ENGINE MyISAM;
ALTER IGNORE TABLE tableA ADD UNIQUE INDEX idx_col1_u (col1)
ALTER TABLE table ENGINE InnoDB;

https://www.cnblogs.com/Alight/p/4118655.html

多线程 ThreadLocal底层实现

ThreadLocal并不是用来解决多线程的共享变量问题,而是提供了线程的局部变量,每个线程都可以通过set()和get()来对这个局部变量进行 *** 作,获取独属于自己的副本。ThreadLocal 实例通常是类中的私有静态字段,并且其状态和线程关联。但不会和其他线程的局部变量进行冲突,即本线程共享内部变量,实现了与其它线程的数据隔离。
每个线程都保持对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例访问; 一个线程消失之后,所有的线程局部实例的副本都会被垃圾回收(除非存在对这些副本的其他引用)。

内部:
其数据结构是每个线程Thread类都有个属性ThreadLocalMap,用来维护该线程的多个ThreadLocal变量,该Map是自定义实现的Entry[]数组结构,并非继承自原生Map类,Entry其中Key即是ThreadLocal变量本身,Value则是具体该线程中的变量副本值。结构如图:

值得注意的是,Entry的Key即ThreadLocal对象是采用弱引用引入的。

https://www.pianshen.com/article/67641124984/
https://segmentfault.com/a/1190000037728236?utm_source=tag-newest

JVM 双亲委派机制以及是否可以打破


双亲委派的作用:
①防止加载同一个.class。通过委托去询问上级是否已经加载过该.class,如果加载过了,则不需要重新加载。保证了数据安全。
②保证核心.class不被篡改。通过委托的方式,保证核心.class不被篡改,即使被篡改也不会被加载,即使被加载也不会是同一个class对象,因为不同的加载器加载同一个.class也不是同一个Class对象。这样则保证了Class的执行安全。

当然可以破坏了,我们知道类的加载方式默认是双亲委派,如果我们有一个类想要通过自定义的类加载器来加载这个类,而不是通过系统默认的类加载器,说白了就是不走双亲委派那一套,而是走自定义的类加载器
我们知道双亲委派的机制是ClassLoader中的loadClass方法实现的,打破双亲委派,其实就是重写这个方法,来用我们自己的方式来实现即可
当然这里要注意一下,Object.class这是对象的顶级类,改变类的类加载器的时候要注意,如果全部改了,Object.class就找不到了,加载不了了
所以呢,这里重写的时候,要注意分类解决,把你想要通过自定义类加载器加载的和想通过默认类加载器加载的分隔开

如果不想打破双亲委派模型,就重写ClassLoader类中的findClass()方法即可,无法被父类加载器加载的类最终会通过这个方法被加载。
而如果想打破双亲委派模型则需要重写ClassLoader类loadClass()方法(当然其中的坑也不会少)。典型的打破双亲委派模型的框架和中间件有tomcat与osgi

补充 OSGi:tomcat是如何打破双亲委派的

既然说到OSGI,就要来解释一下OSGi是什么,以及它的作用
  OSGi(Open Service Gateway Initiative):是OSGi联盟指定的一个基于Java语言的动态模块化规范,这个规范最初是由Sun、IBM、爱立信等公司联合发起,目的是使服务提供商通过住宅网管为各种家用智能设备提供各种服务,后来这个规范在Java的其他技术领域也有不错的发展,现在已经成为Java世界中的“事实上”的模块化标准,并且已经有了Equinox、Felix等成熟的实现。OSGi在Java程序员中最著名的应用案例就是Eclipse IDE
  OSGi中的每一个模块**(称为Bundle)与普通的Java类库区别并不大,两者一般都以JAR格式进行封装,并且内部存储的都是Java Package和Class。但是一个Bundle可以声明它所依赖的Java Package(通过import-Package描述),也可以声明他允许导出发布的Java Package(通过Export-Package描述)。在OSGi里面,Bundle之间的依赖关系从传统的上层模块依赖底层模块转变为平级模块之间的依赖(至少外观上如此),而且类库的可见性能得到精确的控制,一个模块里只有被Export过的Package才可能由外界访问**,其他的Package和Class将会隐藏起来。除了更精确的模块划分和可见性控制外,引入OSGi的另外一个重要理由是,基于OSGi的程序很可能可以实现模块级的热插拔功能,当程序升级更新或调试除错时,可以只停用、重新安装然后启动程序的其中一部分,这对企业级程序开发来说是一个非常有诱惑性的特性
  OSGi之所以能有上述“诱人”的特点,要归功于它灵活的类加载器架构。OSGi的Bundle类加载器之间只有规则,没有固定的委派关系。例如,某个Bundle声明了一个它依赖的Package,如果有其他的Bundle声明发布了这个Package,那么所有对这个Package的类加载动作都会为派给发布他的Bundle类加载器去完成。不涉及某个具体的Package时,各个Bundle加载器是平级关系,只有具体使用某个Package和Class的时候,才会根据Package导入导出定义来构造Bundle间的委派和依赖
  另外,一个Bundle类加载器为其他Bundle提供服务时,会根据Export-Package列表严格控制访问范围。如果一个类存在于Bundle的类库中但是没有被Export,那么这个Bundle的类加载器能找到这个类,但不会提供给其他Bundle使用,而且OSGi平台也不会把其他Bundle的类加载请求分配给这个Bundle来处理
  一个例子:假设存在BundleA、BundleB、BundleC三个模块,并且这三个Bundle定义的依赖关系如下:
  BundleA:声明发布了packageA,依赖了java.*的包
  BundleB:声明依赖了packageA和packageC,同时也依赖了Java.*的包
  BundleC:声明发布了packageC,依赖了packageA
  那么,这三个Bundle之间的类加载器及父类加载器之间的关系如下图:

  由于没有涉及到具体的OSGi实现,所以上图中的类加载器没有指明具体的加载器实现,只是一个体现了加载器之间关系的概念模型,并且只是体现了OSGi中最简单的加载器委派关系。一般来说,在OSGi中,加载一个类可能发生的查找行为和委派关系会比上图中显示的复杂,类加载时的查找规则如下:
    1)以java.*开头的类,委派给父类加载器加载
    2)否则,委派列表名单内的类,委派给父类加载器加载
    3)否则,import列表中的类,委派给Export这个类的Bundle的类加载器加载
    4)否则,查找当前Bundle的ClassPath,使用自己的类加载器加载
    5)否则,查找是否在自己的Fragment Bundle中,如果是,则委派给Fragment bundle的类加载器加载
    6)否则,查找Dynamic import列表的Bundle,委派给对应Bundle的类加载器加载
    7)否则,查找失败
  从之前的图可以看出,在OSGi里面,加载器的关系不再是双亲委派模型的树形架构,而是已经进一步发展成了一种更复杂的、运行时才能确定的网状结构。
  
相关面试题:一个类的静态块是否可能被执行两次
答案,如果一个类,被两个 osgi的bundle加载, 然后又有实例被初始化,其静态块会被执行两次

参考https://www.cnblogs.com/crazymakercircle/p/15554725.html

Spring 被final修饰的类可以被spring代理吗

不可以。分为JDK动态代理和CgLib代理考虑。

场景1:
如果委托类没有实现接口的话,就不能使用newProxyInstance方法,进而不能使用JDK动态代理
场景2:
Cglib是针对类来实现代理的,对指定的目标类生成一个子类,通过方法拦截技术拦截所有父类方法的调用。因为是生成子类,所以就不能用在final修饰的类上。
综合起来,就是 被final修饰的类 ,不可以被spring代理

缓存 消息队列 分布式、微服务 算法与数据结构 设计模式 场景解决

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5693520.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存