首页 > 编程语言 >Java笔记(反射、动态代理、注解)

Java笔记(反射、动态代理、注解)

时间:2024-12-19 13:28:56浏览次数:13  
标签:Java String void 笔记 class 注解 public name

Java笔记(反射、动态代理、注解)

反射

概念

image-20230327145646643

image-20230327145913661

获取class对象的三种方式

image-20230327150139698

获取一个类的全类名

  • 全类名 :包名 + 类名

  • 获取方式如下image-20230327150907119

  • 然后直接在双引号里面粘贴

image-20230327152129022

获取构造方法

image-20230327152504526

  • Declared是表示要获取所有类型的构造方法 ,不管是不是被public修饰

getConstructors

  • 获取所有公共==(被Public修饰)==构造方法

image-20230327153217581

getDeclaredConstructors

  • 获取所有构造方法(包括私有(private修饰)的或者(protected修饰的))

image-20230327153550164

getDeclaredConstructor(形参类型.class)

  • 获取特定的构造方法

image-20230327154015995

指定有参构造创建对象

获取public修饰的构造方法

image-20230713162858874

  • //获取class对象之后 ,用这个对象实例化
    Constructor c1 = clazz.getConstructor(String.class, int.class, String.class);
            Car car1 = (Car)c1.newInstance("夏利", 10, "红色");
            System.out.println(car1+" 参数 "+car1.getName()+" "+car1.getAge()+" "+car1.getColor());
    

获取private修饰的构造方法(暴力反射)

私有的构造方法

image-20230713163614689

用的是getDeclaredConstructor ,并且还要设置setAttribute(true)

image-20230713163537871

Modifiers

  • 获取构造方法的权限修饰符(以整数形式体现)

image-20230327154544682

image-20230327154945891

getParameters

  • 获取某个构造方法里面的全部参数

image-20230327155152406

  • 下面报错的原因是获取的方法是私有(private)的 , 不能在这个类里面创建对象 , 所以需要暴力反射

image-20230327160151211

  • 解决办法

暴力反射

  • 暴力反射 , 需要Construct类点上setAttribute设置为true , 表示临时解除private权限修饰 , 获得访问权限
  • 当需要修改私有的构造方法的对象或者成员变量里面的值的时候需要用暴力反射

image-20230327160604762

获取成员变量

image-20230327160933941

getFields

  • 获取所有公共的成员变量

image-20230327161446928

getDeclaredFields

  • 获取所有成员变量

image-20230327161611497

getDeclaredField(“变量名字”)

  • 获取单个的成员变量

image-20230327161804305

getModifiers

  • 获取某个成员变量的权限修饰符

image-20230327162001724

  • 获取某特定成员变量存储的属性 , 而不是所有变量

image-20230327162438221

  • 此外还可以修改对象里面的值
    • 表示把s对象的name属性的name值改成"lisi"

image-20230327162603807

获取成员方法

getMethods

image-20230327162901182

  • 注意构造方法不同于成员方法
    • 构造方法主要用于构造某个类的对象 , 而成员方法是用来实现某个功能的

image-20230327163639452

image-20230327164100274

invoke

  • 引用\援引

  • 要用调用者s去调用eat方法

  • m是获得了指定的单一方法(eat)

image-20230327164827378

反射的作用

  • 获取一个类里面的所有信息 . 获取到之后 , 再执行其他的业务逻辑
  • 结合配置文件 , 动态的创建对象并调用方法

练习一

image-20230327165059510

image-20230327171120928

//实体类
package com.LiBai.myReflection.myReflect3;

public class Teacher {
    private String name;
    private int salary;
    private int age;
    private double height;

    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                ", salary=" + salary +
                ", age=" + age +
                ", height=" + height +
                '}';
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public float getHeight() {
        return (float) height;
    }

    public void setHeight(float height) {
        this.height = height;
    }

    public Teacher(String name, int salary, int age, double height) {
        this.name = name;
        this.salary = salary;
        this.age = age;
        this.height = height;
    }

    public Teacher(String name, int salary) {
        this.name = name;
        this.salary = salary;
    }

