本篇文章依旧采用小例子来说明,因为我始终觉的,案例驱动是最好的,要不然只看理论的话,看了也不懂,不过建议大家在看完文章之后,在回过头去看看理论,会有更好的理解。
下面开始正文。
【案例1】通过一个对象获得完整的包名和类名
1 package Reflect; 2 3 /** 4 * 通过一个对象获得完整的包名和类名 5 * */ 6 class Demo{ 7 //other codes... 8 } 9 10 class hello{ 11 public static void main(String[] args) { 12 Demo demo=new Demo(); 13 System.out.println(demo.getClass().getName()); 14 } 15 } 16 【运行结果】:Reflect.Demo
添加一句:所有类的对象其实都是Class的实例。
【案例2】实例化Class类对象
1 package Reflect; 2 class Demo{ 3 //other codes... 4 } 5 6 class hello{ 7 public static void main(String[] args) { 8 Class<?> demo1=null; 9 Class<?> demo2=null; 10 Class<?> demo3=null; 11 try{ 12 //一般尽量采用这种形式 13 demo1=Class.forName("Reflect.Demo"); 14 }catch(Exception e){ 15 e.printStackTrace(); 16 } 17 demo2=new Demo().getClass(); 18 demo3=Demo.class; 19 20 System.out.println("类名称 "+demo1.getName()); 21 System.out.println("类名称 "+demo2.getName()); 22 System.out.println("类名称 "+demo3.getName()); 23 24 } 25 }
【运行结果】: 类名称 Reflect.Demo 类名称 Reflect.Demo 类名称 Reflect.Demo
【案例3】通过Class实例化其他类的对象
通过无参构造实例化对象
1 package Reflect; 2 3 class Person{ 4 5 public String getName() { 6 return name; 7 } 8 public void setName(String name) { 9 this.name = name; 10 } 11 public int getAge() { 12 return age; 13 } 14 public void setAge(int age) { 15 this.age = age; 16 } 17 @Override 18 public String toString(){ 19 return "["+this.name+" "+this.age+"]"; 20 } 21 private String name; 22 private int age; 23 } 24 25 class hello{ 26 public static void main(String[] args) { 27 Class<?> demo=null; 28 try{ 29 demo=Class.forName("Reflect.Person"); 30 }catch (Exception e) { 31 e.printStackTrace(); 32 } 33 Person per=null; 34 try { 35 per=(Person)demo.newInstance(); 36 } catch (InstantiationException e) { 37 // TODO Auto-generated catch block 38 e.printStackTrace(); 39 } catch (IllegalAccessException e) { 40 // TODO Auto-generated catch block 41 e.printStackTrace(); 42 } 43 per.setName("Rollen"); 44 per.setAge(20); 45 System.out.println(per); 46 } 47 } 48 【运行结果】: 49 50 [Rollen 20]
1 但是注意一下,当我们把Person中的默认的无参构造函数取消的时候,比如自己定义只定义一个有参数的构造函数之后,会出现错误: 2 3 比如我定义了一个构造函数: 4 5 1 6 2 7 3 8 4 9 public Person(String name, int age) { 10 this.age=age; 11 this.name=name; 12 } 13 然后继续运行上面的程序,会出现: 14 15 java.lang.InstantiationException: Reflect.Person 16 17 at java.lang.Class.newInstance0(Class.java:340) 18 19 at java.lang.Class.newInstance(Class.java:308) 20 21 at Reflect.hello.main(hello.java:39) 22 23 Exception in thread "main" java.lang.NullPointerException 24 25 at Reflect.hello.main(hello.java:47) 26 27 所以大家以后再编写使用Class实例化其他类的对象的时候,一定要自己定义无参的构造函数
类的生命周期
在一个类编译完成之后,下一步就需要开始使用类,如果要使用一个类,肯定离不开JVM。在程序执行中JVM通过装载,链接,初始化这3个步骤完成。
类的装载是通过类加载器完成的,加载器将.class文件的二进制文件装入JVM的方法区,并且在堆区创建描述这个类的java.lang.Class对象。用来封装数据。但是同一个类只会被类装载器装载以前
链接就是把二进制数据组装为可以运行的状态。
链接分为校验,准备,解析这3个阶段
校验一般用来确认此二进制文件是否适合当前的JVM(版本),
准备就是为静态成员分配内存空间,。并设置默认值
解析指的是转换常量池中的代码作为直接引用的过程,直到所有的符号引用都可以被运行程序使用(建立完整的对应关系)
完成之后,类型也就完成了初始化,初始化之后类的对象就可以正常使用了,直到一个对象不再使用之后,将被垃圾回收。释放空间。
当没有任何引用指向Class对象时就会被卸载,结束类的生命周期
将反射用于工厂模式