第十七次总结 类的加载机制和反射机制
- 类加载机制的原理?
- 有哪些类加载器?
- 获得Class对象的几种方式?
- 如何通过Class对象获得构造方法对象?
- 如何通过构造方法对象实例化?
- 如何通过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");
输出结果:


浙公网安备 33010602011771号