java学习日常

接口

interface中的方法默认为public abstract 的 ,变量默认为public static final

集合

在这里插入图片描述

上图的虚线重合就变成了看着像实线,最好从线的出发点去看

请添加图片描述

  • java的数组的长度在创建时候是固定的,所以有了arraylist。
  • arraylist 基于数组实现的,所以查询速度快,增删速度慢,
  • linklist是基于链表结构实现的,所以增删速度快,查询速度慢。
java变量是否需要初始化的问题

1、成员变量:无需初始化。
成员变量无需初始化,系统在创建实例的过程中默认初始化。

补充:以final修饰的成员变量(常量)。必须显性的初始化赋值(可以直接赋值或者通过有参构造的方式)。

2、 方法里面的形参变量: 无需初始化。
Java类方法,属于按值传递机制,调用方法的时候,完成参数的传递,相当形参被初始化。

3、局部变量:必须初始化。
与成员变量不同,局部变量必须显示初始化,才能够使用。

ArrayList与LinkedList
  • ArrayList和LinkedList顾名思义,ArrayList是Array(动态数组)的数据结构,相当于动态数组;
  • LinkedList是Link(链表)的双向数据结构,也可当作堆栈、队列、双端队列。
  • 对于随机访问List时(get和set操作),ArrayList比LinkedList的效率更高,因为
  • inkedList是线性的数据存储方式,所以需要移动指针从前往后依次查找。
    对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据(可以在上述ArrayList代码中体现)。

ArrayList和LinkedList线程不安全,在多线程中不建议使用。hashMap也为现线程不安全,hashTable线程安全。

哈希表的本质是数组加上链表;

排序二叉树的特点为,左子树的值小于子树的根节点的值,右子树的值大于根节点的值。
排序二叉树如果插入的节点本身就是有序的,如 从小到大排列,或者从大到小排列,那么二叉树就变成了一个 链表。
所有实现了 collection接口的容器类都有一个iterator方法用以返回一个实现了Iterator 对象的方法,

自动装箱和拆箱,自动类型转换

  • 基本类型才会自动类型转换
  • 包装类型不会自动类型转换
  • 包装类型可以自动拆箱 (基本类型也可以自动装箱)

类加载阶段

加载-连接(验证、准备、解析)-初始化;

连接阶段分为 验证准备解析。准备阶段为类变量分配内存并设置类变量(static修饰的变量)的初始化(根据数据类型赋值)。对于final修饰的类变量 会根据初始值赋值。

字符串常量池,

字符串常量值中的字符串只存在一份,且被所有线程共享。

字符串常量池中的内容是在类加载完成后,准备阶段后,在堆中生成字符串实例,然后在字符串常量池中,保存该实例的引用,记住:string pool中存的是引用值而不是具体的实例对象,具体的实例对象是在堆中开辟的一块空间存放的。

class常量池简介:

我们写的每一个Java类被编译后,就会形成一份class文件;class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(constant pool table). 从上面的反编译字节码中可以看到,Class的常量池其实就是一张记录着该类的一些常量、方法描述、类描述、变量描述信息的表。主要用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References);
JAVA的三种常量池 字符串常量池 运行时常量池 class文件常量池(class常量池是在编译后每个class文件都有的,class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是 常量池*(constant pool table)*,用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References)。*字面量就是我们所说的常量概念,如文本字符串、被声明为final的常量值等。)

每个class文件都有一个class常量池。

运行时常量池(Runtime Constant Pool):

