java中的反射机制

目录

一、了解类加载器

二、读取配置文件时有两种方法:

三、关于java.lang.Class类的理解

五、获取运行时类(获取Class实例)

六、创建运行时类的对象

七、对运行时类的对象进行操作

1、获取属性结构:

2、获取属性的权限修饰符 数据类型 变量名

3、获取方法结构:

4、获取方法的其他结构@Xxxx权限修饰符 返回值类型 方法名(参数类型1 形参名1,...)throws XxxException{}

5、获取构造器

6、取运行时类的父类

7、获取运行时类的带泛型的父类

8、获取运行时类的带泛型的父类的泛型

9、获取运行时类实现的接口

10、获取当前运行时类所在的包

11、获取当前运行时类声明的注解

12、调用运行时类中指定的结构:属性、方法、构造器(重点)

 问题


一、了解类加载器

public class ClassLoaderTest {
    @Test
    public void test1(){
//        对于自定义类,使用系统类加载器进行加载
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println(classLoader);
//      调用系统类加载器的getParent(),获取扩展类加载器
        ClassLoader classLoaderParent = classLoader.getParent();
        System.out.println(classLoaderParent);
//        调用扩展类加载器的getParent(),无法获取引导类加载器
        ClassLoader parent = classLoaderParent.getParent();
        System.out.println(parent);

        ClassLoader classLoader1 = String.class.getClassLoader();
        System.out.println(classLoader1);
    }

二、读取配置文件时有两种方法:

properties():用来读取配置文件的一个方法
public void test2() throws Exception {
        Properties properties = new Properties();

//        读取配置文件的方式一:
//        FileInputStream fis = new FileInputStream("jdbc.properties");                          //此时文件默认在当前module下
//        properties.load(fis);

//        读取配置文件的方式二:使用ClassLoader
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        InputStream resourceAsStream = classLoader.getResourceAsStream("jdbc1.properties");//此时文件默认在本module下的src中
        properties.load(resourceAsStream);


        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        System.out.println("user = "+user + ",password = "+ password);
    }

三、关于java.lang.Class类的理解

Class类的实例对应着加载到内存中的一个运行时类。
    1.类的加载过程:
        程序经过javac.exe命令以后,会生成一个或多个字节码文件(.cLass结尾)。
        接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。
    此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。

    2.换句话说,Class的实例就对应着一个运行时类。

    3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。

四、那些类型可以有Class对象?

(1) class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
(2) interface:接口
(3)[]:数组
(4) enum:枚举
(5) annotation:注解@interface
(6) primitive type:基本数据类型
(7) void

五、获取运行时类(获取Class实例)

public void test3() throws ClassNotFoundException {
//        方式一:调用运行时类的属性:.class
        Class<Person> personClass = Person.class;
        System.out.println(personClass); //class com.haiyu.java.Person
//        方式二:通过运行时类的对象,调用getClass()

        Person person = new Person();
        Class aClass2 = person.getClass();
        System.out.println(aClass2);

//        方式三:调用Class的静态方法:.forName(String classpath)
        Class aClass = Class.forName("com.haiyu.java.Person");
        System.out.println(aClass);
//        方式四:使用类的加载器:ClassLoader (了解)
        ClassLoader classLoader = ReflectionTest.class.getClassLoader();
        Class<?> aClass1 = classLoader.loadClass("com.haiyu.java.Person");
        System.out.println(aClass1);

    }

六、创建运行时类的对象

newInstance():调用此方法,创建对应的运行时类的对象

要想此方法正常的创建运行时类的对象,要求:
1.运行时类必须提供空参的构造器   2.空参的构造器的访问权限得够。通常就是public的

在javabean中要求提供一个public的空参构造器,原因:
1.便于通过反射创建运行时类的对象,
2.便于子类继承此运行时类时,默认调用super(),保证父类有此构造器
 Class<Person> personClass = Person.class;//获取运行时类
        
