全球顶级java开发面试宝典

发布于:2022-11-27 ⋅ 阅读:(584) ⋅ 点赞:(0)

Java

  1. int可以用于泛型吗?
  2. Integer类型的List里可以有String类型的值吗?
  3. 使用一个静态内部类的静态属性,该外类是否会加载?
  4. 抽象类和接口是否有构造方法,能否被实例化,普通方法是否可以调用抽象方法?
  5. 泛型类型可以被反射获取吗?
  6. 使用类加载器去加载类是否会造成类的初始化?反射呢?
  7. 重载的方法传入null怎么办?
  8. +=小知识与i++小坑
  9. try catch final系列问题,与throw,throws
  10. 重载和重写的区别
  11. 内部类和枚举
  12. Integer内部类IntegerCache和其他
  13. new String(“a”)+new String(“b”)会创建几个对象问题
  14. str.intern()方法

Mysql

  1. 重启mysql后ID会从何开始自增问题

Jvm

  1. 数组内存是否超出问题
  2. 类加载的过程
  3. 类加载的机制

Spring系列

  1. Spring七大事务传播机制
  2. Spring三种注解注入方法
  3. Spring中有两个ID相同的Bean会报错吗?
  4. Spring注册Bean的几种方式
  5. Spring事务实现机制
  6. Spring事务失效原理
  7. Spring的AOP三种使用方式
  8. 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_

  1. REQUIRED(Spring默认的事务传播类型 required:需要有):如果A有事务B有事务,则B加入A一起。如果A没有事务B有事务,则B自己创建事务执行。
  2. SUPPORTS(supports:支持别人): 支持上层做法。A有事务B有事务B就加入A事务执行,A没事务B有事务B就以非事务执行。
  3. MANDATORY(mandatory:强制性的): 该事务需要在可回滚事务中进行。A没事务B有事务就报错。
  4. REQUIRES_NEW(requires_new:启新事务): 无论上层有没有事务,该事务都会新创建执行。A事务调用B事务,A事务挂起,且各回滚各的。
  5. NOT_SUPPORTED(not supported:不支持):以非事务方式运行,若在事务中调用,则外层事务挂起。
  6. NEVER(never:从不): 上下层都不允许有事务,抛异常。
  7. NESTED(nested:嵌套的): A有事务B就嵌套在里面执行,A没事务B就自己创建执行。

REQUIRED和NESTED的区别就是:NESTED嵌套时A回滚B也回滚,B回滚A可以捕获异常而不回滚。REQUIRED加入时A回滚B也回滚,B回滚不能捕获异常而A也会回滚。

2. Spring三种注解注入方法

  1. 在属性上@Autowired等实现注入
    优点:简洁明了
    缺点:注入对象不能用final属性,可能导致循环依赖。

  2. 在构造方法上@Autowired等实现注入 (推荐)
    优点:强制注入防空指针异常,注入对象可以final,可能出现三级缓存不能解决的循环依赖(生命周期在第一步卡住,第三级缓存无法生效)
    缺点:注入过多很不好看

  3. 在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的几种方式

  1. 在xml中注册。
  2. @Configuration的类中方法上@Bean然后return对象注册。
  3. 在@Configuration上@Import({类.class})注册该类。
  4. 开启包扫描,扫描下的@Component等的类注册。
  5. 已注册的一个A类实现FactoryBean<B类>接口重写三个方法,(getObject,getObjectType,isSingleton)分别return B对应的类型属性,
    使用getBean(“A”)来获得getObject提供的对象也就是B,getBean(“&A”)则是获得A。
  6. 在注册的A类实现ImportSelector接口重写方法后return一个String数组,里面放的要注册的类的全限定名。

5. Spring事务实现机制

  1. @EnableTransactionManagement后使用@Tranceactional。
  2. @EnableTransactionManagement会注册配置类,配置类定义了对@Tranceactional使用通知,创建动态代理。
  3. 动态代理事务管理器新建数据库连接,放在ThreadLocal<Map<Datasource,connection>>中,设置自动提交为false,执行事务时会使用自己的数据源去ThreadLocal里面当做key去找连接,使用同一个连接统一提交或回滚事务。

6. Spring事务失效原理

前面提到,执行事务需要动态代理和同一数据源,大部分失效的原因就在于此。(分布式事务在Cloud系列说)

  • 方法private了。类不是bean没有被管理。在事务失败时自己捕获了异常没有处理。数据源不一致。
  • 在一个类中一个事务方法调用另一个事务方法,内部调用时默认使用this进行调用,使得代理对象并未参与导致事务失效。解决方法是:自己注入自己然后进行调用,此时就是使用代理对象进行调用了,或者使用AopContext.currentProxy()拿到代理对象来调用。

7. Spring的AOP三种使用方式

  1. 定义一个类并注册,在里面写好对应的before,after增强方法。在xml中可以指定这个类为切面,去对自己想增强的切点进行指定aop:before,aop:after等的增强。
  2. 定义n个类并注册,实现MethodBeforeAdvice,AfterReturningAdvice等接口重写其中的方法。在xml中通知到对应的切点里,通过接口来识别增强的时机进行方法增强。
  3. 定义一个类@Aspect并注册,在里面的方法上@Pointcut(),@Before(),@After(),@Around()对匹配到的切点进行方法增强。

8. cglib动态代理,jdk动态代理,AspectJ动态代理

  1. jdk动态代理:实现被代理类接口,方法里反射调原方法进行前后增强。
  2. cglib动态代理:继承被代理类,方法里直接调用原方法进行前后增强。(如果final不能被继承就代理不了)
  3. AspectJ代理:编译时在字节码层面对类方法前后进行增强,需要它的编译器。

Mybatis系列

Redis

RabbitMQ

SpringCloud系列

计算机网络和操作系统

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到