• Surpass Day27——Java 反射机制、注解


    08 反射机制

    1、关于反射机制

    1.1 反射机制有什么用?

    ​ 通过java语言中的反射机制可以操作字节码文件。
    ​ 优点类似于黑客。(可以读和修改字节码文件。)
    ​ 通过反射机制可以操作代码片段。(class文件。)

    反射机制使得对象的创建更加地灵活,可以通过读取配置文件的方式随意改变new的对象,不会将java代码写死

    1.2 反射机制的相关类在哪个包下?

    java.lang.reflect.*;
    
    • 1

    1.3 反射机制相关的重要的类有哪些?

    ​ java.lang.Class:代表整个字节码,代表一个类型,代表整个类。

    ​ java.lang.reflect.Method:代表字节码中的方法字节码。代表类中的方法。

    ​ java.lang.reflect.Constructor:代表字节码中的构造方法字节码。代表类中的构造方法

    ​ java.lang.reflect.Field:代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量)。

    java.lang.Classpublic class User{
            // Field
            int no;
    
            // Constructor
            public User(){
    
            }
            public User(int no){
                this.no = no;
            }
    
            // Method
            public void setNo(int no){
                this.no = no;
            }
            public int getNo(){
                return no;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    1.4 在java中获取Class的三种方式

    ​ 第一种:

    Class c = Class.forName("完整类名");
    //这个完整的类名可以通过读取配置文件的方式获取
    
    • 1
    • 2

    ​ 第二种:

    Class c = 对象.getClass();
    
    • 1

    ​ 第三种:

    Class c = int.class;
    Class c = String.class;
    
    • 1
    • 2

    1.5 获取了Class之后,可以调用无参数构造方法来实例化对象

    ​ //c代表的就是日期Date类型
    ​ Class c = Class.forName(“java.util.Date”);

    ​ //实例化一个Date日期类型的对象
    ​ Object obj = c.newInstance();

    ​ 一定要注意:
    ​ newInstance()底层调用的是该类型的无参数构造方法。
    ​ 如果没有这个无参数构造方法会出现"实例化"异常。

    1.6、如果你只想让一个类的“静态代码块”执行的话,你可以怎么做?

    ​ Class.forName(“该类的类名”);
    ​ 这样类就加载,类加载的时候,静态代码块执行!!!!
    ​ 在这里,对该方法的返回值不感兴趣,主要是为了使用“类加载”这个动作。

    1.7 关于路径问题

    String path = Thread.currentThread().getContextClassLoader()
    					 .getResource("写相对路径,但是这个相对路径从src出发开始找").getPath();	
    
    String path = Thread.currentThread().getContextClassLoader()
    					.getResource("abc").getPath();	//必须保证src下有abc文件。
    
    String path = Thread.currentThread().getContextClassLoader()
    					.getResource("a/db").getPath();	//必须保证src下有a目录,a目录下有db文件。
    	
    String path = Thread.currentThread().getContextClassLoader()
    					 .getResource("com/bjpowernode/test.properties").getPath();	
    	//必须保证src下有com目录,com目录下有bjpowernode目录。
    	//bjpowernode目录下有test.properties文件。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    ​ 这种方式是为了获取一个文件的绝对路径。(通用方式,不会受到环境移植的影响。),但是该文件要求放在类路径下,换句话说:也就是放到src下面。

    ​ src下是类的根路径。

    1.8 IO流方式读取配置文件

    //获取一个文件的绝对路径
    String path = Thread.currrntThread().getContextClassLoader()
        								.getResource("classinfo2.properties").getPath();
    FileReader reader = new FileReader(path);
    Properties pro = new Properties();
    pro.load(reader);
    reader.close();
    //通过key获取value
    String class Name = pro.getProperty("className");
    //这里就得到了配置文件中的Name信息,从而可以使用反射机制来new对象
    System.out.println(className);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    1.9 直接以流的形式返回:

    InputStream reader = Thread.currentThread().getContextClassLoader()
    							.getResourceAsStream("com/bjpowernode/test.properties");
    Properties pro = new Properties();
    pro.load(reader);
    reader.close();
    //通过key获取value
    String class Name = pro.getProperty("className");
    System.out.println(className);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    1.10 IO + Properties,怎么快速绑定属性资源文件

    ​ //要求:第一这个文件必须在类路径下
    ​ //第二这个文件必须是以.properties结尾。

    ResourceBundle bundle = ResourceBundle.getBundle("com/bjpowernode/test");
    String value = bundle.getString(key);
    
    • 1
    • 2

    2、关于JDK中自带的类加载器:

    2.1 什么是类加载器?

    ​ 专门负责加载类的命令/工具。ClassLoader

    2.2 JDK中自带了3个类加载器

    ​ 启动类加载器:rt.jar
    ​ 扩展类加载器:ext/*.jar
    ​ 应用类加载器:classpath

    2.3 关于三个类加载器

    假设有这样一段代码:

    	String s = "abc";
    
    • 1

    代码在开始执行之前,会将所需要类全部加载到JVM当中。
    通过类加载器加载,看到以上代码类加载器会找String.class文件,找到就加载,那么是怎么进行加载的呢?

    • 首先通过“启动类加载器”加载。
      注意:启动类加载器专门加载:C:\Program Files\Java\jdk1.8.0_101\jre\lib\rt.jar。
      rt.jar中都是JDK最核心的类库。
    • 如果通过“启动类加载器”加载不到的时候,会通过"扩展类加载器"加载。
      注意:扩展类加载器专门加载:C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext*.jar
    • 如果“扩展类加载器”没有加载到,那么会通过“应用类加载器”加载。
      注意:应用类加载器专门加载:classpath中的类。

    2.4 java中为了保证类加载的安全,使用了双亲委派机制。

    ​ 优先从启动类加载器中加载,这个称为“父”。“父”无法加载到,再从扩展类加载器中加载,这个称为“母”。双亲委派。如果都加载不到,才会考虑从应用类加载器中加载。直到加载到为止。

    3、Field

    3.1 通过反射机制获取属性的常用方法

    获取到所有public修饰的属性

    Field[] fields = 类名.getFields();
    
    • 1

    获取到属性名

    String fieldName = fields[1].getName();//com.bjpowernode.java.bean.Stu
    String fieldName = fields[1].getSimpleName();//Stu
    
    • 1
    • 2

    获取所有的属性

    Field[] fields = 类名.getDeclaredFields();
    
    • 1

    获取属性的类型

    Class fieldType = fields[1].getType();
    String fName = fieldType.getName();
    
    • 1
    • 2

    获取属性的修饰符列表

    int i = fields[1].getModifiers();//返回的是修饰符的代号
    String modifierString = Modifier.toString(i);
    
    • 1
    • 2

    3.2 通过反射机制反编译一个类的属性

    package reflect;
    //通过反射机制反编译一个类的属性
    import java.lang.reflect.Field;
    import java.lang.reflect.Modifier;
    
    public class reflextTest07 {
        public static void main(String[] args) throws ClassNotFoundException {
            StringBuilder stringBuilder = new StringBuilder();//为了拼接字符串
    
            Class studentClass = Class.forName("reflect.Student");
            Field[] fields = studentClass.getDeclaredFields();
    
            stringBuilder.append(Modifier.toString(studentClass.getModifiers()) + " class " + studentClass.getSimpleName() + "{" + "\n");
            for(Field field:fields){
                stringBuilder.append("\t");
                stringBuilder.append(Modifier.toString(field.getModifiers()) + " ");
                stringBuilder.append(field.getType() + " ");
                stringBuilder.append(field.getName());
                stringBuilder.append(";" + "\n");
            }
    
    
            stringBuilder.append("}");
            System.out.println(stringBuilder);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    3.3 通过反射机制访问一个java对象的属性(重点)

    给属性赋值set、获取属性的值get

    三要素:给s对象的no属性赋值1111
    要素1:对象5
    要素2:no属性
    要素3:1111

    我们不使用反射机制,怎么去访问一个对象的属性呢?

    //创建对象
    Student s = new Student();
    
    //给属性赋值
    s.no = 1111;
    
    //读属性值
    System.out.println(s.no);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    使用反射机制,怎么去访问一个对象的属性。(set get)

    //获取类,通过类实例化对象
    Class studentClass = Class.forName("com.bjpowernode.java.bean.Student");
    Object obj=studentClass.newInstance();// obj就是student对象。(底层调用无参数构造方法)
    
    //获取no属性(根据属性的名称来获取Field)
    Field noFiled = studentClass.getDeclaredField("no");
    
    // 给obj对象(Student对象)的no属性赋值
    noFiled.set(obj,22222);//给obj对象的no属性赋值2222
    
    //读取属性的值
    Object obj = noFiled.get(obj);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    **出现的问题:**私有属性不可以通过上述访问进行赋值访问

    **解决方案:**打破封装(缺点),这样设置之后,在外部也可以访问私有属性

    Field noFiled = studentClass.getDeclaredField("no");
    noField.setAccessible(true);
    
    • 1
    • 2

    4、可变长度参数

    int…args这就是可变长度参数
    语法是:类型…(注意:一定是3个点。)

    1、可变长度参数要求的参数个数是:0~N个。
    2、可变长度参数在参数列表中必须在最后一个位置上,而且可变长度参数只能有1个。
    3、可变长度参数可以当中一个数组来看待

    m2(100);
    m2(200, "abe");
    m2(200, "abc", "def");
    m2(200, "abe", "def", "xyz");
    m3("ab","de","kk","ff");
    String[] strs = {"a","b","c"};
    //也可以传1个数组
    m3(strs);
    m3(new String[]{“我","","","",“人"});
    
    
    public static void m(int... angs){
    	System.out.println("m方法执行了!");
    }
    
    //必须在最后,只能有1个。
    public static void m2(int a, String... args1){
        
    }
    
    public static void m3(String.. args)
    	//args有ength属性,说明args是一个数组!
    	//可以将可变长度参数当做一个数组来看。
    	for(int i = 0; i < args.length; i++){
    		System.out.println(args[i]);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    5、Method

    5.1 通过反射机制获取类的Method的常用方法、

    获取所有的Method(包括私有的!)

    Method[] methods = userServiceClass.getDeclaredMethods();
    
    • 1

    获取修饰符列表

    Modifier.toString(method.getModifiers());
    
    • 1

    获取方法的返回值类型

    method.getReturnType().getSimpleName();
    
    • 1

    获取方法名

    method.getName();
    
    • 1

    方法的修饰符列表(一个方法的参数可能会有多个。

    Class[] parameterTypes = method.getParameterTypes();
    
    • 1
    //获取类
    Class userServiceClass = Class.forName("com.UserService");
    //获取所有的Method(包括私有的!)
    Method[] methods = userServiceClass.getDeclaredMethods();
    //System.out.println(methods.Length);// 2
    
    // 通历Method
    for(Method method:methods){
        //获取修饰符列表
        System.out.println(Modifier.toString(method.getModifiers()));
        //获取方法的返回值类型
        System.out.printIn(method.getReturnType().getSimpleName());
        //获取方法名
        System.out.println(method.getName());
        //方法的修饰符列表(一个方法的参数可能会有多个。)
    	Class[] parameterTypes = method.getParameterTypes();
        for(Class parameterType : parameterTypes){
    		System.out.println(parameterType.getSimpleName());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    5.2 通过反射机制访问一个java对象的属性(重点)

    不使用反射机制,怎么调用方法

    //创建对象
    UserService userService = new UserService();
    //调用方法
    boolean loginSuccess = userService.login( name: "admin", password: "123");
    //System.out.printLn(LoginSuccess);
    System.out.println(loginSuccess?“登录成功":"登录失数");
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    使用反射机制来调用一个对象的方法该怎么做?

    Class userServiceClass = Class.forName("com.bjpowernode.java.service.UserService");
    // 创建对象
    Object obj = userServiceClass.newInstance();
    // 获取Method(区分Method的有参数名和参数列表)
    Method loginMethod = userServiceClass.getDeclaredMethod( "login", String.class);
    Method loginMethod2 = userServiceClass.getDeclaredMethod("login", int.class);
    
    //调用方法有几个要素?也需要4要素。
    //反射机制中最最最最最重要的一个方法,必须记住。                                 
    //四要素:LoginMethod方法
    //LoginMethod方法
    //obj对象
    //"odmin","123”实参
    //retVal ue 返回值
    obj invoke loginMethod方法(“admin”,“123”)
        
    //调用方法
    Object retValue = loginMethod.invoke(obj,"admin","123");
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    6、Construct

    6.1 Construct常用的方法

    获取无参构造方法

    Construct[] constructors = 类名.getDeclaredConstructors();
    String s = Modifier.toString(constructor.getModifiers());
    
    • 1
    • 2

    6.2 通过反射机制new对象

    不使用反射机制怎么创建对象

    Vip v1 = new Vip();
    Vip v2 = new Vip( 10, "zhangsan", "2001-10-11", true);
    
    • 1
    • 2

    使用反射机制怎么创建对象呢?

    Class c = Class.forName("com.bjpowernode.java.bean.Vip");
    
    //调用无参数构造方法
    Object obj = c.newInstance();//jdk8
    System.out.println(obj);
    //获取无参数构造方法
    Constructor con2 = c.getDeclaredConstructor();//jdk13
    Object newObj2 = con2.newInstance();
    
    //调用有参数的构造方法怎么办?
    //第一步:先获取到这个有参数的构造方法
    Constructor con = c.getDeclaredConstructor(int.class, String.class, String.class,boolean.class);
    //第二步:调用构造方法new对象
    Object newObj = con.newInstance(110,"jackson","1990-10-11",true)
    System.out.println(newObj);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    7、获取类的父类和接口

    获取String的父类

    Class superClass = 类名.getSuperclass();
    System.out.println(superClass.getName());
    
    • 1
    • 2

    获取所有的接口

    //获取String类实现的所有接口(一个类可以实现多个接口。)
    Class[] interfaces = stringClass.getInterfaces();
    for(Class in:interfaces){
    	System.out.println(in.getName());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    09 注解

    1、关于注解

    1)注解,或者叫做注释类型,英文单词是:Annotation

    2)注解Annotation是一种引用数据类型。编译之后也是生成xxx.class文件。

    3)怎么自定义注解呢?语法格式?

     [修饰符列表] @interface 注解类型名{
     
    }
    
    • 1
    • 2
    • 3

    4)注解怎么使用,用在什么地方?

    ​ 第一:注解使用时的语法格式是:@注解类型名

    ​ 第二:注解可以出现在类上、属性上、方法上、变量上、注解类型

    2、JDK内置了哪些注解呢?

    ​ java.lang包下的注释类型:

    ​ 掌握:
    ​ Deprecated 用 @Deprecated 注释的程序元素,
    ​ 不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择。

    ​ 掌握:
    ​ Override 表示一个方法声明打算重写超类中的另一个方法声明。

    ​ 不用掌握:
    ​ SuppressWarnings 指示应该在注释元素(以及包含在该注释元素中的
    ​ 所有程序元素)中取消显示指定的编译器警告。

    3、元注解

    1)什么是元注解?
    用来标注“注解类型”的“注解”,称为元注解。

    2)常见的元注解有哪些?
    Target、Retention

    3)关于Target注解:
    这是一个元注解,用来标注“注解类型”的“注解”
    这个Target注解用来标注“被标注的注解”可以出现在哪些位置上。

    @Target(ElementType.METHOD)//表示“被标注的注解”只能出现在方法上。
    
    • 1
    @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})//表示该注解可以出现在:构造方法上、字段上、局部变量上、方法上、....类上...
    
    • 1

    ​ 4)关于Retention注解:
    ​ 这是一个元注解,用来标注“注解类型”的“注解”
    ​ 这个Retention注解用来标注“被标注的注解”最终保存在哪里。

    @Retention(RetentionPolicy.SOURCE)//表示该注解只被保留在java源文件中。
    
    • 1
    @Retention(RetentionPolicy.CLASS)//表示该注解被保存在class文件中。
    
    • 1
    @Retention(RetentionPolicy.RUNTIME)//表示该注解被保存在class文件中,并且可以被反射机制所读取。
    
    • 1

    4、Retention的源代码

    //元注解	
    public @interface Retention {
    	//属性
    	RetentionPolicy value();
    }
    		
    RetentionPolicy的源代码:
    public enum RetentionPolicy {
    	SOURCE,
    	CLASS,
    	RUNTIME
    }
    
    //@Retention(value=RetentionPolicy.RUNTIME)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnnotation{}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    5、注解在开发中有什么用呢?

    ​ 需求:
    ​ 假设有这样一个注解,叫做:@Id,这个注解只能出现在类上面,当这个类上有这个注解的时候,要求这个类中必须有一个int类型的id属性。如果没有这个属性就报异常。如果有这个属性则正常执行!

  • 相关阅读:
    从浏览器输入一个URL到最终展示出页面,中间会发送哪些事儿?
    java计算机毕业设计-中小学教育机构培训系统-源代码+系统+数据库+lw文档
    LeetCode 449. Serialize and Deserialize BST【树,BFS,DFS,栈】困难
    工程伦理--7.2 可持续发展概念的定义与内涵
    RabbitMQ快速入门
    找不到msvcp120.dll怎么办?msvcp120.dll修复方法分享!
    SQLite 判断表存在/删除表/创建表及if not exist插入数据
    边缘计算的优势
    Python之函数详解
    超图聚类论文阅读2:Last-step算法
  • 原文地址:https://blog.csdn.net/m0_61163395/article/details/125549845