备战秋招Java知识点回顾

Java基础知识点复习回顾

对象存放在堆内存中。

方法存放在栈内存中。

静态的方法,常量都存放在堆的永久区。变量存储在栈内存中。

什么是字节码:Java源代码经过jvm编译器编译后产生的文件(即扩展为.class的文件),它不面向任何特定的处理器,只面向虚拟机。

1、😘面向对象和面向过程的区别

  • 从语言上分析:

    • 对于C语言来说,是完全面向过程的。
    • 对于C++来说,是一半面向过程,一半面向对象。(C++是半面向对象的)
    • 对于Java来说,是完全面向对象的
  • 面向过程的开发方式主要特点:

    • 注意步骤,注重的是实现这个功能的步骤
    • 告诉面试官面向过程更加注重事情的每一个步骤以及顺序,面向对象更加注重事情有哪些参与者,以及这些参与者需要做什么。
  • 面向过程的缺点:

    • 面向过程最主要是每一步与每一步的因果关系,其中A步骤因果关系到B步骤,A和B联合起来形成一个子模块,子模块和子模块之间又因为因果关系结合在一起,假如其中一个因果关系出现了问题(错误),此时整个的系统的运转都会出现问题。(代码的耦合度太高,扩展能力差)
  • 面向过程的优点:

    • 对于小型项目,采用面向过程的方式进行开发,效率较高。不需要前期进行对象的提取,模型的建立,采用面向过程方式可以直接开始干活。一上来就可以直接写代码,编写因果关系,从而实现功能。

    • 采用面向对象的方式进行开发:耦合度低,扩展力强

  • 找一个合适的案例,说明一下面向对象和面向过程的区别?

    比如洗衣机洗衣服,面向过程就会把洗衣服拆解成一系列的步骤,打开洗衣机,放衣服,放洗衣粉,清洗,烘干。

    而面向对象会把洗衣服拆分出人和洗衣机两个对象。人只负责打开洗衣机,放衣服,放洗衣粉。洗衣机负责清洗,烘干。

  • 当我们采用面向对象的方式贯穿整个系统的话,涉及到三个术语:

    • OOA:面向程序分析
    • OOD:面向程序设计
    • OOP:面向程序编程

2、😘面向对象的三大特征:封装、继承、多态

  • 什么是类?
    • 类实际上在现实世界是不存在的,是一个抽象的概念。是一个模板。是我们人类大脑进行”总结、思考、抽象“的一个结果。
    • 比如:明星是一个类
  • 什么是对象?
    • 对象是真是存在的个体
    • 比如:刘德华是一个对象、郭富城是 一个对象
  • 什么是构造方法,有什么用?
    • 构造方法是一个比较特殊的方法,通过构造方法可以完成对象的创建,以及实例变量的初始化。换句话说:构造方法是用来创建对象的,并且同时给对象的属性赋值。(注意:实例变量没有手动赋值的时候,系统会赋默认值)
    • 当一个类没有提供任何的构造方法,但是系统会默认提供一个无参数的构造方法。(叫做缺省构造器)
    • 使用new运算符类调用构造方法,构造方法名和类名必须一致。
    • 构造方法不需要指定返回值类型,也不能写void
    • 构造方法支持方法重载的
  • 什么是封装?
    • 告诉面试官,封装就是把内部的一些东西不给透明化,只需要让调用者知道调用这个方法能做什么,而这个方法里面的细节不需要让调用者知道内部怎样实现的。
  • 封装的两个作用:
    • 保证内部结构的安全
  • 什么是继承?有什么用?
    • 继承:继承基类的方法,并做出自己的改变或者扩展。(构造方法不能被继承)
    • 继承的基本作用:子类继承父类,代码可以得到复用。(这个不是重要的作用,是基本作用)
    • 主要(重要作用):因为有了继承关系,才有了后期的方法覆盖和多态机制。
  • 什么是多态
    • 多种形态,多种状态,编译和运行有两个不同的状态,在有继承或者实现关系的前提下,创建出来基类的对象,调用刚创建出来的对象的方法,实际执行的不是基类的方法,而是子类的方法。
    • 提高了代码的扩展性

3、😘static、this

static翻译为:静态

  • 所有static修饰的,都是采用”类名 . “的方式访问
  • static修饰的变量:静态变量
  • static修饰的方法:静态方法

变量的分类:

  • 变量根据声明的位置进行划分:
    • 在方法体中声明的变量叫做:局部变量
    • 在方法体外声明的变量叫做:成员变量(实例变量。)。成员变量分为静态****实例变量(static修饰的实例变量)

static静态代码块在什么时候执行?

  • 类加载的时候执行,并且只执行一次
  • 静态代码块有这样的特征/特点
  • 注意:**静态代码块在类加载的时候执行,**并且在main方法执行之前执行

this是一个关键字,是一个引用,保存内存地址指向自身

  • this可以使用在实例方法中,也可以使用在构造方法中
  • this出现在实例方法中其实代表的是当前对象
  • this不能使用在静态方法中
  • this()这种语法只能出现在构造方法第一行,表示当前构造方法调用本类的构造方法,目的是代码复用

4、😘修饰符

  • public表示公开的,任何地方都可以访问。
  • private表示私有的,除了类本身和类内部的方法,外界无法访问该元素。
  • protected表示受保护的,可以被同一个包下的其他类或者子类访问。
  • default表示默认的,被这个修饰的,可以被同一个包中的其他类访问。

5、😘方法重载overload

  • 发生在同一个类中,方法名必须相同,参数列表不同,参数个数不同,方法的返回值和方法的修饰符可以相同也可以不相同。

6、😘方法重写/覆盖/Override

两个类必须要有继承关系,发生在父子类中,方法名,参数列表相同,方法的返回值等于父类,抛出的异常要小于父类。方法的修饰符要大于父类。

7、😘final关键字、常量

  • final修饰的方法无法被覆盖,被重写
  • final修饰的类无法被继承
  • final修饰的变量一旦被赋值不能被修改, (final修饰的变量只能被赋一次值),finally修饰的实例变量系统不会给赋默认值必须要求手动赋值。
  • final修饰的引用只能指向1个对象,并且它只能永远指向该对象,无法在指向其他对象。
  • 常量和静态变量,都是存储在方法区,并且都是在类加载时初始化

8、😘抽象类接口的区别:

  • 接口是多实现,抽象类是单继承。

  • 在jdk1.8之前抽象类和接口都没有方法体。jdk1.8之后接口中里面可以有默认方法(default),静态方法(static),1.9之后私有方法,这三个方法在接口中可以有方法体。其中静态方法只能通过接口名调用,不能通过实现类名或者对象名调用。

  • 实现类 实现了 两个接口,且这两个接口都有静态方法method,那么使用 实现类对象.method 调方法就不知道 调的是哪个方法,所以接口中的静态方法只能用 接口名.方法名 调用

  • 抽象类中可以有普通方法和构造方法,属性可以被各种修饰符修饰。

    而接口中不能有普通方法和构造方法。属性只能被定义为常量。默认被public static finally修饰。

  • 其中接口中的默认方法,静态方法,私有方法不是抽象方法,所以不强制被重写,但是只有默认方法可以被重写,重写的时候去掉default关 键字。