    public Teacher() {

    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

}
//测试类
public class reflectTest {
    public static void main(String[] args) throws IllegalAccessException, IOException {
        Teacher t = new Teacher("小A", 10000, 23, 167.5);
        saveObject(t);
    }
    public static void saveObject(Object obj) throws IllegalAccessException, IOException {
        //1.获取字节码文件的对象,因为已经有了类的对象,所以直接就可以对象点上getClass
        Class<?> clazz = obj.getClass();

        //2.获取IO流
        BufferedWriter bw = new BufferedWriter(new FileWriter("src/a.txt"));

        //3.获取所有的成员变量,因为不知道是不是私有的,所以用Declared
        Field[] fields = clazz.getDeclaredFields();
        //遍历成员变量
        for (Field field : fields) {
            //因为不知道成员变量是否被private修饰(有无访问权限),所以用暴力反射
            field.setAccessible(true);
            //获取成员变量的名字
            String name = field.getName();
            //获取成员变量的值
            Object value = field.get(obj);
            //写出数据
            bw.write(name+"="+value);
            //System.out.println(name+"="+value);
            //写一行换一行
            bw.newLine();
        }
        //关流
        bw.close();

    }

}

练习二

image-20230327190103228

  • 配置文件即.properties文件

  • 读取配置文件的信息

//propertites文件
classname=com.LiBai.myReflection.myReflect4.Teacher
method=teach
public class Teacher {
    private String name;
    private double salary;
    public void teach(){
        System.out.println("老师在教书!");
    }

    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 Teacher() {
    }

    public Teacher(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }
}
public class Student {
    private String name;
    private int age;

    public Student() {

    }

    public 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;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public void study(){
        System.out.println("学生在学习!");
    }
}
public class reflect {
    //读取配置文件的信息
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //读取配置文件中的信息
        Properties prop = new Properties();
        FileInputStream fis = new FileInputStream("src/prop.properties");
        prop.load(fis);
        System.out.println(prop);//{classname=com.LiBai.myReflection.myReflect4.Student, method=study}

        //获取全类名中的信息
        String className = (String) prop.get("classname");
        String methodName = (String) prop.get("method");

        System.out.println(className);//com.LiBai.myReflection.myReflect4.Student
        System.out.println(methodName);//study

        //运用反射去创建对象并运行方法
        Class<?> clazz = Class.forName(className);

        //获取构造方法(暴力反射)
        //调用的是空参构造,但防止空参构造被私有化,所以用Declared
        Constructor<?> con = clazz.getDeclaredConstructor();
        Object o = con.newInstance();
        System.out.println(o);//Student{name='null', age=0}\

        //获取成员方法并运行
        Method method = clazz.getDeclaredMethod(methodName);
        method.setAccessible(true);
        method.invoke(o);//让o去调用这个method


    }
}
  • 以后再想要使用另一个类里面的方法,就只需要改一下配置文件里面的信息就可以 , 例如本例就把配置文件里面那个classname改一次,method改一下就可以了

总结

image-20230327194601175

动态代理

概念

  • 想要增加源码的功能 , 直接修改源码叫做侵入式修改
  • 动态代理可以避免侵入式修改代码

image-20230327194947531

image-20230327195308036

image-20230327200224186

如何为Java对象创建一个代理对象

image-20230327200325758

大练习

public class bigStar implements star{
    private String name;

    public bigStar() {
    }

