JavaSE -- 注解+反射

注解

  • 在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,
    忽略警告等。在JavaEE/Android中注解占据了更重要的角色,例如
    用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗
    代码和XML配置等。
  • 未来的开发模式都是基于注解的,JPA是基于注解的,Spring2.5以
    上都是基于注解的,Hibernate3.x以后也是基于注解的,现在的
    Struts2有一部分也是基于注解的了,注解是一种趋势,一定程度上
    可以说:框架 = 注解 + 反射 + 设计模式。

常见Annotation
使用 Annotation 时要在其前面增加 @ 符号, 并 把该 Annotation 当成
一个修饰符使用。用于修饰它支持的程序元素

  • @Override: 限定重写父类方法, 该注解只能用于方法
  • @Deprecated: 用于表示所修饰的元素(类, 方法等)已过时。通常是因为
    所修饰的结构危险或存在更好的选择
  • @SuppressWarnings: 抑制编译器警告
package com.annotation.javadoc;
public class AnnotationTest{
public static void main(String[] args) {
@SuppressWarnings("unused")
int a = 10;
}
@Deprecated
public void print(){
System.out.println("过时的方法");
}
@Override
public String toString() {
return "重写的toString方法()";
}
}

自定义注解
定义新的 Annotation 类型使用 @interface 关键字
 自定义注解自动继承了java.lang.annotation.Annotation 接口
 Annotation 的成员变量在 Annotation 定义中以无参数方法的形式来声明。其
方法名和返回值定义了该成员的名字和类型。我们称为配置参数。类型只能
是八种基本数据类型、String 类型 、Class 类型 、enum 类型 、Annotation 类型 、
以上所有类型的 数组。
 可以在定义 Annotation 的成员变量时为其指定初始值, 指定成员变量的初始
值可使用 default 关键字
 如果只有一个参数成员,建议使用 参数名为value
 如果定义的注解含有配置参数,那么使用时必须指定参数值,除非它有默认
值。格式是“参数名 = 参数值”,如果只有一个参数成员,且名称为value,
可以省略“value=”
 没有成员定义的 Annotation 称为 标记; 包含成员变量的 Annotation 称为元数
据 Annotation
注意:自定义注解必须配上注解的信息处理流程才有意义。

//定义一个注解
//@Target表示我们的注解可以用在那些地方
@Target(value={ElementType.METHOD,ElementType.Type})
//@Retention 表示我们的注解在什么地方有效 runtime>class>souces
@Retention(value=RetentionPolicy.RUNTIME)
//@Documented 表示是否将我们的注解声称在javadoc中
@Doucumented
//@Inherited 表示子类可以继承父类的注解 
@Inherited
public @interface MyAnnotation{

}
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation{

	//注解的参数 数据类型 参数名称 ();
	//default 默认值
	String naem() default "";
	int age() default -1; // 默认值 -1 代表不存在
}

class Test{
	public static void main(String[] args){
		//调用注解 
		//注解可以显示赋值,如果没有默认值,必须赋值
		@MyAnnotation()
		public void test(){
		
		}
	}
}

反射

  • Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期
    借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内
    部属性及方法。
  • 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个
    类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可
    以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看
    到类的结构,所以,我们形象的称之为: 反射。

正常方式:
引入需要的”包类”名称–》 通过new实例化 --》取得实例化对象
反射方式:
实例化对象 --》 getClass()方法 --》得到完整的“包类”名称

1 、动态语言
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以
被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是 在运
行时代码可以根据某些条件改变自身结构。
主要动态语言:Object-C、C#、JavaScript、PHP、Python、Erlang。
2 、静态语言
与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、
C++。

Java反射机制提供的功能

在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时获取泛型信息
在运行时调用任意一个对象的成员变量和方法
在运行时处理注解
生成动态代理

Java反射相关的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可以完整地得到一个类中的所有被加载的结构
     Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的
    Class对象

Class类中常用的方法
JavaSE -- 注解+反射_第1张图片
获取Class类的实例
1) 前提:若已知具体的类,通过类的class属性获取,该方法最为安全可靠,
程序性能最高
实例: :Class clazz = String.class;

2) 前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象
实例: :Class clazz = “www.atguigu.com”.getClass();

3) 前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方
法forName()获取,可能抛出 ClassNotFoundException
实例: :Class clazz = Class.forName(“java.lang.String”);

4)其他方式(不做要求)
ClassLoader cl = this.getClass().getClassLoader();
Class clazz4 = cl.loadClass(“类的全类名”);

package com.it.demo03;

/**
 * @author shkstart
 * @create 2020-04-17 9:45
 */
public class Test01 {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person = new Student();
        System.out.println("名字为:" + person.name);

        //方式一:已知对象的全类名 通过forName获取Class对象
        Class c1 = Class.forName("com.it.demo03.Student");

