小白学习java第13天:注解和反射

介绍注解:

注解(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);
            }
        }
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值