首页 > 编程语言 > 详细

Java反射机制

时间:2021-04-28 22:19:42      阅读:32      评论:0      收藏:0      [点我收藏+]

1.关于反射的理解

Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何 类的内部信息,并能直接操作任意对象的内部属性及方法。

框架 = 反射 + 注解 + 设计模式。

2.相关API

java.lang.Class:反射的源头 java.lang.reflect.Method java.lang.reflect.Field java.lang.reflect.Constructor

3.关于java.long.Class类的理解

1)类的加载过程:

程序经过javac.exe命令后,会生成一个或多个字节码文件(.class结尾的文件)。然后通过java.exe命令对某个字节码文件进行解释运行进行解释运行。就相当于将某个字节码文件加载到内存中,此过程就称之为类的加载。加载到内存中的类就叫做运行时类,此运行时类,就作为Class的一个实例。

2)Class实例就对应着一个运行时类。

3)加载在内存中的运行时类,会缓存一段时间。在此时间内,我们可以用不同的方式来获取此运行时类。

//获取Class实例的方法
    @Test
    public void test3() throws ClassNotFoundException {
        //方式一:调用运行时类的属性:.class
        Class clazz1 = Person.class;
        System.out.println(clazz1);
        //方式二:通过运行时类的对象,调用getClass()
        Person person = new Person("张三",19);
        Class clazz2 = person.getClass();
        System.out.println(clazz2);
        //(常用)方式三:调用Class类的静态方法:forName(String className)
        Class clazz3 = Class.forName("com.slxy.reflex.Person");
        System.out.println(clazz3);
        //(了解)方式四:使用类的加载器:ClassLoader
        ClassLoader classLoader = PersonTest.class.getClassLoader();
        Class clazz4 = classLoader.loadClass("com.slxy.reflex.Person");
        System.out.println(clazz4);
        System.out.println(clazz1 == clazz4);

    }

4.Class实例可以是那些结构的说明:

 @Test
    public void test4(){
        Class c1 = Object.class;
        Class c2 = Comparable.class;//接口
        Class c3 = String[].class;
        Class c4 = int[][].class;
        Class c5 = ElementType.class;//枚举类
        Class c6 = Override.class;//注解
        Class c7 = int.class;
        Class c8 = void.class;
        Class c9 = Class.class;

        int[] a = new int[2];
        int[] b = new int[3];
        System.out.println(a.getClass()==b.getClass());//结果为true
        //结论:只要数组元素的类型和维度相同,就是同一个Class
    }

5.使用Classloader加载src目录下的配置文件

   public class PersonTest{
    @Test
    public void test5() throws Exception {
        Properties properties = new Properties();
        //读取配置文件的方式一:
        FileInputStream fis = new FileInputStream("src\\jdbc.properties");
        properties.load(fis);
        //读取配置文件的方式二:
        ClassLoader classLoader = PersonTest.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("jdbc.properties");
        properties.load(is);
        
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        System.out.println("user="+user+";password="+password);
    }
   }

技术分享图片

防止properties文件乱码的解决方法:File-->Settings-->Editor-->File Encodings

技术分享图片

6.反射的应用一:通过反射创建运行时类的对象

   @Test
    public void test6() throws IllegalAccessException, InstantiationException {
        Class clazz6 = Person.class;
        /*调用newInstance()方法,创建运行时类的对象。内部调用了运行时类的空参构造器。
            想用此方法正常创建运行时类对象要求:
            1.运行时类必须提供空参构造器。
            2.空参构造器的访问权限得够。通常设置为public
          在javabean中要求提供一个public的空参构造器。原因:
          1.便于通过反射,创建运行时类的对象
          2.便于子类继承此运行时类,默认调用super()时,保证父类有此构造器
        */
        Object obj = clazz6.newInstance();
        Person p = (Person)obj;
        System.out.println(p);
    }

总结:创建类对象的方式:

方式一:new+构造器

方式二:要创建Xxx类的对象,可以考虑:Xxx、Xxxs、XxxFactory、XxxBuilder类中可能构造器被私有化了,可以查看是否有静态方法的存在。可以调用其静态方法,创建Xxx对象。

方式三:通过反射

7.反射应用二:获取运行时类的完整结构(框架中会经常用到的两种):

1)获取运行时类的父类实现的接口

2)获取运行时类的带泛型的父类的泛型

    /*
    获取运行时类的带泛型的父类的泛型
     */
        Class clazz = Person.class;

        Type genericSuperclass = clazz.getGenericSuperclass();
        ParameterizedType paramType = (ParameterizedType) genericSuperclass;
        //获取泛型类型
        Type[] actualTypeArguments = paramType.getActualTypeArguments();
        System.out.println(actualTypeArguments[0].getTypeName());
     /*
      获取运行时类的父类实现的接口
     */
        Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
        for(Class c : interfaces1){
            System.out.println(c);
        }

8.反射应用三:调用运行时类的指定结构

