知其然不知其所以然,大厂常问面试技术如何复习?
1、热门面试题及答案大全
面试前做足功夫,让你面试成功率提升一截,这里一份热门350道一线互联网常问面试题及答案助你拿offer
2、多线程、高并发、缓存入门到实战项目pdf书籍
3、文中提到面试题答案整理
4、Java核心知识面试宝典
覆盖了JVM 、JAVA集合、JAVA多线程并发、JAVA基础、Spring原理、微服务、Netty与RPC、网络、日志、Zookeeper、Kafka、RabbitMQ、Hbase、MongoDB 、Cassandra、设计模式、负载均衡、数据库、一致性算法 、JAVA算法、数据结构、算法、分布式缓存、Hadoop、Spark、Storm的大量技术点且讲解的非常深入
一个参数为value的注解,可以省略参数名称
只有一个参数,名称为value的时候,使用时参数名称可以省略
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Ann3 {
String value();//@1
}
@Ann3(“我是路人甲java”) //@2
public class UseAnnotation3 {
}
@1:注解之后一个参数,名称为value
@2:使用注解,参数名称value省略了
数组类型参数
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Ann4 {
String[] name();//@1
}
@Ann4(name = {“我是路人甲java”, “欢迎和我一起学spring”}) //@2
public class UseAnnotation4 {
@Ann4(name = “如果只有一个值,{}可以省略”) //@3
public class T1 {
}
}
@1:name的类型是一个String类型的数组
@2:name有多个值的时候,需要使用{}包含起来
@3:如果name只有一个值,{}可以省略
为参数指定默认值
通过default为参数指定默认值,用的时候如果没有设置值,则取默认值,没有指定默认值的参数,使用的时候必须为参数设置值,如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Ann5 {
String[] name() default {“路人甲java”, “spring系列”};//@1
int[] score() default 1; //@2
int age() default 30; //@3
String address(); //@4
}
@Ann5(age = 32,address = “上海”) //@5
public class UseAnnotation5 {
}
@1:数组类型通过{}指定默认值
@2:数组类型参数,默认值只有一个省略了{}符号
@3:默认值为30
@4:未指定默认值
@5:age=32对默认值进行了覆盖,并且为address指定了值
综合案例
@Target(value = {
ElementType.TYPE,
ElementType.METHOD,
ElementType.FIELD,
ElementType.PARAMETER,
ElementType.CONSTRUCTOR,
ElementType.LOCAL_VARIABLE
})
@Retention(RetentionPolicy.RUNTIME)
@interface Ann6 {
String value();
ElementType elementType();
}
@Ann6(value = “我用在类上”, elementType = ElementType.TYPE)
public class UseAnnotation6 {
@Ann6(value = “我用在字段上”, elementType = ElementType.FIELD)
private String a;
@Ann6(value = “我用在构造方法上”, elementType = ElementType.CONSTRUCTOR)
public UseAnnotation6(@Ann6(value = “我用在方法参数上”, elementType = ElementType.PARAMETER) String a) {
this.a = a;
}
@Ann6(value = “我用在了普通方法上面”, elementType = ElementType.METHOD)
public void m1() {
@Ann6(value = “我用在了本地变量上”, elementType = ElementType.LOCAL_VARIABLE) String a;
}
}
上面演示了自定义注解在在类、字段、构造器、方法参数、方法、本地变量上的使用,@Ann6注解有个
elementType
参数,我想通过这个参数的值来告诉大家对应@Target中的那个值来限制使用目标的,大家注意一下上面每个elementType
的值。
@Target(ElementType.TYPE_PARAMETER)
这个是1.8加上的,用来标注类型参数,类型参数一般在类后面声明或者方法上声明,这块需要先了解一下泛型泛型,有点难度,会让很多人懵逼,那是因为你没有看这篇文章!不然理解起来比较吃力,来个案例感受一下:
@Target(value = {
ElementType.TYPE_PARAMETER
})
@Retention(RetentionPolicy.RUNTIME)
@interface Ann7 {
String value();
}
public class UseAnnotation7<@Ann7(“T0是在类上声明的一个泛型类型变量”) T0, @Ann7(“T1是在类上声明的一个泛型类型变量”) T1> {
public <@Ann7(“T2是在方法上声明的泛型类型变量”) T2> void m1() {
}
public static void main(String[] args) throws NoSuchMethodException {
for (TypeVariable typeVariable : UseAnnotation7.class.getTypeParameters()) {
print(typeVariable);
}
for (TypeVariable typeVariable : UseAnnotation7.class.getDeclaredMethod(“m1”).getTypeParameters()) {
print(typeVariable);
}
}
private static void print(TypeVariable typeVariable) {
System.out.println(“类型变量名称:” + typeVariable.getName());
Arrays.stream(typeVariable.getAnnotations()).forEach(System.out::println);
}
}
类和方法上面可以声明泛型类型的变量,上面有3个泛型类型变量,我们运行一下看看效果:
类型变量名称:T0
@com.javacode2018.lesson001.demo18.Ann7(value=T0是在类上声明的一个泛型类型变量)
类型变量名称:T1
@com.javacode2018.lesson001.demo18.Ann7(value=T1是在类上声明的一个泛型类型变量)
类型变量名称:T2
@com.javacode2018.lesson001.demo18.Ann7(value=T2是在方法上声明的泛型类型变量)
@Target(ElementType.TYPE_USE)
这个是1.8加上的,能用在任何类型名称上,来个案例感受一下:
@Target({ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@interface Ann10 {
String value();
}
@Ann10(“用在了类上”)
public class UserAnnotation10<@Ann10(“用在了类变量类型V1上”) V1, @Ann10(“用在了类变量类型V2上”) V2> {
private Map<@Ann10(“用在了泛型类型上”) String, Integer> map;
public <@Ann10(“用在了参数上”) T> String m1(String name) {
return null;
}
}
类后面的V1、V2都是类型名称,Map后面的尖括号也是类型名称,m1方法前面也定义了一个类型变量,名称为T
注解信息的获取
为了运行时能准确获取到注解的相关信息,Java在java.lang.reflect
反射包下新增了AnnotatedElement
接口,它主要用于表示目前正在虚拟机中运行的程序中已使用注解的元素,通过该接口提供的方法可以利用反射技术地读取注解的信息,看一下UML图:
-
Package:用来表示包的信息
-
Class:用来表示类的信息
-
Constructor:用来表示构造方法信息
-
Field:用来表示类中属性信息
-
Method:用来表示方法信息
-
Parameter:用来表示方法参数信息
-
TypeVariable:用来表示类型变量信息,如:类上定义的泛型类型变量,方法上面定义的泛型类型变量
AnnotatedElement常用方法
案例
要解析的列如下
package com.javacode2018.lesson001.demo18;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Map;
@Target({ElementType.PACKAGE,
ElementType.TYPE,
ElementType.FIELD,
ElementType.CONSTRUCTOR,
ElementType.METHOD,
ElementType.PARAMETER,
ElementType.TYPE_PARAMETER,
ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@interface Ann11 {
String value();
}
@Target({ElementType.PACKAGE,
ElementType.TYPE,
ElementType.FIELD,
ElementType.CONSTRUCTOR,
ElementType.METHOD,
ElementType.PARAMETER,
ElementType.TYPE_PARAMETER,
ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@interface Ann11_0 {
int value();
}
@Ann11(“用在了类上”)
@Ann11_0(0)
public class UseAnnotation11<@Ann11(“用在了类变量类型V1上”) @Ann11_0(1) V1, @Ann11(“用在了类变量类型V2上”) @Ann11_0(2) V2> {
@Ann11(“用在了字段上”)
@Ann11_0(3)
private String name;
private Map<@Ann11(“用在了泛型类型上,String”) @Ann11_0(4) String, @Ann11(“用在了泛型类型上,Integer”) @Ann11_0(5) Integer> map;
@Ann11(“用在了构造方法上”)
@Ann11_0(6)
public UseAnnotation11() {
this.name = name;
}
@Ann11(“用在了返回值上”)
@Ann11_0(7)
public String m1(@Ann11(“用在了参数上”) @Ann11_0(8) String name) {
return null;
}
}
解析类上的注解
解析这部分
@Ann11(“用在了类上”)
代码
@Test
public void m1() {
for (Annotation annotation : UserAnnotation10.class.getAnnotations()) {
System.out.println(annotation);
}
}
运行输出
@com.javacode2018.lesson001.demo18.Ann11(value=用在了类上)
@com.javacode2018.lesson001.demo18.Ann11_0(value=0)
解析类上的类型变量
解析类名后面的尖括号的部分,即下面的部分:
UseAnnotation11<@Ann11(“用在了类变量类型V1上”) @Ann11_0(1) V1, @Ann11(“用在了类变量类型V2上”) @Ann11_0(2) V2>
用例代码
@Test
public void m2() {
TypeVariable<Class>[] typeParameters = UserAnnotation10.class.getTypeParameters();
for (TypeVariable<Class> typeParameter : typeParameters) {
System.out.println(typeParameter.getName() + “变量类型注解信息:”);
Annotation[] annotations = typeParameter.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
}
}
运行输出
V1变量类型注解信息:
@com.javacode2018.lesson001.demo18.Ann11(value=用在了类变量类型V1上)
@com.javacode2018.lesson001.demo18.Ann11_0(value=1)
V2变量类型注解信息:
@com.javacode2018.lesson001.demo18.Ann11(value=用在了类变量类型V2上)
@com.javacode2018.lesson001.demo18.Ann11_0(value=2)
解析字段name上的注解
用例代码
@Test
public void m3() throws NoSuchFieldException {
Field nameField = UserAnnotation10.class.getDeclaredField(“name”);
for (Annotation annotation : nameField.getAnnotations()) {
System.out.println(annotation);
}
}
运行输出
@com.javacode2018.lesson001.demo18.Ann11(value=用在了字段上)
@com.javacode2018.lesson001.demo18.Ann11_0(value=3)
解析泛型字段map上的注解
用例代码
@Test
public void m4() throws NoSuchFieldException, ClassNotFoundException {
Field field = UseAnnotation11.class.getDeclaredField(“map”);
Type genericType = field.getGenericType();
Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments();
AnnotatedType annotatedType = field.getAnnotatedType();
AnnotatedType[] annotatedActualTypeArguments = ((AnnotatedParameterizedType) annotatedType).getAnnotatedActualTypeArguments();
int i = 0;
for (AnnotatedType actualTypeArgument : annotatedActualTypeArguments) {
Type actualTypeArgument1 = actualTypeArguments[i++];
System.out.println(actualTypeArgument1.getTypeName() + “类型上的注解如下:”);
for (Annotation annotation : actualTypeArgument.getAnnotations()) {
System.out.println(annotation);
}
}
}
运行输出
java.lang.String类型上的注解如下:
@com.javacode2018.lesson001.demo18.Ann11(value=用在了泛型类型上,String)
@com.javacode2018.lesson001.demo18.Ann11_0(value=4)
java.lang.Integer类型上的注解如下:
@com.javacode2018.lesson001.demo18.Ann11(value=用在了泛型类型上,Integer)
@com.javacode2018.lesson001.demo18.Ann11_0(value=5)
解析构造函数上的注解
用例代码
@Test
public void m5() {
Constructor<?> constructor = UseAnnotation11.class.getConstructors()[0];
for (Annotation annotation : constructor.getAnnotations()) {
System.out.println(annotation);
}
}
运行输出
@com.javacode2018.lesson001.demo18.Ann11(value=用在了构造方法上)
@com.javacode2018.lesson001.demo18.Ann11_0(value=6)
解析m1方法上的注解
用例代码
@Test
public void m6() throws NoSuchMethodException {
Method method = UseAnnotation11.class.getMethod(“m1”, String.class);
for (Annotation annotation : method.getAnnotations()) {
System.out.println(annotation);
}
}
运行输出
@com.javacode2018.lesson001.demo18.Ann11(value=用在了返回值上)
@com.javacode2018.lesson001.demo18.Ann11_0(value=7)
解析m1方法参数注解
用例代码
@Test
public void m7() throws NoSuchMethodException {
Method method = UseAnnotation11.class.getMethod(“m1”, String.class);
for (Parameter parameter : method.getParameters()) {
System.out.println(String.format(“参数%s上的注解如下:”, parameter.getName()));
for (Annotation annotation : parameter.getAnnotations()) {
System.out.println(annotation);
}
}
}
运行输出
参数arg0上的注解如下:
@com.javacode2018.lesson001.demo18.Ann11(value=用在了参数上)
@com.javacode2018.lesson001.demo18.Ann11_0(value=8)
上面参数名称为arg0,如果想让参数名称和源码中真实名称一致,操作如下:
如果你编译这个class的时候没有添加参数–parameters,运行的时候你会得到这个结果:
Parameter: arg0
编译的时候添加了–parameters参数的话,运行结果会不一样:
Parameter: args
对于有经验的Maven使用者,–parameters参数可以添加到maven-compiler-plugin的配置部分:
org.apache.maven.plugins
maven-compiler-plugin
3.1
-parameters
1.81.8
@Inherit:实现类之间的注解继承
用法
来看一下这个注解的源码
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
我们通过@Target元注解的属性值可以看出,这个@Inherited 是专门修饰注解的。
作用:让子类可以继承父类中被@Inherited修饰的注解,注意是继承父类中的,如果接口中的注解也使用@Inherited修饰了,那么接口的实现类是无法继承这个注解的
案例
package com.javacode2018.lesson001.demo18;
import java.lang.annotation.*;
public class InheritAnnotationTest {
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface A1{ //@1
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface A2{ //@2
}
@A1 //@3
interface I1{}
@A2 //@4
static class C1{}
static class C2 extends C1 implements I1{} //@5
public static void main(String[] args) {
for (Annotation annotation : C2.class.getAnnotations()) { //@6
System.out.println(annotation);
}
}
}
@1:定义了一个注解A1,上面使用了@Inherited,表示这个具有继承功能
@2:定义了一个注解A2,上面使用了@Inherited,表示这个具有继承功能
@3:定义接口I1,上面使用了@A1注解
@4:定义了一个C1类,使用了A2注解
@5:C2继承了C1并且实现了I1接口
@6:获取C2上以及从父类继承过来的所有注解,然后输出
运行输出:
@com.javacode2018.lesson001.demo18.InheritAnnotationTest$A2()
从输出中可以看出类可以继承父类上被@Inherited修饰的注解,而不能继承接口上被@Inherited修饰的注解,这个一定要注意
@Repeatable重复使用注解
来看一段代码:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Ann12{}
@Ann12
@Ann12
public class UseAnnotation12 {
}
上面代码会报错,原因是:UseAnnotation12上面重复使用了@Ann12注解,默认情况下@Ann12注解是不允许重复使用的。
像上面这样,如果我们想重复使用注解的时候,需要用到@Repeatable
注解
使用步骤
先定义容器注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
@interface Ann12s {
Ann12[] value(); //@1
}
容器注解中必须有个value类型的参数,参数类型为子注解类型的数组。
为注解指定容器
要让一个注解可以重复使用,需要在注解上加上@Repeatable注解,@Repeatable中value的值为容器注解,如下代码中的@2
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
@Repeatable(Ann12s.class)//@2
@interface Ann12 {
String name();
}
使用注解
重复使用相同的注解有2种方式,如下面代码
-
重复使用注解,如下面的类上重复使用@Ann12注解
-
通过容器注解来使用更多个注解,如下面的字段v1上使用@Ann12s容器注解
@Ann12(name = “路人甲Java”)
@Ann12(name = “Spring系列”)
public class UseAnnotation12 {
@Ann12s(
{@Ann12(name = “Java高并发系列,见公众号”),
@Ann12(name = “mysql高手系列,见公众号”)}
)
private String v1;
}
获取注解信息
com.javacode2018.lesson001.demo18.UseAnnotation12
@Test
public void test1() throws NoSuchFieldException {
Annotation[] annotations = UseAnnotation12.class.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
System.out.println(“-------------”);
Field v1 = UseAnnotation12.class.getDeclaredField(“v1”);
Annotation[] declaredAnnotations = v1.getDeclaredAnnotations();
for (Annotation declaredAnnotation : declaredAnnotations) {
System.out.println(declaredAnnotation);
}
}
运行输出:
@com.javacode2018.lesson001.demo18.Ann12s(value=[@com.javacode2018.lesson001.demo18.Ann12(name=路人甲Java), @com.javacode2018.lesson001.demo18.Ann12(name=Spring系列)])
@com.javacode2018.lesson001.demo18.Ann12s(value=[@com.javacode2018.lesson001.demo18.Ann12(name=Java高并发系列,见公众号), @com.javacode2018.lesson001.demo18.Ann12(name=mysql高手系列,见公众号)])
上面就是java中注解的功能,下面我们来介绍spring对于注解方面的支持。
先来看一个问题
代码如下:
package com.javacode2018.lesson001.demo18;
import org.junit.Test;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface A1 {
String value() default “a”;//@0
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@A1
@interface B1 { //@1
String value() default “b”;//@2
}
@B1(“路人甲Java”) //@3
public class UseAnnotation13 {
@Test
public void test1() {
//AnnotatedElementUtils是spring提供的一个查找注解的工具类
System.out.println(AnnotatedElementUtils.getMergedAnnotation(UseAnnotation13.class, B1.class));
System.out.println(AnnotatedElementUtils.getMergedAnnotation(UseAnnotation13.class, A1.class));
}
}
@0:A1注解value参数值默认为a
@1:B1注解上使用到了@A1注解
@2:B1注解value参数值默认为b
@2:UseAnnotation13上面使用了@B1注解,value参数的值为:路人甲java
test1方法中使用到了spring中的一个类
AnnotatedElementUtils
,通过这个工具类可以很方便的获取注解的各种信息,方法中的2行代码用于获取UseAnnotation13类上B1注解和A1注解的信息。
运行test1方法输出:
@com.javacode2018.lesson001.demo18.B1(value=路人甲Java)
@com.javacode2018.lesson001.demo18.A1(value=a)
上面用法很简单,没什么问题。
此时有个问题:此时如果想在UseAnnotation13
上给B1上的A1注解设置值是没有办法的,注解定义无法继承导致的,如果注解定义上面能够继承,那用起来会爽很多,spring通过@Aliasfor方法解决了这个问题。
Spring @AliasFor:对注解进行增强
直接上案例,然后解释代码。
案例1:通过@AliasFor解决刚才难题
感受:
其实我投简历的时候,都不太敢投递阿里。因为在阿里一面前已经过了字节的三次面试,投阿里的简历一直没被捞,所以以为简历就挂了。
特别感谢一面的面试官捞了我,给了我机会,同时也认可我的努力和态度。对比我的面经和其他大佬的面经,自己真的是运气好。别人8成实力,我可能8成运气。所以对我而言,我要继续加倍努力,弥补自己技术上的不足,以及与科班大佬们基础上的差距。希望自己能继续保持学习的热情,继续努力走下去。
也祝愿各位同学,都能找到自己心动的offer。
分享我在这次面试前所做的准备(刷题复习资料以及一些大佬们的学习笔记和学习路线),都已经整理成了电子文档
ass UseAnnotation13 {
@Test
public void test1() {
//AnnotatedElementUtils是spring提供的一个查找注解的工具类
System.out.println(AnnotatedElementUtils.getMergedAnnotation(UseAnnotation13.class, B1.class));
System.out.println(AnnotatedElementUtils.getMergedAnnotation(UseAnnotation13.class, A1.class));
}
}
@0:A1注解value参数值默认为a
@1:B1注解上使用到了@A1注解
@2:B1注解value参数值默认为b
@2:UseAnnotation13上面使用了@B1注解,value参数的值为:路人甲java
test1方法中使用到了spring中的一个类
AnnotatedElementUtils
,通过这个工具类可以很方便的获取注解的各种信息,方法中的2行代码用于获取UseAnnotation13类上B1注解和A1注解的信息。
运行test1方法输出:
@com.javacode2018.lesson001.demo18.B1(value=路人甲Java)
@com.javacode2018.lesson001.demo18.A1(value=a)
上面用法很简单,没什么问题。
此时有个问题:此时如果想在UseAnnotation13
上给B1上的A1注解设置值是没有办法的,注解定义无法继承导致的,如果注解定义上面能够继承,那用起来会爽很多,spring通过@Aliasfor方法解决了这个问题。
Spring @AliasFor:对注解进行增强
直接上案例,然后解释代码。
案例1:通过@AliasFor解决刚才难题
感受:
其实我投简历的时候,都不太敢投递阿里。因为在阿里一面前已经过了字节的三次面试,投阿里的简历一直没被捞,所以以为简历就挂了。
特别感谢一面的面试官捞了我,给了我机会,同时也认可我的努力和态度。对比我的面经和其他大佬的面经,自己真的是运气好。别人8成实力,我可能8成运气。所以对我而言,我要继续加倍努力,弥补自己技术上的不足,以及与科班大佬们基础上的差距。希望自己能继续保持学习的热情,继续努力走下去。
也祝愿各位同学,都能找到自己心动的offer。
分享我在这次面试前所做的准备(刷题复习资料以及一些大佬们的学习笔记和学习路线),都已经整理成了电子文档
[外链图片转存中…(img-jKIOwmCB-1715414743581)]