注解
注解是非常重要的,因为在后续的框架中的底层都会用注解与反射作为桥梁。
注释:程序不可读。描述代码,计算机忽略。
注解:程序可以读取。说明程序,计算机读取。
注解的格式:
以“@注解名”存在,也可以去携带一些参数。
注解可以作用的范围很广,class ,method , package…
定义
注解就是源代码的元数据,通熟的讲就是代码中的标签。注解就有如下的特点:
- 注解是一个附属品,依赖于其他元素(包、类、方法、属性等等)存在。
- 注解本身没有作用,在恰当的时候由外部程序进行解析才会发生作用。
注解的分类
按来源分
- JDK 自带注解,例如:@Override, @Deprecated, @SuppressWornings 。
- 第三方注解。
- 自定义注解。
按生命周期划分
- SOURCE:只存在于源代码中,编译成 class 文件就不存在了。
- Class:存在于源代码中和 class 文件中。
- RUNTIME:注解保留到运行时。
package annotation;
@SuppressWarnings("all")
public class Annotation01 {
//表示一个方法声明的目的是覆盖父类方法声明。如果一个方法是注释,该注释类型的编译器是除非下列条件中的至少一个保持需要产生一个错误信息:
//该方法是重写或实现一个方法在父类声明。
//该方法具有一个签名,是覆盖于任何公开声明的方法在Object。
@Override
public String toString() {
return super.toString();
}
//一个程序单元注释”不是一个程序员应该使用,通常是因为它是危险的,因为一个更好的选择的存在。编译器警告当一个过时的程序元素的使用或重写非过时的代码。
@Deprecated
public static void test(){
System.out.println("@Deprecated");
}
}
元注解
元注解指的是用于修饰注解的注解,包括如下几个:
@Retention:指明 Annotation 的生命周期,传入的值是一个枚举类型,可选值为:
- RetentionPolicy.SOURCE
- RetentionPolicy.CLASS
- RetentionPolicy.RUNTIME
@Target:指明 Annotation 可以修饰程序哪些元素,传入的值为ElemetType[] 类型,值可为:
- ElementType.CONSTRUCTOR :构造器
- ElementType.FIELD:属性
- ElementType.LOCAL_VARIABLE:局部变量
- ElementType.METHOD:方法
- ElementType.PACKAGE:包
- ElementType.PARAMETER:参数
- ElementType.TYPE:类、接口(包括注解类型和 enum 声明)
@Documented:使用此修饰的注解将会被 javadoc 工具提取成文档,使用此注解,其 @Retention 必须被设置为 RetentionPolicy.RUNTIME 。
@Inherited:具有继承性。
package annotation;
import java.lang.annotation.*;
//定义该注解可以作用在类或者方法上
@Target({ElementType.METHOD,ElementType.TYPE})
//定义该注解作用的时效可以到运行时期
@Retention(RetentionPolicy.RUNTIME)
//可以生成文档
@Documented
//具有继承性
@Inherited
public @interface Annotation02 {
}
@Annotation02
class test{
@Annotation02
public void getTest(){
System.out.println("Annotation");
}
}
自定义注解
自定义注解需要注意的问题:
- 使用 @interface 关键字定义。
- 自动继承 java.lang.annotation.Annotation 接口。
- 配置参数的类型只能是八大基本类型、String、Class、enum、Annotation 和对应的数组类型。
- 配置参数声明的语法格式如下([] 表示可省略):
- 类型 变量名() [default 默认值];
- 如果只有一个配置参数,其参数名必须为 value。
- 如果定义的注解含有配置参数,那在使用注解时,必须指定参数值,指定形式为:“参数名=参数值”。如果只有一个参数,直接写参数值即可,定义中指定了默认值的参数可以不指定值,但没有的一定要指定值。
- 没有成员的注解为标记,包含成员的称为元数据。
```java
package annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//定义该注解可以作用在类或者方法上
@Target({ElementType.METHOD,ElementType.TYPE})
//定义该注解作用的时效可以到运行时期
@Retention(RetentionPolicy.RUNTIME)
//可以生成文档
public @interface Annotation03 {
//定义注解参数:参数类型 + 参数名 + () + ;
String type ();
//可以设置默认值
int [] value () default {18};
}
@Annotation03(type = "test")
class doit{
}
反射机制
反射机制让java语言具有动态性。
定义
反射指的是程序在运行期间借助反射 API 取得任何类的内部信息,并通过这些内部信息去操作对应对象的内部属性和方法。
任何一个类,在第一次使用时,就会被 JVM 加载到堆内存的方法区中。JVM 加载类成功后,就会在方法区中产生一个对应的 Class 对象(一个类只要一个 Class 对象),这个 Class 对象包含被加载类的全部结构信息。
package reflection;
public class Reflection01 {
public static void main(String[] args) {
try {
Class<?> c1 = Class.forName("reflection.People");
Class<?> c2 = Class.forName("reflection.People");
//内存中只会去加载一个类模板
System.out.println(c1==c2); //true
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class People{
private String name;
public People() {
}
public People(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Class类
Class 类是在Java语言中定义一个特定类的实现。一个类的定义包含成员变量,成员方法,还有这个类实现的接口,以及这个类的父类。Class类的对象用于表示当前运行的 Java 应用程序中的类和接口。 比如:每个数组均属于一个 Class 类对象,所有具有相同元素类型和维数的数组共享一个Class 对象。基本的 Java 类型(boolean, byte, char, short,int, long, float 和 double) 和 void 类型也可表示为 Class 对象。
- java中有多个加载器,每个加载器能载入多个类,所以只要取得Class类对象,就可利用其getClassLoader()方法取得该类加载器的引用。
- jvm为每种类管理者独一的Class对象。因此我们可以用双等号操作符来比较对象:a1.getClass()==A.class;应该返回的是true。
getClassLoader()
获取该类的类装载器。
getComponentType()
如果当前类表示一个数组,则返回表示该数组组件的Class对象,否则返回null。
getConstructor(Class[])
返回当前Class对象表示的类的指定的公有构造子对象。
getConstructors()
返回当前Class对象表示的类的所有公有构造子对象数组。
getDeclaredConstructor(Class[])
返回当前Class对象表示的类的指定已说明的一个构造子对象。
getDeclaredConstructors()
返回当前Class对象表示的类的所有已说明的构造子对象数组。
getDeclaredField(String)
返回当前Class对象表示的类或接口的指定已说明的一个域对象。
getDeclaredFields()
返回当前Class对象表示的类或接口的所有已说明的域对象数组。
getDeclaredMethod(String,Class[])
返回当前Class对象表示的类或接口的指定已说明的一个方法对象。
getDeclaredMethods()
返回Class对象表示的类或接口的所有已说明的方法数组。
getField(String)
返回当前Class对象表示的类或接口的指定的公有成员域对象。
getFields()
返回当前Class对象表示的类或接口的所有可访问的公有域对象数组。
getInterfaces()
返回当前对象表示的类或接口实现的接口。
getMethod(String,Class[])
返回当前Class对象表示的类或接口的指定的公有成员方法对象。
getMethods()
返回当前Class对象表示的类或接口的所有公有成员方法对象数组,包括已声明的和从父类继承的方法。
getModifiers()
返回该类或接口的Java语言修改器代码。
getName()
返回Class对象表示的类型(类、接口、数组或基类型)的完整路径名字符串。
getResource(String)
按指定名查找资源。
getResourceAsStream(String)
用给定名查找资源。
getSigners()
获取类标记。
getSuperclass()
如果此对象表示除Object外的任一类,那么返回此对象的父类对象。
isArray()
如果Class对象表示一个数组则返回true,否则返回false。
isAssignableFrom(Class)
判定Class对象表示的类或接口是否同参数指定的Class表示的类或接口相同,或是其父类。
isInstance(Object)
此方法是Java语言instanceof操作的动态等价方法。
isInterface()
判定指定的Class对象是否表示一个接口类型。
isPrimitive()
判定指定的Class对象是否表示一个Java的基类型。
newInstance()
创建类的新实例。
toString()
将对象转换为字符串。
Class的获取方式
(1)类的 class 属性
每一个类,都有一个 class 静态属性,这个静态属性就是类对应的 Class 对象。
Class<Person> cl1 = Person.class;
(2)Object 对象 的 getClass() 方法
Person p1 = new Person();
Class<Person> cl2 = (Class<Person>) p1.getClass();
(3)通过 Class 类的 forName() 方法(最常用)
try {
Class cl3 = Class.forName("com.llm.hkl.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
(4)通过 ClassLoader 类(不常用)
ClassLoader cl = Person.class.getClassLoader();
try {
Class cl4 = cl.loadClass("com.llm.hkl.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
内存分析
类加载器的作用
package reflection;
public class Reflection {
public static void main(String[] args) {
//获取系统类的加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//获取系统类的父类加载器--->拓展加载器
ClassLoader systemClassLoaderParent = systemClassLoader.getParent();
System.out.println(systemClassLoaderParent);
//获取拓展类的类加载器---->根加载器(C/C++),java无法获取到其对象
ClassLoader systemClassLoaderParentParent = systemClassLoaderParent.getParent();
System.out.println(systemClassLoaderParentParent);
//jdk.internal.loader.ClassLoaders$AppClassLoader@2f0e140b
//jdk.internal.loader.ClassLoaders$PlatformClassLoader@6acbcfc0
//null
}
}
拓展:Java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的class文件加载到内存生成class对象,而且,加载某个类的class文件时,Java虚拟机采用的是双亲委派机制,即把请求交由父类处理,它是一种任务委派模式
利用反射获取Class模板信息或者创建对象操作
可以在运行过程中操作对象,可以解耦合
-
获取Class对象的成员变量
package com.zyx.reflect.demo01; /* 类的实例代表一个运行 类 java应用程序的类和接口。枚举是一种阶级和注释是一种接口。 每个数组属于一种体现为 类对象是所有数组的元素类型和维数的共享类。原始的java类型 (boolean, byte, char, short, int, long, float,和 double),和关键词 void也表示为 类对象。 */ import java.lang.reflect.Field; public class PersonClass { public static void main(String[] args) throws Exception{ //创建的person的class对象 Class<Person> person = Person.class; //class对象的功能:获取成员变量 Field[] fields = person.getFields();//获取public修饰符的成员变量 for (Field field:fields) { System.out.println(field); } Field name = person.getField("name");//获取public修饰符的成员变量,并且获取指定名字的成员变量 System.out.println(name); System.out.println("**************************************************"); Field[] fs = person.getDeclaredFields();//获取所有的成员变量 for (Field field:fs) { System.out.println(field); } Field id = person.getDeclaredField("id");//获取指定名字的成员变量 System.out.println(id); //field的功能 Person p = new Person(); name.set(p,"zyx");//通过filed对象给指定的成员变量赋值 Object o = name.get(p);//获取值 fs[3].set(p,"湖北武汉"); //对私有的值进行赋值或者是查询,可以通过暴力反射,即对其进行安全访问权限进行解除 id.setAccessible(true);//暴力反射,id为private修饰的私有变量 id.set(p,"888888888888888888"); System.out.println(p); } }
-
获取class对象的构造方法
package com.zyx.reflect.demo01; import java.lang.reflect.Constructor; public class PersonClass2 { public static void main(String[] args) throws Exception{ //创建的person的class对象 Class<Person> person = Person.class; //获取class对象的构造器 Constructor<Person> constructor = person.getConstructor();//获取无参数的构造器 System.out.println(constructor); //获取满参数的构造器 Constructor<Person> constructor1 = person.getConstructor(String.class, int.class, String.class, String.class); System.out.println(constructor1); //通过获取的构造器,创建对象 Person person1 = constructor1.newInstance("邹", 21, "888888888888888888888", "湖北黄冈"); System.out.println(person1); //获取class对象所有的构造器 Constructor<?>[] constructors = person.getConstructors(); for(Constructor constructor2:constructors){ System.out.println(constructor2); } //使用空参构造创建对象可以直接通过class对象创建 Person person2 = person.newInstance(); System.out.println(person2); } }
-
获取成员方法
package com.zyx.reflect.demo01; import java.lang.reflect.Method; import java.util.Arrays; public class PersonClass3 { public static void main(String[] args) throws Exception{ //创建的person的class对象 Class<Person> person = Person.class; //获取class对象的成员方法 Method[] methods = person.getMethods();//获取对象中所有的public修饰的方法 System.out.println(Arrays.toString(methods)); Method eat = person.getMethod("eat");//获取对象中修饰符为public并且方法名为eat()的方法 System.out.println(eat); Method drink = person.getDeclaredMethod("drink");//获取任意修饰符修饰的方法并且方法名为drink System.out.println(drink); //执行方法 //无参数方法的执行 Person p = new Person(); eat.invoke(p); //私有方法可以暴力反射 drink.setAccessible(true); drink.invoke(p); //有参方法的执行 Method eatWhat = person.getDeclaredMethod("eatWhat", String.class, String.class); eatWhat.setAccessible(true); eatWhat.invoke(p,"米饭","红烧肉"); //有参数有返回值类型 Method drinkWhat = person.getDeclaredMethod("drinkWhat", String.class); Object ss = drinkWhat.invoke(p,"奶茶"); System.out.println(ss); //获取方法名 String name = drinkWhat.getName(); System.out.println(name); //获取类名 Class<? extends Method> aClass = drinkWhat.getClass(); System.out.println(aClass); } }
-
测试应用
package com.zyx.reflect.demo02; import java.io.InputStream; import java.lang.reflect.Method; import java.util.Properties; /** * 根据配置文件生成对象,以及调用对象的方法 */ public class CreateClass { public static void main(String[] args) throws Exception{ //1.获取到配置文件 //1.1创建Properties对象用来存储配置文件信息 Properties pro = new Properties(); //1.2通过类加载器获取配置文件的位置并返回一个流对象 ClassLoader classLoader = CreateClass.class.getClassLoader(); InputStream resource = classLoader.getResourceAsStream("pro.properties"); //1.3Properties的load方法存放流 pro.load(resource); //2.读出信息 String className = pro.get("className").toString(); String methodName = pro.get("methodName").toString(); //3.利用反射创建对象 Class<?> name = Class.forName(className); Object o = name.newInstance(); Method method = name.getMethod(methodName); method.invoke(o); } }
-
获取class对象
package com.zyx.reflect.demo03; public class GetClass { public static void main(String[] args) throws Exception { //通过Class.forName获取class对象 Class<?> aClass = Class.forName("com.zyx.reflect.demo03.Person"); System.out.println(aClass); //通过类名.class获取class对象 Class<Person> personClass = Person.class; System.out.println(personClass); //对象.getClass来获取 Person p = new Person("zyx",11); Class<? extends Person> aClass1 = p.getClass(); System.out.println(aClass1); } } /* class com.zyx.reflect.demo03.Person class com.zyx.reflect.demo03.Person class com.zyx.reflect.demo03.Person */
反射操作泛型
相关案例
1.反射加注解进行方法测试
注解
package demo;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 标注该注解的方法将被测试
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
}
待测试对象
package demo;
/**
* 这待测试的类
*/
public class TestApplication {
@Test
public int add(){
return 12+12;
}
@Test
public int sub(){
return 12+12;
}
public int mul(){
int a = 12/0;
return 12+12;
}
@Test
public int div(){
return 12/0;
}
}
测试·
package demo;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Application {
public static void test(String typeName) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException, IOException {
Class<?> c = Class.forName(typeName);
Method[] methods = c.getDeclaredMethods();
File file = new File("D:"+File.separator+"java_file"+File.separator+"exception.txt");
if (!file.exists()){
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
FileWriter writer = new FileWriter(file,true);
for (Method method : methods) {
Test annotation = method.getAnnotation(Test.class);
if (annotation!=null){
try {
method.invoke(c.newInstance(),null);
System.out.println("成功");
} catch (Exception e) {
writer.write(method+"发生了异常"+e.getCause()+"\n");
writer.flush();
writer.close();
}
}
}
}
public static void main(String[] args) throws ClassNotFoundException, IOException, InstantiationException, IllegalAccessException, InvocationTargetException {
String name ="demo.TestApplication";
test(name);
}
}
2.动态代理
抽象角色
package demo01;
public interface AbstractCharacter {
public void doSomething();
}
package demo01;
//真实角色完成的业务逻辑
public class RealCharacter implements AbstractCharacter{
@Override
public void doSomething() {
System.out.println("play basketball");
}
}
package demo01;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 实现InvocationHandler完成动态代理业务逻辑,利用的是反射的原理
*/
public class DynamicAgent implements InvocationHandler {
//要被代理的对象
private Object character;
public DynamicAgent(Object character) {
this.character = character;
}
//生成代理类 需要用到类Proxy
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), character.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(character, args);
doOther();
return invoke;
}
//代理模式新增加的方法
public void doOther(){
System.out.println("good!");
}
}
package demo01;
public class Application {
public static void main(String[] args) {
RealCharacter character = new RealCharacter();
DynamicAgent agent = new DynamicAgent(character);
AbstractCharacter o = (AbstractCharacter) agent.getProxy();
o.doSomething();
}
}