介绍注解:
注解(Annotation)是什么呢?好像和注释有点一样的感觉,注释(Comment)就是给程序员看的,但是注解是让其他程序根据注释信息怎么执行该程序。
比如,@Test就是进行测试,@Override就是进行重写的等等!
那么自定义注解长什么样子呢?
public @interface 注解名称{
public 属性类型 属性名() dafault 默认值;
}
public @interface MyAnnotation {
String a();
boolean b() default true;
}
【这里需要补充的一点就是如果里面就只有定义了value这个变量,你后面引用就可以不写,但是要是多个就必须写!】
那么注解的原理是什么呢,其实就是定义了一个 interface MyAnnotation extends Anonotation,一个接口然后继承,上面@内容,就是对其进行的实现类对象!
元注解:就是用来修饰注解的注解,意思就是对注解进行范围的控制,以及一些功能的限制!
【在在你定义的注解里面进行元注解,你想写这个注解要是在成员变量,而且是想一直保留,就在成员变量上面写
@Target (ElementField.FIELD)
@Retention(RetentionPolicy.RUNTIME)】
@Target:声明被修饰的注解只能在哪些位置使用【例如:@Target (ElementType.TYPE)】
1.TYPE:类、接口
2.FIELD:成员变量
3.METHOD:成员方法
4.PARAMETER:方法参数
5.CONSTRUCTION:构造器
6.LOCAL_VARIABLE:局部变量
@Retention:声明注解的保留周期
1.SOURCE:只是保留在源码阶段,转化成字节码就不存在
2.CLASS:保留字节码文件,运行阶段不存在
3.RUNTIME:一直到运行阶段
反射机制(Reflection):(顾名思义就是反过来,原理就是加载完类之后,在堆内存的方法区中就产生了一个class类型的对象(一个类只有一个class对象),这个对象就包含一个完整的类结构对象。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子就可以看到类的完整结构!!!)
【下面我将详细的介绍一下,利用图进行表示】
1.思考一下我们之前new出来的对象,为什么一点里面的类里面的方法就全部展现出来呢?
思考了这个问题之后我们就开始对于反射有个清楚认识:
那么你可能会想了,那我和之前学习的创建对象,然后对其进行也可以一一的解剖呢,为啥还需要这个呢,因为你能知道哦啊属性的类型吗?你能区分构造方法和方法吗?还有更得细节比如异常注解这些你都没办法!!!,因此我们学习反射是很有必要的。
首先我们第一步就是对其进行获取类:(三种方法!)
package demo05;
//什么叫反射
public class Test01 {
public static void main(String[] args) throws ClassNotFoundException {
//方法1,我们也可以直接根据类来进行
//这个是最为常用的
Class c1 = Class.forName("demo05.Student");
System.out.println(c1.hashCode());
//方法2,上也是比较方便的
//一般是最为参数进行传递
Class c2 = Student.class;
System.out.println(c2.hashCode());
//方法3,我们可以根据创建对象
//是有局限性的,只有创建完对象才可以进行使用
Student student = new Student();
System.out.println("名字:" + student.getName());
Class c3 =student.getClass();
System.out.println(c3.hashCode());
//那么有了class对象我们就需要就考虑的是怎么使用
System.out.println(c3.getSuperclass());
}
}
会获取类然后就开始进行解剖,上面图可以知道,我们是创建了类,然后就开始进行get得到Field、Construct、Method这三个类,然后就进行下一步!【下面我们就行在内存中先后顺序】
构造方法:
下面是我的实现类
package demo05;
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name) {
this.name = name;
}
protected Student( int age) {
this.age = age;
}
private Student( String name,int age) {
this.name = name;
this.age = age;
}
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;
}
public void eat(String name){
System.out.println(name + "在吃东西!");
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
下面是实现的代码
package demo05;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Parameter;
public interface Test {
public static void main(String[] args) throws Exception {
//首先获取字节码文件对象
Class c1 = Class.forName("demo05.Student");
//然后就是对其进行解剖
//只能获得public修饰不能全部获取
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("---------------------------------");
//我想全部进行获取
Constructor[] declaredConstructors = c1.getDeclaredConstructors();
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
System.out.println("---------------------------------");
//进行有选择的选择构造方法
Constructor declaredConstructor = c1.getDeclaredConstructor();//没有输入参数就是无参构造
System.out.println(declaredConstructor);
Constructor declaredConstructor1 = c1.getDeclaredConstructor(String.class);
System.out.println(declaredConstructor1);
Constructor declaredConstructor2 = c1.getDeclaredConstructor(String.class, int.class);
System.out.println(declaredConstructor2);
System.out.println("---------------------------------");
//下面我们有了这些就可以对其过更加详细的解剖
//获取权限修饰符,应用场景就是你在创建对象的时候private是创建不出来的,底层用的就是反射!
System.out.println(declaredConstructor.getModifiers());
//获取参数
Parameter[] parameters = declaredConstructor2.getParameters();
for (Parameter parameter : parameters) {
System.out.println(parameter);
}
System.out.println("---------------------------------");
//我拿到了构造方法我就可以对其new对象
Object o = declaredConstructor1.newInstance("张珊");
System.out.println(o);
//因为declaredConstructor2修饰符是private因此我们需要对其使用临时取消权限
declaredConstructor2.setAccessible(true);
Object o1 = declaredConstructor2.newInstance("张三", 18);
System.out.println(o1);
}
}
成员变量:(和上面大概是一样的)
package demo05;
import java.lang.reflect.Field;
public class Test02 {
public static void main(String[] args) throws Exception {
//首先获取字节码文件对象
Class c1 = Class.forName("demo05.Student");
//得到Field这个类
// Field[] fields = c1.getFields();//我们的成员变量没有所以就是没有
// for (Field field : fields) {
// System.out.println(field);
// }
Field[] declaredFields = c1.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println("---------------------------");
//想要获得有选择的单个
Field name = c1.getDeclaredField("name");
System.out.println(name);
System.out.println("---------------------------");
//然后就是我们能获取更为详细的
System.out.println(name.getModifiers());
System.out.println(name.getName());
System.out.println(name.getType());
//我想获取已经创建好了之后赋值的值
Student student = new Student("张三");
name.setAccessible(true);// private String name;开启权限
Object o = name.get(student);
System.out.println(o);
}
}
成员方法:
package demo05;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
public class Test03 {
public static void main(String[] args) throws Exception {
//首先获取字节码文件对象
Class c1 = Class.forName("demo05.Student");
//获得成员方法对象
//获得所有方法,包括父类里面的所以公共方法里面也还是含有的!
Method[] methods = c1.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("-------------------");
//获取单个
//方法名称+加上参数
Method setName = c1.getMethod("setName", String.class);
System.out.println(setName);
System.out.println("-------------------");
System.out.println(setName.getModifiers());//修饰符
System.out.println(setName.getName());//名字
Parameter[] parameters = setName.getParameters();//获取参数
for (Parameter parameter : parameters) {
System.out.println(parameter);
System.out.println(parameter.getType());
}
//获取异常,因为我们setName没有后面throws 异常类型,所以就没有!
Class[] exceptionTypes = setName.getExceptionTypes();
for (Class exceptionType : exceptionTypes) {
System.out.println(exceptionType);
}
//方法的返回值
Method e = c1.getMethod("eat", String.class);
System.out.println(e);
Student student = new Student();
e.invoke(student,"李四");//第一个参数是方法名字,第二是方案里面的参数
}
}
反射的作用:(也是做一个小的总结!)
1.获取一个类里面的所有信息,获取到之后,再执行其他的业务逻辑
Student实体类:
package demo06;
public class Student {
private String name;
private int age;
private double height;
private String hobby;
public Student() {
}
public Student(String name, int age, double height, String hobby) {
this.name = name;
this.age = age;
this.height = height;
this.hobby = hobby;
}
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;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public void study(){
System.out.println("我要学习!");
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", height=" + height +
", hobby='" + hobby + '\'' +
'}';
}
}
Teache实体类:
package demo06;
public class Teacher {
private String name;
private double salary;
public Teacher() {
}
public Teacher(String name, double salary) {
this.name = name;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public void teaching(){
System.out.println("我要上课教书!");
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", salary=" + salary +
'}';
}
}
package demo06;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
public class Test01 {
public static void main(String[] args) throws IllegalAccessException, IOException {
//创建对象
Student student = new Student("张三", 18, 161.5, "打篮球");
Teacher teacher = new Teacher("李四", 10000);
FileTest(student);
}
//传入一个对象
private static void FileTest(Object object) throws IllegalAccessException, IOException {
//因为有了对象我们是可以直接使用对象,获取字节码对象
Class c1 = object.getClass();
//为了保存在文件里面,我们就需要使用直接流对其进行和读操作
FileWriter fileWriter = new FileWriter("src\\demo06\\note.txt");
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
//获取属性
Field[] declaredFields = c1.getDeclaredFields();
//遍历
for (Field declaredField : declaredFields) {
//取消权限
declaredField.setAccessible(true);
//获取属性的名字
String name = declaredField.getName();
//获取属性赋于后的值
Object value = declaredField.get(object);
//写数据进行
bufferedWriter.write(name + "=" + value);
bufferedWriter.newLine();
System.out.println(name + "=" + value);
}
bufferedWriter.close();
fileWriter.close();
}
}
2.结合配置文件,动态的创建对象并调用方法(上面的代码是不是有个弊端就是我需要在我运行代码里面进行修改,试着想一下就是如果我配置好了一个文件。里面有我需要的参数,我只需要改文本里面的内容就好了)
配置文件prop.properties
ClassName=demo06.Student
Method=study
下面是对于配置文件执行的代码:
package demo06;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
public interface Test02 {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//获取配置文件对象
Properties properties = new Properties();
//创建字节字节输入流进行读操作
FileInputStream fileInputStream = new FileInputStream("src\\prop.properties");
//对文件进行读取
properties.load(fileInputStream);
fileInputStream.close();
System.out.println(properties);
//开始对里面的内容进行解剖
String className =(String) properties.get("ClassName");
// System.out.println(className);
String method =(String) properties.get("Method");
// System.out.println(method);
//使用反射创建对象
Class c1 = Class.forName(className);
//利用反射然后创建对象
Constructor declaredConstructor = c1.getDeclaredConstructor();
Object o = declaredConstructor.newInstance();
// System.out.println(o);
//调用方案之前,你必须创建对象
Method declaredMethod = c1.getDeclaredMethod(method);
declaredMethod.invoke(o);
}
}
最后我们对于注释和反射一起来做一个总结【写一个类似于junit框架单元测试框架@Test就进行执行】
步骤:
1.首先定义自定义注释
2.我们要去执行@注释符,那就需要通过反射对于这个类里面的全部注释进行提取出来
3.然后判断要是有就进行执行
import javax.crypto.spec.PSource;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*
public @interface 注解名称{
public 属性类型 属性名() dafault 默认值;
}
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
import org.junit.Test;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Demo01 {
//@MyAnnotation
public void test1(){
System.out.println("test1");
}
@MyAnnotation
public void tes2(){
System.out.println("test2");
}
//@MyAnnotation
public void test3(){
System.out.println("test3");
}
@MyAnnotation
public void test4(){
System.out.println("test4");
}
public static void main(String[] args) throws Exception {
Demo01 demo01 = new Demo01();
//进行反射获取注释
Class<Demo01> demo01Class = Demo01.class;
//获取全部成员方法
Method[] declaredMethods = demo01Class.getDeclaredMethods();
//遍历进行判断是否有@
for (Method declaredMethod : declaredMethods) {
if (declaredMethod.isAnnotationPresent(MyAnnotation.class)){
//调用有@这个的方法
declaredMethod.invoke(demo01);
}
}
}
}