        //方式二:已知类的实例 通过getClass获取Class对象
        Class c2 = person.getClass();

        //方式三:已知类名 通过.class获取Class对象
        Class<Student> c3 = Student.class;

        System.out.println(c1.hashCode() + "\n" + c2.hashCode() + "\n" + c3.hashCode());
    }


}
//实体类

class Person{
    String name;

    public Person(){}

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

    @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对象


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

类的加载过程(重点)
JavaSE -- 注解+反射_第2张图片
JavaSE -- 注解+反射_第3张图片

  • 加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时
    数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问
    入口(即引用地址)。所有需要访问和使用类数据只能通过这个Class对象。这个加载的
    过程需要类加载器参与。
  • 链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。
    1.验证:确保加载的类信息符合JVM规范,例如:以cafe开头,没有安全方面的问题
    2.准备:正式为类变量(static)分配内存并 设置类变量默认初始值的阶段,这些内存
    都将在方法区中进行分配。
    3.解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
  • 初始化
    1.执行类构造器()方法的过程。类构造器()方法是由编译期自动收集类中
    所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信
    息的,不是构造该类对象的构造器)。
    2.当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类
    的初始化。
    3.虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步。
public class ClassLoadingTest {
public static void main(String[] args) {
System.out.println(A.m);
}
}
class A {
static {
m = 300;
}
static int m = 100;
}
//第二步:链接结束后m=0
//第三步:初始化后,m的值由()方法执行决定
// 这个A的类构造器()方法由类变量的赋值和静态代码块中的语句按照顺序合并
产生,类似于
// (){
// m = 300;
// m = 100;
// }

类的初始化

  • 类 的主动引用 ( 一定会发生类的初始化 )
     当虚拟机启动,先初始化main方法所在的类
     new一个类的对象
     调用类的静态成员(除了final常量)和静态方法
     使用java.lang.reflect包的方法对类进行反射调用
     当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
  • 类的被动引用 ( 不会发生类的初始化 )
     当访问一个静态域时,只有真正声明这个域的类才会被初始化
     当通过子类引用父类的静态变量,不会导致子类初始化
     通过数组定义类引用,不会触发此类的初始化
     引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常
    量池中了)
package com.it.demo03;


/**
 * @author shkstart
 * @create 2020-04-17 9:45
 */
public class Test01 {
    public static void main(String[] args) throws ClassNotFoundException {
            //new一个类对象 会发生初始化
            //如果父类没有初始化,先初始化父类
            //Son son = new Son();

            //反射也会使类初始化 java.lang.refect
            //Class.forName("com.it.demo03.Son");

            //-------------主动引用↑ 被动引用↓------------------------------

            //子类引用父类的静态常量 会初始化父类 不会初始化子类
            //System.out.println(Son.f);

            //引用常量不会初始化
            System.out.println(Son.S);

            //定义数组不会初始化
            Son[] sons = new Son[4];

    }
    static {
        System.out.println("main方法被初始化");
    }
}

class Father{

    static int f = 100;
    static {
        System.out.println("Father类被初始化了");
    }
}

class Son extends Father{

    static {
        System.out.println("Son类被初始化了");
        m = 300;
    }
    static int m = 100;

    static final int S = 10;
}

类加载器

  • 类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为
    方法区中类数据的访问入口。
  • 类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
  • 类加载器作用是用来把类(class)装载进内存的。
  • JVM 规范定义了如下类型的类的加载器。
    JavaSE -- 注解+反射_第4张图片
public class Test01 {
    public static void main(String[] args) throws ClassNotFoundException {

        //获取系统类加载器
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        System.out.println(classLoader);

        //获取系统类加载器的父类加载器 即扩展类加载器
        ClassLoader parent = classLoader.getParent();
        System.out.println(parent);

        //获取扩展类加载器父类加载器  即引导类加载器
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);

        //测试当前类是由哪个类加载器进行加载
        ClassLoader c1 = Test01.class.getClassLoader();
        System.out.println(c1);

        //测试JDK提供的Object类由哪个类加载器加载
        c1 = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(c1);

        //关于类加载器的一个主要方法:getResourceAsStream(String str):获取类路
        //径下的指定文件的输入流
        InputStream in = Test01.class.getClassLoader().getResourceAsStream("jdbc.properties");
        System.out.println(in);
    }

}

获取运行时类的完整结构

public class Test01 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {

        User user = new User();
        Class c1 = user.getClass();

        //获取类的名字
        String name = c1.getName(); //获取完整类名
        String simpleName = c1.getSimpleName();//获取简单类名
        System.out.println("完全类名" + name);
        System.out.println("简单类名" + simpleName);
        
		//获取父类Class
		c1.getSuperClass()

        //获取类下所有属性
        Field[] fields = c1.getFields(); //获取所有public修饰的属性
        for (Field field : fields) {
            System.out.println(field);
        }

