注解和反射

注解 java.Annotation

java.lang 包下

 

@override

重写注解

@Deprecated

已过时

@Suppresswarnings

镇压警告

@

@加参数名 还可以加一些参数值

   对程序做出解释

  可以被其它程序读取

package com.yang.annotation;
//什么是注解
//镇压警告
@SuppressWarnings("all")
public class Test01 extends Object{
    //重写的注解
    @Override
    public String toString() {
        return "Test01{}";
    }
    //已过时,不推荐使用,但是可以使用
    @Deprecated
    public static void text(){
        System.out.println("已过时");
    }
    //镇压警告
@SuppressWarnings("all")
    public static void main(String[] args) {
        text();
    }
}

元注解  meta-annotation 负责注解其他注解

 public @interface 自定义注解名{

    类型 属性名 () default 值;

  //注解类型

 public Anno2 anno() default @Anno2;

//int 数组

 public int[] arr() defualt {1,2,3,4}

}

属性类型

基本数据类型  String Class 注解 枚举 

以上类型的所有数组

import com.yang.Season;
@Deprecated
@SuppressWarnings("all")
@interface Anno{
  public int a() default 1;
  public String str() default "武则天";
  public Season season() default Season.SPRING;
  public  Class classzz() default Anno.class;
  public  Anno2  anno() default  @Anno2;
  public int[]  arr() default {1,2,3};
}

  

注解属性没有默认值,我们要给定属性值

元注解 描述注解的注解

@Target (限制注解使用在哪个地方)进入类中去看很容理解    FIELD,TYPE ,METHOD

@Retention 注解的保留时间    

@Documented 将注解生成到JAVAdao中 (该注解会出现在帮助文档中)

@Inherited 表示自定义的注解可以被子类继承

package com.yang.annotation;

import java.lang.annotation.*;

//测试元注解
@MyAnnotation
public class Test02 {
    @MyAnnotation
    public void test(){

    }
}
//表示我们额注解可以用在那些地方 词义目标
@Target(value = {ElementType.METHOD,ElementType.TYPE})
//Retention表示注解到什么时候有效 source源码级别 CLASS JAVAC编译后 RUNTIME 运行时
//runtine>class>source
@Retention(value = RetentionPolicy.RUNTIME )
//表示是否将我们的注解生成到 JAVAdao中
@Documented
//表示子类可以继承父类的注解
@Inherited
@interface MyAnnotation{

}

反射 Java Reflection

Java可以通过Reflection(反射)是Java可以被看作是动态语言的关键,反射机制允许程序在执行期间借助Reflection API取得任何类的内部信息(类名 类的接口 类的字段 类的属性等等),,并能操作任意对象的内部属性和方法;

 Class c=Class.forName("java.lang.String")                                                                                                                 

加载完类之后,就在堆内存的方法区中产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了完整类的结构信息。因此我们可以通过对象看到类的结构。这个对象相当于一面镜子,透过这个镜子看到类的结构。

   正常方式  import 包类 名称👉 new实例化对象👉取得实例化对象

   反射方式  实例化对象 👉 getClass()方法 👉得到完整的“包类”名称   

静态语言VS动态语言

动态语言

  在运行时可以改其结构的语言:通俗将运行时可以根据某些条件改变自身的结构;例如:新的函数,对象,甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。

     主要的动态语言:Object-C, C#,JavaScript,PHP,Python等

静态语言

  运行时结构不可变的语言就是静态语言。如Java,C,C++。Java不是动态语言,但Java可以成为“准动态语言”。我们可以利用反射机制获得类似动态语言的特征。
优点 

  可以实现动态创建对象和编译,体现出很大的灵活性

缺点

   对行能有影响。使用反射机制基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于直接执行相同的操作。

常用API

java.lang.class     代表类

java.lang.reflect.Method  代表类的方法

java.lang.reflect.Field  代表类的成员变量‘

java.lang.reflect.Constructor 代表类的构造器

.........

 