9、😘"=="和"equals"的区别HashCode

  • "=="比较的是两个对象的内存地址是否相等。要是比较的是数值类型的话,就是比较数值的大小。要是比较的是引用类型的话,就是比较对象的内存地址。

  • equals要是没有重写,就是和"=="一样的效果。

  • String类默认重写了equals方法和toString方法。

  • equals和HashCode

    为什么重写了equals方法还要重写hashcode方法?

    因为在java中,要求一个对象相等,要求它的hashcode是相等的。而hashcode相同的两个对象不一定是代表这两个对象相等。如果没有重写hashcode方法会出现equals比较相等,而hashcode不相等,所以要重写hashcode方法。

    (对象的hashcode是根据对象的内存地址来hash算的,所以会出现不同的对象hashcode相等。)。

10、😘StringBuffer和StringBuilder

String是被finally修饰的,是不可变的,每次操作都会产生一个新的String对象。也被synchronized修饰,是线程安全的。

StringBuffer中的方法都有被synchronized修饰,是线程安全的,效率慢

  • StringBuffer底层实际上是一个byte[]数组,初始化容量为16的数组

StringBuilder的方法都没有被synchronized修饰,是线程不安全的

  • 操作少量数据使用String

  • 单线程操作字符串缓冲区下操作大量数据推荐使用StringBuilder

  • 多线程操作字符串缓冲区下,操作大量数据推荐使用StringBuffer

11、String类

  • java.lang.String
    • String表示字符串类型,属于引用数据类型,不属于基本数据类型
    • 在Java中随便使用双引号括起来都是String对象,例如:“abcd”,“def”,这是两个String对象
    • java中规定,双引号括起来的字符串是不可变的,也就是说"abcd"从出生到死亡都是不变的,不能变成其他的字符串
      • 如果是字符串拼接的话,会生成新的字符串对象,而原先的还是不变的
    • 在JDK当中双引号括起来的字符串,都是直接存储在"方法区"的"字符串常量池"中的。因为字符串使用频率高,为了执行效率
    • new的String对象:String s1 = new String(“ab”); 一定在堆内存当中开辟空间
    • String s1=”hello“,String s2=”hello“,s1对象是与s2对象相等。无论是使用==还是equals比较都是相等的
    • String s1=new String(“hello”),String s2=new String(“hello”),这里s1对象与s2对象是不相等的。使用==返回false,使用equals返回true。(String类型创建对象要注意,new String和直接String是不一样。new String是每次都创建新的对象,直接String传的字符串之前创建过,那么则不会重新创建新对象,直接使用之前的创建过的。)。
    • equals比较的是对象中储存的内容是否相等,==比较的是数值的话则比较值是否相等,比较的是对象的话则比较的是对象的内存地址是否相等(也就是是否是同一个对象。)。只要你是重新new了对象(即使new对象时传递的参数一样)那么使用==比较就返回false。
    • 看下面有几个对象?
      • String s1 = new String(“hello”); String s2 = new String(“hello”);
      • 一共是3个对象:方法区字符串常量中有1个:“hello”
      • 堆内存中有两个String对象。
    • 关于String类中的构造方法:
      • String s = new String(" ");
      • String s = " "; //最常用
      • String s = new String(char数组);
      • String s = new String(char数组,起始下标,长度);
      • String s = new String(byte数组);
      • String s = new String(byte数组,起始下标,长度);
    • char charAt(int index); 返回指定索引的char值
    • int compareTo(String anotherString); 按照字典字母顺序比较字符串的大小。前后一致返回0;前小后大返回-1;前大后小返回1
    • boolean contains(CharSequence s); 判断前面的字符串中是否包含后面的子字符串。包含返回true,不包含返回false
    • boolean endsWith(String suffix); 判断当前字符串是否以某个字符串结尾。
    • boolean equalsIgnoreCase(String anotherString); 判断两个字符串是否相等,并且忽略大小写
    • byte[] getBytes(); 将字符串转换成(字节)byte数组
    • int indexOf(String str); 判断某个子字符串在当前字符串中第一次出现出的索引
    • boolean isEmpty(); 判断某个字符串是否为空字符串
    • int lastIndexOf(String str); 判断某个字符串在当前字符串中最后出现的索引
    • String replace(CharSequence target,CharSequence replacement); 后面参数的字符串替换前面参数的字符串
    • String[] split(String regex); 把字符串按照参数字符串来拆分
    • boolean startsWith(String prefix); 判断某个字符串是否以某个字符串开始
    • String substring(int beginIndex); 截取字符串,参数是起始下标
    • String substring(int beginIndex,int endIndex); beginIndex-起始索引包括,endIndex–结束索引不包括
    • char[] toCharArray(); 把字符串转换成char数组
    • String toLowerCase(); 转换成小写
    • String toUpperCase(); 转换成大写
    • String trim(); 去除字符串前后空白;不会去除中间的空白
    • String中只有一个方法是静态的,不需要new对象; valueOf(); 将非字符串转换成字符串

12、包装类

  • 在jdk1.5之后出现的。可以把基本数据类型自动转换成引用数据类型。叫装箱拆箱

  • 基本数据类型—转换为—>引用数据类型 叫做装箱

  • 引用数据类型—转换为—>基本数据类型 叫做拆箱

  • 因为老版本的jdk在方法参数需要传基本数据类型的时候是不可以的,需要把引用数据类型手动转换成引用数据类型使用。随之就诞生了包装类实现自动装箱和自动拆箱

  • Java中为了提高程序的执行效率,将[-128到127]之间所有的包装对象提前创建好,放到了一个方法区的"整数型常量池"当中了,目的是只要用这个区间的数据不需要在new了,直接从整数型常量池当中取出来。

  • 自动装箱拆箱

    • 运算符(±*/运算)可以自动装箱拆箱,==会比较内存地址,不会触发自动装箱拆箱
    • java中为了提高程序的执行效率,讲[-127到128]之间所有的包装对象提前创建好,放到了一个方法区的"整数常量池"当中,目的是只要用这个区间的数据不需要在new,直接从整数型常量池当中取出来
    Interget a = 128;
    Interget b = 128;
    System.out.println(a==b);//false
    
    //原理:x变量中保存的对象的内存地址和y变量中保存的对象的内存地址是一样的
    Interget x = 128;
    Interget y = 128;
    System.out.println(a==b);//true
    