运行时常量池存在于内存中,也就是class常量池被加载到内存之后的版本,不同之处是:它的字面量可以动态的添加(String#intern()),符号引用可以被解析为直接引用
JVM在执行某个类的时候,必须经过加载、连接、初始化,而连接又包括验证、准备、解析三个阶段。而当类加载到内存中后,jvm就会将class常量池中的内容存放到运行时常量池中,由此可知,运行时常量池也是每个类都有一个。在解析阶段,会把符号引用替换为直接引用,解析的过程会去查询字符串常量池,也就是我们上面所说的StringTable,以保证运行时常量池所引用的字符串与字符串常量池中是一致的。
运行时常量池是一个统称, 也包括字符串常量池, 但是字符串常量池放的只是字符串, 而运行时常量池放中, 还包括类信息, 属性信息, 方法信息, 以及其他基础类型的常量池比如int, long等;

什么是字面量和符号引用:

  • 字面量包括:1.文本字符串 2.八种基本类型的值 3.被声明为final的常量等;
  • 符号引用包括:1.类和方法的全限定名 2.字段的名称和描述符 3.方法的名称和描述符。
    字面量就是我们所说的常量概念,如文本字符串、被声明为final的常量值等。 符号引用是一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可(它与直接引用区分一下,直接引用一般是指向方法区的本地指针,相对偏移量或是一个能间接定位到目标的句柄)。

方法区实现的演变

  • jdk1.7之前, 运行时常量池(包含着字符串常量池)都在方法区, 具体的hotspot虚拟机实现方法区这个概念为永久代;
  • jdk1.7阶段, 字符串常量池从 方法区/永久代 移到堆中, 运行池常量剩下的部分依旧在方法区中(剩下类信息, 属性信息, 方法信息等), 同样是hotspot中的永久代
  • jdk1.8, 方法区的实现从永久代变成了元空间, 字符串常量池依旧在堆中, 运行时常量池在方法区中, 方法区由元空间(类信息)和堆实现(常量池、静态变量)
  • 已经明确的一点是 字符串常量池、静态变量 在jdk7时从方法区移入Java堆中我看了jdk6/7/8三版jvm文档,对运行时常量池的描述都是“方法区的一部分”看知乎很多描述都说这是规范上的描述,实际在物理上运行时常量池已被移入Java堆中
  • 元空间并不是方法区!!! 方法区包括类信息、常量、静态变量等,是JVM规范。 方法区是jvm规范里面的概念。最开始的实现叫PermGen,后来是PermGen + java heap 一起实现,现在叫Metaspace + Java heap 一起协调工作。

你可以把方法区理解为Java中定义的一个接口,把元空间/永久代看做这个接口的具体实现类 方法区是规范,元空间/永久代是Hotspot针对该规范进行的实现。 在JVM规范中,方法区被定义为一种逻辑区域,而方法区具体怎么实现是各JVM的实现细节,所以方法区的内容在堆里也好,不在堆里也好都是符合标准的。

jvm方法区

compute和computeIfPresent区别

compute和computeIfPresent

  • compute():无论key是否存在,都会执行后面方法。若后面方法返回newValue为NULL,则会从Map中remove(key),若返
    newValue不为NULL,则put(key,newValue),简单一句话:newValue有值则插入更新,无值就删除该key
  • computeIfPresent(): 当key存在的时候,才会执行后面方法。
  • computeIfAbsent(): 当key不存在的时候,才会执行后面方法。
  • merge(key,value,function): 当key不存在的时候,直接put(key,value),若key存在,则执行function方法。

类优先于接口

public class MyImplement2 extends MyImplement implements Interface2 此时在实现类MyImplement2中调用helloWorld()方法,到底执行的是MyImplement中的方法还是执行Interface2中的方法?
答:因为类优先于接口,所以将会执行MyImplement中的方法。

发生这种情况的原因是,实现类MyImplement即实现了接口Interface1又实现了接口Interface2,恰巧两个接口中都定义可相同的默认方法。说白了就是编译器此时已经被干懵了,当我们在MyImplement类中调用方法时,它不知道该去调用Interface1的默认方法还是去调用Interface2的方法。解决方法就是在实现类中实现该方法。

实现“继承“ 的方法:

1.继承Thread类(Override它的run方法)
2.实现Runnable接口(实现run方法)
3.使用ExecutorService、Callable、Future实现有返回结果的多线程

== 和 equals() 和 hashCode()

  1. == 用于比较基本数据类型时比较的是值,用于比较引用类型时比较的是引用指向的地址。
  2. Object 中的equals() 与 == 的作用相同,但String类重写了equals()方法,比较的是对象中的内容。
  3. equals()相等的两个对象他们的hashCode()肯定相等,也就是用equals()对比是绝对可靠的。
  4. hashCode()相等的两个对象他们的equal()不一定相等,也就是hashCode()不是绝对可靠的。
String s1= "hello world"  
String s2=new String("hello world")
s1直接指向常量池中的“HelloWorld”,s2指向堆中的对象,堆中的对象存储的是常量池中的相同字符串地址,即"HelloWorld"在常量池中的地址。
== 比较的s1和s2中存储的地址,一个是常量池中的HelloWorld地址,一个是堆中的对象的地址,== 就是false(地址不同),
equals比较的是最终在常量池中的内容,所以是true

方法不可以被继承?

  • 子类继承父类 会自然的继承父类的属性和方法,当你更改子类中对应方法时才叫重写。
  • 在java中,子类构造器会默认调用super()( 无论构造其中是否写有super()),用于初始化父类成员,同时当父类中存在有参构造器时,必须提供无参构造器,子类构造器中并不会自动继承有参构造器,仍然默认调用super(),使用无参构造器。因此,一个类想要被继承必须提供无参构造器。

抽象方法可以在接口和抽象类里面声明,抽象类可以没有抽象方法

1、abstract类不能用来创建abstract类的对象;2、final类不能用来派生子类,因为用final修饰的类不能被继承;3、如2所述,final不能与abstract同时修饰一个类,abstract类就是被用来继承的;4、类中有abstract方法必须用abstract修饰,但abstract类中可以没有抽象方法,接口中也可以有abstract方法。

1.强引用 , 例如 Object a= new Object();只要存在强引用 ,则不能回收。
2.弱引用 , 在内存不够时回收。
3.虚引用,只能存活到一下次垃圾回收。
4.影子引用 , 只是在回收时触发一个事件

关于final的重要知识点
1、final关键字可以用于成员变量、本地变量、方法以及类。
2、 final成员变量必须在声明的时候初始化或者在构造器中初始化,否则就会报编译错误。
3、 你不能够对final变量再次赋值。
4、 本地变量必须在声明时赋值。
5、 在匿名类中所有变量都必须是final变量。
6、 final方法不能被重写。
7、 final类不能被继承。
8、 没有在声明时初始化final变量的称为空白final变量(blank final variable),它们必须在构造器中初始化,或者调用this()初始化。不这么做的话,编译器会报错“final变量(变量名)需要进行初始化”。

数据类型转换

当使用 +、-、*、/、%、运算操作是,遵循如下规则:
只要两个操作数中有一个是double类型的,另一个将会被转换成double类型,并且结果也是double类型,如果两个操作数中有一个是float类型的,另一个将会被转换为float类型,并且结果也是float类型,如果两个操作数中有一个是long类型的,另一个将会被转换成long类型,并且结果也是long类型,否则(操作数为:byte、short、int 、char),两个数都会被转换成int类型,并且结果也是int类型。
语句 1 :(b1 + b2) 被转换为int类型 但是 b3仍为 byte ,所以出错 要么将b3转化为int 要么将(b1 + b2) 强制转换为byte类型。所以语句1错误。
语句 2:b4 、b5被声明final 所以类型是不会转换, 计算结果任然是byte ,所以 语句2正确。
语句 3:(b1 + b4) 结果仍然转换成int 所以语句 3 错误。
语句 4 : (b2 + b5) 结果仍然转换为int , 所以语句4错误。

①无论如何,Integer与new Integer不会相等。不会经历拆箱过程,
②两个都是非new出来的Integer,如果数在-128到127之间,则是true,否则为false
java在编译Integer i2 =
128的时候,被翻译成-> Integer i2 = Integer.valueOf(128);而valueOf()函数会对-128到127之间的数进行缓存
③两个都是new出来的,都为false
④int和integer(无论new否)比,都为true,因为会把Integer自动拆箱为int再去比

final和static修饰


final

     final可以修饰属性、方法、类,局部变量(方法中的变量)。

     final修饰的属性的初始化可以在编译期,也可以在运行期,但是在初始化后就不能改变了。

     final修饰的属性表明是一个常数(创建后不能被修改)。

     final修饰的方法表示该方法在子类中不能被重写,同样该类不能被继承。

static

    static可以修饰属性,方法,代码段,内部类。

    static修饰的属性的初始化在类加载的时候可以改变。

    static修饰的属性强调它们只有一个,但是它不可以修饰局部变量。

    final static

    final static 和static final没有什么区别都可以使用。

static修饰的属性强调它们只有一个,final修饰的属性表明是一个常数,final static 修饰的属性表示一旦给值,就不可以修改,并且可以通过类名访问。

java修饰符

private修饰的变量或者方法只能在本类中访问,(通过本类生成的对象 + "."也是不能访问的)

finally中return语句会覆盖try-catch中的return语句

继承的两种情况

继承 只有两种情况:类继承类(单继承)、接口继承接口(多继承) 实现 只有一种情况:类实现接口(多实现) A 正确 B 接口可以定义成员常量 C 正确 D 类不能有子接口,只能有子类

1.java支持单继承,却可以实现多个接口。a对d错
2.接口没有构造方法,所以不能实例化,抽象类有构造方法,但是不是用来实例化的,是用来初始化的。c对
3.抽象类可以定义普通成员变量而接口不可以,但是抽象类和接口都可以定义静态成员变量,只是接口的静态成员变量要用static final public 来修饰。b错

基类就是父类。。而不是子类。

finally 会不会影响 try中return 语句的值?

先来看一段代码:

public abstract class Test {
  public static void main(String[] args) {
    System.out.println(beforeFinally());
  }

  public static int beforeFinally(){
    int a = 0;
    try{
      a = 1;
      return a;
    }finally{
      a = 2;
    }
  }
}
/**output:
1
*/

从结果上看,貌似finally里的语句是在return之后执行的,其实不然,实际上finally里的语句是在在return
之前执行的。那么问题来了,既然是在之前执行,那为什么a的值没有被覆盖了?

实际过程是这样的:当程序执行到try{}语句中的return方法时,它会干这么一件事,将要返回的结果存储到一个临时栈中,然后程序不会立即返回,而是去执行finally{}中的程序,在执行a = 2时,程序仅仅是覆盖了a的值,但不会去更新临时栈中的那个要返回的值。执行完之后,就会通知主程序“finally的程序执行完毕,可以请求返回了”,这时,就会将临时栈中的值取出来返回。这下应该清楚了,要返回的值是保存至临时栈中的。

再来看一个例子,稍微改下上面的程序:

public abstract class Test {
  public static void main(String[] args) {
    System.out.println(beforeFinally());
  }

  public static int beforeFinally(){
    int a = 0;
    try{
      a = 1;
      return a;
    }finally{
      a = 2;
      return a;
    }
  }
}
/**output:
2
*/

在这里,finally{}里也有一个return,那么在执行这个return时,就会更新临时栈中的值。同样,在执行完finally之后,就会通知主程序请求返回了,即将临时栈中的值取出来返回。故返回值是2。

  • finally语句在try或catch中的return语句执行之后返回之前执行
  • 且finally里的修改语句不能影响try或catch中 return 已经确定的返回值
  • 若finally里也有return语句则覆盖try或catch中的return语句直接返回。

switch支持10种类型 基本类型

以java8为准,switch支持10种类型 基本类型:byte char short int 对于包装类 :Byte,Short,Character,Integer String enum;实际只支持int类型 Java实际只能支持int类型的switch语句,那其他的类型时如何支持的?
a、基本类型byte char short 原因:这些基本数字类型可自动向上转为int, 实际还是用的int。
b、基本类型包装类Byte,Short,Character,Integer 原因:java的自动拆箱机制 可看这些对象自动转为基本类型
c、String 类型 原因:实际switch比较的string.hashCode值,它是一个int类型 如何实现的,网上例子很多。此处不表。
d、enum类型 原因 :实际比较的是enum的ordinal值(表示枚举值的顺序),它也是一个int类型 所以也可以说 switch语句只支持int类型

重载与重写:

重载就是一句话:同名不同参,返回值无关。
覆盖/重写:同名同参
参数列表:个数丶类型丶参数类型顺序等

Java中list为什么有序?

List底层有两种实现方式基于数组ArrayList)和基于链表LinkedList)。