Class类

   对象反射后可以得到的信息:某个类的属性,方法和构造器,某个类到底实现了哪些接口。对于每个类而言,JRE都会为其保留一个不变的Class类型的对象。

一个Class对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void[])的有关信息

Class 本身也是一个类

Class对象只能由系统创建对象

一个加载类在JVM中指挥有一个Class实例

一个Class对象对应的是一个加载JVM中的一个Class文件

通过Class可以完整的得到一个中所有被加载的结构

Class是Relection的根源,针对任何你想动态加载,运行的类,唯有先获得相应的Class对象

获取Class类的实例

  1 以知具体的类,通过类的cass属性获取,该方法最为安全可靠,程序性能最高

     Class claxx=Student.class;

       2   以知某个类的实例,调用该实例的getClass()方法获取Class对象;

  Class  claxx=student.getClass()

       3   以知一个类的全类名,且该类在类路径下,通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException

      Class claxx=Class.forName("com.yang.Student")

      4   内置基本数据类型可以直接用类名.Type

     5   还可以利用ClassLoader

 

 

package com.yang.reflection;
//测试class类的创建方式有哪些
public class Test03 {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person=new Student();
        System.out.println("这个人是"+person.name);
        //方式一通过对象获得
        Class c1=person.getClass();
        System.out.println(c1.hashCode());
        //方式二:forName获得
        Class c2=Class.forName("com.yang.reflection.Student");
        System.out.println(c2.hashCode());
        //通过类名.calss获得
        Class<Student> c3 = Student.class;
        System.out.println(c3.hashCode());
        //方式四:基本内置类型的包装类都有一个Type属性
        Class c4=Integer.TYPE;
        System.out.println(c4);
        //获得父类类型
        Class c5=c1.getSuperclass();
        System.out.println(c5);
    }
}
class Person{
    public String name;

    public Person(String name) {
        this.name = name;
    }

    public Person() {
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}
class Student extends Person{
    //构造器
    public Student(){
        this.name="学生";
    }
}
class Teacher extends Person{
    public Teacher(){
        this.name="老师";
    }
}

  哪些类可以有Class类型

 

package com.yang.reflection;

import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;

import java.lang.annotation.ElementType;

public class Test04 {
    public static void main(String[] args) {
        Class c1=Object.class;      //
        Class c2=Comparable.class;  //接口
        Class c3=String[].class;    //一维数组
        Class c4=int[][].class;     //二维数组
        Class c5=Override.class;    //注解
        Class c6= ElementType.class; //枚举
        Class c7=Integer.class;      //基本数据类型
        Class c8=void.class;          //void
        Class c9=Class.class;         //Class

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);
       //只要元素类型与维度一样,就是同一个class(一个类只有一个class对象)
        int[] a=new int[10];
        int[] b=new int[100];
        System.out.println(a.getClass().hashCode());
        System.out.println(b.getClass().hashCode());

    }








}

类的加载过程

程序要使用某个类,该类没有被加载大内存中。需要进行三个步骤

1 有类的加载器。将类加载到内存中,并为之创建一个java.lang.Class对象。

2将类的二进制数据合并到jre中

3类的初始化 JVM负责类的初始化

jdk 包含jre和java开发工具包,可以编译运行Java程序

jre java的运行环境,针对使用Java的客户,能够运行java字节码(.class)。但不能编译java源码

jvmJava虚拟机,能够解释字节码文件.class,但是不能正确执行

jvm  java虚拟机时java跨平台的关键,java程序首先会被编译为字节码.class,JVM的核心任务就是解释.class,将其映射到真正的Cup指令集或被系统调用

jre  Java的运行环境,只有Jvm是不能运行Java的字节码的(.class),因为解释字节码需要lib库

jdk java的核心,包含Java的运行环境jre和一些Java的工具和一些基础类库。