13、对日期的处理

  • Date date = new Date(); 获取系统当前时间
  • SimpleDateFormat sdf = new SimpleDateFormat (“yyyy-MM-dd”); {yyyy年,MM月,dd日,HH时,mm分,ss秒,SSS毫秒} 填入有参构造。 可以对日期进行格式化
  • sdf 调用Format()方法,把获取的Date()时间传进去就可以输出日期的格式化形式
  • 如何将字符串转换成日期类型:
    • SimpleDateFormat sdf = new SimpleDateFormat (“这里的格式要和给的字符串理的格式相同,不能随便写”);
    • 然后调用sdf的parse()方法,把字符串转换成日期类型
  • System.currentTimeMillis();获取自1970年1月1日 00:00:00 000到当前系统时间的总毫秒数。(通常用来记录时间差的,比如一个方法执行的时间多长等)

14、😘异常处理

**java中所有的异常都来自于顶级父类Throwable类,Throwable类下面有两个子类,一个是Exception(异常类),一个是Error(错误类),其中error是程序无法处理的错误,发生在jvm虚拟机中,一旦出现,程序被迫停止。exception是可以处理的异常,不会导致程序停止。分为运行时异常和编译时异常。**常见的编译时异常:数据库操作异常,文件找不到异常等。运行时异常:数组越界异常,空指针异常。

异常处理的方式有try…catch…finally和throws,第一种是捕获异常,并且进行处理,第二种是向上抛出异常,交给调用者来处理。

所有try…catch与throws最大的不同是,throw里面要是出错了就不会往下执行了,而try….catch里面即使出错了也会往下执行。

try…with…resource。但是这些关闭的资源要实现AutoCloseable接口。

  • throws和throw
    • throws用在方法声明后面,跟的是异常类名,throw用在方法体内,跟的是异常对象名。

      throws可以跟多个异常类名,throw只能抛出一个异常对象名。

      throws表示抛出异常,由该方法的调用者来处理,throw表示抛出异常,由方法体内的语句处理。

😘15、对象拷贝

  • 有三种对象拷贝的方法,第一种是实现Cloneable接口并重写Object类中的clone()方法,第二种实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。
  • 克隆分为浅克隆和深克隆。使用浅克隆,那么被克隆的对象A克隆给对象B,你把A对象中的引用改变,B中也会随之改变。但是把A对象中的基本数据类型改变,B中不会改变。
  • 对当前对象进行克隆,并克隆该对象所包含的8种基本数据类型和String类型属性(拷贝一份该对象并重新分配内存,即产生了新的对象);但如果被克隆的对象中包含除8中数据类型和String类型外的其他类型的属性,浅克隆并不会克隆这些属性(即不会为这些属性分配内存,而是引用原来对象中的属性)
  • 总结:浅克隆只复制基本类型的数据,引用类型的数据只复制了引用的地址,引用的对象并没有复制,在新的对象中修改引用类型的数据会影响原对象中的引用。

浅克隆(被克隆的类实现Cloneable接口,并重写里面的clone。)。

  • Employee employee1 = (Employee) employee.clone(); //克隆employee对象,赋值给employee1对象。

深克隆(被克隆的类实现Cloneable接口并且重写clone方法,并且该类中的引用对象属性也要实现Cloneable接口,并且重写clone方法)

  • Employee employee1 = (Employee) employee.clone(); //克隆employee对象,赋值给employee1对象。

16、😘集合

增强for循环:

​ for(元素类型 变量名 :数组或集合) { }

增强for缺点:没有下标。在需要使用下标的循环中,不建议使用增强for循环

foreach不能对数组或集合进行修改,如果想要修改就要使用for循环。

for循环可以使用break跳出循环,但forEach不能跳出循环;

–Iterable–最大父接口

  • 可迭代的,可遍历的,所有集合元素都是可迭代可遍历的
-----Java中集合都是实现Iterable接口,分为两大类:
  • 一类是单个方式存储元素:

    • 单个方式存储元素,这一类集合中超级父接口:java.util.Collention;
  • 一类是以键值对的方式存储元素:

  • 以键值对的方式存储元素,这一类集合中超级父接口:java.util.Map;

  • 集合不能直接存储基本数据类型。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hrohy8kI-1668857221932)(C:\Users\陈航\AppData\Roaming\Typora\typora-user-images\image-20221113204231897.png)]

–Collection–第二父接口
–List–第三接口(ArrayList,Linkedlist,Vector)
  • List集合存储元素特点:有序可重复
    • 有序:list集合中的元素有下标
    • 允许存放多个Null元素对象
  • 因为有下标,可以根据下标遍历
–Arraylist–实现类
  • Arraylist底层采用动态数组,连续内存存储,适合于下标访问。
  • Arraylist集合是非线程安全的
  • 首次扩容为10,再次扩容时为上次的扩容的时候是1.5倍
    • 底层是右移一位(比如此时长度为15,执行15>>1得到7,用7+15=12)
–Linkedlist–实现类
  • Linkedlist底层是采用双向链表,可以存储在分布的内存当中。适合于数据插入和删除。

  • Linkedlist底层是**双向链表。**无需连续内存。(占用内存高)

  • Linkedlist集合照样有下标,但是检索/查找某个元素的时候效率较低,因为只能从头节点开始一个一个遍历

–Vector–实现类
  • 底层是一个数组,初始化容量是10
  • 扩容之后是原容量的2倍
  • Vector中所有的方法是线程同步的,都带有synchronized关键字。是线程安全的。效率比较低,使用较少
–Set–第三接口(Hashset,Treeset,Sortset,LinkedHashset)
  • Set集合存储元素特点:
    • 无序不可重复**,最多只能允许存放一个null对象。**
    • Set集合中元素没有下标
–HashSet实现类–
  • 无序不可重复
  • 存储时顺序和取出的顺序不同
  • 实际上HashSet集合在new的时候,底层实际上new了一个HashMap集合
  • 放到HashSet集合中的元素实际上是放到HashMap集合的key部分了。
–Set接口下的SortedSet(自动排序集合)接口下面的实现类TreeSet–
  • TreeSet集合底层实际上是一个TreeMap**,而TreeMap底层是一个二叉树。**
  • 放到TreeSet集合中的元素,等同于放到TreeMap的key中了
  • 无序不可重复,但是存储的元素可以自动按照大小顺序排序
  • 称为:可排序集合。
  • 这里的无序指的是存进去的顺序和取出来的顺序不同。并且没有下标
  • TreeSet放进去拿出来就是有顺序的
  • TreeSet/TreeMap是自平衡二叉树,遵循左小右大原则存放
  • TreeSet集合/TreeMap集合采用的是:中序遍历方式。
  • Iterator迭代器采用的是中序遍历方式
  • 放到TreeSet或者TreeMap集合key部分的元素要想做到排序,包括两种方式
    1. 放在集合中的元素实现java.lang.Comparable接口
    2. 在构造TreeSet或者TreeMap集合的时候给他传一个比较器对象

–Map接口–

  • Map和Collection没有继承关系。
  • Map集合以key和value的方式存储数据:键值对
    • key和value都是引用数据类型。
    • key和value都是存储对象的内存地址。
    • 所有Map集合的key特点:无序不可重复的