        fields = c1.getDeclaredFields();//获取类下所有属性
        for (Field field : fields) {
            System.out.println(field);
        }

//        Field field = c1.getField("name");//获取指定public修饰的属性
//        System.out.println(field);

        Field field = c1.getDeclaredField("name");//获取指定类下属性
        System.out.println(field);

        //获取类下所有方法
        Method[] methods = c1.getMethods();//获取类下所有public修饰的方法以及父类的方法
        for (Method method : methods) {
            System.out.println(method);
        }

        methods = c1.getDeclaredMethods(); //获取本类下所有方法
        for (Method method : methods) {
            System.out.println("#" + method);
        }

        Method getName = c1.getMethod("getName", null);//获取指定方法
        Method setName = c1.getMethod("setName",String.class);
        System.out.println(getName);
        System.out.println(setName);

        //获得构造器方法
        Constructor[] constructors = c1.getConstructors();//获取所有public修饰的构造器方法
        constructors = c1.getDeclaredConstructors(); //获取本类所有的构造器方法
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        //获得指定public修饰的构造器方法
        Constructor constructor = c1.getConstructor(String.class,int.class,int.class);
        //获取指定的构造方法
        constructor = c1.getDeclaredConstructor(String.class,int.class,int.class);
        System.out.println(constructor);
    }
}
class User{

    private String name;
    private int age;
    private int id;

    public User() {
    }

    public User(String name, int age, int id) {
        this.name = name;
        this.age = age;
        this.id = id;
    }

    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 int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

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

创建运行时类的对象


        //通过无参构造创建对象
        //要求类必须有无参构造方法
        //要求类的无参构造方法权限足够
        Class c1 = User.class;
        c1.newInstance();
		
		//通过有参构造创建对象
		//获取本类指定参数的构造器
        Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        //通过构造器.newInstance 传入参数 获取对象
        User user = (User) constructor.newInstance("卡莎",18,1);
        System.out.println(user);


调用运行时类的指定结构

 		Class c1 = User.class;
        //通过无参构造创建对象
        User user = (User)c1.newInstance();

        //调用运行类的方法
        //获取指定方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        //通过invoke(); 传入参数执行方法 invoke:激活方法
        // 参数 : (对象,方法的值)
        setName.invoke(user,"卡卡");
        System.out.println(user.getName());


		 //调用运行类的属性
        Field name = c1.getDeclaredField("name");
        //如果调用的属性为private修饰的
        //我们需要将setAccessible 设置为true
        //Accessible:启动和禁用访问安全检查的开关 true:禁用 false:开启
        name.setAccessible(true);
         //通过属性了的set或get方法进行取或存
         //get 取得指定对象obj上此Field的属性内容
         //set 设置指定对象obj上此Field的属性内容
        name.set(user,"你好");
        System.out.println(user.getName());

setAccessible()方法
 Method和Field、Constructor对象都有setAccessible()方法。
 setAccessible启动和禁用访问安全检查的开关。
 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。
 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被
调用,那么请设置为true。
 使得原本无法访问的私有成员也可以访问
 参数值为false则指示反射的对象应该实施Java语言访问检查。

获取泛型信息

public class Test01 {

    public void test01(Map<String,Object> map, List<String> List){
        System.out.println("test01");
    }

    public Map<String,Object> test02(){
        System.out.println("test02");
        return null;
    }
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {

        Class c1 = Test01.class;
        //获取方法
        Method method = c1.getDeclaredMethod("test01", Map.class, List.class);
        //获取方法泛型参数化类型
        Type[] genericParameterTypes = method.getGenericParameterTypes();

        for (Type genericParameterType : genericParameterTypes) {
            System.out.println(genericParameterType);
            //判断是否为参数化类型
            if(genericParameterType instanceof ParameterizedType){
                //强转为参数化类型 获取真实参数类型的值
                Type[] actualTypeArguments = ((ParameterizedType)genericParameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);
                }
            }
        }

    }
}

获取注解信息

public class Test01 {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {

        //1.获取Class
        Class c1 = Class.forName("com.it.demo03.Student");

        //2.通过Class获的注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }

        //3.获取注解的value值
        Table table = (Table)c1.getAnnotation(Table.class);
        System.out.println(table.value());

        //获取属性注解的value
        java.lang.reflect.Field id = c1.getDeclaredField("id");
        Field field = id.getAnnotation(Field.class);
        System.out.println(field.column());
        System.out.println(field.length());
        System.out.println(field.type());


    }
}
@Table("db_student")
class Student{

    @Field(column = "db_id", type = "int",length = 10)
    private int id;
    @Field(column = "db_name", type = "String",length = 10)
    private String name;
    @Field(column = "db_age", type = "int",length = 10)
    private int age;

    public Student() {
    }

    public Student(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

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

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
    String value();
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Field{
    String column();
    String type();
    int length();
}

你可能感兴趣的:(JavaSE-高级)