1.反射机制的作用
通过java语言的反射机制可以操作字节码文件,可以读和修改字节码文件
2.反射机制相关类所在包
java.lang.reflect
3.反射机制相关的重要的类
java.lang.Class:代表字节码文件
java.lang.reflect.Method:代表字节码中的方法字节码
java.lang.reflect.Construotor:代表字节码中的构造方法字节码
java.lang.reflect.Field:代表字节码中的属性字节码
4.获取类的字节码的三种方式
(1)Class c=Class.forName("完整的类名带包名")
Class.forName()
* 1.静态方法
* 2.方法的参数是一个字符串
* 3.字符串需要的是一个完整的类名
* 4.完整类名必须带有包名。java.Lang包也不能省略
1 Class c1=Class.forName("java.lang.String");//c1代表String.cass文件,或者说c1代表String类型 2 Class c2=Class.forName("java.util.Date");//c2代表Date类型 3 Class c3=Class.forName("java.lang.Integer");//c3代表Integer类型 4 Class c4=Class.forName("java.lang.System");//c4代表System类型
(2) 对象.getClass()方法
java中任何一个对象都有一个方法:getClass()
1 String s="abc"; 2 Class x=s.getClass(); //x代表String.class字节码文件,x代表String类型
注:c1和x指向的内存地址相同,都是String类的内存地址
System.out.println(c1==x); //值是true,“==”判断的是对象的内存地址,这里是true,说明c1和x的内存地址是一样的
(3) java语言中任何一种类型,包括基本数据类型,都有.class属性
Class z=String.class; //z代表String类型
Class k=Date.class; //k代表Date类型
Class f=int.class; //f代表int类型
Class e=double.class; //e代表double类型
5.反射机制的优点
可以灵活地实例化对象
先创建一个User类
1 package bean; 3 public class User { 4 5 public User(){ 6 System.out.println("无参数构造方法"); 7 } 9 }
第一种实例化User对象的方式:传统的方式,即通过new方法实例化出一个对象
1 package Reflect; 2 import bean.User;//引入包 3 4 public class ReflectTest03 { 5 public static void main(String[] args) throws Exception{ 6 User user=new User(); 7 } 8 }
第二种方法:先写一个配置文件classinfo.properties
className=bean.User
然后读取这个配置文件中的className,再通过反射机制来实例化User对象
1 package Reflect; 2 import java.io.FileNotFoundException; 3 import java.io.FileReader; 4 import java.util.Properties; 5 public class ReflectTest03 { 6 public static void main(String[] args) throws Exception{ 8 FileReader reader=new FileReader("classinfo.properties");//通过IO流读取classinfo.properties文件 9 Properties pro=new Properties();//创建属性类别对象Map 10 pro.load(reader);//加载 11 reader.close();//关闭 12 String className=pro.getProperty("className");//通过key获取value 13 System.out.println(className);//打印类名 14 15 //通过反射机制实例化对象 16 Class c=Class.forName(className); 17 Object o=c.newInstance(); 18 System.out.println(o);//打印类 20 } 22 }
这里也许没有直观的体现出灵活性在哪,有时候可能会因为觉得方法二的代码量比较多,反而觉得它不如方法一。但是做这样一种设想,如果此时我们要重新实例化一个对象,如果采用第一种方法的话,我们需要重新编写代码,使用new方法来实例化对象,而如果采用第二种方法,我们直接修改配置文件中className的值,不需要再对其它代码做修改。这便是灵活性的体现:
例如:再实例化一个Date对象
方法一:
1 package Reflect; 2 import java.util.Date; 3 import java.util.Properties; 4 public class ReflectTest03 { 5 public static void main(String[] args) throws Exception{ 6 Date date=new Date(); 7 } 8 }
方法二:
直接修改配置文件classinfo.properties中className的值
className=java.util.Date
其他代码无需改动
6.研究:Class.forName
1 package Reflect; 2 3 public class ReflectTest04 { 4 public static void main(String[] args){ 5 try { 6 Class.forName("Reflect.MyClass"); 7 } catch (ClassNotFoundException e) { 8 // TODO Auto-generated catch block 9 e.printStackTrace(); 10 } 11 } 12 13 } 14 15 class MyClass{ 16 //静态代码块在类加载时执行,并且只执行一次 17 static{ 18 System.out.println("MyClass类的静态代码块执行了!"); 19 } 20 }
运行结果:
可以看到静态代码块执行了,这也就说明类被加载了,因为类的加载会导致静态代码块的执行
所以,如果只希望一个类的静态代码块执行,而不希望其他代码执行,可以使用Class.forName(“完整类名”),这个方法的执行会导致类加载,而类加载时会导致静态代码块执行。
7.研究文件路径的问题
(1)一般获取路径的方法:
FileReader reader=new FileReader("classinfo.properties");
存在缺陷:如果代码离开eclipse,那么当前路径可能就不是project的根了,这时这个路径就无效了。可移植性差
(2)通过:Thread.currentThread().getContextClassLoader().getResource(" ").getPath();方法获取文件的绝对路径
1 package Reflect; 2 import java.io.FileNotFoundException; 3 import java.io.FileReader; 4 public class AboutPath { 5 public static void main(String[] args) throws Exception{ 6 String path=Thread.currentThread().getContextClassLoader().getResource("classinfo2.properties").getPath(); 7 System.out.println(path); //通过打印可以发现获取到了文件的绝对路径 8 } 9 }
运行结果:
可以看到,获取到了classinfo2.properties文件的绝对路径。
解释:
Thread.currentThread() 当前线程对象
getContextClassLoader() 线程对象的方法,可以获取到当前线程的类加载器对象
getResource(" ") 这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源。即文件的起点是src
注意:
使用该方法的前提是,这个文件必须在类路径下。什么是类路径下:凡是在src下的都是类路径下.(src是类的路径)
原文:https://www.cnblogs.com/Leeyoung888/p/13907082.html