–HashMap实现类–(非线程安全)
  • key和value允许为空。

  • key不可重复,value可重复

  • HashMap集合底层是哈希表/散列表的数据结构。

    • 相当于是一个Node数组,数组里面是一个单向链表
  • jdk1.7 和1.8,底层的数据结构有什么不同

    • 1.7是数组+链表。 1.8是数组+(链表|红黑树)。(每一个数组元素位置都存放着一个链表。)。

      存储数据的原理:首先计算存入数据的key的哈希值,以及与运算,把得到的值对应到数组的下标。如果下标位置没有元素,则直接创建Node对象存入数组,下标对应的位置上有元素,判断这个元素是红黑树链表,若为红黑树则将key,value封装成一个红黑树node节点添加到红黑树中。若为链表,则将key和value封装成一个链表node节点,此时会拿着k和链表上每一个节点的k进行equals比较。如果其中有一个equals返回了true,那么这个节点的value就会被覆盖。如将所有的equals方法返回值都是false,那么这个新的节点将被添加到链表的末尾,,只有当链表的长度达到8,数组的长度达到64的时候,这时候链表就会变为红黑树,当数组长度减到为8时,红黑树又会变为链表。

  • map.get(k)实现原理:

    • 先调用k的hashCode()方法得出哈希值,通过哈希算法转换成数组下标,通过数组下标快速定位到某个位置上,如果这个位置上什么也没有,返回null。如果这个位置上有单向链表,那么会拿着参数k和单向链表上的每个节点中的k进行equals,如果所有equals方法返回false,那么个体方法返回null,只要其中有一个节点的k和参数k  equals的时候返回true,那么此时这个节点的vaule就是我们要找的value,get方法最终返回这个要找的value,
      
  • 哈希表是一个怎样的数据结构呢?

    • 哈希表是一个数组和单向链表的结合体
      • 数组:在查询方面效率很高,随机增删方面效率很低
      • 单项链表:在随机增删方面效率较高,在查询方面效率很低。
    • 哈希表将以上的两种数据结构融合在一起,充分发挥他们各自的优点
    • 像是门帘。底层有一个Node<K,V>[ ] table一维数组,
      • 在静态内部类HashMap.Node中:
      • 有hash(哈希值是key的hashCode()方法的执行结果。hash值是通过哈希函数/算法生成的),key(存到map集合的key),value(存到map集合的value),next(下一个节点的内存地址)
  • HashMap集合的key部分特点

    • 不可重复是怎么保证的? equals方法来保证HashMap集合的key不可重复,如果key重复了,value会覆盖
  • HashMap集合的默认初始化容量是16,默认加载因子是0.75f

    • 这个默认加载因子是当HashMap集合底层的容量达到75%的时候,数组开始扩容
    • 在空间占用与查询之间取得较好的权衡
    • 大于这个值,空间节省了,但链表就会比较长影响性能
    • 小于这个值,冲突减少了,但扩容就会更频繁,空间占用的多
  • HashMap集合初始化容量必须是2的倍数,这也是官方推荐的。这是因为达到散列均匀,为了提高HashMap集合的存取效率所必须的

  • HashMap集合的key为null

–HashTable实现类–(线程安全的。)。
  • HashTable的key和value不能为null

  • HashTable方法都带有synchronized:线程安全的。使用较少了。只有一把锁,同一时刻只能有一个线程操作它,效率很低,大部分都是使用ConcurrentHashMap。是一个高效的HashMap(Hashtable与ConcurrentHashMap都是线程安全的Map集合。)。

    - Hashtable与ConcurrentHashMap都是线程安全的Map集合 (看课润的hashmap)

    - Hashtable并发度低,整个Hashtable对应一把锁,同一时刻,只能有一个线程操作它

  • 是哈希表的数据结构(数组加链表)

  • 初始化容量是11,默认加载因子是:0.75f

  • HashTable的扩容是:原容量 * 2 + 1

    Hashtable和ConcurrentHashMap对比

    Hashtable与ConcurrentHashMap都是线程安全的Map集合。

    • 而hashTabel慢是由于其实现采用了同步锁机制,在高并发环境下使用将会导致多个线程争抢一把锁,即是出现锁竞争激烈的情况,使得效率低下,所以concurrentHashMap采用的是将其内部使用段(segment)概念,每个每段都可以视为是一个hashTable,相当于有16把不同的锁,当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效的提高并发访问效率

    • 1.8之前ConcurrentHashMap使用了Segment+数组+链表的结构,每个Segment对应一把锁,如果多个线程访问不同的Segment,则不会冲突

    • 1.8开始ConcurrentHashMap将数组的每个头节点作为锁,如果多个线程访问的头节点不同,则不会冲突

–Properties类继承hashtable–
  • Properties是一个map集合,继承HashTable,Properties的key和value都是String类型
  • Properties被称为属性类对象
  • Properties是线程安全的
  • 底层是哈希表
–SortedMap接口–
  • SortedMap集合的key存储元素的特点:
    • 无序不可重复,另外放在SortedMap集合key部分的元素会自动按照大小顺序排序,称为可排序集合
–TreeMap实现类–
  • 底层的数据结构是一个二叉树
  • key部分是无序的,value部分会按照大小顺序排序的
  1. ConcurrentHashMap初始容量16,默认加载因子是0.75f
    1. 1.7是Segment数组+数组+链表,超过原容量四分之三扩容
    2. 1.8之后和HashMap一样,达到四分之三扩容

17、Java8新增Stream操作集合

​ --Stream提供了大量的方法进行聚集操作,这些方法即可以是"中间的"(intermediate),也可以是”末端的“(terminal)–

  • **中间方法:**中间操作允许流保持打开状态,并允许直接调用后续方法。map()方法就是中间方法。中间方法的返回值是另外一个流
  • **末端方法:**末端方法是对流的一个最终操作。当对某个Stream执行末端方法后,该流会被"消耗"掉且不在可用。sum()、count()、average()等方法都是末端方法
  • 短路方法:这种方法会给流增加一些新的属性,比如元素的唯一性、元素的最大数量、保证元素以排序的方式被处理掉。有状态的方法往往需要更大的性能开销
  • 短路方法:短路方法可以尽早结束对流的操作,不必检查所有的元素
–Stream常用的中间方法–
  • filter:过滤Stream中所有不符合predicate的元素
  • mapToXxx:使用ToXxxFunction对流中的元素执行一对一的转换,该方法返回的新流中包含了ToXxxFunction转换生成的所有元素
  • peek:依次对每个元素执行一些操作,该方法返回的流与原有流包含相同的元素。该方法主要用于调试
  • distinct:该方法用于排序流中所有重复的元素(判断元素重复的标准是使用equals()比较返回true)。这是一个有状态的方法
  • sorted:该方法用于保证流中的元素在后续的访问中处于有序状态。这是一个有状态的方法
  • limit:该方法用于保证对该流的后续访问中最大允许访问的元素个数。这是一个有状态的、锻炼方法
