注解
内置注解
- @Oreride:定义在java.lang.Override中,此注释只适用于修辞方法,表示一个方法声明打算重写超类中的另一个方法声明。
- @Deprecated:定义在java.lang.Deprecated中,此注释可以用于修辞方法,属性,类,表示不鼓励程序员使用这样的元素,通常式因为它很危险或者存在更好的选择。
- @SupperessWarnings:定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息。
与前两个注释有所不同,你需要添加一个参数才能正确使用,这些参数都是已经定义好了的。我们选择性使用就好了。
- @SuppressWarnings("all")
- @SuppressWarnings("unchecked")
- @SuppressWarnings(value={"unchecked","deprecation"})
- 等等
元注解
- 元注解的作用就是负责注解其他注解,Java定义了4个标准的
meta-annotation
类型,他们被用来提供对其他annotation
类型作说明.
- 这些类型和他们锁职材的类在
java.lang.annotation
包中可以找到(@Target,@Retention,@Document,@Inherited)
- @Target:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
- @Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期
- @Documented:说明该注解被包含在javacoc中
- @Inherited:说明子类可以继承父类中的该注解
代码示例
| package com.sanduo.annotation.test; |
| import java.lang.annotation.*; |
| |
| @MyAnnotation |
| public class Test02 { |
| @MyAnnotation |
| public void test(){ |
| |
| } |
| } |
| |
| @Retention(RetentionPolicy.RUNTIME) |
| @Target(value = {ElementType.METHOD,ElementType.TYPE}) |
| @Documented |
| @Inherited |
| @interface MyAnnotation{} |
自定义注解
- 使用
@interface
自定义注解,自动继承了import java.lang.annotation.Annotation;
接口
- 分析:
@ interface
用来声明一个注解,格式:public @ interface 注解名
- 其中的每一个方法实际上式声明了一个配置参数
- 方法的名称就是参数的名称.
- 返回类型就是参数的类型(返回值只能式基本类型Clacc,String,enum)
- 可以通过defailt来声明参数的默认值
- 如果只有一个参数成员,一般参数名称为Value
- 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值.
代码示例
| package com.sanduo.annotation.test; |
| import java.lang.annotation.ElementType; |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.lang.annotation.Target; |
| |
| public class Test03 { |
| @MyAnnotation2() |
| public void test(){} |
| @MyAnnotation3("xxx") |
| public void test2(){} |
| } |
| @Target({ElementType.TYPE,ElementType.METHOD}) |
| @Retention(RetentionPolicy.RUNTIME) |
| @interface MyAnnotation2{ |
| |
| String name() default ""; |
| int age() default 0; |
| int id() default -1; |
| String[] schools() default {"黑马程序员"}; |
| } |
| @Target({ElementType.TYPE,ElementType.METHOD}) |
| @Retention(RetentionPolicy.RUNTIME) |
| @interface MyAnnotation3{ |
| String value(); |
| } |
反射
- Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于
Reflection API
取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
| Class c = Class.forName("java.lang.String"); |
- 加载完成类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为反射
- Java反射机制提供的功能
- 在运行时判断任意一个对象所属的类型
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
- 优点:
- 缺点:
- 对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于直接执行相同的操作。
- 反射相关的只要API
- java.lang.Class:代表一个类
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Constructor:代表类的构造器
- Class类
- 在
Object
类中定义了以下方法,此方法将被所有的子类继承
public final native Class<?> getClass();
- 以上的方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射出类的名称。
- Class本身也是一个类
- Class 对象只能有系统建立对象
- 一个加载的类在JVM中只会有一个Class实例
- 一个Class对象对应的是一个加载到JVM中的一个.class文件
- 每个类的实例都会记得自己是有那个Class实例所生成
- 通过Class可以完整地得到一个类中的所有被加载的结构
- Class类是Reflection的根源,针对任何你想动态加载,运行的类,唯有先获得相应的Class对象
得到Class类的几种方式
- a:若已知具体的类,通过类的class属性获取,该方法最为安全可靠
Class clazz = Person.class;
- b:已知某个类的实例,调用该实例的
getClass()
方法获取Class对象
Class clazz = person.getClass();
- c:已知一个类的全类名,且该类名在路径下,可以通过Class类的静态方法forName()获取,可能抛出
ClassNotFoundException
Class clazz = Class.forName("demo01.Student")
- d:基本内置类型的包装类有一个TYPE属性
- 还可以利用ClassLoader
代码示例
| package com.sanduo.reflection; |
| |
| public class Test04 { |
| public static void main(String[] args) throws ClassNotFoundException { |
| Person person = new Person(); |
| |
| Class c1 = person.getClass(); |
| System.out.println(c1.hashCode()); |
| |
| Class c2 = Class.forName("com.sanduo.reflection.Person"); |
| System.out.println(c2.hashCode()); |
| |
| Class c3 = Person.class; |
| System.out.println(c3.hashCode()); |
| |
| Class c4 = Integer.TYPE; |
| System.out.println(c4); |
| |
| Class c5 = c1.getSuperclass(); |
| System.out.println(c5); |
| } |
| } |
| class Person{ |
| public String name; |
| public int age; |
| |
| public Person(String name, int age) { |
| this.name = name; |
| this.age = age; |
| } |
| |
| public Person() { |
| } |
| public void hello(){ |
| System.out.println("hello"); |
| } |
| } |
所有类型的Class对象
- class:外部类、成员(成员内部类,静态内部类),局部内部类,匿名内部类。
- interface:接口
- []:数组
- enum:枚举
- annotation:注解@interface
- primitive type:基本数据类型
- void
代码示例
| package com.sanduo.annotation.demo04; |
| import java.lang.annotation.ElementType; |
| public class Test01 { |
| public static void main(String[] args) { |
| Class c1 = Object.class; |
| Class c2 = Comparable.class; |
| Class c3 = String[].class; |
| Class c4 = int[][].class; |
| Class c5 = Override.class; |
| Class c6 = ElementType.class; |
| Class c7 = Integer.class; |
| Class c8 = void.class; |
| Class c9 = Class.class; |
| |
| int[] a = new int[10]; |
| int[] b = new int[100]; |
| System.out.println(a.getClass().hashCode()); |
| System.out.println(b.getClass().hashCode()); |
| } |
| } |
类的加载与ClassLoader的理解
- 加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行数据结构,然后生成一个代表这个类的
java.lang.Class
对象
- 链接:将Java类的二进制代码合并到JVM的运行状态之中的过程
- 验证:确保加载的类信息符合JVM规范,没有安全方面的问题
- 准备:正式为类变量(static)分配内存并设置类变量类变量的默认值的阶段,这些内存都将在方法区中分配
- 解析:虚拟机常量池的符号引用(常量名)替换为直接引用(地址)的过程
- 初始化:
- 执行类构造器()方法的过程。类构造器()方法是由编译器自动收集类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是类构造信息的,不是构造该类对象的构造器)
- 当初始化一个类的时候,如果发现其父类还没有实例化,则需要先触发其父类的初始化。
- 虚拟机会保证一个类的()方法在多线程环境中被正确的加锁和同步
什么时候会发生类初始化
- 类的主动引用(一定会发生类的初始化)
- 当虚拟机启动,先初始化main方法所在的类
- new一个类的对象
- 调用类的静态成员(除了final常量)和静态方法
- 使用
java.lang.reflect
包的方法对类进行反射调用
- 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
- 类的被动引用(不糊发生类的初始化)
- 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用调用父类的静态变量,不会导致子类初始化
- 通过数组定义类引用,不会触发此类的初始化
- 引用常量不会触发类的初始化(常量在链接阶段就存入调用类的常量池中了)
代码示例
| package com.sanduo.reflection; |
| |
| public class Test05 { |
| static { |
| System.out.println("Main类被初始化"); |
| } |
| public static void main(String[] args) throws ClassNotFoundException { |
| |
| |
| |
| |
| |
| |
| |
| Son[] sons = new Son[5]; |
| } |
| } |
| class Father{ |
| static int a = 1; |
| static { |
| System.out.println("父类被加载"); |
| } |
| } |
| class Son extends Father{ |
| static int b = 1; |
| static { |
| System.out.println("子类被加载"); |
| } |
| final int M = 1; |
| } |
| |
类加载器:将类(class)加载进内存
- 引导类加载器(Bootstap Classloader):用C++编写的, JVM自带的类加载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取
- 扩展类加载器(Extension Classloader):负责/e/ib/ext目录下的jar包或-D java.ext.dirs指定目录下的jar包装入工作库
- 系统类加载器(System Classloader):负责java -classpath或-D java.class. path所指的目录下的类与jar包装入工作,是最常用的加载器
| |
| ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); |
| System.out.println(systemClassLoader); |
| |
| ClassLoader parent = systemClassLoader.getParent(); |
| System.out.println(parent); |
| |
| ClassLoader parent1 = parent.getParent(); |
| System.out.println(parent1); |
| |
| ClassLoader classLoader = Class.forName("com.sia.User").getClassLoader(); |
| System.out.println(classLoader); |
| |
| classLoader = Class.forName ("java.lang.0bject").getClassLoader(); |
| System.out.println(classLoader); |
| |
| System.out.println(System.getProperty("java.class.path")); |
通过反射获取类结构
| Class c = Class.forName("com.sia.User"); |
| c.getName(); |
| c.getSimpleName(); |
| c.getFields(); |
| c.getDeclaredFields(); |
| c.getDeclaredFields("fieldName"); |
| c.getMethods(); |
| c.getDeclaredMethods(); |
| |
| c.getDeclaredMethods("methodName","type"); |
| c.getConstructor(); |
| c.getConstructor(); |
| c.getDeclaredConstructor("type","type"...); |
动态创建对象
| |
| public class User{ |
| private String name; |
| private int age; |
| |
| public User(){ |
| |
| } |
| public User(String name,int age){ |
| |
| } |
| public void setName(String name){ |
| this.name = name; |
| } |
| public String getName(){ |
| return name; |
| } |
| public void setAge(int age){ |
| this.age = age; |
| } |
| public int getAge(){ |
| return age; |
| } |
| } |
| |
| Class c = Class.forName(com.sia.User); |
| User user1 = (User)c.newInstance(); |
| |
| |
| |
| Constructor constructor = c.getDeclaredConstructor(String.class,int.class); |
| User user2 = (User)constructor.newIstance("胡图图","18"); |
| |
| Method setName = c.getDeclaredMethods("setName",String.class); |
| setName.invoke(user2,"胡图图"); |
| |
| |
| Field age = c.getDeclaredField("age"); |
| age.setAccessible("true"); |
| age.set(user2,"18"); |
性能分析
| package com.sanduo.reflection; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| |
| public class Test { |
| |
| public static void test1(){ |
| 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)); |
| } |
| |
| public static void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { |
| 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)); |
| } |
| |
| public static void test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { |
| 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)); |
| } |
| public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { |
| test1(); |
| test2(); |
| test3(); |
| } |
| } |
反射操作泛型
- Java采用泛型擦除的机制来引入泛型, Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题, 但是,一旦编译完成,所有和泛型有关的类型全部擦除
- 为了通过反射操作这些类型, Java新增了ParameterizedType ,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。
- ParameterizedType:表示一种参数化类型,比如Collection。
- GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型 。
- TypeVariable:是各种类型变量的公共父接口 。
- WildcardType:代表一种通配符类型表达式 。
获取泛型类型
| public static void main(String[] args) throws NoSuchMethodException { |
| |
| Method method = Test2.class.getMethod ( name: "test", 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); |
| } |
| } |
| } |
获取返回值类型
| public static void main(String[] args) throws NoSuchMethodException { |
| |
| Method method = Test2.class.getMethod ( name: "test", null) ; |
| |
| Type[] genericParameterTypes = method.getGenericReturnTypes(); |
| |
| for (Type genericParameterType : genericParameterTypes) { |
| |
| if (genericParameterType instanceof ParameterizedType){ |
| |
| Type[] actualTypeArguments = ((ParameterizedType)genericReturnType).getActualTypeArguments(); |
| for (Type actualTypeArgument :actualTypeArguments) { |
| System.out.println(actualTypeArgument); |
| } |
| } |
| } |
反射操作注解
- getAnnotations
- getAnnotation
代码示例
| package com.sanduo.annotation.demo05; |
| |
| import java.lang.annotation.*; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| |
| |
| public class Test01 { |
| public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { |
| Class c1 = Class.forName("com.sanduo.annotation.demo05.Student"); |
| |
| |
| |
| |
| |
| |
| |
| Annotation[] annotations = c1.getAnnotations(); |
| for (Annotation annotation : annotations) { |
| System.out.println(annotation); |
| } |
| |
| Tablesanduo tablesanduo = (Tablesanduo)c1.getAnnotation(Tablesanduo.class); |
| String value = tablesanduo.value(); |
| System.out.println(value); |
| |
| |
| Field f1 = c1.getDeclaredField("name"); |
| Fieldsanduo annotation = f1.getAnnotation(Fieldsanduo.class); |
| System.out.println(annotation.columnName()); |
| System.out.println(annotation.type()); |
| System.out.println(annotation.length()); |
| } |
| } |
| |
| @Tablesanduo("db_student") |
| class Student { |
| @Fieldsanduo(columnName = "id",type = "int",length = 10) |
| private int id; |
| @Fieldsanduo(columnName = "name",type = "varchar",length = 255) |
| private String name; |
| @Fieldsanduo(columnName = "age",type = "int",length = 10) |
| private int age; |
| |
| public Student() { |
| } |
| public Student(int id, String name, int age) { |
| this.id = id; |
| this.name = name; |
| this.age = age; |
| } |
| |
| public int getId() { |
| return id; |
| } |
| |
| public void setId(int id) { |
| this.id = id; |
| } |
| |
| public String getName() { |
| return name; |
| } |
| |
| public void setName(String name) { |
| this.name = name; |
| } |
| |
| public int getAge() { |
| return age; |
| } |
| |
| public void setAge(int age) { |
| this.age = age; |
| } |
| |
| @Override |
| public String toString() { |
| return "Test01{" + |
| "id=" + id + |
| ", name='" + name + '\'' + |
| ", age=" + age + |
| '}'; |
| } |
| } |
| |
| @Target(ElementType.TYPE) |
| @Retention(RetentionPolicy.RUNTIME) |
| @interface Tablesanduo{ |
| String value(); |
| } |
| |
| @Target(ElementType.FIELD) |
| @Retention(RetentionPolicy.RUNTIME) |
| @interface Fieldsanduo{ |
| String columnName(); |
| String type(); |
| int length(); |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了