java学习笔记(三):反射(reflection)【下】

注:该文章是本人通过在BiliBili学习网站的Up主 @遇见狂神说 的教学视频(Bv1p4411P7V3)下学习之后所写的学习总结。在此衷心感谢狂神免费的教学视频!

java学习笔记(二):反射(reflection)【上】

四、ClassLoader

ClassLoader翻译过来就是类加载器,ClassLoader的具体作用就是将class文件加载到jvm虚拟机中去,程序就可以正确运行了。在上文中我们已经说明了类加载的过程和类加载器的作用。JVM中规范定义了如下类型的类的加载器。

  • 引导类加载器:是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库,该加载器无法直接获取。
  • 扩展类加载器:负责jre/lib/ext目录下的jar包或 -D java ext.dirs指定目录下的jar包装入工作库
  • 系统类加载器:负责java -classpath 或 -D java.class.path所指的目录下的类与jar包装入工作,是最常用的加载器。

那么接下来将举一些例子来使用ClassLoader进行一些类加载器的获取。

package kfy.Reflection;

public class test3 {
    public static void main(String[] args) throws ClassNotFoundException {

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

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

        //获取扩展类加载器的父类加载器 -> 根加载器
        ClassLoader parent2 = parent.getParent();
        System.out.println(parent2);

        //测试当前类是那个加载器加载的
        ClassLoader classLoader = Class.forName("kfy.Reflection.test3").getClassLoader();
        System.out.println(classLoader);

        //测试JDK内置的类是那个加载器加载的
        classLoader = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader);

    /*扩展:获得系统类加载器可以加载的路径*/
    /*
       System.out.println(System.getProperty("java.class.path"));
    */
    }
}

运行结果:
sun.misc.Launcher A p p C l a s s L o a d e r @ 18 b 4 a a c 2 < / b r > s u n . m i s c . L a u n c h e r AppClassLoader@18b4aac2</br> sun.misc.Launcher AppClassLoader@18b4aac2</br>sun.misc.LauncherExtClassLoader@1540e19d
null
sun.misc.Launcher$AppClassLoader@18b4aac2
null

五、获取类的运行时结构

通过反射获取运行时类的完整结构,包括Field、Method、Constructor、SuperClass、Interface、Annotation等。

例子:

package kfy.Reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class test4 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class c1 = Class.forName("kfy.Reflection.User2");

        //获得类的名字
        System.out.println(c1.getName());  //获得包名+类名
        System.out.println(c1.getSimpleName());  //获得类名

        System.out.println("================");

        //同理
        User2 user = new User2();
        c1 = user.getClass();
        System.out.println(c1.getName());  //获得包名+类名
        System.out.println(c1.getSimpleName());  //获得类名

        System.out.println("================");

        //获得类的属性
        // Field[] fields = c1.getFields(); 只能获取公有的属性
        Field[] fields = c1.getDeclaredFields(); //获取所有的属性
        for (Field field: fields) {
            System.out.println(field);
        }

        System.out.println("================");

        //也能单独获取某个具体指定的属性
        Field name = c1.getDeclaredField("name");
        System.out.println(name);

        System.out.println("================");

        //获得类的方法
        Method[] methods = c1.getMethods();//获得本类及其父类的全部public方法,得不到私有方法
        for (Method method : methods) {
            System.out.println("getMethods:"+method);
        }
        methods = c1.getDeclaredMethods();//获得本类的所有方法,能得到私有方法
        for (Method method : methods) {
            System.out.println("getDeclaredMethods:"+method);
        }

        System.out.println("================");

        //获得指定方法(因为可能存在重载,所以需要通过参数来获取具体的方法)
        Method getName = c1.getMethod("getName",null);
        Method setName = c1.getMethod("setName", String.class);
        System.out.println(getName);
        System.out.println(setName);

        System.out.println("================");

        //获得指定的构造器
        Constructor[] constructors = c1.getConstructors(); //获得public
        for (Constructor constructor : constructors) {
            System.out.println("getConstructors:"+constructor);
        }
        constructors = c1.getDeclaredConstructors(); //获得全部
        for (Constructor constructor : constructors) {
            System.out.println("getDeclaredConstructors:"+constructor);
        }

        //获得指定的构造器
        Constructor constructor1 = c1.getDeclaredConstructor(int.class,String.class);
        System.out.println(constructor1);

    }
}


//pojo 实体类
class User2{
    private int id;
    private String name;
    public User2() {
    }
    public User2(int id, String name) {
        this.id = id;
        this.name = name;
    }
    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;
    }

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

六、通过反射动态创建对象

在上一节我们能够通过反射取得Class对象后,我们便可以通过反射来动态创建对象了。

创建类的对象:调用Class对象的newinstance()方法。

  • (1)类必须有一个无参数的构造器
  • (2)类的构造器需要有足够的访问权限

如果要使用有参构造器,则需要以下步骤:

  1. 通过Class类的getDeclaredConstructor(Class …parameterTypes)取得本类的指定形参类型的构造器
  2. 向构造器的形参中传递一个对象数组,里面包含了构造器中所需的各个参数
  3. 通过Constructor实例化对象