–Stream常用的末端方法–
  • forEach(Consumer action):遍历流中所有的集合,对每个元素执行action
  • toArray:讲流中所有元素转换成一个数组
  • reduce:该方法有三个重载版本。都用于通过某种操作来合并流种的元素
  • min:返回流所有元素的最小值
  • max:返回流所有元素的最大值
  • count:返回流种所有元素的数量
  • anyMatch:判断流中是否至少包含一个元素符合Predicate条件
  • allMatch:判断流中是否每个元素都符合Predicate条件
  • noneMatch:判断流中是否所有元素都不符合Predicate条件
  • findFirst:返回流中的第一个元素
  • findAny:返回流种任意一个元素

18、IO流

  • java IO流的四大家族:

    • java.io.InputStream 字节输入流
    • java.io.OutputStream 字节输出流
    • java.io.Reader 字符输入流
    • java.io.Writer 字符输出流
  • 在java中只要“类名”以Stream结尾的都是字节流。以**"Reader/Writer"结尾**的都是字符流

  • java.io包下需要掌握的流有16个:

    • 文件专属
      • java.io.FileInputStream
      • java.io.FileOutoutStream
      • java.io.FileReader
      • java.io.FileWriter
    • 转换流(将字节流转换成字符流)
      • java.io.InputStream
      • java.io.OutputStream
    • 缓冲流专属:
      • java.io.BufferedReader
      • java.io.BufferedWriter
      • java.io.BufferedInputStream
      • java.io.BufferedOutputStream
    • 数据流专属
      • java.io.DataInputStream
      • java.io.DataOutputStream
    • 标准输出流
      • java.io.PrintWriter
      • java.io.PrintStream
    • 对象专属流
      • java.io.ObjectInputStream
      • java.io.ObjectOutputStream

19、类加载

jdk自带的类加载器:bootsrapClassLoader,ExtClassLoader,AppClassLoader,其中BootsrapClassLoader是extclassloader的父类加载器,默认负责加载%JAVA_HOME%lib文件夹下面jar包和class文件。ExtClassLoader是AppClassLoader的父类加载器,负责加载%JAVA_HOME%lib文件夹下面的ext文件夹下面的jar包和class类。AppClassLoader是自定义类加载器的父类,负责加载classpath下的类文件。

类加载过程分为三个阶段

  • 加载

    • 将类的字节码载入方法区,并创建类.class对象
    • 如果次类的父类没有加载,先加载父类
    • 加载时懒惰执行(用到才加载)
  • 链接

    • 验证—验证类是否符合Class规范,合法性、安全性检查
    • 准备—为static变量分配空间,设置默认值
    • 解析—将常量池的符号引用解析为直接引用
  • 初始化

    • 执行静态代码块与非final静态变量的赋值
    • 初始化时懒惰执行

    何为双亲委派

    • 所谓双亲委派,就是指优先委派上级类加载器进行加载,如果上级类加载器

      • 能找到这个类,由上级加载,加载后该类也对下级加载器可见

      • 找不到这个类,则下级类加载器才有资格执行加载

        类加载首先从AppClassLoader类加载开始,

        这时AppClassLoader就向上查找其他的类加载器查找缓存,

        是否加载过该类,有的话返回,没有的话继续向上查找。

        要是一直查找到最上面还没有查找到,就开始从最上面

        的加载路径中向下查找。有的话返回,没有的话继续向下查找。

    • 如果一个类加载器收到了类加载请求,它首先不会自动去尝试加载这个类,而是把这个类委托给父类加载器去完成,每一层依次这样,因此所有的加载请求都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成该加载请求(找不到所需的类)时,这个时候子加载器才会尝试自己去加载,这个过程就是双亲委派机制!

23、JVM

反射:能够动态的获取类的属性和方法,这种机制成为反射。

  • 垃圾回收不会涉及到栈内存:
因为栈内存是一次次的方法调用,调用完弹出。
垃圾回收是堆内存的无用对象
  • 栈内存不建议设置过大
比如栈内存设置1mb,那500mb就可以有500个线程
如果设置2mb,那么500mb就只有250个线程
  • 方法内的局部变量是否线程安全
如果方法内局部变量没有逃离方法的作用访问,它是线程安全的
如果是局部变量引用了对象,并逃离方法的作用方法马,需要考虑线程安全的
  • 栈内存溢出