首先要明确的是:既然前提是「有序」,那么有序的表现是什么?就是在遍历元素的时候其顺序与添加的顺序是一致的,因此应该使用默认的add操作,不能随便指定位置插入,否则即使用的是List也不能表现出有序的特征!

1)数组:是一种紧凑的数据结构,占用的是一块连续的内存,每个元素都挨个排列,可以通过索引快速获取指定位置的元素,一般情况下添加元素时按照从左往右顺序添加,因此它是有序的。

2)链表:内存中是不连续的,但是其中的每个节点都有个变量记录了该节点的下一个节点(简单起见这里用单向链表举例):

N1 -> N2 -> … -> Nn

选定一个节点,就能通过该节点的next指针(变量)找到下一个节点。

而链表插入操作一般是边界操作(头/尾插入),只要插入操作调用的方法一样,就能保证其中元素的顺序与插入时一致。

题外话:

我们都知道HashSet(这里强调HashSet,是因为Set并不一定是无序的,要注意实现)是无序且不允许重复的,它基于HashMap,基本就是直接调用HashMap的API,而HashMap的元素的存储位置是根据元素(对象)的hashCode: int计算所得,可能先添加的元素hashCode比较大,而后添加的元素hashCode比较小,因此造成添加顺序与实际元素顺序不符的情况,这就是无序