总体来说就是,我们利用JDK(调用JAVA API)开发了属于我们自己的JAVA程序后,通过JDK中的编译程序(javac)将我们的文本java文件编译成JAVA字节码,在JRE上运行这些JAVA字节码,JVM解析这些字节码,映射到CPU指令集或OS的系统调用。

类的加载

加载

     将Class字节码加载到内存中,并将静态的数据转换为方法区运行时动态的数据结构,然后在堆内存中生成java.lang.Class对象,作为方法区中类数据的访问接口

链接

  jre中的lib解释字节码文件,合并到JVM(bin)的运行状态之中。

             步骤1  验证:验证加载类信息(解释的字节码文件)是否符合JVM的规范。没有安全问题。

     步骤2   准备: 正式为变量(Static) 分配内存(内存在方法去中进行分配(特殊的堆内存)), 将静态区变量设为默认值。

    步骤3      解析       虚拟机常量池中的符号引用(常量名),解析为直接引用(地址)

初始化

    类构造器<clinit>()方法    类构造器是构造类信息的,不是构造该类对象的

    <clinit>()类构造器,是由编译时期自动收集类中所有类变量的赋值动作,和静态代码块语句合并产生的

    初始化一个类,若父类没有初始化,先初始化父类

    JVM会保证一个类的

package com.yang.reflection;

import jdk.internal.dynalink.beans.StaticClass;

public class Test05 {
    public static void main(String[] args) {
       A a=new A();
        System.out.println(A.m);
        /*
        1 加载到内存产生一个class对象
        2 链接 链接结束后 m=0
        3c初始化 <clinit>(){
                //合并代码块
                System.out.println("A静态代码初始化");
                m=300;
                 System.out.println("A的无参构造初始化");
        }
         */
    }
}
class A{
    static {
        System.out.println("A静态代码初始化");
        m=300;
    }
    static  int m=100;
    public A(){
        System.out.println("A的无参构造初始化");
    }
}

m最后等于100

<clinit>()类构造器在多线程中的被正确的加锁或同步。

 

 

类的主动引用

 1当JVM启动时先初始化Main方法所在的类

 2 new 一个类新的对象时

3 调用类的静态成员(除了final常量)和静态方法

4 使用java.lang.reflect包的方法对类进行反射调用

5 当初话一个类时,先初始化他的父类

类的被动引用(不会发生类的初始化)

  子类调用父类的静态成员变量或者方法不会产生子类的初始化

 通过数组定义类的引用,不会触发此类的初始化

引用常量不会触发此类的初始化(常量在链接阶段就调入常量池中了)

package com.yang.reflection;
//测试类什么时候会初始化和

import jdk.internal.dynalink.beans.StaticClass;

public class Test06 {
    static {
        System.out.println("Main类被加载");
    }

    public static void main(String[] args) throws ClassNotFoundException {
        //主动引用
        //Son son=new Son();
        //反射也会产生主动引用
        //Class.forName("com.yang.reflection.Son");
        //不会产生类的阴用的方法
        //子类调用父类的静态变量或方法不会产生子类的初始化
        System.out.println(Son.b);
        //通过数组定义类的引用并不会触发类的初始化
        Son[] arr=new Son[10];
        //调用类中的常量不会触发父类和子类的初始化
    }
}
class Father{
    static int b=100;
    static{
        System.out.println("父类被加载");
    }
}
class Son extends Father{
    static {
        System.out.println("子类被加载");
        m=300;
    }
    static int m=100;
    static  final int M=1;
}

类加载器的作用

 

加载

     将Class字节码加载到内存中,并将静态的数据转换为方法区运行时动态的数据结构,然后在堆内存中生成java.lang.Class对象,作为方法区中类数据的访问接口

类缓存

 标准的JavaSe类加载器可以按照要求查找类,但一旦某个类被加载到类加载器中,它将维持加载一段时间,不过JVM垃圾回收机制可以回收这些Class类对象。

类加载器

  引导类加载器        C++编写的,时JVM自带的类加载器,用来装载核心类库,该类加载器无法直接获取    rt.jar

  扩展类加载器   负责jre/lib/ext目录下的jar包或者D java。ext。dirs指定目录的下jar包装入工作库