一般是栈帧过多,方法反复调用且没有返回,就会出现溢出,一般是递归没有正确的错误结束条件
  • 哪些部分会出现内存溢出。(程序计数器不会出现内存溢出)
    • 出现OutOfMemoryError
      • 堆内存耗尽–对象越来越多,又一直在使用,不能被垃圾回收
      • 方法区内存耗尽,加载的类越来越多,很多框架都会运行期间动态产生新的类
      • 虚拟机栈累计–每个线程最多会占用1M内存,线程个数越来越多,而又长时间运行不销毁时
    • 出现StackOverflowError的区域
      • 虚拟机栈内部–方法调用次数过多
  • 方法区与永久代、元空间之间的关系
    • 方法区时jvm规范中定义的一块内存区域,用来存储元数据、方法字节码、即时编译器需要等待的信息等
    • 永久代时Hotpot虚拟机对jvm规范的实现(1.8之前)现在基本不用
    • 元空间时Hostpot虚拟机对jvm规范的实现(1.8以后),使用本地内存作为这些信息的存储空间
  • jvm垃圾回收算法
  • 标记清除(目前基本不使用)
  • 标记整理
  • 标记复制
  • 说说GC和分代回收算法
    • GC的目的在于实现无用对象内存自动释放,减少内存碎片、加快分配速度
    • GC要点
      • 回收区域时堆内存。不包括虚拟机栈,在方法调用结束会自动释放方法占用内存
      • 判断无用对象,使用可达性分析算法,三色标记法标记存活对象,回收未标记对象
      • GC具体的实现称为垃圾回收器
      • GC大都采用了分代回收思想,理论依据时大部分对象招生夕灭,用完立刻就可以回收,另由少部分对象会长时间存活,每次很难回收,根据这两类对象的特性回收区域分为新生代和老生代,不同区域应用不同的回收策略
      • 根据GC的规模可以分为Minor GC、Mixed GC、Full GC
    • Parallel GC (注重吞吐量)
    • ConcurrentMarkSweep GC (注重响应时间)
    • G1 GC
      • 在jdk9之后变成默认的回收器
      • 响应时间与吞吐量兼顾
      • 划分成多个区域
      • 新生代回收、并发标记、混合手机
  • 三色标记
    • 黑色–已标记
    • 灰色–标记中
    • 白色–还未标记
  • 对象引用类型分为哪几类
    • 强引用
      • 普通变量赋值即为强引用,如A a = new A();
      • 通过GC Root的引用链,如果强引用不到该对象,该对象才能被回收
    • 软引用(softReference)
      • 例如:SoftReference a = new SoftReference (new A());
      • 如果仅有软引用改对象时,首次垃圾回收不会回收该对象,如果内存仍不足,再次回收时才会释放对象
      • 软引用自身需要配合引用队列来释放
      • 典型例子就是反射数据
    • 弱引用(WeakReference)
      • 例如:WeakReference a = new WeakReference(new A());
      • 如果仅有弱引用引用该对象时,只要发生垃圾回收,就会释放该对象
      • 弱引用自身需要配合引用队列来释放
      • 典型例子就是ThreadLocalMap中的Entry对象
    • 虚引用(PhantomReference)
      • 例如:PhantomReference a = new PhantomReference(new A());
      • 必须配合引用队列一起使用,当虚引用引用的对象被回收时,会被虚引用对象入队,由Reference Handler线程释放其关联的外部资源
      • 典型例子就是:Clearner释放DirectByteBuffer占用的直接内存
  • finalize的理解
    • 一般回答:他是Object中的一个方法,子类重写它,垃圾回收时此方法会被调用,可以在其中进行一些资源释放和清理工作
    • 优秀回答:将资源释放和清理放在finalize方法中非常不好,非常影响性能,严重时甚至会引起oom,从java9开始就被标注为@Deprecated,不建议被使用了
    • 为什么finalize不好?
      • FinalizerThread是守护线程,代码很有可能没来得及执行完,线程就结束了,造成资源没有正确释放
      • 异常被吞掉这个就太糟了,你甚至不能判断有没有在释放资源时发生错误
      • 影响性能
        • 重写了finalize方法的对象在第一次被gc时,并不能及时释放它占用的内存,因为要等着FinalizerThread调用完finalize,把它从第一个unfinalized队列移除后,第二次gc时才能真正释放内存
        • 可以想象到gc本就因为内存不足引起,finalize调用又很慢(两个队列的移除操作,都是串行执行的,用来释放连接类的资源也应该不快),不能及时释放内存,对象释放不及时就会逐渐移入老年代,老年代垃圾积累过多就会容易full gc,full gc后释放速度如果仍跟不上创建新对象的速度,就睡OOM(Out Of Memory内存不足)
      • 质疑?
        • 有的文章提到【Finalizer线程会和我们的主线程进行竞争,不过由于它的优先级较低,获取到的cpu时间较少,因此它永远也赶不上主线程的步伐】这个显然是错误的,FinalizerThread的优先级较普通线程更高,赶不上步伐的原因应该时finalize执行慢等原因综合导致

24、多线程

什么是线程?

线程与进程相似,但线程是比进程更小的执行单位,线程运行在进程中,一个进程中可以产生多个线程,多个线程共享同一块内存空间。

线程通常有五种状态:创建(new),就绪(start),运行(run),阻塞(sleep,wait,join),死亡。阻塞又分为等待阻塞(运行的线程调用wait方法。)。同步阻塞(多个线程抢夺同一把锁,该锁被其他线程抢夺,则jvm则把该线程放入锁池中。)。其他阻塞(运行的线程调用sleep方法。)。

sleep和wait的区别?

sleep是Thread类的静态方法,wait是Object类的本地方法每个对象都有。sleep不会释放锁,但是wait会释放锁。sleep不需要被唤醒,但是wait需要被唤醒。

notify和notifyall的区别?

notify是唤醒此对象上等待的单个线程,notifyall是唤醒此对象上等待的所有线程。

1:多线程创建的四种方式

一、继承Thread类创建多线程

  • 创建一个类继承Thread类
  • 重写Thread类中的run()方法,在run()方法中实现线程需要完成的功能
  • 创建Thread类的实现类的对象,并调用这个对象的start()方法,调用start()后会自动启动当前线程,并调用当前线程的run()方法。

二、实现Runnable接口创建多线程

  • 创建一个类实现Runnable接口
  • 在这个实现类中实现Runnable接口的run()方法
  • 创建这个实现类的对象,并将这个对象作为参数传入Thread类的构造器中,然后创建Tread类的对象,调用Tread类对象的start()方法。

三、实现Callable接口创建多线程(jdk5.0后新增的)

  • 创建一个类实现Callable接口
  • 在这个实现类中实现Callable接口的call()方法,并创建这个类的对象
  • 将这个Callable接口实现类的对象作为参数传递到FutureTask类的构造器中,创建FutureTask类的对象。
  • 将这个FutureTask类的对象作为参数传递到Thread类的构造器中,创建Thread类的对象,并调用这个对象的start()方法。

前两种创建线程存在一个问题,无法获得线程的执行结果,而是用第三种方式创建线程调用get方法可以得到线程的执行结果。

四、通过线程池创建多线程(jdk5.0后新增的方法)

  • 创建提供指定线程数量的线程池,即创建ExecutorService对象。

  • (😘)创建线程池的四个方法:都是先创建ExecutorService对象,

  • (😘)①再调用CacheThreadPool方法(创建可缓存的线程池,如果线程池长度超过需要,则回收,要是不够,则新建线程。)。

  • (😘)②再调用FixThreadPool方法(创建固定大小的线程池,每提交一个任务就创建一个工作线程,要是工作线程数量线程池的最大数量,超出则放入池队列中),

  • (😘)③再调用SingleThreadExecutor方法(这是一个单线程的Executor,它创建单个工作 线程来执行任务,如果这个线程异常结束,会创建一个新的来代替它;它的特点时能确保依照任务在队列中的顺序来串行执行 ),

  • (😘)④再调用SingleThreadScheduledExecutor方法(创建一个单线程可执行任务的线程池,而且以延迟或定时的方式来执行任务 )。

  • (😘)调用execute()方法执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象

  • (😘)execute()和submit()向线程池提交一个任务,交由线程池去执行。submit()但是它和execute()方法不同,它能够返回任务执行的结果。

  • 关闭线程池

  • (😘)shutdown()

    关闭线程池的方法,不会接受新的任务,会等到已经存在的任务执行完成

    (😘)shutdownNow()

    关闭线程池的方法,不会接受新的任务,已经运行的任务也会被中断执行

class MyRunnable implements Runnable{
    @Override
    public void run() {
       System.out.println("通过线程池创建多线程");
    }
}
 
public class ThreadCreate4 {
    public static void main(String[] args) {
        //1、创建线程池对象
        ExecutorService service = Executors.newFixedThreadPool(100);
        //2、这里传入的参数是Runnable接口实现类的对象,并调用execute()方法
        service.execute(new MyRunnable());
        //3、关闭线程池
        service.shutdown();
    }
}

