第十七次总结 类的加载机制和反射机制

  1. 类加载机制的原理?
  2. 有哪些类加载器?
  3. 获得Class对象的几种方式?
  4. 如何通过Class对象获得构造方法对象?
  5. 如何通过构造方法对象实例化?
  6. 如何通过Class对象获得属性对象和方法对象?

1.类加载机制的原理?

1.启动JVM
2.将需要运行的class文件加载到虚拟机内存中
3.找到主类,开始执行主函数


2.有哪些类加载器?

class文件是如何被加载到JVM内存中的?
通过类加载器将class文件加载到JVM内存中


类加载器:ClassLoader
AppClassLoader 应用类加载器,负责加载核心类,加载自己写的类
ExtClassLoader 扩展类加载器,负责加载扩展类库
bootstrap JVM内置的加载器
Bootstrap类加载器是JVM的一部分,是由C++编写的,
随着启动虚拟机就会创建一个bootstrap类加载器对象


加载步骤:
1.先委托父类加载类,如果父类已经加载,就不需要再次加载,如果父类没有加载,再由本加载器加载

为什么要先委托父类加载类?

MyClassLoader  自定义类加载器
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;

/**
 * 自定义类加载器移除先委托父类加载类的功能
 */
public class MyClassLoader extends  ClassLoader{
    //class文件的根路径
    String parent="D:\idea11\untitled\类的加载机制和反射机制";

    //加载Class对象的方法
    public Class<?> findClass(String name){
        Class c=null;
        try {
            //先委托给父类加载器加载
           c = super.loadClass(name);
            //如果父类加载器未加载
            if(c==null) {
                byte[] bs = loadClassData(name);
                c = this.defineClass(name, bs, 0, bs.length);
            }
            }catch (Exception ef){
            ef.printStackTrace();
        }
        return c;

    }


    //根据类名来从class文件中读取字节数据
    //com.newer.test.Student
    private byte[] loadClassData(String name){
        //将类类路径解析为文件路径
        name = name.replace(".","\\");
        name = name+".class";
        String path = parent+"\\"+name;

        try {
            //建立文件字节输入流
            FileInputStream fis = new FileInputStream(path);
            //创建一个字节数组输出流来缓存读入的数据
            ByteArrayOutputStream bos = new ByteArrayOutputStream();

            int t = fis.read();
            while(t!=-1){
                bos.write(t);
                t = fis.read();
            }
            //将字节数组输出流转换成字节数组
            byte[] bs = bos.toByteArray();

            fis.close();
            bos.close();
            return bs;
        }catch (Exception ef){
            ef.printStackTrace();
        }

        return null;
    }
}

 

Text 测试类

 

 输出结果:

 

 出现了学生不是学生的情况,

因为加载了2次中Student是系统的Student

自动加载器解决方法:

 

 

2.解析类路径,将类路径转换成文件路径
3.通过文件输入流来读取class文件,得到字节数组
4.将字节数组转换成类对象,对象的类型是Class类型

 

任何一个类都只能被加载一次
每一个class文件读取到JVM内中之后,都转换成了一个Class对象


3.获得Class对象的几种方式?

获得Class对象的几种方式

//1.通过类名.class
Class c1 = Student.class;

//2.通过对象的getClass()方法
Class c2 = stu.getClass();

//3.通过类加载器获得class对象
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Class c3 = classLoader.loadClass("com.newer.test.Student");

//4.通过 Class.forName()获得Class对象;
Class c4 = Class.forName("com.newer.test.Student");

MainClass 对象获得class

public class MainClass {
    public static void main(String[] args) throws Exception {
        Student student = new Student();

        //获得Student的Class对象
        //1.通过类名.class
        Class c1 = Student.class;

        //2.通过对象的getClass()方法
        Class c2 = student.getClass();

        //3.通过类加载器获得class对象
        ClassLoader loader = ClassLoader.getSystemClassLoader();
        Class c3 = loader.loadClass("text.Student");

        //4.通过 Class.forName()获得Class对象;
        Class c4 = Class.forName("text.Student");

        System.out.println(c1 == c2);
        System.out.println(c2 == c3);
        System.out.println(c3 == c4);
    }
}

 

输出结果:

 

 

 数组类型的Class对象

 

 

 数组与二维数组不是同一个class

第一种方式,类型.class
  
        Class c1 = int[].class;
        Class c2 = long[].class;

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c1==c2);

 

 

 

