注解
注解概念
-
Annotation是从JDK5.0开始引入的新技术
-
Annotation的作用:
- 和注释一样,注解不是程序本身,而是对程序作出解释。
- 可以被其他程序(比如:编译器等)读取
-
Annotation在哪里使用?
- 可以附加在package、class、method、field等上面,相等于给他们添加了额外的辅助信息,然后结合反射机制实现对这些元数据的访问
内置注解
- @Override:定义在java.lang包中,此注解只适用于修饰方法,表示该方法打算重写父类中的同名方法,并且具有检查作用
- @Deprecated:定义在java.lang包中,此注释可以修饰方法、属性、类,不推荐使用注解,可以使用, 但是有风险或者有更好的方式。案例如下:
- @SupressWarnings:定义在java.lang包中,用来抑制编译时产生的黄色警告信息,虽然这些警告信息不会影响编译结果,但是看着不舒服,然后该注释和前两个注释有所不同,你需要添加一个参数才能正确使用,这些参数都是已经定义好了的,具体参数信息如下所示:
@SuppressWarnings(“all”) // 压制所有警告,下面有例子:
@SuppressWarnings(“unchecked”) // 压制"unchecked"警告
@SuppressWarnings(value={“unchecked”, “deprecation”}) // 压制"unchecked", "deprecation"警告
注意:public @interface SuppressWarnings {String[] value();}
,其中value是参数名,而String[]是参数是类型,当注解只使用value这一个参数的时候,value可以省略,例如上面的@SuppressWarnings("all")
和@SuppressWarnings("unchecked")
就是省略了前面的value,但是仅限参数名是value,并且注解中只使用value这一个参数的情况才可以 省略value
实际编码,不建议使用警告镇压注解,因为警告可以帮助我们识别程序的一些错误。
元注解
-
元注解作用:负责解释其他注解,Java中定义了4个标准的meta-annotation类型,他们被用来对其他注解类型进行解释说明。
-
元注解位置:在java.lang.annotation包中
- 元注解四大类型
- @Target:用于描述注解的使用范围,也就是描述注解可以使用在什么地方,
public @interface Target {ElementType[] value();}
可以看出可以它是一个数组,可以有多个值,里面的值设置的都是定义的注解MyAnnotation的使用范围,比如在类上、方法上等等,你可以通过@Target,然后点击里面的ElementType去查看
//测试元注解 public class Test(){ @MyAnnotation public void test(){ } } //定义一个元注解 @Target(value=ElementType.METHOD) @interface MyAnnotation{ }
- @Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(SOURCE < CLASS < RUNTIME),SOURCE是源码级别,CLASS 是源码级别和编译之后的字节码文件中有效,RUNTIME是在源码级别、编译之后的字节码文件、JVM中运行都有效,最常用的就是RUNTIME
- @Documented:说明该注解将被包含在javadoc文档中
- @Inherited:说明子类可以继承父类中的该注解
自定义注解
- 使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
- @interface 用来声明一个注解,格式是:
修饰符 @interface 注解名{定义内容}
- 内容中的每一个方法实际上是声明了一个配置参数,方法的名称就是参数的名称,方法的返回值类型就是参数的类型
- 返回值类型只能是基本类型、Class、String、enum,如果返回值是String[]的时候,赋值的时候要用{“X1”, “X2”}这种形式;
- 可以通过default来声明参数的默认值,声明默认值的可以不在自定义注解中赋值,反之必须赋值
- 如果只有一个参数成员,一般参数名使用value字段,这是因为当参数名称是value并且自定义注解中只需要写一个参数的时候,可以省略参数名称,只有value可以这样,其他的都不行
- 注解元素必须有值,我们定义注解元素时,经常使用空字符串、0、-1等作为默认值,当默认值为-1,代表不存在
public class Test01 {
//注解可以显示的赋值,如果没有默认值,我们就必须给注解赋值
@MyAnnotation(age=18,name="sdf")
public void test(){
}
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
String name() default "";
int age() default 0;
int id() default -1;
String[] schools() default {"sd","sd"};
}
反射
反射概述
动态语言vs静态语言
- 动态语言:通俗点说就是在运行时代码可以根据某些条件改变自身结构。
- 主要动态语言有:Object-C、C#、JavaScript、PHP、Python等。
反射概念
- Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息,并且能直接操作任意对象的内部属性以及方法
- 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构,这个对象就像一面镜子,透过这个镜子可以看到类的结构,这就是反射。
获取反射对象
一、反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
- ……
二、反射相关的主要API
- java.lang.Class:代表一个类,它就是Object类getClass()方法的返回值,它是唯一的所以类都指向它
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Constructor:代表类的构造器
- ……
三、反射初体验
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
// 通过反射返回类的Class对象,这里的Class就是Object类中getClass()方法的返回值Class是一样的,获取的c1是Class类的对象
Class c1 = Class.forName("com.atguigu.demo.User");
System.out.println(c1);
// 一个类在方法区中只有一个Class对象
// 一个类被加载之后,类的整个结构(构造器、方法、属性等等)都会被封装在Class对象中
Class c2 = Class.forName("com.atguigu.demo.User");
System.out.println(c1.hashCode());
System.out.println(c2.hashCode()); //hashCode值相等,说明是同一个对象
}
}
@Data
class User{
private Integer id;
private String name;
private Integer age;
}
结果:
class com.atguigu.demo.User
1066516207
1066516207
四、Class类详解
得到Class类的几种方式
一、Class类
二、Class类的常用方法
三、获取Class类的实例
- 通过类的class属性获取,该方法最为安全可靠,程序性能最高
Class c1 = User.class;
- 通过类的实例中的getClass()方法获取
User user =new User();
Class c2 = user.getClass();
- 通过类的全限定类名获取
Class c3 = Class.forName("com.atguigu.demo.User");
- 通过基本内置类型的包装类的Type属性(了解)
Class c4 = Integer.TYPE;
- 通过ClassLoader类加载器获取
Constructor constructor = c1.getDeclaredConstructor(String.class);
Class c5 = constructor.getDeclaringClass();
四、测试+提升
代码:
public class Test {
public static void main(String[] args) throws Exception {
// 普通new创建对象
User student = new Student("mingming");
System.out.println("这个人是:" + student.name);
// 方式1:通过类的class属性获取,该方法最为安全可靠,程序性能最高(获取Student的Class对象)
Class c1 = Student.class;
System.out.println(c1.hashCode());
// 方式2:通过类的实例中的getClass()方法获取(获取Student的Class对象)
Class c2 = student.getClass();
System.out.println(c2.hashCode());
// 方式3:通过类的全限定类名获取(获取Student的Class对象)
Class c3 = Class.forName("com.atguigu.demo.Student");
System.out.println(c3.hashCode());
// 方式4:通过基本内置类型的包装类的Type属性(了解)
Class c4 = Integer.TYPE;
// 方法5:通过ClassLoader类加载器获取
Constructor constructor = c1.getDeclaredConstructor(String.class);
Class c5 = constructor.getDeclaringClass();
System.out.println(c5.hashCode());
// 获取父类类型
Class c6 = c1.getSuperclass();
System.out.println(c6);
// 获取Student对象
Student s1 = (Student) c1.newInstance(); // 本质调用了类的无参构造器
System.out.println(s1);
// 使用构造器创建Student对象
Student s2 = (Student) constructor.newInstance("mingming");
System.out.println(s2);
// 通过反射调用普通方法
// 其中hello是方法名称,String.class代表方法需要的参数的Class对象,c1是Student类对应的Class对象,s1代表Student类的实例对象,“tiantian”代表具体的参数值
Method method = c1.getDeclaredMethod("hello", String.class);
method.invoke(s1, "tiantian");
// 通过反射给属性赋值
Field age = c1.getDeclaredField("age");
// 无论是调用私有方法还是给私有属性赋值,都需要关闭程序的安全监测,才能调用方法或者设置属性
age.setAccessible(true);
age.set(s1,12);
System.out.println(s1.getAge());
}
}
class User {
public String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Student extends User {
private Integer age;
public Student() {
}
public Student(String name) {
this.name = name;
}
public void hello(String str) {
System.out.println("Hello!!! " + str);
}
public Integer getAge(){
return this.age;
}
}
结果:
这个人是:mingming
1066516207
1066516207
1066516207
1066516207
class com.atguigu.demo.User
com.atguigu.demo.Student@1a6c5a9e
com.atguigu.demo.Student@37bba400
Hello!!! tiantian
12
所有类型的Class对象
一、具有CLass对象的类型
// 1.class:外部类、成员内部类和静态内部类、局部内部类、匿名内部类
Class c1 = Object.class;
//2.interface:接口
Class c2 = Comparable.class;
//3.[]:数组
Class c3 = String[].class;
//4.enum:枚举
Class c5 = ElementType.class;
//5.annotation:注解
Class c4 = Override.class;
//6.基本内置类型的包装类,例如Integer等
Class c6 =Integer.class;
//7.void
Class c7 = void.class;
//8.Class类
Class c8 =Class.class;
二、元素类型、维度和Class对象的关系
例子:
public class Test {
public static void main(String[] args) {
Class c1 = int[].class; // 一维整型数组
Class c2 = int[][].class; // 二维整型数组
Class c3 = String[].class; // 一维字符串数组
int[] i1 = new int[10]; // 初值容量为10的一维整型数组
int[] i2 = new int[100]; // 初始容量为100的一维整型数组
System.out.println("一维整型数组:" + c1.hashCode());
System.out.println("二维整型数组:" + c2.hashCode());
System.out.println("一维字符串数组:" +c3.hashCode());
System.out.println("初值容量为10的一维整型数组:" + i1.getClass().hashCode());
System.out.println("初始容量为100的一维整型数组:" + i2.getClass().hashCode());
}
}
结果:
一维整型数组:312714112
二维整型数组:692404036
一维字符串数组:1554874502
初值容量为10的一维整型数组:312714112
初始容量为100的一维整型数组:312714112
结论:
- 元素类型不同,Class对象不同,例如int[ ]、String[ ]
- 元素类型相同,但是维度不同,Class对象不同,例如int[ ]和int[ ][ ]
- 元素类型相同,维度相同,但是具体内容不同,Class对象相同,例如:int[10]和int[100]
类加载
类加载内存分析
java内存分析
类的加载过程
类的加载与ClassLoader的理解
实例代码:
public class Test03 {
public static void main(String[] args) {
A a =new A();
System.out.println(A.m);
/**
* 1.加载到内存,会产生一个类对应的class对象
* 2.链接,链接结束后 m=0
* 3.初始化:<client>(){
* System.out.println("A类静态代码块初始化");
* m=300;
* m=100;
* }
*/
}
}
class A{
static {
System.out.println("A类静态代码块初始化");
m=300;
}
static int m =100;
public A() {
System.out.println("A的无参构造初始化");
}
}
结果:
A类静态代码块初始化
A的无参构造初始化
100
如图所示:
分析类初始化
什么时候会发生类初始化
示例代码:
public class Test04 {
static {
System.out.println("Main类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
//1.主动引用
Son son =new Son();
//反射也会产生主动引用
Class.forName("com.atxins.idea.annotation.Son");
//不会产生类的引用的方法
System.out.println(Son.b);
//new一个数组
Son[] array =new Son[5];
//引用常量
System.out.println(Son.M);
}
}
class Father{
static int b=1;
static {
System.out.println("父类被加载");
}
}
class Son extends Father{
static {
System.out.println("子类被加载");
m=300;
}
static int m=100;
static final int M=1;
}
结果:
1.Main类被加载
父类被加载
子类被加载
2.Main类被加载
父类被加载
子类被加载
3.Main类被加载
父类被加载
1
4.Main类被加载
5.Main类被加载
1
类加载器
类加载器的作用
演示代码:
public class Test05 {
public static void main(String[] args) {
//获取系统类的加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//获取系统类加载器的父类加载器-->扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
//获取扩展类加载器的父类加载器-->根加载器(c/c++)
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);
}
}
结果:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@12a3a380
null
sun.misc.Launcher$AppClassLoader@18b4aac2
null
C:\Program Files\Java\jdk1.8.0_201\jre\lib\charsets.jar;
C:\Program Files\Java\jdk1.8.0_201\jre\lib\deploy.jar;
F:\WorkSpace_idea\练习\IdeaTest\target\classes;
....
D:\maven-repository\org\springframework\spring-expression\5.1.9.RELEASE\spring-expression-5.1.9.RELEASE.jar;
D:\maven-repository\commons-logging\commons-logging\1.2\commons-logging-1.2.jar;D:\Software\IntelliJ IDEA 2019.1.3\lib\idea_rt.jar
获取类运行时结构
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1 = Class.forName("com.atxins.idea.annotation.User");
//通过外部传入获得类的名字
System.out.println(c1.getName());//获得包名+类名 com.atxins.idea.annotation.User
System.out.println(c1.getSimpleName());//获得类名 User
//通过对象获得类名
User user =new User();
System.out.println(user.getClass().getName());
System.out.println(user.getClass().getSimpleName());
//获取类的属性
Arrays.stream(c1.getFields()).forEach(System.out::println);//只能找到public属性
Arrays.stream(c1.getDeclaredFields()).forEach(System.out::println);//找到全部的属性
//获得指定的属性
Field name = c1.getDeclaredField("name");
System.out.println(name);
//获得类的方法
Arrays.stream(c1.getMethods()).forEach(System.out::println);//获得本类及其父类的全部public方法
Arrays.stream(c1.getDeclaredMethods()).forEach(System.out::println);//获得本类的所有方法
//获得指定的方法
Method getName = c1.getMethod("getName", null);//无参
System.out.println(getName);
System.out.println(c1.getMethod("setName", String.class));//有参,需要参数涉及重载
//获得指定的构造器
Arrays.stream(c1.getConstructors()).forEach(System.out::println);//找到含有public的构造器
Arrays.stream(c1.getDeclaredConstructors()).forEach(System.out::println);//找到全部的构造器
//获得指定的构造器
System.out.println(c1.getConstructor(String.class, int.class, int.class));
}
运行结果:
com.atxins.idea.annotation.User
User
----------------------------------------
com.atxins.idea.annotation.User
User
----------------------------------------
private java.lang.String com.atxins.idea.annotation.User.name
private int com.atxins.idea.annotation.User.id
private int com.atxins.idea.annotation.User.age
----------------------------------------
private java.lang.String com.atxins.idea.annotation.User.name
----------------------------------------
public java.lang.String com.atxins.idea.annotation.User.toString()
public java.lang.String com.atxins.idea.annotation.User.getName()
public int com.atxins.idea.annotation.User.getId()
public void com.atxins.idea.annotation.User.setName(java.lang.String)
public void com.atxins.idea.annotation.User.setAge(int)
public void com.atxins.idea.annotation.User.setId(int)
public int com.atxins.idea.annotation.User.getAge()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
----------------------------------------
public java.lang.String com.atxins.idea.annotation.User.toString()
public java.lang.String com.atxins.idea.annotation.User.getName()
public int com.atxins.idea.annotation.User.getId()
public void com.atxins.idea.annotation.User.setName(java.lang.String)
private void com.atxins.idea.annotation.User.test()
public void com.atxins.idea.annotation.User.setAge(int)
public void com.atxins.idea.annotation.User.setId(int)
public int com.atxins.idea.annotation.User.getAge()
----------------------------------------
public java.lang.String com.atxins.idea.annotation.User.getName()
----------------------------------------
public void com.atxins.idea.annotation.User.setName(java.lang.String)
----------------------------------------
public com.atxins.idea.annotation.User()
public com.atxins.idea.annotation.User(java.lang.String,int,int)
----------------------------------------
public com.atxins.idea.annotation.User()
public com.atxins.idea.annotation.User(java.lang.String,int,int)
----------------------------------------
public com.atxins.idea.annotation.User(java.lang.String,int,int)
动态创建对象执行方法
有了Class对象,可以做什么?
调用指定的方法
setAccessible
实例代码:
//动态的创建对象,通过反射
public class Test07 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
//获得class对象
Class c1 = Class.forName("com.atxins.idea.annotation.User");
//构造一个对象
User user = (User) c1.newInstance();//本质是调用了类的无参构造器
System.out.println(user);
//通过构造器创建对象
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
User user1 = (User) constructor.newInstance("axin", 001, 12);
System.out.println(user1);
//通过反射获取一个方法
//invoke激活的意思(对象,"方法的值")
Method setName = c1.getDeclaredMethod("setName", String.class);
setName.invoke(user,"axin最帅");
System.out.println(user.getName());
//通过反射操作属性
Field name = c1.getDeclaredField("name");
//不能直接操作私有属性,我们需要关闭程序的安全检测,属性或方法的setAccessible(true)
name.setAccessible(true);
name.set(user,"axin依旧帅");
System.out.println(user.getName());
}
}
结果:
User{name='null', id=0, age=0}
User{name='axin', id=1, age=12}
axin最帅
axin依旧帅
性能对比分析
//分析性能问题
public class Test08 {
//普通方式调用
public static void test(){
User user =new User();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
user.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方式执行10亿次"+(endTime-startTime)+"ms");
}
//反射方式调用
public static void test2() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
User user =new User();
Class c1 = user.getClass();
Method getName = c1.getDeclaredMethod("getName", null);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user, null);
}
long endTime = System.currentTimeMillis();
System.out.println("反射方式执行10亿次"+(endTime-startTime)+"ms");
}
//反射方式调用 关闭检测
public static void test3() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
User user =new User();
Class c1 = user.getClass();
Method getName = c1.getDeclaredMethod("getName", null);
getName.setAccessible(true);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user, null);
}
long endTime = System.currentTimeMillis();
System.out.println("关闭检测方式执行10亿次"+(endTime-startTime)+"ms");
}
public static void main(String[] args) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
test();
test2();
test3();
}
}
获取泛型信息
反射操作泛型
//通过反射获取泛型
public class Test09 {
public void test01(Map<String,User> map, List<User> list){
System.out.println("test01");
}
public Map<String,User> test02(){
System.out.println("test02");
return null;
}
public static void main(String[] args) throws NoSuchMethodException {
Method method = Test09.class.getDeclaredMethod("test01", Map.class, List.class);
Type[] genericParameterTypes = method.getGenericParameterTypes();//获得泛型的参数信息
for (Type genericParameterType : genericParameterTypes) {
if(genericParameterType instanceof ParameterizedType){//泛型的参数类型是否属于参数化类型
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();//获得真实类型
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
method=Test09.class.getDeclaredMethod("test02",null);
Type genericReturnType = method.getGenericReturnType();//获得返回值是泛型
if(genericReturnType instanceof ParameterizedType){//泛型的参数类型是否属于参数化类型
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();//获得真实类型
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
}
结果:
class java.lang.String
class com.atxins.idea.annotation.User
class com.atxins.idea.annotation.User
--------------------------
class java.lang.String
class com.atxins.idea.annotation.User
反射操作注解
//反射操作注解
public class Test10 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("com.atxins.idea.annotation.Student2");
Annotation[] annotations = c1.getAnnotations();
Arrays.stream(annotations).forEach(System.out::println);
//获取注解的value的值
Tableaxin tableaxin = (Tableaxin)c1.getAnnotation(Tableaxin.class);
System.out.println(tableaxin.value());
//获得属性注解的值
Field f = c1.getDeclaredField("name");
Filedaxin annotation = f.getAnnotation(Filedaxin.class);
System.out.println(annotation.columeName());
System.out.println(annotation.type());
System.out.println(annotation.length());
}
}
@Tableaxin("db_student")
class Student2{
@Filedaxin(columeName = "db_id",type = "int",length = 10)
private int id;
@Filedaxin(columeName = "db_age",type = "int",length = 10)
private int age;
@Filedaxin(columeName = "db_name",type = "varchar",length = 3)
private String name;
public Student2() {
}
public Student2(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student2{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Tableaxin{
String value();
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Filedaxin{
String columeName();
String type();
int length();
}