4:在 Java 程序中怎么保证多线程的运行安全

  • 使用安全类,比如 Java. util. concurrent 下的类。

  • 使用自动锁 synchronized。

  • 使用手动锁 Lock。 手动锁 Java 示例代码如下:

    Lock lock = new ReentrantLock();
    lock. lock();
    try {
    System. out. println("获得锁");
    } catch (Exception e) {
    // TODO: handle exception
    } finally {
    System. out. println("释放锁");
    lock. unlock();
    }
    

5:Java程序中怎么保证多线程的运行安全

  • 线程安全主要是三方面体现(synchorized的作用):
    • 原子性:同一时刻只能有一个线程对数据操作
    • 可见性:一个线程对内存的修改可以及时地被其他线程看到
    • 有序性:线程按照代码的顺序执行。

6:什么是死锁

  • 死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,他们都将无法推进下去

7:怎么防止死锁

  • 死锁有四个必要条件:
    • 互斥条件:进程对所分配到的资源不允许其他进程进行访问,若其他进程访问该资源,只能等待,直至占有该资源的进程使用完成后释放该资源
    • 请求和保持条件:进程获得一定的资源之后,又对其他资源发出请求,但是该资源可能被其他进程占有,此事情求阻塞,但又对自己获得的资源保持不放
    • 不可剥夺条件:是指进程已获得的资源,在未完成使用之前,不可被剥夺,只能在使用完后自己释放
    • 环路等待条件:是指进程发生死锁后,若该进程之间形成一种头尾相接的循环等待资源关系

8:lock和synchronized区别

  • 语法层面
    • synchronized是关键字,源码在jvm中,用c++语言实现
    • Lock是接口,源码由jdk提供,用java语言实现
    • 使用synchronized时,退出同步代码块锁会自动释放,而使用Lock时,需要手动调用unlock方法释放锁
  • 功能层面
    • 二者均属于悲观锁、都具备基本的互斥、同步、锁重入功能
    • Lock提供了许多synchronized不具备的功能,例如获取等待状态、公平锁、可打断、可超时、多条件变量
    • Lock有适合不同场景的实现,入ReentrantLock(可重入锁)、ReentrantReadWriteLock(适合读多写少)
  • 性能层面
    • 在没有竞争的时候,synchronized做了很多优化,如偏向锁、轻量级锁,性能不赖
    • 在竞争激烈的时候,Lock的实现通常会提供更好的性能

9:volatile能否保证线程安全

  • 线程安全要考虑三个方面:可见性、有序性、原子性
    • 可见性指:一个线程对共享变量修改,另一个线程能看到最新的结果
    • 有序性指:一个线程内代码按编写顺序执行
    • 原子性指:一个线程内多行代码以一个整体允许,期间不能有其他线程的代码插队
  • volatile能够保证共享变量的可见性和有序性,但并不能保证原子性,不能保证线程安全

volatile 与 synchronized 的区别

1)volatile 本质是告诉JVM当前在寄存器中的值是不确定的,需要重新从主内存中读取。synchronized 则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞。

2)volatile 仅能使用在变量级别,synchronized 则可以使用在变量,方法上。

3)volatile能保证数据的可见性,但是不能保证原子性;而synchronized可以保证原子性,也可以保证可见性。

4)volatile 不会造成线程的阻塞,而synchronized 可能会造成线程的阻塞。

5)当一个域(接口中的变量,属性等)的值依赖于它之前的值时,volatile 就无法工作了,如 n++。当某个域的值受到其他域的值的限制,volatile 也无法工作,如 Range类的 lower 和 upper 边界,必须遵循 lower<=upper 的限制。

6)使用 volatile 而不是 synchronized 的唯一安全情况是类中只有一个可变的域。

synchronized 和 Lock 的区别

1)Lock 是一个接口,而synchronized 是Java中的关键字,synchronized 是内置的语言实现。

2) synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象的发生;而 Lock 在发生异常时,如果没有主动通过 unlock() 去手动释放,则可能造成死锁现象,因此使用 Lock 时需要在try-catch-finally 块中释放锁。

3)Lock 可以让等待锁的线程响应中断,而 synchronized 却不行,等待的线程会一直等待下去,不能够响应中断。

4)Lock 中的 tryLock() 可以知道有没有成功获取锁,如果获取到锁则放回true反之false,这样可以避免线程阻塞问题,而synchronized 无法办到。

5)Lock 可以提高多个线程进行读操作的效率。

10:Java中的悲观锁和乐观锁

  1. 悲观锁的代表时synchronized和Lock锁
    1. 核心思想是【线程只有占了锁,才能去操作共享变量,每次只有一个线程占锁成功,获取锁失败的线程都要停下来等待】
    2. 线程从允许到阻塞,再从阻塞到唤醒,设计到线程上下文切换,如果频繁发生,影响性能
    3. 实际上,线程在获取synchronized和Lock锁时,如果锁已被占用,会做几次重试操作,减少阻塞的机会
  2. 乐观锁的代表是Atomiclnteger,使用cas(compareAndSetxx)来保证原子性
    1. 核心思想是【无需加锁,每次只有一个线程能成功修改共享变量,其他失败的线程不需要停止,不断重试直到成功】
    2. 由于线程一直运行,不需要阻塞,因此不涉及线程上下 文切换
    3. 它需要多核cpu支持,且线程数不应超过cpu核数

11:ThreadLocal的理解

  • ThreadLocal可以实现【资源对象】的线程隔离,让每个线程各自用各自的【资源对象】,避免争用引发的线程安全问题

  • ThreadLocal同时实现了线程内的资源共享

  • 其原理是,每个线程内有一个ThreadLocalMap类型的成员变量,用来存储资源对象

    • 调用set方法,就是以ThreadLocal自己作为key,资源对象作为value,放入当前线程的ThreadLocalMap集合中
    • 调用get方法,就是以ThreadLocal自己作为key,到当前线程中查找关联的资源值
    • 调用remove方法,就是以ThreadLocal自己作为key,移除当前线程关联的资源值
  • 为什么ThreadLocalMap中的key(即ThreadLocal)要设计为弱引用 ?

    • Thread可能需要长时间运行(如线程池中的线程),如果key不在使用,需要在内存不足(GC)时释放其占用的内存

    • 但GC仅是让key的内存释放,后续还要根据key是否为null来进一步释放值的内存,释放时机有:

      • 获取key发现null key

      • set key时,会使用启发式扫描,清楚临近的null key,启发次数与元素个数,是否发现null key有关

      • remove时(推荐),因为一般使用ThreadLocal时都把它作为静态变量,因此GC无法回收

  1. 什么是反射?

反射就是能够动态的获取类的属性和方法。

Get**和Post的区别

1.get是从服务器上获取数据,post是向服务器传送数据,

2.get传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制。

3.get安全性非常低,post安全性较高。但是执行效率却比Post方法好。

4.在进行文件上传时只能使用post而不能是get。

数据库三范式

​ 第一范式:数据库表中的所有字段值都是不可分解的原子值。(就是每一个字段都不能再分解了。)