系统类加载器      负责java-classpath或者 -D java.class.path 所指的目录下的类与jar包的装入工作,时常用的类加载器

 获取运行时类的完整结构

 Field Method Constructor Superclass  Interface Annotation

package com.yang.reflection;
import java.lang.Class;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

//获得类的信息
public class Test08 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class c1=Class.forName("com.yang.reflection.User");
        //获得类的名字
        System.out.println(c1.getName());//获得包名+类名
        System.out.println(c1.getSimpleName());//获得类名
        //获得类的属性
        System.out.println("===================================");
        Field[] fields=c1.getFields();//只能找到public属性
        for (Field field : fields) {
            System.out.println(field);
        }
        fields=c1.getDeclaredFields();//找到全部的属性
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("====================================");
        //获得指定属性的值
        Field field=c1.getDeclaredField("name");
        System.out.println(field);
        System.out.println("====================================");

        //获得类的方法
        Method[] methods=c1.getMethods(); //获取本类及其父类的public所有方法
        for (Method method : methods) {
            System.out.println("正常的"+method);
        }
        Method[] methods1=c1.getDeclaredMethods();  //获取本类的所有方法
        for (int i = 0; i < methods1.length; i++) {
            System.out.println("getDeclaredMethods"+methods1[i]);
        }
        System.out.println("====================================");

        //获得指定的方法
        //重载 所以需要参数
       Method method= c1.getMethod("getName",null);
       Method method1=c1.getMethod("setName", String.class)  ;

        System.out.println("无参获取指定的方法"+method);
        System.out.println("有参获取指定的方法"+method1);
        System.out.println("====================================");
        //获得指定的构造器
        Constructor[] constructor=c1.getConstructors();
        for (Constructor constructor1 : constructor) {
            System.out.println(constructor1);
        }
        Constructor[] constructors=c1.getDeclaredConstructors();
        for (Constructor constructor1 : constructors) {
            System.out.println(constructor1);
        }
        //获得指定的构造器
        Constructor constructor1=c1.getDeclaredConstructor(String.class,int.class,int.class);
        System.out.println("指定构造器"+constructor1);
        
       
    }
}

 

 动态创建对象的执行方法

有了class对象能做什么

 public static <user3> void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        //获得class对象
        Class c=Class.forName("com.yang.reflection.User");
        //构造一个对象
        User user=(User) c.newInstance();//本质上调用了无参构造器,没有无参构造报错。
        System.out.println(user);

        //通过构造器创建对象
        Constructor constructors=c.getDeclaredConstructor(String.class,int.class,int.class);
        User user1=(User) constructors.newInstance("王重阳",001,18);
        System.out.println("构造器创建的对象"+user1);

        //通过反射调用方法
        User user2= (User) c.newInstance();
        //通过反射获取一个方法
        Method method=c.getMethod("setName", String.class);
        //invoke 激活
        method.invoke(user2,"王重阳");
        System.out.println(user2.getName());

        //通过反射操作属性
        User user3=(User)c.newInstance();
        Field field=c.getDeclaredField("name");
        //不能操作私有成员变量,需要求该权限检测的开关
        field.setAccessible(true);//关掉权限检测
        field.set(user3,"王老六");
        System.out.println(user3.getName());
        
    }

  

关闭权限检测能减少利用反射调用方法的运行时间

setAccessible(true)

 

反射操作泛型

java采用泛型擦除机制来引入泛型,java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制转换类型转换问题,但是一旦编译完成,所有和泛型相关的类型全部擦除。

 

为了通过反射操作这些类型,Java新增了ParameterizedType,GenericArrayType,TypeVariable,和wildcardType几种类型来代不能被归一到Class类型但是又和原始类型齐名的类型

 

posted @ 2021-10-13 21:30  互联.王  阅读(23)  评论(0编辑  收藏  举报