 Person person = personClass.newInstance();//调用运行时类的newInstance()创建对象,调的是空参构造器

七、对运行时类的对象进行操作

1、获取属性结构:

    public void test1(){
        Class clazz = Person.class;

//        getFields():获取当前运行时类及其所有父类中声明为public的属性
        Field[] field = clazz.getFields();
        for (Field f : field){
            System.out.println(f);
        }
        System.out.println("*********************");

//        getDeclaredFields():获取当前运行时类声明的所有属性(不包括父类中声明的属性)
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field f : declaredFields){
            System.out.println(f);
        }
    }

2、获取属性的权限修饰符 数据类型 变量名

public void test2(){
        Class<Person> clazz = Person.class;

        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field f : declaredFields){
//            获取属性的权限修饰符
            int modi = f.getModifiers();
            System.out.print(Modifier.toString(modi)+ "\t");
//            获取属性的数据类型
            Class<?> type = f.getType();
            System.out.print(type.getName() + "\t");
//            获取属性的变量名
            String name = f.getName();
            System.out.print(name);

            System.out.println();

        }

    }

3、获取方法结构:

public void test1() {
        Class clazz = Person.class;
//        getMethods():获取当前运行时类及其所有父类中声明为public的方法

        Method[] methods = clazz.getMethods();
        for (Method m : methods) {
            System.out.println(m);
        }

        System.out.println("*******************************");
//        getDeclaredMethods():获取当前运行时类声明的所有方法(不包括父类中声明的属性)
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method m : declaredMethods) {
            System.out.println(m);
        }

    }

4、获取方法的其他结构
@Xxxx
权限修饰符 返回值类型 方法名(参数类型1 形参名1,...)throws XxxException{}

 public void test2() {
        Class clazz = Person.class;
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method m : declaredMethods) {
//            1.获取方法声明的注解
            Annotation[] annotations = m.getAnnotations();
            for (Annotation a : annotations) {
                System.out.println(a);
            }
//            2.获取权限修饰符
            int modifiers = m.getModifiers();
            System.out.print(Modifier.toString(modifiers) + "\t");
//            3.返回值类型
            System.out.print(m.getReturnType().getName() + "\t");
//            4.方法名
            System.out.print(m.getName() + "(");
//            5.形参列表
            Class<?>[] parameterTypes = m.getParameterTypes();
            if (!(parameterTypes == null || parameterTypes.length == 0)) {
                for (int i = 0; i < parameterTypes.length; i++) {
                    if (i == parameterTypes.length - 1) {
                        System.out.print(parameterTypes[i].getName() + " args_" + i);
                        break;
                    }
                    System.out.print(parameterTypes[i].getName() + " args_" + i + ",");
                }
            }
            System.out.print(")");
//            6.抛出的异常
            Class<?>[] exceptionTypes = m.getExceptionTypes();
            if (!(exceptionTypes == null || exceptionTypes.length == 0)) {
                System.out.print(" throws ");
                for (int i = 0; i < exceptionTypes.length; i++) {
                    if (i == exceptionTypes.length - 1) {
                        System.out.print(exceptionTypes[i].getName());
                        break;
                    }
                    System.out.print(exceptionTypes[i].getName() + ",");
                }
            }
            System.out.println();
        }

    }

5、获取构造器

public void test1() {
        Class clazz = Person.class;
        // getConstructors():获取当前运行时类中声明为public的构造器
        Constructor[] constructors = clazz.getConstructors();
        for (Constructor c : constructors) {
            System.out.println(c);
        }

        System.out.println();
        // getDeclaredConstructors():获取当前运行时类中声明的所有构造器
        Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
        for (Constructor d : declaredConstructors) {
            System.out.println(d);
        }
    }

6、取运行时类的父类

 @Test
    public void test2() {
        Class clazz = Person.class;
        Class superclass = clazz.getSuperclass();
        System.out.println(superclass);
    }

