Java反射代码以及讲解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qichangjian/article/details/86661414

类的声明周期:

1.加载:
 把 .class 加载 到 方法区的内存中,
 并且 创建 一个 对应的对象  在 堆中(Class);
2.连接
   1)验证:
      对字节码文件 .class进行验证。
   2)准备
      对静态变量 分配空间 ,进行默认初始化;
   3)把 符号引用 替换 为 直接引用;。
3.初始化
对静态 变量 声明处 或者 静态块处初始化。

反射: 获得 运行时 类的信息。

 访问属性:
   Field
          getDeclaredFields();  获得 所有的属性(包含私有)
   常用方法: getName()              名
          getType()              类型
          getModifiers()         访问权限

 访问方法:
    Method
            getDeclaredMethods() 获得所有方法
   常用方法:getReturnType() 获得范围值类型
            getParameterTypes() 获得参数列表
  访问构造:
    Constructor
           getDeclaredConstructors(); //获得所有
   常用方法:newInstance() 调用构造

测试加载器测试:

/**
直接调用加载器
    static中的数据在编译器的时候就加载了
 */
public class TestDemo1 {
    static{
        System.out.println("static");
    }

    public static void main(String[] args) throws ClassNotFoundException {
        Class.forName("com.qcj.fanshe.TestDemo1");//这句加不加,静态代码块都加载
    }
}

三种加载器的区别:

/**
 * 三种加载器的区别:
 * 自定义加载器 -》 系统(应用)类加载器 -》 扩展类加载器 -》 根类加载器
 */
public class TestDemo2 {
    public static void main(String[] args) {
        //系统类(应用类)加载器     使用范围我们平时写的java在classpath路径下的经过编译的.class文件
        ClassLoader loader = TestDemo2.class.getClassLoader();
        System.out.println(loader);//看看谁加载的
        //扩展类加载器   父加载器 父扩展     加载jdk/jre/lib/ext下的扩展jar包
        System.out.println(loader.getParent());
        //根加载器                          加载jdk/jre/lib下的jar包
        System.out.println(loader.getParent().getParent());
    }
}

class.forName()和classLoader区别:

/**
        class.forName()和classLoader区别:
java中class.forName()和classLoader都可用来对类进行加载。
class.forName()前者除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。
而classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
 */
public class TestDemo3 {
    public static void main(String[] args) throws ClassNotFoundException {
        //加载类                                          完全限定命名
        ClassLoader.getSystemClassLoader().loadClass("com.qcj.fanshe.Demo1");//只引起类的加载,不引起类的初始化

        //加载类,并初始化  第二个参数:true(默认false) 引起初始化 第三个参数:系统类加载器
        Class.forName("com.qcj.fanshe.Demo1",true,ClassLoader.getSystemClassLoader());
        Class.forName("com.qcj.fanshe.Demo1");
    }
}

class Demo1{
    static{
        System.out.println("Demo1 static");
    }
}

自定义类加载器:

/**   自定义类加载器(重要)
 * 注意:需要重写ClassLoader.loadClass中调用的findClass方法
 *      主要用于自定义的class(也就是不在classpath范围内的class文件)
 *
 *     前期需要在d盘下,写一个C.java doc窗口编译 javac C.java 得到想要的 C.class文件
 */
public class TestDemo4 {
    public static void main(String[] args) throws ClassNotFoundException {
        //自定义类加载器 加载类 .class
        MyLoader my = new MyLoader("d:/");
        Class c = Class.forName("C",true,my);//是true 引起初始化(所以会调用static中的内容)   false 不显示
        System.out.println(c.getClassLoader());//获得是哪个类加载的
        //断开
    }
}

//自定义类加载器
class MyLoader extends ClassLoader{
    String path;
    MyLoader(String path){
        this.path = path;
    }

    //需要重写findClass方法
    //name 传进来的是完全限定命名
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class<?> c = null;
        //"d:/" + "c" + ".class" -> "d:/c.class"
        //有包名
        //path = path + name.replace(".","/").concat(".class");//有包名就替
        //没有包名
        path = path + name.concat(".class");