    public bigStar(String name) {
        this.name = name;
    }
    //唱歌
    //this.name是获取这个方法之外的name , 也就是成员变量的name
    //注意this.name和name不一样,前面表示成员变量里面的name,后面表示歌名name
    @Override
    public String sing(String name){
        System.out.println(this.name+"正在唱"+name);
        return "谢谢";
    }
    //跳舞
    @Override
    public void dance(){
        System.out.println(this.name+"正在跳舞");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public interface star {
    //定义接口类来代理方法
    //删掉方法体,因为在接口里面都是抽象方法

    //唱歌
    public abstract String sing(String name);
    //跳舞
    public abstract void dance();

}
//类的作用:创建一个代理
public class proxyUtil {
    /*
    * 方法的作用:
    *   给一个明星的对象创建一个代理
    * 形参:被代理的明星对象
    * 返回值:方法的返回值是一个代理
    *   因为代理也要实现star的接口
    *
    * 需求:
    *   外面的人想要大明星唱一首歌
    * 1.获取代理的对象
    *   代理对象=proxyUtil.creatProxy
    * 2.再调用代理的唱歌方法
    *   代理对象.唱歌的方法();
    * */
    public static star creatProxy(bigStar bigStar){
        //java.lang.reflect.Proxy类:提供了为对象产生代理的方法,且该类静态,可直接类名调用方法
        //参数一:类加载器,就是一个把字节码文件加载到内存当中的工具,注意是把当前的加载到内存当中,并且再让他把代理加载到内存当中
        //参数二:指定接口,这些接口用来指定生成的代理长什么样,也就是有哪些方法,而代码当中以数组方式体现
        //      也就是我们生成的代理可以代理star接口中所有的方法
        //参数三:用来指定生成的代理对象要干什么事
        star star = (dynamicproxy.star) Proxy.newProxyInstance(
                proxyUtil.class.getClassLoader(),
                new Class[]{star.class},//把接口的字节码放在数组当中
                new InvocationHandler() {
                    //这个类才是真正放代理要干的事情
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        /*
                        * 参数一:代理的对象
                        * 参数二:要运行的方法 sing
                        * 参数三:调用sing方法时传递的实参
                        *       在定义sing方法时要传入歌曲的名字
                        * */
                        if ("sing".equals(method.getName())){
                            //如果当前运行的是唱歌的方法
                            System.out.println("准备话筒,收钱");
                        }else if ("dance".equals(method.getName())){
                            System.out.println("准备场地,收钱");
                        }
                        //去找大明星开始唱歌或者跳舞
                        //代码的表现形式,调用大明星里面唱歌或者跳舞的方法
                        //反射 括号里是调用方法的对象和需要传递的参数
                        return method.invoke(bigStar,args);

                    }
                });


        return star;
    }
}
public class Test {
    //需求:让大明星唱歌
    public static void main(String[] args) {
        //先创建一个明星对象
        bigStar bigStar = new bigStar("鸡哥");

        //1.获取代理的对象
        star proxy = proxyUtil.creatProxy(bigStar);

        //2.调用唱歌的方法
        String result = proxy.sing("只因你太美");
        System.out.println(result);

        //3.调用跳舞的方法
        proxy.dance();

    }

}

总结

image-20230327205206793

注解

概念

  • 概念:Java注解又称Java标注,是在 JDK5 时引入的新特性,注解(也被称为元数据)。Java注解它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。

  • 应用:

    1生成文档这是最常见的,也是java 最早提供的注解;

    2.在编译时进行格式检查,如@Override放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出;

    3.跟踪代码依赖性,实现替代配置文件功能,比较常见的是spring 2.5 开始的基于注解配置,作用就是减少配置;

    4.在反射的 Class, Method, Field 等函数中,有许多于 Annotation 相关的接口,可以在反射中解析并使用 Annotation

注解(Override Deprecated SuppressWarning)

三种基本的Annotaton

image-20230329081033093

image-20230329081745223

  • Deprecated 是在想要定义的方法的头上加的
  • SuppressWarning是在要调用已经定义好的类或者方法处加的
  • 看到调用test1的地方加上了横线

image-20230403205636131

  • 看到在调用处加上SuppressWarning之后,test1的横线消失了

image-20230403205738556

元注解

  • 元注解

  • 元注解就是解释注解的注解,它注解的对象是我们前面介绍的注解,如:@Override、@Deprecated@SuppressWarnings

  • image-20230403174540328

image-20230329082848174

image-20230329082455256

image-20230403175105482

public enum ElementType {
 
    TYPE, // 类、接口、枚举类
 
    FIELD, // 成员变量(包括:枚举常量)
 
    METHOD, // 成员方法
 
    PARAMETER, // 方法参数
 
    CONSTRUCTOR, // 构造方法
 
    LOCAL_VARIABLE, // 局部变量
 
    ANNOTATION_TYPE, // 注解类
 
    PACKAGE, // 可用于修饰:包
 
    TYPE_PARAMETER, // 类型参数,JDK 1.8 新增
 
    TYPE_USE // 使用类型的任何地方,JDK 1.8 新增
 
}

自定义注解

image-20230329083602187

public class test {
    //自定义注解
    //注意看下面的注解只需要规定age,不需要规定其他参数,因为其他参数在定义注解的时候就有默认值
    @myAnnotation(age=18)
    public void test01(){
        
    }
    
    @myAnnotation2(value = "小李")
    public void test02(){
        
    }
}

//目标作用域:类和方法上面
@Target({ElementType.TYPE,ElementType.METHOD})
//存活时间:runtime表示注解可以保留到程序运行的时候
@Retention(RetentionPolicy.RUNTIME)
@interface myAnnotation{
    //注解的参数:参数类型+参数名();
    //注意后面加括号并不代表他是一个方法,只是myAnnotation这个注解的参数的特殊写法
    String name() default "小明";
    int age();
    //default表示默认值,不加default的话在使用注解的时候要自行加上参数
    int id() default -1;
    //这是定义了一个参数为数组
    String[] schools() default {"清华大学","北京大学"};
}

//目标作用域:类和方法上面
@Target({ElementType.TYPE,ElementType.METHOD})
//存活时间:runtime表示注解可以保留到程序运行的时候
@Retention(RetentionPolicy.RUNTIME)
@interface myAnnotation2{
    String value();
}
  • 自定义注解练习
    • 左边是自定义的注解类
    • 右边是注解的营业类

image-20230403181042369