1)如何操作运行时类中指定的属性

方式一: 不推荐
@Test
public void Test1() throws Exception {
    Class clazz1 = Person.class;
    Person p = (Person) clazz1.newInstance();
    Field age = clazz1.getField("age");
    age.set(p,18);
    System.out.println(age.get(p));
}
方式二:
@Test
public void Test2() throws Exception {
    Class clazz2 = Person.class;
    //创建运行时类的对象
    Person p = (Person) clazz2.newInstance();
    //getDeclaredField(String name):获取运行时类中指定变量名的属性
    Field name = clazz2.getDeclaredField("name");
    //保证当前属性是可访问的
    name.setAccessible(true);
    //获取或设置指定对象的此属性值
    //set():参数1:指明设置哪个对象的属性   参数2:将此属性值设置为多少
    name.set(p,"Tom");
    //get():参数1:获取哪个对象的当前属性值
    System.out.println(name.get(p));
}

2)如何操作运行时类中指定的方法

方式一:不推荐
@Test
    public void Test1() throws Exception {
        Class clazz1 = Person.class;
        Person p = (Person)clazz1.newInstance();
        Method show = clazz1.getMethod("show",String.class);
        show.invoke(p,"Tom");
    }
方式二:
    @Test
    public void Test2() throws Exception {
        Class clazz1 = Person.class;
         //创建运行时类的对象
        Person p = (Person)clazz1.newInstance();
        /*  1.获取指定的某个方法
        getDeclaredMethod():参数1 :指明获取的方法的名称  参数2:指明获取的方法的形参列表*/
        Method nationshow = clazz1.getDeclaredMethod("nationShow", String.class);
        //2.保证当前方法是可访问的
        nationshow.setAccessible(true);
        /*
        3. 调用方法的invoke():参数1:方法的调用者  参数2:给方法形参赋值的实参
        invoke()的返回值即为对应类中调用的方法的返回值。
         */
        nationshow.invoke(p,"中国");

    }

3)如何操作运行时类的构造器

@Test
    public void Test3() throws Exception {
        Class clazz = Person.class;
        //获取指定的构造器
        Constructor con = clazz.getDeclaredConstructor(String.class);
        //设置此构造器可以访问
        con.setAccessible(true);
        //调用此构造器创建运行时类的对象
        Person p = (Person)con.newInstance("李四");
        System.out.println(p);
    }

9.反射应用四:动态代理

静态代理举例:

package com.slxy.reflex;
/*静态代理
* 特点:代理类和被代理类在编译期间就已经确定了
* */

public class ProxyTest {
    public static void main(String[] args) {
        Star star = new Star();
        Proxy proxy = new Proxy(star);
        proxy.otherWork();
        proxy.sing();
    }
}
interface Show{
     void otherWork();
     void sing();
}
//被代理类
class Star implements Show{

    @Override
    public void otherWork() {

    }

    @Override
    public void sing() {
        System.out.println("表演唱歌");
    }
}
//代理类
class Proxy implements Show{
    Show star;
    public Proxy(Star star){
        this.star = star;
    }

    @Override
    public void otherWork() {
        System.out.println("做一些准备工作");
    }

    @Override
    public void sing() {
        star.sing();
    }
}

动态代理

package com.slxy.reflex;

import sun.java2d.Surface;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//动态代理
/*
要想实现动态代理,需要解决的问题?
问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。
 */
public class ProxyTest1 {
    public static void main(String[] args) {
        SuperMan superMan = new SuperMan();
        Human proxy =(Human)PorxyFactory.getProxyInstance(superMan);
        proxy.faith();
        proxy.eat("饺子");
        System.out.println("*************");
        Star star = new Star();
        Show proxy1 =(Show) PorxyFactory.getProxyInstance(star);
        proxy1.otherWork();
        proxy1.sing();
    }
}
interface Human{
    void faith();
    void eat(String str);
}
//被代理类
class SuperMan implements Human{

    @Override
    public void faith() {
        System.out.println("I believe i can fly!");
    }

    @Override
    public void eat(String str) {
        System.out.println("我喜欢吃" + str);
    }
}
//动态代理类
class PorxyFactory{
     //调用此方法,返回一个代理类的对象。解决问题一
    public static Object getProxyInstance(Object obj){//obj:被代理类的对象
        MyInvocationHandler handler = new MyInvocationHandler(obj);

        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
    }

}
class MyInvocationHandler implements InvocationHandler{
    private Object obj;
    public MyInvocationHandler(Object obj){
        this.obj = obj;
    }
     //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()
    //将被代理类要执行的方法a的功能就声明在invoke()中
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
        //obj:被代理类的对象
        Object value = method.invoke(obj,args);
         //上述方法的返回值就作为当前类中的invoke()的返回值。
        return value;
    }
}

 

Java反射机制

原文:https://www.cnblogs.com/miao-wu/p/14715757.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!