Java注解和反射(静态语言和动态语言、类加载器、反射)
一、注解(Annotation)
Java不注解是程序本身 ,但可以对程序作出解释.(这一点和注释(comment)没什么区别) , 可以被其他程序(比如:编译器等)读取
-
定义格式
注解是以"@注释名"在代码中存在的 , 还可以添加一些参数值 , 例如@SuppressWarnings(value=“unchecked”).
-
使用场景
可以附加在package , class , method , field 等上面 , 相当于给他们添加了额外的辅助信息, 我们可以通过反射机制编程实现对这些元数据的访问
-
内置注解
- @Override : 定义在 java.lang.Override 中 , 此注释只适用于修辞方法 , 表示一个方法声明打算 重写超类中的另一个方法声明
- @Deprecated : 定义在java.lang.Deprecated中 , 此注释可以用于修辞方法 , 属性 , 类 , 表示不 鼓励程序员使用这样的元素 , 通常是因为它很危险或者存在更好的选择
- @SuppressWarnings : 定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息,需添加特定参数才能使用,例如“all”,“unchecked”,"value={“unchecked”,“deprecation”}"等
-
元注解
元注解的作用就是负责注解其他注解 , Java定义了4个标准的meta-annotation类型,他们被用来 提供对其他annotation类型作说明
- @Target : 用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
- @Retention : 表示需要在什么级别保存该注释信息 , 用于描述注解的生命周期 (SOURCE < CLASS < RUNTIME)
- @Document:说明该注解将被包含在javadoc中
- @Inherited:说明子类可以继承父类中的该注解
-
自定义注解
使用 @interface自定义注解时 , 自动继承了java.lang.annotation.Annotation接口
-
@ interface用来声明一个注解 , 格式 : public @ interface 注解名 { 定义内容 }
-
其中的每一个方法实际上是声明了一个配置参数.
-
方法的名称就是参数的名称.
-
返回值类型就是参数的类型 ( 返回值只能是基本类型,Class , String , enum ).
-
可以通过default来声明参数的默认值 .
-
如果只有一个参数成员 , 一般参数名为value
-
注解元素必须要有值 , 我们定义注解元素时 , 经常使用空字符串,0作为默认值 .
import java.lang.annotation.*; @MyAnnotation public class Test1 { //有默认值可以不写,无默认值的要赋值 @MyAnnotation(name = "张",age = 18,schools = "邮电大学",classs = "电子") public void test(){} //只有一个参数时,默认使用value()做参数名,使用时可以省略参数名 @MyAnnotation2("西邮") public void test2(){} } @Target(value = {ElementType.METHOD,ElementType.TYPE,ElementType.FIELD}) //作用域 @Retention(RetentionPolicy.RUNTIME)//运行时级别 @Documented//生成Doc文档 @Inherited //子类可以继承父类的注解 @interface MyAnnotation{ //参数类型 参数名 String name() default ""; int age() default 0; int id() default -1; //-1表示不存在 String classs() default ""; String[] schools() default {"西开","西邮"}; } @Target(value = ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation2{ String value(); }
-
二、反射
一、静态语言和动态语言
-
动态语言
- 概述:是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被 引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代 码可以根据某些条件改变自身结构
- 主要动态语言:Object-C、C#、JavaScript、PHP、Python等。
-
静态语言
- 概述:与动态语言相对应的,运行时结构不可变的语言就是静态语言。
- 主要静态语言:Java、C、C++
-
Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性, 我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更 加灵活
二、类加载器
-
类的加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化
-
加载
就是指将class文件读入内存,并为之创建一个Class对象。任何类被使用时系统都会建立一Class对象
-
连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
-
初始化
JVM进行类的初始化
执行类构造器()方法的过程。类构造器()方法是由编译期自动收集类中所有类变量的赋值动作和静态 代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)
当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化
虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步
-
-
类的加载过程
- 创建类的实例
- 类的静态变量,或者为静态变量赋值
- 类的静态方法
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
-
类加载器
负责将.class文件加载到内在中,并为之生成对应的Class对象
-
类加载器的组成
-
System ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径
-
Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载,在JDK中JRE的lib目录下ext目录
-
Bootstrap ClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类的加载,用C++编写
-
-
案例
public class TestClassLoader { public static void main(String[] args) throws ClassNotFoundException { //1.获得系统类加载器 ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); System.out.println(systemClassLoader); //2.获得系统类加载器的父类加载器---扩展类加载器 ClassLoader parent = systemClassLoader.getParent(); System.out.println(parent); //3.扩展类加载器的父类---根加载器(最底层 c++编写) ClassLoader parent1 = parent.getParent(); System.out.println(parent1); //4.测试当前类是哪个加载器加载的 ClassLoader classLoader = Class.forName("org.westos.Test0607.TestClassLoader").getClassLoader(); System.out.println(classLoader); //5.测试Object类是哪个加载器加载的 ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader(); System.out.println(classLoader1); //系统类加载器可以加载类的路径 String property = System.getProperty("java.class.path"); System.out.println(property); } }
运行结果:
三、反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
-
Class类
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass方法自动构造
-
获取Class类的实例
-
若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高
Class personClass = Person.class;
-
已知某个类的实例,调用该实例的getClass()方法获取Class对象
Person person = new Person(); Class personClass = person.getClass();
-
已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取, 可能抛出ClassNotFoundException
public static void main(String[] args) throws ClassNotFoundException { Class aClass = Class.forName("org.westos.homework0406.Person"); }
-
内置基本数据类型可以直接用类名.Type 获取Class对象
Class<Integer> integerClass = Integer.TYPE;
-
-
有Class对象的类型
- class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
- interface:接口
- []:数组
- enum:枚举
- annotation:注解@interface
- primitive type:基本数据类型
- void
import java.lang.annotation.ElementType; public class TestReflection3 { public static void main(String[] args) { Class<Object> c1 = Object.class; //类 Class<Runnable> c2 = Runnable.class;//接口 Class<String[]> c3 = String[].class;//一维数组 Class<int[][]> c4 = int[][].class;//二维数组 Class<ElementType> c5 = ElementType.class;//枚举 Class<Override> c6 = Override.class;//注解 Class<Void> c7 = Void.class;//void Class<Class> c8 = Class.class; //数组类型一样,同一个维度,只能有一个class对象 String[] a = new String[10]; String[] b = new String[11]; Class<? extends String[]> c9 = a.getClass(); Class<? extends String[]> c10 = b.getClass(); System.out.println(c9==c10); System.out.println(c1); System.out.println(c2); System.out.println(c3); System.out.println(c4); System.out.println(c5); System.out.println(c6); System.out.println(c7); System.out.println(c8); System.out.println(c9); System.out.println(c10); } }
运行结果:
-
Class对象获取构造方法并使用
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class Test10 { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Class<Person> personClass = Person.class; //获取所有构造方法 Constructor<?>[] constructors = personClass.getDeclaredConstructors(); for (Constructor<?> constructor : constructors) { System.out.println(constructor); } //获取空参构造方法 Constructor<Person> constructor = personClass.getConstructor(null); System.out.println(constructor); //获取有参构造 Constructor<Person> constructor1 = personClass.getConstructor(String.class); System.out.println(constructor1); //通过获取到的构造方法创建对象 //Object obj = con.newInstance(null); Person person = constructor.newInstance(); System.out.println(person); } } //输出: public org.westos.Test0607.Person() public org.westos.Test0607.Person(java.lang.String) public org.westos.Test0607.Person() public org.westos.Test0607.Person(java.lang.String) Person{name='null'}
-
通过反射获取私有构造方法
AccessibleObject 类是 Field、Method 和 Constructor 对象的父类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。
对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或 Constructor 对象来设置或获取字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。常用方法如下:
public void setAccessible(boolean flag) throws SecurityException
参数值为
true
则指示反射的对象在使用时应该取消 Java 语言访问检查。参数值为false
则指示反射的对象应该实施 Java 语言访问检查。-
获取私有构造方法,步骤如下
-
获取到Class对象
-
获取指定的构造方法
-
暴力访问, 通过setAccessible(boolean flag)方法
-
通过构造方法类Constructor中的方法,创建对象
-
-
案例
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class Test11 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InvocationTargetException { //1,获取到Class对象 Class c = Class.forName("org.westos.Test0607.Person");//包名.类名 //2,获取指定的构造方法 //private Person(String name, int age) Constructor con = c.getDeclaredConstructor(String.class); //3,暴力反射 con.setAccessible(true);//取消 Java 语言访问检查 //4,通过构造方法类中的功能,创建对象 Object obj = con.newInstance("小明"); System.out.println(obj); } } //运行结果: Person{name='小明'}
-
-
通过反射获取成员变量并使用
在反射机制中,把类中的成员变量使用类Field表示。可通过Class类中提供的方法获取成员变量:
-
返回一个成员变量
public Field getField(String name) 获取指定的 public修饰的变量
public Field getDeclaredField(String name) 获取指定的任意变量
-
返回多个成员变量
public Field[] getFields() 获取所有public 修饰的变量
public Field[] getDeclaredFields() 获取所有的 变量 (包含私有)
-
案例
import java.lang.reflect.Field; public class Test12 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException { //获取Class对象 Class c = Class.forName("org.westos.Test0607.Animal"); //获取成员变量 //Field[] fields = c.getFields(); Field[] fields = c.getDeclaredFields(); for (Field field : fields) { System.out.println(field); } System.out.println("-----------------"); //一个变量 //public int name; Field field = c.getDeclaredField("age"); System.out.println(field); } } //运行结果 private java.lang.String org.westos.Test0607.Animal.name private int org.westos.Test0607.Animal.age ----------------- private int org.westos.Test0607.Animal.age
- 获取对象并赋值
import java.lang.reflect.Field; public class Test13 { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException { //通过放射获取对象 Class<?> c = Class.forName("org.westos.Test0607.Animal"); Object o = c.newInstance(); //反射获取成员变量 Field name = c.getDeclaredField("name"); Field age = c.getDeclaredField("age"); //取消 Java 语言访问检查 name.setAccessible(true); age.setAccessible(true); //赋值 name.set(o,"旺财"); age.set(o,3); System.out.println(o.toString()); } } //运行结果 Animal{name='旺财', age=3}
-
-
通过反射获取成员方法并使用
在反射机制中,把类中的成员方法使用类Method表示。可通过Class类中提供的方法获取成员方法:
-
返回获取一个方法:
-
public Method getMethod(String name, Class<?>… parameterTypes)
获取public 修饰的方法
-
public Method getDeclaredMethod(String name, Class<?>… parameterTypes)
获取任意的方法,包含私有的
参数1: name 要查找的方法名称; 参数2: parameterTypes 该方法的参数类型
-
-
返回获取多个方法:
-
public Method[] getMethods() 获取本类与父类中所有public 修饰的方法
-
public Method[] getDeclaredMethods() 获取本类中所有的方法(包含私有的)
-
-
public Object invoke(Object obj, Object… args)
执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。
-
案例
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Test14 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //获取Class对象 Class<?> c = Class.forName("org.westos.Test0607.Animal"); //获取构造方法 Constructor<?> constructor = c.getConstructor(String.class,int.class); //获取对象 Object o = constructor.newInstance("小黄",3); //获取指定方法 Method getAge = c.getDeclaredMethod("getAge",null); //执行找到的方法 Object invoke = getAge.invoke(o, null); System.out.println(invoke); } } //运行结果 3
-
-
反射操作泛型
-
Java采用泛型擦除的机制来引入泛型 , Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题 , 但是 , 一旦编译完成 , 所有和泛型有关的类型全部擦除
-
为了通过反射操作这些类型 , Java新增了 ParameterizedType , GenericArrayType , TypeVariable 和 WildcardType 几种类型来代表不能被归一到Class类中的类型但是又和原 始类型齐名的类型
-
ParameterizedType : 表示一种参数化类型,比如Collection
-
GenericArrayType : 表示一种元素类型是参数化类型或者类型变量的数组类型
-
TypeVariable : 是各种类型变量的公共父接口
-
WildcardType : 代表一种通配符类型表达式
-
public Class<?> getReturnType() 返回一个 Class 对象,该对象描述了此 Method 对象所表示的方法的正式返回类型
-
public Type getGenericReturnType()
返回表示由此 Method 对象所表示方法的正式返回类型的 Type 对象。
如果返回类型是参数化类型,则返回的 Type 对象必须实际反映源代码中所用参数的实际类型。
如果返回类型是类型变量或参数化类型,则创建它。否则将解析它。 -
public Type getActualTypeArguments 获得真实的类型参数
-
案例
import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import java.util.Map; //测试反射获取泛型 @SuppressWarnings("all") public class Test08 { //带有泛型参数的方法 public void test01(Map<String, User> map, List<User> list) { System.out.println("test01"); } //带有泛型返回值的方法 public Map<Integer, User> test2() { System.out.println("test02"); return null; } public static void main(String[] args) throws Exception { //获得指定方法的泛型信息 Method method = Test08.class.getDeclaredMethod("test01", Map.class, List.class); //获得泛型参数类型信息 Type[] t = method.getGenericParameterTypes(); for (Type type : t) { System.out.println("#" + type); if (type instanceof ParameterizedType) { //getActualTypeArguments 获得真实的类型参数 Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println("真实的泛型类型:" + actualTypeArgument); } } } //获得返回值泛型信息 Method method2 = Test08.class.getMethod("test2", null); //获得泛型参数类型信息 //getGenericReturnType获得泛型返回值信息 Type genericReturnType = method2.getGenericReturnType(); if (genericReturnType instanceof ParameterizedType) { //getActualTypeArguments 获得真实的类型参数 Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println("真实的返回值泛型类型:" + actualTypeArgument); } } } } //输出: #java.util.Map<java.lang.String, com.kuang.reflection.User> 真实的泛型类型:class java.lang.String 真实的泛型类型:class com.kuang.reflection.User #java.util.List<com.kuang.reflection.User> 真实的泛型类型:class com.kuang.reflection.User 真实的返回值泛型类型:class java.lang.Integer 真实的返回值泛型类型:class com.kuang.reflection.User
-
-
反射操作注解
import java.lang.annotation.*; //使用反射读取注解 /* 1.定义注解 2.在类中使用注解 3.使用反射获取注解 */ public class Test8 { public static void main(String[] args) throws Exception{ //通过反射获取注解信息 Class<?> c = Class.forName("org.westos.Test0607.student2"); Annotation[] annotations = c.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } //通过注解的value方法获得注解的值 Table annotation = c.getAnnotation(Table.class); System.out.println(annotation.value()); java.lang.reflect.Field id = c.getDeclaredField("id"); System.out.println(id); //获得字段的注解 Field annotation1 = id.getAnnotation(Field.class); //获得注解的参数信息 System.out.println(annotation1.columnName()); System.out.println(annotation1.length()); System.out.println(annotation1.type()); } } //学生实体类 @Table("db_student") class student2{ @Field(columnName = "id",type = "int",length = 10) private int id; @Field(columnName = "db_age",type = "int",length = 3) private int age; @Field(columnName = "db_name",type = "varchar",length = 20) 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 Table{ String value(); } //字段类的注解 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface Field{ //参数类型 参数名 String columnName();//列名 String type();//类型 int length();//长度 } //输出: @org.westos.Test0607.Table(value=db_student) db_student private int org.westos.Test0607.student2.id id 10 int