Java
- int可以用于泛型吗?
- Integer类型的List里可以有String类型的值吗?
- 使用一个静态内部类的静态属性,该外类是否会加载?
- 抽象类和接口是否有构造方法,能否被实例化,普通方法是否可以调用抽象方法?
- 泛型类型可以被反射获取吗?
- 使用类加载器去加载类是否会造成类的初始化?反射呢?
- 重载的方法传入null怎么办?
- +=小知识与i++小坑
- try catch final系列问题,与throw,throws
- 重载和重写的区别
- 内部类和枚举
- Integer内部类IntegerCache和其他
- new String(“a”)+new String(“b”)会创建几个对象问题
- str.intern()方法
Mysql
Jvm
Spring系列
- Spring七大事务传播机制
- Spring三种注解注入方法
- Spring中有两个ID相同的Bean会报错吗?
- Spring注册Bean的几种方式
- Spring事务实现机制
- Spring事务失效原理
- Spring的AOP三种使用方式
- cglib动态代理,jdk动态代理,AspectJ动态代理
Mybatis系列
Redis
RabbitMQ
SpringCloud系列
计算机网络和操作系统
Java
1. int可以作为泛型吗?
int是基本数据类型,泛型需要为对象,可以使用其包装类Integer。另外int[]数组是对象,可用于泛型。
2. Integer类型的List里可以有String类型的值吗?
可以看到,引用重新指向就可以插入其他类型的值。泛型只在编译阶段做检查,后面就会进行擦除。
可以使用Collections.checkedList(new ArrayList(), Integer.class)生成运行阶段也可检查泛型的集合,若类型不一致会报Cast异常。
3. 使用一个静态内部类的静态属性,该外类是否会加载?
不会进行加载,静态内部类编译后算是一个单独的类。另外的:
- 通过子类来使用其父类的静态字段只会触发父类的初始化(执行静态代码块等,不会实例化)。如B继承A,A中有静态属性a,通过B.a使用该属性不会触发B的初始化。
- 常量在编译阶段会通过编译传播优化存入调用类的常量池中,使得使用时不会造成被调用类加载。如C调用D的static final String的属性,就不会进行D的初始化。
- 通过数组定义来引用类,不会造成该类的初始化,而是会触发虚拟机自动生成的一个类的初始化,该类代表了一个元素类型为原类的数组。如E类,new E[10]; 时不会使E初始化。
4. 抽象类和接口是否有构造方法,能否被实例化,普通方法是否可以调用抽象方法?
- 抽象类可以有构造方法,接口无法有构造方法。
- 抽象类虽有构造方法,但和接口一样不能进行实例化,而是提供给子类实现调用的。
- 普通方法可以调用抽象方法,因为抽象方法最终会实现,且抽象类和接口不能实例化调用方法。
5. 泛型类型可以被反射获取吗?
类成员中的有类型泛型可以被反射获取,而局部声明的类通常需自己传入指定类型,是不能被获取的。
反射获取泛型是访问类模板(类信息),如果类模板中是没有类型的泛型则不能被获取。
其中:有类型泛型是ArrayList< Integer >这样的,无类型泛型即ArrayList< T >等这样子的。
6. 使用类加载器去加载类是否会造成类的初始化?反射呢?
类加载器去加载类不会造成类初始化,而反射需要反射已经加载好的类模板,经过了初始化。关于初始化可以查看类加载的过程。
7. 重载的方法传入null怎么办?
重载的方法传入null,会优先使用类型更明确的那个方法。比如一个需要传入Integer一个需要传入Object就会调用Integer这个,此时如果还有需要传入为String的则会报错。同理动态参数列表int…其他方法优先。
8. +=小知识与i++小坑
+=会隐式进行转换,看此图。
而 i=i++ ,i++为先值备份后自增,后面把备份给i,此时i重新赋为了原值。
9. try catch final系列问题,与throw,throws
- final最终都会被执行,除非前面代码使程序终止。
- try或catch里面return了a变量,值会被临时存储,如果final改变了a的值但是没有return,临时保存的值是不会改变的,照样return那个临时存储的值。如果final改变了a变量并且进行return那么会直接返回改变后的值。
- catch捕获了A异常,后续再尝试捕获A异常的子类时会报错。catch捕获了B异常的子类,后续再尝试捕获B异常的父类不会报错但是不会进行捕获。
- thorw用于在方法体显式的抛出一个异常,throws用于在方法处抛出可能会发生的异常给调用者处理。throw一次抛出一个异常,throws可以用逗号隔开抛出多个异常。
10. 重载和重写的区别
- 重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重载对返回类型没有特殊的要求,不能根据返回类型进行区分。
- 重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的参数列表,有兼容的返回类型(本身或其子类),比父类被重写方法更好访问(权限要更高)。
11. 内部类和枚举
内部类可分为:成员内部类,局部内部类,静态内部类,匿名内部类。
- 成员内部类
作为外类的类成员存在,初始化方式为 new外类().new 成员内部类();
成员内部类访问外类的属性方式为 外类.this.属性,访问外类的方法可直接调用。 - 局部内部类
存在外类的方法中,和局部变量一样不能过多修饰,只能用final和abstract对其修饰。 - 静态内部类
静态初始化类和静态成员类,编译后相当于一个额外的类。 - 匿名内部类
此类写出来相当于一个实现了InterfaceA接口的无名类,通常用作传参。
new InterfaceA(){
@Override
}
// --------------------------
而如果接口里只有一个方法,可以简化为
()->{}
()里为那个方法的参数,{}里为方法体
- 枚举类默认继承了Enum,不能再继承其他类,可以实现接口。
- 枚举类Enum重写了Object的很多方法并且final了,所以我们在自己写枚举类时只能重写toString()方法。
- 枚举类的构造方法需要为private,并且写了任意方法后列举枚举值时在最后需要加冒号。
- 枚举类都有默认的ordinal()和name()方法,来获取枚举值列举时的属性。
- 枚举类在创建单例时有不错的安全性,在有反射干扰其时会直接报错。
12. Integer内部类IntegerCache和其他
- 我们都知道,在Integer a=1;赋值时会进行默认的valueOf(1)装箱操作,会判断该数是否在-128~127之间,如果在则会return IntegerCache内部类中的缓存的Integer数组cache中的Integer值。
- 如果Integer a=1;Integer b=1;使得a和b是一样的都是cache中同一个引用。如果Integer a=new Integer(1);Integer b=new Integer(1); 则a和b都会新建引用。
- 另外的,其它包装类也有这样的缓存机制,但是只有Integer包装类可以使用JVM参数改变缓存数组最大值,可以使得127之外的数也进缓存。
13. new String(“a”)+new String(“b”)会创建几个对象问题
- 我们先知道,new String(“a”)时,如果字符串常量池中已经存在"a"这个对象,则只会创建new String()值为"a"的对象在堆中。如果字符串常量池没有"a",则会产生一个"a"在字符串常量池和一个new String(“a”)在堆中。
- new String(“a”)+new String(“b”)需要借助StringBuilder来完成拼接再转化回去,则会产生:
new StringBuilder(),new String(“a”),字符串常量池中的"a",new String(“b”),字符串常量池中的"b",和一个new String(“ab”)共六个对象。 - StringBuilder在toString时有new String(“ab”,0,cont)会创建一个new String(“ab”)类似的对象,但是在字符串常量池里不会生成"ab"对象。另外String.valueOf(int a)等一些方法也不会在字符串常量池中创建字符串值对象。
14. str.intern()方法
str.intern() 方法的作用为返回str在字符串常量池中的值。在不同的jdk版本会有一些坑。jdk1.7把字符串常量池从永久代放到了堆区(永久代是堆中的非堆),jdk1.8去除了永久代变成了元空间。
如以下代码片段,这三个对象是否相等捏:
String strA = String.valueOf(8);
String strB = strA.intern();
String strC = "8";
在jdk1.7之后:
- String.valueOf(8)字符串常量池并不会创建"8"。
- strA.intern()会发现字符串常量池没有"8"而创建调用者strA的引用放入池中。
- 在strC引用字符串常量池中值为"8"的对象时,寻找到的则是strA的引用。则strA,strB,strC三个对象相等。
在jdk1.7版本之前:
- String.valueOf(8)字符串常量池并不会创建"8"。
- strA.intern()会发现字符串常量池没有"8"而创建"8"对象放入字符串常量池中,可以理解为非堆不会存储引用只能存值。
- 在strC引用字符串常量池中值为"8"的对象时,寻找到的则是"8"对象。则strB,strC相等,strA与它们不一样。
Mysql
1. 一张表里面有ID自增主键,当insert了17条记录之后,删除了第15,16,17条记录,再把mysql重启,再insert一条记录,这条记录的ID是多少?
innodb会把最大id记录在内存中,myisam把最大id记录在数据文件中。重启导致内存丢失,则innodb会从15开始,而myisam数据文件的最大id未丢失,从18开始。
Jvm
1. 已知内存中没有连续的20M内存,但是不连续的内存有很多。有一个长度为20的数组,每个下标对应对象大小为1M,问内存能否放得下这个数组?
数组下标存储对象的引用,即下标对应对象引用分配在连续的空间,而引用对应的对象可以在其他不连续的空间,所以大概率放得下。
2. 类加载的过程
->加载 :类加载器把.class文件加载到内存中,创建Class对象模板。
->验证 :文件格式验证,元数据验证,字节码验证,符号引用验证。
->准备 :给引用分配内存空间,并且赋默认值(final static不会赋值,后续可能会用静态代码块改变,而final使其不能变)。
->解析 :将符号引用替换为直接引用。
->初始化:为类的静态变量赋真正的值。
->使用 :
->卸载 :
3. 类加载的机制
- 全盘负责:如果该加载器负责加载该Class,则该Class依赖引用的其他类也由该加载器加载。
- 双亲委派:向上向下直到。遇到类加载请求先给上一级类加载器看是否加载过,加载过则直接使用(大部分核心类已经加载了),没加载则持续递到顶级再往下,此时谁能加载就给谁加载。防止上级的类被篡改,如自己写一个String就没用。
- 缓存:缓存保存所有加载过的类,当需要在缓存中找,没有就加载。
Spring系列
1. Spring七大事务传播机制(例子为A调用B)
TransactionDefinition定义了Spring的七大事务传播机制。PROPAGATION_
- REQUIRED(Spring默认的事务传播类型 required:需要有):如果A有事务B有事务,则B加入A一起。如果A没有事务B有事务,则B自己创建事务执行。
- SUPPORTS(supports:支持别人): 支持上层做法。A有事务B有事务B就加入A事务执行,A没事务B有事务B就以非事务执行。
- MANDATORY(mandatory:强制性的): 该事务需要在可回滚事务中进行。A没事务B有事务就报错。
- REQUIRES_NEW(requires_new:启新事务): 无论上层有没有事务,该事务都会新创建执行。A事务调用B事务,A事务挂起,且各回滚各的。
- NOT_SUPPORTED(not supported:不支持):以非事务方式运行,若在事务中调用,则外层事务挂起。
- NEVER(never:从不): 上下层都不允许有事务,抛异常。
- NESTED(nested:嵌套的): A有事务B就嵌套在里面执行,A没事务B就自己创建执行。
REQUIRED和NESTED的区别就是:NESTED嵌套时A回滚B也回滚,B回滚A可以捕获异常而不回滚。REQUIRED加入时A回滚B也回滚,B回滚不能捕获异常而A也会回滚。
2. Spring三种注解注入方法
在属性上@Autowired等实现注入
优点:简洁明了
缺点:注入对象不能用final属性,可能导致循环依赖。在构造方法上@Autowired等实现注入 (推荐)
优点:强制注入防空指针异常,注入对象可以final,可能出现三级缓存不能解决的循环依赖(生命周期在第一步卡住,第三级缓存无法生效)
缺点:注入过多很不好看在set方法上@Autowired实现注入
优点:注入对象可以为null,可以在类构造后重新注入
缺点:注入对象不能final
其中,@Autowired优先通过byType进行注入,可通过@Qualifier辅助指定byName进行注入。@Resource为默认优先byName进行注入,可用其提供的属性指定byType或byName进行注入。在xml中也有使用pc命名空间来控制注入位置,使用autowired标签来控制byName,byType的注入。
3. Spring中有两个ID相同的Bean会报错吗?
- 在同一个xml文件里不能存在相同ID的Bean,在把xml解析成BeanDefinition时会验证ID唯一性而报错。在不同xml文件里可以存在相同ID的Bean,但是根据解析顺序会进行覆盖。
- 在@Configuration中使用@Bean注册时,只会保留第一个被注册的实例,后续不会生效。
4. Spring注册Bean的几种方式
- 在xml中注册。
- @Configuration的类中方法上@Bean然后return对象注册。
- 在@Configuration上@Import({类.class})注册该类。
- 开启包扫描,扫描下的@Component等的类注册。
- 已注册的一个A类实现FactoryBean<B类>接口重写三个方法,(getObject,getObjectType,isSingleton)分别return B对应的类型属性,
使用getBean(“A”)来获得getObject提供的对象也就是B,getBean(“&A”)则是获得A。 - 在注册的A类实现ImportSelector接口重写方法后return一个String数组,里面放的要注册的类的全限定名。
5. Spring事务实现机制
- @EnableTransactionManagement后使用@Tranceactional。
- @EnableTransactionManagement会注册配置类,配置类定义了对@Tranceactional使用通知,创建动态代理。
- 动态代理事务管理器新建数据库连接,放在ThreadLocal<Map<Datasource,connection>>中,设置自动提交为false,执行事务时会使用自己的数据源去ThreadLocal里面当做key去找连接,使用同一个连接统一提交或回滚事务。
6. Spring事务失效原理
前面提到,执行事务需要动态代理和同一数据源,大部分失效的原因就在于此。(分布式事务在Cloud系列说)
- 方法private了。类不是bean没有被管理。在事务失败时自己捕获了异常没有处理。数据源不一致。
- 在一个类中一个事务方法调用另一个事务方法,内部调用时默认使用this进行调用,使得代理对象并未参与导致事务失效。解决方法是:自己注入自己然后进行调用,此时就是使用代理对象进行调用了,或者使用AopContext.currentProxy()拿到代理对象来调用。
7. Spring的AOP三种使用方式
- 定义一个类并注册,在里面写好对应的before,after增强方法。在xml中可以指定这个类为切面,去对自己想增强的切点进行指定aop:before,aop:after等的增强。
- 定义n个类并注册,实现MethodBeforeAdvice,AfterReturningAdvice等接口重写其中的方法。在xml中通知到对应的切点里,通过接口来识别增强的时机进行方法增强。
- 定义一个类@Aspect并注册,在里面的方法上@Pointcut(),@Before(),@After(),@Around()对匹配到的切点进行方法增强。
8. cglib动态代理,jdk动态代理,AspectJ动态代理
- jdk动态代理:实现被代理类接口,方法里反射调原方法进行前后增强。
- cglib动态代理:继承被代理类,方法里直接调用原方法进行前后增强。(如果final不能被继承就代理不了)
- AspectJ代理:编译时在字节码层面对类方法前后进行增强,需要它的编译器。
Mybatis系列
Redis
RabbitMQ
SpringCloud系列
计算机网络和操作系统