        //包名有了 需要用流读取
        FileInputStream fin = null;
        try {
            fin = new FileInputStream(path);
            byte[] b = new byte[fin.available()];
            int len = fin.read(b);//读取class中内容到 b数组
            c = this.defineClass(name,b,0,len);//把b中存在的东西解析成方法区能够识别的方式
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                fin.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return c;
    }
}

安全管理器:

/**
 * 安全管理器
 */
public class TestDemo5 {
    public static void main(String[] args) throws IOException {
        System.setSecurityManager(new MySecurityManage());

        File f = new File("D:/C.class");
        FileInputStream fileInputStream = new FileInputStream(f);
        System.out.println(fileInputStream.read());
        fileInputStream.close();
    }
}

//自定义安全管理器   安全权限访问控制
class MySecurityManage extends SecurityManager{
    @Override
    public void checkRead(String file) {
        if(file.endsWith(".class")){
            throw new SecurityException("不能读取class文件");
        }
    }
}

反射:获得运行时类的信息:

/**
 * 反射:获得运行时类的信息
 *          属性
 *          方法
 *          构造方法
 */
public class TestDemo6 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        //从Person.class 中获取信息:反射
        //步骤一:获得字节码文件对应的在堆中对象
        //1. 方法1
        //Class<Person> c2 = Person.class;
        //2. 方法2 new
        //Class<? extends Person> c1 = new Person().getClass();
        //3. 方法3 完全限定命名
        Class<?> c = Class.forName("com.qcj.fanshe.Person");

        System.out.println("--------------属性-------------------");
        //步骤二 : 有了对象后可以获得属性
        //获得共有的属性
        //Field[] fs = c.getFields();
        //获取 所有的属性  共有跟私有
        Field[] fs = c.getDeclaredFields();
        for (Field f:fs) {
            System.out.println(f.getName());//名
            System.out.println(f.getType());//数据类型
            System.out.println(Modifier.toString(f.getModifiers()));//访问权限 需要转换成string
        }
        Field f = c.getDeclaredField("no");
        //如果不怎么也不允许破坏封装 用安全管理器
        //System.setSecurityManager(new SecurityManager());
        f.setAccessible(true);//是否有访问权限 破坏了封装
        Object obj = c.newInstance();//获得一个实例对象 需要加入f.setAccessible(true);/
        f.set(obj,22);//设置对象obj 的 no是22
        System.out.println("f.get(obj):"+f.get(obj)); //得到对象obj的no属性的值

        //-------------------------获得方法
        System.out.println("------------方法-------------------");
        Method[] ms = c.getDeclaredMethods();
        for (Method m:ms) {
            System.out.println(m.getName());
            System.out.println(Modifier.toString(m.getModifiers()));
            System.out.println(m.getReturnType());
            System.out.println(Arrays.toString(m.getParameterTypes()));//返回参数列表
            System.out.println("------");
        }
        //返回单个方法
        Method m1 = c.getDeclaredMethod("f");
        m1.invoke(obj);// 调用方法 (对象+参数)
        //带返回值的
        Method m2 = c.getDeclaredMethod("sf",String.class,int.class);//参数类型
        String s = (String)m2.invoke(obj,"hel" , 2);
        System.out.println("返回值:" + s);

        System.out.println("----------------构造方法---------------------");
        Constructor[] crs = c.getConstructors();
        for (Constructor cr:crs) {
            System.out.println(Arrays.toString(cr.getParameterTypes()));//参数类型
        }
        //单个构造
        Constructor cr1 = c.getConstructor();//得到无参构造
        cr1.newInstance();//用newInstance与用new是区别的,区别在于创建对象的方式不一样,前者是使用类加载机制
        Constructor cr2 = c.getConstructor(int.class,String.class);//有参数构造
        cr2.newInstance(111,"abc");

    }
}

class Person{
    private int no;
    public String name;

    public Person() {
        System.out.println("无参数构造");
    }

    public Person(int no, String name) {
        this.no = no;
        this.name = name;
        System.out.println("有参数构造");
    }

    public void f(){
        System.out.println("无参数f");
    }

    public String sf(String str,int num){
        System.out.println("带参数的sf");
        return str + num;
    }
}

猜你喜欢

转载自blog.csdn.net/qichangjian/article/details/86661414