通过反射调用指定的方法:

  1. 通过Class类的getMethod(String name,Class …parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型
  2. 使用Object invoke(Object obj,Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息

SetAccessible方法:

  1. Method、Field、Constructor对象都有该方法
  2. 作用是启动和禁用访问安全检查的开关
  3. 参数值为true则表示关闭访问安全检查

接下来通过例子来说明如何通过反射动态创建对象

package kfy.Reflection;

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

//通过反射动态的创建对象
public class test5 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {

        //获取Class对象
        Class c1 = Class.forName("kfy.Reflection.User2");//这里继续用上一个例子中的User2实体类

        //构造一个对象
        User2 user = (User2)c1.newInstance(); //本质是调用了类的无参构造器,如果该类没有无参构造器则会报错
        System.out.println(user);

        //通过有参构造器创建对象
        Constructor constructor = c1.getDeclaredConstructor(int.class,String.class);
        User2 user2 = (User2) constructor.newInstance(1,"沐可");
        System.out.println(user2);

        //通过反射调用普通方法
        User2 user3 = (User2) c1.newInstance();
        //通过反射获取一个方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        /**
         * invoke:激活的意思
         * (对象,"方法的值")
         */
        setName.invoke(user3,"沐可2");
        System.out.println(user3.getName());

        //通过反射操作属性
        User2 user4 = (User2) c1.newInstance();
        Field name = c1.getDeclaredField("name");
        //因为name是私有属性,不能直接访问,想要直接操作它必须关掉程序的安全检测
        name.setAccessible(true); //true表示关闭安全检测
        name.set(user4,"沐可3");
        System.out.println(user4.getName());

    }
}

六、通过反射操作泛型

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

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

  • ParameterizedType:表示一种参数化类型,比如Collection
  • GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
  • TypeVariable:是各种类型变量的公共父接口
  • WildcardType:代表一种通配符类型表达式

通过例子来理解如何通过反射来操作泛型:

package kfy.Reflection;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

public class test6 {

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

    public Map<String,User2> test02(){
        System.out.println("test02");
        return null;
    }

    public static void main(String[] args) throws NoSuchMethodException {
       Method method = test6.class.getMethod("test01", Map.class, List.class);
       //获取它的参数类型
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println("#"+genericParameterType);
            //判断这个类型是否属于结构化参数类型
            if(genericParameterType instanceof ParameterizedType){
                //是的话就来获取他的真实参数信息
               Type[] types = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (Type type : types) {
                    System.out.println(type);
                }
            }
        }

        method = test6.class.getMethod("test02", null);
        //获取它的返回值类型
        Type genericParameterType = method.getGenericReturnType();
        //判断这个类型是否属于结构化参数类型
        if(genericParameterType instanceof ParameterizedType){
            //是的话就来获取他的真实参数信息
            Type[] types = ((ParameterizedType) genericParameterType).getActualTypeArguments();
            for (Type type : types) {
                System.out.println(type);
            }
        }
    }
}

七、通过反射获取注解信息

1、什么是ORM?

Object relationship Mapping -->对象关系映射

  • 类和数据库表结构对应
  • 属性和数据库字段对应
  • 对象和数据库记录对应
2、反射操作注解

我们直接通过例子来说明:

package kfy.Reflection;

import java.lang.annotation.*;
import java.lang.reflect.Field;

//反射操作注解
public class test7 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("kfy.Reflection.Student2");

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

        //获取注解的value的值
        TypeAnnotation annotation = (TypeAnnotation)c1.getAnnotation(TypeAnnotation.class);
        String value = annotation.value();
        System.out.println(value);

        //获得类指定的注解
        Field f = c1.getDeclaredField("name");
        FieldAnnotation annotation1 = f.getAnnotation(FieldAnnotation.class);
        System.out.println(annotation1.columnName());
        System.out.println(annotation1.type());
        System.out.println(annotation1.length());


    }
}

//一个简单的实体类
@TypeAnnotation("db_Student")
class Student2{
    @FieldAnnotation(columnName = "db_id",type = "int",length = 10)
    private int id;
    @FieldAnnotation(columnName = "db_age",type = "int",length = 10)
    private int age;
    @FieldAnnotation(columnName = "db_name",type = "varchar",length = 3)
    private String name;

    public Student2() {
    }

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

    public int getId() {
        return id;
    }

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

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

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

//自定义一个类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TypeAnnotation{
    String value();
}

//自定义一个属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldAnnotation{
    String columnName();
    String type();
    int length();
}

八、总结

我们已经学习完了注解和反射,具体了了解了什么是注解,如何自定义一个注解,以及什么是反射,如何通过反射来获取对象以及对象的信息,最后更是学会了如何用反射来获得注解信息。在学完这些基础之后,再去学习SpringBoot等相关知识就会更加容易理解。那么注解和反射的学习笔记就到此结束。我们下一个知识点再见。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值