输出结果:

 

 

 

第二种方式  数组对象

int[] a = {1,2,3,4,5,6};
Class c3 = a.getClass();

 

 基本数据类型类型的Class对象

类型.class

     Class c5 = int.class;
        Class c6 = long.class;
        System.out.println(c5==c6);

 

包装类的Class对象

第一张方式  类型.class

 Class c7 = Integer.class;

 

 

第二种方式 类型.TYPE

Class c5 = int.class;
Class c8 = Integer.TYPE;
System.out.println(c5==c8);

 

 

输出结果:

 

 

 为什么Interger.TYPE和int.class相等?

 

Interger.TYPE获得的是int的class

拿到包装类对应的基本类型的Class对象

 


4.如何通过Class对象获得构造方法对象?

反射机制

1.为什么需要获得Class对象

程序在运行时能够动态的获得类的相关信息的机制,叫做反射机制

 

正着走:先拿到类的信息,再使用这个类

反着走:先使用这个类,再拿到类的信息

 

类的所有信息都在Class对象中

Class类的反射机制中的核心类

 

如何拿到JVM内存中的class文件的数据?

 

 Student类

package text;

public class Student {
    private int age;
    private String name;
    private int num;

    public Student() {
    }

    private Student(int age) {
        this.age = age;
    }

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

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

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

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

 

 

 

//1.获得所有可见的构造方法
Constructor[] cons= c.getConstructors();

     //加载类,获得类的Class对象
            Class c = Class.forName("text.Student");
    
            //通过Class对象获得类的信息
            //1.获得所有可见的构造方法
            Constructor [] cons = c.getConstructors();
            for (Constructor con : cons){
                System.out.println(con);
            }

 

输出结果:

 

 

 

//2.获得所有已经声明的构造方法
Constructor[] cons2= c.getDeclaredConstructors();

 //2.获得所有已经声明的构造方法
        Constructor [] cons2 =c.getDeclaredConstructors();
        for (Constructor con : cons2){
            System.out.println(con);
        }

 

输出结果:

 

 

 


//3.获得指定的可见的某一个构造方法
Constructor cc = c.getConstructor(int.class,String.class,int.class);

不定项参数的方法
//一个方法中只能有一个不定项参数
//不定项参数必须是最后一个参数
public static void test(int a,int...t){

}

 //3.获得指定的可见的某一个构造方法
        Constructor con1=c.getConstructor(int.class,String.class,int.class);
        System.out.println(con1)

 

输出结果:

 

 

 

 

 

 如果用此方法调用私有的构造方法?

 

 

 

 

输出结果:

 

 

 

//4.从声明的方法中获得指定的构造方法
Constructor cc2 = c.getDeclaredConstructor(int.class);

Constructor con2=c.getDeclaredConstructor(int.class);
        System.out.println(con2);

 

输出结果:

 

 


5.如何通过构造方法对象实例化?

//通过构造方法对象得到类的实例对象
Object obj =  con1.newInstance(22,"张三",18);
        System.out.println(obj);

 

 输出结果:

 

  public static void main(String[] args)throws Exception {
        //得到Class对象
        Class c = Class.forName("classDemo.Student");
        //得到构造方法对象
        Constructor con=c.getConstructor(int.class,String.class,int.class);
        //根据构造方法实例化对象
        Object obj=con.newInstance(1234,"张三",20);
        System.out.println(obj);
        //根据class对象获得属性
        Field f=c.getDeclaredField("name");
        f.setAccessible(true);
        f.set(obj,"法外狂徒张三");
        System.out.println(obj);

        Field f2 =c.getDeclaredField("age");
        f2.setAccessible(true);
        f2.set(obj,21);
        System.out.println(obj);

 

 

 输出结果:

 

 


6.通过Class对象获得属性对象和方法对象?

        Class c2 = Class.forName("classDemo.Student");
         //得到构造方法对象
        Constructor con=c2.getConstructor(int.class,String.class,int.class);
        //根据构造方法实例化对象
        Object obj=con.newInstance(1234,"法外狂徒张三",20);
        //通过Class对象获得方法
        Method method =c2.getMethod("study");
        //方法对象调用
        method.invoke(obj);

        Method method2 =c2.getMethod("study",String.class);
        method2.invoke(obj,"java");    

 

 

输出结果:

 

posted @ 2020-08-12 14:29  Zc小白  阅读(113)  评论(0)    收藏  举报