总结:

  • List只是具备有序的性质,但是否有序依然要视具体的操作来定;
  • HashSet天然无序,它不受调用方式、插入顺序的影响,只以hashCode决定元素位置。

@Inner与@Inner(value = false)区别

@Inner@Inner(value = false) 是什么 基于微服务架构SpringCloud OAuthToken的接口调用。
区别 @Inner是对内部调用公开,拦截外部调用 @Inner(value = false)是对外部暴露 相同点 两者都会舍弃token鉴权。
所以也可得到@Inner(value = false)与不写这个注解的区别是是否还有token鉴权

他们本质都利用了将uri加入到security.oauth2.client.ignore-urls配置中,二者详细区别见下文
@Inner使用及原理

spring 初始化bean的两种方式

实现InitializingBean接口

InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候会执行该方法。

在配置文件中指定init-method

在spring初始化bean的时候,如果该bean是实现了InitializingBean接口,并且同时在配置文件中指定了init-method,系统则是先调用afterPropertiesSet方法,然后在调用init-method中指定的方法

AbstractAutowireCapableBeanFactory类中的invokeInitMethods讲解的非常清楚,源码如下:

protected  void  invokeInitMethods(String beanName,  final  Object bean, RootBeanDefinition mbd)  throws  Throwable {
     //判断该bean是否实现了实现了InitializingBean接口,如果实现了InitializingBean接口,则只掉调用bean的afterPropertiesSet方法
     boolean  isInitializingBean = (bean  instanceof  InitializingBean);
     if  (isInitializingBean && (mbd ==  null  || !mbd.isExternallyManagedInitMethod( "afterPropertiesSet" ))) {
         if  (logger.isDebugEnabled()) {
             logger.debug( "Invoking afterPropertiesSet() on bean with name '"  + beanName +  "'" );
         }
         
         if  (System.getSecurityManager() !=  null ) {
             try  {
                 AccessController.doPrivileged( new  PrivilegedExceptionAction<Object>() {
                     public  Object run()  throws  Exception {
                         //直接调用afterPropertiesSet
                         ((InitializingBean) bean).afterPropertiesSet();
                         return  null ;
                     }
                 },getAccessControlContext());
             }  catch  (PrivilegedActionException pae) {
                 throw  pae.getException();
             }
         }                
         else  {
             //直接调用afterPropertiesSet
             ((InitializingBean) bean).afterPropertiesSet();
         }
     }
     if  (mbd !=  null ) {
         String initMethodName = mbd.getInitMethodName();
         //判断是否指定了init-method方法,如果指定了init-method方法,则再调用制定的init-method
         if  (initMethodName !=  null  && !(isInitializingBean &&  "afterPropertiesSet" .equals(initMethodName)) &&
                 !mbd.isExternallyManagedInitMethod(initMethodName)) {
             //进一步查看该方法的源码,可以发现init-method方法中指定的方法是通过反射实现
             invokeCustomInitMethod(beanName, bean, mbd);
         }
     }
}