​ 第二范式:需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部 分相关(主要针对联合主键而言)

​ 第三范式:需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关

25、Spring

spring:就是一个j2ee容器框架,用来装javabean。

ioc(控制反转):是一种设计思想,将原本在程序中手动创建对象的控制权交由Spring框架来管理。使用依赖注入来完成(ioc)控制反转。

依赖注入:类之间的依赖关系交给容器负责。简单来讲a依赖b,但a不创建(或销毁)b,仅使用b,b的创建(或销毁)交给容器。

依赖注入的方法是构造器注入,Strter方法注入。

如何实现ioc容器(控制反转):1、配置文件配置包扫描路径2、扫描包获取.class文件3、通过反射、确定需要交给IOC管理的类4、对需要注入的类进行依赖注入

bean的自动装配(@AutoWirted):byName:通过参数名自动装配。byType:通过参数类型自动装配。

将一个类声明为bean的注解有哪些?@Component,@Repository,@Service,@Controller

bean的生命周期:六个阶段Bean定义、实例化、属性赋值、初始化、生存期、销毁。

spring中bean的作用域(@scope注解):singleton(单例,默认的作用域),prototype(原型。每调用一次getBean()方法则获取一个新的Bean对象。),request(一个请求对应一个Bean。),session(一个会话对应一个Bean。)

AOP

  • 在不惊动原始设计的基础上为方法进行功能增强

  • 常用注解:

  • 前置通知
    @Before:用于配置前置通知。指定增强的方法在切入点方法之前执行
    后置通知
    @AfterReturning:用于配置后置通知。指定增强的方法在切入点方法之后执行
    环绕通知
    @Around:用于配置环绕通知。指定增强的方法在切入点方法之前和之后都执行 其中环绕通知需要依赖ProceedingJoinPoint形参
    异常抛出通知
    @AfterThrowing:用于配置异常抛出通知。指定增强的方法在出现异常时执行
    最终通知
    @After:用于配置最终通知。无论增强方式执行是否有异常都会执行

beanfactory和applicationContext有什么区别?

ApplicationContext是BeanFactoryf的子接口,ApplicationContext提供了更完整的功能。

BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该
Bean进行加载实例化。ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。

@Component和@Bean的区别是什么?

1.作用对象不同:@Component注解作用于类,而@Bean注解作用于方法。
2.@Bean注解比Component注解的自定义性更强,而且很多地方我们只能通过@Bean注解来注册bean。比如当我们引用第三方库中的类需要装配到Spring容器时,则只能通过@Bean来实现。

常用注解:

异常注解:

当自定义类加@ControllerAdvice注解时,方法需要返回json数据时,每个方法还需要添加@ResponseBody注解
当自定义类加@RestControllerAdvicei注解时,方法自动返回json数据,每个方法无需再添加@ResponseBody注解

@ExceptionHandler作用于方法上,告诉系统这个方法是用来处理异常的称为异常处理方法。

@RequestMapping:处理url地址栏映射的注解,可以作用于类上,可以作用于方法上。

@AutoWirted:对类的成员变量,方法,及构造函数进行标注,完成自动装配。(默认按类型注入。)。

@RequestBody:接收前段发送来的json数据。

@configurationproties将整个文件映射成一个作用于类上,可以将yal里的参数与该类的属性进行一一匹配赋值。

自动装配的原理(springapplication注解):

@springbootApplication注解是一个复合注解,是springboot自动装配的核心。、

@springbootApplication下面有@SpringbootConfiguration(配置类)和@EnableAutoConfiguration和@ComponentScan(扫描自己注册的组件),@EnableAutoConfiguration下面有@import注解(扫描当前系统中的METE-INF/spring.factories中的文件,这个文件中定义了所有配置类)和@AutoConfigurationPackage(将@componentScan扫描的组件加载到ioc容器中。)。

事务:

Spring支持两种事务方式,分别是编程式事务和声明式事务,通常情况下只需要使用声明式事务@Transactiona注解。

在@Transanctioon注解里面可以有属性,propagation(事物的传播行为),事务超时时间(timeout),事务隔离级别(isolation)

事务的传播行为:REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
REQU帆E$NW:新建事务,如果当前存在事务,把当前事务挂起g
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。

事务隔离级别:🏮未提交读(Read uncommitted),最低的隔离级别,允许🔵“脏读”(dirty reads):事务可以看到其他事务“尚未提交”的修改。如果另一个事务回滚,那么当前事务读到的数据就是脏数据。
🏮提交读(read committed),一个事务可能会遇到不可重复读(Non Repeatable Read)的问题。🔵不可重复读是指,在一个事务内,多次读同一数据,在这个事务还没有结束时,如果另一个事务恰好修改了这个数据,那么,在第一个事务中,两次读取的数据就可能不一致。
🏮可重复读(repeatable read),一个事务可能会遇到幻读(Phantom Read)的问题。🔵幻读是指,在一个事务中,第一次查询某条记录,发现没有,但是,当试图更新这条不存在的记录时,竟然能成功,并且,再次读取同一条记录,它就神奇地出现了。
🏮串行化(Serializable),最严格的隔离级别,所有事务按照次序依次执行,因此,脏读、不可重复读、幻读都不会出现。虽然 Serializable 隔离级别下的事务具有最高的安全性,但是,由于事务是串行执行,所以效率会大大下降,应用程序的性能会急剧降低。如果没有特别重要的情景,一般都不会使用 Serializable 隔离级别。

ansanctioon注解里面可以有属性,propagation(事物的传播行为),事务超时时间(timeout),事务隔离级别(isolation)

事务的传播行为:REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
REQU帆E$NW:新建事务,如果当前存在事务,把当前事务挂起g
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。

事务隔离级别:🏮未提交读(Read uncommitted),最低的隔离级别,允许🔵“脏读”(dirty reads):事务可以看到其他事务“尚未提交”的修改。如果另一个事务回滚,那么当前事务读到的数据就是脏数据。
🏮提交读(read committed),一个事务可能会遇到不可重复读(Non Repeatable Read)的问题。🔵不可重复读是指,在一个事务内,多次读同一数据,在这个事务还没有结束时,如果另一个事务恰好修改了这个数据,那么,在第一个事务中,两次读取的数据就可能不一致。
🏮可重复读(repeatable read),一个事务可能会遇到幻读(Phantom Read)的问题。🔵幻读是指,在一个事务中,第一次查询某条记录,发现没有,但是,当试图更新这条不存在的记录时,竟然能成功,并且,再次读取同一条记录,它就神奇地出现了。
🏮串行化(Serializable),最严格的隔离级别,所有事务按照次序依次执行,因此,脏读、不可重复读、幻读都不会出现。虽然 Serializable 隔离级别下的事务具有最高的安全性,但是,由于事务是串行执行,所以效率会大大下降,应用程序的性能会急剧降低。如果没有特别重要的情景,一般都不会使用 Serializable 隔离级别。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值