  • 元注解练习
  • target

image-20230403181613224

  • 上图表示定义了一个注解类,并且被target修饰且只能用在type(类)里面 , 那么这个MyTest3注解了就只能修饰类 , 不能修饰成员方法和成员变量

  • retention

  • 源码(刚刚写好的java代码)(Source)->编译时(.class文件)(默认值)->运行时(runtime开发时常用)

注解的解析

image-20230403182234256

解析注解练习

  • 第一行得到类的对象
  • 第二行得到类中方法的对象
    • getDeclaredMethod括号里面的参数是方法的名字

image-20230403182823158

练习一:解析类的注解
  • 如果在maven项目里面使用test注解,需要现在test所在类里面补充main方法
//先定义一个注解类
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//定义使用范围
@Target({ElementType.TYPE,ElementType.METHOD})
//定义存活时间
@Retention(RetentionPolicy.RUNTIME)
public @interface Mytest4 {
    //定义参数
    String value();
    double aaa() default 100;
    //数组元素
    String[] bbb();
}

//定义一个使用注解的类
@Mytest4(value = "蜘蛛精",aaa = 99.5,bbb={"123","456"})
public class Demo {
    @Mytest4(value = "哈哈哈",aaa = 1999.9,bbb={"好好学习","天天向上"})
    public void test1(){
    }
}

//测试类
//获取被注解修饰的类的注解信息
import org.junit.Test;
import java.util.Arrays;


public class AnnotationTest3 {
    public static void main(String[] args) {

    }
    @Test
    public void parseClass() throws NoSuchMethodException {
        //先要获取类的对象
        Class c = Demo.class;       
        //解析类上的注解
        //判断类上是否存在那个注解
        if (c.isAnnotationPresent(Mytest4.class)){
            Mytest4 mytest4 = (Mytest4) c.getDeclaredAnnotation(Mytest4.class);
            System.out.println(mytest4.value());
            System.out.println(mytest4.aaa());
            System.out.println(Arrays.toString(mytest4.bbb()));

        }

    }

}

练习二:解析方法的注解
  • 注意一定要先获取方法的对象再获取这个方法的注解
 @Test
    public void parseMethod() throws NoSuchMethodException {
        //先获取方法所在类的对象,再用这个类调用这个要获取的方法
        Class c = Demo.class;
        //getDeclaredMethod是获取特定的方法
        //得到方法的对象
        Method test = c.getDeclaredMethod("test1");
        //开始获取方法的注解
        //注意括号里面写的是定义注解的所在类,而不是注解的应用类
        if (test.isAnnotationPresent(Mytest4.class)){
            //注意下面括号里获取的还是定义注解的所在类,通过这个类再去获取应用到方法(test1)上面的注解信息 ,test是test1方法的对象
            Mytest4 mytest4 = test.getDeclaredAnnotation(Mytest4.class);
            System.out.println(mytest4.aaa());
            System.out.println(mytest4.value());
            String string = Arrays.toString(mytest4.bbb());
            System.out.println(string);
        }

    }

}

模拟JUnit框架

image-20230403204314855

//规定一个注解类
package AnnotationTest2;

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 MyTest {

}

//测试类
package AnnotationTest2;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class AnnotationTest {

    //定义一些测试方法 , 其中一些没有注解修饰
    public void test1(){
        System.out.println("======test1======");
    }
    @MyTest
    public void test2(){
        System.out.println("======test2======");
    }

    public void test3(){
        System.out.println("======test3======");
    }
    @MyTest
    public void test4(){
        System.out.println("======test4======");
    }

    //要达到的目的是运行有注解的方法,不运行没有注解的方法
    //这里不能直接右键运行,因为没有运行的菜单,JUnit右键可以直接运行,但是现在是模拟JUnit框架,所以需要自己设计一个运行的方法
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
        //对应invoke方法
        AnnotationTest a = new AnnotationTest();
        //获取到这个类里面所有的测试方法,遍历查看有没有注解修饰,如果有就运行,没有就不运行
        //获得类对象
        Class c = AnnotationTest.class;
        //获得对象里面的所有方法,自动返回数组
        Method[] methods = c.getDeclaredMethods();
        //遍历这个数组
        for (Method method : methods) {
            //先判断是否有注解
            //用每个遍历出来的方法去调用判断是否存在注解
            if (method.isAnnotationPresent(MyTest.class)){
                //如果存在MyTest修饰的注解,就运行,使用invoke方法调用要运行的方法,括号里面是调用者
                method.invoke(a);
            }
        }

    }
}