7、获取运行时类的带泛型的父类

public void test3() {
        Class clazz = Person.class;
        Type genericSuperclass = clazz.getGenericSuperclass();
        System.out.println(genericSuperclass);
    }

8、获取运行时类的带泛型的父类的泛型

public void test4() {
        Class clazz = Person.class;
        Type genericSuperclass = clazz.getGenericSuperclass();
//        强转为这个:带参数的类型
        ParameterizedType p = (ParameterizedType) genericSuperclass;
//          获取带参数的类型的参数
        Type[] actualTypeArguments = p.getActualTypeArguments();
        for (Type a : actualTypeArguments){
            System.out.println(a.getTypeName());
        }

    }

9、获取运行时类实现的接口

public void test5(){
        Class clazz = Person.class;
        Class[] interfaces = clazz.getInterfaces();
        for (Class i : interfaces){
            System.out.println(i);
        }

        System.out.println();
//获取运行时类的父类实现的接口
        Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
        for (Class i : interfaces1){
            System.out.println(i);
        }
    }

10、获取当前运行时类所在的包

 public void tset6(){
        Class clazz = Person.class;
        Package aPackage = clazz.getPackage();
        System.out.println(aPackage);
    }

11、获取当前运行时类声明的注解

public void tset7(){
        Class clazz = Person.class;
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation a : annotations){
            System.out.println(a);
        }
    }

12、调用运行时类中指定的结构:属性、方法、构造器(重点)

public class ReflectionTest {
    @Test
    public void test1() throws Exception {
        Class clazz = Person.class;
//创建运行时类的对象
        Person person = (Person) clazz.newInstance();

//获取运行时类中指定变量名的属性,一般就用getDeclaredXxx()来获取。不用getXxx()
        Field name = clazz.getDeclaredField("name");

        //保证当前属性是可以访问的  !!!
        name.setAccessible(true);
//设置当前属性的值
        name.set(person, "tom");
//获取当前属性的值
        int pname = (int) name.get(person);
        System.out.println(pname);
    }
//如何操作运行时类中指定的方法 ----需要掌握
    @Test
    public void test2() throws Exception {
        Class clazz = Person.class;
//创建运行时类的对象
        Person person = (Person) clazz.newInstance();
//        1.获取指定的某个方法
//        getDeclaredMethod():参数1 ︰指明获取的方法的名称﹑     参数2:指明获取的方法的形参列表
        Method show = clazz.getDeclaredMethod("show", String.class, int.class);
//        保证方法是可以访问的
        show.setAccessible(true);
//        调用方法的invock():参数1:方法的调用者  参数2:给方法形参赋值的实参
//        invoke的返回值就是对应类中调用方法的返回值
        Object p = show.invoke(person, "haiyu", 12);
        System.out.println(p);

        System.out.println();

//        如何调用静态方法
        Method showDesc = clazz.getDeclaredMethod("showDesc");
        showDesc.setAccessible(true);
//        如果调用的运行时类的方法没有返回值,则invoke()返回null
        Object invoke = showDesc.invoke(clazz);
        System.out.println(invoke);//null

    }

//    如何调用运行时类中指定的构造器
    @Test
    public void test3() throws Exception {
        Class<Person> clazz = Person.class;
//获取指定的构造器:getDeclaredConstructor():
        Constructor<Person> declaredConstructor = clazz.getDeclaredConstructor();

        declaredConstructor.setAccessible(true);
//        调用此构造器创建运行时类的对象
        Person person = declaredConstructor.newInstance();
        System.out.println(person);


    }
}

 问题

疑问1:通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用那个?
 建议:直接new的方式。
反射主要是用它的动态性

疑问2:反射机制与面向对象中的封装性是不是矛盾的?如何看待两个技术?
 不矛盾。  
封装性是建议调用公共的,不建议调用私有的;
而反射机制是看能不能调用,非要调用的话公共的私有的都行。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

java塑造中...

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值