1.String类型的几种存储方式,是否是线程安全的,两个字符串相加是否是新的实例?
- 有二种存储方式:
- String str=“test”,这是第一种创建的“test”保存在(方法区)字符串常量池中,string是final修饰的,所以只能一次赋值不能改变,所以字符串不能改变
- String str=new String(“test”),第二种方式在堆中开辟空间,当调用new()方法时,jvm将会调用String的构造函数,同时引用常量池中的test字符串,在堆中创建一个string对象,并返回堆中的引用地址,没当用同样的方法new一个test字符串的时候,就会创建一个String对象,然后指向常量池中的test
- 是线程安全的:因为String是用final修饰的,只能一次赋值不能改变
- String str=“a”+“b”:这样的话不产生新的对象,String str= a+b,会创建新的对象:如果右对象参与相加,编译器无法处理,结果就会创建新对象,如果字符直接相加,会先经编译器处理,气且常量使用过就是一个对象,下次使用不产生新的对象
- tips:因为字符串是不可变的,所以在创建的时候HashCode就被缓存了,不需要重新计算,这就使得字符串会适合作为Map的键,字符串的处理速度要快于其他键对象,这就是HAshMap中键为啥是String字符串类型(把这个顺便说了,面试官会感觉你知识面很广)
2.Stringbuffer用append方式,对象是否发生变化?
- stringBuffer实际上是一个动态字符串数组,类对象可以多次修改,append()相当于加号不产生新的对象,线程安全的
- stringBuilder是线程不安全的,但速度上更快
3.Java的值传递
- 值传递:方法在调用时,实参通过形参把他内容的副本拷贝,传入方法内部,方法内的都是对这个副本的操作,不影响原始值
- 引用传递:引用是指向真实的地址值,在方法的调用时,实现的地址通过方法的调用传递给形参,在方法体内形参和实参如果都指向同一个内存地址,对形参的操作会影响真实内容,否则不会
4.Java内存区域的划分,线程共享的部分,哪些部分会有溢出的情况?
- 线程共享的有java堆和方法区,线程独享的都本地方法区、栈、pc寄存器
- 除了pc寄存器都可能会发生溢出
- java堆:无限循环new对象,在list中保存防止被gc回收,会报memory leak内存泄漏
- 方法区:生成大量的动态类,或无线循环调用String 的intern()方法产生不同的String对象实例放到list中,前者测试方法区中非常量池部分,后者测试常量池
- 虚拟机栈和本地方法区:单线程的话递归调用一个简单方法会抛出StackOverflowError,多线程无线创建线程,被增加内存会抛出OutOfMemoryError
5.final类
- 不能被继承,不能被覆盖,地址引用和装载在编译时(inline编译)完成执行速度快
tip
- final 变量必须在声明的时候初始化或者在构造器中初始化
- 在匿名类中所有的变量都必须是final变量
6.static修饰符
-
static是一个用于修饰成员的修饰符
-
static修饰的成员被所有的对象共享
-
static优于对象存在,static的成员随着类的加载就已经存在了
-
修饰的成员直接用ClassName.调用
-
修饰的数据是静态变量共享数据,存储在方法区中,对象共享,一般的成员变量存储在堆内存的对象中,也叫对象的特有数据
浅拷贝
- 对于基本数据类型,因为基本数据类型是值传递,所以直接将属性赋值给新的对象,其中一个对象修改,不会影响另一个
- 对于引用类型,接口数组和类,因为类型是引用传递,所以浅拷贝只是把内存地址赋值给了成员变量,他们指向同一内存空间,改变其中一个,会对另一个产生影响
深拷贝
- 对于基本数据类型和浅拷贝一样
- 对于引用数据类型,会创建一个新的内存空间然后拷贝里面的内容,所以他们指向不同的内存空间,改变一个,不影响另一个
tips
- 实现对象的拷贝的类,要实现Cloneable接口,并重写clone()方法
创建线程方式,run和start的区别。几种线程池设计模式
- 有继承THread类和实现runable接口两种方式
- 直接调用run方法会沿着主线程来执行方法,调用start方法会调用JVM——StartThread方法去创建一个新的子线程,并通过run 方法启动子线程,run只是thread的一个普通方法
设计模式
- 主要有单例设计模式、享元设计模式、代理模式、装饰者模式
- 单例模式:一般有懒汉式、饿汉式、双重检查加锁式几种实现方式
- 懒汉式:资源不在初始化阶段全部加载或者初始化,而是等到使用的时候才去判断加载,优点,初始化比较快,在运行的时候也只加载一次
- 饱汉式:所有的资源加载和对象实例化都在初始化阶段,接下来直接使用就行,优点:资源直接加载,运行时性能更高,但资源消耗大,耗时比较长
- 从安全角度看,不加同步的饿汉式是不安全的,比如两个线程同时调用getInstance方法,那就可能导致并发安全问题,加synchronzied 可实现线程安全,但会降低访问速度,更好的解决方案就是双重加锁式
- 双重检查加锁:先判断对象是否已经被初始化,再决定要不要加锁。
- 检查变量是否被初始化(不去获得锁),1如果已被初始化则立即返回。2获取锁。3再次检查变量是否已经被初始化,如果还没被初始化就初始化一个对象。
执行双重检查是因为,如果多个线程同时了通过了第一次检查,并且其中一个线程首先通过了第二次检查并实例化了对象,那么剩余通过了第一次检查的线程就不会再去实例化对象。
这样,除了初始化的时候会出现加锁的情况,后续的所有调用都会避免加锁而直接返回,解决了性能消耗的问题。 - 双重加锁式的实现用到的是volatile关键字,被valatiel修饰的变量,不会被本地线程缓存,所有对该变量的操作都是直接在共享内存(主内存)进行的,从而保证了多个线程的同步安全
mysql的char和varchar的区别;行级锁和表级锁的概念,以及给项目带来的影响
- char是定长,超过截取,不足补空格,最多存放255个字符
- varchar是不定长,规定长度内有多少存多少,超出舍去
- mysql的默认innoDb引擎支持行锁和表锁,而myisam不支持行锁
- 表锁:不会发生死锁,发生锁冲突的几率高,并发低
- 有两种模式:表(共享)读锁,表(独占)写锁
- 读锁会阻塞写,写锁会阻塞读和写
- myisam不适合做主表的引擎,因为加入写锁后,其他的线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成永久阻塞
- 行锁:会发生死锁,发生锁冲突几率低,并发高
- mysql的innodb引擎支持的行锁,与Oracle不同,mysql的行锁通过索引加载的如果没有索引会自动锁全表,那就不是行锁了
- tips:间隙锁:
- 进行范围条件检索数据时,对于键值在条件范围内并不存在的记录加锁,就是间接锁