​	

标签:Java,String,void,笔记,class,注解,public,name
From: https://blog.csdn.net/weixin_73749601/article/details/144547741

相关文章

  • Java笔记(数据结构与算法[树、栈、列表、队列、数组])
    Java笔记(数据结构与算法[树、栈、列表、队列、数组])链表栈,队列,数组树易错点:二叉树的插入,数据往二叉树里面插入的时候,每一个数据都要和每一个节点相比较,不可能插入到某两个节点中间,最后一定是挂(添加)到二叉树的最后一排的某个节点上度:每......
  • Java笔记(抽象类、接口、内部类、final关键字)
    Java笔记(抽象类、接口、内部类、final关键字)(一).抽象类抽象方法所在的类就是抽象类,抽象方法是在public和void中间加一个abstract,表示子类继承父类(父类是抽象类)的方法时必须重写,否则直接报错1.抽象方法和抽象类2.抽象类和抽象方法的定义格式3.抽象类和抽象方法的注意......
  • Java项目整合Redis
    业务分析(思路)一个正常的业务操作是从前端到后端再到数据库,以商城的商品详情为例,当用户点击一个商品跳转进入详情页面时,从前端传入此商品的id,通过请求发至后端,后端接收该参数后即执行相应的方法,执行数据库sql操作。那么假定一个商城有十万或者数十万甚至更多的商品,那么商品数......
  • Java基础知识
    Java基础01.注释注释不会被执行!书写注释是一个非常好的习惯!!!平时写代码一定要注意规范!!Java中的注释有三种:单行注释多行注释文档注释具体操作代码如下:publicclassHelloWorld{publicstaticvoidmain(String[]args){//单行注释:只能注释一行文字......
  • WPF笔记12——List<T> 与 ObservableCollection<T>
    代码1,使用ObservableCollection<T>:/*优点:(1)使用ObservableCollection<Student>来存储学生数据。在WPF中,ObservableCollection是一个非常适合数据绑定的集合类型。当集合中的元素发生变化(如添加、删除、修改元素)时,它会自动通知UI更新,这符合MVVM模式下视图与视图模型之间......
  • Java如何用HaspMap统计次数并排序详解
    java统计单词频率继上一讲Java用PDFTextStripper来解析pdf文件提取文字-ivanlee717-博客园讲了如何接收和解析pdf之后,我们把pdf文件全部转为了String类型的字符串,那么这一讲聊聊怎么去统计每个词出现的频率。正则过滤首先我们需要把单词弄出来,把其他的文字过滤掉。Pattern......
  • JAVA健身房管理系统设计与实现
    大家好我是小俊学长,混迹在java圈的辛苦码农。今天要和大家聊的是一款《JAVA健身房管理系统设计与实现》毕业设计项目。项目源码以及部署相关请联系小俊学长,文末附上联系信息。......
  • (2024最新毕设合集)基于SpringBoot的社交型音乐网站+25664|可做计算机毕业设计JAVA、PHP
    目 录摘要1绪论1.1研究背景与意义1.3研究内容1.4论文结构与章节安排2 社交型音乐网站分析2.1可行性分析2.2系统流程分析2.2.1数据增加流程2.2.2数据修改流程2.2.3数据删除流程2.3 系统功能分析2.3.1功能性分析2.3.2非功能性分析2.4 ......
  • 生鲜供应系统|Java|SSM|VUE| 前后端分离
                 【技术栈】1⃣️:架构:B/S、MVC2⃣️:系统环境:Windowsh/Mac3⃣️:开发环境:IDEA、JDK1.8、Maven、Mysql5.7+4⃣️:技术栈:Java、Mysql、SSM、Mybatis-Plus、VUE、jquery,html5⃣️数据库可视化工具:navicat6⃣️服务器:SpringBoot自带apachetomcat......
  • java小知识-ShutdownHook(优雅关闭)
    作者:京东物流崔冬冬一、先提出一个问题我们如果在JVM退出的时候做一些事情,比如关闭远程链接,怎么实现呢?二、ShutdownHook简介java里有个方法Runtime.getRuntime#addShutdownHook,是否了解呢?ShutdownHook是什么意思呢,看单词解释“关闭钩子”,addShutdownHook就是添加一个关闭钩......