springboot注入bean方式

  • 将主配置类(@SpringBootApplication标注的类)的所在包及下面所有子包里面的所有组件扫描到Spring容器(spring在启动时候,默认会扫描启动类所在的包以及子包下的bean
  • Spring Boot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中
  • 使用 @configuration + @bean
  • 再@configuration上再使用@ImportResource导入我们编写的 bean.xml
  • 使用@Import 快速给容器中导入一个组件(在其他框架与spring整合时使用它来导入外来组件)
  • 自定义组件想要使用Spring容器底层的一些组件(ApplicationContextBeanFactory等),需要实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件
Spring Boot 项目的运行方式
  • IDE 中直接运行 main 方法
  • mvn spring-boot:run 命令启动
  • 打包后通过 java -jar 方式启动
  • 打包后通过 Tomcat 启动

其中前两种是开发环境下运行的主要方式

插件有如下几种调用方式
  • mvn 生命周期阶段:如 mvn clean,maven 会自动执行与该生命周期绑定的插件目标。
  • mvn groupId:artifactId[:version]:goal,如 mvn org.apache.maven.plugins:maven-clean-plugin:3.1.0:clean,对于版本号 version 来说是可以省略的,如果省略 maven 会使用本地仓库中最新的版本。
  • mvn 插件前缀:goal:插件前缀可以理解为插件的标识,用于简化插件的调用,例如 mvn spring-boot:run 中的 spring-boot 就是 spring-boot-maven-plugin 插件的前缀,自定义插件如果遵循 xxx-maven-plugin 的形式,maven 默认会将 maven-plugin 前面的内容作为插件前缀。

注解

@Inner

@Inner与@Inner(value = false) 是什么 基于微服务架构SpringCloud OAuth无Token的接口调用。 区别 @Inner是对内部调用公开,拦截外部调用 @Inner(value = false)是对外部暴露 相同点 两者都会舍弃token鉴权,如果… 当value为false时,咱不做任何处理,此时Inner仅起到了一个ignore-url的作用。

@ExceptionHandler

@ExceptionHandler:用于指定异常处理方法。当与@RestControllerAdvice配合使用时,用于全局处理控制器里的异常。注解了@RestControllerAdvice的类的方法可以使用@ExceptionHandler、@InitBinder、@ModelAttribute注解到方法上。

@importResource

@importResource用来 将之前我们在xml文件里写法生成的bean给导入到@configuration所在的类下,用于迁移老写法的bean;

@Resource 和@component

@Resource @Autired 还有一个是全部属性注册bean的注解
而 @component @service @等等是注册bean的注解,区别于上面是bean的使用

@ConfigurationProperties注解

@ConfigurationProperties注解
如果一个配置类只配置@ConfigurationProperties注解,而没有使用@Component,那么在IOC容器中是获取不到properties 配置文件转化的bean。说白了 @EnableConfigurationProperties 相当于把使用 @ConfigurationProperties 的类进行了一次注入。

Configuration properties scanning was enabled by default in Spring Boot 2.2.0 but as of Spring Boot 2.2.1 you must opt-in using @ConfigurationPropertiesScan.

因为Spring会通过类路径的扫描自动注册@ConfigurationProperties类。

你需要做的是在Application类中使用@ConfigurationPropertiesScan注解来扫描配置类的包地址,如:

@ConfigurationPropertiesScan("com.jay.mydemo.config")

@ConditionalOnProperty

@ConditionalOnProperty(prefix = “notification”, name = “service”, havingValue = “sms”)
借助于hadingValue属性,清楚地表明,仅当notification.service设置为sms时,才希望加载SmsNotification。

@PostMapping 中produces和consumes的使用

@PostMapping(value = “/accepted”, produces = {MediaType.APPLICATION_OCTET_STREAM_VALUE, MediaType.APPLICATION_JSON_VALUE})
produces是指定返回的请求类型 consumes是该接口只接受指定的请求类型

@Autowired/@Qualifier/@Resource/@Value区别详解

  • @Autowired根据属性类型进行自动装配。
  • @Qualifier 是根据属性名称进行注入。@Qualifier 注解的使用,需要和上面 @Autowired 一起使用。
  • @Resource 可以根据属性类型注入,也可以根据属性名称注入。(不写参数根据类型注入,写参数name根据名字注入)
  • @Value 注入普通类型属性。(简单数据类型)

@Autowired 为啥用于构造方法上?

最近看到有些工程里面将@Autowired使用在了构造方法上,觉得有些奇怪,为何不写在方法上?

一番百度后有了答案

原来是执行顺序的问题

Java变量的初始化顺序为:静态变量或静态语句块–>实例变量或初始化语句块–>构造方法–>@Autowired

如果在构造方法中操作了之前用@Autowired注解的对象、方法,就会空指针报错

因为此时还没有成功的注入bean。

所以推荐在构造方法上使用@Autowired

@Order

  1. 注解@Order或者接口Ordered的作用是定义Spring IOC容器中Bean的执行顺序的优先级,而不是定义Bean的加载顺序,Bean的加载顺序不受@Order或Ordered接口的影响(个人理解:在项目启动的时候会将所有的bean进行注入,注入后我们调用所需要的bean的先后顺序不会受到这@Order注解的影响)
  2. 观察@Order的注解说明,第一句写着: @Order defines the sort order for an annotated component. 提到这个注解只是对component排序,那么哪里会收到这个排序数值的影响呢?
  3. @Order会影响依赖注入的顺序,如果存在同样类型的多个bean,且依赖声明使用了List< BeanInterface> ,会将所有bean实例按照Order声明的顺序放入一个ArrayList中注入,如果用的是Collection或者Set则无效,因为类型本身无序。而CommandLineRunner声明的run方法,会在bean被IOC容器装配完成之后被调用,方法注释简单明了的一句Callback used to run the bean可以理解为bean实例真正构建完成之后的回调方法,而这个方法会受到@Order的顺序影响
  4. 除了以上两种用法,@Aspect声明的切面类继承了OncePerRequestFilter的过滤器等,它们的作用顺序也会受到Order的影响。

@DependsOn

@DependsOn 指定创建Bean依赖顺序
@DependsOn注解是Spring中提供的一个指定Spring创建Bean的依赖顺序的注解。例如,在Spring中需要创建A对象和B对象,可以使用@DependsOn注解指定创建A对象时依赖B对象,此时,在Spring中就会先创建B对象,然后再创建A对象。

@RequestHeader  
@RequiredArgsConstructor
@Inner 
@AllArgsConstructor(注意和@RequiredArgsConstructor 区别)
@RequestPart("file") MultipartFile file
@PreAuthorize("@pms.hasPermission('sys_post_get')")
@SneakyThrows
@EnableConfigurationProperties(DataSourceProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)

lombok注解

@UtilityClass: 作用于类,将类标记为 final,并且类、内部类中的方法、字段都标记为 static

先执行继承的接口里的方法还是继承的父类中的方法

public class MyImplement2 extends MyImplement implements Interface2 此时在实现类MyImplement2中调用helloWorld()方法,到底执行的是MyImplement中的方法还是执行Interface2中的方法?
答:因为类优先于接口,所以将会执行MyImplement中的方法。

发生这种情况的原因是,实现类MyImplement即实现了接口Interface1又实现了接口Interface2,恰巧两个接口中都定义可相同的默认方法。说白了就是编译器此时已经被干懵了,当我们在MyImplement类中调用方法时,它不知道该去调用Interface1的默认方法还是去调用Interface2的方法。解决方法就是在实现类中实现该方法。

子类可以访问protect的父类但是不一定能访问 default修饰的父类

@profiles.active@的作用

当在多配置文件中,需要切换配置文件时,通常的做法都是修改激活的文件名称,而spring.profiles.active=@profiles.active@ 是配合 maven profile进行选择不同配置文件进行启动,可以避免修改文件,而在maven打包是指定使用哪个配置文件。

切面

切点表达式类型

Spring AOP 支持以下几种切点表达式类型。

execution
匹配方法切入点。根据表达式描述匹配方法,是最通用的表达式类型,可以匹配方法、类、包。

表达式模式:

execution(modifier? ret-type declaring-type?name-pattern(param-pattern) throws-pattern?)

表达式解释:

  • modifier:匹配修饰符,public, private 等,省略时匹配任意修饰符

  • ret-type:匹配返回类型,使用*匹配任意类型

  • declaring-type:匹配目标类,省略时匹配任意类型

    • ..匹配包及其子包的所有类
  • name-pattern:匹配方法名称,使用 * 表示通配符

    • *匹配任意方法
    • set* 匹配名称以 set 开头的方法
  • param-pattern:匹配参数类型和数量

    • () 匹配没有参数的方法
    • (..) 匹配有任意数量参数的方法
    • (*) 匹配有一个任意类型参数的方法
    • (*,String) 匹配有两个参数的方法,并且第一个为任意类型,第二个为 String 类型
  • throws-pattern:匹配抛出异常类型,省略时匹配任意类型

使用示例:

// 匹配public方法
execution(public * *(..))

// 匹配名称以set开头的方法
execution(* set*(..))

// 匹配AccountService接口或类的方法
execution(* com.xyz.service.AccountService.*(..))

// 匹配service包及其子包的类或接口
execution(* com.xyz.service..*(..))

aop注解的五种advice之环绕通知

   @Around("(@within(op) || @annotation(op)) && !execution(* Object.*(..))")
   public Object around(ProceedingJoinPoint point, BatchFileOperation op) throws Throwable {
       BizRelatedFileFxService sv = beanFactory.getBean(BizRelatedFileFxService.class);
       sv.beforeBatchInvocation();
       Object r = point.proceed();
       sv.afterBatchInvocation();
       return r;
   }
   // 这里的op就是该BatchFileOperation自定义注解所生效的地方的的类名

@within和@annotation的区别

  • @within 对象级别
  • @annotation 方法级别
@Around("@within(org.springframework.web.bind.annotation.RestController") 
        这个用于拦截标注在类上面的@RestController注解

@Around("@annotation(org.springframework.web.bind.annotation.RestController") 
         这个用于拦截标注在方法上面的@RestController注解

自定义注解

请添加图片描述

注意@Target里的ElementType.Type 是表示注解

AopContext.currentProxy()

原来在springAOP的用法中,只有代理的类才会被切入,我们在controller层调用service的方法的时候,是可以被切入的,但是如果我们在service层 A方法中,调用B方法,切点切的是B方法,那么这时候是不会切入的,解决办法就是如上所示,在A方法中使用((Service)AopContext.currentProxy()).B() 来调用B方法,这样一来,就能切入了!

java 什么时候必须使用this

  1. 局部变量和实例变量同名时,使用this关键字来区分它们。
	@Data
	public class EntityMerger<E, D> {
	
	    private List<D> dto;
	
	    /**
	     * 执行合并.
	     */
	    public void merge() {
	        // dto map
	        val dto = new ArrayList<>(this.dto);
	    }
	}

	public class Person {
	    String name;
	    
	    public void setName(String name) {
	        this.name = name; // 使用this关键字指向实例变量name
	    }
	}

  1. 在构造方法中,当需要调用本类中另一个构造方法时,可以使用this关键字
    public Person() {
        this("张三", 20); // 调用本类中带参数的构造方法
    }
  1. 当一个方法的形参与实例变量同名时,可以使用this关键字来区分它们。
	public class Person {
	    String name;
	    int age;
	    
	    public void setPerson(String name, int age) {
	        this.name = name;
	        this.age = age;
	    }
	}

lamdaquery的使用

1. new LambdaQueryWrapper<InfoApply>
2. Wrappers.lambdaQuery(ReviewTask.class)
3. Wrappers.<SysMenu>query().lambda()
4. Wrappers.<SysRoleMenu>lambdaQuery()
5. QueryWrapper<JoinItemOffice> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda()

sleep 和 yeild

sleep 不会释放锁, yeild 不释放锁,且只能使同等优先级的线程有执行的机会。 wait是object的方法,会释放锁

【因此,我们可以发现,wait和notify方法均可释放对象的锁,但wait同时释放CPU控制权,即它后面的代码停止执行,线程进入阻塞状态,而notify方法不立刻释放CPU控制权,而是在相应的synchronized(){}语句块执行结束,再自动释放锁。】

释放锁后,JVM会在等待resoure的线程中选取一线程,赋予其对象锁,唤醒线程,继续执行。这样就提供了在线程间同步、唤醒的操作。Thread.sleep()与Object.wait()二者都可以暂停当前线程,释放CPU控制权,主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制,而在同步块中的Thread.sleep()方法并不释放锁,仅释放CPU控制权。

外键的作用

外键的作用是保证数据的一致性和完整性,避免数据冗余

从表插入新行,其外键值不是主表的主键值便阻止插入。
从表修改外键值,新值不是主表的主键值便阻止修改。
主表删除行,其主键值在从表里存在便阻止删除(要想删除,必须先删除从表的相关行)。
主表修改主键值,旧值在从表里存在便阻止修改(要想修改,必须先删除从表的相关行)

Serializable接口

现在需要将一个对象返回给前端,一般就需要实现 Serializable接口

匿名内部类

匿名内部类只是局部内部类的一种简化形式,本质是一个对象,是实现了该接口或者继承了该抽象类的一个子类
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

泛型方法

在一个方法签名中的返回值前面声明了一个 < T > 时,该方法就被声明为一个泛型方法

使用bean的方式

componentScan ;spring.factories; @autowried @resource ;@RequiredArgsConstructor

@RequiredArgsConstructor 下所有成员变量必须使用final修饰

获取系统路径下资源

ReadTest.class.getResource(“/”) === ReadTest.class.getClassLoader().getSystemResource(“”)

github上 jar包 doc包 和 sources包区别

jar 就是一堆.class文件, 源代码编译出来的包, 可以直接运行的
java doc就是这个源文件中的doc 文档注释, 包括字段, 方法等注释
sources 就是源代码包

为什么nginx 的conf.js不可以用文本编辑器编辑保存?

conf文件被记事本编辑过,保存成了含BOM头的文件
使用其他编辑器将文件另存为UTF-8不含Bom头的格式
注:记事本编辑UTF-8都会加BOM头

Java中模板引擎

Java中模板引擎有许多,模板引擎作为动态网页发展进步的产物,在最初并且流传度最广的jsp就是一个模板引擎,但由于jsp的缺点比较多,所以很多人弃用jsp选用第三方的模板引擎,市面上开源的第三方的模板引擎也比较多,有Thymeleaf、FreeMaker、Velocity等模板引擎受众较广。

使用 Thymeleaf 优点:

动静分离:Thymeleaf 使用 html 通过一些特定标签语法代表其含义,而且并未破坏html结构,即使无网络、不通过后端渲染也能在浏览器成功打开,大大方便了界面的测试和修改;

@MapKey 是MyBatis框架的注解,作用是将List结果集转换成key-value形式的Map结果集,方便快速从结果集中查询指定结果。

resultType="com.ace.admin.api.vo.UserVO"  如果执行sql返回的数据为单行,那么结果集会被封装成一个map,resultType的值为map里value的类型
resultType="com.ace.admin.api.vo.UserVO"  如果执行sql返回的数据为多行,那么结果集会被封装成一个List resultType的值为list集合里map里的value的类型
resultType="com.ace.admin.api.vo.UserVO"resultType="map"的区别?
类似mysql中左连接、右连接,resultType值为一个对象的话,会拿mybatis返回的结果集和这个对象做映射,如果对象的某个属性未匹配到结果集中的值,则赋值为null或者基本类型的默认值;同理resultType为"map"的话,mybatis返回的结果集里的每一列(非空值)会保留。且所对应的2mapper接口的返回类型应该为Map<String,Object/Satring/Interger>而不应该是Map<String,UserVO>这种具体的类型

在mysql中IFNULL() 函数用于判断第一个表达式是否为 NULL,如果第一个值不为NULL就执行第一个值。第一个值为 NULL 则返回第二个参数的值。

一般来说spring boot默认的扫描路径是扫描启动类同级及其子级包下的所有文件

springboot配置文件的加载顺序

在这里插入图片描述

spring 中提供了这三种 方式, 针对 某一个特定 的bean 被初始化后的一些操作,

还提供了 在 整个spring容器初始化后执行一些操作,包括: 实现监听容器刷新完成扩展点 ApplicationListener、实现 CommandLineRunner接口、实现ApplicationRunner接口(这两个 Runner接口是springboot 中提供的)。

整个容器初始化完成后执行特定bean初始化后执行提供者
@PostConstructspring
@Bean(initMethod=“”)spring
nitializingBean接口spring
pplicationEventApplicationListener 接口spring
ommandLineRunner接口springboot
pplicationRunner接口springboot

日期操作

LocalDateTime.of(java.time.LocalDate.of(LocalDateTime.now().getYear(),12,31),LocalTime.MAX);
DateUtil.endOfYear(new Date());

2342

yml配置文件中${key:defaultValue}这种写法表示这个表达式的取值规则是先通过key在yml文件中或启动命令中获取value,如果能获取到就用,不能获取到就使用冒号右边的defaultValue,如下图所示

在这里插入图片描述

从启动命令中传入参数举例:
采用java -jar的方式启动jar时

 java -jar -DMYSQL_HOST=192.168.1.20 -DMYSQL_PORT=3306 -DMYSQL_USERNAME=root -DMYSQL_PASSWORD=root xxx.jar

其中参数名前-D是设置系统属性值;

map.merger

Map<Integer, String> map = new HashMap<>();
map.put(1, "str1");
map.put(2, "str2");
map.merge(3, "My", String::concat);
System.out.println(map);  //result: {1=str1, 2=str2, 3=My}
Map<Integer, String> map = new HashMap<>();
map.put(1, "str1");
map.put(2, "str2");
map.merge(1, "mmm", String::concat);
System.out.println(map);  //result: {1=str1mmm, 2=str2}
        Map<String, Map<String, Integer>> statisticsMap = new HashMap<>();
        statisticsMap.computeIfAbsent(subAreaCode, k -> new HashMap<>()).merge(dayFormat, 1, Integer::sum);
        这里merge拿到的是statisticsMap 的value的这个map

@Builder构造器模式时的默认值

基本类型int boolean这些会赋值相应类型,引用类型会赋值为null

常用语法

Optional.ofNullable 
Optional.ofNullable(chaFeeItemsRecordDTO).map(ChaFeeItemsRecordDTO::getDerateCategory).ifPresent(item::setReductionItem)
如果chaFeeItemsRecordDTO 拿到derateCategory 属性值,调用setReductionItem 方法
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值